Skip to content

Commit 5a898a6

Browse files
gohaberegclaude
andcommitted
chore(openspec): archive add-plugin-public-api
Folds the change's delta specs into openspec/specs/ (+11 added, ~4 modified) and moves it to changes/archive/2026-08-26-add-plugin-public-api/. Two corrections were needed first, both surfaced by the merge of main: - The keyboard-shortcuts delta was filed under `core`, but #180 extracted that requirement into its own `shortcuts-plugin` capability. The fold matches requirements by header text within the target capability, so archiving as-is would have silently appended a duplicate to core/spec.md instead of updating the intended requirement. - As a MODIFIED block the delta replaces the whole requirement, and it carried only 3 of the 7 existing scenarios — dropping the IME, first-match, IndexError, destroy and non-string-shortcut cases, all still true and still tested. The capability's Purpose is updated in this commit rather than left to the fold, which rewrites requirement blocks only. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 05a9a98 commit 5a898a6

16 files changed

Lines changed: 307 additions & 45 deletions

File tree

openspec/changes/add-plugin-public-api/.openspec.yaml renamed to openspec/changes/archive/2026-08-26-add-plugin-public-api/.openspec.yaml

File renamed without changes.

openspec/changes/add-plugin-public-api/design.md renamed to openspec/changes/archive/2026-08-26-add-plugin-public-api/design.md

File renamed without changes.

openspec/changes/add-plugin-public-api/proposal.md renamed to openspec/changes/archive/2026-08-26-add-plugin-public-api/proposal.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@ Plugins registered through `core.use()` are currently write-only participants: t
2323
### Modified Capabilities
2424
- `sdk`: the plugin contract requirement changes — `EditorjsPluginConstructor` gains the static `name`, `EditorjsPlugin` gains the optional public-API declaration, `EditorAPI` gains the `plugins` namespace, and `BaseToolOptions` gains the `plugins` key.
2525
- `ui`: the blocks-holder requirement changes — `BlocksUI` delegates `keydown` as a `KeydownUIEvent` and yields to plugins that claim a key before applying its own undo/redo handling.
26-
- `core`: the keyboard-shortcuts requirement changes — `ShortcutsPlugin` sources shortcuts from `options.plugins.shortcuts` rather than `options.shortcut`, and exposes a runtime registration API. `Core` additionally builds and owns the plugin registry that backs `api.plugins`.
26+
- `core`: `Core` builds and owns the plugin registry that backs `api.plugins`.
27+
- `shortcuts-plugin`: the keyboard-shortcuts requirement changes — `ShortcutsPlugin` sources shortcuts from `options.plugins.shortcuts` rather than `options.shortcut`, and exposes a runtime registration API. The capability's `## Purpose` paragraph is updated in this change directly, since the mechanical fold rewrites requirement blocks only and leaves surrounding prose untouched.
2728

2829
## Impact
2930

