@openpen/module-api 1.1.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/_chunks/AppButtonDropdown.vue_vue_type_style_index_0_lang-B9dHU-LE.js +246 -0
  3. package/dist/_chunks/AppButtonDropdown.vue_vue_type_style_index_0_lang-B9dHU-LE.js.map +1 -0
  4. package/dist/_chunks/inject-keys-CRXCpN-M.js +196 -0
  5. package/dist/_chunks/inject-keys-CRXCpN-M.js.map +1 -0
  6. package/dist/_chunks/module-context-registry-Cn1IWYu9.js +16 -0
  7. package/dist/_chunks/module-context-registry-Cn1IWYu9.js.map +1 -0
  8. package/dist/_chunks/passthrough-DKU7JYuQ.js +6 -0
  9. package/dist/_chunks/passthrough-DKU7JYuQ.js.map +1 -0
  10. package/dist/cursors.d.ts +83 -0
  11. package/dist/define-module.d.ts +17 -0
  12. package/dist/host/color-utils.d.ts +11 -0
  13. package/{src/host/event-bus.ts → dist/host/event-bus.d.ts} +7 -5
  14. package/dist/host/host-commands.d.ts +1 -0
  15. package/dist/host/index.d.ts +24 -0
  16. package/dist/host/index.js +51 -0
  17. package/dist/host/index.js.map +1 -0
  18. package/{src/host/module-context-registry.ts → dist/host/module-context-registry.d.ts} +5 -19
  19. package/dist/host/passthrough.d.ts +2 -0
  20. package/dist/host/popup-anchor.d.ts +3 -0
  21. package/dist/host/registry.d.ts +39 -0
  22. package/dist/host/registry.js +37 -0
  23. package/dist/host/registry.js.map +1 -0
  24. package/dist/host/slot-entries.d.ts +2 -0
  25. package/dist/host/stroke-store.d.ts +6 -0
  26. package/dist/host/stroke-style.d.ts +3 -0
  27. package/dist/host/types.d.ts +176 -0
  28. package/dist/index.d.ts +28 -0
  29. package/dist/index.js +349 -0
  30. package/dist/index.js.map +1 -0
  31. package/{src/inject-keys.ts → dist/inject-keys.d.ts} +37 -66
  32. package/{src/locale.ts → dist/locale.d.ts} +3 -22
  33. package/dist/slots.d.ts +57 -0
  34. package/dist/types/contributions.d.ts +288 -0
  35. package/dist/types/control-bar-layout.d.ts +227 -0
  36. package/dist/types/module.d.ts +208 -0
  37. package/{src/types/notification.ts → dist/types/notification.d.ts} +12 -14
  38. package/dist/types/tool.d.ts +77 -0
  39. package/dist/uikit/components/AppBanner.vue.d.ts +34 -0
  40. package/dist/uikit/components/AppButton.vue.d.ts +30 -0
  41. package/dist/uikit/components/AppButtonDropdown.vue.d.ts +52 -0
  42. package/dist/uikit/components/AppDialog.vue.d.ts +53 -0
  43. package/dist/uikit/components/AppPopover.vue.d.ts +43 -0
  44. package/dist/uikit/components/AppSegmented.vue.d.ts +19 -0
  45. package/dist/uikit/components/AppSelect.vue.d.ts +26 -0
  46. package/dist/uikit/components/AppSlider.vue.d.ts +43 -0
  47. package/dist/uikit/components/AppTabs.vue.d.ts +35 -0
  48. package/dist/uikit/components/AppToggle.vue.d.ts +15 -0
  49. package/dist/uikit/components/AppTooltip.vue.d.ts +27 -0
  50. package/dist/uikit/components/DialogHost.vue.d.ts +3 -0
  51. package/{src/uikit/composables/dialog-internal-keys.ts → dist/uikit/composables/dialog-internal-keys.d.ts} +3 -5
  52. package/dist/uikit/composables/useDialog.d.ts +399 -0
  53. package/dist/uikit/composables/useDialogPluginComponent.d.ts +9 -0
  54. package/dist/uikit/index.d.ts +42 -0
  55. package/dist/uikit/index.js +898 -0
  56. package/dist/uikit/index.js.map +1 -0
  57. package/{src/uikit/internal.ts → dist/uikit/internal.d.ts} +1 -1
  58. package/dist/uikit/internal.js +157 -0
  59. package/dist/uikit/internal.js.map +1 -0
  60. package/dist/uikit/module-api.css +851 -0
  61. package/dist/uikit/primitives.d.ts +42 -0
  62. package/{src/use-module-context.ts → dist/use-module-context.d.ts} +2 -15
  63. package/{src/validation.ts → dist/validation.d.ts} +2 -6
  64. package/package.json +28 -14
  65. package/src/cursors.ts +0 -369
  66. package/src/define-module.ts +0 -55
  67. package/src/host/color-utils.ts +0 -18
  68. package/src/host/host-commands.ts +0 -9
  69. package/src/host/index.ts +0 -53
  70. package/src/host/passthrough.ts +0 -10
  71. package/src/host/popup-anchor.ts +0 -14
  72. package/src/host/slot-entries.ts +0 -9
  73. package/src/host/stroke-store.ts +0 -20
  74. package/src/host/stroke-style.ts +0 -15
  75. package/src/index.ts +0 -131
  76. package/src/slots.ts +0 -135
  77. package/src/types/contributions.ts +0 -333
  78. package/src/types/control-bar-layout.ts +0 -174
  79. package/src/types/module.ts +0 -225
  80. package/src/types/tool.ts +0 -82
  81. package/src/uikit/components/.gitkeep +0 -0
  82. package/src/uikit/components/AppBanner.vue +0 -131
  83. package/src/uikit/components/AppButton.vue +0 -135
  84. package/src/uikit/components/AppDialog.vue +0 -393
  85. package/src/uikit/components/AppPopover.vue +0 -268
  86. package/src/uikit/components/AppSegmented.vue +0 -128
  87. package/src/uikit/components/AppSelect.vue +0 -235
  88. package/src/uikit/components/AppSlider.vue +0 -204
  89. package/src/uikit/components/AppTabs.vue +0 -163
  90. package/src/uikit/components/AppToggle.vue +0 -96
  91. package/src/uikit/components/AppTooltip.vue +0 -118
  92. package/src/uikit/components/DialogHost.vue +0 -272
  93. package/src/uikit/composables/useDialog.ts +0 -141
  94. package/src/uikit/composables/useDialogPluginComponent.ts +0 -27
  95. package/src/uikit/index.ts +0 -79
  96. package/src/uikit/primitives.ts +0 -150
  97. /package/{src → dist}/uikit/tokens.css +0 -0
