Use when developing or modifying the @handsontable/angular-wrapper package - Angular components with decorators, NgZone performance optimization, and ng-packagr build system
日本語の概要は準備中です。原文の説明を表示しています。
Use when creating a new Handsontable plugin, modifying an existing plugin's behavior, adding hooks or options to a plugin, or working with the plugin lifecycle (enablePlugin, disablePlugin, updatePlugin). Covers the full plugin contract, conflict registration, settings validation, and IndexMapper integration.
インストールする前に、エージェントに与えられる指示の中身を確認できます。
src/plugins/{pluginName}/
├── index.ts # Re-exports PLUGIN_KEY, PLUGIN_PRIORITY, ClassName
├── {pluginName}.ts # Main class extending BasePlugin
├── AGENTS.md # Plugin knowledge file, REQUIRED (see below)
├── CLAUDE.md # symlink -> AGENTS.md, never a copy
├── types.ts # (optional) exported plugin-local types
├── __tests__/ # *.unit.js unit tests (new E2E is Playwright, in tests/e2e/)
└── {submodules}/ # UI classes, strategies, etc.
Statics (PLUGIN_KEY, PLUGIN_PRIORITY, SETTING_KEYS, PLUGIN_DEPS, DEFAULT_SETTINGS, SETTINGS_VALIDATORS), lifecycle order, onUpdateSettings, hard conflicts, and the PLUGIN_PRIORITY table: src/plugins/base/AGENTS.md. Pick a free priority number from that table and add your row to it.
src/plugins/pagination/pagination.ts)# prefix for all internal state, no @private JSDoc.#on* handlers to addHook as arrow function class fields, directly: this.addHook('afterLoadData', this.#onAfterLoadData) (priority stays the 3rd arg). .bind(this) builds a new function per call, so the registered reference can never be removed. Existing inline-wrapper sites work; leave them as they are.this.addHook() auto-cleans on disablePlugin(); this.hot.addHook() does not. Register new hook names at module level: Hooks.getSingleton().register('beforeMyAction'); (import Hooks from '../../core/hooks').this.getSetting('key') (dot notation supported); defaults come from DEFAULT_SETTINGS.this.isHardConflictBlocked() in enablePlugin():
import { registerConflict } from '../base/conflictRegistry';
registerConflict(PLUGIN_KEY, ['nestedRows', 'mergeCells']);
enablePlugin(), unregister in disablePlugin():
this.#map = this.hot.rowIndexMapper.createAndRegisterIndexMap(this.pluginName, 'hiding', false);
// 'hiding' = HidingMap (not rendered, stays in DataMap); 'trimming' = TrimmingMap (removed from DataMap)
hot reference). Swappable logic uses a strategy (e.g. autoPageSize vs fixedPageSize).this.hot.batch(() => { ... }) for one render, but never around code that can run host code (updateSettings, loadData, another plugin's hooks). Core#batch/batchRender do not resume in a finally, so a listener that throws mid-batch leaves the grid render-suspended for good. For such code, call this.hot.suspendRender() and put this.hot.resumeRender() in a finally.suspendRender() + try/finally resumeRender()), and run the swap directly when this.hot.view does not exist yet (initial setup). Reference: src/plugins/sheetsBar/sheetsBar.ts (#applySheet). A plugin that also owns a layout slot (handsontable/AGENTS.md "Wrapper UI placement") registers its UI once in enablePlugin(); this method owns only the settings/data swap.hot.getPlugin('{Name}'), never imports; no circular dependencies between plugins.afterDataProviderFetchError and afterRowsMutationError. Built-in DataProvider toasts: src/plugins/dataProvider/AGENTS.md.index.ts: export { PLUGIN_KEY, PLUGIN_PRIORITY, ClassName } from './pluginName';src/plugins/index.ts.src/dataMap/metaManager/metaSchema.ts.src/core/settings.ts (GridSettings); npm run build:types regenerates the public .d.ts files into tmp/.AGENTS.md and symlink CLAUDE.md to it with the relative target, so the link resolves in a git worktree:
cd handsontable/src/plugins/myPlugin && ln -s AGENTS.md CLAUDE.md
Git stores it as mode 120000; a regular file in git status means you copied.PLUGIN_PRIORITY row to the table in src/plugins/base/AGENTS.md, in the same change: nothing enforces it, so it drifts silently.A plugin with UI (buttons, inputs, navigation bars) integrates with src/focusManager/. Reference: Pagination (#registerFocusScope / #unregisterFocusScope).
shortcutsContextName, and only that context runs, so an empty one kills every grid shortcut while your UI is up (emptyDataState shipped that way; DEV-53). An overlay the user still thinks of as "the grid underneath" adds fallbackShortcutsContextName: 'grid' to registerScope() and inherits every grid shortcut, including ones added later (reference: emptyDataState). A modal scope leaves it unset: letting grid shortcuts through a modal is the bug. Register a shortcut in your own context only to OVERRIDE one, and record in your AGENTS.md why the grid's version does not fit.coversGridBody: true. Inheriting and covering are separate questions. Without the flag, a content-writing shortcut sees that the grid still draws cells and lets Delete reach data the user cannot touch (DEV-2917: emptyDataState covered a rendered grid during a DataProvider fetch and the dataset was wiped). The flag is read from every ENABLED scope, not only the active one.colspan/rowspan from hot.getCellMeta(row, col) (set by MergeCells via afterGetCellMeta), not from DOM attributes; the meta is available regardless of viewport state.Every plugin directory carries an AGENTS.md with CLAUDE.md symlinked to it; a new plugin without one is incomplete. Edit AGENTS.md, never CLAUDE.md.
It answers "what must I never get wrong here, and where do I look next": the reason a guard exists, the load-bearing ordering, the shortcut that looks right and is wrong. Required sections:
# {PluginName} plugin – {one-line focus}, then one or two sentences naming the files covered and saying "Read this before touching X." (10 pre-existing files lack ## Where to look next or ## Testing; new files carry both, and fixing those 10 belongs in its own change.)afterSelection read the stale value" survives; the bare rule gets undone.## Where to look next: sibling plugins, the .ai/ reference, ../base/AGENTS.md for the contract, and this skill for the workflow.## Testing: targeted commands and anything non-obvious about the suite (a __tests__/ split, a spec failing for an unrelated reason, a case needing a real device).Length follows the plugin: stretchColumns/AGENTS.md (three bullets) and base/AGENTS.md (the contract for every plugin) are the two ends; read one of each before writing yours.
Where a fact belongs. A trap found while changing an existing plugin goes in that plugin's AGENTS.md. handsontable/AGENTS.md holds rules that span plugins; for a core-wide rule with one worked example, state the rule there and put the example in the plugin file with a pointer back.
tests/e2e/ (skill handsontable-playwright-e2e) for a plugin's UI, interaction, and rendering. The presence gate blocks new legacy *.spec.js; editing an existing one (async it() callbacks) is fine.__tests__/*.unit.js): strategies and helpers in isolation.updateSettings(), enablePlugin()/disablePlugin() toggling, and interactions with other plugins (sorting, filters, hidden rows).Base class: src/plugins/base/base.ts. Deeper context: handsontable/.ai/ARCHITECTURE.md, handsontable/.ai/CONVENTIONS.md.
まだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
Use when developing or modifying the @handsontable/angular-wrapper package - Angular components with decorators, NgZone performance optimization, and ng-packagr build system
日本語の概要は準備中です。原文の説明を表示しています。
Use when testing a local or public URL on a real device via BrowserStack, opening a live browser session on Android, iOS, or desktop browsers, or when the user mentions BrowserStack, real device testing, mobile testing, cross-browser testing, touch testing, or wants to verify behavior on a specific phone, tablet, or browser version. Also trigger when the user says "test this on Android/iPhone/Safari/Chrome mobile", asks to open a live session, or mentions demo-mobile.html, even if they don't mention BrowserStack by name.
日本語の概要は準備中です。原文の説明を表示しています。
Use when a source code change needs a changelog entry, or before committing and pushing any bug fix, feature, or behavior change to source code - detecting when entries are required, categorizing changes correctly (added/changed/fixed/deprecated/removed/security), writing user-facing titles, and creating the JSON entry in .changelogs/
日本語の概要は準備中です。原文の説明を表示しています。
Query the pre-built code-review-graph knowledge graph (Tree-sitter, whole monorepo) instead of walking call chains with Grep+Read – 2-6x cheaper, and it catches dynamic dispatch that grep misses. Use for ANY task that spans multiple files, even when the user never mentions a graph or asks for it - fixing or tracing a bug ("fix this bug", "why does X happen", "trace this"), exploring unfamiliar code ("how does X work", "who calls X", "what imports Y", "where is X used or handled"), planning or doing a refactor ("rename X", "is it safe to change or remove X", "what would break", "blast radius", "find dead code"), or reviewing changes ("review this PR", "review this branch or diff"). When you are about to Grep for a symbol's callers, callees, or importers, use this skill.
日本語の概要は準備中です。原文の説明を表示しています。
Use when working with row or column indexes in Handsontable - translating between physical, visual, and renderable coordinates, using IndexMapper, or debugging index-related bugs where rows or columns appear in wrong positions
日本語の概要は準備中です。原文の説明を表示しています。
Use when creating code examples for documentation pages - JavaScript, TypeScript, React, Angular, and Vue variants with proper imports, registration, and license key
日本語の概要は準備中です。原文の説明を表示しています。