@vetta-org/plugin-sdk 0.1.1 → 0.2.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 (87) hide show
  1. package/dist/activity-tab.d.ts +2 -0
  2. package/dist/activity-tab.d.ts.map +1 -1
  3. package/dist/activity-tab.js +1 -1
  4. package/dist/activity-tab.js.map +1 -1
  5. package/dist/agent.d.ts +158 -2
  6. package/dist/agent.d.ts.map +1 -1
  7. package/dist/agent.js +14 -1
  8. package/dist/agent.js.map +1 -1
  9. package/dist/ai.d.ts +93 -0
  10. package/dist/ai.d.ts.map +1 -0
  11. package/dist/ai.js +2 -0
  12. package/dist/ai.js.map +1 -0
  13. package/dist/app-actions.d.ts +9 -0
  14. package/dist/app-actions.d.ts.map +1 -1
  15. package/dist/app-actions.js.map +1 -1
  16. package/dist/artifacts.d.ts +31 -0
  17. package/dist/artifacts.d.ts.map +1 -0
  18. package/dist/artifacts.js +2 -0
  19. package/dist/artifacts.js.map +1 -0
  20. package/dist/browser.d.ts +119 -0
  21. package/dist/browser.d.ts.map +1 -0
  22. package/dist/browser.js +2 -0
  23. package/dist/browser.js.map +1 -0
  24. package/dist/capture.d.ts +64 -0
  25. package/dist/capture.d.ts.map +1 -0
  26. package/dist/capture.js +2 -0
  27. package/dist/capture.js.map +1 -0
  28. package/dist/context.d.ts +46 -5
  29. package/dist/context.d.ts.map +1 -1
  30. package/dist/context.js.map +1 -1
  31. package/dist/conversation.d.ts +66 -2
  32. package/dist/conversation.d.ts.map +1 -1
  33. package/dist/conversation.js.map +1 -1
  34. package/dist/gateway.d.ts +37 -0
  35. package/dist/gateway.d.ts.map +1 -0
  36. package/dist/gateway.js +14 -0
  37. package/dist/gateway.js.map +1 -0
  38. package/dist/index.d.ts +20 -9
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +4 -0
  41. package/dist/index.js.map +1 -1
  42. package/dist/jobs.d.ts +34 -0
  43. package/dist/jobs.d.ts.map +1 -0
  44. package/dist/jobs.js +2 -0
  45. package/dist/jobs.js.map +1 -0
  46. package/dist/manifest-schema.d.ts +234 -0
  47. package/dist/manifest-schema.d.ts.map +1 -0
  48. package/dist/manifest-schema.js +145 -0
  49. package/dist/manifest-schema.js.map +1 -0
  50. package/dist/manifest.d.ts +21 -55
  51. package/dist/manifest.d.ts.map +1 -1
  52. package/dist/manifest.js +342 -1
  53. package/dist/manifest.js.map +1 -1
  54. package/dist/media.d.ts +193 -0
  55. package/dist/media.d.ts.map +1 -0
  56. package/dist/media.js +11 -0
  57. package/dist/media.js.map +1 -0
  58. package/dist/network.d.ts +2 -1
  59. package/dist/network.d.ts.map +1 -1
  60. package/dist/network.js.map +1 -1
  61. package/dist/npm-package.d.ts +17 -0
  62. package/dist/npm-package.d.ts.map +1 -0
  63. package/dist/npm-package.js +38 -0
  64. package/dist/npm-package.js.map +1 -0
  65. package/dist/official.d.ts +218 -10
  66. package/dist/official.d.ts.map +1 -1
  67. package/dist/official.js.map +1 -1
  68. package/dist/permissions.d.ts +2 -1
  69. package/dist/permissions.d.ts.map +1 -1
  70. package/dist/permissions.js +54 -1
  71. package/dist/permissions.js.map +1 -1
  72. package/dist/prompt-attachment.d.ts +30 -6
  73. package/dist/prompt-attachment.d.ts.map +1 -1
  74. package/dist/prompt-attachment.js +35 -1
  75. package/dist/prompt-attachment.js.map +1 -1
  76. package/dist/quickjs.d.ts +147 -0
  77. package/dist/quickjs.d.ts.map +1 -0
  78. package/dist/quickjs.js +2 -0
  79. package/dist/quickjs.js.map +1 -0
  80. package/dist/storage.d.ts +7 -0
  81. package/dist/storage.d.ts.map +1 -1
  82. package/dist/storage.js.map +1 -1
  83. package/dist/ui.d.ts +223 -15
  84. package/dist/ui.d.ts.map +1 -1
  85. package/dist/ui.js.map +1 -1
  86. package/package.json +21 -2
  87. package/src/tailwind-theme.css +30 -0
package/dist/ui.d.ts CHANGED
@@ -9,6 +9,133 @@ export interface PluginGlobalSlotContribution {
9
9
  id: string;
10
10
  component: ComponentType;
11
11
  }
