diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4c26bda..fe28334 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: python-version: "3.12" - name: Run Pre-Commit Hooks in CI - uses: pre-commit/action@v3.0.1 + run: uvx pre-commit run --all-files - name: Install Dependencies run: uv pip install -e ".[test]" || uv pip install -r requirements.txt || true @@ -63,7 +63,7 @@ jobs: - name: Upload Semgrep results to GitHub Security uses: github/codeql-action/upload-sarif@v3 - if: always() + if: always() && github.event_name != 'pull_request' with: sarif_file: semgrep-results.sarif @@ -79,7 +79,7 @@ jobs: # Pinned securely to v0.36.0 commit SHA to mitigate CVE-2026-33634 - name: Run Trivy File System Scanner - uses: aquasecurity/trivy-action@ed142fda32cedf951e7f3d537877df4c9ba8f192 # v0.36.0 + uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 with: scan-type: 'fs' ignore-unfixed: true @@ -89,6 +89,6 @@ jobs: - name: Upload Trivy results to GitHub Security uses: github/codeql-action/upload-sarif@v3 - if: always() + if: always() && github.event_name != 'pull_request' with: sarif_file: trivy-results.sarif diff --git a/.github/workflows/dependabot-linear.yml b/.github/workflows/dependabot-linear.yml new file mode 100644 index 0000000..d2a22e2 --- /dev/null +++ b/.github/workflows/dependabot-linear.yml @@ -0,0 +1,37 @@ +name: Sync Dependabot PRs to Linear +on: + pull_request: + types: [opened] + +jobs: + create-linear-ticket: + if: github.actor == 'dependabot[bot]' + runs-on: ubuntu-latest + steps: + - name: Create Linear Issue + env: + LINEAR_TOKEN: ${{ secrets.LINEAR_TOKEN }} + PR_TITLE: ${{ github.event.pull_request.title }} + PR_URL: ${{ github.event.pull_request.html_url }} + run: | + JSON_PAYLOAD=$(jq -n \ + --arg title "Dependabot: $PR_TITLE" \ + --arg desc "GitHub PR: $PR_URL\nOpened by Dependabot." \ + --arg team "a46f213b-5a47-43bb-bea9-ca254bd3dfe2" \ + --arg project "329d07ee-9041-4706-9244-bd2dc7dfb982" \ + '{ + query: "mutation IssueCreate($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id number identifier url } } }", + variables: { + input: { + title: $title, + description: $desc, + teamId: $team, + projectId: $project + } + } + }') + + curl -X POST https://api.linear.app/graphql \ + -H "Content-Type: application/json" \ + -H "Authorization: $LINEAR_TOKEN" \ + -d "$JSON_PAYLOAD" diff --git a/.gitignore b/.gitignore index 9151139..4eeb71e 100644 --- a/.gitignore +++ b/.gitignore @@ -18,7 +18,3 @@ # Python bytecode cache __pycache__/ *.pyc - - - - diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 966ca8f..e919d30 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,5 @@ # .pre-commit-config.yaml -exclude: '^(Machine Learning|Cybersecurity|Papers|Study|Templates|Attachments|Miscellaneous|Inbox|__site|__obsidian|node_modules)/' +exclude: '^(\.obsidian|Coffee|Machine Learning|Cybersecurity|Miscellaneous|PDFs|Papers|Study|Templates|Attachments|Inbox|__site|__obsidian|node_modules)/' repos: # 1. General Repository Hygiene diff --git a/.skills/skills/clip-to-vault/clip-to-vault/SKILL.md b/.skills/skills/clip-to-vault/clip-to-vault/SKILL.md index 5d7245c..e6f34ea 100644 --- a/.skills/skills/clip-to-vault/clip-to-vault/SKILL.md +++ b/.skills/skills/clip-to-vault/clip-to-vault/SKILL.md @@ -14,6 +14,7 @@ The **clip-to-vault** skill transforms web content into vault notes through synt ### Input The agent accepts a **URL** pointing to: + - Technical blog posts (e.g., Anthropic blog, Papers with Code) - Official documentation (LLM APIs, ML frameworks) - Conference talks or transcripts @@ -135,6 +136,7 @@ constitutional_ai, value_alignment, llm_training, ai_safety ## Inbox Awareness When the user requests a fast capture rather than a full synthesis (e.g., "clip this to inbox", "save to inbox quickly"), the skill should create a minimal capture instead: + - **File location**: `Inbox/[title].md` (at vault root) - **Minimal format**: Title, URL, 1–2 sentence summary, no internal links or full synthesis required - The Inbox serves as a staging area for rapid ideas; items can be processed into full notes later @@ -149,4 +151,3 @@ The skill can also **process existing Inbox items** into proper notes. When the - **Check for duplicates**: Before creating a new note, verify that the vault does not already contain this information. - **Style consistency**: Match the vault's academic tone. No casual language, no contractions, third-person perspective. - **Link with intention**: Every wikilink should be meaningful. Readers should want to follow them to deepen understanding. - diff --git a/.skills/skills/connect-ideas/connect-ideas/SKILL.md b/.skills/skills/connect-ideas/connect-ideas/SKILL.md index bdce453..00b612a 100644 --- a/.skills/skills/connect-ideas/connect-ideas/SKILL.md +++ b/.skills/skills/connect-ideas/connect-ideas/SKILL.md @@ -14,11 +14,13 @@ This skill analyzes two related notes or a note and its linked references to unc ### 1. Gather Input Notes Request the user provide: + - Primary note name or path (the focus note) - Secondary note name or path (the comparison note), OR a list of linked notes to analyze - Any specific aspect to emphasize (e.g., methodological, theoretical, practical) Read both notes fully. Document: + - Primary themes and key concepts in each - Explicit claims, definitions, and arguments - Examples, evidence, or cited resources @@ -27,6 +29,7 @@ Read both notes fully. Document: ### 2. Identify Shared Concepts Compare the notes and list **shared conceptual terrain**: + - Overlapping terminology or frameworks - Related but differently-named concepts - Common underlying principles or assumptions @@ -37,11 +40,13 @@ Create a table or bullet list showing how each note addresses the shared concept ### 3. Detect Contradictions or Tensions Scan for potential disagreements: + - Do the notes reach conflicting conclusions on the same topic? - Do they prioritize different aspects (e.g., theory vs. practice)? - Are there unstated assumptions in each note that contradict the other? For each tension found: + - State it clearly and objectively - Explain the reasoning behind each position - Assess whether this is a true contradiction or a complementary perspective @@ -50,6 +55,7 @@ For each tension found: ### 4. Discover Non-Obvious Connections Look beyond surface-level similarity: + - Does one note provide background context that explains a gap in the other? - Can concepts from Note A apply as a solution or example in Note B? - Do the two notes address the same problem from different angles? @@ -60,11 +66,13 @@ Articulate each hidden connection in a short sentence with clear reasoning. ### 5. Suggest Specific Wikilinks For each connection found, recommend: + - A wikilink to add to the primary note (format: `[[note-name|anchor-text]]`) - A reciprocal wikilink to add to the secondary note - Optional: A suggested context phrase where the link should appear Prioritize links that would: + - Help future readers jump between related ideas - Fill gaps in the vault's knowledge graph - Make tacit connections explicit @@ -72,6 +80,7 @@ Prioritize links that would: ### 6. Optional: Produce a Mermaid Diagram If the relationship is complex or multi-faceted, create a Mermaid diagram showing: + - The two main notes as nodes - Shared concepts as intermediate nodes - Arrows labeled with the type of relationship (contradicts, extends, applies-to, provides-context-for, etc.) @@ -82,7 +91,7 @@ Use a graph or flowchart format. Keep labels concise. Structure the analysis as follows: -``` +```` ## Connection Analysis: [Note A] ↔ [Note B] ### Shared Conceptual Ground @@ -120,7 +129,7 @@ graph LR B -->|addresses| C A -->|employs| D B -->|contrasts with| D -``` +```` ``` @@ -152,3 +161,4 @@ graph LR #### Tags connect_ideas, vault_maintenance, knowledge_graph, cross_reference, analysis_tool +``` diff --git a/.skills/skills/content-generation/content-generation/SKILL.md b/.skills/skills/content-generation/content-generation/SKILL.md index e03885d..98e333b 100644 --- a/.skills/skills/content-generation/content-generation/SKILL.md +++ b/.skills/skills/content-generation/content-generation/SKILL.md @@ -12,6 +12,7 @@ This skill transforms academic or technical knowledge vault notes into polished, ## Target Platforms & Tone Adaptation **LinkedIn Posts** (150–300 words): + - Conversational, first-person voice ("I've learned...", "Here's what I found...") - Hook opening: provocative question, surprising insight, or personal observation - 2–3 key takeaways, clearly structured @@ -20,6 +21,7 @@ This skill transforms academic or technical knowledge vault notes into polished, - Tone: accessible, relatable, thought-leadership **Blog Posts** (600–1,500 words): + - Narrative-driven introduction with real-world context - Structured sections with H2/H3 headers - Mix of explanation, examples, and actionable insights @@ -29,6 +31,7 @@ This skill transforms academic or technical knowledge vault notes into polished, - Hashtags: not required; metadata/SEO keywords optional **Technical Articles** (800–2,000+ words): + - Depth and nuance; cite sources and vault cross-references - Professional, authoritative tone (suitable for Medium, Dev.to, personal site) - Code snippets or architecture diagrams allowed @@ -38,27 +41,32 @@ This skill transforms academic or technical knowledge vault notes into polished, ## Workflow ### 1. Input Detection + - User selects text from a vault note or provides note reference - Agent asks (if unclear): "What format would you like? **LinkedIn post**, **blog article**, or **technical deep-dive**?" ### 2. Insight Extraction + - Identify **2–3 most shareable, non-obvious insights** from the note - Prioritize: novel findings > practical lessons > conceptual clarity - Avoid generic statements; surface surprising or underexplored angles - Look for personal experience or lessons learned (Gianfranco's voice) ### 3. Structure & Adaptation + - Rewrite content in target tone; remove academic formality - Create engaging opening hook (question, stat, or observation) - Restructure information for readability (short paragraphs, bullets for LinkedIn; narrative flow for blog) - Preserve technical accuracy; simplify jargon without losing precision ### 4. Call-to-Action & Engagement + - LinkedIn: end with question, invitation to debate, or "What's your experience?" - Blog: conclusion with implications, link to related vault notes, or "What's next?" - Article: future directions, open problems, or invitation for feedback ### 5. Output & Polish + - Generate markdown ready to copy-paste - Suggest hashtags for LinkedIn (include in output) - For blog/article: include title, optional subtitle, and metadata notes @@ -67,6 +75,7 @@ This skill transforms academic or technical knowledge vault notes into polished, ## Example Workflow **Input**: Selection from `[[AI Safety/Prompt Injection Vulnerabilities]]` note + ``` [User selects technical explanation of prompt injection attack vectors] ``` @@ -74,11 +83,13 @@ This skill transforms academic or technical knowledge vault notes into polished, **Agent Detects**: Blog-post request (length and depth suggest longer form) **Agent Extracts**: + - Insight 1: Most attacks succeed via **indirect injection** (document upload, search results), not direct user input - Insight 2: Defense requires **multi-layered approach** (parsing, sandboxing, monitoring), not single mitigation - Insight 3: Teams overlook **supply-chain injection** (third-party APIs as attack surface) **Agent Outputs** (Blog post structure): + ```markdown # The Supply Chain Blind Spot in LLM Security @@ -89,6 +100,7 @@ This skill transforms academic or technical knowledge vault notes into polished, ## The Indirect Injection Problem [Explanation of attack vectors beyond direct user input] + - Document uploads - Web search results - Third-party API responses @@ -119,6 +131,7 @@ This skill transforms academic or technical knowledge vault notes into polished, ## Output Format Return a markdown file with: + 1. **Title** and optional **Subtitle** 2. **Metadata** (platform, word count, reading time, suggested hashtags) 3. **Content** (ready to publish) diff --git a/.skills/skills/cross-domain-connector/cross-domain-connector/SKILL.md b/.skills/skills/cross-domain-connector/cross-domain-connector/SKILL.md index 4d39f66..9dea78b 100644 --- a/.skills/skills/cross-domain-connector/cross-domain-connector/SKILL.md +++ b/.skills/skills/cross-domain-connector/cross-domain-connector/SKILL.md @@ -14,6 +14,7 @@ This skill identifies shared patterns, mental models, techniques, and principles ### 1. Define the Scope and Domains Request the user provide: + - A specific note or concept to analyze for cross-domain applications, OR - Two or more domains to compare (e.g., "AI Safety and Cybersecurity") - Desired depth: "surface-level patterns" vs. "deep structural similarities" @@ -28,22 +29,26 @@ If domains are provided, scan broadly within each domain's folder structure. Look for: **Methodological Parallels**: + - Do different domains use similar problem-solving approaches? (e.g., adversarial thinking in both red-teaming and penetration testing) - Do they employ comparable frameworks or taxonomies? (e.g., threat modeling in both cybersecurity and AI safety) - Are there shared data structures, algorithms, or architectural patterns? (e.g., graph-based representations in both knowledge graphs and network security) **Conceptual Overlaps**: + - Do domains use different terminology for the same underlying idea? (e.g., "prompt injection" in LLMs ≈ "code injection" in cybersecurity) - Are there shared risk categories? (e.g., supply chain attacks in both software security and ML) - Do domains address similar trade-offs? (e.g., interpretability vs. performance in ML mirrors transparency vs. security in systems) **Technique Transfer Opportunities**: + - Could a technique from Domain A solve a problem in Domain B? - Example: Adversarial training from ML might strengthen red team assessments in cybersecurity - Example: OWASP threat modeling from cybersecurity could be adapted to AI safety scenarios - Are there tools, metrics, or evaluation frameworks that generalize? (e.g., ROC curves in ML and detection systems in cybersecurity) **Mental Model Abstractions**: + - What abstract problem structure appears in both domains? - Example: Resource allocation under constraints (MLOps > Agentic Systems meets Cybersecurity > Incident Response) - Example: Detecting anomalies (Computer Vision > Segmentation meets Cybersecurity > Intrusion Detection) @@ -54,20 +59,25 @@ Look for: For each cross-domain connection discovered, create an entry with: **Pattern Name**: A clear, memorable name for the shared concept or approach + - Example: "Adversarial Thinking" or "Supply Chain Risk" or "Defense in Depth" **Domain A Expression**: How this pattern manifests in the first domain + - Cite specific vault notes, techniques, or literature - Use format: `[[note-name]]` for internal references **Domain B Expression**: How the same pattern manifests in the second domain + - Provide equal detail; avoid making one domain's version seem more "primary" **Similarity Assessment**: Why are these genuinely parallel? + - List 3-4 structural or functional similarities - Acknowledge any differences that complicate the analogy **Transfer Potential**: Could practitioners move knowledge from one domain to the other? + - What would need to adapt (terminology, tools, constraints)? - What would transfer directly? - Are there documented examples of this transfer already? (Cite them.) @@ -77,12 +87,15 @@ For each cross-domain connection discovered, create an entry with: Move beyond direct analogies. Look for: **Inverse Applications**: Domain A solves problem X; could the solution strategy apply to problem -X (the opposite) in Domain B? + - Example: Data augmentation in ML (creating synthetic training data) has an inverse in cybersecurity (creating deceptive decoy systems) **Complementary Approaches**: Do domains use different strategies for the same underlying goal, and could combining them be fruitful? + - Example: NLP uses tokenization and embeddings for text; Computer Vision uses convolutional filters and pooling for images—both are form-preserving compressions with different structures **Meta-Level Patterns**: What are the domains doing at a higher level of abstraction? + - Example: Both [[LLM-security]] and [[network-security]] are trying to distinguish authorized from unauthorized behavior; the mechanisms differ but the meta-problem is shared ### 5. Create a Mermaid Diagram @@ -125,6 +138,7 @@ graph TB ``` Use: + - **Solid arrows** for direct parallels - **Dashed arrows** for indirect or inverse relationships - **Color-coding** to distinguish domains @@ -135,6 +149,7 @@ Use: For significant cross-domain connections, propose new synthetic notes: **Bridge Note Proposal**: + - **Title**: A name capturing the shared pattern (e.g., "Adversarial Thinking: AI and Cybersecurity") - **Purpose**: Why synthesize this? What reader need does it serve? - **Structure**: @@ -146,6 +161,7 @@ For significant cross-domain connections, propose new synthetic notes: - **Suggested File Location**: Determine appropriate folder (e.g., a new "Cross-Domain" folder or under the more theoretical domain) Example proposal: + ``` **Bridge Note: "Adversarial Thinking in AI Safety and Penetration Testing"** - Location: Machine Learning/AI Safety/ or a new shared folder @@ -158,20 +174,23 @@ Example proposal: Provide specific wikilink suggestions: **Bidirectional Links**: + - In `[[AI Safety]]` notes, add: "The adversarial thinking methodology parallels approaches in [[penetration-testing-mindset]]." - In `[[Penetration Testing]]` notes, add: "Similar threat modeling appears in [[AI-safety-robustness]]." **Hub Notes** (if they exist): + - If a note on a shared pattern already exists (e.g., "Threat Modeling"), suggest adding a "Cross-Domain Applications" section with wikilinks **Hierarchical Linking**: + - If a bridge note is created, link it from both domain-specific hub notes ## Output Format Present the analysis as: -``` +```` ## Cross-Domain Analysis: [Primary Domain/Concept] Across the Vault ### Executive Summary @@ -209,13 +228,14 @@ This analysis identifies [N] significant patterns shared across [domain list]. T ```mermaid [Mermaid visualization] -``` +```` --- ### Proposed Bridge Notes **Note 1: [Title]** + - **Purpose**: Synthesizes approaches to [abstract pattern] across [[Domain A]] and [[Domain B]] - **Structure**: [Brief outline of sections] - **Suggested Location**: [Folder path] @@ -226,9 +246,11 @@ This analysis identifies [N] significant patterns shared across [domain list]. T ### Wikilink Additions **To add to [[existing-note-A]]**: + - Near discussion of [topic]: "See also [[domain-b-parallel]] for similar approaches in [[Domain B]]." **To add to [[existing-note-B]]**: + - Near discussion of [topic]: "The principle of [[cross-domain-concept]] also appears in [[Domain A]] contexts via [[domain-a-note]]." --- @@ -236,6 +258,7 @@ This analysis identifies [N] significant patterns shared across [domain list]. T ## Scanning Strategy **Broad Vault Scan**: + 1. List all major topic folders: Machine Learning, Cybersecurity, Papers, Miscellaneous 2. Identify sub-domains: AI Safety, NLP, Agentic AI; Network Security, OSCP, etc. 3. Sample 3-5 notes from each sub-domain (focusing on foundational or frequently-linked notes) @@ -243,6 +266,7 @@ This analysis identifies [N] significant patterns shared across [domain list]. T 5. Compare patterns across sub-domains **Focused Scan** (if user specifies a note): + 1. Read the specified note thoroughly 2. Extract its core pattern or methodology 3. Search the vault (mentally or via grep) for analogous patterns @@ -264,6 +288,7 @@ When examining each domain, ask: ## Avoiding False Analogies Be critical: + - **Surface similarity ≠ deep parallelism**: Ensure structural similarity, not just terminology. - **Scope mismatch**: A technique that works for small networks may not scale to distributed systems; acknowledge this. - **Context dependency**: A mental model might apply only under specific assumptions. Flag these. @@ -272,4 +297,5 @@ Be critical: --- #### Tags + cross_domain_connector, pattern_recognition, knowledge_transfer, interdisciplinary, vault_synthesis, lateral_thinking diff --git a/.skills/skills/devils-advocate/devils-advocate/SKILL.md b/.skills/skills/devils-advocate/devils-advocate/SKILL.md index ff91c6a..d82f29c 100644 --- a/.skills/skills/devils-advocate/devils-advocate/SKILL.md +++ b/.skills/skills/devils-advocate/devils-advocate/SKILL.md @@ -14,11 +14,13 @@ This skill identifies potential weaknesses, oversimplifications, or blind spots ### 1. Analyze the Core Argument Request the user provide: + - The note name/path or a specific claim/section to critique - The intended context (e.g., "Is this theory universally applicable?" "Are there practical limitations?") - Any particular domains or perspectives to emphasize in the critique Read the note thoroughly. Extract: + - The main thesis or claim - Supporting arguments and evidence cited - Assumptions (stated and unstated) @@ -30,47 +32,57 @@ Read the note thoroughly. Extract: Generate **at least three independent, substantive objections**. Each should attack from a different angle: **Type A: Empirical Challenge** + - Does evidence exist that contradicts the claim? Is the cited evidence incomplete or selective? - Example: "The note claims X is always true, but studies Z and Y show exceptions in domain B." **Type B: Theoretical Limitation** + - Are there unstated assumptions that may not hold? Does the underlying theory have known limitations? - Example: "This argument assumes rationality, but behavioral economics demonstrates that agents often act irrationally." **Type C: Practical Constraint** + - Does the concept work in theory but fail in practice? Are there operational, resource, or environmental barriers? - Example: "While theoretically sound, deploying this approach requires computational resources unavailable in resource-constrained environments." **Type D: Scope Boundary** + - Is the claim overgeneralized? Does it apply across all domains, or only in specific contexts? - Example: "This principle is well-established in supervised learning but may not transfer to reinforcement learning scenarios." **Type E: Alternative Explanation** + - Could the evidence or observations be explained by a different mechanism or theory? - Example: "Rather than causation, the observed correlation might reflect confounding variable Z." **Type F: Value or Priority Conflict** + - Even if the argument is logically sound, does it prioritize one value over another? Would reasonable people disagree on that priority? - Example: "Maximizing accuracy may come at the cost of interpretability; different stakeholders may weigh these trade-offs differently." ### 3. For Each Objection: State, Explain, Evidence, Strengthen **State the objection clearly**: + - Phrase as a direct challenge or alternative view, not a question - Use third-person academic tone - Example: "The note's assumption that all datasets exhibit similar statistical properties is overstated." **Explain the reasoning**: + - Why is this a legitimate concern? What logical or empirical foundation supports it? - Walk through the reasoning step by step - Reference domain knowledge from the vault (e.g., known vulnerabilities, experimental findings, established theory) **Cite relevant counter-evidence or frameworks**: + - If possible, reference papers, vault notes, or concrete examples that support the objection - Use format: `[[vault-note]]` for internal references, `[author, YEAR](URL)` for external sources - If no direct evidence exists, acknowledge this: "While direct evidence is limited, this concern has been raised by [source]." **Suggest how to strengthen the original argument**: + - What would the original note need to address this objection? - Could the scope be narrowed? The assumptions made explicit? The evidence expanded? - Example: "The argument would be stronger if it explicitly acknowledged its domain (supervised learning) and explored whether findings transfer to [[reinforcement-learning]]." @@ -90,6 +102,7 @@ Include the tier assessment in your output so readers understand the severity an ### 5. Identify Blind Spots or Implicit Assumptions Beyond direct objections, flag: + - **Unstated assumptions**: What must be true for the argument to hold? (e.g., "The argument assumes unbounded computational resources.") - **Excluded perspectives**: What viewpoint is conspicuously absent? (e.g., "The note emphasizes efficiency but does not discuss ethical implications.") - **Temporal sensitivity**: Is this argument time-dependent? (e.g., "This was state-of-the-art in 2020, but recent work [note] suggests evolution.") @@ -97,6 +110,7 @@ Beyond direct objections, flag: ### 6. Suggest Related Notes or Disagreements in the Vault If the vault contains: + - Notes that directly contradict the original note's claims - Notes that explore the same question from a different angle - Papers or resources that present alternative viewpoints @@ -187,3 +201,4 @@ For each claim in the note, ask: #### Tags devils_advocate, critical_analysis, objections, counterarguments, intellectual_rigor, peer_review +``` diff --git a/.skills/skills/diagram-generator/diagram-generator/SKILL.md b/.skills/skills/diagram-generator/diagram-generator/SKILL.md index cd343d4..c3e8453 100644 --- a/.skills/skills/diagram-generator/diagram-generator/SKILL.md +++ b/.skills/skills/diagram-generator/diagram-generator/SKILL.md @@ -11,42 +11,47 @@ This skill transforms text-heavy vault notes into clear, scannable Mermaid diagr ## Diagram Types & When to Use -| Diagram Type | Best For | Example Notes | -|---|---|---| -| **Flowchart** (graph LR/TD) | Processes, pipelines, decision flows | MLOps deployment pipeline, attack vector chains, algorithm steps | -| **Sequence Diagram** | Interactions between actors/components over time | Client-server flows, multi-agent communication, API request chains | -| **Class Diagram** | Object relationships, inheritance, data structures | LLM architecture layers, security framework components | -| **State Diagram** | State transitions, finite state machines | Model lifecycle (training → validation → deployment), threat escalation phases | -| **Mindmap** | Concept clustering, hierarchical topics | Machine learning taxonomy, cybersecurity domains, vault structure | -| **Entity Relationship** | Data relationships and cardinality | Database schemas, knowledge graph structure | +| Diagram Type | Best For | Example Notes | +| --------------------------- | -------------------------------------------------- | ------------------------------------------------------------------------------ | +| **Flowchart** (graph LR/TD) | Processes, pipelines, decision flows | MLOps deployment pipeline, attack vector chains, algorithm steps | +| **Sequence Diagram** | Interactions between actors/components over time | Client-server flows, multi-agent communication, API request chains | +| **Class Diagram** | Object relationships, inheritance, data structures | LLM architecture layers, security framework components | +| **State Diagram** | State transitions, finite state machines | Model lifecycle (training → validation → deployment), threat escalation phases | +| **Mindmap** | Concept clustering, hierarchical topics | Machine learning taxonomy, cybersecurity domains, vault structure | +| **Entity Relationship** | Data relationships and cardinality | Database schemas, knowledge graph structure | ## Selection Criteria **Choose Flowchart** if: + - Note describes a process, pipeline, or workflow - Contains decision points or conditional steps - Has sequential or iterative phases - Example: `[[MLOps/Model Deployment]]` → deployment pipeline flowchart **Choose Sequence Diagram** if: + - Note involves multiple actors/systems interacting - Emphasizes temporal order or request-response patterns - Contains negotiation or handoff between components - Example: `[[Agentic AI/Multi-Agent Systems]]` → agent coordination diagram **Choose Mindmap** if: + - Note is conceptual or taxonomic (no procedural flow) - Organizes ideas into hierarchies - Suitable as quick-reference structure - Example: `[[Machine Learning/AI Safety]]` → mindmap of threat categories **Choose Class Diagram** if: + - Note describes architectural or structural relationships - Contains inheritance or composition relationships - Suitable for software/system design notes - Example: `[[MLOps/Agentic Systems]]` → architecture component relationships **Choose State Diagram** if: + - Note focuses on state transitions or lifecycle phases - Contains distinct states and conditions for moving between them - Example: `[[Cybersecurity/OSCP]]` → vulnerability exploitation phases @@ -56,35 +61,41 @@ This skill transforms text-heavy vault notes into clear, scannable Mermaid diagr ## Workflow ### 1. Note Analysis + - Read entire note and identify primary topic - Look for: processes (→ flowchart), interactions (→ sequence), hierarchies (→ mindmap), structures (→ class/entity) - Identify **key concepts, actors, steps, or relationships** - Note any unclear or implicit connections that visualization could clarify ### 2. Scope Definition + - Decide what to visualize (entire note? single section? specific workflow?) - Target **5–12 nodes** maximum for clarity; larger notes may need multiple diagrams - If note is very complex, create **2–3 focused diagrams** rather than one dense one - Define clear entry/exit points or scope boundaries ### 3. Diagram Type Selection + - Apply selection criteria from table above - Consider: What is the note's **primary message**? What visual structure reveals it? - Example: A note on LLM security might need both a **process diagram** (attack chain) and a **mindmap** (threat taxonomy) ### 4. Content Extraction + - List all nodes/concepts to include (keep below 12) - Identify connections: causality, dependency, sequence, hierarchy, composition - Note any labels that clarify relationships (e.g., "requires", "triggers", "inherits from") - Identify decision points or branches in flows ### 5. Mermaid Code Generation + - Write clean, readable Mermaid syntax - Use clear, descriptive labels (3–5 words per node) - Maintain consistent formatting and indentation - Test logic: Can a reader follow the diagram without the original note? ### 6. Integration & Output + - Option A: Embed diagram in original note (with brief caption) - Option B: Create standalone visual note linked from original - Option C: Provide diagram code for copy-paste @@ -99,6 +110,7 @@ This skill transforms text-heavy vault notes into clear, scannable Mermaid diagr **Agent Selects**: Flowchart (pipeline with decision points) **Output** (Mermaid): + ```mermaid graph LR A["Model Training"] --> B["Validation"] @@ -124,6 +136,7 @@ graph LR **Agent Selects**: Mindmap (taxonomic structure) **Output** (Mermaid): + ```mermaid mindmap root((AI Safety)) @@ -156,6 +169,7 @@ mindmap **Agent Selects**: Class Diagram (component relationships) **Output** (Mermaid): + ```mermaid graph TD A["Agent Core"] --> B["Task Scheduler"] @@ -178,6 +192,7 @@ graph TD **Agent Selects**: Sequence Diagram (multi-actor flow) **Output** (Mermaid): + ```mermaid sequenceDiagram participant User @@ -199,16 +214,19 @@ sequenceDiagram ## Mermaid Best Practices **Readability**: + - Keep node labels **3–5 words** max ("LLM Fine-tuning" not "The process of fine-tuning language models") - Use **clear, brief connectors** ("requires", "triggers", "inherits from", "produces") - Avoid **crossing lines**; rearrange nodes for clarity **Structure**: + - Flowchart: Use `graph LR` (left-to-right) for processes, `graph TD` (top-down) for hierarchies - Mindmap: Root node centered; expand radially outward - Sequence: Vertical flow; label each interaction clearly **Styling** (optional but enhances readability): + ```mermaid graph LR A["Start"]:::highlight --> B["Process"] @@ -218,6 +236,7 @@ graph LR ``` **Size Limits**: + - Aim for **5–12 nodes** per diagram - If exceeding 15 nodes, split into multiple focused diagrams - Prefer multiple simple diagrams over one complex one @@ -225,7 +244,9 @@ graph LR ## Output Formats ### Format A: Embed in Existing Note + Add to relevant section (e.g., new `## Visualization` subsection): + ```markdown ## Architecture Visualization @@ -239,7 +260,9 @@ See detailed explanations in [[related note]] for each component. ``` ### Format B: Standalone Visual Note + Create new `#note` or `#quicknote`: + ``` Created: 2026-02-20 14:30 #quicknote @@ -259,15 +282,17 @@ Detailed discussion: [[Original Note Title]] ``` ### Format C: Diagram Code Block + Standalone markdown for copy-paste: -```` + +``` Mermaid Diagram: [Concept Title] Source Note: [[Original Note]] \`\`\`mermaid [diagram code] \`\`\` -```` +``` ## Quality Checklist @@ -293,6 +318,7 @@ Source Note: [[Original Note]] ## Revision Suggestions After generating diagram, offer: + - "Should I simplify this further (fewer nodes)?" - "Would a different diagram type (flowchart vs mindmap) work better?" - "Should I split this into 2 diagrams for clarity?" diff --git a/.skills/skills/elaborate/elaborate/SKILL.md b/.skills/skills/elaborate/elaborate/SKILL.md index 1d51a6a..ff122ea 100644 --- a/.skills/skills/elaborate/elaborate/SKILL.md +++ b/.skills/skills/elaborate/elaborate/SKILL.md @@ -14,12 +14,14 @@ This skill takes a condensed note or selected passage and provides richer exposi ### 1. Understand the Target Content Request the user provide: + - The note name/path or a specific passage to elaborate - The intended audience (e.g., "someone new to the topic" vs. "someone with ML background") - Any specific sections or claims that need deeper treatment - Preferred length (e.g., "add 1-2 paragraphs per point" vs. "comprehensive expansion") Read the source material. Identify: + - Claims that lack sufficient justification or evidence - Terminology that could benefit from clearer definition - Gaps where background context would improve understanding @@ -30,18 +32,22 @@ Read the source material. Identify: For each concept that needs elaboration: **Define precisely**: Provide a clear, academic definition. Distinguish from similar or related terms. + - Example: "**Regularization** differs from feature selection; it penalizes model complexity without removing features." **Provide historical or theoretical context**: Why does this concept exist? What problem does it solve? + - Example: "Regularization emerged to address the overfitting problem identified in early statistical learning theory, particularly as model capacity increased." **Cite foundational ideas**: Reference key papers, researchers, or frameworks when relevant. + - Link to vault notes with `[[relevant-concept]]` - Reference external sources with `[author et al., YEAR](URL)` format ### 3. Add Context About Why It Matters For each expanded concept, explain: + - **Practical significance**: How does this concept apply in real-world scenarios? - **Theoretical importance**: What broader principles or fields does it connect to? - **Current relevance**: Is this concept still important, evolving, or superseded? Why? @@ -53,9 +59,11 @@ Use concrete domains from the vault (ML, AI Safety, cybersecurity, MLOps) as anc Add **one to two concrete examples per concept** when possible: **For technical concepts**: Use pseudocode, mathematical notation, or step-by-step walkthroughs sparingly. Prefer prose explanations. + - Example: Instead of code, write "a model with high regularization strength prioritizes simplicity over training accuracy, reducing variance at the cost of slightly higher bias." **For abstract concepts**: Use analogies grounded in familiar domains. + - Example: "Just as a legal contract uses constraints and penalties to enforce compliance, **loss functions** with regularization terms enforce model simplicity." **For domain concepts**: Reference real applications from the vault's scope (e.g., OSCP scenarios, real recommender systems, actual LLM vulnerabilities). @@ -63,12 +71,14 @@ Add **one to two concrete examples per concept** when possible: ### 5. Suggest Related Concepts to Explore For each elaborated section, list 2-3 related ideas: + - Use format: "See also: `[[related-note-A]]`, `[[related-note-B]]` for deeper context on [topic]." - If related notes don't exist, suggest them as potential new notes: "[A note on X would complement this discussion.]" ### 6. Preserve Original Structure and Voice Maintain the original note's: + - Main argument and conclusions - Existing wikilinks and references - Academic tone (third-person, formal vocabulary, no contractions) @@ -79,6 +89,7 @@ Slot elaborated content into appropriate sections (usually expanding existing pa ### 7. Apply Vault Writing Conventions Ensure the expanded text follows these rules: + - **Bold** key terms on first introduction - Use `[[wikilinks]]` for internal vault references - Use `[title](URL)` for external sources @@ -128,7 +139,7 @@ Present the elaboration as: Ask yourself for each section: 1. Would a reader unfamiliar with this topic understand all the terms used? -2. Does the note explain *why* this concept matters, not just *what* it is? +2. Does the note explain _why_ this concept matters, not just _what_ it is? 3. Are there concrete examples that illustrate the abstract principle? 4. What foundational concepts does this build on? Are those referenced? 5. How does this fit into the broader landscape of the vault's topics? @@ -145,4 +156,5 @@ Ask yourself for each section: --- #### Tags + elaborate, expand_notes, enrich_content, clarity, writing_support, vault_development diff --git a/.skills/skills/feynman-explainer/feynman-explainer/SKILL.md b/.skills/skills/feynman-explainer/feynman-explainer/SKILL.md index af37a9c..1117b6e 100644 --- a/.skills/skills/feynman-explainer/feynman-explainer/SKILL.md +++ b/.skills/skills/feynman-explainer/feynman-explainer/SKILL.md @@ -12,6 +12,7 @@ The Feynman Explainer applies the Feynman Technique to vault notes: breaking com ## Input Accepts: + - A complex vault note or topic (e.g., "Attention Mechanisms", "Privilege Escalation via Kernel Exploits", "Transformer Architecture") - Optional target audience (e.g., "first-year student", "non-technical background", "new OSCP student") - Optional focus area (e.g., "focus on the intuition, not the math") @@ -20,7 +21,7 @@ Accepts: Output is saved to `Study/{topic}/` as a `#quicknote` that acts as a companion to the original note: -```markdown +````markdown 2026-02-20 HH:MM #quicknote @@ -61,6 +62,7 @@ graph TD style A fill:#f9f,stroke:#333 style C fill:#bbf,stroke:#333 ``` +```` ### What's Confusing About This Concept? @@ -73,13 +75,16 @@ This section **explicitly identifies gaps** in understanding by acknowledging co ### Related Vault Notes to Revisit If the simplified explanation reveals gaps, suggest reviewing: + - [[related-vault-note-1]] — covers foundational concept X - [[related-vault-note-2]] — explains variant or application - [[related-vault-note-3]] — discusses edge cases #### Tags + simplified, feynman, intuition, [topic_tag], [subtopic_tag] -``` + +```` ## The Feynman Technique (4 Steps) @@ -175,7 +180,7 @@ graph TD #### Tags simplified, feynman, suid, privilege_escalation, linux, oscp -``` +```` ## Key Writing Rules for Feynman Output @@ -199,6 +204,7 @@ After generating a Feynman explanation: ## Special Rules for OSCP Context When simplifying OSCP concepts: + - Focus on the **practical exploitation chain**, not defensive theory - Use **real tools** (nmap, sqlmap, msfvenom) in examples rather than abstract descriptions - Emphasize **enumeration first** — explain why gathering information comes before exploitation diff --git a/.skills/skills/flashcard-generator/flashcard-generator/SKILL.md b/.skills/skills/flashcard-generator/flashcard-generator/SKILL.md index 5cb3e47..7f244fb 100644 --- a/.skills/skills/flashcard-generator/flashcard-generator/SKILL.md +++ b/.skills/skills/flashcard-generator/flashcard-generator/SKILL.md @@ -12,6 +12,7 @@ The flashcard generator transforms conceptual notes from the vault into high-qua ## Input Accepts: + - A full note or note selection from the vault - Optional difficulty level specification (basic, intermediate, advanced) - Optional topic/subtopic filter (e.g., "create intermediate cards only") @@ -20,10 +21,12 @@ Accepts: ## Output Format Cards are generated as a markdown file saved to: + - `Study/OSCP/` for cybersecurity/OSCP content - `Study/{topic}/` for non-OSCP topics (e.g., `Study/Machine Learning/`, `Study/NLP/`) Format specification: + ```markdown 2026-02-20 14:30 #quicknote @@ -49,12 +52,14 @@ Generated from [[original-note-title]]. **Total Cards Generated**: N #### Tags + flashcards, active_recall, [topic_tag], [subtopic_tag] ``` ## Card Quality Guidelines The agent must create **atomic** cards: + - One concept per card (not compound questions) - Answer is 1-3 sentences (not essays) - No ambiguity in question or answer diff --git a/.skills/skills/interview-prep/interview-prep/SKILL.md b/.skills/skills/interview-prep/interview-prep/SKILL.md index 29042b3..bc0e4b2 100644 --- a/.skills/skills/interview-prep/interview-prep/SKILL.md +++ b/.skills/skills/interview-prep/interview-prep/SKILL.md @@ -12,6 +12,7 @@ The interview prep generator transforms vault knowledge into realistic technical ## Input Accepts: + - A vault topic or concept (e.g., "Transformer Architecture", "SQL Injection", "RAG Pipelines", "Privilege Escalation") - Optional job role context (e.g., "ML Engineer", "Security Engineer", "MLOps Engineer") - Optional company type context (e.g., "startup", "big tech", "fintech") @@ -76,11 +77,13 @@ These questions test deeper understanding and application. A good answer shows t [Explain X from [[vault-note-x]]. Explain Y from [[vault-note-y]]. Highlight key trade-offs: X is better for use case A because of property P, while Y is better for use case B because of property Q. End with a real-world scenario showing when each applies.] **What to Look For**: + - Does the candidate understand both concepts? - Can they articulate trade-offs (not just list features)? - Do they think about context and practical application? **Follow-Up Questions**: + - "What would change your decision if the system had constraint Z?" - "Can you give me an example from your experience where you chose X?" @@ -100,6 +103,7 @@ These questions test deeper understanding and application. A good answer shows t [Explain which you would choose and why, based on the specific context provided in the question.] **What to Look For**: + - Systematic thinking (problem definition → options → evaluation) - Understanding of trade-offs - Ability to justify decisions with reasoning @@ -125,15 +129,17 @@ These questions assess mastery and problem-solving under uncertainty. Good answe [Identify the failure mode. Explain what normally happens (from [[vault-note]]). Explain what changes when this edge case occurs. Describe mitigation strategies with trade-offs. Discuss monitoring and detection.] -**Example**: "Your SQL injection defense relies on parameterized queries. But what if someone injects a malicious query *inside* the parameterized value itself? How would you detect and prevent second-order SQL injection?" (See [[SQL Injection]] for context.) +**Example**: "Your SQL injection defense relies on parameterized queries. But what if someone injects a malicious query _inside_ the parameterized value itself? How would you detect and prevent second-order SQL injection?" (See [[SQL Injection]] for context.) **What to Look For**: + - Deep understanding of how systems fail - Ability to think several steps ahead - Knowledge of defense-in-depth principles - Mention of monitoring and observability **Follow-Up Questions**: + - "How would you test for this vulnerability?" - "What is the highest-impact variant of this edge case you can think of?" @@ -150,6 +156,7 @@ These questions assess mastery and problem-solving under uncertainty. Good answe **Example**: "Design a real-time privilege escalation detection system that monitors system logs and flags suspicious activity. What events would you monitor? How would you distinguish legitimate system behavior from exploitation attempts? How would you minimize false positives?" **What to Look For**: + - Ability to translate requirements into technical design - Systematic thinking about scalability and reliability - Discussion of trade-offs (latency vs. accuracy, cost vs. performance) @@ -168,6 +175,7 @@ These questions assess mastery and problem-solving under uncertainty. Good answe **Example**: "Your WAF is blocking legitimate traffic. You suspect a false positive, but you need to verify. How would you diagnose whether the issue is a WAF rule, an encoding problem, or something else?" (See [[WAF Evasion]] and [[Web Application Vulnerabilities]].) **What to Look For**: + - Methodical debugging approach (not random guessing) - Technical depth (knows what tools and logs to check) - Understanding of the underlying system @@ -178,18 +186,21 @@ These questions assess mastery and problem-solving under uncertainty. Good answe ## Interview Context Guidelines ### For ML Engineer Interviews + - Emphasize **algorithmic understanding** in basic level (gradient descent, loss functions, activation functions) - Focus intermediate level on **trade-offs in model design** (architecture choices, hyperparameters, regularization) - Emphasize advanced questions on **production challenges** (inference latency, monitoring, drift detection, retraining) - Reference [[Machine Learning]] vault notes ### For Security Engineer Interviews + - Emphasize **threat modeling and vulnerability identification** in basic level - Focus intermediate level on **defensive choices and tools** (WAF rules, SIEM tuning, incident response procedures) - Emphasize advanced questions on **attack chains and real-world scenarios** (multi-stage exploitation, supply chain attacks, zero-day response) - Reference [[AI Safety]], [[Cybersecurity]] vault notes ### For MLOps Engineer Interviews + - Emphasize **pipeline architecture and deployment** in basic level - Focus intermediate level on **model serving, monitoring, and rollback** strategies - Emphasize advanced questions on **scaling, failure recovery, and operational burden** (auto-scaling, canary deployments, cost optimization) @@ -248,6 +259,7 @@ This is a classic server-side validation bypass, discussed in [[OWASP Top 10 for - Threat model before coding (reference [[Threat Modeling]]) **What to Look For**: + - Does the candidate understand why client-side validation failed? - Do they think systematically about prevention (not just fixing this one bug)? - Do they mention monitoring and alerting (production visibility)? @@ -262,6 +274,7 @@ This is a classic server-side validation bypass, discussed in [[OWASP Top 10 for A template `Interview-Questions-Template.md` should be available in the `Templates/` directory for users to adapt for new topics. #### Tags + interview, technical_interview, practice, [topic_tag], model_answers ``` @@ -274,11 +287,13 @@ interview, technical_interview, practice, [topic_tag], model_answers ## Special Considerations ### For OSCP-Focused Interviews + - Emphasize **practical exploitation knowledge** in basic questions (tool usage, vulnerability mechanics) - Focus intermediate questions on **real OSCP exam scenarios** (What do you do when Plan A fails? How do you manage time pressure?) - Advanced questions should simulate **exam conditions** (limited time, multiple targets, no Internet access) ### For Research or Academic Interviews + - Emphasize **paper comprehension and novelty** in basic questions - Focus intermediate questions on **research methodology and implications** (Why is this approach novel? What are limitations?) - Advanced questions should explore **open research problems** and **future directions** (What would you investigate next? How would you extend this work?) diff --git a/.skills/skills/knowledge-gap-finder/knowledge-gap-finder/SKILL.md b/.skills/skills/knowledge-gap-finder/knowledge-gap-finder/SKILL.md index d6ac3f3..e8f5f5c 100644 --- a/.skills/skills/knowledge-gap-finder/knowledge-gap-finder/SKILL.md +++ b/.skills/skills/knowledge-gap-finder/knowledge-gap-finder/SKILL.md @@ -97,6 +97,7 @@ Prioritization combines: ## Inbox Awareness The skill should also check `Inbox/` for unprocessed items and include this in gap analysis: + - **Report unprocessed Inbox items**: Count items and flag any sitting longer than 1 week (check `Created:` timestamp) - **Identify staging patterns**: Note if certain topics (e.g., paper links, web clips) accumulate in Inbox without being processed - **Suggest batch processing**: If multiple papers or research items are present, recommend running **paper-ingest** or **clip-to-vault** batch jobs diff --git a/.skills/skills/literature-map/literature-map/SKILL.md b/.skills/skills/literature-map/literature-map/SKILL.md index 43c48f3..1374879 100644 --- a/.skills/skills/literature-map/literature-map/SKILL.md +++ b/.skills/skills/literature-map/literature-map/SKILL.md @@ -14,6 +14,7 @@ The **literature-map** skill synthesizes the vault's existing research notes int ### Input The agent accepts a **topic query**, such as: + - "prompt injection defences" - "RAG evaluation" - "transformer architecture" @@ -49,6 +50,7 @@ The agent accepts a **topic query**, such as: - **Cluster by methodology** if applicable Example: + ```mermaid graph LR A["Prompt Injection
(2023)"] -->|cites| B["Semantic Attacks
(2024)"] @@ -148,4 +150,3 @@ literature_review, prompt_injection, ai_safety, research_landscape - Gaps should be honest and constructive, not a criticism of existing work - For large fields (>20 papers), consider breaking into subtopic maps (e.g., "Detection-focused" vs. "Prevention-focused") - Update this note periodically as new papers are added to the vault - diff --git a/.skills/skills/obsidian-notes/obsidian-notes/SKILL.md b/.skills/skills/obsidian-notes/obsidian-notes/SKILL.md index 3d5035c..8061c0c 100644 --- a/.skills/skills/obsidian-notes/obsidian-notes/SKILL.md +++ b/.skills/skills/obsidian-notes/obsidian-notes/SKILL.md @@ -54,11 +54,11 @@ When placing a new note, pick the most specific existing folder. Key placement g The vault uses three note types, each with a distinct purpose and template: -| Type | Tag | When to use | -|------|-----|-------------| -| **Note** | `#note` | Full research notes, in-depth topic explorations | -| **Quick Note** | `#quicknote` | Brief summaries, short concept explanations | -| **Paper** | `#paper` | Academic paper summaries | +| Type | Tag | When to use | +| -------------- | ------------ | ------------------------------------------------ | +| **Note** | `#note` | Full research notes, in-depth topic explorations | +| **Quick Note** | `#quicknote` | Brief summaries, short concept explanations | +| **Paper** | `#paper` | Academic paper summaries | Choose `#note` for topics that deserve thorough coverage (500+ words). Choose `#quicknote` for concepts that can be captured in a paragraph or two with a few bullet points. @@ -74,6 +74,7 @@ Created: YYYY-MM-DD HH:MM Line 1 is the creation timestamp in ISO format with 24-hour time. Line 2 is the note type tag. No YAML `---` fences, no other metadata fields. This is intentional — keep it minimal. **Do NOT use:** + - YAML frontmatter blocks (`---`) - Additional metadata fields (author, status, aliases) - Multiple tags on line 2 @@ -185,6 +186,7 @@ Use `[[wikilinks]]` for every reference to another concept in the vault: - `[[Secure SDLC]]` — cross-domain links When introducing a link, provide context: + - "This is one of the [[Vulnerabilities in LLM-base applications]]." - "For more details on the types of attacks, check: [[Prompt Injection types]]." - "Security is integrated at each phase of the [[Secure SDLC]]." @@ -192,12 +194,14 @@ When introducing a link, provide context: ### External Links Use standard Markdown links with descriptive text: + - Inline: `[OWASP](https://owasp.org/...)` - Numbered references: `[1](https://arxiv.org/pdf/...)` ### Linking Strategy The vault follows a hub-and-spoke model: + - **Hub notes** (`#note`) are comprehensive topic overviews that smaller notes link to - **Spoke notes** (`#quicknote`) link UP to their parent concepts - When creating a new note, always check for existing related notes and add `[[wikilinks]]` in both directions @@ -217,6 +221,7 @@ Every note ends with a Tags section: ``` Tag conventions: + - All lowercase - Multi-word tags use underscores: `#threat_modeling`, `#static_code_analysis` - 2-6 tags per note diff --git a/.skills/skills/oscp-scenarios/oscp-scenarios/SKILL.md b/.skills/skills/oscp-scenarios/oscp-scenarios/SKILL.md index 9aa352c..a542fad 100644 --- a/.skills/skills/oscp-scenarios/oscp-scenarios/SKILL.md +++ b/.skills/skills/oscp-scenarios/oscp-scenarios/SKILL.md @@ -12,6 +12,7 @@ The OSCP scenario generator transforms security concepts from the vault (privile ## Input Accepts: + - A security/exploitation concept from the vault (e.g., "privilege escalation via SUID binaries", "SQL injection to RCE", "buffer overflow exploitation") - Optional difficulty level (easy, medium, hard) - Optional constraints (e.g., "network filtering enabled", "WAF active", "assume limited system info") @@ -97,12 +98,14 @@ Once inside the web application context: **Scenario Created**: 2026-02-20 | **Difficulty**: Medium | **Time**: 45 min | **Status**: Ready for practice #### Tags + oscp, scenario, sql_injection, privilege_escalation, lab, practice, windows ``` ## Scenario Structure Components ### 1. Scenario Details (Required) + - **Difficulty**: Easy, Medium, or Hard - **Estimated Time**: 30–120 minutes - **Technique**: Primary exploitation technique @@ -112,25 +115,31 @@ oscp, scenario, sql_injection, privilege_escalation, lab, practice, windows - **Success Criteria**: What constitutes completion (flag, RCE, creds, persistence) ### 2. Reconnaissance & Enumeration (Required) + Lists actionable enumeration commands specific to the target OS and services. This section mirrors real OSCP exam approach: gather information systematically before attempting exploitation. **Quality standard**: Commands should be: + - Executable with standard tools - OS-specific and realistic - Ordered logically (passive → active) - Include expected output hints ### 3. Exploitation Approach (Required) + Explains the attack chain without giving away exact payloads: + - Vulnerability hypothesis - Step-by-step exploitation logic - Tools needed - Fallback methods if primary approach fails -**Quality standard**: Learner should understand *why* each step happens, not just *how* to copy-paste commands. +**Quality standard**: Learner should understand _why_ each step happens, not just _how_ to copy-paste commands. ### 4. Post-Exploitation & Escalation + Objectives after initial access: + - Persistence establishment - Privilege escalation path - Lateral movement prep @@ -138,7 +147,9 @@ Objectives after initial access: - Success verification ### 5. Hints (Optional) + Non-spoiler hints if learner is stuck: + - "The service is vulnerable to X" - "Check for Y file type in Z directory" - "The encoding/obfuscation technique is Z" @@ -146,18 +157,21 @@ Non-spoiler hints if learner is stuck: ## Difficulty Guidance **Easy (30-40 min)** + - One clear vulnerability path - Exploit available in Metasploit or public PoC - Minimal enumeration steps - No privilege escalation needed **Medium (45-90 min)** + - Two vulnerability chains (initial access + privilege escalation) - Requires custom payload development or exploitation - Moderate enumeration - Post-exploitation complexity **Hard (90-120 min)** + - Multiple exploitation paths, requires choosing optimal one - Kernel vulnerability or complex logic bug - Extensive enumeration and pattern recognition @@ -181,6 +195,7 @@ Non-spoiler hints if learner is stuck: ## Post-Generation After creating a scenario, the agent should recommend: + - Which vault notes to review for theory - Whether to attempt easy/medium/hard version first - Related scenarios that build on the same techniques diff --git a/.skills/skills/paper-ingest/paper-ingest/SKILL.md b/.skills/skills/paper-ingest/paper-ingest/SKILL.md index 29cb19e..2dce5ff 100644 --- a/.skills/skills/paper-ingest/paper-ingest/SKILL.md +++ b/.skills/skills/paper-ingest/paper-ingest/SKILL.md @@ -14,6 +14,7 @@ The **paper-ingest** skill automates the process of adding academic papers to th ### Input The agent accepts: + - **PDF file** uploaded directly or from local system - **arXiv URL** (e.g., `https://arxiv.org/abs/2312.xxxxx`) - **Paper URL** (conference sites like OpenReview, ACL Anthology, IEEE Xplore, etc.) @@ -62,6 +63,7 @@ The agent accepts: ### Output A complete `#paper` note ready to file into the vault with: + - All metadata and sections filled - Internal vault links established - Suggested folder path @@ -112,6 +114,7 @@ prompt_injection, llm_security, ai_safety, detection ## Inbox Processing The skill can scan `Inbox/` for paper-related items (titles, links, arXiv URLs) and batch-process them into proper `#paper` notes. When the user requests "process inbox papers" or "ingest papers from inbox": + - Identify paper citations, arXiv links, or paper titles sitting in Inbox items - Offer to convert each into a full `#paper` note following the standard workflow - Report which papers were successfully ingested and suggest folder placements @@ -123,4 +126,3 @@ The skill can scan `Inbox/` for paper-related items (titles, links, arXiv URLs) - Search the vault thoroughly before assuming a related concept doesn't exist - If a paper contradicts or extends an existing vault note, explicitly mention this in the opening paragraph or Ideas section - For highly technical papers, the **In deep** section can be substantial (500+ words); for surveys, focus on **Main idea** and **Results** - diff --git a/.skills/skills/search-online/search-online/SKILL.md b/.skills/skills/search-online/search-online/SKILL.md index f57d3a6..31ba97c 100644 --- a/.skills/skills/search-online/search-online/SKILL.md +++ b/.skills/skills/search-online/search-online/SKILL.md @@ -14,6 +14,7 @@ The **search-online** skill researches a topic across the web and synthesizes fi ### Input The agent accepts a **topic query**, such as: + - "latest advances in retrieval-augmented generation" - "OSCP exam prerequisites and study strategies" - "LLM evaluation benchmarks 2025" @@ -159,6 +160,7 @@ rag, retrieval_augmented_generation, llm_systems, information_retrieval ## Inbox Capture Option Research results can optionally be saved to `Inbox/` as quick captures when the user prefers a fast save over full synthesis. When requested (e.g., "save search results to inbox"), create a minimal entry with: + - Topic query, search date, list of key sources with URLs - 1–2 sentence summary of main findings - This allows rapid capture of research; items can later be promoted to full vault notes via **clip-to-vault** or **paper-ingest** @@ -170,4 +172,3 @@ Research results can optionally be saved to `Inbox/` as quick captures when the - Avoid SEO traps: Skip listicles, outdated rankings, and unverified claims - Link intentionally: Every wikilink should make sense in context - Add disclaimer if information is rapidly evolving (e.g., LLM benchmarks, security landscapes) - diff --git a/.skills/skills/summarize-note/summarize-note/SKILL.md b/.skills/skills/summarize-note/summarize-note/SKILL.md index 9207903..c1b6afd 100644 --- a/.skills/skills/summarize-note/summarize-note/SKILL.md +++ b/.skills/skills/summarize-note/summarize-note/SKILL.md @@ -23,18 +23,21 @@ This skill condenses verbose or detailed vault notes into **schematic, precise, The agent should infer or ask which mode fits the user's intent: ### Mode A: Replace Note Content + - Rewrite the entire note as schematic bullet points - Keeps original metadata (frontmatter, tags, references) - Best for: Notes that are verbose or poorly structured - Result: Leaner, more scannable version of same note ### Mode B: TL;DR Prepend + - Add a `## TL;DR` section at the top of the existing note - Provides quick reference; original content remains below - Best for: Important notes that readers should digest quickly - Result: Fast scanning + deeper reading option ### Mode C: Standalone #quicknote + - Create a new `#quicknote` file linked to the original - Cross-reference both notes - Best for: Creating a summary note to link in hub-and-spoke structure @@ -43,16 +46,19 @@ The agent should infer or ask which mode fits the user's intent: ## Workflow ### 1. Input Reception + - User selects text from a note or names a note to summarize - Agent asks (if unclear): "How would you like the summary? **Replace content**, **add TL;DR section**, or **create new quicknote**?" ### 2. Content Analysis + - Identify the note's core topic and intended audience - Scan for repeated or overlapping ideas - Extract 2–5 primary concepts; note relationships - Flag any examples, case studies, or tangential sections for removal ### 3. Extraction & Structuring + - Create bullet-point outline of essential ideas only - Use **bold key terms** to start each bullet (enables visual scanning) - Arrange bullets in logical order (conceptual hierarchy, process flow, or importance) @@ -60,11 +66,13 @@ The agent should infer or ask which mode fits the user's intent: - Eliminate: passive voice, hedging language, unnecessary qualifiers ### 4. Link Preservation & Addition + - Copy all original `[[wikilinks]]` into corresponding summary bullets - Identify new conceptual connections; suggest additional cross-references - Example: If summarizing LLM prompt injection note, suggest links to `[[AI Safety/Defense Mechanisms]]` and `[[MLOps/LLM Observability]]` ### 5. Validation + - Read summary in isolation: Does it make sense? - Count redundancies: Are all bullets independent? - Measure compression: Is it 20–50% of original? @@ -73,6 +81,7 @@ The agent should infer or ask which mode fits the user's intent: ## Example Transformation **Original Note Excerpt** (from `[[Machine Learning/NLP & LLMs/Fine-tuning]]`): + ``` Fine-tuning is a technique where a pre-trained language model is adapted to a specific task or domain by training it further on task-specific data. This process involves several steps, @@ -85,6 +94,7 @@ dataset achieves good results faster than training from scratch. ``` **Schematic Summary** (Mode A output): + ``` ## Fine-Tuning @@ -100,6 +110,7 @@ dataset achieves good results faster than training from scratch. ## Schematic Format Guidelines **Bullet Structure**: + ``` - **Key Term**: Concise definition or main idea - **Relationship**: How this concept connects to others @@ -107,6 +118,7 @@ dataset achieves good results faster than training from scratch. ``` **Nesting** (for complex topics): + ``` - **Main concept** - **Sub-concept A**: Details @@ -115,12 +127,14 @@ dataset achieves good results faster than training from scratch. ``` **Avoid**: + - "It is important to note that..." (filler) - Repeating the same idea twice (remove redundancy) - Examples unless they're essential for understanding - Disclaimer language ("It could be argued that...") **Use**: + - Active voice: "Model learns patterns" vs "Patterns are learned" - Parallel structure: Bullets at same level follow same grammar pattern - Specificity: "Gradient clipping prevents exploding gradients" vs "Prevents problems" @@ -128,12 +142,14 @@ dataset achieves good results faster than training from scratch. ## Link Handling **Preserve Original Links**: + ``` Original: "This is related to [[AI Safety/Prompt Injection Vulnerabilities]]" Summary: "- **Vulnerability surface**: [[AI Safety/Prompt Injection Vulnerabilities]]" ``` **Add New Links** (if improved clarity): + ``` Before summary: Disconnected concept After summary: "- **Mitigation approach**: [[MLOps/LLM Observability]] + [[AI Safety/Defense Mechanisms]]" @@ -144,6 +160,7 @@ After summary: "- **Mitigation approach**: [[MLOps/LLM Observability]] + [[AI Sa ## Metadata & Output **For Mode A (Replace)**: + ``` --- Created: 2026-02-20 14:30 @@ -157,6 +174,7 @@ Created: 2026-02-20 14:30 ``` **For Mode B (TL;DR Prepend)**: + ``` --- Created: [original date] @@ -173,6 +191,7 @@ Created: [original date] ``` **For Mode C (New Quicknote)**: + ``` --- Created: 2026-02-20 14:30 @@ -202,6 +221,7 @@ See full analysis: [[Original Note Title]] ## Revision Prompts After generating summary, offer: + - "Would you like this even more concise?" - "Should I restructure this hierarchically?" - "Should I add more cross-links to related notes?" diff --git a/README.md b/README.md index 70cf844..e8bbb2f 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,12 @@ I hold a Master's degree in Engineering in Computer Science from Sapienza Univer When I'm not working, you'll find me swimming, nerding out over specialty coffee, building mechanical keyboards, or cooking. Technical Notes & Links: -- [Technical notes](https://gianfree-notes.netlify.app/): ML, AI security, and cybersecurity -- [Medium](https://medium.com/@gianfrancoromani) -- [GitHub](https://github.com/GianRomani) + +- [Technical notes](https://gianfree-notes.netlify.app/): ML, AI security, and cybersecurity +- [Medium](https://medium.com/@gianfrancoromani) +- [GitHub](https://github.com/GianRomani) Get in touch: -- [gianfranco.romani@outlook.it](mailto:gianfranco.romani@outlook.it) -- [LinkedIn](https://www.linkedin.com/in/gian-romani/) + +- [gianfranco.romani@outlook.it](mailto:gianfranco.romani@outlook.it) +- [LinkedIn](https://www.linkedin.com/in/gian-romani/) diff --git a/custom-site-assets/convert.py b/custom-site-assets/convert.py index 468e995..bf23192 100644 --- a/custom-site-assets/convert.py +++ b/custom-site-assets/convert.py @@ -13,7 +13,6 @@ ) if __name__ == "__main__": - Settings.parse_env() Settings.sub_file(site_dir / "config.toml") Settings.sub_file(site_dir / "content/_index.md") diff --git a/custom-site-assets/sass/custom.scss b/custom-site-assets/sass/custom.scss index 3e31f8e..6c46389 100644 --- a/custom-site-assets/sass/custom.scss +++ b/custom-site-assets/sass/custom.scss @@ -19,16 +19,16 @@ --bg-code: #f3ede2; --border-color: #e6dfd1; --border-glow: #dcd3be; - + --text-main: #2b2c2e; --text-muted: #6b6e72; --text-headings: #1c1d1e; - + /* Mediterranean Sicily Accents (Muted Turquoise & Blue) */ --accent-teal: #4c8a89; --accent-glow: #63afae; --accent-soft: rgba(99, 175, 174, 0.12); - + /* Code Syntax Highlighting (Japandi Warm Earth & Forest) */ --syntax-comment: #8a857b; --syntax-keyword: #b85c37; @@ -37,7 +37,7 @@ --syntax-function: #2d6a68; --syntax-variable: #4e5154; --syntax-builtin: #a3486c; - + /* Font Stack mappings */ --font-headings: 'Fraunces', Georgia, serif; --font-body: 'Alegreya', Georgia, serif; @@ -58,16 +58,16 @@ body.dark { --bg-code: #27282b; --border-color: #2e3033; --border-glow: #383a3f; - + --text-main: #e6dfd3; --text-muted: #96938d; --text-headings: #f5f2eb; - + /* Mediterranean Sicily Accents (Glowing Cyan-Teal) */ --accent-teal: #63afae; --accent-glow: #99d8d9; --accent-soft: rgba(99, 175, 174, 0.18); - + /* Code Syntax Highlighting (Glowing Terracotta & Cyan-Teal) */ --syntax-comment: #76797c; --syntax-keyword: #f4a261; @@ -487,7 +487,7 @@ body.dark .navbar-brand { border-radius: 50% !important; object-fit: cover !important; border: 1.5px solid var(--accent-teal) !important; - transition: transform 0.8s cubic-bezier(0.16, 1, 0.3, 1), + transition: transform 0.8s cubic-bezier(0.16, 1, 0.3, 1), border-radius 0.8s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.8s cubic-bezier(0.16, 1, 0.3, 1) !important; } @@ -674,7 +674,7 @@ body.dark .tag-coffee { flex-grow: 1 !important; height: 100% !important; } - + .navbar-form { flex: 0 1 450px !important; margin: 0 auto !important; @@ -1146,7 +1146,7 @@ body.dark .docs-links a.docs-link.active { padding: 0.4rem 0 !important; display: flex; flex-direction: column !important; - + // Webkit Custom Scrollbar inside suggestions box &::-webkit-scrollbar { width: 6px !important; @@ -1185,15 +1185,15 @@ body.dark .docs-links a.docs-link.active { border-bottom: 1px solid var(--border-color) !important; margin: 0 !important; padding: 0 !important; - + &:last-child { border-bottom: none !important; } - + &:first-child { margin-top: 0 !important; } - + &:last-child { margin-bottom: 0 !important; } @@ -1210,7 +1210,7 @@ body.dark .docs-links a.docs-link.active { transition: background-color 0.2s ease, border-left-color 0.2s ease !important; border-left: 3px solid transparent !important; background: transparent !important; - + &:hover, &:focus { background-color: var(--bg-code) !important; border-left-color: var(--accent-teal) !important; @@ -1247,7 +1247,7 @@ body.dark .docs-links a.docs-link.active { padding: 0 !important; width: 100% !important; margin-top: 0.1rem !important; - + // Custom highlights in teaser matching user's query .search-highlight { background-color: var(--accent-soft) !important; @@ -1265,7 +1265,7 @@ body.dark .docs-links a.docs-link.active { font-family: var(--font-ui) !important; color: var(--text-muted) !important; font-size: 0.9rem !important; - + span { color: var(--accent-teal) !important; font-weight: 600 !important; diff --git a/custom-site-assets/static/js/custom.js b/custom-site-assets/static/js/custom.js index c776816..0b680c4 100644 --- a/custom-site-assets/static/js/custom.js +++ b/custom-site-assets/static/js/custom.js @@ -1,16 +1,16 @@ /** * Custom micro-interactions and dynamic enhancement for Gianfranco's Second Brain */ -document.addEventListener('DOMContentLoaded', () => { +document.addEventListener("DOMContentLoaded", () => { // Create mouse-glow element - const glow = document.createElement('div'); - glow.id = 'glow-pointer'; + const glow = document.createElement("div"); + glow.id = "glow-pointer"; document.body.appendChild(glow); // Follow cursor - document.addEventListener('mousemove', (e) => { - glow.style.left = e.clientX + 'px'; - glow.style.top = e.clientY + 'px'; + document.addEventListener("mousemove", (e) => { + glow.style.left = e.clientX + "px"; + glow.style.top = e.clientY + "px"; }); // Dynamically style inline hashtags @@ -28,20 +28,22 @@ document.addEventListener('DOMContentLoaded', () => { // Helper to escape HTML characters function escapeHTML(str) { - return str.replace(/[&<>'"]/g, - tag => ({ - '&': '&', - '<': '<', - '>': '>', - "'": ''', - '"': '"' - }[tag] || tag) + return str.replace( + /[&<>'"]/g, + (tag) => + ({ + "&": "&", + "<": "<", + ">": ">", + "'": "'", + '"': """, + })[tag] || tag, ); } // Function to dynamically wrap hashtags in note content function formatHashtags() { - const content = document.querySelector('.docs-content'); + const content = document.querySelector(".docs-content"); if (!content) return; const hashtagRegex = /(^|\s)(#[a-zA-Z0-9_\-\/]+)/g; @@ -49,34 +51,61 @@ function formatHashtags() { // Helper to recursively traverse text nodes function traverse(node) { // Skip specific elements to avoid breaking them - const skipTags = ['A', 'CODE', 'PRE', 'SCRIPT', 'STYLE', 'BUTTON', 'INPUT', 'TEXTAREA', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6']; + const skipTags = [ + "A", + "CODE", + "PRE", + "SCRIPT", + "STYLE", + "BUTTON", + "INPUT", + "TEXTAREA", + "H1", + "H2", + "H3", + "H4", + "H5", + "H6", + ]; if (node.nodeType === Node.ELEMENT_NODE) { if (skipTags.includes(node.tagName)) return; } if (node.nodeType === Node.TEXT_NODE) { const text = node.nodeValue; - if (text.includes('#')) { + if (text.includes("#")) { // Reset regex index hashtagRegex.lastIndex = 0; if (hashtagRegex.test(text)) { hashtagRegex.lastIndex = 0; - - const tempSpan = document.createElement('span'); + + const tempSpan = document.createElement("span"); const escapedText = escapeHTML(text); - tempSpan.innerHTML = escapedText.replace(hashtagRegex, (match, space, hashtag) => { - let categoryClass = ''; - const tagLower = hashtag.toLowerCase(); - if (tagLower.includes('paper')) categoryClass = ' tag-paper'; - else if (tagLower.includes('study') || tagLower.includes('guide')) categoryClass = ' tag-study'; - else if (tagLower.includes('security') || tagLower.includes('oscp') || tagLower.includes('cyber')) categoryClass = ' tag-security'; - else if (tagLower.includes('ml') || tagLower.includes('agentic')) categoryClass = ' tag-ml'; - else if (tagLower.includes('ops') || tagLower.includes('mcp')) categoryClass = ' tag-mlops'; - else if (tagLower.includes('coffee')) categoryClass = ' tag-coffee'; - - return `${space}${hashtag}`; - }); - + tempSpan.innerHTML = escapedText.replace( + hashtagRegex, + (match, space, hashtag) => { + let categoryClass = ""; + const tagLower = hashtag.toLowerCase(); + if (tagLower.includes("paper")) categoryClass = " tag-paper"; + else if (tagLower.includes("study") || tagLower.includes("guide")) + categoryClass = " tag-study"; + else if ( + tagLower.includes("security") || + tagLower.includes("oscp") || + tagLower.includes("cyber") + ) + categoryClass = " tag-security"; + else if (tagLower.includes("ml") || tagLower.includes("agentic")) + categoryClass = " tag-ml"; + else if (tagLower.includes("ops") || tagLower.includes("mcp")) + categoryClass = " tag-mlops"; + else if (tagLower.includes("coffee")) + categoryClass = " tag-coffee"; + + return `${space}${hashtag}`; + }, + ); + const parent = node.parentNode; if (parent) { while (tempSpan.firstChild) { @@ -101,34 +130,40 @@ function formatHashtags() { // Persist sidebar scroll position across page reloads function initSidebarScrollPreserve() { - const sidebar = document.querySelector('.docs-sidebar'); + const sidebar = document.querySelector(".docs-sidebar"); if (sidebar) { // Restore scroll position - const savedScrollTop = sessionStorage.getItem('sidebar-scroll'); + const savedScrollTop = sessionStorage.getItem("sidebar-scroll"); if (savedScrollTop) { sidebar.scrollTop = parseInt(savedScrollTop, 10); } // Save scroll position on scroll - sidebar.addEventListener('scroll', () => { - sessionStorage.setItem('sidebar-scroll', sidebar.scrollTop); - }, { passive: true }); + sidebar.addEventListener( + "scroll", + () => { + sessionStorage.setItem("sidebar-scroll", sidebar.scrollTop); + }, + { passive: true }, + ); } } // Table of Contents ScrollSpy using IntersectionObserver function initTocScrollSpy() { - const tocLinks = document.querySelectorAll('#TableOfContents a'); - const headings = Array.from(document.querySelectorAll('.docs-content h2, .docs-content h3')); + const tocLinks = document.querySelectorAll("#TableOfContents a"); + const headings = Array.from( + document.querySelectorAll(".docs-content h2, .docs-content h3"), + ); if (tocLinks.length > 0 && headings.length > 0) { - const activeClass = 'active'; + const activeClass = "active"; let activeHeading = null; const observerOptions = { root: null, - rootMargin: '-10% 0px -75% 0px', // Trigger when heading is in upper section of viewport - threshold: 0 + rootMargin: "-10% 0px -75% 0px", // Trigger when heading is in upper section of viewport + threshold: 0, }; const observer = new IntersectionObserver((entries) => { @@ -144,15 +179,15 @@ function initTocScrollSpy() { function updateActiveTocLink() { if (!activeHeading) return; - const id = activeHeading.getAttribute('id'); + const id = activeHeading.getAttribute("id"); if (!id) return; - const container = document.querySelector('.docs-toc'); + const container = document.querySelector(".docs-toc"); let activeLink = null; tocLinks.forEach((link) => { - const href = link.getAttribute('href'); - if (href && href.endsWith('#' + id)) { + const href = link.getAttribute("href"); + if (href && href.endsWith("#" + id)) { link.classList.add(activeClass); activeLink = link; } else { @@ -163,15 +198,17 @@ function initTocScrollSpy() { if (container && activeLink) { const containerRect = container.getBoundingClientRect(); const linkRect = activeLink.getBoundingClientRect(); - + // Calculate the relative top of the link inside the container - const relativeTop = linkRect.top - containerRect.top + container.scrollTop; + const relativeTop = + linkRect.top - containerRect.top + container.scrollTop; const linkHeight = linkRect.height; const containerHeight = containerRect.height; - + // Target scroll to center the active link in the viewport - let targetScrollTop = relativeTop - (containerHeight / 2) + (linkHeight / 2); - + let targetScrollTop = + relativeTop - containerHeight / 2 + linkHeight / 2; + // Clamp scroll boundaries const maxScrollTop = container.scrollHeight - containerHeight; if (targetScrollTop < 0) { @@ -179,94 +216,103 @@ function initTocScrollSpy() { } else if (targetScrollTop > maxScrollTop) { targetScrollTop = maxScrollTop; } - + container.scrollTo({ top: targetScrollTop, - behavior: 'smooth' + behavior: "smooth", }); } } // Scroll listener for Top and Bottom page fallbacks - window.addEventListener('scroll', () => { - const scrollY = window.scrollY || document.documentElement.scrollTop; - const windowHeight = window.innerHeight; - const docHeight = document.documentElement.scrollHeight; - const container = document.querySelector('.docs-toc'); - - // Top fallback: highlight first TOC link and scroll TOC container to 0 - if (scrollY < 50) { - tocLinks.forEach((link, idx) => { - if (idx === 0) { - link.classList.add(activeClass); - if (container && container.scrollTop > 0) { - container.scrollTo({ top: 0, behavior: 'smooth' }); + window.addEventListener( + "scroll", + () => { + const scrollY = window.scrollY || document.documentElement.scrollTop; + const windowHeight = window.innerHeight; + const docHeight = document.documentElement.scrollHeight; + const container = document.querySelector(".docs-toc"); + + // Top fallback: highlight first TOC link and scroll TOC container to 0 + if (scrollY < 50) { + tocLinks.forEach((link, idx) => { + if (idx === 0) { + link.classList.add(activeClass); + if (container && container.scrollTop > 0) { + container.scrollTo({ top: 0, behavior: "smooth" }); + } + } else { + link.classList.remove(activeClass); } - } else { - link.classList.remove(activeClass); - } - }); - return; - } + }); + return; + } - // Bottom fallback: highlight last TOC link and scroll TOC container to bottom - if (scrollY + windowHeight >= docHeight - 50) { - tocLinks.forEach((link, idx) => { - if (idx === tocLinks.length - 1) { - link.classList.add(activeClass); - if (container) { - const maxScroll = container.scrollHeight - container.clientHeight; - if (container.scrollTop < maxScroll - 5) { - container.scrollTo({ top: container.scrollHeight, behavior: 'smooth' }); + // Bottom fallback: highlight last TOC link and scroll TOC container to bottom + if (scrollY + windowHeight >= docHeight - 50) { + tocLinks.forEach((link, idx) => { + if (idx === tocLinks.length - 1) { + link.classList.add(activeClass); + if (container) { + const maxScroll = + container.scrollHeight - container.clientHeight; + if (container.scrollTop < maxScroll - 5) { + container.scrollTo({ + top: container.scrollHeight, + behavior: "smooth", + }); + } } + } else { + link.classList.remove(activeClass); } - } else { - link.classList.remove(activeClass); - } - }); - return; - } - }, { passive: true }); + }); + return; + } + }, + { passive: true }, + ); } } // Fix collapsible sidebar categories (handles nested folders and folders without direct pages) function initCollapsibleSidebar() { - const sections = document.querySelectorAll('.collapsible-section'); + const sections = document.querySelectorAll(".collapsible-section"); if (sections.length === 0) return; - const isSidebarCollapsed = typeof sidebar_collapsed !== 'undefined' ? sidebar_collapsed : false; + const isSidebarCollapsed = + typeof sidebar_collapsed !== "undefined" ? sidebar_collapsed : false; sections.forEach((section) => { const wrapper = section.nextElementSibling; - if (!wrapper || !wrapper.classList.contains('collapsible-wrapper')) return; + if (!wrapper || !wrapper.classList.contains("collapsible-wrapper")) return; // Auto-expand folder if it contains the currently active note/link - const hasActiveLink = wrapper.querySelector('a.active') !== null; - + const hasActiveLink = wrapper.querySelector("a.active") !== null; + if (hasActiveLink || !isSidebarCollapsed) { - section.classList.add('open'); - wrapper.classList.add('open'); - wrapper.style.height = 'auto'; + section.classList.add("open"); + wrapper.classList.add("open"); + wrapper.style.height = "auto"; } else { - section.classList.remove('open'); - wrapper.classList.remove('open'); - wrapper.style.height = '0px'; + section.classList.remove("open"); + wrapper.classList.remove("open"); + wrapper.style.height = "0px"; } // Clone to remove old click event listener from main.js const newSection = section.cloneNode(true); section.parentNode.replaceChild(newSection, section); - newSection.addEventListener('click', (e) => { + newSection.addEventListener("click", (e) => { e.preventDefault(); - newSection.classList.toggle('open'); - wrapper.classList.toggle('open'); - - if (wrapper.classList.contains('open')) { - wrapper.style.height = 'auto'; + newSection.classList.toggle("open"); + wrapper.classList.toggle("open"); + + if (wrapper.classList.contains("open")) { + wrapper.style.height = "auto"; } else { - wrapper.style.height = '0px'; + wrapper.style.height = "0px"; } }); }); diff --git a/custom-site-assets/static/js/graph.js b/custom-site-assets/static/js/graph.js index e5bf712..cead832 100644 --- a/custom-site-assets/static/js/graph.js +++ b/custom-site-assets/static/js/graph.js @@ -1,12 +1,16 @@ // Query dark mode setting function isDark() { - return localStorage.getItem("theme") === "dark" || (!localStorage.getItem("theme") && window.matchMedia("(prefers-color-scheme: dark)").matches); + return ( + localStorage.getItem("theme") === "dark" || + (!localStorage.getItem("theme") && + window.matchMedia("(prefers-color-scheme: dark)").matches) + ); } // Get URL of current page and also current node var curr_url = decodeURI(window.location.href.replace(location.origin, "")); if (curr_url.endsWith("/")) { - curr_url = curr_url.slice(0, -1); + curr_url = curr_url.slice(0, -1); } // Get graph element @@ -14,34 +18,40 @@ var container = document.getElementById("graph"); // Parse nodes and edges try { - var curr_node = graph_data.nodes.filter((node) => decodeURI(node.url) == curr_url); + var curr_node = graph_data.nodes.filter( + (node) => decodeURI(node.url) == curr_url, + ); } catch (error) { - var curr_node = null; + var curr_node = null; } var nodes = null; var edges = new vis.DataSet(graph_data.edges); if (curr_node.length > 0) { - curr_node = curr_node[0]; - - // Get nodes connected to current - var connected_nodes = graph_data.edges - .filter((edge) => edge.from == curr_node.id || edge.to == curr_node.id) - .map((edge) => { - if (edge.from == curr_node.id) { - return edge.to; - } - return edge.from; - }); - - if (graph_is_local) { - nodes = new vis.DataSet(graph_data.nodes.filter((node) => node.id == curr_node.id || connected_nodes.includes(node.id))); - } else { - nodes = new vis.DataSet(graph_data.nodes); - } + curr_node = curr_node[0]; + + // Get nodes connected to current + var connected_nodes = graph_data.edges + .filter((edge) => edge.from == curr_node.id || edge.to == curr_node.id) + .map((edge) => { + if (edge.from == curr_node.id) { + return edge.to; + } + return edge.from; + }); + + if (graph_is_local) { + nodes = new vis.DataSet( + graph_data.nodes.filter( + (node) => node.id == curr_node.id || connected_nodes.includes(node.id), + ), + ); + } else { + nodes = new vis.DataSet(graph_data.nodes); + } } else { - curr_node = null; - nodes = new vis.DataSet(graph_data.nodes); + curr_node = null; + nodes = new vis.DataSet(graph_data.nodes); } // Get nodes and edges from generated javascript @@ -49,17 +59,17 @@ var max_node_val = Math.max(...nodes.map((node) => node.value)); // Highlight current node and set to center if (curr_node) { - nodes.update({ - id: curr_node.id, - value: Math.max(4, max_node_val * 2.5), - shape: "star", - color: "#63afae", // Keep Sicily Teal accent - font: { - strokeWidth: 1, - }, - x: 0, - y: 0, - }); + nodes.update({ + id: curr_node.id, + value: Math.max(4, max_node_val * 2.5), + shape: "star", + color: "#63afae", // Keep Sicily Teal accent + font: { + strokeWidth: 1, + }, + x: 0, + y: 0, + }); } // Construct graph @@ -67,136 +77,142 @@ var options = ___GRAPH_OPTIONS___; // Adjust options dynamically for better user experience if (graph_is_local) { - if (!options.interaction) { - options.interaction = {}; - } - options.interaction.zoomView = false; // Disable scroll wheel zooming to prevent page scroll hijacking - options.interaction.dragNodes = true; // Explicitly enable dragging nodes - options.interaction.dragView = true; // Explicitly enable panning the view + if (!options.interaction) { + options.interaction = {}; + } + options.interaction.zoomView = false; // Disable scroll wheel zooming to prevent page scroll hijacking + options.interaction.dragNodes = true; // Explicitly enable dragging nodes + options.interaction.dragView = true; // Explicitly enable panning the view } else { - if (!options.interaction) { - options.interaction = {}; - } - options.interaction.zoomSpeed = 0.25; // Gentle, less sensitive zoom speed - options.interaction.dragNodes = true; - options.interaction.dragView = true; + if (!options.interaction) { + options.interaction = {}; + } + options.interaction.zoomSpeed = 0.25; // Gentle, less sensitive zoom speed + options.interaction.dragNodes = true; + options.interaction.dragView = true; } var graph = new vis.Network( - container, - { - nodes: nodes, - edges: edges, - }, - options + container, + { + nodes: nodes, + edges: edges, + }, + options, ); // Gentle trackpad zoom speed proxy interceptor -if (graph && graph.interactionHandler && graph.interactionHandler.body && graph.interactionHandler.body.eventListeners && graph.interactionHandler.body.eventListeners.onMouseWheel) { - var originalOnMouseWheel = graph.interactionHandler.body.eventListeners.onMouseWheel; - graph.interactionHandler.body.eventListeners.onMouseWheel = function(event) { - var proxyEvent = new Proxy(event, { - get: function(target, prop) { - if (prop === 'deltaY') { - var delta = target.deltaY; - // Trackpad pinch-to-zoom has ctrlKey set to true in browser zoom event emulation - var multiplier = target.ctrlKey ? 0.05 : 0.25; - return delta * multiplier; - } - var value = target[prop]; - if (typeof value === 'function') { - return value.bind(target); - } - return value; - } - }); - originalOnMouseWheel(proxyEvent); - }; +if ( + graph && + graph.interactionHandler && + graph.interactionHandler.body && + graph.interactionHandler.body.eventListeners && + graph.interactionHandler.body.eventListeners.onMouseWheel +) { + var originalOnMouseWheel = + graph.interactionHandler.body.eventListeners.onMouseWheel; + graph.interactionHandler.body.eventListeners.onMouseWheel = function (event) { + var proxyEvent = new Proxy(event, { + get: function (target, prop) { + if (prop === "deltaY") { + var delta = target.deltaY; + // Trackpad pinch-to-zoom has ctrlKey set to true in browser zoom event emulation + var multiplier = target.ctrlKey ? 0.05 : 0.25; + return delta * multiplier; + } + var value = target[prop]; + if (typeof value === "function") { + return value.bind(target); + } + return value; + }, + }); + originalOnMouseWheel(proxyEvent); + }; } - // Clickable URL graph.on("selectNode", function (params) { - if (params.nodes.length === 1) { - var node = nodes.get(params.nodes[0]); - if (graph_link_replace) { - window.open(node.url, "_self"); - } else { - window.open(node.url, "_blank"); - } - } + if (params.nodes.length === 1) { + var node = nodes.get(params.nodes[0]); + if (graph_link_replace) { + window.open(node.url, "_self"); + } else { + window.open(node.url, "_blank"); + } + } }); // Sticky nodes: Pin node in place on dragEnd graph.on("dragEnd", function (params) { - if (params.nodes.length === 1) { - var nodeId = params.nodes[0]; - nodes.update({ - id: nodeId, - fixed: { - x: true, - y: true - } - }); - } + if (params.nodes.length === 1) { + var nodeId = params.nodes[0]; + nodes.update({ + id: nodeId, + fixed: { + x: true, + y: true, + }, + }); + } }); // Unpin all nodes when clicking empty canvas space graph.on("click", function (params) { - if (params.nodes.length === 0) { - var allNodes = nodes.get(); - var updates = allNodes - .filter(function(node) { - return node.fixed && (node.fixed.x || node.fixed.y); - }) - .map(function(node) { - return { - id: node.id, - fixed: { - x: false, - y: false - } - }; - }); - if (updates.length > 0) { - nodes.update(updates); - } - } + if (params.nodes.length === 0) { + var allNodes = nodes.get(); + var updates = allNodes + .filter(function (node) { + return node.fixed && (node.fixed.x || node.fixed.y); + }) + .map(function (node) { + return { + id: node.id, + fixed: { + x: false, + y: false, + }, + }; + }); + if (updates.length > 0) { + nodes.update(updates); + } + } }); // Focus on current node + scaling var initialScale = 0.35; graph.once("afterDrawing", function () { - initialScale = graph.getScale(); - if (curr_node) { - if (!graph_is_local) { - graph.focus(curr_node.id, { - scale: graph.getScale() * 1.8, - }); - } - } else { - var clientHeight = container.clientHeight; - graph.moveTo({ - position: { - x: 0, - y: -clientHeight / 3, - }, - scale: graph.getScale() * 1.2, - }); - } + initialScale = graph.getScale(); + if (curr_node) { + if (!graph_is_local) { + graph.focus(curr_node.id, { + scale: graph.getScale() * 1.8, + }); + } + } else { + var clientHeight = container.clientHeight; + graph.moveTo({ + position: { + x: 0, + y: -clientHeight / 3, + }, + scale: graph.getScale() * 1.2, + }); + } }); // Enforce zoom limits on global graph if (!graph_is_local) { - graph.on("zoom", function (params) { - var minScale = Math.min(0.35, initialScale * 0.85); - var maxScale = 2.5; - if (graph.getScale() < minScale) { - graph.moveTo({ scale: minScale }); - } else if (graph.getScale() > maxScale) { - graph.moveTo({ scale: maxScale }); - } - }); + graph.on("zoom", function (params) { + var minScale = Math.min(0.35, initialScale * 0.85); + var maxScale = 2.5; + if (graph.getScale() < minScale) { + graph.moveTo({ scale: minScale }); + } else if (graph.getScale() > maxScale) { + graph.moveTo({ scale: maxScale }); + } + }); } // Zoom buttons event listeners @@ -204,24 +220,24 @@ var zoomInBtn = document.getElementById("zoom-in-btn"); var zoomOutBtn = document.getElementById("zoom-out-btn"); if (zoomInBtn && zoomOutBtn) { - zoomInBtn.addEventListener("click", function(e) { - e.preventDefault(); - var currentScale = graph.getScale(); - var newScale = currentScale * 1.25; - if (newScale <= 2.5) { - graph.moveTo({ scale: newScale }); - } - }); - - zoomOutBtn.addEventListener("click", function(e) { - e.preventDefault(); - var currentScale = graph.getScale(); - var newScale = currentScale / 1.25; - var minScale = Math.min(0.35, initialScale * 0.85); - if (newScale >= minScale) { - graph.moveTo({ scale: newScale }); - } else { - graph.moveTo({ scale: minScale }); - } - }); + zoomInBtn.addEventListener("click", function (e) { + e.preventDefault(); + var currentScale = graph.getScale(); + var newScale = currentScale * 1.25; + if (newScale <= 2.5) { + graph.moveTo({ scale: newScale }); + } + }); + + zoomOutBtn.addEventListener("click", function (e) { + e.preventDefault(); + var currentScale = graph.getScale(); + var newScale = currentScale / 1.25; + var minScale = Math.min(0.35, initialScale * 0.85); + if (newScale >= minScale) { + graph.moveTo({ scale: newScale }); + } else { + graph.moveTo({ scale: minScale }); + } + }); } diff --git a/custom-site-assets/static/js/search.js b/custom-site-assets/static/js/search.js index 308f9f6..7ae5134 100644 --- a/custom-site-assets/static/js/search.js +++ b/custom-site-assets/static/js/search.js @@ -1,8 +1,8 @@ -var suggestions = document.getElementById('suggestions'); -var userinput = document.getElementById('userinput'); +var suggestions = document.getElementById("suggestions"); +var userinput = document.getElementById("userinput"); if (suggestions && userinput) { - document.addEventListener('keydown', inputFocus); + document.addEventListener("keydown", inputFocus); function inputFocus(e) { const isCmdK = (e.metaKey || e.ctrlKey) && e.keyCode === 75; @@ -11,10 +11,10 @@ if (suggestions && userinput) { // Handle Cmd + K toggle if (isCmdK) { e.preventDefault(); - if (document.body.classList.contains('command-palette-open')) { + if (document.body.classList.contains("command-palette-open")) { userinput.blur(); - suggestions.classList.add('d-none'); - document.body.classList.remove('command-palette-open'); + suggestions.classList.add("d-none"); + document.body.classList.remove("command-palette-open"); } else { userinput.focus(); } @@ -22,51 +22,59 @@ if (suggestions && userinput) { } // Handle '/' focus (only if not already inside an input) - if (isForwardSlash - && document.activeElement.tagName !== "INPUT" - && document.activeElement.tagName !== "TEXTAREA") { + if ( + isForwardSlash && + document.activeElement.tagName !== "INPUT" && + document.activeElement.tagName !== "TEXTAREA" + ) { e.preventDefault(); userinput.focus(); } - if (e.keyCode === 27 ) { // Escape key to close suggestions + if (e.keyCode === 27) { + // Escape key to close suggestions userinput.blur(); - suggestions.classList.add('d-none'); - document.body.classList.remove('command-palette-open'); + suggestions.classList.add("d-none"); + document.body.classList.remove("command-palette-open"); } } // Hide suggestions when clicking outside - document.addEventListener('click', function(event) { + document.addEventListener("click", function (event) { var isClickInsideSuggestions = suggestions.contains(event.target); - var isClickInput = (event.target === userinput); + var isClickInput = event.target === userinput; if (!isClickInsideSuggestions && !isClickInput) { - suggestions.classList.add('d-none'); - document.body.classList.remove('command-palette-open'); + suggestions.classList.add("d-none"); + document.body.classList.remove("command-palette-open"); } }); // Handle keyboard navigation inside search suggestions - document.addEventListener('keydown', suggestionFocus); + document.addEventListener("keydown", suggestionFocus); function suggestionFocus(e) { - const focusableSuggestions = suggestions.querySelectorAll('.suggestion-link'); - if (suggestions.classList.contains('d-none') || focusableSuggestions.length === 0) { + const focusableSuggestions = + suggestions.querySelectorAll(".suggestion-link"); + if ( + suggestions.classList.contains("d-none") || + focusableSuggestions.length === 0 + ) { return; } const focusable = [...focusableSuggestions]; const index = focusable.indexOf(document.activeElement); - if (e.keyCode === 38) { // Up arrow + if (e.keyCode === 38) { + // Up arrow e.preventDefault(); if (index === 0) { userinput.focus(); } else if (index > 0) { focusableSuggestions[index - 1].focus(); } - } - else if (e.keyCode === 40) { // Down arrow + } else if (e.keyCode === 40) { + // Down arrow e.preventDefault(); if (index === -1) { focusableSuggestions[0].focus(); @@ -77,58 +85,59 @@ if (suggestions && userinput) { } // Initialize elasticlunr search execution - (function(){ + (function () { if (!window.searchIndex) { console.warn("Search index not found"); return; } var index = elasticlunr.Index.load(window.searchIndex); - - userinput.addEventListener('input', show_results, true); - userinput.addEventListener('focus', function() { - document.body.classList.add('command-palette-open'); + + userinput.addEventListener("input", show_results, true); + userinput.addEventListener("focus", function () { + document.body.classList.add("command-palette-open"); if (this.value.trim().length > 0) { show_results.call(this); } }); - userinput.addEventListener('click', function() { - document.body.classList.add('command-palette-open'); + userinput.addEventListener("click", function () { + document.body.classList.add("command-palette-open"); if (this.value.trim().length > 0) { show_results.call(this); } }); - - suggestions.addEventListener('click', accept_suggestion, true); - + + suggestions.addEventListener("click", accept_suggestion, true); + function show_results() { var value = this.value.trim(); - suggestions.innerHTML = ''; // Safely clear all old suggestions + suggestions.innerHTML = ""; // Safely clear all old suggestions if (value.length === 0) { - suggestions.classList.add('d-none'); + suggestions.classList.add("d-none"); return; } var options = { bool: "OR", fields: { - title: {boost: 2, expand: true}, - body: {boost: 1, expand: true}, - expand: true - } + title: { boost: 2, expand: true }, + body: { boost: 1, expand: true }, + expand: true, + }, }; var results = index.search(value, options); - suggestions.classList.remove('d-none'); + suggestions.classList.remove("d-none"); // Filter pages that have content/body to show - var validResults = results.filter(function(page) { - return page.doc.body && page.doc.body.trim() !== ''; + var validResults = results.filter(function (page) { + return page.doc.body && page.doc.body.trim() !== ""; }); if (validResults.length === 0) { - var noResults = document.createElement('div'); - noResults.className = 'suggestion-no-results'; - noResults.innerHTML = 'No results found for "' + escapeHTML(value) + '"'; + var noResults = document.createElement("div"); + noResults.className = "suggestion-no-results"; + noResults.innerHTML = + 'No results found for "' + escapeHTML(value) + '"'; suggestions.appendChild(noResults); return; } @@ -138,19 +147,19 @@ if (suggestions && userinput) { for (var i = 0; i < maxResults; i++) { var page = validResults[i]; - var entry = document.createElement('div'); - entry.className = 'suggestion-item'; + var entry = document.createElement("div"); + entry.className = "suggestion-item"; - var a = document.createElement('a'); + var a = document.createElement("a"); a.href = page.ref; - a.className = 'suggestion-link'; + a.className = "suggestion-link"; - var title = document.createElement('div'); - title.className = 'suggestion-title'; + var title = document.createElement("div"); + title.className = "suggestion-title"; title.textContent = page.doc.title; - var teaser = document.createElement('div'); - teaser.className = 'suggestion-teaser'; + var teaser = document.createElement("div"); + teaser.className = "suggestion-teaser"; teaser.innerHTML = makeTeaser(page.doc.body, items); a.appendChild(title); @@ -161,8 +170,8 @@ if (suggestions && userinput) { } function accept_suggestion() { - suggestions.innerHTML = ''; - suggestions.classList.add('d-none'); + suggestions.innerHTML = ""; + suggestions.classList.add("d-none"); } // Snippet extraction with search highlights @@ -171,14 +180,14 @@ if (suggestions && userinput) { var NORMAL_WORD_WEIGHT = 2; var FIRST_WORD_WEIGHT = 8; var TEASER_MAX_WORDS = 20; - + var stemmedTerms = terms.map(function (w) { return elasticlunr.stemmer(w.toLowerCase()); }); var termFound = false; var index = 0; var weighted = []; // contains elements of ["word", weight, index_in_document] - + // split in sentences, then words var sentences = body.toLowerCase().split(". "); for (var i in sentences) { @@ -201,15 +210,15 @@ if (suggestions && userinput) { } index += 1; } - + if (weighted.length === 0) { if (body.length !== undefined && body.length > TEASER_MAX_WORDS * 10) { - return escapeHTML(body.substring(0, TEASER_MAX_WORDS * 10)) + '...'; + return escapeHTML(body.substring(0, TEASER_MAX_WORDS * 10)) + "..."; } else { return escapeHTML(body); } } - + var windowWeights = []; var windowSize = Math.min(weighted.length, TEASER_MAX_WORDS); var curSum = 0; @@ -217,13 +226,13 @@ if (suggestions && userinput) { curSum += weighted[i][1]; } windowWeights.push(curSum); - + for (var i = 0; i < weighted.length - windowSize; i++) { curSum -= weighted[i][1]; curSum += weighted[i + windowSize][1]; windowWeights.push(curSum); } - + var maxSumIndex = 0; if (termFound) { var maxFound = 0; @@ -234,7 +243,7 @@ if (suggestions && userinput) { } } } - + var teaser = []; var startIndex = weighted[maxSumIndex][2]; for (var i = maxSumIndex; i < maxSumIndex + windowSize; i++) { @@ -243,21 +252,25 @@ if (suggestions && userinput) { teaser.push(escapeHTML(body.substring(startIndex, word[2]))); startIndex = word[2]; } - + if (word[1] === TERM_WEIGHT) { teaser.push(""); } - + startIndex = word[2] + word[0].length; var re = /^[\x00-\xff]+$/; - if (word[1] !== TERM_WEIGHT && word[0].length >= 12 && !re.test(word[0])) { + if ( + word[1] !== TERM_WEIGHT && + word[0].length >= 12 && + !re.test(word[0]) + ) { var strBefor = body.substring(word[2], startIndex); var strAfter = substringByByte(strBefor, 12); teaser.push(escapeHTML(strAfter)); } else { teaser.push(escapeHTML(body.substring(word[2], startIndex))); } - + if (word[1] === TERM_WEIGHT) { teaser.push(""); } @@ -265,19 +278,21 @@ if (suggestions && userinput) { teaser.push("…"); return teaser.join(""); } - }()); + })(); } // Helper to escape HTML characters safely function escapeHTML(str) { - return str.replace(/[&<>'"]/g, - tag => ({ - '&': '&', - '<': '<', - '>': '>', - "'": ''', - '"': '"' - }[tag] || tag) + return str.replace( + /[&<>'"]/g, + (tag) => + ({ + "&": "&", + "<": "<", + ">": ">", + "'": "'", + '"': """, + })[tag] || tag, ); } @@ -292,7 +307,7 @@ function substringByByte(str, maxLength) { var code = str.codePointAt(i).toString(16); if (code.length > 4) { i++; - if ((i + 1) < str.length) { + if (i + 1 < str.length) { flag = str.codePointAt(i + 1).toString(16) == "200d"; } } diff --git a/custom-site-assets/templates/docs/page.html b/custom-site-assets/templates/docs/page.html index ae28439..2f2d37f 100644 --- a/custom-site-assets/templates/docs/page.html +++ b/custom-site-assets/templates/docs/page.html @@ -18,7 +18,7 @@ {{ macros_toc::docs_toc(page=page) }}

{{ page.title | safe }}

- + {# Reading time and last-updated metadata badges #}
{% if page.extra.reading_time %} diff --git a/custom-site-assets/templates/index.html b/custom-site-assets/templates/index.html index ecc708d..4d86f37 100644 --- a/custom-site-assets/templates/index.html +++ b/custom-site-assets/templates/index.html @@ -2,16 +2,16 @@ {% block seo %} {{ super() }} - + {% set title = "About me" %} {% if config.extra.title_addition %} {% set title_addition = title ~ " | " ~ config.extra.title_addition %} {% else %} {% set title_addition = title %} {% endif %} - + {% set description = config.description %} - + {{ macros_head::seo(title=title_addition, title_addition=title_addition, description=description, is_home=true) }} {% endblock seo %} @@ -30,10 +30,10 @@
{{ macros_sidebar::docs_sidebar(current_section=current_section) }} - + {# Retrieve the README page directly for the home landing #} {% set page = get_page(path="docs/readme.md") %} - + {{ macros_toc::docs_toc(page=page) }}

About me

diff --git a/custom-site-assets/templates/macros/docs-sidebar.html b/custom-site-assets/templates/macros/docs-sidebar.html index 447bca5..b49a6ad 100644 --- a/custom-site-assets/templates/macros/docs-sidebar.html +++ b/custom-site-assets/templates/macros/docs-sidebar.html @@ -3,12 +3,12 @@