@@ -1,14 +0,0 @@
1
- /**
2
- * @openpen/module-api/host — Popup Anchor
3
- *
4
- * Re-exports the usePopupAnchor composable and its pure helper
5
- * calculatePopupAnchor for module components that need anchor-relative
6
- * popup positioning.
7
- *
8
- * Exposed here so module code can import without cross-layer relative paths.
9
- */
10
-
11
- export {
12
- usePopupAnchor,
13
- calculatePopupAnchor,
14
- } from '../../../../src/composables/usePopupAnchor'
@@ -1,9 +0,0 @@
1
- /**
2
- * @openpen/module-api/host — Slot Entries Reader
3
- *
4
- * Re-exports getSlotEntries so module components can render UI driven
5
- * by other modules' contributions to the same slot (for example,
6
- * the shape-picker rendering all canvas.shapes contributions).
7
- */
8
-
9
- export { getSlotEntries } from '../../../../src/core/runtime/contribution-store'
@@ -1,20 +0,0 @@
1
- /**
2
- * @openpen/module-api/host — Stroke Store
3
- *
4
- * Re-exports the stroke store APIs that module tools need.
5
- * Only the minimum surface required by stroke-eraser (and similar tools)
6
- * is exposed: getAllStrokes, removeStrokeById, pushCommand.
7
- *
8
- * Host-internal state mutation helpers (clearAll, addStroke, undo, redo)
9
- * are intentionally NOT exported — modules must use hostCommands for
10
- * undo/redo and canvas clear.
11
- */
12
-
13
- export {
14
- getAllStrokes,
15
- removeStrokeById,
16
- pushCommand,
17
- } from '../../../../src/services/stroke-store'
18
-
19
- // Re-export Stroke type via the public SDK types, not host internals.
20
- export type { Stroke } from '../types/tool'
@@ -1,15 +0,0 @@
1
- /**
2
- * @openpen/module-api/host — Stroke Style
3
- *
4
- * Re-exports the public API of the host useStrokeStyle composable.
5
- * Module components MUST import from here, not from src/composables directly.
6
- *
7
- * Exposed here so module components can access it without cross-layer imports.
8
- */
9
-
10
- // Re-export the public API functions that modules need.
11
- // initStrokeStyleFromSettings is host-boot-only and intentionally NOT exported here.
12
- export { useStrokeStyle } from '../../../../src/composables/useStrokeStyle'
13
-
14
- // Re-export the StrokeColor type via the public SDK types (not host internals).
15
- export type { StrokeColor } from '../types/tool'
package/src/index.ts DELETED
@@ -1,131 +0,0 @@
1
- /**
2
- * @openpen/module-api — public contract surface for OpenPen modules.
3
- *
4
- * This is the **only** import path that built-in modules and plugins
5
- * are allowed to use, alongside relative imports, `node:*`, and
6
- * third-party npm packages. Boundary tests in the OpenPen main repo
7
- * enforce this rule on `src/core/modules/**` and on plugin code.
8
- */
9
-
10
- // Core helpers
11
- export { defineModule } from './define-module'
12
- export { useModuleContext } from './use-module-context'
13
- export { isValidModuleId, MODULE_ID_RE } from './validation'
14
- export { resolveLabel, sanitizeIdForI18n } from './locale'
15
- export type { LocaleMap } from './locale'
16
-
17
- // Slot catalogue
18
- export {
19
- ALL_SLOTS,
20
- V1_ACTIVE_SLOTS,
21
- V1_RESERVED_SLOTS,
22
- CONTRIBUTION_KEY_TO_SLOT_ID,
23
- isKnownSlot,
24
- getSlot,
25
- } from './slots'
26
- export type { SlotDefinition, SlotCategory, SlotStatus } from './slots'
27
-
28
- // Tool primitives (Point, Stroke, StrokeStyle, Tool, …)
29
- export type {
30
- Point,
31
- StrokeColor,
32
- StrokeStyle,
33
- Stroke,
34
- PointerModifiers,
35
- Tool,
36
- } from './types/tool'
37
-
38
- // Control bar layout types + helpers
39
- export type { ControlBarLayout, LayoutGroup, GroupInset, ControlBarContribution } from './types/control-bar-layout'
40
- export {
41
- ControlBarLayoutSchema,
42
- LayoutGroupSchema,
43
- GroupInsetSchema,
44
- DEFAULT_CONTROL_BAR_LAYOUT,
45
- repairLayoutL3a,
46
- repairLayoutL3b,
47
- } from './types/control-bar-layout'
48
-
49
- // Notification types
50
- export type { NotifyPayload, NotifyHandle } from './types/notification'
51
-
52
- // Module + Contribution types
53
- export type { OpenPenModule, ModuleSetupContext } from './types/module'
54
- export type {
55
- ModuleContributions,
56
- ToolContribution,
57
- ShapeContribution,
58
- StrokeStyleContribution,
59
- HistoryCommandContribution,
60
- CanvasLayerContribution,
61
- HtmlOverlayContribution,
62
- StrokeTransformerContribution,
63
- SettingsTabContribution,
64
- SettingsPanelContribution,
65
- CursorContribution,
66
- CursorSpec,
67
- SvgCursorSpec,
68
- PngCursorSpec,
69
- Hotspot,
70
- StatusContribution,
71
- ModalContribution,
72
- TrayMenuContribution,
73
- ContextMenuContribution,
74
- ThemeTokenContribution,
75
- ShortcutContribution,
76
- WindowBehaviorContribution,
77
- LocaleContribution,
78
- EventSubscriptionContribution,
79
- LifecycleContribution,
80
- StorageContribution,
81
- MainHandlerContribution,
82
- FileDropContribution,
83
- } from './types/contributions'
84
-
85
- // Host ↔ module inject keys (Vue provide/inject bridge)
86
- export {
87
- STROKE_STYLE_CONTEXT_KEY,
88
- SNAP_EDGE_KEY,
89
- IS_VERTICAL_KEY,
90
- MODAL_MANAGER_KEY,
91
- WRAPPER_EL_KEY,
92
- ANCHOR_EL_KEY,
93
- CONTROL_BAR_ANIMATING_KEY,
94
- HOST_DIALOG_OPEN_COUNT_KEY,
95
- POPOVER_PLACEMENT_HINT_KEY,
96
- ACTIVE_TOOL_KEY,
97
- } from './inject-keys'
98
- export type { StrokeStyleContext, SnapEdge, ModalManager, PopoverPlacementHint } from './inject-keys'
99
-
100
- // Cursor compilation + sanitisation (used by host runtime; helpers
101
- // re-exported so plugins can validate cursor specs in their own tests).
102
- export {
103
- compileCursor,
104
- sanitizeSvgMarkup,
105
- sanitizeCursorContributions,
106
- isSafeRelativePath,
107
- pluginHostname,
108
- pluginAssetUrl,
109
- SAFE_CURSOR_KEYWORDS,
110
- } from './cursors'
111
- export type {
112
- CompiledCursor,
113
- CursorResolutionContext,
114
- CursorSanitizeRule,
115
- CursorSanitizeDiagnostic,
116
- CursorSanitizeOptions,
117
- CursorSanitizeResult,
118
- } from './cursors'
119
-
120
- // Re-export zod so modules don't need a separate import path.
121
- export { z } from 'zod'
122
-
123
- // Host-internal registry writers — re-exported here so the host's module
124
- // loader pulls them through the SAME externalised entry point
125
- // (`@openpen/module-api`, runtime bundle: openpen-runtime/module-api.js)
126
- // that `useModuleContext` reads from. Importing them via
127
- // `@openpen/module-api/host` instead would inline a duplicate
128
- // module-context-registry into the host bundle, breaking state-sharing
129
- // between writers and readers in production builds (see
130
- // project_dev_prod_parity.md). Plugin authors MUST NOT use these.
131
- export { setModuleContext, clearModuleContext } from './host/module-context-registry'
package/src/slots.ts DELETED
@@ -1,135 +0,0 @@
1
- /**
2
- * OpenPen Contribution Slot catalogue.
3
- *
4
- * Slots are the only places where modules can affect the host app.
5
- * Adding a slot is an architectural change — it usually requires a
6
- * new adapter on the OpenPen side and a minor version bump of this
7
- * package. Removing a slot is a breaking change.
8
- *
9
- * `status: 'v1'` slots are wired through to runtime adapters.
10
- * `status: 'reserved'` slots accept registrations and pass validation;
11
- * no runtime adapter is active yet so contributions are stored but
12
- * inert. Forward-compatible: modules can ship against reserved slots
13
- * without any plugin-side changes once an adapter lands.
14
- */
15
-
16
- export type SlotCategory = 'canvas' | 'ui' | 'system'
17
- export type SlotStatus = 'v1' | 'reserved'
18
-
19
- export interface SlotDefinition {
20
- /** Dotted slot id, e.g. `'canvas.tools'`. */
21
- id: string
22
- /** Implementation status — see file header. */
23
- status: SlotStatus
24
- /** High-level grouping for documentation and UI. */
25
- category: SlotCategory
26
- /** One-sentence purpose, shown in `docs/reference/slots.md`. */
27
- description: string
28
- }
29
-
30
- /**
31
- * v1 active slots (16). Wired through to runtime adapters.
32
- */
33
- export const V1_ACTIVE_SLOTS: readonly SlotDefinition[] = [
34
- // Canvas
35
- { id: 'canvas.tools', status: 'v1', category: 'canvas', description: 'Drawing tools driven by pointer events.' },
36
- { id: 'canvas.shapes', status: 'v1', category: 'canvas', description: 'Shape primitives consumed by the shape tool.' },
37
- { id: 'canvas.stroke.style', status: 'v1', category: 'canvas', description: 'Writers into the shared stroke style store.' },
38
- { id: 'canvas.layers.background', status: 'v1', category: 'canvas', description: 'Render below strokes (grids, watermarks).' },
39
- { id: 'canvas.layers.overlay', status: 'v1', category: 'canvas', description: 'Render above strokes (rulers, guides).' },
40
- { id: 'canvas.html.overlay', status: 'v1', category: 'canvas', description: 'Mount HTML elements above the canvas (text, stickers).' },
41
- // UI
42
- { id: 'ui.control-bar', status: 'v1', category: 'ui', description: 'Control bar item slot (groups and ordering managed by layout config).' },
43
- { id: 'ui.settings.tabs', status: 'v1', category: 'ui', description: 'Tabs in the settings window.' },
44
- { id: 'ui.settings.panels', status: 'v1', category: 'ui', description: 'Sections inside the Features settings tab, grouped by module.' },
45
- { id: 'ui.cursors', status: 'v1', category: 'ui', description: 'Drawing-mode cursor styles.' },
46
- { id: 'ui.status', status: 'v1', category: 'ui', description: 'Ephemeral status badges on the control bar.' },
47
- { id: 'ui.modals', status: 'v1', category: 'ui', description: 'Registered modals managed by the global modal stack.' },
48
- // System
49
- { id: 'system.shortcuts', status: 'v1', category: 'system', description: 'Global and drawing-mode keyboard shortcuts.' },
50
- { id: 'system.locales', status: 'v1', category: 'system', description: 'i18n dictionary contributions.' },
51
- { id: 'system.main.handlers', status: 'v1', category: 'system', description: 'Node-side IPC handlers for main process capabilities.' },
52
- { id: 'system.events', status: 'v1', category: 'system', description: 'Subscribers and emitters on the domain event bus.' },
53
- { id: 'system.lifecycle', status: 'v1', category: 'system', description: 'App lifecycle hooks (onReady / onSuspend / onQuit).' },
54
- ] as const
55
-
56
- /**
57
- * v1 reserved slots. Registrations pass validation but no runtime
58
- * adapter is active yet.
59
- */
60
- export const V1_RESERVED_SLOTS: readonly SlotDefinition[] = [
61
- { id: 'canvas.history.commands', status: 'reserved', category: 'canvas', description: 'Undo/redo history commands. Adapter not yet active; built-in command types are used internally.' },
62
- { id: 'canvas.stroke.transformers', status: 'reserved', category: 'canvas', description: 'Post-process strokes after creation (smooth, simplify).' },
63
- { id: 'ui.tray.menu', status: 'reserved', category: 'ui', description: 'System tray menu items. Adapter not yet active.' },
64
- { id: 'ui.context.menu', status: 'reserved', category: 'ui', description: 'Right-click context menu items on canvas / control bar.' },
65
- { id: 'ui.theme.tokens', status: 'reserved', category: 'ui', description: 'CSS custom property contributions (colour swatches, spacing tokens).' },
66
- { id: 'system.window.behaviors', status: 'reserved', category: 'system', description: 'Window behavior customizations. Adapter not yet active.' },
67
- { id: 'system.storage', status: 'reserved', category: 'system', description: 'Per-plugin isolated storage at ~/.openpen/plugins/<id>/data/. Adapter not yet active; first real consumer will drive the storage backend design.' },
68
- { id: 'system.file.drop', status: 'reserved', category: 'system', description: 'Handlers for files dropped onto the canvas.' },
69
- ] as const
70
-
71
- /**
72
- * All slots (active + reserved). Source of truth for validation.
73
- */
74
- export const ALL_SLOTS: readonly SlotDefinition[] = [
75
- ...V1_ACTIVE_SLOTS,
76
- ...V1_RESERVED_SLOTS,
77
- ]
78
-
79
- const SLOT_BY_ID = new Map<string, SlotDefinition>(
80
- ALL_SLOTS.map((s) => [s.id, s])
81
- )
82
-
83
- /** Returns `true` when `id` matches a slot in {@link ALL_SLOTS} (active or reserved). */
84
- export function isKnownSlot(id: string): boolean {
85
- return SLOT_BY_ID.has(id)
86
- }
87
-
88
- /**
89
- * Returns the {@link SlotDefinition} for a known slot id, or `undefined` if
90
- * the id is not registered in {@link ALL_SLOTS}.
91
- */
92
- export function getSlot(id: string): SlotDefinition | undefined {
93
- return SLOT_BY_ID.get(id)
94
- }
95
-
96
- /**
97
- * Mapping from `contributes` field name (camelCase) to slot id (dotted).
98
- *
99
- * Modules express contributions via the ergonomic camelCase keys on
100
- * `contributes` (e.g. `historyCommands`); the validator maps them
101
- * back to slot ids (`canvas.history.commands`) before checking against
102
- * `ALL_SLOTS`.
103
- *
104
- * Adding a new field here is part of adding a new slot.
105
- */
106
- export const CONTRIBUTION_KEY_TO_SLOT_ID: Record<string, string> = {
107
- // Canvas
108
- tools: 'canvas.tools',
109
- shapes: 'canvas.shapes',
110
- strokeStyle: 'canvas.stroke.style',
111
- historyCommands: 'canvas.history.commands',
112
- backgroundLayers: 'canvas.layers.background',
113
- overlayLayers: 'canvas.layers.overlay',
114
- htmlOverlays: 'canvas.html.overlay',
115
- strokeTransformers: 'canvas.stroke.transformers',
116
- // UI
117
- controlBar: 'ui.control-bar',
118
- settingsTabs: 'ui.settings.tabs',
119
- settingsPanels: 'ui.settings.panels',
120
- cursors: 'ui.cursors',
121
- status: 'ui.status',
122
- modals: 'ui.modals',
123
- trayMenu: 'ui.tray.menu',
124
- contextMenu: 'ui.context.menu',
125
- themeTokens: 'ui.theme.tokens',
126
- // System
127
- shortcuts: 'system.shortcuts',
128
- windowBehaviors: 'system.window.behaviors',
129
- locales: 'system.locales',
130
- events: 'system.events',
131
- lifecycle: 'system.lifecycle',
132
- storage: 'system.storage',
133
- mainHandlers: 'system.main.handlers',
134
- fileDrop: 'system.file.drop',
135
- }
@@ -1,333 +0,0 @@
1
- import type { Component } from 'vue'
2
- import type { LocaleMap } from '../locale'
3
- import type { Stroke, Tool } from './tool'
4
- import type { ControlBarContribution } from './control-bar-layout'
5
-
6
- export type { ControlBarContribution }
7
-
8
- /**
9
- * The shape of `OpenPenModule.contributes`. Every field is optional;
10
- * `defineModule` requires at least one field to be present and rejects
11
- * any unknown field at module-author boundary time.
12
- *
13
- * Each field corresponds to a slot in {@link import('../slots').ALL_SLOTS}.
14
- * The mapping from camelCase field name to dotted slot id lives in
15
- * `CONTRIBUTION_KEY_TO_SLOT_ID`.
16
- */
17
- export interface ModuleContributions {
18
- // ── Canvas ──────────────────────────────────────────────────────────────
19
- tools?: ToolContribution[]
20
- shapes?: ShapeContribution[]
21
- strokeStyle?: StrokeStyleContribution
22
- historyCommands?: HistoryCommandContribution[]
23
- backgroundLayers?: CanvasLayerContribution[]
24
- overlayLayers?: CanvasLayerContribution[]
25
- htmlOverlays?: HtmlOverlayContribution[]
26
- /** ⏳ Reserved slot — accepts registrations, adapter not yet wired. */
27
- strokeTransformers?: StrokeTransformerContribution[]
28
-
29
- // ── UI ──────────────────────────────────────────────────────────────────
30
- controlBar?: ControlBarContribution[]
31
- settingsTabs?: SettingsTabContribution[]
32
- settingsPanels?: SettingsPanelContribution[]
33
- cursors?: CursorContribution[]
34
- status?: StatusContribution[]
35
- modals?: ModalContribution[]
36
- trayMenu?: TrayMenuContribution[]
37
- /** ⏳ Reserved slot — accepts registrations, adapter not yet wired. */
38
- contextMenu?: ContextMenuContribution[]
39
- /** ⏳ Reserved slot — accepts registrations, adapter not yet wired. */
40
- themeTokens?: ThemeTokenContribution
41
-
42
- // ── System ──────────────────────────────────────────────────────────────
43
- shortcuts?: ShortcutContribution[]
44
- windowBehaviors?: WindowBehaviorContribution[]
45
- locales?: LocaleContribution
46
- events?: EventSubscriptionContribution[]
47
- lifecycle?: LifecycleContribution
48
- storage?: StorageContribution
49
- mainHandlers?: MainHandlerContribution
50
- /** ⏳ Reserved slot — accepts registrations, adapter not yet wired. */
51
- fileDrop?: FileDropContribution[]
52
- }
53
-
54
- // ════════════════════════════════════════════════════════════════════════
55
- // Below: per-slot contribution shapes. These are the locked public-facing
56
- // names referenced by docs and the validator; their internals can extend
57
- // without breaking modules.
58
- // ════════════════════════════════════════════════════════════════════════
59
-
60
- /**
61
- * Drawing tool driven by pointer events. Implements the full `Tool`
62
- * interface (onPointerDown / Move / Up) plus optional metadata for
63
- * the toolbar UI and an optional `renderStroke` for tools whose
64
- * strokes need custom redraw semantics (shapes, erasers, anything
65
- * beyond a simple polyline).
66
- */
67
- export interface ToolContribution extends Tool {
68
- id: string
69
- label?: string | LocaleMap
70
- /**
71
- * SVG markup string for this tool's icon in the control-bar button.
72
- *
73
- * The host renders tool icons via `v-html`, so only raw SVG strings are
74
- * accepted (not Vue components or CSS class names). Recommended size is
75
- * `width="16" height="16"` with `stroke="currentColor"` so the icon
76
- * inherits the button's colour automatically.
77
- *
78
- * @example
79
- * icon: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 17c3-3 6-6 9-3s6 0 9-3"/></svg>'
80
- *
81
- * Note: built-in tools (freehand, line, eraser) use their own Vue button
82
- * components rather than this field — `icon` is the lightweight path for
83
- * plugin-contributed tools that do not need a custom button component.
84
- */
85
- icon?: string
86
- /**
87
- * Custom redraw renderer. When omitted, canvas-engine falls back
88
- * to a polyline render through `stroke.points` — sufficient for
89
- * freehand- and line-style tools.
90
- */
91
- renderStroke?: (canvasCtx: CanvasRenderingContext2D, stroke: Stroke) => void
92
- }
93
-
94
- /** Shape primitive used by the shape tool. */
95
- export interface ShapeContribution {
96
- id: string
97
- label?: string | LocaleMap
98
- /**
99
- * Vue component or SVG string for the shape icon in control-bar / sub-panel.
100
- * Host renders via `<component :is="icon">`.
101
- * Component icons MAY accept an optional `filled?: boolean` prop —
102
- * the host passes the current fill-toggle state so icons can mirror it
103
- * (e.g. solid vs outline rendering). Plugin icons that ignore the prop
104
- * still work; they just won't reflect fill state visually.
105
- */
106
- icon?: Component | string
107
- draw: (...args: unknown[]) => void
108
- }
109
-
110
- /**
111
- * Stroke style writer. Modules declare which keys of the shared
112
- * stroke style store they write to; collision detection happens at
113
- * module-validator time.
114
- */
115
- export interface StrokeStyleContribution {
116
- /** Keys this module writes (e.g. `['lineWidth']`, `['color']`). */
117
- provides: string[]
118
- }
119
-
120
- /**
121
- * Undo/redo command handler for the `canvas.history.commands` slot.
122
- *
123
- * The host calls `undo` / `redo` with whatever `payload` was stored when the
124
- * command was pushed onto the history stack. The contributing module decides
125
- * the payload shape — the host treats it as `unknown` and passes it through
126
- * without inspection.
127
- */
128
- export interface HistoryCommandContribution {
129
- type: string
130
- undo: (payload: unknown) => void
131
- redo: (payload: unknown) => void
132
- }
133
-
134
- export interface CanvasLayerContribution {
135
- id: string
136
- /** Render priority within the layer; higher = drawn later (on top). */
137
- order?: number
138
- render: (ctx: CanvasRenderingContext2D) => void
139
- }
140
-
141
- export interface HtmlOverlayContribution {
142
- id: string
143
- component: Component
144
- }
145
-
146
- export interface StrokeTransformerContribution {
147
- id: string
148
- /** Receives a stroke, returns a transformed stroke. Slot ⏳ reserved. */
149
- transform: (stroke: unknown) => unknown
150
- }
151
-
152
- export interface SettingsTabContribution {
153
- id: string
154
- label: string | LocaleMap
155
- component: Component
156
- }
157
-
158
- export interface SettingsPanelContribution {
159
- id: string
160
- label: string | LocaleMap
161
- component: Component
162
- }
163
-
164
- /** Hotspot offset in CSS pixels relative to the cursor image's top-left. */
165
- export interface Hotspot {
166
- x: number
167
- y: number
168
- }
169
-
170
- /**
171
- * Vector cursor: inline SVG markup OR a plugin-internal relative path
172
- * to a `.svg` file. URL-shaped strings, absolute paths, and `..` segments
173
- * are rejected at contribution-validation time.
174
- */
175
- export interface SvgCursorSpec {
176
- svg: string
177
- hotspot?: Hotspot
178
- /** CSS keyword fallback for legacy / non-overlay contexts. Defaults to `'crosshair'`. */
179
- fallback?: string
180
- }
181
-
182
- /**
183
- * Raster cursor: plugin-internal relative path to a `.png` file. Inline
184
- * form is not supported — PNG payload comes through `openpen-plugin://`.
185
- */
186
- export interface PngCursorSpec {
187
- png: string
188
- hotspot?: Hotspot
189
- fallback?: string
190
- }
191
-
192
- /**
193
- * Cursor specification. The legacy `string` form must be one of the
194
- * whitelisted CSS keywords (see `SAFE_CURSOR_KEYWORDS` in cursors.ts);
195
- * any string containing `url(` / `image-set(` / `-webkit-image-set(` is
196
- * rejected to keep the asset trust boundary closed.
197
- */
198
- export type CursorSpec = string | SvgCursorSpec | PngCursorSpec
199
-
200
- export interface CursorContribution {
201
- id: string
202
- cursor: CursorSpec
203
- }
204
-
205
- export interface StatusContribution {
206
- id: string
207
- component: Component
208
- }
209
-
210
- export interface ModalContribution {
211
- id: string
212
- component: Component
213
- }
214
-
215
- export interface TrayMenuContribution {
216
- id: string
217
- label: string | LocaleMap
218
- click: () => void
219
- }
220
-
221
- export interface ContextMenuContribution {
222
- id: string
223
- label: string | LocaleMap
224
- /** Where the menu may appear; runtime filters by context at open time. */
225
- context: 'canvas' | 'toolbar' | 'tray'
226
- click: () => void
227
- }
228
-
229
- /**
230
- * CSS custom property contributions for the `ui.theme.tokens` slot.
231
- *
232
- * Keys are CSS custom property names (e.g. `'--my-plugin-accent'`).
233
- * Values are CSS color or length values (e.g. `'#ff6600'`, `'4px'`).
234
- * The host merges all contributions into `:root` at startup.
235
- */
236
- export type ThemeTokenContribution = Record<string, string>
237
-
238
- export interface ShortcutContribution {
239
- id: string
240
- /** Accelerator string; tinykeys-style or Electron globalShortcut format. */
241
- keys: string
242
- /** `'global'` registers OS-wide; `'drawing'` only when overlay is active. */
243
- scope: 'global' | 'drawing'
244
- handler: () => void
245
- /** Human-readable name. Omit to hide this shortcut from the user-visible shortcuts list. */
246
- label?: string | LocaleMap
247
- /** Optional secondary description shown below the label in the shortcuts settings panel. */
248
- sublabel?: string | LocaleMap
249
- /** When true, the user can rebind this key. The chosen key is persisted under `config.json → customShortcuts`. */
250
- userCustomizable?: boolean
251
- }
252
-
253
- export interface WindowBehaviorContribution {
254
- id: string
255
- /** Reserved — concrete behaviour API not yet defined. */
256
- [key: string]: unknown
257
- }
258
-
259
- /** Leaf or branch in a locale dictionary. Leaves must be strings. */
260
- type LocaleDictValue = string | LocaleDict
261
-
262
- /**
263
- * Recursive locale dictionary.
264
- * Leaves are translated strings; branches are nested sub-dictionaries.
265
- * Use the `interface` form (not `type`) to allow self-reference without
266
- * triggering TypeScript's circular-type restrictions.
267
- */
268
- interface LocaleDict {
269
- [key: string]: LocaleDictValue
270
- }
271
-
272
- /**
273
- * A locale dictionary keyed by BCP-47 tag (e.g. `'en'`, `'zh-Hant'`).
274
- *
275
- * Values are per-locale message dictionaries. Supports both flat and nested
276
- * layouts — vue-i18n treats `.` in ctx.t(key) as a nested path separator,
277
- * so keys like `'notif.ready'` must use a nested object dict
278
- * `{ notif: { ready: '...' } }`, not a flat key `{ 'notif.ready': '...' }`.
279
- *
280
- * Non-string leaves are rejected at compile time; use ctx.t() to resolve
281
- * keys to strings before passing them to runtime APIs.
282
- */
283
- export type LocaleContribution = Record<string, LocaleDict>
284
-
285
- /**
286
- * Subscription to a named domain event on the `system.events` slot.
287
- *
288
- * `event` must match the event name emitted by another module (or the host)
289
- * via `system.events`. The host routes each emitted event to all registered
290
- * handlers whose `event` string matches.
291
- */
292
- export interface EventSubscriptionContribution {
293
- event: string
294
- handler: (payload: unknown) => void
295
- }
296
-
297
- /**
298
- * App lifecycle hooks for the `system.lifecycle` slot.
299
- *
300
- * Call order: `setup` (from `OpenPenModule.setup`) → `onReady` (host fully
301
- * booted) → `onSuspend` (overlay hidden) → `onQuit` (app shutting down).
302
- * All hooks may return a Promise; the host awaits each before advancing.
303
- */
304
- export interface LifecycleContribution {
305
- onReady?: () => void | Promise<void>
306
- onSuspend?: () => void | Promise<void>
307
- onQuit?: () => void | Promise<void>
308
- }
309
-
310
- /**
311
- * Marker that this module wants an isolated storage area at
312
- * `~/.openpen/plugins/<id>/data/`. Capacity / quota policy is defined
313
- * by the host runtime.
314
- */
315
- export interface StorageContribution {
316
- /** If true, the host pre-creates the data directory at module load. */
317
- preCreate?: boolean
318
- }
319
-
320
- /**
321
- * Map of action name to async handler. Routed from the renderer via
322
- * `window.openPenApi.moduleCall(moduleId, action, payload)`.
323
- */
324
- export type MainHandlerContribution = Record<
325
- string,
326
- (payload: unknown) => unknown | Promise<unknown>
327
- >
328
-
329
- export interface FileDropContribution {
330
- /** MIME types this handler accepts (e.g. `['image/png']`). */
331
- accept: string[]
332
- handle: (files: File[]) => void | Promise<void>
333
- }