@ycodium-ai/plugin-api 0.1.0 → 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.
package/README.md CHANGED
@@ -12,7 +12,15 @@ own state across restarts.
12
12
  npm install --save-dev @ycodium-ai/plugin-api
13
13
  ```
14
14
 
15
- This package is types only at build time — nothing of it ends up in your bundle.
15
+ Types are erased at build time. Runtime exports such as `definePlugin` come from the
16
+ Ycodium host's shared copy; keep this package external when bundling your plugin.
17
+
18
+ The CSS subpath `@ycodium-ai/plugin-api/ui.css` is the public Tailwind preset for
19
+ plugin pages: control-axis utilities, semantic colors, status animations, and
20
+ `surface-glass`. It does not reset browser defaults. Host `index.css` and first-party plugin
21
+ stylesheets import this same file. It does not reset browser defaults. Token _values_ still come from the host
22
+ document (`:root` and `applyTheme`). Chart series colors are host CSS variables
23
+ `--provider-*`, not a second copy of the palette.
16
24
 
17
25
  ## A plugin is an npm package
18
26
 
@@ -37,11 +45,11 @@ Declare the entry file under `ycodium.plugin`:
37
45
  And export the definition:
38
46
 
39
47
  ```ts
40
- import { definePlugin, PLUGIN_API_VERSION } from "@ycodium-ai/plugin-api";
48
+ import { definePlugin } from "@ycodium-ai/plugin-api";
41
49
 