30-
- **Code**: `packages/sdk/src/entities/EditorjsPlugin.ts`, `BaseTool.ts`, `packages/sdk/src/api/EditorAPI.ts` (new `PluginsAPI.ts`), `packages/sdk/src/tools/facades/BaseToolFacade.ts`; `packages/core/src/index.ts` (plugin instantiation + registry), `packages/core/src/api/` (new `PluginsAPI`), `packages/core/src/plugins/ShortcutsPlugin.ts`; `packages/tools/{bold,italic,inline-link}`; `packages/plugins/clipboard-plugin` and `packages/dom-adapters` gain a `name`; `packages/ui/src/Blocks/Blocks.ts` gains the `KeydownUIEvent` dispatch.
31+
- **Code**: `packages/sdk/src/entities/EditorjsPlugin.ts`, `BaseTool.ts`, `packages/sdk/src/api/EditorAPI.ts` (new `PluginsAPI.ts`), `packages/sdk/src/tools/facades/BaseToolFacade.ts`; `packages/core/src/index.ts` (plugin instantiation + registry), `packages/core/src/api/` (new `PluginsAPI`), `packages/plugins/shortcuts-plugin/src/index.ts`; `packages/tools/{bold,italic,inline-link}`; `packages/plugins/clipboard-plugin` and `packages/dom-adapters` gain a `name`; `packages/ui/src/Blocks/Blocks.ts` gains the `KeydownUIEvent` dispatch.
3132
- **APIs**: additive for `EditorAPI`; breaking for plugin constructors (static `name`) and for tools declaring a flat `shortcut`.
3233
- **Ordering**: plugin instantiation currently happens before tools are prepared, and plugins receive the `EditorAPI` in their constructor — the registry must therefore be populated lazily enough that a plugin constructed first can still reach a plugin constructed later. Addressed in design.md.
3334
- **Docs**: `docs/plugins.md` (Registration, EditorAPI, Lifecycle sections) is superseded in part and must be updated; `docs/diagrams/plugin-lifecycle-flow.mmd` gains the registry step.

openspec/changes/add-plugin-public-api/specs/core/spec.md renamed to openspec/changes/archive/2026-08-26-add-plugin-public-api/specs/core/spec.md

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,3 @@
1-
## MODIFIED Requirements
2-
3-
### Requirement: Keyboard shortcuts plugin
4-
The system SHALL provide a `ShortcutsPlugin` (an `EditorjsPlugin` with `name` `shortcuts`) that maps keyboard shortcuts declared in a tool's `options.plugins.shortcuts` to inline-tool application through the `EditorAPI`, and that exposes a public API for registering and unregistering shortcuts at runtime.
5-
6-
#### Scenario: Triggering an inline tool via shortcut
7-
- **GIVEN** an inline tool is registered with `options.plugins.shortcuts.shortcut` set to a key combination (e.g. `CMD+B`)
8-
- **WHEN** that key combination is pressed while the editor has focus
9-
- **THEN** `ShortcutsPlugin` applies the corresponding inline tool to the current selection via the `EditorAPI`
10-
11-
#### Scenario: Legacy flat shortcut key is ignored
12-
- **GIVEN** a tool declares a flat `options.shortcut` and no `options.plugins.shortcuts`
13-
- **WHEN** that key combination is pressed
14-
- **THEN** no inline tool is applied, since shortcuts are sourced only from the namespaced key
15-
16-
#### Scenario: Registering a shortcut at runtime
17-
- **GIVEN** an integrator holds `api.plugins.shortcuts`
18-
- **WHEN** a shortcut and handler are registered through that public API
19-
- **THEN** pressing the shortcut invokes the handler, and unregistering it through the same API stops further invocations
20-
21-
Implemented in `src/plugins/ShortcutsPlugin.ts`.
22-
231
## ADDED Requirements
242

253
### Requirement: Plugin registry

openspec/changes/add-plugin-public-api/specs/plugin-public-api/spec.md renamed to openspec/changes/archive/2026-08-26-add-plugin-public-api/specs/plugin-public-api/spec.md

File renamed without changes.

openspec/changes/add-plugin-public-api/specs/sdk/spec.md renamed to openspec/changes/archive/2026-08-26-add-plugin-public-api/specs/sdk/spec.md

