@appthen/vibe-workspace 0.1.0 → 0.1.1

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/PLUGIN_DEV.md CHANGED
@@ -24,6 +24,14 @@ pnpm dev
24
24
 
25
25
  `pnpm install` 是首次准备依赖的步骤。依赖缓存准备好后,`dev`、`validate`、`test`、`build` 和 `pack` 不依赖 marketplace,可以离线完成。
26
26
 
27
+ 如果同时修改 `@appthen/vibe-workspace` 本身,不要为了本地调试发布 npm。可以在 SDK 仓库运行 `pnpm run build:lib -- --watch`,然后从扩展工作区运行:
28
+
29
+ ```bash
30
+ pnpm dev -- --package-root /path/to/vibe-workspace
31
+ ```
32
+
33
+ `--package-root` 会让本地 Workbench、扩展构建和模板运行时使用指定目录的 `dist`。只有要交付给其他开发者时才发布 npm。
34
+
27
35
  ## 2. 工作区结构
28
36
 
29
37
  ```text
@@ -71,6 +79,7 @@ pnpm pack -- acme.example
71
79
  "apiVersion": "1",
72
80
  "main": "./src/index.tsx",
73
81
  "sourceType": "umd",
82
+ "icon": "data:image/svg+xml,%3Csvg ...%3E%3C/svg%3E",
74
83
  "runtime": {
75
84
  "sidebarViewId": "sidebar.acme.example"
76
85
  },
@@ -92,6 +101,23 @@ pnpm pack -- acme.example
92
101
  - `runtime.sidebarViewId`:声明扩展侧边栏视图。
93
102
  - `runtime.autoActivate`:只用于必须在 Workbench 启动时注册的扩展能力,普通扩展不要开启。
94
103
  - `accessPolicy`:声明扩展自身的访问策略,会与租户和工作台策略合并。
104
+ - `icon`:侧边栏和移动端导航图标。v1 使用小型 `data:image/svg+xml` 或 HTTPS 图片 URL;暂不支持从扩展包引用相对路径静态资源。
105
+
106
+ 图标建议使用不超过 32x32 的单色或双色 SVG data URL。需要多文件图片、字体或本地资源目录时,先上传到可信 CDN,再把 HTTPS URL 写入 Manifest;这样不会引入未定义的资源打包和 CDN 路径问题。
107
+
108
+ ## 5.1 工作台主题
109
+
110
+ 主题属于 Workbench 配置,不属于扩展激活副作用。离线工作区可以在 `themes/*.json` 定义主题,并在 `appthen-extension-workspace.json` 指定默认主题:
111
+
112
+ ```json
113
+ {
114
+ "devWorkbench": {
115
+ "theme": "acme.sales"
116
+ }
117
+ }
118
+ ```
119
+
120
+ `pnpm dev` 会自动加载 `themes/` 下的 JSON。主题可通过 `extends` 继承 `vercel`、`dracula` 等内置主题;设置菜单中的主题替换会写入浏览器 localStorage,刷新后仍会保留。修改主题文件后重启 `pnpm dev` 以重新载入。
95
121
 
96
122
  ## 5. 编写扩展
97
123
 
package/README.md CHANGED
@@ -30,6 +30,12 @@ appthen-extension publish [extensionId] --server <url> --tenant <tenantId>
30
30
  appthen-extension install <package-file> --server <url> --tenant <tenantId>
31
31
  ```
32
32
 
33
+ 开发 Workbench SDK 本身时,不需要发布 npm。先在 SDK 仓库执行 `pnpm run build:lib -- --watch`,再从扩展工作区指向本地 SDK:
34
+
35
+ ```bash
36
+ pnpm dev -- --package-root /path/to/vibe-workspace
37
+ ```
38
+
33
39
  `publish` uploads and updates the tenant extension record. It does not mount the extension into a Workbench unless `--install-tenant` is provided. Workbench mounting and permissions are configured separately.
34
40
 
35
41
  ## Stable Extension API
@@ -1159,21 +1159,26 @@ class ThemeService {
1159
1159
  constructor() {
1160
1160
  this.currentTheme = DraculaTheme;
1161
1161
  this.listeners = [];
1162
+ this.customThemes = /* @__PURE__ */ new Map();
1162
1163
  this.applyTheme(DraculaTheme);
1163
1164
  }
1165
+ registerTheme(theme) {
1166
+ if (!(theme == null ? void 0 : theme.id) || !theme.label || !theme.colors || !theme.type) return false;
1167
+ this.customThemes.set(theme.id, theme);
1168
+ this.listeners.forEach((l) => l());
1169
+ return true;
1170
+ }
1171
+ registerThemes(themes) {
1172
+ let changed = false;
1173
+ themes.forEach((theme) => {
1174
+ if (this.registerThemeWithoutNotify(theme)) changed = true;
1175
+ });
1176
+ if (changed) this.listeners.forEach((l) => l());
1177
+ }
1164
1178
  setTheme(themeId) {
1165
1179
  console.log("[ThemeService] setTheme called with:", themeId);
1166
- if (themeId === "dracula" || themeId === "appthen") {
1167
- this.applyTheme(DraculaTheme);
1168
- } else if (themeId === "vercel") {
1169
- this.applyTheme(VercelTheme);
1170
- } else if (themeId === "appthen-light") {
1171
- this.applyTheme(AppThenLightTheme);
1172
- } else if (themeId === "vs-light") {
1173
- this.applyTheme(LightTheme);
1174
- } else {
1175
- this.applyTheme(DarkTheme);
1176
- }
1180
+ const theme = this.resolveTheme(themeId) || DarkTheme;
1181
+ this.applyTheme(theme);
1177
1182
  }