42
50
  export default definePlugin({
43
51
  id: "my-plugin",
44
- apiVersion: PLUGIN_API_VERSION,
52
+ apiVersion: 2,
45
53
  activate: (ctx) => {
46
54
  ctx.log.info("hello");
47
55
 
@@ -69,12 +77,45 @@ ycodium plugin add my-ycodium-plugin
69
77
 
70
78
  ## Bundle your entry file
71
79
 
72
- The host loads your entry by absolute path, from a directory inside the user's Ycodium home
73
- — not inside your package's dependency tree. A bare `import` at runtime will not resolve
74
- there.
80
+ Bundle your own code and private dependencies into an ESM entry. Keep
81
+ `@ycodium-ai/plugin-api` and its subpaths external. For esbuild:
82
+
83
+ ```js
84
+ external: ["@ycodium-ai/plugin-api", "@ycodium-ai/plugin-api/*"];
85
+ ```
86
+
87
+ The host resolves these imports to its shared API module, including for bundled official
88
+ plugins, npm installations, local directories, and temporary plugins. Plugins do not need
89
+ to copy the API into their package or depend on a particular installation directory.
90
+
91
+ Browser bundles use the same rule. Map runtime API imports to the host's module table,
92
+ `window.__ycodiumPluginHost.modules`, as you map React to the host's React instance.
93
+ For example, an alias module for a UI helper can re-export the host value:
94
+
95
+ ```js
96
+ const api = window.__ycodiumPluginHost.modules?.["@ycodium-ai/plugin-api/ui"];
97
+ if (!api) throw new Error("This plugin needs a newer Ycodium app. Update the app.");
98
+ export const { pluginUiHostHasCapability } = api;
99
+ ```
100
+
101
+ The table also exposes `@ycodium-ai/plugin-api`. Keep API implementations out of page bundles;
102
+ type-only imports need no alias. Web and embedded plugin pages provide the same module table.
103
+
104
+ ## Embedded views and host controls
105
+
106
+ `register(bridge)` can return an optional `views` map alongside `pages` and `panels`.
107
+ A view has no navigation entry. Its caller supplies the agreed props; the host loads it
108
+ from the same machine-scoped bundle and translation context as the full page. This lets
109
+ an embedded detail and its full page share one implementation. Missing or unloaded views
110
+ show an unavailable state.
75
111
 
76
- **Bundle your entry into one ESM file with no runtime imports.** Types from this package are
77
- compile-time only and erase during bundling, so importing it for types is fine.
112
+ The host UI surface includes `InputGroup`, `InputGroupAddon`, `InputGroupInput`,
113
+ `DocumentPanel`, and `MasterDetail`. `DocumentPanel` accepts `PluginUiDocumentTab` descriptors,
114
+ an active id, close/activate callbacks, and a `widthStorageKey`; the host supplies its tab
115
+ strip, menus, resize handle, and stored width. `MasterDetail` takes the list as children,
116
+ a `detail`, an `onBack` callback, and `onMasterVisibilityChange`. Stop hidden list observers
117
+ when that callback reports `false`. The list stays mounted to retain its scroll position.
118
+ `useLinkContextMenu` supplies the host's copy/open menu for a link.
78
119
 
79
120
  ## Permissions
80
121
 
@@ -96,7 +137,9 @@ shows up there without anyone registering it.
96
137
 
97
138
  ## THERE WILL BE COMPATIBILITY-BREAKING CHANGES
98
139
 
99
- `PLUGIN_API_VERSION` is checked for **exact equality**. The current API is 2. Plugins targeting
140
+ The definition's `apiVersion` is checked for **exact equality**. Declare the version your
141
+ plugin targets as a literal; do not read the host's `PLUGIN_API_VERSION` to make the declaration
142
+ automatically match. The current API is 2. Plugins targeting
100
143
  API 1 are refused before activation. API 2 replaces the Teams-specific host methods with generic
101
144
  execution ownership, command receipts, notifications, and plugin contributions. Update plugins
102
145
  against the current types and republish; there is no compatibility layer.
package/css/ui.css ADDED
@@ -0,0 +1,180 @@
1
+ /* 宿主与插件共用工具类注册;令牌值由宿主主题提供,不重置浏览器默认样式。
2
+ 插件在自己的 @scope 下生成工具类,不依赖宿主恰好产出的同名类。 */
3
+
4
+ @theme inline {
5
+ --spacing-control-1: var(--control-h-1);
6
+ --spacing-control-2: var(--control-h-2);
7
+ --spacing-control-3: var(--control-h-3);
8
+ --spacing-control-4: var(--control-h-4);
9
+ --spacing-control-5: var(--control-h-5);
10
+ --spacing-nested-1: var(--control-h-nested-1);
11
+ --spacing-nested-2: var(--control-h-nested-2);
12
+ --spacing-nested-3: var(--control-h-nested-3);
13
+ --spacing-nested-4: var(--control-h-nested-4);
14
+ --spacing-nested-5: var(--control-h-nested-5);
15
+ --spacing-icon-1: var(--control-icon-1);
16
+ --spacing-icon-2: var(--control-icon-2);
17
+ --spacing-icon-3: var(--control-icon-3);
18
+ --spacing-icon-4: var(--control-icon-4);
19
+ --spacing-icon-5: var(--control-icon-5);
20
+ --radius-control-1: var(--control-radius-1);
21
+ --radius-control-2: var(--control-radius-2);
22
+ --radius-control-3: var(--control-radius-3);
23
+ --radius-control-4: var(--control-radius-4);
24
+ --radius-control-5: var(--control-radius-5);
25
+ --text-control-1: var(--control-text-1);
26
+ --text-control-1--line-height: var(--control-leading-1);
27
+ --text-control-2: var(--control-text-2);
28
+ --text-control-2--line-height: var(--control-leading-2);
29
+ --text-control-3: var(--control-text-3);
30
+ --text-control-3--line-height: var(--control-leading-3);
31
+ --text-control-4: var(--control-text-4);
32
+ --text-control-4--line-height: var(--control-leading-4);
33
+ --text-control-5: var(--control-text-5);
34
+ --text-control-5--line-height: var(--control-leading-5);
35
+
36
+ --animate-skeleton: skeleton 2s infinite linear;
37
+ /* Duty-cycled indicator animations: long holds with stepped ramps, so the
38
+ compositor updates discrete frames instead of every vsync. */
39
+ --animate-status-pulse: status-pulse 2s infinite;
40
+ --animate-ghost-pulse: ghost-pulse 2.4s infinite;
41
+ --animate-status-ping: status-ping 2s infinite;
42
+ --animate-status-spin: status-spin 0.9s infinite steps(3);
43
+ --animate-tab-run-breathe: tab-run-breathe 1.8s ease-in-out infinite;
44
+
45
+ --color-warning-foreground: var(--warning-foreground);
46
+ --color-warning: var(--warning);
47
+ --color-warning-surface: var(--warning-surface);
48
+ --color-error-foreground: var(--error-foreground);
49
+ --color-error: var(--error);
50
+ --color-error-surface: var(--error-surface);
51
+ --color-update-foreground: var(--update-foreground);
52
+ --color-update: var(--update);
53
+ --color-update-surface: var(--update-surface);
54
+ --color-success-foreground: var(--success-foreground);
55
+ --color-success: var(--success);
56
+ --color-info-foreground: var(--info-foreground);
57
+ --color-info: var(--info);
58
+ --color-destructive-foreground: var(--destructive-foreground);
59
+ --color-ring: var(--ring);
60
+ --color-input: var(--contrast-input);
61
+ --color-border: var(--contrast-border);
62
+ --color-destructive: var(--destructive);
63
+ --color-accent-foreground: var(--contrast-accent-foreground);
64
+ --color-accent: var(--accent);
65
+ --color-accent-pressed: var(--accent-pressed);
66
+ --color-muted-foreground: var(--contrast-muted-foreground);
67
+ --color-muted: var(--muted);
68
+ --color-placeholder: var(--contrast-placeholder);
69
+ --color-secondary-label: var(--contrast-secondary-label);
70
+ --color-icon-muted: var(--contrast-icon-muted);
71
+ --color-secondary-foreground: var(--contrast-secondary-foreground);
72
+ --color-secondary: var(--secondary);
73
+ --color-primary-foreground: var(--primary-foreground);
74
+ --color-primary: var(--primary);
75
+ --color-popover-foreground: var(--contrast-popover-foreground);
76
+ --color-popover: var(--popover);
77
+ --color-card-foreground: var(--contrast-card-foreground);
78
+ --color-card: var(--card);
79
+ --color-foreground: var(--contrast-foreground);
80
+ --color-background: var(--background);
81
+
82
+ --radius-sm: calc(var(--radius) - 4px);
83
+ --radius-md: calc(var(--radius) - 2px);
84
+ --radius-lg: var(--radius);
85
+ --radius-xl: calc(var(--radius) + 4px);
86
+ --radius-2xl: calc(var(--radius) + 8px);
87
+ --radius-3xl: calc(var(--radius) + 12px);
88
+ --radius-4xl: calc(var(--radius) + 16px);
89
+
90
+ @keyframes skeleton {
91
+ /* Transform-only so the highlight sweep stays on the compositor, then a
92
+ long hold with the band parked off-screen instead of a constant shimmer. */
93
+ 0% {
94
+ transform: translateX(-100%);
95
+ }
96
+ 60%,
97
+ 100% {
98
+ transform: translateX(100%);
99
+ }
100
+ }
101
+ @keyframes ghost-pulse {
102
+ /* The loading ghosts' breath. Stepped like the status indicators, so however many bars a
103
+ ghost holds, the compositor draws a handful of discrete frames per cycle rather than one
104
+ per vsync — which on a 120Hz display is the difference between ~14 and ~288 updates. */
105
+ 0%,
106
+ 42% {
107
+ opacity: 1;
108
+ animation-timing-function: steps(4);
109
+ }
110
+ 50%,
111
+ 92% {
112
+ opacity: 0.55;
113
+ animation-timing-function: steps(4);
114
+ }
115
+ 100% {
116
+ opacity: 1;
117
+ }
118
+ }
119
+ @keyframes status-pulse {
120
+ 0%,
121
+ 40% {
122
+ opacity: 1;
123
+ animation-timing-function: steps(6);
124
+ }
125
+ 50%,
126
+ 90% {
127
+ opacity: 0.5;
128
+ animation-timing-function: steps(6);
129
+ }
130
+ 100% {
131
+ opacity: 1;
132
+ }
133
+ }
134
+ /* 只转 45° 就回到起点:消费者是 8 段等距的虚线圈,转满一格跟自己完全重合,
135
+ 所以循环点无缝,不必真的转满 360°。steps(3) 让一圈只有三帧——分步是这里的
136
+ 全部意义,连续旋转会在高刷屏上每 vsync 重绘一次。 */
137
+ @keyframes status-spin {
138
+ to {
139
+ transform: rotate(45deg);
140
+ }
141
+ }
142
+ /* 会话运行点的呼吸。**只动 opacity**,别加 `transform: scale`——实测在 DPR 1.5
143
+ 的屏幕上 Blink 不把 transform 动画交给合成器,每帧回主线程做一次全局样式
144
+ 重算:5 秒 810 次(无动画时 27 次)。rotate 与 scale 都一样,跟元素大小无关,
145
+ 4px 的点加了 scale 照样 810 次。只淡入淡出是 80 次。 */
146
+ @keyframes tab-run-breathe {
147
+ 0%,
148
+ 100% {
149
+ opacity: 1;
150
+ }
151
+ 50% {
152
+ opacity: 0.35;
153
+ }
154
+ }
155
+ @keyframes status-ping {
156
+ /* Burst first (immediate feedback for click ripples), then hold
157
+ invisible for the rest of the cycle. Mirrors animate-ping's
158
+ 75%-scale start. */
159
+ 0% {
160
+ opacity: 0.9;
161
+ scale: 0.75;
162
+ animation-timing-function: steps(8);
163
+ }
164
+ 40%,
165
+ 100% {
166
+ opacity: 0;
167
+ scale: 2;
168
+ }
169
+ }
170
+ }
171
+
172
+ @utility surface-glass {
173
+ background: color-mix(in srgb, var(--background) var(--glass-opacity), transparent);
174
+ -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
175
+ backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
176
+
177
+ @supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
178
+ background: var(--background) !important;
179
+ }
180
+ }