diff --git a/.skills/skills/raindrop-cite/SKILL.md b/.skills/skills/raindrop-cite/SKILL.md new file mode 100644 index 0000000..2bcf2f4 --- /dev/null +++ b/.skills/skills/raindrop-cite/SKILL.md @@ -0,0 +1,66 @@ +--- +name: raindrop-cite +description: | + **Raindrop Citing & Knowledge Lookup**: Search the user's curated Raindrop.io bookmark library across 7 structured collections (AI & LLMs, Cybersecurity, Software Engineering, Data Science, Papers & Research, Lifestyle, Inbox) using normalized tags. Formats findings into high-quality academic markdown citations for Obsidian note `## References` sections, technical ADRs, and study guides. + - MANDATORY TRIGGERS: cite raindrop, raindrop cite, search raindrop, find raindrop bookmarks, enrich note with raindrop, lookup raindrop +--- + +## Purpose + +The **raindrop-cite** skill enables the agent to discover, retrieve, and cite curated bookmarks, research papers, tool repos, and technical articles from the user's Raindrop.io account. Rather than inventing or searching external web citations blindly, this skill prioritizes the user's personal, vetted bookmark knowledge base. + +--- + +## Raindrop Collection Taxonomy & ID Registry + +| Collection | ID | Primary Domain & Topics | Target Tags | +| ------------------------ | ---------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | +| **AI & LLMs** | `74060706` | AI/ML systems, agent frameworks, LLM eval, prompt engineering, RAG, reasoning | `ai`, `llm`, `agents`, `rag`, `reasoning`, `deep-learning`, `mcp`, `harness`, `claude`, `multi-agent`, `prompt`, `skills`, `llmevaluation` | +| **Cybersecurity** | `74060707` | Offensive security, OSCP labs, PrivEsc, Active Directory, LLM security, red teaming | `cybersecurity`, `llmsecurity`, `pentesting`, `oscp` | +| **Software Engineering** | `74060708` | Architecture, systems design, developer tools, leadership, Python, TUI | `software-engineering`, `engineering-leadership`, `github`, `python`, `obsidian`, `tools` | +| **Data Science** | `74060709` | Data pipelines, ML ops, SQL, analytics, algorithms | `data-science`, `sql`, `text2sql`, `algorithm`, `document_analysis`, `graph` | +| **Papers & Research** | `74060710` | Academic papers, survey papers, formal benchmarks | `paper`, `survey`, `explainability` | +| **Lifestyle** | `74060712` | Coffee, travel, cooking, personal care | `coffee`, `cooking`, `house`, `travelling`, `Japan`, `german`, `personal-care` | +| **Inbox** | `74060713` | Triage / newly captured bookmarks | _(unrouted items)_ | + +--- + +## Workflow + +### 1. Identify Context & Query Intent + +- Analyze the topic of the current Obsidian note, design log (ADR), or user question. +- Map the topic to one or more primary Collections (from table above) and relevant tags. + +### 2. Search Raindrop + +Query the Raindrop API or MCP server using search parameters: + +- **By Tag**: `{"search": "#llmsecurity"}` or `{"search": "#agents"}` +- **By Collection**: Query `/rest/v1/raindrops/{collection_id}?search={keywords}` +- **Global Search**: Query `/rest/v1/raindrops/0?search={query}` + +### 3. Filter & Synthesize + +- Select the top 1 to 4 most relevant bookmarks. +- Extract: + - Title and clean URL + - Collection name and assigned tags + - Highlights / excerpts / notes attached to the bookmark + +### 4. Format Citations + +Format the selected bookmarks into the standard markdown citation style for Obsidian and codebase notes: + +```markdown +## References + +1. [Article Title](https://example.com/url) — _Saved in AI & LLMs (`#agents`, `#llm`)_ +2. [Research Repository](https://github.com/org/repo) — _Saved in Cybersecurity (`#llmsecurity`)_ +``` + +If updating an existing Obsidian note: + +- Check if `## References` section exists. +- Append newly discovered Raindrop links in numbered or bulleted list format. +- Preserve existing references. diff --git a/.skills/skills/raindrop-cite/raindrop-cite/SKILL.md b/.skills/skills/raindrop-cite/raindrop-cite/SKILL.md new file mode 100644 index 0000000..7ac084c --- /dev/null +++ b/.skills/skills/raindrop-cite/raindrop-cite/SKILL.md @@ -0,0 +1,82 @@ +--- +name: raindrop-cite +description: | + **Raindrop Citing & Knowledge Lookup**: Search the user's curated Raindrop.io bookmark library across 7 structured collections (AI & LLMs, Cybersecurity, Software Engineering, Data Science, Papers & Research, Lifestyle, Inbox) using normalized tags. Formats findings into high-quality academic markdown citations for Obsidian note `## References` sections, technical ADRs, and study guides. + - MANDATORY TRIGGERS: cite raindrop, raindrop cite, search raindrop, find raindrop bookmarks, enrich note with raindrop, lookup raindrop +--- + +## Purpose + +The **raindrop-cite** skill enables the agent to discover, retrieve, and cite curated bookmarks, research papers, tool repos, and technical articles from the user's Raindrop.io account. Rather than inventing or searching external web citations blindly, this skill prioritizes the user's personal, vetted bookmark knowledge base. + +--- + +## Raindrop Collection Taxonomy & ID Registry + +| Collection | ID | Primary Domain & Topics | Target Tags | +| ------------------------ | ---------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | +| **AI & LLMs** | `74060706` | AI/ML systems, agent frameworks, LLM eval, prompt engineering, RAG, reasoning | `ai`, `llm`, `agents`, `rag`, `reasoning`, `deep-learning`, `mcp`, `harness`, `claude`, `multi-agent`, `prompt`, `skills`, `llmevaluation` | +| **Cybersecurity** | `74060707` | Offensive security, OSCP labs, PrivEsc, Active Directory, LLM security, red teaming | `cybersecurity`, `llmsecurity`, `pentesting`, `oscp` | +| **Software Engineering** | `74060708` | Architecture, systems design, developer tools, leadership, Python, TUI | `software-engineering`, `engineering-leadership`, `github`, `python`, `obsidian`, `tools` | +| **Data Science** | `74060709` | Data pipelines, ML ops, SQL, analytics, algorithms | `data-science`, `sql`, `text2sql`, `algorithm`, `document_analysis`, `graph` | +| **Papers & Research** | `74060710` | Academic papers, survey papers, formal benchmarks | `paper`, `survey`, `explainability` | +| **Lifestyle** | `74060712` | Coffee, travel, cooking, personal care | `coffee`, `cooking`, `house`, `travelling`, `Japan`, `german`, `personal-care` | +| **Inbox** | `74060713` | Triage / newly captured bookmarks | _(unrouted items)_ | + +--- + +## Workflow + +### 1. Identify Context & Query Intent + +- Analyze the topic of the current Obsidian note, design log (ADR), or user question. +- Map the topic to one or more primary Collections (from table above) and relevant tags. + +### 2. Search Raindrop + +Query the Raindrop API or MCP server using search parameters: + +- **By Tag**: `{"search": "#llmsecurity"}` or `{"search": "#agents"}` +- **By Collection**: Query `/rest/v1/raindrops/{collection_id}?search={keywords}` +- **Global Search**: Query `/rest/v1/raindrops/0?search={query}` + +### 3. Filter & Synthesize + +- Select the top 1 to 4 most relevant bookmarks. +- Extract: + - Title and clean URL + - Collection name and assigned tags + - Highlights / excerpts / notes attached to the bookmark + +### 4. Format Citations + +Format the selected bookmarks into the standard markdown citation style for Obsidian and codebase notes: + +```markdown +## References + +1. [Article Title](https://example.com/url) — _Saved in AI & LLMs (`#agents`, `#llm`)_ +2. [Research Repository](https://github.com/org/repo) — _Saved in Cybersecurity (`#llmsecurity`)_ +``` + +If updating an existing Obsidian note: + +- Check if `## References` section exists. +- Append newly discovered Raindrop links in numbered or bulleted list format. +- Preserve existing references. + +--- + +## Example Usage + +### Scenario: Drafting an Obsidian note on Prompt Injection Defenses + +1. Agent queries Raindrop: `search="#llmsecurity prompt injection"` in collection `74060707`. +2. Raindrop returns `seojoonkim/prompt-guard` and `Numbat EDR Research`. +3. Agent inserts citations into the note: + ```markdown + ## References + + 1. [seojoonkim/prompt-guard: Advanced prompt injection defense system](https://github.com/seojoonkim/prompt-guard) — _Saved in Cybersecurity (`#llmsecurity`)_ + 2. [Securing Agents with Numbat](https://research.perplexity.ai/articles/securing-agents-across-perplexity%E2%80%99s-client-endpoints-with-numbat) — _Saved in Cybersecurity (`#llmsecurity`, `#harness`)_ + ``` diff --git a/custom-site-assets/sass/custom.scss b/custom-site-assets/sass/custom.scss index 6c46389..925c0a0 100644 --- a/custom-site-assets/sass/custom.scss +++ b/custom-site-assets/sass/custom.scss @@ -749,8 +749,182 @@ body.dark .form-control.is-search:focus { background: var(--accent-teal) !important; } +/* Mobile Sidebar Toggle Button */ +.sidebar-toggle-btn { + background: transparent !important; + border: 1px solid var(--border-color) !important; + border-radius: 6px !important; + padding: 0.45rem 0.55rem !important; + margin-right: 0.75rem !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + cursor: pointer !important; + color: var(--text-main) !important; + transition: all 0.2s ease !important; +} + +.sidebar-toggle-btn:hover, +.sidebar-toggle-btn.active { + background-color: var(--bg-hover) !important; + border-color: var(--accent-teal) !important; +} + +.sidebar-toggle-btn .navicon { + position: relative; + display: block; + width: 18px; + height: 2px; + background-color: var(--text-main); + transition: all 0.2s ease-out; +} + +.sidebar-toggle-btn .navicon::before, +.sidebar-toggle-btn .navicon::after { + content: ""; + position: absolute; + left: 0; + width: 18px; + height: 2px; + background-color: var(--text-main); + transition: all 0.2s ease-out; +} + +.sidebar-toggle-btn .navicon::before { + top: -6px; +} + +.sidebar-toggle-btn .navicon::after { + bottom: -6px; +} + +.sidebar-toggle-btn.active .navicon { + background-color: transparent !important; +} + +.sidebar-toggle-btn.active .navicon::before { + transform: rotate(-45deg); + top: 0; +} + +.sidebar-toggle-btn.active .navicon::after { + transform: rotate(45deg); + bottom: 0; +} + +/* Off-Canvas Mobile Navigation Drawer (< 992px) */ +@media (max-width: 991.98px) { + .docs-sidebar { + position: fixed !important; + top: 0 !important; + left: 0 !important; + bottom: 0 !important; + width: 85vw !important; + max-width: 340px !important; + height: 100vh !important; + padding: 1.25rem 1.15rem 4rem 1.15rem !important; + background-color: var(--bg-base) !important; + border-right: 1px solid var(--border-color) !important; + z-index: 1100 !important; + overflow-y: auto !important; + -webkit-overflow-scrolling: touch !important; + transform: translateX(-100%) !important; + transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.28s ease !important; + order: unset !important; + display: block !important; + } + + .docs-sidebar.drawer-open { + transform: translateX(0) !important; + box-shadow: 14px 0 36px rgba(0, 0, 0, 0.35) !important; + } + + .sidebar-header { + display: flex !important; + align-items: center !important; + justify-content: space-between !important; + padding-bottom: 0.85rem !important; + margin-bottom: 1.25rem !important; + border-bottom: 1px solid var(--border-color) !important; + } + + .sidebar-title { + font-family: var(--font-ui) !important; + font-size: 1.05rem !important; + font-weight: 700 !important; + color: var(--text-headings) !important; + letter-spacing: -0.01em !important; + } + + .sidebar-close-btn { + background: var(--bg-panel) !important; + border: 1px solid var(--border-color) !important; + border-radius: 6px !important; + color: var(--text-muted) !important; + width: 32px !important; + height: 32px !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + cursor: pointer !important; + transition: all 0.2s ease !important; + } + + .sidebar-close-btn:hover { + color: var(--text-headings) !important; + border-color: var(--accent-teal) !important; + background-color: var(--bg-hover) !important; + } + + .sidebar-backdrop { + position: fixed !important; + top: 0 !important; + left: 0 !important; + right: 0 !important; + bottom: 0 !important; + width: 100% !important; + height: 100% !important; + background-color: rgba(0, 0, 0, 0.5) !important; + backdrop-filter: blur(4px) !important; + -webkit-backdrop-filter: blur(4px) !important; + z-index: 1080 !important; + opacity: 0 !important; + pointer-events: none !important; + transition: opacity 0.28s ease !important; + } + + .sidebar-backdrop.active { + opacity: 1 !important; + pointer-events: auto !important; + } + + body.drawer-locked { + overflow: hidden !important; + touch-action: none !important; + } + + .docs-content { + margin-left: 0 !important; + margin-right: 0 !important; + width: 100% !important; + max-width: 100% !important; + padding: 1.5rem 1rem 4rem 1rem !important; + flex: 1 1 100% !important; + } + + .docs-toc { + display: none !important; + } +} + /* Sidebar Fixed Panel Layout on Desktop */ @media (min-width: 992px) { + .sidebar-header, + .sidebar-backdrop, + .sidebar-toggle-btn { + display: none !important; + } + .docs-sidebar { position: fixed !important; top: var(--navbar-height) !important; /* locked below header */ @@ -762,6 +936,7 @@ body.dark .form-control.is-search:focus { border-right: 1px solid var(--border-color) !important; background-color: var(--bg-base) !important; z-index: 900 !important; + transform: none !important; } } diff --git a/custom-site-assets/static/js/custom.js b/custom-site-assets/static/js/custom.js index 0b680c4..3c8f982 100644 --- a/custom-site-assets/static/js/custom.js +++ b/custom-site-assets/static/js/custom.js @@ -24,6 +24,9 @@ document.addEventListener("DOMContentLoaded", () => { // Fix and initialize collapsible sidebar folders initCollapsibleSidebar(); + + // Initialize mobile off-canvas drawer navigation + initMobileDrawer(); }); // Helper to escape HTML characters @@ -317,3 +320,108 @@ function initCollapsibleSidebar() { }); }); } + +// Initialize Mobile Navigation Off-Canvas Drawer +function initMobileDrawer() { + const sidebar = + document.getElementById("docs-sidebar") || + document.querySelector(".docs-sidebar"); + const backdrop = document.getElementById("sidebar-backdrop"); + const toggleBtn = document.getElementById("mobile-sidebar-toggle"); + const closeBtn = document.getElementById("sidebar-close-btn"); + const menuCheckbox = document.getElementById("menu-btn"); + + if (!sidebar) return; + + function openDrawer() { + sidebar.classList.add("drawer-open"); + if (backdrop) backdrop.classList.add("active"); + if (toggleBtn) { + toggleBtn.setAttribute("aria-expanded", "true"); + toggleBtn.classList.add("active"); + } + document.body.classList.add("drawer-locked"); + } + + function closeDrawer() { + sidebar.classList.remove("drawer-open"); + if (backdrop) backdrop.classList.remove("active"); + if (toggleBtn) { + toggleBtn.setAttribute("aria-expanded", "false"); + toggleBtn.classList.remove("active"); + } + document.body.classList.remove("drawer-locked"); + if (menuCheckbox) menuCheckbox.checked = false; + } + + if (toggleBtn) { + toggleBtn.addEventListener("click", (e) => { + e.preventDefault(); + e.stopPropagation(); + if (sidebar.classList.contains("drawer-open")) { + closeDrawer(); + } else { + openDrawer(); + } + }); + } + + // Support clicking fallback menu icon if present + const menuIcon = document.querySelector(".menu-icon"); + if (menuIcon) { + menuIcon.addEventListener("click", (e) => { + e.preventDefault(); + e.stopPropagation(); + if (sidebar.classList.contains("drawer-open")) { + closeDrawer(); + } else { + openDrawer(); + } + }); + } + + if (closeBtn) { + closeBtn.addEventListener("click", (e) => { + e.preventDefault(); + closeDrawer(); + }); + } + + if (backdrop) { + backdrop.addEventListener("click", (e) => { + e.preventDefault(); + closeDrawer(); + }); + } + + // Close drawer when a note link is clicked on mobile + const sidebarLinks = sidebar.querySelectorAll("a.docs-link"); + sidebarLinks.forEach((link) => { + link.addEventListener("click", () => { + if (window.innerWidth < 992) { + closeDrawer(); + } + }); + }); + + // ESC key closes drawer + document.addEventListener("keydown", (e) => { + if (e.key === "Escape" && sidebar.classList.contains("drawer-open")) { + closeDrawer(); + } + }); + + // Close drawer if viewport resizes above desktop threshold + window.addEventListener( + "resize", + () => { + if ( + window.innerWidth >= 992 && + sidebar.classList.contains("drawer-open") + ) { + closeDrawer(); + } + }, + { passive: true }, + ); +} diff --git a/custom-site-assets/templates/docs/page.html b/custom-site-assets/templates/docs/page.html index 2f2d37f..01c73da 100644 --- a/custom-site-assets/templates/docs/page.html +++ b/custom-site-assets/templates/docs/page.html @@ -11,6 +11,7 @@ {% endblock header %} {% block content %} +