1178
1183
  setUserTheme(themeId) {
1179
1184
  this.setTheme(themeId);
@@ -1193,7 +1198,30 @@ class ThemeService {
1193
1198
  return this.currentTheme;
1194
1199
  }
1195
1200
  getAvailableThemes() {
1196
- return [DraculaTheme, VercelTheme, AppThenLightTheme, DarkTheme, LightTheme];
1201
+ return [DraculaTheme, VercelTheme, AppThenLightTheme, DarkTheme, LightTheme, ...this.customThemes.values()].map((theme) => this.resolveTheme(theme.id) || theme);
1202
+ }
1203
+ registerThemeWithoutNotify(theme) {
1204
+ if (!(theme == null ? void 0 : theme.id) || !theme.label || !theme.colors || !theme.type) return false;
1205
+ const previous = this.customThemes.get(theme.id);
1206
+ this.customThemes.set(theme.id, theme);
1207
+ return JSON.stringify(previous) !== JSON.stringify(theme);
1208
+ }
1209
+ resolveTheme(themeId, seen = /* @__PURE__ */ new Set()) {
1210
+ var _a2, _b;
1211
+ const builtIn = [DraculaTheme, VercelTheme, AppThenLightTheme, DarkTheme, LightTheme].find((theme2) => theme2.id === themeId || themeId === "appthen" && theme2.id === "dracula" || themeId === "vs-light" && theme2.id === "light");
1212
+ const theme = builtIn || this.customThemes.get(themeId);
1213
+ if (!theme || seen.has(theme.id)) return theme;
1214
+ if (!theme.extends) return theme;
1215
+ seen.add(theme.id);
1216
+ const parent = this.resolveTheme(theme.extends, seen);
1217
+ if (!parent) return theme;
1218
+ return {
1219
+ ...parent,
1220
+ ...theme,
1221
+ colors: { ...parent.colors, ...theme.colors },
1222
+ portalColors: { ...parent.portalColors, ...theme.portalColors },
1223
+ monaco: { ...parent.monaco, ...theme.monaco, colors: { ...(_a2 = parent.monaco) == null ? void 0 : _a2.colors, ...(_b = theme.monaco) == null ? void 0 : _b.colors } }
1224
+ };
1197
1225
  }
1198
1226
  applyTheme(theme) {
1199
1227
  this.currentTheme = theme;
@@ -136955,6 +136983,7 @@ function WorkbenchProvider({
136955
136983
  var _a2, _b, _c, _d, _e2, _f, _g, _h;
136956
136984
  const [state, dispatch2] = useReducer(workbenchReducer, initialState);
136957
136985
  useEffect(() => {
136986
+ var _a3;
136958
136987
  if (config) {
136959
136988
  const resolvedConfig = config.scene || config.workbenchId !== "vibecoding" ? config : createVibeCodingWorkbenchConfig(config);
136960
136989
  const resolvedExtensions = resolveRuntimeExtensions(resolvedConfig);
@@ -136968,6 +136997,9 @@ function WorkbenchProvider({
136968
136997
  state.activeLeftExtensionId
136969
136998
  )
136970
136999
  });
137000
+ if ((_a3 = resolvedConfig.themeCatalog) == null ? void 0 : _a3.length) {
137001
+ themeService.registerThemes(resolvedConfig.themeCatalog);
137002
+ }
136971
137003
  if (resolvedConfig.theme) {
136972
137004
  const themeId = themeService.getPreferredThemeId() || resolvedConfig.theme;
136973
137005
  console.log("[Workbench] Applying theme:", themeId);
@@ -137486,6 +137518,7 @@ function WorkbenchProvider({
137486
137518
  const loadWorkbenchConfig = async () => {
137487
137519
  console.log("[Workbench] Loading config for:", workbenchId);
137488
137520
  const applyResolvedConfig = (resolvedConfig) => {
137521
+ var _a4;
137489
137522
  if (activeWorkbenchConfigKeyRef.current !== configKey) return;
137490
137523
  const resolvedExtensions = resolveRuntimeExtensions(resolvedConfig);
137491
137524
  dispatch2({ type: "SET_WORKBENCH_CONFIG", payload: resolvedConfig });
@@ -137498,6 +137531,9 @@ function WorkbenchProvider({
137498
137531
  state.activeLeftExtensionId
137499
137532
  )
137500
137533
  });
137534
+ if ((_a4 = resolvedConfig.themeCatalog) == null ? void 0 : _a4.length) {
137535
+ themeService.registerThemes(resolvedConfig.themeCatalog);
137536
+ }
137501
137537
  if (resolvedConfig.theme) {
137502
137538
  const themeId = themeService.getPreferredThemeId() || resolvedConfig.theme;
137503
137539
  console.log("[Workbench] Applying theme:", themeId);
@@ -139503,7 +139539,7 @@ const WorkbenchSettingsMenu = ({
139503
139539
  onClose
139504
139540
  }) => {
139505
139541
  const [currentTheme, setCurrentTheme] = useState(themeService.getCurrentTheme());
139506
- const themes = useMemo(() => themeService.getAvailableThemes(), []);
139542
+ const themes = themeService.getAvailableThemes();
139507
139543
  useEffect(() => {
139508
139544
  return themeService.onThemeChange(() => {
139509
139545
  setCurrentTheme(themeService.getCurrentTheme());
@@ -140990,4 +141026,4 @@ export {
140990
141026
  normalizeFileSrc as y,
140991
141027
  previewService as z
140992
141028
  };
140993
- //# sourceMappingURL=Workbench-Dhd6kXGh.js.map
141029
+ //# sourceMappingURL=Workbench-Bh2vD56R.js.map