12
+ /**
13
+ * Props handed to a workspace-view component. A workspace view owns the whole
14
+ * content area, so unlike panel slots it is addressed by its own route and can
15
+ * be deep-linked; the host passes back its identity so one component can serve
16
+ * several registered views.
17
+ */
18
+ export interface PluginWorkspaceViewProps {
19
+ /** The contribution id as registered (NOT namespaced). */
20
+ viewId: string;
21
+ /** The owning plugin id. */
22
+ pluginId: string;
23
+ }
24
+ /**
25
+ * Badge tone. The host maps it to its own theme colors — a plugin cannot pick a
26
+ * raw color, so badges stay consistent with the built-in navigation entries.
27
+ */
28
+ export type PluginNavBadgeTone = "accent" | "danger" | "default" | "warning";
29
+ /**
30
+ * A badge on the plugin's sidebar navigation entry.
31
+ *
32
+ * `beta` is the host's own preset — it renders exactly like the built-in
33
+ * 知识库 Beta 标识, wording included (and localized by the host), so plugins do
34
+ * not have to spell "Beta" themselves in every locale.
35
+ *
36
+ * `count` renders `99+` past 99 and disappears at 0 — an unread badge showing
37
+ * "0" is worse than no badge.
38
+ */
39
+ export type PluginNavBadge = {
40
+ kind: "beta";
41
+ } | {
42
+ kind: "text";
43
+ text: string;
44
+ tone?: PluginNavBadgeTone;
45
+ } | {
46
+ kind: "count";
47
+ count: number;
48
+ tone?: PluginNavBadgeTone;
49
+ } | {
50
+ kind: "dot";
51
+ tone?: PluginNavBadgeTone;
52
+ };
53
+ /**
54
+ * A **workspace view**(工作区视图): a full-page surface a plugin contributes to
55
+ * the host's primary navigation. It is the plugin counterpart of a built-in page
56
+ * such as 自动化 / 知识库 — the host gives it its own route
57
+ * (`/workspace/<pluginId>/<viewId>`) and a sidebar entry, and the plugin owns the
58
+ * entire content area while it is open.
59
+ *
60
+ * Use it for standalone workbenches that are NOT bound to one conversation
61
+ * (dashboards, boards, consoles). Conversation-scoped UI belongs in an
62
+ * {@link PluginActivityTabContribution activity tab}; floating UI belongs in a
63
+ * {@link PluginGlobalSlotContribution global slot}.
64
+ */
65
+ export interface PluginWorkspaceViewContribution {
66
+ /** Unique within the plugin; the host namespaces it as `${pluginId}:${id}`. */
67
+ id: string;
68
+ /** Sidebar entry label. Supports `%catalogKey%` i18n lookup. */
69
+ label: string;
70
+ /**
71
+ * Sidebar entry icon as an **iconify class string** (e.g.
72
+ * `"icon-[solar--widget-5-linear]"`) — NOT a React node, because the host
73
+ * renders it inside its own nav button and persists nav layout by key.
74
+ *
75
+ * Omit it to inherit the plugin's own `plugin.json` icon: a packaged image
76
+ * (svg / png / webp …) is masked with the theme's foreground color, so a plugin
77
+ * that ships artwork does not have to find a lookalike in an icon set. Set
78
+ * {@link PluginWorkspaceViewContribution.iconTint} to `false` to keep its colors.
79
+ */
80
+ icon?: string;
81
+ /**
82
+ * Whether an image icon is tinted with the theme's foreground color. Defaults to
83
+ * `true`, which keeps the entry consistent with the built-in navigation and legible
84
+ * in every theme.
85
+ *
86
+ * Pass `false` for a full-color logo. Weigh it first: the entry renders at 16px
87
+ * beside monochrome built-ins, and a fixed-color image cannot follow the active
88
+ * theme — a dark logo can disappear on a dark sidebar. Has no effect on Iconify
89
+ * class icons, which are always tinted.
90
+ */
91
+ iconTint?: boolean;
92
+ /** Optional one-line description; shown as the nav entry tooltip. */
93
+ description?: string;
94
+ /**
95
+ * Initial badge on the sidebar entry. `{ kind: "text" }` supports the same
96
+ * `%catalogKey%` i18n lookup as {@link label}. Update it at runtime with
97
+ * {@link PluginUiApi.setWorkspaceViewBadge}.
98
+ */
99
+ badge?: PluginNavBadge;
100
+ /** Zero-props aside from {@link PluginWorkspaceViewProps}. */
101
+ component: ComponentType<PluginWorkspaceViewProps>;
102
+ /** Sort hint among this plugin's own views (ascending). Defaults to 0. */
103
+ navOrder?: number;
104
+ }
105
+ /**
106
+ * What a workspace view puts into the **host page header** — the window's top
107
+ * bar that also carries the drag region, the macOS traffic-light gutter and the
108
+ * sidebar trigger.
109
+ *
110
+ * Without this the header still renders, showing the app name above the
111
+ * plugin's own toolbar — two stacked bars. Set `hideTitle` and move the view's
112
+ * toolbar in here and the surface gets a single, host-consistent header.
113
+ *
114
+ * `left` / `right` are ReactNodes rendered by the host inside that bar (left:
115
+ * right after the sidebar trigger; right: before the window controls). They are
116
+ * rendered in the plugin's i18n + CSS scope, so `useTranslation()` and the
117
+ * plugin's own classes work as usual. Pass fresh nodes whenever the view's
118
+ * state changes — this is a live setter, not a one-time registration.
119
+ */
120
+ export interface PluginWorkspaceViewHeader {
121
+ /** Replaces the host title text. Supports `%catalogKey%` i18n lookup. */
122
+ title?: string;
123
+ /** Drop the host title entirely (use when {@link left} carries its own). */
124
+ hideTitle?: boolean;
125
+ /** Rendered in the header's left cluster, after the sidebar trigger. */
126
+ left?: ReactNode;
127
+ /** Rendered in the header's action cluster, before the window controls. */
128
+ right?: ReactNode;
129
+ /**
130
+ * Float the host header OVER the view instead of stacking above it. The view
131
+ * then owns the full content height, and its top `~44px` sit under the
132
+ * transparent header strip (window drag region / traffic lights / sidebar
133
+ * trigger all keep working on top). Use for immersive full-page surfaces
134
+ * whose own hero starts at the very top; leave unset for toolbar-style
135
+ * headers that need their own opaque row.
136
+ */
137
+ immersive?: boolean;
138
+ }
12
139
  export interface PluginAudioMetadata {
13
140
  /** Embedded title; preview renderers should fall back to file.name. */
14
141
  title?: string;
@@ -63,10 +190,17 @@ export interface PluginFilePreviewContribution {
63
190
  * tab to the "addable pool" — it renders only after the user attaches it in
64
191
  * the activity panel (scoped by session cwd).
65
192
  */
193
+ /** Host residency policy for an activity-panel tab. */
194
+ export type PluginActivityTabRetention = "active-only" | "warm" | "pinned";
66
195
  export interface PluginActivityTabContribution {
67
196
  id: string;
68
197
  label: string;
69
- /** Tab icon as a React node (not an iconify class string). */
198
+ /**
199
+ * Tab icon as a React node (not an iconify class string).
200
+ * Omit to inherit the host-resolved brand icon from `plugin.json#icon`
201
+ * (`ctx.plugin.iconUrl`). Prefer omitting for package brand icons so the
202
+ * plugin never loads assets via absolute `/…` paths or host protocols.
203
+ */
70
204
  icon?: ReactNode;
71
205
  component: ComponentType;
72
206
  /**
@@ -82,13 +216,36 @@ export interface PluginActivityTabContribution {
82
216
  * 工作台跟随输入栏 toggle)。无论哪种,用户仍可用减号手动隐藏。
83
217
  */
84
218
  initiallyVisible?: boolean;
219
+ /**
220
+ * Component residency while the tab is inactive. Defaults to `"warm"`:
221
+ * visited tabs stay mounted in the host's bounded LRU cache. Use
222
+ * `"active-only"` for cheap, stateless content and `"pinned"` for runtimes
223
+ * that must never be evicted while available.
224
+ */
225
+ retention?: PluginActivityTabRetention;
226
+ /**
227
+ * @deprecated Use {@link PluginActivityTabContribution.retention}. `true`
228
+ * maps to `"pinned"`; `false` maps to `"active-only"`.
229
+ */
230
+ keepAliveWhenAvailable?: boolean;
231
+ }
232
+ /** Explicit conversation scope for an activity-tab command. */
233
+ export interface PluginActivityTabTargetOptions {
234
+ /**
235
+ * Absolute cwd whose activity-tab state should be changed. Omit to target
236
+ * the conversation currently displayed by the host. Tool handlers and
237
+ * background work should pass the triggering session's cwd so a foreground
238
+ * navigation change cannot redirect the command to another conversation.
239
+ */
240
+ cwd?: string;
85
241
  }
86
242
  /** Options for {@link PluginUiApi.openActivityTab}. */
87
- export interface PluginOpenActivityTabOptions {
243
+ export interface PluginOpenActivityTabOptions extends PluginActivityTabTargetOptions {
88
244
  /**
89
- * Desired panel width as it opens: a pixel number, or `"max"` for the widest
90
- * the current window allows. The host clamps to its min/max bounds. Omit to
91
- * keep the user's current width.
245
+ * Desired panel width as it opens: a pixel number, or `"max"` to track the
246
+ * widest the window allows `"max"` keeps following window resizes until the
247
+ * user drags the divider. The host clamps to its min/max bounds. Omit to keep
248
+ * the user's current width.
92
249
  */
93
250
  width?: number | "max";
94
251
  }
@@ -268,6 +425,42 @@ export interface PluginNotifyOptions {
268
425
  }
269
426
  export interface PluginUiApi {
270
427
  registerGlobalSlot(contribution: PluginGlobalSlotContribution): Disposable;
428
+ /**
429
+ * Register a **workspace view**(工作区视图)— a full-page surface with its own
430
+ * route and sidebar entry, on par with the host's built-in pages. Needs the
431
+ * `ui.slot.workspace-view` permission (missing permission = **warn+noop**).
432
+ *
433
+ * The entry lands in the sidebar's「更多」收纳 by default; the user can pin it
434
+ * to the top region or reorder it, and that layout is remembered.
435
+ */
436
+ registerWorkspaceView(contribution: PluginWorkspaceViewContribution): Disposable;
437
+ /**
438
+ * Navigate to one of this plugin's own workspace views. `viewId` is the
439
+ * contribution id passed to {@link PluginUiApi.registerWorkspaceView}. No-op
440
+ * when the view is not registered (e.g. permission missing).
441
+ */
442
+ openWorkspaceView(viewId: string): void;
443
+ /**
444
+ * Update (or clear, with `null`) the badge on one of this plugin's workspace
445
+ * view entries. Re-registering the view is NOT a way to do this — it would
446
+ * remount the whole surface — so live badges (unread counts, status dots)
447
+ * must come through here.
448
+ *
449
+ * No-op when the view is not registered (e.g. permission missing).
450
+ */
451
+ setWorkspaceViewBadge(viewId: string, badge: PluginNavBadge | null): void;
452
+ /**
453
+ * Fill (or clear, with `null`) the host page header while one of this
454
+ * plugin's workspace views is open. Call it from the view component (an
455
+ * effect that re-runs on state change) and clear it on unmount — the host
456
+ * keeps the entry keyed by view, and only applies it on that view's route.
457
+ *
458
+ * The header itself is never removed: it owns the window drag region and the
459
+ * macOS traffic-light gutter. This is how a view takes it over instead.
460
+ *
461
+ * No-op when the view is not registered (e.g. permission missing).
462
+ */
463
+ setWorkspaceViewHeader(viewId: string, header: PluginWorkspaceViewHeader | null): void;
271
464
  /**
272
465
  * Register a preview component keyed by file extension. The host dispatches
273
466
  * registered extensions before its built-in fallback renderers; first
@@ -317,7 +510,7 @@ export interface PluginUiApi {
317
510
  registerShortcutScope(contribution: PluginShortcutScopeContribution): Disposable;
318
511
  /**
319
512
  * Programmatically attach (if needed) and activate one of this plugin's
320
- * own activity tabs in the current conversation's activity panel. `tabId`
513
+ * own activity tabs in a conversation's activity panel. `tabId`
321
514
  * is the contribution id passed to registerActivityTab. Any payload (e.g.
322
515
  * which image to edit) is passed via the plugin's own in-memory state.
323
516
  *
@@ -325,22 +518,29 @@ export interface PluginUiApi {
325
518
  * `"max"` to expand it to the widest the current window allows (the host
326
519
  * still clamps to its min/max and auto-hides the sidebar when needed). Omit
327
520
  * to leave the user's current width untouched.
521
+ *
522
+ * Omit `options.cwd` for the conversation currently displayed by the host.
523
+ * Tool handlers and background work should pass their session cwd explicitly.
328
524
  */
329
525
  openActivityTab(tabId: string, options?: PluginOpenActivityTabOptions): void;
330
526
  /**
331
- * 把本插件的某个活动面板标签卡在当前会话里上栏 / 下栏,**不激活也不展开
527
+ * 把本插件的某个活动面板标签卡在目标会话里上栏 / 下栏,**不激活也不展开
332
528
  * 面板**——`openActivityTab` 是「用户此刻要看它」,这个是「它现在该不该在
333
529
  * 栏里」。配合 `initiallyVisible: false` 使用,插件即可完全掌握自己标签卡的
334
530
  * 出现条件(如 git 只在仓库目录里上栏、工作台跟随输入栏 toggle)。
335
531
  *
336
532
  * 上栏记录按会话 cwd 持久化(ADR-0026),所以只需在条件变化时调用一次;
337
- * 用户随后用减号手动隐藏的结果不会被重复调用覆盖。当前没有活动会话时为
338
- * no-op(无处记录),插件应在会话就绪后重新判定。
533
+ * 用户随后用减号手动隐藏的结果不会被重复调用覆盖。缺省作用于宿主当前显示的
534
+ * 会话;工具 handler 与后台任务应通过 `options.cwd` 传入自己的会话 cwd。既没有
535
+ * 显式 cwd、也没有活动会话时为 no-op(无处记录)。
339
536
  */
340
- setActivityTabVisible(tabId: string, visible: boolean): void;
537
+ setActivityTabVisible(tabId: string, visible: boolean, options?: PluginActivityTabTargetOptions): void;
341
538
  /**
342
- * 直接设置活动面板宽度:像素值,或 `"max"` 表示当前窗口下的最大宽度(宿主仍
343
- * 会夹到自己的 min/max 内,必要时自动收起侧边栏)。
539
+ * 直接设置活动面板宽度:像素值,或 `"max"` 表示「跟随窗口拉满」(宿主仍会夹到
540
+ * 自己的 min/max 内,必要时自动收起侧边栏)。
541
+ *
542
+ * `"max"` 是一种持续状态而非一次性求值:窗口尺寸变化时面板跟着变宽变窄,直到
543
+ * 用户拖动分隔条或有人写入具体像素为止。传数字则是一次性的固定宽度。
344
544
  *
345
545
  * 与 `openActivityTab(id, { width })` 的区别:那里的宽度只在标签卡首次 attach
346
546
  * 时生效(避免 activate 重放覆盖用户手拖的宽度);这个是命令式的,每次调用都
@@ -348,9 +548,10 @@ export interface PluginUiApi {
348
548
  */
349
549
  setActivityPanelWidth(width: number | "max"): void;
350
550
  /**
351
- * Bind or clear plugin-owned one-shot context for the next outgoing prompt.
352
- * The host renders its label/icon, merges metadata and hidden instructions,
353
- * then clears it after send or when the user closes the capsule.
551
+ * Bind or clear plugin-owned context for outgoing prompts. The host renders
552
+ * its label/icon and carries a structured `context` as a versioned snapshot.
553
+ * Attachments default to one-shot; `lifecycle: "sticky"` remains until the
554
+ * plugin or user clears the capsule.
354
555
  */
355
556
  setPromptAttachment(attachment: PluginPromptAttachment | null): void;
356
557
  /**
@@ -380,6 +581,13 @@ export interface PluginUiApi {
380
581
  * can only write what it already rendered.
381
582
  */
382
583
  copyImage(dataUrl: string): Promise<void>;
584
+ /**
585
+ * Hand a URL to the OS default browser (Electron `shell.openExternal`) —
586
+ * NOT the in-app browser panel. Only `http:`/`https:` are accepted; the host
587
+ * rejects every other protocol, so this cannot be used to launch arbitrary
588
+ * schemes. Needs `shell.openExternal`.
589
+ */
590
+ openExternal(url: string): Promise<void>;
383
591
  /**
384
592
  * Show a global toast in the host UI (bottom-right). No permission required.
385
593
  * Prefer this over swallowing errors into opaque UI copy: pass `error` so
package/dist/ui.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ComponentType, ReactNode } from \"react\";\nimport type { ConversationMessage } from \"./conversation.js\";\nimport type { Disposable } from \"./disposable.js\";\nimport type { PluginImageRef } from \"./images.js\";\nimport type { PluginPromptAttachment } from \"./prompt-attachment.js\";\nimport type { ConversationScenario } from \"./scenario.js\";\nimport type { PluginShortcutScopeContribution } from \"./shortcuts.js\";\n\nexport interface PluginGlobalSlotContribution {\n\tid: string;\n\tcomponent: ComponentType;\n}\n\nexport interface PluginAudioMetadata {\n\t/** Embedded title; preview renderers should fall back to file.name. */\n\ttitle?: string;\n\tartist?: string;\n\t/** Embedded cover art as a Data URL when available. */\n\tcoverDataUrl?: string;\n}\n\nexport interface PluginPreviewUrlOptions {\n\t/** Optional media hint for ambiguous containers such as webm. */\n\tmediaKind?: \"audio\" | \"video\";\n}\n\n/**\n * The file handed to a file-preview plugin component. The host does NOT\n * pre-read or guess encoding — the plugin decides whether to read text or\n * bytes. `path` is also exposed for plugins that prefer native fetch.\n */\nexport interface PluginPreviewFile {\n\tpath: string | null;\n\tname: string;\n\textension: string;\n\tmime: string;\n\tsize: number;\n\t/** Read the file as a UTF-8 string. */\n\treadText(): Promise<string>;\n\t/** Read the raw file bytes. */\n\treadBytes(): Promise<ArrayBuffer>;\n\t/** A fetchable streaming URL for the file (Range-capable). */\n\tgetUrl(options?: PluginPreviewUrlOptions): string;\n\t/**\n\t * Subscribe to on-disk changes of this file. The listener fires (debounced\n\t * by the host) whenever the file's contents change, letting previews update\n\t * live. Returns a Disposable; call dispose() to stop watching. A no-op for\n\t * files without a real path (url-only sources).\n\t */\n\twatch(listener: () => void): Disposable;\n\t/**\n\t * Host-provided audio metadata for local media files. Returns null when the\n\t * host cannot provide metadata, e.g. url-only sources or unsupported files.\n\t */\n\tgetAudioMetadata?(): Promise<PluginAudioMetadata | null>;\n}\n\nexport interface PluginFilePreviewProps {\n\tfile: PluginPreviewFile;\n}\n\nexport interface PluginFilePreviewContribution {\n\t/** Lower-case extensions without the dot, e.g. [\"svg\"]. */\n\textensions: string[];\n\tcomponent: ComponentType<PluginFilePreviewProps>;\n}\n\n/**\n * An activity-panel tab contributed by a plugin. Registering only adds the\n * tab to the \"addable pool\" — it renders only after the user attaches it in\n * the activity panel (scoped by session cwd).\n */\nexport interface PluginActivityTabContribution {\n\tid: string;\n\tlabel: string;\n\t/** Tab icon as a React node (not an iconify class string). */\n\ticon?: ReactNode;\n\tcomponent: ComponentType;\n\t/**\n\t * 允许该标签卡出现的对话场景 slug 列表(镜像工具的 scope_use)。**fail-closed**:\n\t * 未声明/空数组 = 任何会话都不显示;声明后仅在列出的场景里出现(如\n\t * `[\"project\", \"conversation\"]`)。会话页插槽据此随对话类型显隐。\n\t */\n\tscope_use?: readonly ConversationScenario[];\n\t/**\n\t * 注册后是否默认在标签栏里(缺省 `true`)。声明 `false` 表示「出现条件由我\n\t * 自己决定」——注册只是入池,之后由 {@link PluginUiApi.setActivityTabVisible}\n\t * 或 {@link PluginUiApi.openActivityTab} 决定何时上栏(如 git 只在仓库目录、\n\t * 工作台跟随输入栏 toggle)。无论哪种,用户仍可用减号手动隐藏。\n\t */\n\tinitiallyVisible?: boolean;\n}\n\n/** Options for {@link PluginUiApi.openActivityTab}. */\nexport interface PluginOpenActivityTabOptions {\n\t/**\n\t * Desired panel width as it opens: a pixel number, or `\"max\"` for the widest\n\t * the current window allows. The host clamps to its min/max bounds. Omit to\n\t * keep the user's current width.\n\t */\n\twidth?: number | \"max\";\n}\n\nexport interface PluginCaptureRegion {\n\tx: number;\n\ty: number;\n\twidth: number;\n\theight: number;\n}\n\n/**\n * Decoration a plugin contributes to the next outgoing prompt while its input\n * action is active. `metadata` is shallow-merged into the prompt request and\n * `instructions` are appended as hidden, model-visible guidance.\n */\nexport interface PluginPromptDecoration {\n\tmetadata?: Record<string, unknown>;\n\t/** Hidden plugin-owned instructions appended to the next agent turn. */\n\tinstructions?: string[];\n}\n\n/**\n * An action button shown in a row beneath the AI input bar. Rendered as a\n * toggle: clicking activates, clicking again deactivates. While active, the\n * host calls `decoratePrompt()` before each send and merges the returned\n * metadata into the outgoing prompt request. The plugin owns any side state\n * (it is the same Module Federation instance as its other slots).\n */\nexport interface PluginInputActionContribution {\n\tid: string;\n\tlabel: string;\n\t/** Button icon as a React node (not an iconify class string). */\n\ticon?: ReactNode;\n\t/** Whether the action begins in the active state. Defaults to false. */\n\tdefaultActive?: boolean;\n\t/**\n\t * 该输入动作所依赖的 agent 工具名。设置后,仅当该工具在当前会话处于激活(按工具的\n\t * scope_use 解析)时才显示这个 badge——避免在工具被场景屏蔽(如批量任务)时仍显示一个\n\t * 点了也无效的开关。不设则始终显示。例如「图像生成」设为 \"generate_image\"。\n\t */\n\trequiresActiveTool?: string;\n\t/**\n\t * 允许该 toggle 出现的对话场景 slug 列表(镜像工具的 scope_use)。**fail-closed**:\n\t * 未声明/空数组 = 任何会话都不显示;声明后仅在列出的场景里出现。与 `requiresActiveTool`\n\t * 取「与」:两者都满足才显示。\n\t */\n\tscope_use?: readonly ConversationScenario[];\n\t/**\n\t * When true, this plugin's agent contributions (tools / skills / MCP /\n\t * systemPrompt) and Activity Tabs are hard-isolated: inactive until this\n\t * toggle is on (ADR-0041, knowledgeMode-style). Default false.\n\t */\n\thardIsolation?: boolean;\n\t/**\n\t * Fired when the user toggles the action; `active` is the new state. Return\n\t * `false` when `active` is true to VETO the activation (e.g. the plugin needs\n\t * configuration first) — the toggle stays off. Deactivation can't be vetoed.\n\t */\n\tonToggle?(active: boolean): boolean | void;\n\t/**\n\t * Called by the host before sending while the action is active. The\n\t * returned metadata is merged into the outgoing prompt request.\n\t */\n\tdecoratePrompt?(): PluginPromptDecoration | void;\n}\n\n/**\n * A declarative, serializable descriptor for one card rendered beneath a\n * message. `type` selects a registered card renderer; `key` drives cross-turn\n * dedup (same key across turns = one logical card, shown only under its latest\n * anchor); `payload` carries STABLE REFERENCES (e.g. image ids), not a content\n * snapshot — the renderer resolves live state from it. `title`/`icon` label the\n * card's tab (override the renderer's registration defaults). Rides a tool\n * result's out-of-band `details.cards`; crosses the agent→host boundary, so\n * `icon` is an icon-symbol string, not a node.\n */\nexport interface CardDescriptor {\n\ttype: string;\n\tkey?: string;\n\tpayload?: unknown;\n\ttitle?: string;\n\ticon?: string;\n}\n\n/** A pending (in-flight) tool call handed to a renderer's `pendingFor`. */\nexport interface PluginPendingToolCall {\n\ttoolName: string;\n\targs: Record<string, unknown>;\n}\n\n/**\n * Props for a card renderer. `descriptor` is the card's data; `pending` is true\n * while it was synthesized from an in-flight tool (render a skeleton); `message`\n * is the anchoring conversation message.\n */\nexport interface PluginCardProps {\n\tdescriptor: CardDescriptor;\n\tpending: boolean;\n\tmessage: ConversationMessage;\n}\n\n/**\n * A card renderer registered by a plugin, keyed by `type`. A descriptor whose\n * `type` matches is rendered by `component`. `title`/`icon` are the default tab\n * label/icon (a descriptor may override `title`). `pendingFor`, given an\n * in-flight tool call, returns a provisional descriptor so a skeleton card\n * appears (and claims a tab) before the tool's result lands — or null when this\n * renderer doesn't handle that tool. The `type` must be globally unique across\n * plugins (convention: prefix with the plugin id, e.g. \"image-gen:preview\").\n */\nexport interface PluginCardRendererContribution {\n\ttype: string;\n\tcomponent: ComponentType<PluginCardProps>;\n\ttitle?: string;\n\t/** Default tab icon as a React node (not an iconify class string). */\n\ticon?: ReactNode;\n\tpendingFor?: (toolCall: PluginPendingToolCall) => CardDescriptor | null;\n}\n\n/** A tool call handed to a tool-call slot renderer. */\nexport interface PluginToolCallSlotToolCall {\n\ttoolCallId: string;\n\ttoolName: string;\n\targs: Record<string, unknown>;\n\tstatus: \"pending\" | \"success\" | \"error\";\n\tresult?: string;\n\tisError?: boolean;\n}\n\nexport interface PluginToolCallSlotProps {\n\ttoolCall: PluginToolCallSlotToolCall;\n}\n\n/**\n * A component that **replaces the host's default inline transcript rendering**\n * for a specific tool (matched by `toolName`; first registered renderer wins).\n * This is how a plugin renders rich UI for its OWN agent tool's output — plugin\n * tools cannot emit `details.cards`, so the message card system (registerCardRenderer)\n * does not serve them; the tool-call slot does.\n */\nexport interface PluginToolCallSlotContribution {\n\tid: string;\n\ttoolName: string;\n\tcomponent: ComponentType<PluginToolCallSlotProps>;\n}\n\n/**\n * A card rendered at the bottom of the message list for the LATEST turn — NOT\n * bound to a tool call. The host mounts `component` in the footer slot and\n * re-mounts it each session/turn; the plugin owns visibility entirely (it reads\n * live state via SDK hooks — `useActiveConversation`, `useConversationMessages`,\n * `ctx.conversation.on(\"turn-end\")` — and returns `null` to render nothing,\n * e.g. git renders only inside a repo that has changes). `scope_use` gates which\n * conversation scenarios it may appear in (**fail-closed**, mirrors the other\n * slots). `id` must be unique within the plugin.\n */\nexport interface PluginTurnCardContribution {\n\tid: string;\n\tcomponent: ComponentType;\n\t/**\n\t * 允许该 turn 卡出现的对话场景 slug 列表(镜像其它插槽的 scope_use)。**fail-closed**:\n\t * 未声明/空数组 = 任何会话都不显示;声明后仅在列出的场景里出现(如 `[\"project\"]`)。\n\t */\n\tscope_use?: readonly ConversationScenario[];\n}\n\n/**\n * Options for {@link PluginUiApi.notify}. Surfaces a host toast so users can\n * see plugin failures without opening DevTools.\n */\nexport interface PluginNotifyOptions {\n\t/** Toast body. Required even when `error` is set (user-facing summary). */\n\tmessage: string;\n\t/** Defaults to plugin display name when omitted. */\n\ttitle?: string;\n\t/** Defaults to `\"info\"`; when `error` is set and variant omitted, host uses `\"error\"`. */\n\tvariant?: \"info\" | \"success\" | \"warning\" | \"error\";\n\t/**\n\t * Optional failure cause. Host formats message + stack and adds a\n\t * \"复制堆栈\" action so the user can paste it into bug reports / chat.\n\t */\n\terror?: unknown;\n\t/**\n\t * Auto-dismiss delay in ms. `0` keeps the toast until dismissed.\n\t * Defaults: sticky (`0`) when `error` is present, otherwise host default (~4s).\n\t */\n\tdurationMs?: number;\n}\n\nexport interface PluginUiApi {\n\tregisterGlobalSlot(contribution: PluginGlobalSlotContribution): Disposable;\n\t/**\n\t * Register a preview component keyed by file extension. The host dispatches\n\t * registered extensions before its built-in fallback renderers; first\n\t * registrant wins on conflict.\n\t */\n\tregisterFilePreview(contribution: PluginFilePreviewContribution): Disposable;\n\t/**\n\t * Register an activity-panel tab into the addable pool. The user attaches\n\t * it manually via the panel's \"+\" picker; attach records are keyed by the\n\t * session cwd.\n\t */\n\tregisterActivityTab(contribution: PluginActivityTabContribution): Disposable;\n\t/**\n\t * Register a toggle action shown beneath the AI input bar. While active,\n\t * its `decoratePrompt()` annotates the next outgoing prompt.\n\t */\n\tregisterInputAction(contribution: PluginInputActionContribution): Disposable;\n\t/**\n\t * Register a card renderer keyed by `type`. Cards rendered beneath a message\n\t * come from tool results' out-of-band `details.cards` (or `pendingFor` for\n\t * in-flight tools); the host resolves each card's `type` to a registered\n\t * renderer. Multiple cards under one message are shown as a tab-switcher\n\t * (\"收纳\") or a flat list.\n\t */\n\tregisterCardRenderer(contribution: PluginCardRendererContribution): Disposable;\n\t/**\n\t * Register a renderer that replaces the host's default inline transcript UI\n\t * for a tool call (matched by `toolName`). The complement to card renderers:\n\t * use it to render UI for a plugin's own agent tool output.\n\t */\n\tregisterToolCallSlot(contribution: PluginToolCallSlotContribution): Disposable;\n\t/**\n\t * Register a card rendered at the bottom of the message list for the latest\n\t * turn — NOT bound to a tool call. The host mounts the component in the\n\t * footer slot; the plugin owns visibility (return `null` to render nothing,\n\t * e.g. git renders only in a repo with changes). Gated by `scope_use`\n\t * (fail-closed). Needs the `ui.slot.turn-card` permission.\n\t */\n\tregisterTurnCard(contribution: PluginTurnCardContribution): Disposable;\n\t/**\n\t * Register a keyboard shortcut scope on the host's shared ShortcutScopeStack\n\t * (same path as host UI: modal > overlay > surface > app). Needs\n\t * `ui.shortcuts.register`. Plugins cannot use kind `\"app\"` (reserved for\n\t * host-configurable global actions). Prefer {@link usePluginShortcutScope}\n\t * from React components with a module-captured `registerShortcutScope`.\n\t */\n\tregisterShortcutScope(contribution: PluginShortcutScopeContribution): Disposable;\n\t/**\n\t * Programmatically attach (if needed) and activate one of this plugin's\n\t * own activity tabs in the current conversation's activity panel. `tabId`\n\t * is the contribution id passed to registerActivityTab. Any payload (e.g.\n\t * which image to edit) is passed via the plugin's own in-memory state.\n\t *\n\t * Pass `options.width` to size the panel as it opens — a pixel number, or\n\t * `\"max\"` to expand it to the widest the current window allows (the host\n\t * still clamps to its min/max and auto-hides the sidebar when needed). Omit\n\t * to leave the user's current width untouched.\n\t */\n\topenActivityTab(tabId: string, options?: PluginOpenActivityTabOptions): void;\n\t/**\n\t * 把本插件的某个活动面板标签卡在当前会话里上栏 / 下栏,**不激活也不展开\n\t * 面板**——`openActivityTab` 是「用户此刻要看它」,这个是「它现在该不该在\n\t * 栏里」。配合 `initiallyVisible: false` 使用,插件即可完全掌握自己标签卡的\n\t * 出现条件(如 git 只在仓库目录里上栏、工作台跟随输入栏 toggle)。\n\t *\n\t * 上栏记录按会话 cwd 持久化(ADR-0026),所以只需在条件变化时调用一次;\n\t * 用户随后用减号手动隐藏的结果不会被重复调用覆盖。当前没有活动会话时为\n\t * no-op(无处记录),插件应在会话就绪后重新判定。\n\t */\n\tsetActivityTabVisible(tabId: string, visible: boolean): void;\n\t/**\n\t * 直接设置活动面板宽度:像素值,或 `\"max\"` 表示当前窗口下的最大宽度(宿主仍\n\t * 会夹到自己的 min/max 内,必要时自动收起侧边栏)。\n\t *\n\t * 与 `openActivityTab(id, { width })` 的区别:那里的宽度只在标签卡首次 attach\n\t * 时生效(避免 activate 重放覆盖用户手拖的宽度);这个是命令式的,每次调用都\n\t * 生效,供插件在自己的标签卡被激活、进入某种视图时按需调整。用户随后仍可拖动。\n\t */\n\tsetActivityPanelWidth(width: number | \"max\"): void;\n\t/**\n\t * Bind or clear plugin-owned one-shot context for the next outgoing prompt.\n\t * The host renders its label/icon, merges metadata and hidden instructions,\n\t * then clears it after send or when the user closes the capsule.\n\t */\n\tsetPromptAttachment(attachment: PluginPromptAttachment | null): void;\n\t/**\n\t * Open the host's global full-screen image previewer for the given image.\n\t * Only the image reference (id/url) crosses over — bytes stay out-of-band.\n\t *\n\t * Pass `group` (e.g. all images of the message) to open as an image group:\n\t * the previewer shows a thumbnail strip + arrows and starts at `ref`.\n\t */\n\tpreviewImage(ref: PluginImageRef, group?: PluginImageRef[]): void;\n\t/**\n\t * Open the app settings, scrolled to and highlighting THIS plugin's own\n\t * settings section (e.g. so the user can fill in a required API key/model).\n\t * The host owns the navigation; the plugin only asks to jump there.\n\t */\n\topenPluginSettings(): void;\n\t/**\n\t * Capture a rectangle in the current Vetta window and open the host save\n\t * dialog. Coordinates use renderer DIP values such as getBoundingClientRect().\n\t * Requires `ui.slot.activity-tab`.\n\t */\n\tcaptureRegion(rect: PluginCaptureRegion, defaultFileName: string): Promise<string | null>;\n\t/**\n\t * Copy an image to the system clipboard. Takes a `data:image/...;base64,`\n\t * URL and goes through the native clipboard, so it does not depend on the\n\t * renderer's `ClipboardItem` support. No permission required — the plugin\n\t * can only write what it already rendered.\n\t */\n\tcopyImage(dataUrl: string): Promise<void>;\n\t/**\n\t * Show a global toast in the host UI (bottom-right). No permission required.\n\t * Prefer this over swallowing errors into opaque UI copy: pass `error` so\n\t * the host attaches a one-click \"copy stack\" action for the user.\n\t *\n\t * Capture `ctx.ui.notify` in `activate` if React components need it\n\t * (components do not receive `ctx`).\n\t */\n\tnotify(options: PluginNotifyOptions): void;\n}\n"]}
1
+ {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ComponentType, ReactNode } from \"react\";\nimport type { ConversationMessage } from \"./conversation.js\";\nimport type { Disposable } from \"./disposable.js\";\nimport type { PluginImageRef } from \"./images.js\";\nimport type { PluginPromptAttachment } from \"./prompt-attachment.js\";\nimport type { ConversationScenario } from \"./scenario.js\";\nimport type { PluginShortcutScopeContribution } from \"./shortcuts.js\";\n\nexport interface PluginGlobalSlotContribution {\n\tid: string;\n\tcomponent: ComponentType;\n}\n\n/**\n * Props handed to a workspace-view component. A workspace view owns the whole\n * content area, so unlike panel slots it is addressed by its own route and can\n * be deep-linked; the host passes back its identity so one component can serve\n * several registered views.\n */\nexport interface PluginWorkspaceViewProps {\n\t/** The contribution id as registered (NOT namespaced). */\n\tviewId: string;\n\t/** The owning plugin id. */\n\tpluginId: string;\n}\n\n/**\n * Badge tone. The host maps it to its own theme colors — a plugin cannot pick a\n * raw color, so badges stay consistent with the built-in navigation entries.\n */\nexport type PluginNavBadgeTone = \"accent\" | \"danger\" | \"default\" | \"warning\";\n\n/**\n * A badge on the plugin's sidebar navigation entry.\n *\n * `beta` is the host's own preset — it renders exactly like the built-in\n * 知识库 Beta 标识, wording included (and localized by the host), so plugins do\n * not have to spell \"Beta\" themselves in every locale.\n *\n * `count` renders `99+` past 99 and disappears at 0 — an unread badge showing\n * \"0\" is worse than no badge.\n */\nexport type PluginNavBadge =\n\t| { kind: \"beta\" }\n\t| { kind: \"text\"; text: string; tone?: PluginNavBadgeTone }\n\t| { kind: \"count\"; count: number; tone?: PluginNavBadgeTone }\n\t| { kind: \"dot\"; tone?: PluginNavBadgeTone };\n\n/**\n * A **workspace view**(工作区视图): a full-page surface a plugin contributes to\n * the host's primary navigation. It is the plugin counterpart of a built-in page\n * such as 自动化 / 知识库 — the host gives it its own route\n * (`/workspace/<pluginId>/<viewId>`) and a sidebar entry, and the plugin owns the\n * entire content area while it is open.\n *\n * Use it for standalone workbenches that are NOT bound to one conversation\n * (dashboards, boards, consoles). Conversation-scoped UI belongs in an\n * {@link PluginActivityTabContribution activity tab}; floating UI belongs in a\n * {@link PluginGlobalSlotContribution global slot}.\n */\nexport interface PluginWorkspaceViewContribution {\n\t/** Unique within the plugin; the host namespaces it as `${pluginId}:${id}`. */\n\tid: string;\n\t/** Sidebar entry label. Supports `%catalogKey%` i18n lookup. */\n\tlabel: string;\n\t/**\n\t * Sidebar entry icon as an **iconify class string** (e.g.\n\t * `\"icon-[solar--widget-5-linear]\"`) — NOT a React node, because the host\n\t * renders it inside its own nav button and persists nav layout by key.\n\t *\n\t * Omit it to inherit the plugin's own `plugin.json` icon: a packaged image\n\t * (svg / png / webp …) is masked with the theme's foreground color, so a plugin\n\t * that ships artwork does not have to find a lookalike in an icon set. Set\n\t * {@link PluginWorkspaceViewContribution.iconTint} to `false` to keep its colors.\n\t */\n\ticon?: string;\n\t/**\n\t * Whether an image icon is tinted with the theme's foreground color. Defaults to\n\t * `true`, which keeps the entry consistent with the built-in navigation and legible\n\t * in every theme.\n\t *\n\t * Pass `false` for a full-color logo. Weigh it first: the entry renders at 16px\n\t * beside monochrome built-ins, and a fixed-color image cannot follow the active\n\t * theme — a dark logo can disappear on a dark sidebar. Has no effect on Iconify\n\t * class icons, which are always tinted.\n\t */\n\ticonTint?: boolean;\n\t/** Optional one-line description; shown as the nav entry tooltip. */\n\tdescription?: string;\n\t/**\n\t * Initial badge on the sidebar entry. `{ kind: \"text\" }` supports the same\n\t * `%catalogKey%` i18n lookup as {@link label}. Update it at runtime with\n\t * {@link PluginUiApi.setWorkspaceViewBadge}.\n\t */\n\tbadge?: PluginNavBadge;\n\t/** Zero-props aside from {@link PluginWorkspaceViewProps}. */\n\tcomponent: ComponentType<PluginWorkspaceViewProps>;\n\t/** Sort hint among this plugin's own views (ascending). Defaults to 0. */\n\tnavOrder?: number;\n}\n\n/**\n * What a workspace view puts into the **host page header** — the window's top\n * bar that also carries the drag region, the macOS traffic-light gutter and the\n * sidebar trigger.\n *\n * Without this the header still renders, showing the app name above the\n * plugin's own toolbar — two stacked bars. Set `hideTitle` and move the view's\n * toolbar in here and the surface gets a single, host-consistent header.\n *\n * `left` / `right` are ReactNodes rendered by the host inside that bar (left:\n * right after the sidebar trigger; right: before the window controls). They are\n * rendered in the plugin's i18n + CSS scope, so `useTranslation()` and the\n * plugin's own classes work as usual. Pass fresh nodes whenever the view's\n * state changes — this is a live setter, not a one-time registration.\n */\nexport interface PluginWorkspaceViewHeader {\n\t/** Replaces the host title text. Supports `%catalogKey%` i18n lookup. */\n\ttitle?: string;\n\t/** Drop the host title entirely (use when {@link left} carries its own). */\n\thideTitle?: boolean;\n\t/** Rendered in the header's left cluster, after the sidebar trigger. */\n\tleft?: ReactNode;\n\t/** Rendered in the header's action cluster, before the window controls. */\n\tright?: ReactNode;\n\t/**\n\t * Float the host header OVER the view instead of stacking above it. The view\n\t * then owns the full content height, and its top `~44px` sit under the\n\t * transparent header strip (window drag region / traffic lights / sidebar\n\t * trigger all keep working on top). Use for immersive full-page surfaces\n\t * whose own hero starts at the very top; leave unset for toolbar-style\n\t * headers that need their own opaque row.\n\t */\n\timmersive?: boolean;\n}\n\nexport interface PluginAudioMetadata {\n\t/** Embedded title; preview renderers should fall back to file.name. */\n\ttitle?: string;\n\tartist?: string;\n\t/** Embedded cover art as a Data URL when available. */\n\tcoverDataUrl?: string;\n}\n\nexport interface PluginPreviewUrlOptions {\n\t/** Optional media hint for ambiguous containers such as webm. */\n\tmediaKind?: \"audio\" | \"video\";\n}\n\n/**\n * The file handed to a file-preview plugin component. The host does NOT\n * pre-read or guess encoding — the plugin decides whether to read text or\n * bytes. `path` is also exposed for plugins that prefer native fetch.\n */\nexport interface PluginPreviewFile {\n\tpath: string | null;\n\tname: string;\n\textension: string;\n\tmime: string;\n\tsize: number;\n\t/** Read the file as a UTF-8 string. */\n\treadText(): Promise<string>;\n\t/** Read the raw file bytes. */\n\treadBytes(): Promise<ArrayBuffer>;\n\t/** A fetchable streaming URL for the file (Range-capable). */\n\tgetUrl(options?: PluginPreviewUrlOptions): string;\n\t/**\n\t * Subscribe to on-disk changes of this file. The listener fires (debounced\n\t * by the host) whenever the file's contents change, letting previews update\n\t * live. Returns a Disposable; call dispose() to stop watching. A no-op for\n\t * files without a real path (url-only sources).\n\t */\n\twatch(listener: () => void): Disposable;\n\t/**\n\t * Host-provided audio metadata for local media files. Returns null when the\n\t * host cannot provide metadata, e.g. url-only sources or unsupported files.\n\t */\n\tgetAudioMetadata?(): Promise<PluginAudioMetadata | null>;\n}\n\nexport interface PluginFilePreviewProps {\n\tfile: PluginPreviewFile;\n}\n\nexport interface PluginFilePreviewContribution {\n\t/** Lower-case extensions without the dot, e.g. [\"svg\"]. */\n\textensions: string[];\n\tcomponent: ComponentType<PluginFilePreviewProps>;\n}\n\n/**\n * An activity-panel tab contributed by a plugin. Registering only adds the\n * tab to the \"addable pool\" — it renders only after the user attaches it in\n * the activity panel (scoped by session cwd).\n */\n/** Host residency policy for an activity-panel tab. */\nexport type PluginActivityTabRetention = \"active-only\" | \"warm\" | \"pinned\";\n\nexport interface PluginActivityTabContribution {\n\tid: string;\n\tlabel: string;\n\t/**\n\t * Tab icon as a React node (not an iconify class string).\n\t * Omit to inherit the host-resolved brand icon from `plugin.json#icon`\n\t * (`ctx.plugin.iconUrl`). Prefer omitting for package brand icons so the\n\t * plugin never loads assets via absolute `/…` paths or host protocols.\n\t */\n\ticon?: ReactNode;\n\tcomponent: ComponentType;\n\t/**\n\t * 允许该标签卡出现的对话场景 slug 列表(镜像工具的 scope_use)。**fail-closed**:\n\t * 未声明/空数组 = 任何会话都不显示;声明后仅在列出的场景里出现(如\n\t * `[\"project\", \"conversation\"]`)。会话页插槽据此随对话类型显隐。\n\t */\n\tscope_use?: readonly ConversationScenario[];\n\t/**\n\t * 注册后是否默认在标签栏里(缺省 `true`)。声明 `false` 表示「出现条件由我\n\t * 自己决定」——注册只是入池,之后由 {@link PluginUiApi.setActivityTabVisible}\n\t * 或 {@link PluginUiApi.openActivityTab} 决定何时上栏(如 git 只在仓库目录、\n\t * 工作台跟随输入栏 toggle)。无论哪种,用户仍可用减号手动隐藏。\n\t */\n\tinitiallyVisible?: boolean;\n\t/**\n\t * Component residency while the tab is inactive. Defaults to `\"warm\"`:\n\t * visited tabs stay mounted in the host's bounded LRU cache. Use\n\t * `\"active-only\"` for cheap, stateless content and `\"pinned\"` for runtimes\n\t * that must never be evicted while available.\n\t */\n\tretention?: PluginActivityTabRetention;\n\t/**\n\t * @deprecated Use {@link PluginActivityTabContribution.retention}. `true`\n\t * maps to `\"pinned\"`; `false` maps to `\"active-only\"`.\n\t */\n\tkeepAliveWhenAvailable?: boolean;\n}\n\n/** Explicit conversation scope for an activity-tab command. */\nexport interface PluginActivityTabTargetOptions {\n\t/**\n\t * Absolute cwd whose activity-tab state should be changed. Omit to target\n\t * the conversation currently displayed by the host. Tool handlers and\n\t * background work should pass the triggering session's cwd so a foreground\n\t * navigation change cannot redirect the command to another conversation.\n\t */\n\tcwd?: string;\n}\n\n/** Options for {@link PluginUiApi.openActivityTab}. */\nexport interface PluginOpenActivityTabOptions extends PluginActivityTabTargetOptions {\n\t/**\n\t * Desired panel width as it opens: a pixel number, or `\"max\"` to track the\n\t * widest the window allows — `\"max\"` keeps following window resizes until the\n\t * user drags the divider. The host clamps to its min/max bounds. Omit to keep\n\t * the user's current width.\n\t */\n\twidth?: number | \"max\";\n}\n\nexport interface PluginCaptureRegion {\n\tx: number;\n\ty: number;\n\twidth: number;\n\theight: number;\n}\n\n/**\n * Decoration a plugin contributes to the next outgoing prompt while its input\n * action is active. `metadata` is shallow-merged into the prompt request and\n * `instructions` are appended as hidden, model-visible guidance.\n */\nexport interface PluginPromptDecoration {\n\tmetadata?: Record<string, unknown>;\n\t/** Hidden plugin-owned instructions appended to the next agent turn. */\n\tinstructions?: string[];\n}\n\n/**\n * An action button shown in a row beneath the AI input bar. Rendered as a\n * toggle: clicking activates, clicking again deactivates. While active, the\n * host calls `decoratePrompt()` before each send and merges the returned\n * metadata into the outgoing prompt request. The plugin owns any side state\n * (it is the same Module Federation instance as its other slots).\n */\nexport interface PluginInputActionContribution {\n\tid: string;\n\tlabel: string;\n\t/** Button icon as a React node (not an iconify class string). */\n\ticon?: ReactNode;\n\t/** Whether the action begins in the active state. Defaults to false. */\n\tdefaultActive?: boolean;\n\t/**\n\t * 该输入动作所依赖的 agent 工具名。设置后,仅当该工具在当前会话处于激活(按工具的\n\t * scope_use 解析)时才显示这个 badge——避免在工具被场景屏蔽(如批量任务)时仍显示一个\n\t * 点了也无效的开关。不设则始终显示。例如「图像生成」设为 \"generate_image\"。\n\t */\n\trequiresActiveTool?: string;\n\t/**\n\t * 允许该 toggle 出现的对话场景 slug 列表(镜像工具的 scope_use)。**fail-closed**:\n\t * 未声明/空数组 = 任何会话都不显示;声明后仅在列出的场景里出现。与 `requiresActiveTool`\n\t * 取「与」:两者都满足才显示。\n\t */\n\tscope_use?: readonly ConversationScenario[];\n\t/**\n\t * When true, this plugin's agent contributions (tools / skills / MCP /\n\t * systemPrompt) and Activity Tabs are hard-isolated: inactive until this\n\t * toggle is on (ADR-0041, knowledgeMode-style). Default false.\n\t */\n\thardIsolation?: boolean;\n\t/**\n\t * Fired when the user toggles the action; `active` is the new state. Return\n\t * `false` when `active` is true to VETO the activation (e.g. the plugin needs\n\t * configuration first) — the toggle stays off. Deactivation can't be vetoed.\n\t */\n\tonToggle?(active: boolean): boolean | void;\n\t/**\n\t * Called by the host before sending while the action is active. The\n\t * returned metadata is merged into the outgoing prompt request.\n\t */\n\tdecoratePrompt?(): PluginPromptDecoration | void;\n}\n\n/**\n * A declarative, serializable descriptor for one card rendered beneath a\n * message. `type` selects a registered card renderer; `key` drives cross-turn\n * dedup (same key across turns = one logical card, shown only under its latest\n * anchor); `payload` carries STABLE REFERENCES (e.g. image ids), not a content\n * snapshot — the renderer resolves live state from it. `title`/`icon` label the\n * card's tab (override the renderer's registration defaults). Rides a tool\n * result's out-of-band `details.cards`; crosses the agent→host boundary, so\n * `icon` is an icon-symbol string, not a node.\n */\nexport interface CardDescriptor {\n\ttype: string;\n\tkey?: string;\n\tpayload?: unknown;\n\ttitle?: string;\n\ticon?: string;\n}\n\n/** A pending (in-flight) tool call handed to a renderer's `pendingFor`. */\nexport interface PluginPendingToolCall {\n\ttoolName: string;\n\targs: Record<string, unknown>;\n}\n\n/**\n * Props for a card renderer. `descriptor` is the card's data; `pending` is true\n * while it was synthesized from an in-flight tool (render a skeleton); `message`\n * is the anchoring conversation message.\n */\nexport interface PluginCardProps {\n\tdescriptor: CardDescriptor;\n\tpending: boolean;\n\tmessage: ConversationMessage;\n}\n\n/**\n * A card renderer registered by a plugin, keyed by `type`. A descriptor whose\n * `type` matches is rendered by `component`. `title`/`icon` are the default tab\n * label/icon (a descriptor may override `title`). `pendingFor`, given an\n * in-flight tool call, returns a provisional descriptor so a skeleton card\n * appears (and claims a tab) before the tool's result lands — or null when this\n * renderer doesn't handle that tool. The `type` must be globally unique across\n * plugins (convention: prefix with the plugin id, e.g. \"image-gen:preview\").\n */\nexport interface PluginCardRendererContribution {\n\ttype: string;\n\tcomponent: ComponentType<PluginCardProps>;\n\ttitle?: string;\n\t/** Default tab icon as a React node (not an iconify class string). */\n\ticon?: ReactNode;\n\tpendingFor?: (toolCall: PluginPendingToolCall) => CardDescriptor | null;\n}\n\n/** A tool call handed to a tool-call slot renderer. */\nexport interface PluginToolCallSlotToolCall {\n\ttoolCallId: string;\n\ttoolName: string;\n\targs: Record<string, unknown>;\n\tstatus: \"pending\" | \"success\" | \"error\";\n\tresult?: string;\n\tisError?: boolean;\n}\n\nexport interface PluginToolCallSlotProps {\n\ttoolCall: PluginToolCallSlotToolCall;\n}\n\n/**\n * A component that **replaces the host's default inline transcript rendering**\n * for a specific tool (matched by `toolName`; first registered renderer wins).\n * This is how a plugin renders rich UI for its OWN agent tool's output — plugin\n * tools cannot emit `details.cards`, so the message card system (registerCardRenderer)\n * does not serve them; the tool-call slot does.\n */\nexport interface PluginToolCallSlotContribution {\n\tid: string;\n\ttoolName: string;\n\tcomponent: ComponentType<PluginToolCallSlotProps>;\n}\n\n/**\n * A card rendered at the bottom of the message list for the LATEST turn — NOT\n * bound to a tool call. The host mounts `component` in the footer slot and\n * re-mounts it each session/turn; the plugin owns visibility entirely (it reads\n * live state via SDK hooks — `useActiveConversation`, `useConversationMessages`,\n * `ctx.conversation.on(\"turn-end\")` — and returns `null` to render nothing,\n * e.g. git renders only inside a repo that has changes). `scope_use` gates which\n * conversation scenarios it may appear in (**fail-closed**, mirrors the other\n * slots). `id` must be unique within the plugin.\n */\nexport interface PluginTurnCardContribution {\n\tid: string;\n\tcomponent: ComponentType;\n\t/**\n\t * 允许该 turn 卡出现的对话场景 slug 列表(镜像其它插槽的 scope_use)。**fail-closed**:\n\t * 未声明/空数组 = 任何会话都不显示;声明后仅在列出的场景里出现(如 `[\"project\"]`)。\n\t */\n\tscope_use?: readonly ConversationScenario[];\n}\n\n/**\n * Options for {@link PluginUiApi.notify}. Surfaces a host toast so users can\n * see plugin failures without opening DevTools.\n */\nexport interface PluginNotifyOptions {\n\t/** Toast body. Required even when `error` is set (user-facing summary). */\n\tmessage: string;\n\t/** Defaults to plugin display name when omitted. */\n\ttitle?: string;\n\t/** Defaults to `\"info\"`; when `error` is set and variant omitted, host uses `\"error\"`. */\n\tvariant?: \"info\" | \"success\" | \"warning\" | \"error\";\n\t/**\n\t * Optional failure cause. Host formats message + stack and adds a\n\t * \"复制堆栈\" action so the user can paste it into bug reports / chat.\n\t */\n\terror?: unknown;\n\t/**\n\t * Auto-dismiss delay in ms. `0` keeps the toast until dismissed.\n\t * Defaults: sticky (`0`) when `error` is present, otherwise host default (~4s).\n\t */\n\tdurationMs?: number;\n}\n\nexport interface PluginUiApi {\n\tregisterGlobalSlot(contribution: PluginGlobalSlotContribution): Disposable;\n\t/**\n\t * Register a **workspace view**(工作区视图)— a full-page surface with its own\n\t * route and sidebar entry, on par with the host's built-in pages. Needs the\n\t * `ui.slot.workspace-view` permission (missing permission = **warn+noop**).\n\t *\n\t * The entry lands in the sidebar's「更多」收纳 by default; the user can pin it\n\t * to the top region or reorder it, and that layout is remembered.\n\t */\n\tregisterWorkspaceView(contribution: PluginWorkspaceViewContribution): Disposable;\n\t/**\n\t * Navigate to one of this plugin's own workspace views. `viewId` is the\n\t * contribution id passed to {@link PluginUiApi.registerWorkspaceView}. No-op\n\t * when the view is not registered (e.g. permission missing).\n\t */\n\topenWorkspaceView(viewId: string): void;\n\t/**\n\t * Update (or clear, with `null`) the badge on one of this plugin's workspace\n\t * view entries. Re-registering the view is NOT a way to do this — it would\n\t * remount the whole surface — so live badges (unread counts, status dots)\n\t * must come through here.\n\t *\n\t * No-op when the view is not registered (e.g. permission missing).\n\t */\n\tsetWorkspaceViewBadge(viewId: string, badge: PluginNavBadge | null): void;\n\t/**\n\t * Fill (or clear, with `null`) the host page header while one of this\n\t * plugin's workspace views is open. Call it from the view component (an\n\t * effect that re-runs on state change) and clear it on unmount — the host\n\t * keeps the entry keyed by view, and only applies it on that view's route.\n\t *\n\t * The header itself is never removed: it owns the window drag region and the\n\t * macOS traffic-light gutter. This is how a view takes it over instead.\n\t *\n\t * No-op when the view is not registered (e.g. permission missing).\n\t */\n\tsetWorkspaceViewHeader(viewId: string, header: PluginWorkspaceViewHeader | null): void;\n\t/**\n\t * Register a preview component keyed by file extension. The host dispatches\n\t * registered extensions before its built-in fallback renderers; first\n\t * registrant wins on conflict.\n\t */\n\tregisterFilePreview(contribution: PluginFilePreviewContribution): Disposable;\n\t/**\n\t * Register an activity-panel tab into the addable pool. The user attaches\n\t * it manually via the panel's \"+\" picker; attach records are keyed by the\n\t * session cwd.\n\t */\n\tregisterActivityTab(contribution: PluginActivityTabContribution): Disposable;\n\t/**\n\t * Register a toggle action shown beneath the AI input bar. While active,\n\t * its `decoratePrompt()` annotates the next outgoing prompt.\n\t */\n\tregisterInputAction(contribution: PluginInputActionContribution): Disposable;\n\t/**\n\t * Register a card renderer keyed by `type`. Cards rendered beneath a message\n\t * come from tool results' out-of-band `details.cards` (or `pendingFor` for\n\t * in-flight tools); the host resolves each card's `type` to a registered\n\t * renderer. Multiple cards under one message are shown as a tab-switcher\n\t * (\"收纳\") or a flat list.\n\t */\n\tregisterCardRenderer(contribution: PluginCardRendererContribution): Disposable;\n\t/**\n\t * Register a renderer that replaces the host's default inline transcript UI\n\t * for a tool call (matched by `toolName`). The complement to card renderers:\n\t * use it to render UI for a plugin's own agent tool output.\n\t */\n\tregisterToolCallSlot(contribution: PluginToolCallSlotContribution): Disposable;\n\t/**\n\t * Register a card rendered at the bottom of the message list for the latest\n\t * turn — NOT bound to a tool call. The host mounts the component in the\n\t * footer slot; the plugin owns visibility (return `null` to render nothing,\n\t * e.g. git renders only in a repo with changes). Gated by `scope_use`\n\t * (fail-closed). Needs the `ui.slot.turn-card` permission.\n\t */\n\tregisterTurnCard(contribution: PluginTurnCardContribution): Disposable;\n\t/**\n\t * Register a keyboard shortcut scope on the host's shared ShortcutScopeStack\n\t * (same path as host UI: modal > overlay > surface > app). Needs\n\t * `ui.shortcuts.register`. Plugins cannot use kind `\"app\"` (reserved for\n\t * host-configurable global actions). Prefer {@link usePluginShortcutScope}\n\t * from React components with a module-captured `registerShortcutScope`.\n\t */\n\tregisterShortcutScope(contribution: PluginShortcutScopeContribution): Disposable;\n\t/**\n\t * Programmatically attach (if needed) and activate one of this plugin's\n\t * own activity tabs in a conversation's activity panel. `tabId`\n\t * is the contribution id passed to registerActivityTab. Any payload (e.g.\n\t * which image to edit) is passed via the plugin's own in-memory state.\n\t *\n\t * Pass `options.width` to size the panel as it opens — a pixel number, or\n\t * `\"max\"` to expand it to the widest the current window allows (the host\n\t * still clamps to its min/max and auto-hides the sidebar when needed). Omit\n\t * to leave the user's current width untouched.\n\t *\n\t * Omit `options.cwd` for the conversation currently displayed by the host.\n\t * Tool handlers and background work should pass their session cwd explicitly.\n\t */\n\topenActivityTab(tabId: string, options?: PluginOpenActivityTabOptions): void;\n\t/**\n\t * 把本插件的某个活动面板标签卡在目标会话里上栏 / 下栏,**不激活也不展开\n\t * 面板**——`openActivityTab` 是「用户此刻要看它」,这个是「它现在该不该在\n\t * 栏里」。配合 `initiallyVisible: false` 使用,插件即可完全掌握自己标签卡的\n\t * 出现条件(如 git 只在仓库目录里上栏、工作台跟随输入栏 toggle)。\n\t *\n\t * 上栏记录按会话 cwd 持久化(ADR-0026),所以只需在条件变化时调用一次;\n\t * 用户随后用减号手动隐藏的结果不会被重复调用覆盖。缺省作用于宿主当前显示的\n\t * 会话;工具 handler 与后台任务应通过 `options.cwd` 传入自己的会话 cwd。既没有\n\t * 显式 cwd、也没有活动会话时为 no-op(无处记录)。\n\t */\n\tsetActivityTabVisible(tabId: string, visible: boolean, options?: PluginActivityTabTargetOptions): void;\n\t/**\n\t * 直接设置活动面板宽度:像素值,或 `\"max\"` 表示「跟随窗口拉满」(宿主仍会夹到\n\t * 自己的 min/max 内,必要时自动收起侧边栏)。\n\t *\n\t * `\"max\"` 是一种持续状态而非一次性求值:窗口尺寸变化时面板跟着变宽变窄,直到\n\t * 用户拖动分隔条或有人写入具体像素为止。传数字则是一次性的固定宽度。\n\t *\n\t * 与 `openActivityTab(id, { width })` 的区别:那里的宽度只在标签卡首次 attach\n\t * 时生效(避免 activate 重放覆盖用户手拖的宽度);这个是命令式的,每次调用都\n\t * 生效,供插件在自己的标签卡被激活、进入某种视图时按需调整。用户随后仍可拖动。\n\t */\n\tsetActivityPanelWidth(width: number | \"max\"): void;\n\t/**\n\t * Bind or clear plugin-owned context for outgoing prompts. The host renders\n\t * its label/icon and carries a structured `context` as a versioned snapshot.\n\t * Attachments default to one-shot; `lifecycle: \"sticky\"` remains until the\n\t * plugin or user clears the capsule.\n\t */\n\tsetPromptAttachment(attachment: PluginPromptAttachment | null): void;\n\t/**\n\t * Open the host's global full-screen image previewer for the given image.\n\t * Only the image reference (id/url) crosses over — bytes stay out-of-band.\n\t *\n\t * Pass `group` (e.g. all images of the message) to open as an image group:\n\t * the previewer shows a thumbnail strip + arrows and starts at `ref`.\n\t */\n\tpreviewImage(ref: PluginImageRef, group?: PluginImageRef[]): void;\n\t/**\n\t * Open the app settings, scrolled to and highlighting THIS plugin's own\n\t * settings section (e.g. so the user can fill in a required API key/model).\n\t * The host owns the navigation; the plugin only asks to jump there.\n\t */\n\topenPluginSettings(): void;\n\t/**\n\t * Capture a rectangle in the current Vetta window and open the host save\n\t * dialog. Coordinates use renderer DIP values such as getBoundingClientRect().\n\t * Requires `ui.slot.activity-tab`.\n\t */\n\tcaptureRegion(rect: PluginCaptureRegion, defaultFileName: string): Promise<string | null>;\n\t/**\n\t * Copy an image to the system clipboard. Takes a `data:image/...;base64,`\n\t * URL and goes through the native clipboard, so it does not depend on the\n\t * renderer's `ClipboardItem` support. No permission required — the plugin\n\t * can only write what it already rendered.\n\t */\n\tcopyImage(dataUrl: string): Promise<void>;\n\t/**\n\t * Hand a URL to the OS default browser (Electron `shell.openExternal`) —\n\t * NOT the in-app browser panel. Only `http:`/`https:` are accepted; the host\n\t * rejects every other protocol, so this cannot be used to launch arbitrary\n\t * schemes. Needs `shell.openExternal`.\n\t */\n\topenExternal(url: string): Promise<void>;\n\t/**\n\t * Show a global toast in the host UI (bottom-right). No permission required.\n\t * Prefer this over swallowing errors into opaque UI copy: pass `error` so\n\t * the host attaches a one-click \"copy stack\" action for the user.\n\t *\n\t * Capture `ctx.ui.notify` in `activate` if React components need it\n\t * (components do not receive `ctx`).\n\t */\n\tnotify(options: PluginNotifyOptions): void;\n}\n"]}
package/dist/ui.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"","sourcesContent":["import type { ComponentType, ReactNode } from \"react\";\nimport type { ConversationMessage } from \"./conversation.js\";\nimport type { Disposable } from \"./disposable.js\";\nimport type { PluginImageRef } from \"./images.js\";\nimport type { PluginPromptAttachment } from \"./prompt-attachment.js\";\nimport type { ConversationScenario } from \"./scenario.js\";\nimport type { PluginShortcutScopeContribution } from \"./shortcuts.js\";\n\nexport interface PluginGlobalSlotContribution {\n\tid: string;\n\tcomponent: ComponentType;\n}\n\nexport interface PluginAudioMetadata {\n\t/** Embedded title; preview renderers should fall back to file.name. */\n\ttitle?: string;\n\tartist?: string;\n\t/** Embedded cover art as a Data URL when available. */\n\tcoverDataUrl?: string;\n}\n\nexport interface PluginPreviewUrlOptions {\n\t/** Optional media hint for ambiguous containers such as webm. */\n\tmediaKind?: \"audio\" | \"video\";\n}\n\n/**\n * The file handed to a file-preview plugin component. The host does NOT\n * pre-read or guess encoding — the plugin decides whether to read text or\n * bytes. `path` is also exposed for plugins that prefer native fetch.\n */\nexport interface PluginPreviewFile {\n\tpath: string | null;\n\tname: string;\n\textension: string;\n\tmime: string;\n\tsize: number;\n\t/** Read the file as a UTF-8 string. */\n\treadText(): Promise<string>;\n\t/** Read the raw file bytes. */\n\treadBytes(): Promise<ArrayBuffer>;\n\t/** A fetchable streaming URL for the file (Range-capable). */\n\tgetUrl(options?: PluginPreviewUrlOptions): string;\n\t/**\n\t * Subscribe to on-disk changes of this file. The listener fires (debounced\n\t * by the host) whenever the file's contents change, letting previews update\n\t * live. Returns a Disposable; call dispose() to stop watching. A no-op for\n\t * files without a real path (url-only sources).\n\t */\n\twatch(listener: () => void): Disposable;\n\t/**\n\t * Host-provided audio metadata for local media files. Returns null when the\n\t * host cannot provide metadata, e.g. url-only sources or unsupported files.\n\t */\n\tgetAudioMetadata?(): Promise<PluginAudioMetadata | null>;\n}\n\nexport interface PluginFilePreviewProps {\n\tfile: PluginPreviewFile;\n}\n\nexport interface PluginFilePreviewContribution {\n\t/** Lower-case extensions without the dot, e.g. [\"svg\"]. */\n\textensions: string[];\n\tcomponent: ComponentType<PluginFilePreviewProps>;\n}\n\n/**\n * An activity-panel tab contributed by a plugin. Registering only adds the\n * tab to the \"addable pool\" — it renders only after the user attaches it in\n * the activity panel (scoped by session cwd).\n */\nexport interface PluginActivityTabContribution {\n\tid: string;\n\tlabel: string;\n\t/** Tab icon as a React node (not an iconify class string). */\n\ticon?: ReactNode;\n\tcomponent: ComponentType;\n\t/**\n\t * 允许该标签卡出现的对话场景 slug 列表(镜像工具的 scope_use)。**fail-closed**:\n\t * 未声明/空数组 = 任何会话都不显示;声明后仅在列出的场景里出现(如\n\t * `[\"project\", \"conversation\"]`)。会话页插槽据此随对话类型显隐。\n\t */\n\tscope_use?: readonly ConversationScenario[];\n\t/**\n\t * 注册后是否默认在标签栏里(缺省 `true`)。声明 `false` 表示「出现条件由我\n\t * 自己决定」——注册只是入池,之后由 {@link PluginUiApi.setActivityTabVisible}\n\t * 或 {@link PluginUiApi.openActivityTab} 决定何时上栏(如 git 只在仓库目录、\n\t * 工作台跟随输入栏 toggle)。无论哪种,用户仍可用减号手动隐藏。\n\t */\n\tinitiallyVisible?: boolean;\n}\n\n/** Options for {@link PluginUiApi.openActivityTab}. */\nexport interface PluginOpenActivityTabOptions {\n\t/**\n\t * Desired panel width as it opens: a pixel number, or `\"max\"` for the widest\n\t * the current window allows. The host clamps to its min/max bounds. Omit to\n\t * keep the user's current width.\n\t */\n\twidth?: number | \"max\";\n}\n\nexport interface PluginCaptureRegion {\n\tx: number;\n\ty: number;\n\twidth: number;\n\theight: number;\n}\n\n/**\n * Decoration a plugin contributes to the next outgoing prompt while its input\n * action is active. `metadata` is shallow-merged into the prompt request and\n * `instructions` are appended as hidden, model-visible guidance.\n */\nexport interface PluginPromptDecoration {\n\tmetadata?: Record<string, unknown>;\n\t/** Hidden plugin-owned instructions appended to the next agent turn. */\n\tinstructions?: string[];\n}\n\n/**\n * An action button shown in a row beneath the AI input bar. Rendered as a\n * toggle: clicking activates, clicking again deactivates. While active, the\n * host calls `decoratePrompt()` before each send and merges the returned\n * metadata into the outgoing prompt request. The plugin owns any side state\n * (it is the same Module Federation instance as its other slots).\n */\nexport interface PluginInputActionContribution {\n\tid: string;\n\tlabel: string;\n\t/** Button icon as a React node (not an iconify class string). */\n\ticon?: ReactNode;\n\t/** Whether the action begins in the active state. Defaults to false. */\n\tdefaultActive?: boolean;\n\t/**\n\t * 该输入动作所依赖的 agent 工具名。设置后,仅当该工具在当前会话处于激活(按工具的\n\t * scope_use 解析)时才显示这个 badge——避免在工具被场景屏蔽(如批量任务)时仍显示一个\n\t * 点了也无效的开关。不设则始终显示。例如「图像生成」设为 \"generate_image\"。\n\t */\n\trequiresActiveTool?: string;\n\t/**\n\t * 允许该 toggle 出现的对话场景 slug 列表(镜像工具的 scope_use)。**fail-closed**:\n\t * 未声明/空数组 = 任何会话都不显示;声明后仅在列出的场景里出现。与 `requiresActiveTool`\n\t * 取「与」:两者都满足才显示。\n\t */\n\tscope_use?: readonly ConversationScenario[];\n\t/**\n\t * When true, this plugin's agent contributions (tools / skills / MCP /\n\t * systemPrompt) and Activity Tabs are hard-isolated: inactive until this\n\t * toggle is on (ADR-0041, knowledgeMode-style). Default false.\n\t */\n\thardIsolation?: boolean;\n\t/**\n\t * Fired when the user toggles the action; `active` is the new state. Return\n\t * `false` when `active` is true to VETO the activation (e.g. the plugin needs\n\t * configuration first) — the toggle stays off. Deactivation can't be vetoed.\n\t */\n\tonToggle?(active: boolean): boolean | void;\n\t/**\n\t * Called by the host before sending while the action is active. The\n\t * returned metadata is merged into the outgoing prompt request.\n\t */\n\tdecoratePrompt?(): PluginPromptDecoration | void;\n}\n\n/**\n * A declarative, serializable descriptor for one card rendered beneath a\n * message. `type` selects a registered card renderer; `key` drives cross-turn\n * dedup (same key across turns = one logical card, shown only under its latest\n * anchor); `payload` carries STABLE REFERENCES (e.g. image ids), not a content\n * snapshot — the renderer resolves live state from it. `title`/`icon` label the\n * card's tab (override the renderer's registration defaults). Rides a tool\n * result's out-of-band `details.cards`; crosses the agent→host boundary, so\n * `icon` is an icon-symbol string, not a node.\n */\nexport interface CardDescriptor {\n\ttype: string;\n\tkey?: string;\n\tpayload?: unknown;\n\ttitle?: string;\n\ticon?: string;\n}\n\n/** A pending (in-flight) tool call handed to a renderer's `pendingFor`. */\nexport interface PluginPendingToolCall {\n\ttoolName: string;\n\targs: Record<string, unknown>;\n}\n\n/**\n * Props for a card renderer. `descriptor` is the card's data; `pending` is true\n * while it was synthesized from an in-flight tool (render a skeleton); `message`\n * is the anchoring conversation message.\n */\nexport interface PluginCardProps {\n\tdescriptor: CardDescriptor;\n\tpending: boolean;\n\tmessage: ConversationMessage;\n}\n\n/**\n * A card renderer registered by a plugin, keyed by `type`. A descriptor whose\n * `type` matches is rendered by `component`. `title`/`icon` are the default tab\n * label/icon (a descriptor may override `title`). `pendingFor`, given an\n * in-flight tool call, returns a provisional descriptor so a skeleton card\n * appears (and claims a tab) before the tool's result lands — or null when this\n * renderer doesn't handle that tool. The `type` must be globally unique across\n * plugins (convention: prefix with the plugin id, e.g. \"image-gen:preview\").\n */\nexport interface PluginCardRendererContribution {\n\ttype: string;\n\tcomponent: ComponentType<PluginCardProps>;\n\ttitle?: string;\n\t/** Default tab icon as a React node (not an iconify class string). */\n\ticon?: ReactNode;\n\tpendingFor?: (toolCall: PluginPendingToolCall) => CardDescriptor | null;\n}\n\n/** A tool call handed to a tool-call slot renderer. */\nexport interface PluginToolCallSlotToolCall {\n\ttoolCallId: string;\n\ttoolName: string;\n\targs: Record<string, unknown>;\n\tstatus: \"pending\" | \"success\" | \"error\";\n\tresult?: string;\n\tisError?: boolean;\n}\n\nexport interface PluginToolCallSlotProps {\n\ttoolCall: PluginToolCallSlotToolCall;\n}\n\n/**\n * A component that **replaces the host's default inline transcript rendering**\n * for a specific tool (matched by `toolName`; first registered renderer wins).\n * This is how a plugin renders rich UI for its OWN agent tool's output — plugin\n * tools cannot emit `details.cards`, so the message card system (registerCardRenderer)\n * does not serve them; the tool-call slot does.\n */\nexport interface PluginToolCallSlotContribution {\n\tid: string;\n\ttoolName: string;\n\tcomponent: ComponentType<PluginToolCallSlotProps>;\n}\n\n/**\n * A card rendered at the bottom of the message list for the LATEST turn — NOT\n * bound to a tool call. The host mounts `component` in the footer slot and\n * re-mounts it each session/turn; the plugin owns visibility entirely (it reads\n * live state via SDK hooks — `useActiveConversation`, `useConversationMessages`,\n * `ctx.conversation.on(\"turn-end\")` — and returns `null` to render nothing,\n * e.g. git renders only inside a repo that has changes). `scope_use` gates which\n * conversation scenarios it may appear in (**fail-closed**, mirrors the other\n * slots). `id` must be unique within the plugin.\n */\nexport interface PluginTurnCardContribution {\n\tid: string;\n\tcomponent: ComponentType;\n\t/**\n\t * 允许该 turn 卡出现的对话场景 slug 列表(镜像其它插槽的 scope_use)。**fail-closed**:\n\t * 未声明/空数组 = 任何会话都不显示;声明后仅在列出的场景里出现(如 `[\"project\"]`)。\n\t */\n\tscope_use?: readonly ConversationScenario[];\n}\n\n/**\n * Options for {@link PluginUiApi.notify}. Surfaces a host toast so users can\n * see plugin failures without opening DevTools.\n */\nexport interface PluginNotifyOptions {\n\t/** Toast body. Required even when `error` is set (user-facing summary). */\n\tmessage: string;\n\t/** Defaults to plugin display name when omitted. */\n\ttitle?: string;\n\t/** Defaults to `\"info\"`; when `error` is set and variant omitted, host uses `\"error\"`. */\n\tvariant?: \"info\" | \"success\" | \"warning\" | \"error\";\n\t/**\n\t * Optional failure cause. Host formats message + stack and adds a\n\t * \"复制堆栈\" action so the user can paste it into bug reports / chat.\n\t */\n\terror?: unknown;\n\t/**\n\t * Auto-dismiss delay in ms. `0` keeps the toast until dismissed.\n\t * Defaults: sticky (`0`) when `error` is present, otherwise host default (~4s).\n\t */\n\tdurationMs?: number;\n}\n\nexport interface PluginUiApi {\n\tregisterGlobalSlot(contribution: PluginGlobalSlotContribution): Disposable;\n\t/**\n\t * Register a preview component keyed by file extension. The host dispatches\n\t * registered extensions before its built-in fallback renderers; first\n\t * registrant wins on conflict.\n\t */\n\tregisterFilePreview(contribution: PluginFilePreviewContribution): Disposable;\n\t/**\n\t * Register an activity-panel tab into the addable pool. The user attaches\n\t * it manually via the panel's \"+\" picker; attach records are keyed by the\n\t * session cwd.\n\t */\n\tregisterActivityTab(contribution: PluginActivityTabContribution): Disposable;\n\t/**\n\t * Register a toggle action shown beneath the AI input bar. While active,\n\t * its `decoratePrompt()` annotates the next outgoing prompt.\n\t */\n\tregisterInputAction(contribution: PluginInputActionContribution): Disposable;\n\t/**\n\t * Register a card renderer keyed by `type`. Cards rendered beneath a message\n\t * come from tool results' out-of-band `details.cards` (or `pendingFor` for\n\t * in-flight tools); the host resolves each card's `type` to a registered\n\t * renderer. Multiple cards under one message are shown as a tab-switcher\n\t * (\"收纳\") or a flat list.\n\t */\n\tregisterCardRenderer(contribution: PluginCardRendererContribution): Disposable;\n\t/**\n\t * Register a renderer that replaces the host's default inline transcript UI\n\t * for a tool call (matched by `toolName`). The complement to card renderers:\n\t * use it to render UI for a plugin's own agent tool output.\n\t */\n\tregisterToolCallSlot(contribution: PluginToolCallSlotContribution): Disposable;\n\t/**\n\t * Register a card rendered at the bottom of the message list for the latest\n\t * turn — NOT bound to a tool call. The host mounts the component in the\n\t * footer slot; the plugin owns visibility (return `null` to render nothing,\n\t * e.g. git renders only in a repo with changes). Gated by `scope_use`\n\t * (fail-closed). Needs the `ui.slot.turn-card` permission.\n\t */\n\tregisterTurnCard(contribution: PluginTurnCardContribution): Disposable;\n\t/**\n\t * Register a keyboard shortcut scope on the host's shared ShortcutScopeStack\n\t * (same path as host UI: modal > overlay > surface > app). Needs\n\t * `ui.shortcuts.register`. Plugins cannot use kind `\"app\"` (reserved for\n\t * host-configurable global actions). Prefer {@link usePluginShortcutScope}\n\t * from React components with a module-captured `registerShortcutScope`.\n\t */\n\tregisterShortcutScope(contribution: PluginShortcutScopeContribution): Disposable;\n\t/**\n\t * Programmatically attach (if needed) and activate one of this plugin's\n\t * own activity tabs in the current conversation's activity panel. `tabId`\n\t * is the contribution id passed to registerActivityTab. Any payload (e.g.\n\t * which image to edit) is passed via the plugin's own in-memory state.\n\t *\n\t * Pass `options.width` to size the panel as it opens — a pixel number, or\n\t * `\"max\"` to expand it to the widest the current window allows (the host\n\t * still clamps to its min/max and auto-hides the sidebar when needed). Omit\n\t * to leave the user's current width untouched.\n\t */\n\topenActivityTab(tabId: string, options?: PluginOpenActivityTabOptions): void;\n\t/**\n\t * 把本插件的某个活动面板标签卡在当前会话里上栏 / 下栏,**不激活也不展开\n\t * 面板**——`openActivityTab` 是「用户此刻要看它」,这个是「它现在该不该在\n\t * 栏里」。配合 `initiallyVisible: false` 使用,插件即可完全掌握自己标签卡的\n\t * 出现条件(如 git 只在仓库目录里上栏、工作台跟随输入栏 toggle)。\n\t *\n\t * 上栏记录按会话 cwd 持久化(ADR-0026),所以只需在条件变化时调用一次;\n\t * 用户随后用减号手动隐藏的结果不会被重复调用覆盖。当前没有活动会话时为\n\t * no-op(无处记录),插件应在会话就绪后重新判定。\n\t */\n\tsetActivityTabVisible(tabId: string, visible: boolean): void;\n\t/**\n\t * 直接设置活动面板宽度:像素值,或 `\"max\"` 表示当前窗口下的最大宽度(宿主仍\n\t * 会夹到自己的 min/max 内,必要时自动收起侧边栏)。\n\t *\n\t * 与 `openActivityTab(id, { width })` 的区别:那里的宽度只在标签卡首次 attach\n\t * 时生效(避免 activate 重放覆盖用户手拖的宽度);这个是命令式的,每次调用都\n\t * 生效,供插件在自己的标签卡被激活、进入某种视图时按需调整。用户随后仍可拖动。\n\t */\n\tsetActivityPanelWidth(width: number | \"max\"): void;\n\t/**\n\t * Bind or clear plugin-owned one-shot context for the next outgoing prompt.\n\t * The host renders its label/icon, merges metadata and hidden instructions,\n\t * then clears it after send or when the user closes the capsule.\n\t */\n\tsetPromptAttachment(attachment: PluginPromptAttachment | null): void;\n\t/**\n\t * Open the host's global full-screen image previewer for the given image.\n\t * Only the image reference (id/url) crosses over — bytes stay out-of-band.\n\t *\n\t * Pass `group` (e.g. all images of the message) to open as an image group:\n\t * the previewer shows a thumbnail strip + arrows and starts at `ref`.\n\t */\n\tpreviewImage(ref: PluginImageRef, group?: PluginImageRef[]): void;\n\t/**\n\t * Open the app settings, scrolled to and highlighting THIS plugin's own\n\t * settings section (e.g. so the user can fill in a required API key/model).\n\t * The host owns the navigation; the plugin only asks to jump there.\n\t */\n\topenPluginSettings(): void;\n\t/**\n\t * Capture a rectangle in the current Vetta window and open the host save\n\t * dialog. Coordinates use renderer DIP values such as getBoundingClientRect().\n\t * Requires `ui.slot.activity-tab`.\n\t */\n\tcaptureRegion(rect: PluginCaptureRegion, defaultFileName: string): Promise<string | null>;\n\t/**\n\t * Copy an image to the system clipboard. Takes a `data:image/...;base64,`\n\t * URL and goes through the native clipboard, so it does not depend on the\n\t * renderer's `ClipboardItem` support. No permission required — the plugin\n\t * can only write what it already rendered.\n\t */\n\tcopyImage(dataUrl: string): Promise<void>;\n\t/**\n\t * Show a global toast in the host UI (bottom-right). No permission required.\n\t * Prefer this over swallowing errors into opaque UI copy: pass `error` so\n\t * the host attaches a one-click \"copy stack\" action for the user.\n\t *\n\t * Capture `ctx.ui.notify` in `activate` if React components need it\n\t * (components do not receive `ctx`).\n\t */\n\tnotify(options: PluginNotifyOptions): void;\n}\n"]}
1
+ {"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"","sourcesContent":["import type { ComponentType, ReactNode } from \"react\";\nimport type { ConversationMessage } from \"./conversation.js\";\nimport type { Disposable } from \"./disposable.js\";\nimport type { PluginImageRef } from \"./images.js\";\nimport type { PluginPromptAttachment } from \"./prompt-attachment.js\";\nimport type { ConversationScenario } from \"./scenario.js\";\nimport type { PluginShortcutScopeContribution } from \"./shortcuts.js\";\n\nexport interface PluginGlobalSlotContribution {\n\tid: string;\n\tcomponent: ComponentType;\n}\n\n/**\n * Props handed to a workspace-view component. A workspace view owns the whole\n * content area, so unlike panel slots it is addressed by its own route and can\n * be deep-linked; the host passes back its identity so one component can serve\n * several registered views.\n */\nexport interface PluginWorkspaceViewProps {\n\t/** The contribution id as registered (NOT namespaced). */\n\tviewId: string;\n\t/** The owning plugin id. */\n\tpluginId: string;\n}\n\n/**\n * Badge tone. The host maps it to its own theme colors — a plugin cannot pick a\n * raw color, so badges stay consistent with the built-in navigation entries.\n */\nexport type PluginNavBadgeTone = \"accent\" | \"danger\" | \"default\" | \"warning\";\n\n/**\n * A badge on the plugin's sidebar navigation entry.\n *\n * `beta` is the host's own preset — it renders exactly like the built-in\n * 知识库 Beta 标识, wording included (and localized by the host), so plugins do\n * not have to spell \"Beta\" themselves in every locale.\n *\n * `count` renders `99+` past 99 and disappears at 0 — an unread badge showing\n * \"0\" is worse than no badge.\n */\nexport type PluginNavBadge =\n\t| { kind: \"beta\" }\n\t| { kind: \"text\"; text: string; tone?: PluginNavBadgeTone }\n\t| { kind: \"count\"; count: number; tone?: PluginNavBadgeTone }\n\t| { kind: \"dot\"; tone?: PluginNavBadgeTone };\n\n/**\n * A **workspace view**(工作区视图): a full-page surface a plugin contributes to\n * the host's primary navigation. It is the plugin counterpart of a built-in page\n * such as 自动化 / 知识库 — the host gives it its own route\n * (`/workspace/<pluginId>/<viewId>`) and a sidebar entry, and the plugin owns the\n * entire content area while it is open.\n *\n * Use it for standalone workbenches that are NOT bound to one conversation\n * (dashboards, boards, consoles). Conversation-scoped UI belongs in an\n * {@link PluginActivityTabContribution activity tab}; floating UI belongs in a\n * {@link PluginGlobalSlotContribution global slot}.\n */\nexport interface PluginWorkspaceViewContribution {\n\t/** Unique within the plugin; the host namespaces it as `${pluginId}:${id}`. */\n\tid: string;\n\t/** Sidebar entry label. Supports `%catalogKey%` i18n lookup. */\n\tlabel: string;\n\t/**\n\t * Sidebar entry icon as an **iconify class string** (e.g.\n\t * `\"icon-[solar--widget-5-linear]\"`) — NOT a React node, because the host\n\t * renders it inside its own nav button and persists nav layout by key.\n\t *\n\t * Omit it to inherit the plugin's own `plugin.json` icon: a packaged image\n\t * (svg / png / webp …) is masked with the theme's foreground color, so a plugin\n\t * that ships artwork does not have to find a lookalike in an icon set. Set\n\t * {@link PluginWorkspaceViewContribution.iconTint} to `false` to keep its colors.\n\t */\n\ticon?: string;\n\t/**\n\t * Whether an image icon is tinted with the theme's foreground color. Defaults to\n\t * `true`, which keeps the entry consistent with the built-in navigation and legible\n\t * in every theme.\n\t *\n\t * Pass `false` for a full-color logo. Weigh it first: the entry renders at 16px\n\t * beside monochrome built-ins, and a fixed-color image cannot follow the active\n\t * theme — a dark logo can disappear on a dark sidebar. Has no effect on Iconify\n\t * class icons, which are always tinted.\n\t */\n\ticonTint?: boolean;\n\t/** Optional one-line description; shown as the nav entry tooltip. */\n\tdescription?: string;\n\t/**\n\t * Initial badge on the sidebar entry. `{ kind: \"text\" }` supports the same\n\t * `%catalogKey%` i18n lookup as {@link label}. Update it at runtime with\n\t * {@link PluginUiApi.setWorkspaceViewBadge}.\n\t */\n\tbadge?: PluginNavBadge;\n\t/** Zero-props aside from {@link PluginWorkspaceViewProps}. */\n\tcomponent: ComponentType<PluginWorkspaceViewProps>;\n\t/** Sort hint among this plugin's own views (ascending). Defaults to 0. */\n\tnavOrder?: number;\n}\n\n/**\n * What a workspace view puts into the **host page header** — the window's top\n * bar that also carries the drag region, the macOS traffic-light gutter and the\n * sidebar trigger.\n *\n * Without this the header still renders, showing the app name above the\n * plugin's own toolbar — two stacked bars. Set `hideTitle` and move the view's\n * toolbar in here and the surface gets a single, host-consistent header.\n *\n * `left` / `right` are ReactNodes rendered by the host inside that bar (left:\n * right after the sidebar trigger; right: before the window controls). They are\n * rendered in the plugin's i18n + CSS scope, so `useTranslation()` and the\n * plugin's own classes work as usual. Pass fresh nodes whenever the view's\n * state changes — this is a live setter, not a one-time registration.\n */\nexport interface PluginWorkspaceViewHeader {\n\t/** Replaces the host title text. Supports `%catalogKey%` i18n lookup. */\n\ttitle?: string;\n\t/** Drop the host title entirely (use when {@link left} carries its own). */\n\thideTitle?: boolean;\n\t/** Rendered in the header's left cluster, after the sidebar trigger. */\n\tleft?: ReactNode;\n\t/** Rendered in the header's action cluster, before the window controls. */\n\tright?: ReactNode;\n\t/**\n\t * Float the host header OVER the view instead of stacking above it. The view\n\t * then owns the full content height, and its top `~44px` sit under the\n\t * transparent header strip (window drag region / traffic lights / sidebar\n\t * trigger all keep working on top). Use for immersive full-page surfaces\n\t * whose own hero starts at the very top; leave unset for toolbar-style\n\t * headers that need their own opaque row.\n\t */\n\timmersive?: boolean;\n}\n\nexport interface PluginAudioMetadata {\n\t/** Embedded title; preview renderers should fall back to file.name. */\n\ttitle?: string;\n\tartist?: string;\n\t/** Embedded cover art as a Data URL when available. */\n\tcoverDataUrl?: string;\n}\n\nexport interface PluginPreviewUrlOptions {\n\t/** Optional media hint for ambiguous containers such as webm. */\n\tmediaKind?: \"audio\" | \"video\";\n}\n\n/**\n * The file handed to a file-preview plugin component. The host does NOT\n * pre-read or guess encoding — the plugin decides whether to read text or\n * bytes. `path` is also exposed for plugins that prefer native fetch.\n */\nexport interface PluginPreviewFile {\n\tpath: string | null;\n\tname: string;\n\textension: string;\n\tmime: string;\n\tsize: number;\n\t/** Read the file as a UTF-8 string. */\n\treadText(): Promise<string>;\n\t/** Read the raw file bytes. */\n\treadBytes(): Promise<ArrayBuffer>;\n\t/** A fetchable streaming URL for the file (Range-capable). */\n\tgetUrl(options?: PluginPreviewUrlOptions): string;\n\t/**\n\t * Subscribe to on-disk changes of this file. The listener fires (debounced\n\t * by the host) whenever the file's contents change, letting previews update\n\t * live. Returns a Disposable; call dispose() to stop watching. A no-op for\n\t * files without a real path (url-only sources).\n\t */\n\twatch(listener: () => void): Disposable;\n\t/**\n\t * Host-provided audio metadata for local media files. Returns null when the\n\t * host cannot provide metadata, e.g. url-only sources or unsupported files.\n\t */\n\tgetAudioMetadata?(): Promise<PluginAudioMetadata | null>;\n}\n\nexport interface PluginFilePreviewProps {\n\tfile: PluginPreviewFile;\n}\n\nexport interface PluginFilePreviewContribution {\n\t/** Lower-case extensions without the dot, e.g. [\"svg\"]. */\n\textensions: string[];\n\tcomponent: ComponentType<PluginFilePreviewProps>;\n}\n\n/**\n * An activity-panel tab contributed by a plugin. Registering only adds the\n * tab to the \"addable pool\" — it renders only after the user attaches it in\n * the activity panel (scoped by session cwd).\n */\n/** Host residency policy for an activity-panel tab. */\nexport type PluginActivityTabRetention = \"active-only\" | \"warm\" | \"pinned\";\n\nexport interface PluginActivityTabContribution {\n\tid: string;\n\tlabel: string;\n\t/**\n\t * Tab icon as a React node (not an iconify class string).\n\t * Omit to inherit the host-resolved brand icon from `plugin.json#icon`\n\t * (`ctx.plugin.iconUrl`). Prefer omitting for package brand icons so the\n\t * plugin never loads assets via absolute `/…` paths or host protocols.\n\t */\n\ticon?: ReactNode;\n\tcomponent: ComponentType;\n\t/**\n\t * 允许该标签卡出现的对话场景 slug 列表(镜像工具的 scope_use)。**fail-closed**:\n\t * 未声明/空数组 = 任何会话都不显示;声明后仅在列出的场景里出现(如\n\t * `[\"project\", \"conversation\"]`)。会话页插槽据此随对话类型显隐。\n\t */\n\tscope_use?: readonly ConversationScenario[];\n\t/**\n\t * 注册后是否默认在标签栏里(缺省 `true`)。声明 `false` 表示「出现条件由我\n\t * 自己决定」——注册只是入池,之后由 {@link PluginUiApi.setActivityTabVisible}\n\t * 或 {@link PluginUiApi.openActivityTab} 决定何时上栏(如 git 只在仓库目录、\n\t * 工作台跟随输入栏 toggle)。无论哪种,用户仍可用减号手动隐藏。\n\t */\n\tinitiallyVisible?: boolean;\n\t/**\n\t * Component residency while the tab is inactive. Defaults to `\"warm\"`:\n\t * visited tabs stay mounted in the host's bounded LRU cache. Use\n\t * `\"active-only\"` for cheap, stateless content and `\"pinned\"` for runtimes\n\t * that must never be evicted while available.\n\t */\n\tretention?: PluginActivityTabRetention;\n\t/**\n\t * @deprecated Use {@link PluginActivityTabContribution.retention}. `true`\n\t * maps to `\"pinned\"`; `false` maps to `\"active-only\"`.\n\t */\n\tkeepAliveWhenAvailable?: boolean;\n}\n\n/** Explicit conversation scope for an activity-tab command. */\nexport interface PluginActivityTabTargetOptions {\n\t/**\n\t * Absolute cwd whose activity-tab state should be changed. Omit to target\n\t * the conversation currently displayed by the host. Tool handlers and\n\t * background work should pass the triggering session's cwd so a foreground\n\t * navigation change cannot redirect the command to another conversation.\n\t */\n\tcwd?: string;\n}\n\n/** Options for {@link PluginUiApi.openActivityTab}. */\nexport interface PluginOpenActivityTabOptions extends PluginActivityTabTargetOptions {\n\t/**\n\t * Desired panel width as it opens: a pixel number, or `\"max\"` to track the\n\t * widest the window allows — `\"max\"` keeps following window resizes until the\n\t * user drags the divider. The host clamps to its min/max bounds. Omit to keep\n\t * the user's current width.\n\t */\n\twidth?: number | \"max\";\n}\n\nexport interface PluginCaptureRegion {\n\tx: number;\n\ty: number;\n\twidth: number;\n\theight: number;\n}\n\n/**\n * Decoration a plugin contributes to the next outgoing prompt while its input\n * action is active. `metadata` is shallow-merged into the prompt request and\n * `instructions` are appended as hidden, model-visible guidance.\n */\nexport interface PluginPromptDecoration {\n\tmetadata?: Record<string, unknown>;\n\t/** Hidden plugin-owned instructions appended to the next agent turn. */\n\tinstructions?: string[];\n}\n\n/**\n * An action button shown in a row beneath the AI input bar. Rendered as a\n * toggle: clicking activates, clicking again deactivates. While active, the\n * host calls `decoratePrompt()` before each send and merges the returned\n * metadata into the outgoing prompt request. The plugin owns any side state\n * (it is the same Module Federation instance as its other slots).\n */\nexport interface PluginInputActionContribution {\n\tid: string;\n\tlabel: string;\n\t/** Button icon as a React node (not an iconify class string). */\n\ticon?: ReactNode;\n\t/** Whether the action begins in the active state. Defaults to false. */\n\tdefaultActive?: boolean;\n\t/**\n\t * 该输入动作所依赖的 agent 工具名。设置后,仅当该工具在当前会话处于激活(按工具的\n\t * scope_use 解析)时才显示这个 badge——避免在工具被场景屏蔽(如批量任务)时仍显示一个\n\t * 点了也无效的开关。不设则始终显示。例如「图像生成」设为 \"generate_image\"。\n\t */\n\trequiresActiveTool?: string;\n\t/**\n\t * 允许该 toggle 出现的对话场景 slug 列表(镜像工具的 scope_use)。**fail-closed**:\n\t * 未声明/空数组 = 任何会话都不显示;声明后仅在列出的场景里出现。与 `requiresActiveTool`\n\t * 取「与」:两者都满足才显示。\n\t */\n\tscope_use?: readonly ConversationScenario[];\n\t/**\n\t * When true, this plugin's agent contributions (tools / skills / MCP /\n\t * systemPrompt) and Activity Tabs are hard-isolated: inactive until this\n\t * toggle is on (ADR-0041, knowledgeMode-style). Default false.\n\t */\n\thardIsolation?: boolean;\n\t/**\n\t * Fired when the user toggles the action; `active` is the new state. Return\n\t * `false` when `active` is true to VETO the activation (e.g. the plugin needs\n\t * configuration first) — the toggle stays off. Deactivation can't be vetoed.\n\t */\n\tonToggle?(active: boolean): boolean | void;\n\t/**\n\t * Called by the host before sending while the action is active. The\n\t * returned metadata is merged into the outgoing prompt request.\n\t */\n\tdecoratePrompt?(): PluginPromptDecoration | void;\n}\n\n/**\n * A declarative, serializable descriptor for one card rendered beneath a\n * message. `type` selects a registered card renderer; `key` drives cross-turn\n * dedup (same key across turns = one logical card, shown only under its latest\n * anchor); `payload` carries STABLE REFERENCES (e.g. image ids), not a content\n * snapshot — the renderer resolves live state from it. `title`/`icon` label the\n * card's tab (override the renderer's registration defaults). Rides a tool\n * result's out-of-band `details.cards`; crosses the agent→host boundary, so\n * `icon` is an icon-symbol string, not a node.\n */\nexport interface CardDescriptor {\n\ttype: string;\n\tkey?: string;\n\tpayload?: unknown;\n\ttitle?: string;\n\ticon?: string;\n}\n\n/** A pending (in-flight) tool call handed to a renderer's `pendingFor`. */\nexport interface PluginPendingToolCall {\n\ttoolName: string;\n\targs: Record<string, unknown>;\n}\n\n/**\n * Props for a card renderer. `descriptor` is the card's data; `pending` is true\n * while it was synthesized from an in-flight tool (render a skeleton); `message`\n * is the anchoring conversation message.\n */\nexport interface PluginCardProps {\n\tdescriptor: CardDescriptor;\n\tpending: boolean;\n\tmessage: ConversationMessage;\n}\n\n/**\n * A card renderer registered by a plugin, keyed by `type`. A descriptor whose\n * `type` matches is rendered by `component`. `title`/`icon` are the default tab\n * label/icon (a descriptor may override `title`). `pendingFor`, given an\n * in-flight tool call, returns a provisional descriptor so a skeleton card\n * appears (and claims a tab) before the tool's result lands — or null when this\n * renderer doesn't handle that tool. The `type` must be globally unique across\n * plugins (convention: prefix with the plugin id, e.g. \"image-gen:preview\").\n */\nexport interface PluginCardRendererContribution {\n\ttype: string;\n\tcomponent: ComponentType<PluginCardProps>;\n\ttitle?: string;\n\t/** Default tab icon as a React node (not an iconify class string). */\n\ticon?: ReactNode;\n\tpendingFor?: (toolCall: PluginPendingToolCall) => CardDescriptor | null;\n}\n\n/** A tool call handed to a tool-call slot renderer. */\nexport interface PluginToolCallSlotToolCall {\n\ttoolCallId: string;\n\ttoolName: string;\n\targs: Record<string, unknown>;\n\tstatus: \"pending\" | \"success\" | \"error\";\n\tresult?: string;\n\tisError?: boolean;\n}\n\nexport interface PluginToolCallSlotProps {\n\ttoolCall: PluginToolCallSlotToolCall;\n}\n\n/**\n * A component that **replaces the host's default inline transcript rendering**\n * for a specific tool (matched by `toolName`; first registered renderer wins).\n * This is how a plugin renders rich UI for its OWN agent tool's output — plugin\n * tools cannot emit `details.cards`, so the message card system (registerCardRenderer)\n * does not serve them; the tool-call slot does.\n */\nexport interface PluginToolCallSlotContribution {\n\tid: string;\n\ttoolName: string;\n\tcomponent: ComponentType<PluginToolCallSlotProps>;\n}\n\n/**\n * A card rendered at the bottom of the message list for the LATEST turn — NOT\n * bound to a tool call. The host mounts `component` in the footer slot and\n * re-mounts it each session/turn; the plugin owns visibility entirely (it reads\n * live state via SDK hooks — `useActiveConversation`, `useConversationMessages`,\n * `ctx.conversation.on(\"turn-end\")` — and returns `null` to render nothing,\n * e.g. git renders only inside a repo that has changes). `scope_use` gates which\n * conversation scenarios it may appear in (**fail-closed**, mirrors the other\n * slots). `id` must be unique within the plugin.\n */\nexport interface PluginTurnCardContribution {\n\tid: string;\n\tcomponent: ComponentType;\n\t/**\n\t * 允许该 turn 卡出现的对话场景 slug 列表(镜像其它插槽的 scope_use)。**fail-closed**:\n\t * 未声明/空数组 = 任何会话都不显示;声明后仅在列出的场景里出现(如 `[\"project\"]`)。\n\t */\n\tscope_use?: readonly ConversationScenario[];\n}\n\n/**\n * Options for {@link PluginUiApi.notify}. Surfaces a host toast so users can\n * see plugin failures without opening DevTools.\n */\nexport interface PluginNotifyOptions {\n\t/** Toast body. Required even when `error` is set (user-facing summary). */\n\tmessage: string;\n\t/** Defaults to plugin display name when omitted. */\n\ttitle?: string;\n\t/** Defaults to `\"info\"`; when `error` is set and variant omitted, host uses `\"error\"`. */\n\tvariant?: \"info\" | \"success\" | \"warning\" | \"error\";\n\t/**\n\t * Optional failure cause. Host formats message + stack and adds a\n\t * \"复制堆栈\" action so the user can paste it into bug reports / chat.\n\t */\n\terror?: unknown;\n\t/**\n\t * Auto-dismiss delay in ms. `0` keeps the toast until dismissed.\n\t * Defaults: sticky (`0`) when `error` is present, otherwise host default (~4s).\n\t */\n\tdurationMs?: number;\n}\n\nexport interface PluginUiApi {\n\tregisterGlobalSlot(contribution: PluginGlobalSlotContribution): Disposable;\n\t/**\n\t * Register a **workspace view**(工作区视图)— a full-page surface with its own\n\t * route and sidebar entry, on par with the host's built-in pages. Needs the\n\t * `ui.slot.workspace-view` permission (missing permission = **warn+noop**).\n\t *\n\t * The entry lands in the sidebar's「更多」收纳 by default; the user can pin it\n\t * to the top region or reorder it, and that layout is remembered.\n\t */\n\tregisterWorkspaceView(contribution: PluginWorkspaceViewContribution): Disposable;\n\t/**\n\t * Navigate to one of this plugin's own workspace views. `viewId` is the\n\t * contribution id passed to {@link PluginUiApi.registerWorkspaceView}. No-op\n\t * when the view is not registered (e.g. permission missing).\n\t */\n\topenWorkspaceView(viewId: string): void;\n\t/**\n\t * Update (or clear, with `null`) the badge on one of this plugin's workspace\n\t * view entries. Re-registering the view is NOT a way to do this — it would\n\t * remount the whole surface — so live badges (unread counts, status dots)\n\t * must come through here.\n\t *\n\t * No-op when the view is not registered (e.g. permission missing).\n\t */\n\tsetWorkspaceViewBadge(viewId: string, badge: PluginNavBadge | null): void;\n\t/**\n\t * Fill (or clear, with `null`) the host page header while one of this\n\t * plugin's workspace views is open. Call it from the view component (an\n\t * effect that re-runs on state change) and clear it on unmount — the host\n\t * keeps the entry keyed by view, and only applies it on that view's route.\n\t *\n\t * The header itself is never removed: it owns the window drag region and the\n\t * macOS traffic-light gutter. This is how a view takes it over instead.\n\t *\n\t * No-op when the view is not registered (e.g. permission missing).\n\t */\n\tsetWorkspaceViewHeader(viewId: string, header: PluginWorkspaceViewHeader | null): void;\n\t/**\n\t * Register a preview component keyed by file extension. The host dispatches\n\t * registered extensions before its built-in fallback renderers; first\n\t * registrant wins on conflict.\n\t */\n\tregisterFilePreview(contribution: PluginFilePreviewContribution): Disposable;\n\t/**\n\t * Register an activity-panel tab into the addable pool. The user attaches\n\t * it manually via the panel's \"+\" picker; attach records are keyed by the\n\t * session cwd.\n\t */\n\tregisterActivityTab(contribution: PluginActivityTabContribution): Disposable;\n\t/**\n\t * Register a toggle action shown beneath the AI input bar. While active,\n\t * its `decoratePrompt()` annotates the next outgoing prompt.\n\t */\n\tregisterInputAction(contribution: PluginInputActionContribution): Disposable;\n\t/**\n\t * Register a card renderer keyed by `type`. Cards rendered beneath a message\n\t * come from tool results' out-of-band `details.cards` (or `pendingFor` for\n\t * in-flight tools); the host resolves each card's `type` to a registered\n\t * renderer. Multiple cards under one message are shown as a tab-switcher\n\t * (\"收纳\") or a flat list.\n\t */\n\tregisterCardRenderer(contribution: PluginCardRendererContribution): Disposable;\n\t/**\n\t * Register a renderer that replaces the host's default inline transcript UI\n\t * for a tool call (matched by `toolName`). The complement to card renderers:\n\t * use it to render UI for a plugin's own agent tool output.\n\t */\n\tregisterToolCallSlot(contribution: PluginToolCallSlotContribution): Disposable;\n\t/**\n\t * Register a card rendered at the bottom of the message list for the latest\n\t * turn — NOT bound to a tool call. The host mounts the component in the\n\t * footer slot; the plugin owns visibility (return `null` to render nothing,\n\t * e.g. git renders only in a repo with changes). Gated by `scope_use`\n\t * (fail-closed). Needs the `ui.slot.turn-card` permission.\n\t */\n\tregisterTurnCard(contribution: PluginTurnCardContribution): Disposable;\n\t/**\n\t * Register a keyboard shortcut scope on the host's shared ShortcutScopeStack\n\t * (same path as host UI: modal > overlay > surface > app). Needs\n\t * `ui.shortcuts.register`. Plugins cannot use kind `\"app\"` (reserved for\n\t * host-configurable global actions). Prefer {@link usePluginShortcutScope}\n\t * from React components with a module-captured `registerShortcutScope`.\n\t */\n\tregisterShortcutScope(contribution: PluginShortcutScopeContribution): Disposable;\n\t/**\n\t * Programmatically attach (if needed) and activate one of this plugin's\n\t * own activity tabs in a conversation's activity panel. `tabId`\n\t * is the contribution id passed to registerActivityTab. Any payload (e.g.\n\t * which image to edit) is passed via the plugin's own in-memory state.\n\t *\n\t * Pass `options.width` to size the panel as it opens — a pixel number, or\n\t * `\"max\"` to expand it to the widest the current window allows (the host\n\t * still clamps to its min/max and auto-hides the sidebar when needed). Omit\n\t * to leave the user's current width untouched.\n\t *\n\t * Omit `options.cwd` for the conversation currently displayed by the host.\n\t * Tool handlers and background work should pass their session cwd explicitly.\n\t */\n\topenActivityTab(tabId: string, options?: PluginOpenActivityTabOptions): void;\n\t/**\n\t * 把本插件的某个活动面板标签卡在目标会话里上栏 / 下栏,**不激活也不展开\n\t * 面板**——`openActivityTab` 是「用户此刻要看它」,这个是「它现在该不该在\n\t * 栏里」。配合 `initiallyVisible: false` 使用,插件即可完全掌握自己标签卡的\n\t * 出现条件(如 git 只在仓库目录里上栏、工作台跟随输入栏 toggle)。\n\t *\n\t * 上栏记录按会话 cwd 持久化(ADR-0026),所以只需在条件变化时调用一次;\n\t * 用户随后用减号手动隐藏的结果不会被重复调用覆盖。缺省作用于宿主当前显示的\n\t * 会话;工具 handler 与后台任务应通过 `options.cwd` 传入自己的会话 cwd。既没有\n\t * 显式 cwd、也没有活动会话时为 no-op(无处记录)。\n\t */\n\tsetActivityTabVisible(tabId: string, visible: boolean, options?: PluginActivityTabTargetOptions): void;\n\t/**\n\t * 直接设置活动面板宽度:像素值,或 `\"max\"` 表示「跟随窗口拉满」(宿主仍会夹到\n\t * 自己的 min/max 内,必要时自动收起侧边栏)。\n\t *\n\t * `\"max\"` 是一种持续状态而非一次性求值:窗口尺寸变化时面板跟着变宽变窄,直到\n\t * 用户拖动分隔条或有人写入具体像素为止。传数字则是一次性的固定宽度。\n\t *\n\t * 与 `openActivityTab(id, { width })` 的区别:那里的宽度只在标签卡首次 attach\n\t * 时生效(避免 activate 重放覆盖用户手拖的宽度);这个是命令式的,每次调用都\n\t * 生效,供插件在自己的标签卡被激活、进入某种视图时按需调整。用户随后仍可拖动。\n\t */\n\tsetActivityPanelWidth(width: number | \"max\"): void;\n\t/**\n\t * Bind or clear plugin-owned context for outgoing prompts. The host renders\n\t * its label/icon and carries a structured `context` as a versioned snapshot.\n\t * Attachments default to one-shot; `lifecycle: \"sticky\"` remains until the\n\t * plugin or user clears the capsule.\n\t */\n\tsetPromptAttachment(attachment: PluginPromptAttachment | null): void;\n\t/**\n\t * Open the host's global full-screen image previewer for the given image.\n\t * Only the image reference (id/url) crosses over — bytes stay out-of-band.\n\t *\n\t * Pass `group` (e.g. all images of the message) to open as an image group:\n\t * the previewer shows a thumbnail strip + arrows and starts at `ref`.\n\t */\n\tpreviewImage(ref: PluginImageRef, group?: PluginImageRef[]): void;\n\t/**\n\t * Open the app settings, scrolled to and highlighting THIS plugin's own\n\t * settings section (e.g. so the user can fill in a required API key/model).\n\t * The host owns the navigation; the plugin only asks to jump there.\n\t */\n\topenPluginSettings(): void;\n\t/**\n\t * Capture a rectangle in the current Vetta window and open the host save\n\t * dialog. Coordinates use renderer DIP values such as getBoundingClientRect().\n\t * Requires `ui.slot.activity-tab`.\n\t */\n\tcaptureRegion(rect: PluginCaptureRegion, defaultFileName: string): Promise<string | null>;\n\t/**\n\t * Copy an image to the system clipboard. Takes a `data:image/...;base64,`\n\t * URL and goes through the native clipboard, so it does not depend on the\n\t * renderer's `ClipboardItem` support. No permission required — the plugin\n\t * can only write what it already rendered.\n\t */\n\tcopyImage(dataUrl: string): Promise<void>;\n\t/**\n\t * Hand a URL to the OS default browser (Electron `shell.openExternal`) —\n\t * NOT the in-app browser panel. Only `http:`/`https:` are accepted; the host\n\t * rejects every other protocol, so this cannot be used to launch arbitrary\n\t * schemes. Needs `shell.openExternal`.\n\t */\n\topenExternal(url: string): Promise<void>;\n\t/**\n\t * Show a global toast in the host UI (bottom-right). No permission required.\n\t * Prefer this over swallowing errors into opaque UI copy: pass `error` so\n\t * the host attaches a one-click \"copy stack\" action for the user.\n\t *\n\t * Capture `ctx.ui.notify` in `activate` if React components need it\n\t * (components do not receive `ctx`).\n\t */\n\tnotify(options: PluginNotifyOptions): void;\n}\n"]}
package/package.json CHANGED
@@ -1,16 +1,35 @@
1
1
  {
2
2
  "name": "@vetta-org/plugin-sdk",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ },
12
+ "./manifest": {
13
+ "types": "./dist/manifest.d.ts",
14
+ "import": "./dist/manifest.js"
15
+ },
16
+ "./npm-package": {
17
+ "types": "./dist/npm-package.d.ts",
18
+ "import": "./dist/npm-package.js"
19
+ },
20
+ "./tailwind-theme.css": "./src/tailwind-theme.css"
21
+ },
7
22
  "files": [
8
- "dist"
23
+ "dist",
24
+ "src/tailwind-theme.css"
9
25
  ],
10
26
  "scripts": {
11
27
  "clean": "rm -rf dist",
12
28
  "build": "tsgo -p tsconfig.build.json"
13
29
  },
30
+ "dependencies": {
31
+ "@sinclair/typebox": "^0.34.41"
32
+ },
14
33
  "peerDependencies": {
15
34
  "react": "^19.0.0"
16
35
  },