[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6F46WN94Z5P6G2J8E86TES8":3,"skill-same:01M6F46WN94Z5P6G2J8E86TES8":75},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":26,"publishedAt":46,"linkOnly":47,"markdownContent":48,"markdownBody":49,"frontMatter":50,"refName":29,"commitSha":53,"skillDirectory":54,"forksCount":55,"repositoryHtmlUrl":56,"repositoryHomepageUrl":57,"ownerHtmlUrl":58,"ownerBlogUrl":6,"ownerDescription":59,"updatedAt":60,"install":61,"resources":74},"01M6F46WN94Z5P6G2J8E86TES8","obsidian",null,"Comprehensive guidelines for Obsidian.md plugin development including all 27 ESLint rules from eslint-plugin-obsidianmd v0.1.9, TypeScript best practices, memory management, API usage (requestUrl vs fetch), UI\u002FUX standards, locale file sentence-case enforcement, and submission requirements. Use when working with Obsidian plugins, main.ts files, manifest.json, Plugin class, MarkdownView, TFile, vault operations, or any Obsidian API development.","ComeOnOliver\u002Fskillshub","ComeOnOliver","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F43215937?v=4","01M6E5JNFECJAAVFE0W7SMWWD2","Haoran Shu","01M6E5JNNDQS7AJZDYFQ8TPZCK",67,"MIT","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fgapmiss\u002Fobsidian-plugin-skill\u002Fobsidian\u002FSKILL.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000032","设计师",[5,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45],"requirements","typescript","main","files","enforcement","standards","use","v0","fetch","Rules","typescript best practices","api","manifest","ui","ux","best practices","development","guidelines","memory","2026-11-10 10:12:30",false,"---\nname: obsidian\ndescription: Comprehensive guidelines for Obsidian.md plugin development including all 27 ESLint rules from eslint-plugin-obsidianmd v0.1.9, TypeScript best practices, memory management, API usage (requestUrl vs fetch), UI\u002FUX standards, locale file sentence-case enforcement, and submission requirements. Use when working with Obsidian plugins, main.ts files, manifest.json, Plugin class, MarkdownView, TFile, vault operations, or any Obsidian API development.\nlicense: MIT\nmetadata: \n  version: 1.4.0\n---\n\n# Obsidian Plugin Development Guidelines\n\nFollow these comprehensive guidelines derived from the official Obsidian ESLint plugin rules, submission requirements, and best practices.\n\n## Getting Started\n\n### Quick Start Tool\n\nFor new plugin projects, an interactive boilerplate generator is available:\n- **Script**: `tools\u002Fcreate-plugin.js` in the skill repository\n- **Command**: Invoke `create-plugin` using your agent's method (`\u002Fcreate-plugin`, `$create-plugin`, or `@create-plugin`)\n- Generates minimal, best-practice boilerplate with no sample code\n- Detects existing projects and only adds missing files\n\nRecommend the boilerplate generator when users ask how to create a new plugin, want to start a new project, or need help setting up the basic structure.\n\n---\n\n## Rules Reference (eslint-plugin-obsidianmd v0.1.9)\n\n### Submission & Naming\n| # | Rule | ✅ Do | ❌ Don't |\n|---|------|--------|----------|\n| 1 | Plugin ID | Omit \"obsidian\"; don't end with \"plugin\" | Include \"obsidian\" or end with \"plugin\" |\n| 2 | Plugin name | Omit \"Obsidian\"; don't end with \"Plugin\" | Include \"Obsidian\" or end with \"Plugin\" |\n| 3 | Plugin name | Don't start with \"Obsi\" or end with \"dian\" | Start with \"Obsi\" or end with \"dian\" |\n| 4 | Description | Omit \"Obsidian\", \"This plugin\", etc. | Use \"Obsidian\" or \"This plugin\" |\n| 5 | Description | End with `.?!)` punctuation | Leave description without terminal punctuation |\n\n### Memory & Lifecycle\n| # | Rule | ✅ Do | ❌ Don't |\n|---|------|--------|----------|\n| 6 | Event cleanup | Use `registerEvent()` for automatic cleanup | Register events without cleanup |\n| 7 | View references | Return views\u002Fcomponents directly | Store view references in plugin properties or pass plugin as component to `MarkdownRenderer` |\n\n### Type Safety\n| # | Rule | ✅ Do | ❌ Don't |\n|---|------|--------|----------|\n| 8 | TFile\u002FTFolder | Use `instanceof` for type checking | Cast to TFile\u002FTFolder; use `any`; use `var` |\n\n### UI\u002FUX\n| # | Rule | ✅ Do | ❌ Don't |\n|---|------|--------|----------|\n| 9 | UI text | Sentence case — \"Advanced settings\" | Title Case — \"Advanced Settings\" |\n| 10 | JSON locale | Sentence case in JSON locale files (`recommendedWithLocalesEn`) | Title case in locale JSON |\n| 11 | TS\u002FJS locale | Sentence case in TS\u002FJS locale modules | Title case in locale modules |\n| 12 | Command names | Omit \"command\" in command names\u002FIDs | Include \"command\" in names\u002FIDs |\n| 13 | Command IDs | Omit plugin ID\u002Fname from command IDs\u002Fnames | Duplicate plugin ID in command IDs |\n| 14 | Hotkeys | No default hotkeys | Set default hotkeys |\n| 15 | Settings headings | Use `.setHeading()` | Create manual HTML headings; use \"General\", \"settings\", or plugin name in headings |\n\n### API Best Practices\n| # | Rule | ✅ Do | ❌ Don't |\n|---|------|--------|----------|\n| 16 | Active file edits | Use Editor API | Use `Vault.modify()` for active file edits |\n| 17 | Background file mods | Use `Vault.process()` | Use `Vault.modify()` for background modifications |\n| 18 | User paths | Use `normalizePath()` | Hardcode `.obsidian` path; use raw user paths |\n| 19 | OS detection | Use `Platform` API | Use `navigator.platform`\u002F`userAgent` |\n| 20 | Network requests | Use `requestUrl()` | Use `fetch()` |\n| 21 | Logging | Minimize console logging; none in `onload`\u002F`onunload` in production | Use `console.log` in `onload`\u002F`onunload` |\n\n### Styling\n| # | Rule | ✅ Do | ❌ Don't |\n|---|------|--------|----------|\n| 22 | CSS variables | Use Obsidian CSS variables for all styling | Hardcode colors, sizes, or spacing |\n| 23 | CSS scope | Scope CSS to plugin containers | Use broad CSS selectors |\n| 24 | Style elements | Use `styles.css` file (`no-forbidden-elements`) | Create `\u003Clink>` or `\u003Cstyle>` elements; assign styles via JavaScript |\n\n### Accessibility (MANDATORY)\n| # | Rule | ✅ Do | ❌ Don't |\n|---|------|--------|----------|\n| 25 | Keyboard access | Make all interactive elements keyboard accessible; Tab through all elements | Create inaccessible interactive elements |\n| 26 | ARIA labels | Provide ARIA labels for icon buttons; use `data-tooltip-position` for tooltips | Use icon buttons without ARIA labels |\n| 27 | Focus indicators | Use `:focus-visible` with Obsidian CSS variables; touch targets ≥ 44×44px | Remove focus indicators; make touch targets \u003C 44×44px |\n\n### Security & Compatibility\n| Rule | ✅ Do | ❌ Don't |\n|------|--------|----------|\n| DOM safety | Use Obsidian DOM helpers (`createDiv()`, `createSpan()`, `createEl()`) | Use `innerHTML`\u002F`outerHTML` or `document.createElement` |\n| iOS compat | Avoid regex lookbehind (iOS \u003C 16.4 incompatibility) | Use regex lookbehind |\n\n### Code Quality\n| Rule | ✅ Do | ❌ Don't |\n|------|--------|----------|\n| Sample code | Remove all sample\u002Ftemplate code | Keep class names like MyPlugin, SampleModal |\n| Object.assign | `Object.assign({}, defaults, overrides)` (`object-assign`) | `Object.assign(defaultsVar, other)` — mutates defaults |\n| LICENSE | Copyright holder must not be \"Dynalist Inc.\"; year must be current (`validate-license`) | Leave \"Dynalist Inc.\" as holder or use an outdated year |\n| Async | Use async\u002Fawait | Use Promise chains |\n\n---\n\n## Detailed Guidelines\n\nFor comprehensive information on specific topics, see the reference files:\n\n### [Memory Management & Lifecycle](reference\u002Fmemory-management.md)\n- Using `registerEvent()`, `addCommand()`, `registerDomEvent()`, `registerInterval()`\n- Avoiding view references in plugin\n- Not using plugin as component\n- Proper leaf cleanup\n\n### [Type Safety](reference\u002Ftype-safety.md)\n- Using `instanceof` instead of type casting\n- Avoiding `any` type\n- Using `const` and `let` over `var`\n\n### [UI\u002FUX Standards](reference\u002Fui-ux.md)\n- Sentence case enforcement (TypeScript, JSON locale, TS\u002FJS locale modules)\n- `recommendedWithLocalesEn` config for locale file checks\n- Command naming conventions (no \"command\", no plugin name, no plugin ID)\n- Settings and configuration best practices\n\n### [File & Vault Operations](reference\u002Ffile-operations.md)\n- View access patterns\n- Editor vs Vault API\n- Atomic file operations\n- File management\n- Path handling\n\n### [CSS Styling Best Practices](reference\u002Fcss-styling.md)\n- Avoiding inline styles\n- Using Obsidian CSS variables\n- Scoping plugin styles\n- Theme support\n- Spacing and layout\n\n### [Accessibility (A11y)](reference\u002Faccessibility.md)\n- Keyboard navigation (MANDATORY)\n- ARIA labels and roles (MANDATORY)\n- Tooltips and accessibility\n- Focus management (MANDATORY)\n- Focus visible styles (MANDATORY)\n- Screen reader support (MANDATORY)\n- Mobile and touch accessibility (MANDATORY)\n- Accessibility checklist\n\n### [Code Quality & Best Practices](reference\u002Fcode-quality.md)\n- Removing sample code\n- Security best practices\n- Platform compatibility\n- API usage best practices\n- Async\u002Fawait patterns\n- DOM helpers\n\n### [Plugin Submission Requirements](reference\u002Fsubmission.md)\n- Repository structure\n- Submission process\n- Semantic versioning\n- Testing checklist\n- Additional resources and important notes\n\n---\n\n## Plugin Submission Validation Workflow\n\nBefore submitting a plugin, follow this sequence:\n\n1. **Run ESLint** — `npx eslint .` using `eslint-plugin-obsidianmd`; fix all errors (warnings are informational)\n2. **Validate manifest** — Confirm `id`, `name`, `description`, `version`, and `minAppVersion` meet naming and formatting rules (rules 1–5)\n3. **Check LICENSE** — Copyright holder must not be \"Dynalist Inc.\" and the year must be current\n4. **Test on mobile** — Verify no regex lookbehind, no `fetch()`, and touch targets ≥ 44×44px (skip only if plugin is declared desktop-only)\n5. **Keyboard accessibility audit** — Tab through all interactive elements; confirm focus indicators and ARIA labels are present\n6. **Submit** — Open a PR to the community plugins repository with the updated `manifest.json` and `community-plugins.json` entry\n\nIf ESLint reports new errors after fixing, re-run from step 1.\n\n---\n\n## When Reviewing\u002FWriting Code\n\nUse this checklist for code review and implementation:\n\n1. **Memory management**: Are components and views properly managed?\n2. **Type safety**: Using `instanceof` instead of casts?\n3. **UI text**: Is everything in sentence case?\n4. **Command naming**: No redundant words?\n5. **File operations**: Using preferred APIs?\n6. **Mobile compatibility**: No iOS-incompatible features?\n7. **Sample code**: Removed all boilerplate?\n8. **Manifest**: Correct version, valid structure?\n9. **Accessibility**: Keyboard navigation, ARIA labels, focus indicators?\n10. **Testing**: Can you use the plugin without a mouse?\n11. **Touch targets**: Are all interactive elements at least 44×44px?\n12. **Focus styles**: Using `:focus-visible` and proper CSS variables?\n\n---\n\n## Common Patterns\n\n### Proper Command Registration\n\n```typescript\n\u002F\u002F ✅ CORRECT\nthis.addCommand({\n  id: 'insert-timestamp',\n  name: 'Insert timestamp',\n  editorCallback: (editor: Editor, view: MarkdownView) => {\n    editor.replaceSelection(new Date().toISOString());\n  }\n});\n```\n\n### Safe Type Narrowing\n\n```typescript\n\u002F\u002F ✅ CORRECT\nconst file = this.app.vault.getAbstractFileByPath(path);\nif (file instanceof TFile) {\n  \u002F\u002F TypeScript now knows it's a TFile\n  await this.app.vault.read(file);\n}\n```\n\n### Keyboard Accessible Button\n\n```typescript\n\u002F\u002F ✅ CORRECT\nconst button = containerEl.createEl('button', {\n  attr: {\n    'aria-label': 'Open settings',\n    'data-tooltip-position': 'top'\n  }\n});\nbutton.setText('⚙️');\n\nbutton.addEventListener('keydown', (e) => {\n  if (e.key === 'Enter' || e.key === ' ') {\n    e.preventDefault();\n    performAction();\n  }\n});\n```\n\n### Themed CSS\n\n```css\n\u002F* ✅ CORRECT *\u002F\n.my-plugin-modal {\n  background: var(--modal-background);\n  color: var(--text-normal);\n  padding: var(--size-4-4);\n  border-radius: var(--radius-m);\n  font-size: var(--font-ui-medium);\n}\n\n.my-plugin-button:focus-visible {\n  outline: 2px solid var(--interactive-accent);\n  outline-offset: 2px;\n}\n```\n\n---\n\nWhen helping with Obsidian plugin development, proactively apply these rules and suggest improvements based on these guidelines. Refer to the detailed reference files for comprehensive information on specific topics.\n\n","# Obsidian Plugin Development Guidelines\n\nFollow these comprehensive guidelines derived from the official Obsidian ESLint plugin rules, submission requirements, and best practices.\n\n## Getting Started\n\n### Quick Start Tool\n\nFor new plugin projects, an interactive boilerplate generator is available:\n- **Script**: `tools\u002Fcreate-plugin.js` in the skill repository\n- **Command**: Invoke `create-plugin` using your agent's method (`\u002Fcreate-plugin`, `$create-plugin`, or `@create-plugin`)\n- Generates minimal, best-practice boilerplate with no sample code\n- Detects existing projects and only adds missing files\n\nRecommend the boilerplate generator when users ask how to create a new plugin, want to start a new project, or need help setting up the basic structure.\n\n---\n\n## Rules Reference (eslint-plugin-obsidianmd v0.1.9)\n\n### Submission & Naming\n| # | Rule | ✅ Do | ❌ Don't |\n|---|------|--------|----------|\n| 1 | Plugin ID | Omit \"obsidian\"; don't end with \"plugin\" | Include \"obsidian\" or end with \"plugin\" |\n| 2 | Plugin name | Omit \"Obsidian\"; don't end with \"Plugin\" | Include \"Obsidian\" or end with \"Plugin\" |\n| 3 | Plugin name | Don't start with \"Obsi\" or end with \"dian\" | Start with \"Obsi\" or end with \"dian\" |\n| 4 | Description | Omit \"Obsidian\", \"This plugin\", etc. | Use \"Obsidian\" or \"This plugin\" |\n| 5 | Description | End with `.?!)` punctuation | Leave description without terminal punctuation |\n\n### Memory & Lifecycle\n| # | Rule | ✅ Do | ❌ Don't |\n|---|------|--------|----------|\n| 6 | Event cleanup | Use `registerEvent()` for automatic cleanup | Register events without cleanup |\n| 7 | View references | Return views\u002Fcomponents directly | Store view references in plugin properties or pass plugin as component to `MarkdownRenderer` |\n\n### Type Safety\n| # | Rule | ✅ Do | ❌ Don't |\n|---|------|--------|----------|\n| 8 | TFile\u002FTFolder | Use `instanceof` for type checking | Cast to TFile\u002FTFolder; use `any`; use `var` |\n\n### UI\u002FUX\n| # | Rule | ✅ Do | ❌ Don't |\n|---|------|--------|----------|\n| 9 | UI text | Sentence case — \"Advanced settings\" | Title Case — \"Advanced Settings\" |\n| 10 | JSON locale | Sentence case in JSON locale files (`recommendedWithLocalesEn`) | Title case in locale JSON |\n| 11 | TS\u002FJS locale | Sentence case in TS\u002FJS locale modules | Title case in locale modules |\n| 12 | Command names | Omit \"command\" in command names\u002FIDs | Include \"command\" in names\u002FIDs |\n| 13 | Command IDs | Omit plugin ID\u002Fname from command IDs\u002Fnames | Duplicate plugin ID in command IDs |\n| 14 | Hotkeys | No default hotkeys | Set default hotkeys |\n| 15 | Settings headings | Use `.setHeading()` | Create manual HTML headings; use \"General\", \"settings\", or plugin name in headings |\n\n### API Best Practices\n| # | Rule | ✅ Do | ❌ Don't |\n|---|------|--------|----------|\n| 16 | Active file edits | Use Editor API | Use `Vault.modify()` for active file edits |\n| 17 | Background file mods | Use `Vault.process()` | Use `Vault.modify()` for background modifications |\n| 18 | User paths | Use `normalizePath()` | Hardcode `.obsidian` path; use raw user paths |\n| 19 | OS detection | Use `Platform` API | Use `navigator.platform`\u002F`userAgent` |\n| 20 | Network requests | Use `requestUrl()` | Use `fetch()` |\n| 21 | Logging | Minimize console logging; none in `onload`\u002F`onunload` in production | Use `console.log` in `onload`\u002F`onunload` |\n\n### Styling\n| # | Rule | ✅ Do | ❌ Don't |\n|---|------|--------|----------|\n| 22 | CSS variables | Use Obsidian CSS variables for all styling | Hardcode colors, sizes, or spacing |\n| 23 | CSS scope | Scope CSS to plugin containers | Use broad CSS selectors |\n| 24 | Style elements | Use `styles.css` file (`no-forbidden-elements`) | Create `\u003Clink>` or `\u003Cstyle>` elements; assign styles via JavaScript |\n\n### Accessibility (MANDATORY)\n| # | Rule | ✅ Do | ❌ Don't |\n|---|------|--------|----------|\n| 25 | Keyboard access | Make all interactive elements keyboard accessible; Tab through all elements | Create inaccessible interactive elements |\n| 26 | ARIA labels | Provide ARIA labels for icon buttons; use `data-tooltip-position` for tooltips | Use icon buttons without ARIA labels |\n| 27 | Focus indicators | Use `:focus-visible` with Obsidian CSS variables; touch targets ≥ 44×44px | Remove focus indicators; make touch targets \u003C 44×44px |\n\n### Security & Compatibility\n| Rule | ✅ Do | ❌ Don't |\n|------|--------|----------|\n| DOM safety | Use Obsidian DOM helpers (`createDiv()`, `createSpan()`, `createEl()`) | Use `innerHTML`\u002F`outerHTML` or `document.createElement` |\n| iOS compat | Avoid regex lookbehind (iOS \u003C 16.4 incompatibility) | Use regex lookbehind |\n\n### Code Quality\n| Rule | ✅ Do | ❌ Don't |\n|------|--------|----------|\n| Sample code | Remove all sample\u002Ftemplate code | Keep class names like MyPlugin, SampleModal |\n| Object.assign | `Object.assign({}, defaults, overrides)` (`object-assign`) | `Object.assign(defaultsVar, other)` — mutates defaults |\n| LICENSE | Copyright holder must not be \"Dynalist Inc.\"; year must be current (`validate-license`) | Leave \"Dynalist Inc.\" as holder or use an outdated year |\n| Async | Use async\u002Fawait | Use Promise chains |\n\n---\n\n## Detailed Guidelines\n\nFor comprehensive information on specific topics, see the reference files:\n\n### [Memory Management & Lifecycle](reference\u002Fmemory-management.md)\n- Using `registerEvent()`, `addCommand()`, `registerDomEvent()`, `registerInterval()`\n- Avoiding view references in plugin\n- Not using plugin as component\n- Proper leaf cleanup\n\n### [Type Safety](reference\u002Ftype-safety.md)\n- Using `instanceof` instead of type casting\n- Avoiding `any` type\n- Using `const` and `let` over `var`\n\n### [UI\u002FUX Standards](reference\u002Fui-ux.md)\n- Sentence case enforcement (TypeScript, JSON locale, TS\u002FJS locale modules)\n- `recommendedWithLocalesEn` config for locale file checks\n- Command naming conventions (no \"command\", no plugin name, no plugin ID)\n- Settings and configuration best practices\n\n### [File & Vault Operations](reference\u002Ffile-operations.md)\n- View access patterns\n- Editor vs Vault API\n- Atomic file operations\n- File management\n- Path handling\n\n### [CSS Styling Best Practices](reference\u002Fcss-styling.md)\n- Avoiding inline styles\n- Using Obsidian CSS variables\n- Scoping plugin styles\n- Theme support\n- Spacing and layout\n\n### [Accessibility (A11y)](reference\u002Faccessibility.md)\n- Keyboard navigation (MANDATORY)\n- ARIA labels and roles (MANDATORY)\n- Tooltips and accessibility\n- Focus management (MANDATORY)\n- Focus visible styles (MANDATORY)\n- Screen reader support (MANDATORY)\n- Mobile and touch accessibility (MANDATORY)\n- Accessibility checklist\n\n### [Code Quality & Best Practices](reference\u002Fcode-quality.md)\n- Removing sample code\n- Security best practices\n- Platform compatibility\n- API usage best practices\n- Async\u002Fawait patterns\n- DOM helpers\n\n### [Plugin Submission Requirements](reference\u002Fsubmission.md)\n- Repository structure\n- Submission process\n- Semantic versioning\n- Testing checklist\n- Additional resources and important notes\n\n---\n\n## Plugin Submission Validation Workflow\n\nBefore submitting a plugin, follow this sequence:\n\n1. **Run ESLint** — `npx eslint .` using `eslint-plugin-obsidianmd`; fix all errors (warnings are informational)\n2. **Validate manifest** — Confirm `id`, `name`, `description`, `version`, and `minAppVersion` meet naming and formatting rules (rules 1–5)\n3. **Check LICENSE** — Copyright holder must not be \"Dynalist Inc.\" and the year must be current\n4. **Test on mobile** — Verify no regex lookbehind, no `fetch()`, and touch targets ≥ 44×44px (skip only if plugin is declared desktop-only)\n5. **Keyboard accessibility audit** — Tab through all interactive elements; confirm focus indicators and ARIA labels are present\n6. **Submit** — Open a PR to the community plugins repository with the updated `manifest.json` and `community-plugins.json` entry\n\nIf ESLint reports new errors after fixing, re-run from step 1.\n\n---\n\n## When Reviewing\u002FWriting Code\n\nUse this checklist for code review and implementation:\n\n1. **Memory management**: Are components and views properly managed?\n2. **Type safety**: Using `instanceof` instead of casts?\n3. **UI text**: Is everything in sentence case?\n4. **Command naming**: No redundant words?\n5. **File operations**: Using preferred APIs?\n6. **Mobile compatibility**: No iOS-incompatible features?\n7. **Sample code**: Removed all boilerplate?\n8. **Manifest**: Correct version, valid structure?\n9. **Accessibility**: Keyboard navigation, ARIA labels, focus indicators?\n10. **Testing**: Can you use the plugin without a mouse?\n11. **Touch targets**: Are all interactive elements at least 44×44px?\n12. **Focus styles**: Using `:focus-visible` and proper CSS variables?\n\n---\n\n## Common Patterns\n\n### Proper Command Registration\n\n```typescript\n\u002F\u002F ✅ CORRECT\nthis.addCommand({\n  id: 'insert-timestamp',\n  name: 'Insert timestamp',\n  editorCallback: (editor: Editor, view: MarkdownView) => {\n    editor.replaceSelection(new Date().toISOString());\n  }\n});\n```\n\n### Safe Type Narrowing\n\n```typescript\n\u002F\u002F ✅ CORRECT\nconst file = this.app.vault.getAbstractFileByPath(path);\nif (file instanceof TFile) {\n  \u002F\u002F TypeScript now knows it's a TFile\n  await this.app.vault.read(file);\n}\n```\n\n### Keyboard Accessible Button\n\n```typescript\n\u002F\u002F ✅ CORRECT\nconst button = containerEl.createEl('button', {\n  attr: {\n    'aria-label': 'Open settings',\n    'data-tooltip-position': 'top'\n  }\n});\nbutton.setText('⚙️');\n\nbutton.addEventListener('keydown', (e) => {\n  if (e.key === 'Enter' || e.key === ' ') {\n    e.preventDefault();\n    performAction();\n  }\n});\n```\n\n### Themed CSS\n\n```css\n\u002F* ✅ CORRECT *\u002F\n.my-plugin-modal {\n  background: var(--modal-background);\n  color: var(--text-normal);\n  padding: var(--size-4-4);\n  border-radius: var(--radius-m);\n  font-size: var(--font-ui-medium);\n}\n\n.my-plugin-button:focus-visible {\n  outline: 2px solid var(--interactive-accent);\n  outline-offset: 2px;\n}\n```\n\n---\n\nWhen helping with Obsidian plugin development, proactively apply these rules and suggest improvements based on these guidelines. Refer to the detailed reference files for comprehensive information on specific topics.\n\n",{"name":5,"license":15,"metadata":51,"description":7},{"version":52},"1.4.0","def8531e65114c0fca8fb8551c1871ee0eed705c","skills\u002Fgapmiss\u002Fobsidian-plugin-skill\u002Fobsidian",24,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub","https:\u002F\u002Fskillshub.wtf","https:\u002F\u002Fgithub.com\u002FComeOnOliver","AI & Blockchain Engineer\r\n","2026-06-24 22:07:19",{"source":8,"skillName":5,"commands":62,"prompt":72,"zipAvailable":73},[63,66,69],{"tool":64,"command":65},"npx","npx skills add ComeOnOliver\u002Fskillshub --skill obsidian",{"tool":67,"command":68},"bunx","bunx skills add ComeOnOliver\u002Fskillshub --skill obsidian",{"tool":70,"command":71},"pnpm","pnpm dlx skills add ComeOnOliver\u002Fskillshub --skill obsidian","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fgapmiss\u002Fobsidian-plugin-skill\u002Fobsidian\u002FSKILL.md",true,[],[76,90,100,109,115,122],{"skillNo":77,"name":78,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":79,"categories":80,"keywords":86,"publishedAt":46},"01M6FKJN13GMG4KY57J3QD8GY5","model-hierarchy-skill","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzscole\u002Fmodel-hierarchy-skill\u002Fmodel-hierarchy-skill\u002FSKILL.md",[81],{"categoryNo":82,"categoryName":83,"taxonomyType":84,"taxonomyTypeDesc":85},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[87,88,89],"model","model hierarchy","model hierarchy skill",{"skillNo":91,"name":92,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":93,"categories":94,"keywords":96,"publishedAt":46},"01M6FKJJJ6GCAMR4PPPRD12M5X","zoom-mcp","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-mcp\u002FSKILL.md",[95],{"categoryNo":82,"categoryName":83,"taxonomyType":84,"taxonomyTypeDesc":85},[97,98,99],"MCP","zoom","zoom mcp",{"skillNo":101,"name":102,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":103,"categories":104,"keywords":105,"publishedAt":46},"01M6FKJE3KYT3KPEZMEEHYKA6K","zoom-apps-sdk","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-apps-sdk\u002FSKILL.md",[],[106,107,108,98],"sdk","zoom apps sdk","apps",{"skillNo":110,"name":111,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":112,"categories":113,"keywords":114,"publishedAt":46},"01M6FKJ9EDH9Q5VWDF8HAJRSP0","windows","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwindows\u002FSKILL.md",[],[111],{"skillNo":116,"name":117,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":118,"categories":119,"keywords":121,"publishedAt":46},"01M6FKJ666BEEFC3AE9GJ72Q93","whiteboard","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwhiteboard\u002FSKILL.md",[120],{"categoryNo":82,"categoryName":83,"taxonomyType":84,"taxonomyTypeDesc":85},[117],{"skillNo":123,"name":124,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":125,"categories":126,"keywords":127,"publishedAt":46},"01M6FKJ21WFCDGTJQ2Q91T9DVW","websockets","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwebsockets\u002FSKILL.md",[],[124]]