File renamed without changes.
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
## MODIFIED Requirements
2+
3+
### Requirement: Keyboard shortcuts plugin
4+
The system SHALL provide a `ShortcutsPlugin` (an `EditorjsPlugin` with `name` `shortcuts`) that, on construction, subscribes to the `core:tool:loaded` and `ui:key-down` events, registers the string `shortcut` each loaded tool addresses to it under `options.plugins.shortcuts`, applies the matching inline tool to the current selection via the `EditorAPI` when its shortcut is pressed, and exposes a public API for registering and unregistering shortcuts at runtime. Tool-declared and API-registered shortcuts SHALL share one table, so a shortcut always resolves to exactly one handler.
5+
6+
#### Scenario: Registering a shortcut from a loaded tool
7+
- **GIVEN** a tool is loaded whose `options.plugins.shortcuts.shortcut` is a string (e.g. `CMD+B`)
8+
- **WHEN** the `core:tool:loaded` event fires
9+
- **THEN** the plugin registers a handler for that shortcut string which applies the tool
10+
11+
#### Scenario: Ignoring tools without a string shortcut
12+
- **GIVEN** a tool is loaded that addresses no slice to the `shortcuts` plugin, or whose `options.plugins.shortcuts.shortcut` is absent or is not a string
13+
- **WHEN** the `core:tool:loaded` event fires
14+
- **THEN** the plugin registers no shortcut for that tool
15+
16+
#### Scenario: Legacy flat shortcut key is ignored
17+
- **GIVEN** a tool declares a flat `options.shortcut` and no `options.plugins.shortcuts`
18+
- **WHEN** that key combination is pressed
19+
- **THEN** no inline tool is applied, since shortcuts are sourced only from the namespaced key
20+
21+
#### Scenario: Triggering an inline tool via shortcut
22+
- **GIVEN** an inline tool is registered with `options.plugins.shortcuts.shortcut` set to a key combination (e.g. `CMD+B`)
23+
- **WHEN** that key combination is pressed while the editor has focus
24+
- **THEN** the plugin prevents the native event's default action and applies the corresponding inline tool to the current selection via `api.selection.applyInlineTool`
25+
26+
#### Scenario: Registering a shortcut at runtime
27+
- **GIVEN** an integrator holds `api.plugins.shortcuts`
28+
- **WHEN** a shortcut and handler are registered through that public API
29+
- **THEN** pressing the shortcut invokes the handler, and unregistering it through the same API stops further invocations
30+
31+
#### Scenario: Matching only the first registered shortcut
32+
- **GIVEN** more than one registered shortcut would match the keydown
33+
- **WHEN** the `ui:key-down` event fires
34+
- **THEN** the plugin applies only the first matching tool and stops
35+
36+
#### Scenario: Ignoring keydown during IME composition
37+
- **GIVEN** the native keydown event has `isComposing === true`
38+
- **WHEN** the `ui:key-down` event fires
39+
- **THEN** the plugin performs no matching and leaves the native event untouched
40+
41+
#### Scenario: Tolerating a missing caret when applying a tool
42+
- **GIVEN** applying the inline tool throws an `IndexError` (e.g. no caret in a text input)
43+
- **WHEN** a matching shortcut is dispatched
44+
- **THEN** the plugin swallows the error and leaves the editor unchanged, while any other error propagates
45+
46+
#### Scenario: Releasing shortcuts on destroy
47+
- **GIVEN** a `ShortcutsPlugin` instance has registered shortcuts
48+
- **WHEN** `destroy()` is called
49+
- **THEN** it clears the registered shortcuts so subsequent keydowns dispatch nothing
50+
51+
Shortcuts for block tools and block tunes (a `shortcuts` map under `options.plugins.shortcuts`) are reserved for future work and not yet implemented.
52+
53+
Implemented in `src/index.ts`, validated by its co-located `.spec.ts`.

openspec/changes/add-plugin-public-api/specs/tool-plugin-options/spec.md renamed to openspec/changes/archive/2026-08-26-add-plugin-public-api/specs/tool-plugin-options/spec.md

File renamed without changes.

openspec/changes/add-plugin-public-api/specs/ui/spec.md renamed to openspec/changes/archive/2026-08-26-add-plugin-public-api/specs/ui/spec.md

File renamed without changes.

openspec/changes/add-plugin-public-api/tasks.md renamed to openspec/changes/archive/2026-08-26-add-plugin-public-api/tasks.md

File renamed without changes.

0 commit comments

Comments
 (0)