@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
@@ -0,0 +1,57 @@
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
+ export type SlotCategory = 'canvas' | 'ui' | 'system';
16
+ export type SlotStatus = 'v1' | 'reserved';
17
+ export interface SlotDefinition {
18
+ /** Dotted slot id, e.g. `'canvas.tools'`. */
19
+ id: string;
20
+ /** Implementation status — see file header. */
21
+ status: SlotStatus;
22
+ /** High-level grouping for documentation and UI. */
23
+ category: SlotCategory;
24
+ /** One-sentence purpose, shown in `docs/slots/index.md`. */
25
+ description: string;
26
+ }
27
+ /**
28
+ * v1 active slots (16). Wired through to runtime adapters.
29
+ */
30
+ export declare const V1_ACTIVE_SLOTS: readonly SlotDefinition[];
31
+ /**
32
+ * v1 reserved slots. Registrations pass validation but no runtime
33
+ * adapter is active yet.
34
+ */
35
+ export declare const V1_RESERVED_SLOTS: readonly SlotDefinition[];
36
+ /**
37
+ * All slots (active + reserved). Source of truth for validation.
38
+ */
39
+ export declare const ALL_SLOTS: readonly SlotDefinition[];
40
+ /** Returns `true` when `id` matches a slot in {@link ALL_SLOTS} (active or reserved). */
41
+ export declare function isKnownSlot(id: string): boolean;
42
+ /**
43
+ * Returns the {@link SlotDefinition} for a known slot id, or `undefined` if
44
+ * the id is not registered in {@link ALL_SLOTS}.
45
+ */
46
+ export declare function getSlot(id: string): SlotDefinition | undefined;
47
+ /**
48
+ * Mapping from `contributes` field name (camelCase) to slot id (dotted).
49
+ *
50
+ * Modules express contributions via the ergonomic camelCase keys on
51
+ * `contributes` (e.g. `historyCommands`); the validator maps them
52
+ * back to slot ids (`canvas.history.commands`) before checking against
53
+ * `ALL_SLOTS`.
54
+ *
55
+ * Adding a new field here is part of adding a new slot.
56
+ */
57
+ export declare const CONTRIBUTION_KEY_TO_SLOT_ID: Record<string, string>;
@@ -0,0 +1,288 @@
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
+ export type { ControlBarContribution };
6
+ /**
7
+ * The shape of `OpenPenModule.contributes`. Every field is optional;
8
+ * `defineModule` requires at least one field to be present and rejects
9
+ * any unknown field at module-author boundary time.
10
+ *
11
+ * Each field corresponds to a slot in {@link import('../slots').ALL_SLOTS}.
12
+ * The mapping from camelCase field name to dotted slot id lives in
13
+ * `CONTRIBUTION_KEY_TO_SLOT_ID`.
14
+ */
15
+ export interface ModuleContributions {
16
+ tools?: ToolContribution[];
17
+ shapes?: ShapeContribution[];
18
+ strokeStyle?: StrokeStyleContribution;
19
+ historyCommands?: HistoryCommandContribution[];
20
+ backgroundLayers?: CanvasLayerContribution[];
21
+ overlayLayers?: CanvasLayerContribution[];
22
+ htmlOverlays?: HtmlOverlayContribution[];
23
+ /** ⏳ Reserved slot — accepts registrations, adapter not yet wired. */
24
+ strokeTransformers?: StrokeTransformerContribution[];
25
+ controlBar?: ControlBarContribution[];
26
+ settingsTabs?: SettingsTabContribution[];
27
+ settingsPanels?: SettingsPanelContribution[];
28
+ cursors?: CursorContribution[];
29
+ status?: StatusContribution[];
30
+ modals?: ModalContribution[];
31
+ trayMenu?: TrayMenuContribution[];
32
+ /** ⏳ Reserved slot — accepts registrations, adapter not yet wired. */
33
+ contextMenu?: ContextMenuContribution[];
34
+ /** ⏳ Reserved slot — accepts registrations, adapter not yet wired. */
35
+ themeTokens?: ThemeTokenContribution;
36
+ shortcuts?: ShortcutContribution[];
37
+ windowBehaviors?: WindowBehaviorContribution[];
38
+ locales?: LocaleContribution;
39
+ events?: EventSubscriptionContribution[];
40
+ lifecycle?: LifecycleContribution;
41
+ storage?: StorageContribution;
42
+ mainHandlers?: MainHandlerContribution;
43
+ /** ⏳ Reserved slot — accepts registrations, adapter not yet wired. */
44
+ fileDrop?: FileDropContribution[];
45
+ }
46
+ /**
47
+ * Drawing tool driven by pointer events. Implements the full `Tool`
48
+ * interface (onPointerDown / Move / Up) plus optional metadata for
49
+ * the toolbar UI and an optional `renderStroke` for tools whose
50
+ * strokes need custom redraw semantics (shapes, erasers, anything
51
+ * beyond a simple polyline).
52
+ */
53
+ export interface ToolContribution extends Tool {
54
+ id: string;
55
+ label?: string | LocaleMap;
56
+ /**
57
+ * SVG markup string for this tool's icon in the control-bar button.
58
+ *
59
+ * The host renders tool icons via `v-html`, so only raw SVG strings are
60
+ * accepted (not Vue components or CSS class names). Recommended size is
61
+ * `width="16" height="16"` with `stroke="currentColor"` so the icon
62
+ * inherits the button's colour automatically.
63
+ *
64
+ * @example
65
+ * 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>'
66
+ *
67
+ * Note: built-in tools (freehand, line, eraser) use their own Vue button
68
+ * components rather than this field — `icon` is the lightweight path for
69
+ * plugin-contributed tools that do not need a custom button component.
70
+ */
71
+ icon?: string;
72
+ /**
73
+ * Custom redraw renderer. When omitted, canvas-engine falls back
74
+ * to a polyline render through `stroke.points` — sufficient for
75
+ * freehand- and line-style tools.
76
+ */
77
+ renderStroke?: (canvasCtx: CanvasRenderingContext2D, stroke: Stroke) => void;
78
+ }
79
+ /** Shape primitive used by the shape tool. */
80
+ export interface ShapeContribution {
81
+ id: string;
82
+ label?: string | LocaleMap;
83
+ /**
84
+ * Vue component or SVG string for the shape icon in control-bar / sub-panel.
85
+ * Host renders via `<component :is="icon">`.
86
+ * Component icons MAY accept an optional `filled?: boolean` prop —
87
+ * the host passes the current fill-toggle state so icons can mirror it
88
+ * (e.g. solid vs outline rendering). Plugin icons that ignore the prop
89
+ * still work; they just won't reflect fill state visually.
90
+ */
91
+ icon?: Component | string;
92
+ draw: (...args: unknown[]) => void;
93
+ }
94
+ /**
95
+ * Stroke style writer. Modules declare which keys of the shared
96
+ * stroke style store they write to; collision detection happens at
97
+ * module-validator time.
98
+ */
99
+ export interface StrokeStyleContribution {
100
+ /** Keys this module writes (e.g. `['lineWidth']`, `['color']`). */
101
+ provides: string[];
102
+ }
103
+ /**
104
+ * Undo/redo command handler for the `canvas.history.commands` slot.
105
+ *
106
+ * The host calls `undo` / `redo` with whatever `payload` was stored when the
107
+ * command was pushed onto the history stack. The contributing module decides
108
+ * the payload shape — the host treats it as `unknown` and passes it through
109
+ * without inspection.
110
+ */
111
+ export interface HistoryCommandContribution {
112
+ type: string;
113
+ undo: (payload: unknown) => void;
114
+ redo: (payload: unknown) => void;
115
+ }
116
+ export interface CanvasLayerContribution {
117
+ id: string;
118
+ /** Render priority within the layer; higher = drawn later (on top). */
119
+ order?: number;
120
+ render: (ctx: CanvasRenderingContext2D) => void;
121
+ }
122
+ export interface HtmlOverlayContribution {
123
+ id: string;
124
+ component: Component;
125
+ }
126
+ export interface StrokeTransformerContribution {
127
+ id: string;
128
+ /** Receives a stroke, returns a transformed stroke. Slot ⏳ reserved. */
129
+ transform: (stroke: unknown) => unknown;
130
+ }
131
+ export interface SettingsTabContribution {
132
+ id: string;
133
+ label: string | LocaleMap;
134
+ component: Component;
135
+ }
136
+ export interface SettingsPanelContribution {
137
+ id: string;
138
+ label: string | LocaleMap;
139
+ component: Component;
140
+ }
141
+ /** Hotspot offset in CSS pixels relative to the cursor image's top-left. */
142
+ export interface Hotspot {
143
+ x: number;
144
+ y: number;
145
+ }
146
+ /**
147
+ * Vector cursor: inline SVG markup OR a plugin-internal relative path
148
+ * to a `.svg` file. URL-shaped strings, absolute paths, and `..` segments
149
+ * are rejected at contribution-validation time.
150
+ */
151
+ export interface SvgCursorSpec {
152
+ svg: string;
153
+ hotspot?: Hotspot;
154
+ /** CSS keyword fallback for legacy / non-overlay contexts. Defaults to `'crosshair'`. */
155
+ fallback?: string;
156
+ }
157
+ /**
158
+ * Raster cursor: plugin-internal relative path to a `.png` file. Inline
159
+ * form is not supported — PNG payload comes through `openpen-plugin://`.
160
+ */
161
+ export interface PngCursorSpec {
162
+ png: string;
163
+ hotspot?: Hotspot;
164
+ fallback?: string;
165
+ }
166
+ /**
167
+ * Cursor specification. The legacy `string` form must be one of the
168
+ * whitelisted CSS keywords (see `SAFE_CURSOR_KEYWORDS` in cursors.ts);
169
+ * any string containing `url(` / `image-set(` / `-webkit-image-set(` is
170
+ * rejected to keep the asset trust boundary closed.
171
+ */
172
+ export type CursorSpec = string | SvgCursorSpec | PngCursorSpec;
173
+ export interface CursorContribution {
174
+ id: string;
175
+ cursor: CursorSpec;
176
+ }
177
+ export interface StatusContribution {
178
+ id: string;
179
+ component: Component;
180
+ }
181
+ export interface ModalContribution {
182
+ id: string;
183
+ component: Component;
184
+ }
185
+ export interface TrayMenuContribution {
186
+ id: string;
187
+ label: string | LocaleMap;
188
+ click: () => void;
189
+ }
190
+ export interface ContextMenuContribution {
191
+ id: string;
192
+ label: string | LocaleMap;
193
+ /** Where the menu may appear; runtime filters by context at open time. */
194
+ context: 'canvas' | 'toolbar' | 'tray';
195
+ click: () => void;
196
+ }
197
+ /**
198
+ * CSS custom property contributions for the `ui.theme.tokens` slot.
199
+ *
200
+ * Keys are CSS custom property names (e.g. `'--my-plugin-accent'`).
201
+ * Values are CSS color or length values (e.g. `'#ff6600'`, `'4px'`).
202
+ * The host merges all contributions into `:root` at startup.
203
+ */
204
+ export type ThemeTokenContribution = Record<string, string>;
205
+ export interface ShortcutContribution {
206
+ id: string;
207
+ /** Accelerator string; tinykeys-style or Electron globalShortcut format. */
208
+ keys: string;
209
+ /** `'global'` registers OS-wide; `'drawing'` only when overlay is active. */
210
+ scope: 'global' | 'drawing';
211
+ handler: () => void;
212
+ /** Human-readable name. Omit to hide this shortcut from the user-visible shortcuts list. */
213
+ label?: string | LocaleMap;
214
+ /** Optional secondary description shown below the label in the shortcuts settings panel. */
215
+ sublabel?: string | LocaleMap;
216
+ /** When true, the user can rebind this key. The chosen key is persisted under `config.json → customShortcuts`. */
217
+ userCustomizable?: boolean;
218
+ }
219
+ export interface WindowBehaviorContribution {
220
+ id: string;
221
+ /** Reserved — concrete behaviour API not yet defined. */
222
+ [key: string]: unknown;
223
+ }
224
+ /** Leaf or branch in a locale dictionary. Leaves must be strings. */
225
+ type LocaleDictValue = string | LocaleDict;
226
+ /**
227
+ * Recursive locale dictionary.
228
+ * Leaves are translated strings; branches are nested sub-dictionaries.
229
+ * Use the `interface` form (not `type`) to allow self-reference without
230
+ * triggering TypeScript's circular-type restrictions.
231
+ */
232
+ interface LocaleDict {
233
+ [key: string]: LocaleDictValue;
234
+ }
235
+ /**
236
+ * A locale dictionary keyed by BCP-47 tag (e.g. `'en'`, `'zh-Hant'`).
237
+ *
238
+ * Values are per-locale message dictionaries. Supports both flat and nested
239
+ * layouts — vue-i18n treats `.` in ctx.t(key) as a nested path separator,
240
+ * so keys like `'notif.ready'` must use a nested object dict
241
+ * `{ notif: { ready: '...' } }`, not a flat key `{ 'notif.ready': '...' }`.
242
+ *
243
+ * Non-string leaves are rejected at compile time; use ctx.t() to resolve
244
+ * keys to strings before passing them to runtime APIs.
245
+ */
246
+ export type LocaleContribution = Record<string, LocaleDict>;
247
+ /**
248
+ * Subscription to a named domain event on the `system.events` slot.
249
+ *
250
+ * `event` must match the event name emitted by another module (or the host)
251
+ * via `system.events`. The host routes each emitted event to all registered
252
+ * handlers whose `event` string matches.
253
+ */
254
+ export interface EventSubscriptionContribution {
255
+ event: string;
256
+ handler: (payload: unknown) => void;
257
+ }
258
+ /**
259
+ * App lifecycle hooks for the `system.lifecycle` slot.
260
+ *
261
+ * Call order: `setup` (from `OpenPenModule.setup`) → `onReady` (host fully
262
+ * booted) → `onSuspend` (overlay hidden) → `onQuit` (app shutting down).
263
+ * All hooks may return a Promise; the host awaits each before advancing.
264
+ */
265
+ export interface LifecycleContribution {
266
+ onReady?: () => void | Promise<void>;
267
+ onSuspend?: () => void | Promise<void>;
268
+ onQuit?: () => void | Promise<void>;
269
+ }
270
+ /**
271
+ * Marker that this module wants an isolated storage area at
272
+ * `~/.openpen/plugins/<id>/data/`. Capacity / quota policy is defined
273
+ * by the host runtime.
274
+ */
275
+ export interface StorageContribution {
276
+ /** If true, the host pre-creates the data directory at module load. */
277
+ preCreate?: boolean;
278
+ }
279
+ /**
280
+ * Map of action name to async handler. Routed from the renderer via
281
+ * `window.openPenApi.moduleCall(moduleId, action, payload)`.
282
+ */
283
+ export type MainHandlerContribution = Record<string, (payload: unknown) => unknown | Promise<unknown>>;
284
+ export interface FileDropContribution {
285
+ /** MIME types this handler accepts (e.g. `['image/png']`). */
286
+ accept: string[];
287
+ handle: (files: File[]) => void | Promise<void>;
288
+ }
@@ -0,0 +1,227 @@
1
+ import { z } from 'zod';
2
+ import type { Component } from 'vue';
3
+ import type { LocaleMap } from '../locale';
4
+ export declare const GroupInsetSchema: z.ZodObject<{
5
+ enabled: z.ZodBoolean;
6
+ /** Optional CSS color override for the inset background; defaults to --openpen-color-control-group. */
7
+ color: z.ZodOptional<z.ZodString>;
8
+ }, "strip", z.ZodTypeAny, {
9
+ enabled: boolean;
10
+ color?: string | undefined;
11
+ }, {
12
+ enabled: boolean;
13
+ color?: string | undefined;
14
+ }>;
15
+ export declare const LayoutGroupSchema: z.ZodObject<{
16
+ id: z.ZodString;
17
+ items: z.ZodArray<z.ZodString, "many">;
18
+ separator: z.ZodOptional<z.ZodEnum<["auto", "always", "never"]>>;
19
+ /**
20
+ * Optional inset rendering for this group. When `enabled: true`, the group
21
+ * MUST render with a visible bg + border + padding container (the design's
22
+ * "grouped tools" look) AND its inner items MUST shrink so the group's
23
+ * outer height matches an unwrapped 36px button — i.e. enabling inset
24
+ * MUST NOT increase control-bar height.
25
+ */
26
+ inset: z.ZodOptional<z.ZodObject<{
27
+ enabled: z.ZodBoolean;
28
+ /** Optional CSS color override for the inset background; defaults to --openpen-color-control-group. */
29
+ color: z.ZodOptional<z.ZodString>;
30
+ }, "strip", z.ZodTypeAny, {
31
+ enabled: boolean;
32
+ color?: string | undefined;
33
+ }, {
34
+ enabled: boolean;
35
+ color?: string | undefined;
36
+ }>>;
37
+ }, "strip", z.ZodTypeAny, {
38
+ id: string;
39
+ items: string[];
40
+ separator?: "never" | "auto" | "always" | undefined;
41
+ inset?: {
42
+ enabled: boolean;
43
+ color?: string | undefined;
44
+ } | undefined;
45
+ }, {
46
+ id: string;
47
+ items: string[];
48
+ separator?: "never" | "auto" | "always" | undefined;
49
+ inset?: {
50
+ enabled: boolean;
51
+ color?: string | undefined;
52
+ } | undefined;
53
+ }>;
54
+ export declare const ControlBarLayoutSchema: z.ZodObject<{
55
+ version: z.ZodLiteral<1>;
56
+ groups: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodArray<z.ZodObject<{
57
+ id: z.ZodString;
58
+ items: z.ZodArray<z.ZodString, "many">;
59
+ separator: z.ZodOptional<z.ZodEnum<["auto", "always", "never"]>>;
60
+ /**
61
+ * Optional inset rendering for this group. When `enabled: true`, the group
62
+ * MUST render with a visible bg + border + padding container (the design's
63
+ * "grouped tools" look) AND its inner items MUST shrink so the group's
64
+ * outer height matches an unwrapped 36px button — i.e. enabling inset
65
+ * MUST NOT increase control-bar height.
66
+ */
67
+ inset: z.ZodOptional<z.ZodObject<{
68
+ enabled: z.ZodBoolean;
69
+ /** Optional CSS color override for the inset background; defaults to --openpen-color-control-group. */
70
+ color: z.ZodOptional<z.ZodString>;
71
+ }, "strip", z.ZodTypeAny, {
72
+ enabled: boolean;
73
+ color?: string | undefined;
74
+ }, {
75
+ enabled: boolean;
76
+ color?: string | undefined;
77
+ }>>;
78
+ }, "strip", z.ZodTypeAny, {
79
+ id: string;
80
+ items: string[];
81
+ separator?: "never" | "auto" | "always" | undefined;
82
+ inset?: {
83
+ enabled: boolean;
84
+ color?: string | undefined;
85
+ } | undefined;
86
+ }, {
87
+ id: string;
88
+ items: string[];
89
+ separator?: "never" | "auto" | "always" | undefined;
90
+ inset?: {
91
+ enabled: boolean;
92
+ color?: string | undefined;
93
+ } | undefined;
94
+ }>, "many">, {
95
+ id: string;
96
+ items: string[];
97
+ separator?: "never" | "auto" | "always" | undefined;
98
+ inset?: {
99
+ enabled: boolean;
100
+ color?: string | undefined;
101
+ } | undefined;
102
+ }[], {
103
+ id: string;
104
+ items: string[];
105
+ separator?: "never" | "auto" | "always" | undefined;
106
+ inset?: {
107
+ enabled: boolean;
108
+ color?: string | undefined;
109
+ } | undefined;
110
+ }[]>, {
111
+ id: string;
112
+ items: string[];
113
+ separator?: "never" | "auto" | "always" | undefined;
114
+ inset?: {
115
+ enabled: boolean;
116
+ color?: string | undefined;
117
+ } | undefined;
118
+ }[], {
119
+ id: string;
120
+ items: string[];
121
+ separator?: "never" | "auto" | "always" | undefined;
122
+ inset?: {
123
+ enabled: boolean;
124
+ color?: string | undefined;
125
+ } | undefined;
126
+ }[]>, {
127
+ id: string;
128
+ items: string[];
129
+ separator?: "never" | "auto" | "always" | undefined;
130
+ inset?: {
131
+ enabled: boolean;
132
+ color?: string | undefined;
133
+ } | undefined;
134
+ }[], {
135
+ id: string;
136
+ items: string[];
137
+ separator?: "never" | "auto" | "always" | undefined;
138
+ inset?: {
139
+ enabled: boolean;
140
+ color?: string | undefined;
141
+ } | undefined;
142
+ }[]>;
143
+ }, "strip", z.ZodTypeAny, {
144
+ version: 1;
145
+ groups: {
146
+ id: string;
147
+ items: string[];
148
+ separator?: "never" | "auto" | "always" | undefined;
149
+ inset?: {
150
+ enabled: boolean;
151
+ color?: string | undefined;
152
+ } | undefined;
153
+ }[];
154
+ }, {
155
+ version: 1;
156
+ groups: {
157
+ id: string;
158
+ items: string[];
159
+ separator?: "never" | "auto" | "always" | undefined;
160
+ inset?: {
161
+ enabled: boolean;
162
+ color?: string | undefined;
163
+ } | undefined;
164
+ }[];
165
+ }>;
166
+ export interface GroupInset {
167
+ enabled: boolean;
168
+ /** Optional CSS color override for the inset background; defaults to --openpen-color-control-group. */
169
+ color?: string;
170
+ }
171
+ export interface LayoutGroup {
172
+ id: string;
173
+ items: string[];
174
+ separator?: 'auto' | 'always' | 'never';
175
+ /** When `enabled: true`, render the group with the visible "grouped tools" container. */
176
+ inset?: GroupInset;
177
+ }
178
+ export interface ControlBarLayout {
179
+ version: 1;
180
+ groups: LayoutGroup[];
181
+ }
182
+ export interface ControlBarContribution {
183
+ /** Item id — MUST be globally unique across all modules. */
184
+ id: string;
185
+ /** Vue component rendered for this item. */
186
+ component: Component;
187
+ /**
188
+ * Preferred group id on first install.
189
+ * MUST NOT be 'default' (reserved).
190
+ * When omitted, item falls into the 'default' group.
191
+ */
192
+ defaultGroup?: string;
193
+ /**
194
+ * Hints used when host auto-creates the defaultGroup.
195
+ * Ignored if the group already exists in the layout.
196
+ */
197
+ groupHint?: {
198
+ separator?: 'auto' | 'always' | 'never';
199
+ label?: string | LocaleMap;
200
+ };
201
+ }
202
+ /** Baseline layout used on first launch: a single empty `'default'` group. */
203
+ export declare const DEFAULT_CONTROL_BAR_LAYOUT: ControlBarLayout;
204
+ /**
205
+ * Apply L3a non-destructive repairs to a parsed layout:
206
+ * - Ensure 'default' group exists
207
+ * - Replace invalid separator values with 'auto'
208
+ * Returns the (possibly mutated copy) and whether any repair was made.
209
+ *
210
+ * @internal Host-only utility; not part of the plugin author API.
211
+ */
212
+ export declare function repairLayoutL3a(layout: ControlBarLayout): {
213
+ layout: ControlBarLayout;
214
+ repaired: boolean;
215
+ };
216
+ /**
217
+ * Apply L3b potentially-destructive repairs:
218
+ * - De-duplicate item ids across groups (keep last occurrence)
219
+ * - Trim ghost ids when count ≥ 20 (noop — ghost trimming is a UX action)
220
+ * Returns the repaired layout and whether any repair was made.
221
+ *
222
+ * @internal Host-only utility; not part of the plugin author API.
223
+ */
224
+ export declare function repairLayoutL3b(layout: ControlBarLayout, knownItemIds: Set<string>): {
225
+ layout: ControlBarLayout;
226
+ repaired: boolean;
227
+ };