obsidian-web-clipper
Author and debug Obsidian Web Clipper extension templates: template JSON, variables, filters, template logic, schema.org/CSS selectors, AI interpreter prompts, URL/schema triggers. Use when generating, importing, or fixing a clipper template, or matching one to a target site. Not for general scraping.
What this skill does
# Obsidian Web Clipper Templates
Obsidian Web Clipper is a browser extension that saves web content to an Obsidian vault as Markdown notes. Templates define how pages are captured — what metadata to extract, how to format the note, and which sites to auto-match.
Templates are configured as JSON. Users can import/export individual templates or full settings backups. When generating templates, output valid JSON the user can import directly.
**Caution:** full settings exports contain interpreter API keys in plaintext (`interpreter_settings.providers[].apiKey`). Never quote them; flag exports stored in version control.
## Adapt to the user's vault conventions
Templates are deeply opinionated about folder layout, frontmatter property names, category/tag conventions, and note body structure. **Match the user's existing conventions rather than inventing.** Before drafting a template, look for:
- Vault-level docs (`CLAUDE.md`, `AGENTS.md`, READMEs)
- An existing templates folder, sample notes of the type being clipped, or a previously exported clipper settings file
- Any YAML property-sort configuration (Linter or similar)
When conventions aren't discoverable, default to minimal vendor-neutral choices and flag the assumptions you made so the user can adjust before import.
If the vault has a property sort order, mirror that order in the template's `properties` array — the extension preserves array order in the output frontmatter, so freshly clipped notes won't reshuffle on lint.
## Template JSON Schema
```json
{
"schemaVersion": "0.1.0",
"name": "Template Name",
"behavior": "create",
"noteNameFormat": "{{title}} - {{site}}",
"noteContentFormat": "{{content}}",
"vault": "VaultName",
"path": "folder/subfolder",
"context": "",
"properties": [],
"triggers": []
}
```
| Field | Type | Description |
| ------------------- | ------ | ----------------------------------------------------------- |
| `schemaVersion` | string | Always `"0.1.0"` |
| `name` | string | Display name shown in the extension |
| `behavior` | string | How the note is created (see below) |
| `noteNameFormat` | string | Filename for the note, supports variables |
| `noteContentFormat` | string | Note body content, supports variables |
| `vault` | string | Target vault name (optional — omit to use default) |
| `path` | string | Folder path within the vault (optional) |
| `context` | string | Limits what page content the interpreter AI sees (optional) |
| `properties` | array | Frontmatter properties (optional) |
| `triggers` | array | Auto-match rules for URLs/schemas (optional) |
When exporting a single template, the JSON is the template object directly (no wrapper). When part of a full settings export, templates are stored as `template_[id]` keys.
### Behaviors
| Value | Description |
| ------------------ | ----------------------------------- |
| `create` | Create a new note |
| `append-daily` | Append to today's daily note |
| `prepend-daily` | Prepend to today's daily note |
| `append-specific` | Append to a specific existing note |
| `prepend-specific` | Prepend to a specific existing note |
## Variables
All variables use `{{variableName}}` syntax. Filters chain with pipes: `{{variable|filter1|filter2:"arg"}}`. There are four kinds:
- **Preset variables** — built-in page metadata: `{{title}}`, `{{url}}`, `{{author}}`, `{{site}}`, `{{published}}`, `{{description}}`, `{{content}}` (full article markdown), `{{date}}`, `{{highlights}}`, etc. Look up the full list in the live docs.
- **Schema variables** — extract Schema.org JSON-LD from the page. Patterns:
```
{{schema:name}} — first match anywhere
{{schema:@Recipe:name}} — scoped to a specific @type
{{schema:author.name}} — nested keys via dots
{{schema:image[0].contentUrl}} — array index
{{schema:actors[*].name}} — flatten arrays
```
Schema-driven templates are usually the right choice for domain-specific clippers (recipes, films, books, jobs) since one `schema:@Type` trigger matches across many sites.
- **Selector variables** — pull content via CSS selectors when there's no schema:
```
{{selector:h1}} — text content
{{selector:img.hero?src}} — attribute value
{{selectorHtml:article}} — raw HTML
{{selectorHtml:body|markdown}} — HTML → Markdown
```
- **Interpreter variables** — natural-language prompts evaluated by an LLM (requires the user has an LLM provider configured in extension settings):
```
{{"a summary of the page"}}
{{"3 tags describing this content"}}
{{"return JSON array with fields: author, text"|map:item => item.text|join:"\n"}}
```
The `context` field at the template top level controls what page content the AI sees. Use `{{selectorHtml:#main}}` or wrap structured context like `<page>\nTitle: {{title}}\n{{content}}\n</page>` to keep prompts focused.
## Filters
Chain filters with `|`. Look up specific filter signatures in the live docs (see Reference at the bottom). The patterns and gotchas below are the operationally non-obvious parts.
### Filter gotchas (verified via testing)
These trip up template authors most often:
- **String concatenation with `+` doesn't work inside `map` callbacks.** The expression parser treats `+` as an unexpected character and fails the template import with `"Unexpected character '+' in template"`. Use template literals instead:
```
✗ |map:item => "- " + item (parse error on import)
✓ |map:item => "- ${item}" (template literal — correct)
```
- **Built-in filters cannot be chained inside `map`.** Flatten or transform inside `map`, then apply filters to the result outside:
```
✗ |map:item => item.text|trim (filter not allowed inside map)
✓ |map:item => item.text|join:"\n"|trim (filter chain outside map)
```
- **For arrays of objects with multi-line per-item output, use `map` to flatten to flat keys, then `template`.** Nested key access in `template` literals is not documented and unreliable; flattening first is the safe pattern:
```
|map:item => ({name: item.author.name, body: item.reviewBody})
|template:"${name}:\n> ${body}\n\n"
```
- **`duration` outputs `HH:mm:ss`, not compact human format.** For something like `45m` or `1h 30m`, skip the filter and chain `replace` on the raw ISO 8601 string:
```
{{schema:@Recipe:prepTime|replace:"PT":""|replace:"H":"h "|replace:"M":"m"|trim}}
→ "PT1H30M" becomes "1h 30m"
→ "PT45M" becomes "45m"
```
- **`|list` works on string arrays directly** — no need to map a `"- " + item` prefix yourself. Use `|list` for bullets, `|list:numbered` for numbered, `|list:task` for checkboxes.
### Common Filter Chains
```
{{url|split:"?"|slice:0,1}} — strip query params
{{schema:actors[*].name|wikilink|slice:0,4|join}} — first 4 actors as wikilinks
{{highlights|map:item => item.text|join:"\n\n"|blockquote}} — highlights as blockquote
{{date|date:"YYYY-MM-DD-ddd"}} — formatted date with day name
{{"return JSON..."|map:item => item.title|join:"\n"}} — AI → structured → formatted
```
## Template logic
Templates support Twig/Liquid-style logic in `noteContentFormat`, `noteNameFormat`, and property values: `{% if %}`/`{% elseif %}`/`{% else %}` conditionals, `{% for %}` loops (with a `loop` object: `loop.index`, `loop.first`, `loop.last`, etc.), `{% set %}` variable assignment, and `??` fallbacks (`{{title ?? "Untitled"}}`). Full sRelated in Web Dev
generating-lwc-components
IncludedLightning Web Components with PICKLES methodology and 165-point scoring. Use this skill when the user creates or edits LWC components, builds wire service patterns, or writes Jest tests for LWC. TRIGGER when: user creates/edits LWC components, touches lwc/**/*.js, .html, .css, .js-meta.xml files, or asks about wire service, SLDS, or Jest LWC tests. DO NOT TRIGGER when: Apex classes (use generating-apex), Aura components, or Visualforce.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Set up queries with useQuery, mutations with useMutation, configure QueryClient caching strategies, implement optimistic updates, and handle infinite scroll with useInfiniteQuery. Use when: setting up data fetching in React projects, migrating from v4 to v5, or fixing object syntax required errors, query callbacks removed issues, cacheTime renamed to gcTime, isPending vs isLoading confusion, keepPreviousData removed problems.
document-processor-api
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
nutrient-document-processing
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Covers useMutationState, simplified optimistic updates, throwOnError, network mode (offline/PWA), and infiniteQueryOptions. Use when setting up data fetching, fixing v4→v5 migration errors (object syntax, gcTime, isPending, keepPreviousData), or debugging SSR/hydration issues with streaming server components.
accelint-nextjs-best-practices
IncludedNext.js performance optimization and best practices. Use when writing Next.js code (App Router or Pages Router); implementing Server Components, Server Actions, or API routes; optimizing RSC serialization, data fetching, or server-side rendering; reviewing Next.js code for performance issues; fixing authentication in Server Actions; or implementing Suspense boundaries, parallel data fetching, or request deduplication.