@dsh-workbench/core 0.2.2 → 0.2.4

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/client.js CHANGED
@@ -1243,6 +1243,8 @@ window.__ModuleLoader__.load({
1243
1243
  "project.search.label": "Search workspaces",
1244
1244
  "project.search.placeholder": "Search workspaces (folder / name / path)",
1245
1245
  "project.search.empty": "No workspace matches “{query}”.",
1246
+ "project.group.workspaces": "Workspaces",
1247
+ "project.group.workshop": "Workshop",
1246
1248
  "project.rename.row": "Rename “{name}”",
1247
1249
  "project.rename.current": "Give this workspace a display name…",
1248
1250
  "project.rename.title": "Give “{name}” a display name",
@@ -1417,6 +1419,7 @@ window.__ModuleLoader__.load({
1417
1419
  "panels.market.add.failureIntegrity": "The downloaded package failed its integrity check",
1418
1420
  "panels.market.add.failureTimeout": "The install timed out",
1419
1421
  "panels.market.add.failurePnpmMissing": "pnpm was not found, cannot install",
1422
+ "panels.market.add.failureInstalled": "It is already in this profile: this dsh runs with hot reload, which refuses to install over the same package (remove it first) — and removing it failed too",
1420
1423
  "panels.market.add.failureGeneric": "Something went wrong during install — see the install details",
1421
1424
  "panels.market.add.approveTitle": "Your approval is needed for install scripts",
1422
1425
  "panels.market.add.approveCaution": "Only allow packages you trust: install scripts run on this machine with your permissions.",
@@ -1588,6 +1591,8 @@ window.__ModuleLoader__.load({
1588
1591
  "project.search.label": "搜索工作区",
1589
1592
  "project.search.placeholder": "搜索工作区(目录名 / 中文名 / 路径)",
1590
1593
  "project.search.empty": "没有匹配「{query}」的工作区。",
1594
+ "project.group.workspaces": "工作区",
1595
+ "project.group.workshop": "工坊",
1591
1596
  "project.rename.row": "改「{name}」的中文名",
1592
1597
  "project.rename.current": "改当前工作区的中文名…",
1593
1598
  "project.rename.title": "给「{name}」配一个中文名",
@@ -1762,6 +1767,7 @@ window.__ModuleLoader__.load({
1762
1767
  "panels.market.add.failureIntegrity": "下载的安装包校验失败",
1763
1768
  "panels.market.add.failureTimeout": "安装超时",
1764
1769
  "panels.market.add.failurePnpmMissing": "没有找到 pnpm,无法安装",
1770
+ "panels.market.add.failureInstalled": "它已经在这份 profile 里了:这一版 dsh 开着热更新,同一个包不许就地覆盖装(得先卸载再装),而这次也没能把它卸下来",
1765
1771
  "panels.market.add.failureGeneric": "安装过程中出错,原因见安装详情",
1766
1772
  "panels.market.add.approveTitle": "需要你允许安装脚本",
1767
1773
  "panels.market.add.approveCaution": "只允许你信得过的包:安装脚本会以你的权限在这台机器上运行。",
@@ -2210,23 +2216,37 @@ window.__ModuleLoader__.load({
2210
2216
  function tailLines(text, lines = 6) {
2211
2217
  return String(text ?? "").trim().split("\n").slice(-lines).join("\n");
2212
2218
  }
2219
+ function candidateStarts(raw) {
2220
+ const starts = [];
2221
+ for (let index = 0; index < raw.length; index += 1) {
2222
+ const char = raw[index];
2223
+ if (char !== "[" && char !== "{") continue;
2224
+ if (index === 0 || raw[index - 1] === "\n") starts.push(index);
2225
+ }
2226
+ const earliest = [raw.indexOf("["), raw.indexOf("{")].filter((index) => index >= 0);
2227
+ if (earliest.length > 0) {
2228
+ const head = Math.min(...earliest);
2229
+ if (!starts.includes(head)) starts.push(head);
2230
+ }
2231
+ return starts;
2232
+ }
2213
2233
  function parseJsonLoose(text) {
2214
2234
  const raw = String(text ?? "").trim();
2215
2235
  if (!raw) return void 0;
2216
2236
  try {
2217
2237
  return JSON.parse(raw);
2218
2238
  } catch {
2219
- const starts = [raw.indexOf("["), raw.indexOf("{")].filter((index) => index >= 0);
2220
- if (starts.length === 0) return void 0;
2221
- const from = Math.min(...starts);
2222
- const close = raw[from] === "[" ? "]" : "}";
2223
- const to = raw.lastIndexOf(close);
2224
- if (to <= from) return void 0;
2225
- try {
2226
- return JSON.parse(raw.slice(from, to + 1));
2227
- } catch {
2228
- return;
2239
+ for (const from of candidateStarts(raw)) {
2240
+ const close = raw[from] === "[" ? "]" : "}";
2241
+ const to = raw.lastIndexOf(close);
2242
+ if (to <= from) continue;
2243
+ try {
2244
+ return JSON.parse(raw.slice(from, to + 1));
2245
+ } catch {
2246
+ continue;
2247
+ }
2229
2248
  }
2249
+ return;
2230
2250
  }
2231
2251
  }
2232
2252
  var MARKDOWN_IMAGE = /!\[[^\]]*\]\([^)]*\)/g;
@@ -4692,6 +4712,7 @@ window.__ModuleLoader__.load({
4692
4712
  integrity: "panels.market.add.failureIntegrity",
4693
4713
  timeout: "panels.market.add.failureTimeout",
4694
4714
  "pnpm-missing": "panels.market.add.failurePnpmMissing",
4715
+ "already-installed": "panels.market.add.failureInstalled",
4695
4716
  "operation-error": "panels.market.add.failureGeneric",
4696
4717
  "incompatible-version": "panels.market.add.failureGeneric",
4697
4718
  "not-bundle": "panels.market.add.failureGeneric",
@@ -6254,8 +6275,12 @@ window.__ModuleLoader__.load({
6254
6275
  //#endregion
6255
6276
  //#region src/shared/api/diy.ts
6256
6277
  var DIY_PRESET_ID = "workbench-diy";
6278
+ var WORKSHOP_RELATIVE = "workbench/diy";
6257
6279
  var WORKSHOP_TITLE = "工作台DIY";
6258
6280
  var WORKSHOP_PAST_TITLES = ["DIY 工坊", "工作台自带 · DIY 模块住这儿"];
6281
+ function isWorkshopPath(path) {
6282
+ return path.replace(/\\/g, "/").replace(/\/+$/, "").endsWith(`/${WORKSHOP_RELATIVE}`);
6283
+ }
6259
6284
  //#endregion
6260
6285
  //#region src/app/units/index.ts
6261
6286
  var CLIENT_UNITS = [];
@@ -8783,6 +8808,29 @@ window.__ModuleLoader__.load({
8783
8808
  }));
8784
8809
  }
8785
8810
  //#endregion
8811
+ //#region src/app/layouts/sidebar/workspace-groups.ts
8812
+ function workspaceSections(items, headings) {
8813
+ const workspaces = [];
8814
+ const workshop = [];
8815
+ for (const item of items) if (isWorkshopPath(item.path)) workshop.push(item);
8816
+ else workspaces.push(item);
8817
+ const sections = [];
8818
+ if (workspaces.length > 0) sections.push({
8819
+ id: "workspaces",
8820
+ heading: workshop.length > 0 ? headings.workspaces : null,
8821
+ items: workspaces
8822
+ });
8823
+ if (workshop.length > 0) sections.push({
8824
+ id: "workshop",
8825
+ heading: headings.workshop,
8826
+ items: workshop
8827
+ });
8828
+ return sections;
8829
+ }
8830
+ function flattenSections(sections) {
8831
+ return sections.flatMap((section) => section.items);
8832
+ }
8833
+ //#endregion
8786
8834
  //#region src/app/layouts/sidebar/WorkspaceRowAction.tsx
8787
8835
  function WorkspaceRowAction({ workspace, t, onEdit }) {
8788
8836
  return /* @__PURE__ */ h(_deepseek_ai_dsh_client_ui_primitives.Button, {
@@ -8830,8 +8878,8 @@ window.__ModuleLoader__.load({
8830
8878
  //#endregion
8831
8879
  //#region src/app/layouts/sidebar/workspace-items.tsx
8832
8880
  function workspaceMenuItems(options) {
8833
- const { results, currentId, hoverId, t, onHover, onEdit } = options;
8834
- return results.map((workspace) => ({
8881
+ const { sections, currentId, hoverId, t, onHover, onEdit } = options;
8882
+ const rowOf = (workspace) => ({
8835
8883
  id: workspace.workspaceId,
8836
8884
  icon: /* @__PURE__ */ h(_deepseek_ai_dsh_client_ui_primitives.IconFolderOpenRegular, { size: 16 }),
8837
8885
  label: /* @__PURE__ */ h(WorkspaceRowLabel, {
@@ -8844,7 +8892,12 @@ window.__ModuleLoader__.load({
8844
8892
  onEdit: () => onEdit(workspace)
8845
8893
  }) : void 0
8846
8894
  })
8847
- }));
8895
+ });
8896
+ return sections.flatMap((section) => [...section.heading === null ? [] : [{
8897
+ type: "label",
8898
+ id: section.id,
8899
+ text: section.heading
8900
+ }], ...section.items.map(rowOf)]);
8848
8901
  }
8849
8902
  //#endregion
8850
8903
  //#region src/app/layouts/sidebar/ProjectSwitcher.tsx
@@ -8864,7 +8917,12 @@ window.__ModuleLoader__.load({
8864
8917
  const wasOpenRef = useRef(false);
8865
8918
  const currentId = current?.workspaceId ?? null;
8866
8919
  const results = useMemo(() => matchWorkspaces(items, query), [items, query]);
8867
- const cursorId = cursor === null || results.length === 0 ? null : results[Math.min(cursor, results.length - 1)]?.workspaceId ?? null;
8920
+ const sections = useMemo(() => workspaceSections(results, {
8921
+ workspaces: t("project.group.workspaces"),
8922
+ workshop: t("project.group.workshop")
8923
+ }), [results, t]);
8924
+ const ordered = useMemo(() => flattenSections(sections), [sections]);
8925
+ const cursorId = cursor === null || ordered.length === 0 ? null : ordered[Math.min(cursor, ordered.length - 1)]?.workspaceId ?? null;
8868
8926
  useEffect(() => {
8869
8927
  if (open) {
8870
8928
  wasOpenRef.current = true;
@@ -8887,11 +8945,11 @@ window.__ModuleLoader__.load({
8887
8945
  setCursor(null);
8888
8946
  },
8889
8947
  onCursor: (step) => {
8890
- if (results.length === 0) return;
8891
- setCursor((at) => at === null ? step === 1 ? 0 : results.length - 1 : (at + step + results.length) % results.length);
8948
+ if (ordered.length === 0) return;
8949
+ setCursor((at) => at === null ? step === 1 ? 0 : ordered.length - 1 : (at + step + ordered.length) % ordered.length);
8892
8950
  },
8893
8951
  onSubmit: () => {
8894
- const pick = results[Math.min(cursor ?? 0, results.length - 1)];
8952
+ const pick = ordered[Math.min(cursor ?? 0, ordered.length - 1)];
8895
8953
  if (pick === void 0) return;
8896
8954
  close();
8897
8955
  onPick(pick.workspaceId);
@@ -8921,7 +8979,7 @@ window.__ModuleLoader__.load({
8921
8979
  id: "empty",
8922
8980
  text: t("project.search.empty", { query: query.trim() })
8923
8981
  }] : [], ...workspaceMenuItems({
8924
- results,
8982
+ sections,
8925
8983
  currentId,
8926
8984
  hoverId,
8927
8985
  t,
@@ -9642,7 +9700,7 @@ window.__ModuleLoader__.load({
9642
9700
  disposeDictionary?.();
9643
9701
  };
9644
9702
  }, "dsh-workbench: dictionaries");
9645
- const workshopWorkspace = () => ctx.workspaces.list.getSnapshot().items.find((item) => item.path.endsWith("/workbench/diy"));
9703
+ const workshopWorkspace = () => ctx.workspaces.list.getSnapshot().items.find((item) => isWorkshopPath(item.path));
9646
9704
  const openDiySession = (workspaceId, fallback) => {
9647
9705
  const remoteCreate = sessionCreateOf(ctx.remote);
9648
9706
  try {
@@ -1236,6 +1236,8 @@ var en = {
1236
1236
  "project.search.label": "Search workspaces",
1237
1237
  "project.search.placeholder": "Search workspaces (folder / name / path)",
1238
1238
  "project.search.empty": "No workspace matches “{query}”.",
1239
+ "project.group.workspaces": "Workspaces",
1240
+ "project.group.workshop": "Workshop",
1239
1241
  "project.rename.row": "Rename “{name}”",
1240
1242
  "project.rename.current": "Give this workspace a display name…",
1241
1243
  "project.rename.title": "Give “{name}” a display name",
@@ -1410,6 +1412,7 @@ var en = {
1410
1412
  "panels.market.add.failureIntegrity": "The downloaded package failed its integrity check",
1411
1413
  "panels.market.add.failureTimeout": "The install timed out",
1412
1414
  "panels.market.add.failurePnpmMissing": "pnpm was not found, cannot install",
1415
+ "panels.market.add.failureInstalled": "It is already in this profile: this dsh runs with hot reload, which refuses to install over the same package (remove it first) — and removing it failed too",
1413
1416
  "panels.market.add.failureGeneric": "Something went wrong during install — see the install details",
1414
1417
  "panels.market.add.approveTitle": "Your approval is needed for install scripts",
1415
1418
  "panels.market.add.approveCaution": "Only allow packages you trust: install scripts run on this machine with your permissions.",
@@ -1581,6 +1584,8 @@ var zh = {
1581
1584
  "project.search.label": "搜索工作区",
1582
1585
  "project.search.placeholder": "搜索工作区(目录名 / 中文名 / 路径)",
1583
1586
  "project.search.empty": "没有匹配「{query}」的工作区。",
1587
+ "project.group.workspaces": "工作区",
1588
+ "project.group.workshop": "工坊",
1584
1589
  "project.rename.row": "改「{name}」的中文名",
1585
1590
  "project.rename.current": "改当前工作区的中文名…",
1586
1591
  "project.rename.title": "给「{name}」配一个中文名",
@@ -1755,6 +1760,7 @@ var zh = {
1755
1760
  "panels.market.add.failureIntegrity": "下载的安装包校验失败",
1756
1761
  "panels.market.add.failureTimeout": "安装超时",
1757
1762
  "panels.market.add.failurePnpmMissing": "没有找到 pnpm,无法安装",
1763
+ "panels.market.add.failureInstalled": "它已经在这份 profile 里了:这一版 dsh 开着热更新,同一个包不许就地覆盖装(得先卸载再装),而这次也没能把它卸下来",
1758
1764
  "panels.market.add.failureGeneric": "安装过程中出错,原因见安装详情",
1759
1765
  "panels.market.add.approveTitle": "需要你允许安装脚本",
1760
1766
  "panels.market.add.approveCaution": "只允许你信得过的包:安装脚本会以你的权限在这台机器上运行。",
@@ -2203,23 +2209,37 @@ function quoteShellArg(value) {
2203
2209
  function tailLines(text, lines = 6) {
2204
2210
  return String(text ?? "").trim().split("\n").slice(-lines).join("\n");
2205
2211
  }
2212
+ function candidateStarts(raw) {
2213
+ const starts = [];
2214
+ for (let index = 0; index < raw.length; index += 1) {
2215
+ const char = raw[index];
2216
+ if (char !== "[" && char !== "{") continue;
2217
+ if (index === 0 || raw[index - 1] === "\n") starts.push(index);
2218
+ }
2219
+ const earliest = [raw.indexOf("["), raw.indexOf("{")].filter((index) => index >= 0);
2220
+ if (earliest.length > 0) {
2221
+ const head = Math.min(...earliest);
2222
+ if (!starts.includes(head)) starts.push(head);
2223
+ }
2224
+ return starts;
2225
+ }
2206
2226
  function parseJsonLoose(text) {
2207
2227
  const raw = String(text ?? "").trim();
2208
2228
  if (!raw) return void 0;
2209
2229
  try {
2210
2230
  return JSON.parse(raw);
2211
2231
  } catch {
2212
- const starts = [raw.indexOf("["), raw.indexOf("{")].filter((index) => index >= 0);
2213
- if (starts.length === 0) return void 0;
2214
- const from = Math.min(...starts);
2215
- const close = raw[from] === "[" ? "]" : "}";
2216
- const to = raw.lastIndexOf(close);
2217
- if (to <= from) return void 0;
2218
- try {
2219
- return JSON.parse(raw.slice(from, to + 1));
2220
- } catch {
2221
- return;
2232
+ for (const from of candidateStarts(raw)) {
2233
+ const close = raw[from] === "[" ? "]" : "}";
2234
+ const to = raw.lastIndexOf(close);
2235
+ if (to <= from) continue;
2236
+ try {
2237
+ return JSON.parse(raw.slice(from, to + 1));
2238
+ } catch {
2239
+ continue;
2240
+ }
2222
2241
  }
2242
+ return;
2223
2243
  }
2224
2244
  }
2225
2245
  var MARKDOWN_IMAGE = /!\[[^\]]*\]\([^)]*\)/g;
@@ -4685,6 +4705,7 @@ var FAILURE_KEYS = {
4685
4705
  integrity: "panels.market.add.failureIntegrity",
4686
4706
  timeout: "panels.market.add.failureTimeout",
4687
4707
  "pnpm-missing": "panels.market.add.failurePnpmMissing",
4708
+ "already-installed": "panels.market.add.failureInstalled",
4688
4709
  "operation-error": "panels.market.add.failureGeneric",
4689
4710
  "incompatible-version": "panels.market.add.failureGeneric",
4690
4711
  "not-bundle": "panels.market.add.failureGeneric",
@@ -6247,8 +6268,12 @@ function registerMarketPanel(ctx, units) {
6247
6268
  //#endregion
6248
6269
  //#region src/shared/api/diy.ts
6249
6270
  var DIY_PRESET_ID = "workbench-diy";
6271
+ var WORKSHOP_RELATIVE = "workbench/diy";
6250
6272
  var WORKSHOP_TITLE = "工作台DIY";
6251
6273
  var WORKSHOP_PAST_TITLES = ["DIY 工坊", "工作台自带 · DIY 模块住这儿"];
6274
+ function isWorkshopPath(path) {
6275
+ return path.replace(/\\/g, "/").replace(/\/+$/, "").endsWith(`/${WORKSHOP_RELATIVE}`);
6276
+ }
6252
6277
  //#endregion
6253
6278
  //#region src/app/units/index.ts
6254
6279
  var CLIENT_UNITS = [];
@@ -8776,6 +8801,29 @@ function WorkspaceSearchField({ t, query, onQuery, onCursor, onSubmit }) {
8776
8801
  }));
8777
8802
  }
8778
8803
  //#endregion
8804
+ //#region src/app/layouts/sidebar/workspace-groups.ts
8805
+ function workspaceSections(items, headings) {
8806
+ const workspaces = [];
8807
+ const workshop = [];
8808
+ for (const item of items) if (isWorkshopPath(item.path)) workshop.push(item);
8809
+ else workspaces.push(item);
8810
+ const sections = [];
8811
+ if (workspaces.length > 0) sections.push({
8812
+ id: "workspaces",
8813
+ heading: workshop.length > 0 ? headings.workspaces : null,
8814
+ items: workspaces
8815
+ });
8816
+ if (workshop.length > 0) sections.push({
8817
+ id: "workshop",
8818
+ heading: headings.workshop,
8819
+ items: workshop
8820
+ });
8821
+ return sections;
8822
+ }
8823
+ function flattenSections(sections) {
8824
+ return sections.flatMap((section) => section.items);
8825
+ }
8826
+ //#endregion
8779
8827
  //#region src/app/layouts/sidebar/WorkspaceRowAction.tsx
8780
8828
  function WorkspaceRowAction({ workspace, t, onEdit }) {
8781
8829
  return /* @__PURE__ */ h(_deepseek_ai_dsh_client_ui_primitives.Button, {
@@ -8823,8 +8871,8 @@ function WorkspaceRowLabel({ workspace, current, action, onHover }) {
8823
8871
  //#endregion
8824
8872
  //#region src/app/layouts/sidebar/workspace-items.tsx
8825
8873
  function workspaceMenuItems(options) {
8826
- const { results, currentId, hoverId, t, onHover, onEdit } = options;
8827
- return results.map((workspace) => ({
8874
+ const { sections, currentId, hoverId, t, onHover, onEdit } = options;
8875
+ const rowOf = (workspace) => ({
8828
8876
  id: workspace.workspaceId,
8829
8877
  icon: /* @__PURE__ */ h(_deepseek_ai_dsh_client_ui_primitives.IconFolderOpenRegular, { size: 16 }),
8830
8878
  label: /* @__PURE__ */ h(WorkspaceRowLabel, {
@@ -8837,7 +8885,12 @@ function workspaceMenuItems(options) {
8837
8885
  onEdit: () => onEdit(workspace)
8838
8886
  }) : void 0
8839
8887
  })
8840
- }));
8888
+ });
8889
+ return sections.flatMap((section) => [...section.heading === null ? [] : [{
8890
+ type: "label",
8891
+ id: section.id,
8892
+ text: section.heading
8893
+ }], ...section.items.map(rowOf)]);
8841
8894
  }
8842
8895
  //#endregion
8843
8896
  //#region src/app/layouts/sidebar/ProjectSwitcher.tsx
@@ -8857,7 +8910,12 @@ function ProjectSwitcher({ items, current, onPick, onAdd, onRename, t }) {
8857
8910
  const wasOpenRef = useRef(false);
8858
8911
  const currentId = current?.workspaceId ?? null;
8859
8912
  const results = useMemo(() => matchWorkspaces(items, query), [items, query]);
8860
- const cursorId = cursor === null || results.length === 0 ? null : results[Math.min(cursor, results.length - 1)]?.workspaceId ?? null;
8913
+ const sections = useMemo(() => workspaceSections(results, {
8914
+ workspaces: t("project.group.workspaces"),
8915
+ workshop: t("project.group.workshop")
8916
+ }), [results, t]);
8917
+ const ordered = useMemo(() => flattenSections(sections), [sections]);
8918
+ const cursorId = cursor === null || ordered.length === 0 ? null : ordered[Math.min(cursor, ordered.length - 1)]?.workspaceId ?? null;
8861
8919
  useEffect(() => {
8862
8920
  if (open) {
8863
8921
  wasOpenRef.current = true;
@@ -8880,11 +8938,11 @@ function ProjectSwitcher({ items, current, onPick, onAdd, onRename, t }) {
8880
8938
  setCursor(null);
8881
8939
  },
8882
8940
  onCursor: (step) => {
8883
- if (results.length === 0) return;
8884
- setCursor((at) => at === null ? step === 1 ? 0 : results.length - 1 : (at + step + results.length) % results.length);
8941
+ if (ordered.length === 0) return;
8942
+ setCursor((at) => at === null ? step === 1 ? 0 : ordered.length - 1 : (at + step + ordered.length) % ordered.length);
8885
8943
  },
8886
8944
  onSubmit: () => {
8887
- const pick = results[Math.min(cursor ?? 0, results.length - 1)];
8945
+ const pick = ordered[Math.min(cursor ?? 0, ordered.length - 1)];
8888
8946
  if (pick === void 0) return;
8889
8947
  close();
8890
8948
  onPick(pick.workspaceId);
@@ -8914,7 +8972,7 @@ function ProjectSwitcher({ items, current, onPick, onAdd, onRename, t }) {
8914
8972
  id: "empty",
8915
8973
  text: t("project.search.empty", { query: query.trim() })
8916
8974
  }] : [], ...workspaceMenuItems({
8917
- results,
8975
+ sections,
8918
8976
  currentId,
8919
8977
  hoverId,
8920
8978
  t,
@@ -9635,7 +9693,7 @@ function mount(ctx) {
9635
9693
  disposeDictionary?.();
9636
9694
  };
9637
9695
  }, "dsh-workbench: dictionaries");
9638
- const workshopWorkspace = () => ctx.workspaces.list.getSnapshot().items.find((item) => item.path.endsWith("/workbench/diy"));
9696
+ const workshopWorkspace = () => ctx.workspaces.list.getSnapshot().items.find((item) => isWorkshopPath(item.path));
9639
9697
  const openDiySession = (workspaceId, fallback) => {
9640
9698
  const remoteCreate = sessionCreateOf(ctx.remote);
9641
9699
  try {
@@ -1239,6 +1239,8 @@ var en = {
1239
1239
  "project.search.label": "Search workspaces",
1240
1240
  "project.search.placeholder": "Search workspaces (folder / name / path)",
1241
1241
  "project.search.empty": "No workspace matches “{query}”.",
1242
+ "project.group.workspaces": "Workspaces",
1243
+ "project.group.workshop": "Workshop",
1242
1244
  "project.rename.row": "Rename “{name}”",
1243
1245
  "project.rename.current": "Give this workspace a display name…",
1244
1246
  "project.rename.title": "Give “{name}” a display name",
@@ -1413,6 +1415,7 @@ var en = {
1413
1415
  "panels.market.add.failureIntegrity": "The downloaded package failed its integrity check",
1414
1416
  "panels.market.add.failureTimeout": "The install timed out",
1415
1417
  "panels.market.add.failurePnpmMissing": "pnpm was not found, cannot install",
1418
+ "panels.market.add.failureInstalled": "It is already in this profile: this dsh runs with hot reload, which refuses to install over the same package (remove it first) — and removing it failed too",
1416
1419
  "panels.market.add.failureGeneric": "Something went wrong during install — see the install details",
1417
1420
  "panels.market.add.approveTitle": "Your approval is needed for install scripts",
1418
1421
  "panels.market.add.approveCaution": "Only allow packages you trust: install scripts run on this machine with your permissions.",
@@ -1584,6 +1587,8 @@ var zh = {
1584
1587
  "project.search.label": "搜索工作区",
1585
1588
  "project.search.placeholder": "搜索工作区(目录名 / 中文名 / 路径)",
1586
1589
  "project.search.empty": "没有匹配「{query}」的工作区。",
1590
+ "project.group.workspaces": "工作区",
1591
+ "project.group.workshop": "工坊",
1587
1592
  "project.rename.row": "改「{name}」的中文名",
1588
1593
  "project.rename.current": "改当前工作区的中文名…",
1589
1594
  "project.rename.title": "给「{name}」配一个中文名",
@@ -1758,6 +1763,7 @@ var zh = {
1758
1763
  "panels.market.add.failureIntegrity": "下载的安装包校验失败",
1759
1764
  "panels.market.add.failureTimeout": "安装超时",
1760
1765
  "panels.market.add.failurePnpmMissing": "没有找到 pnpm,无法安装",
1766
+ "panels.market.add.failureInstalled": "它已经在这份 profile 里了:这一版 dsh 开着热更新,同一个包不许就地覆盖装(得先卸载再装),而这次也没能把它卸下来",
1761
1767
  "panels.market.add.failureGeneric": "安装过程中出错,原因见安装详情",
1762
1768
  "panels.market.add.approveTitle": "需要你允许安装脚本",
1763
1769
  "panels.market.add.approveCaution": "只允许你信得过的包:安装脚本会以你的权限在这台机器上运行。",
@@ -2206,23 +2212,37 @@ function quoteShellArg(value) {
2206
2212
  function tailLines(text, lines = 6) {
2207
2213
  return String(text ?? "").trim().split("\n").slice(-lines).join("\n");
2208
2214
  }
2215
+ function candidateStarts(raw) {
2216
+ const starts = [];
2217
+ for (let index = 0; index < raw.length; index += 1) {
2218
+ const char = raw[index];
2219
+ if (char !== "[" && char !== "{") continue;
2220
+ if (index === 0 || raw[index - 1] === "\n") starts.push(index);
2221
+ }
2222
+ const earliest = [raw.indexOf("["), raw.indexOf("{")].filter((index) => index >= 0);
2223
+ if (earliest.length > 0) {
2224
+ const head = Math.min(...earliest);
2225
+ if (!starts.includes(head)) starts.push(head);
2226
+ }
2227
+ return starts;
2228
+ }
2209
2229
  function parseJsonLoose(text) {
2210
2230
  const raw = String(text ?? "").trim();
2211
2231
  if (!raw) return void 0;
2212
2232
  try {
2213
2233
  return JSON.parse(raw);
2214
2234
  } catch {
2215
- const starts = [raw.indexOf("["), raw.indexOf("{")].filter((index) => index >= 0);
2216
- if (starts.length === 0) return void 0;
2217
- const from = Math.min(...starts);
2218
- const close = raw[from] === "[" ? "]" : "}";
2219
- const to = raw.lastIndexOf(close);
2220
- if (to <= from) return void 0;
2221
- try {
2222
- return JSON.parse(raw.slice(from, to + 1));
2223
- } catch {
2224
- return;
2235
+ for (const from of candidateStarts(raw)) {
2236
+ const close = raw[from] === "[" ? "]" : "}";
2237
+ const to = raw.lastIndexOf(close);
2238
+ if (to <= from) continue;
2239
+ try {
2240
+ return JSON.parse(raw.slice(from, to + 1));
2241
+ } catch {
2242
+ continue;
2243
+ }
2225
2244
  }
2245
+ return;
2226
2246
  }
2227
2247
  }
2228
2248
  var MARKDOWN_IMAGE = /!\[[^\]]*\]\([^)]*\)/g;
@@ -4688,6 +4708,7 @@ var FAILURE_KEYS = {
4688
4708
  integrity: "panels.market.add.failureIntegrity",
4689
4709
  timeout: "panels.market.add.failureTimeout",
4690
4710
  "pnpm-missing": "panels.market.add.failurePnpmMissing",
4711
+ "already-installed": "panels.market.add.failureInstalled",
4691
4712
  "operation-error": "panels.market.add.failureGeneric",
4692
4713
  "incompatible-version": "panels.market.add.failureGeneric",
4693
4714
  "not-bundle": "panels.market.add.failureGeneric",
@@ -6250,8 +6271,12 @@ function registerMarketPanel(ctx, units) {
6250
6271
  //#endregion
6251
6272
  //#region src/shared/api/diy.ts
6252
6273
  var DIY_PRESET_ID = "workbench-diy";
6274
+ var WORKSHOP_RELATIVE = "workbench/diy";
6253
6275
  var WORKSHOP_TITLE = "工作台DIY";
6254
6276
  var WORKSHOP_PAST_TITLES = ["DIY 工坊", "工作台自带 · DIY 模块住这儿"];
6277
+ function isWorkshopPath(path) {
6278
+ return path.replace(/\\/g, "/").replace(/\/+$/, "").endsWith(`/${WORKSHOP_RELATIVE}`);
6279
+ }
6255
6280
  //#endregion
6256
6281
  //#region src/app/units/index.ts
6257
6282
  var CLIENT_UNITS = [];
@@ -8779,6 +8804,29 @@ function WorkspaceSearchField({ t, query, onQuery, onCursor, onSubmit }) {
8779
8804
  }));
8780
8805
  }
8781
8806
  //#endregion
8807
+ //#region src/app/layouts/sidebar/workspace-groups.ts
8808
+ function workspaceSections(items, headings) {
8809
+ const workspaces = [];
8810
+ const workshop = [];
8811
+ for (const item of items) if (isWorkshopPath(item.path)) workshop.push(item);
8812
+ else workspaces.push(item);
8813
+ const sections = [];
8814
+ if (workspaces.length > 0) sections.push({
8815
+ id: "workspaces",
8816
+ heading: workshop.length > 0 ? headings.workspaces : null,
8817
+ items: workspaces
8818
+ });
8819
+ if (workshop.length > 0) sections.push({
8820
+ id: "workshop",
8821
+ heading: headings.workshop,
8822
+ items: workshop
8823
+ });
8824
+ return sections;
8825
+ }
8826
+ function flattenSections(sections) {
8827
+ return sections.flatMap((section) => section.items);
8828
+ }
8829
+ //#endregion
8782
8830
  //#region src/app/layouts/sidebar/WorkspaceRowAction.tsx
8783
8831
  function WorkspaceRowAction({ workspace, t, onEdit }) {
8784
8832
  return /* @__PURE__ */ h(Button, {
@@ -8826,8 +8874,8 @@ function WorkspaceRowLabel({ workspace, current, action, onHover }) {
8826
8874
  //#endregion
8827
8875
  //#region src/app/layouts/sidebar/workspace-items.tsx
8828
8876
  function workspaceMenuItems(options) {
8829
- const { results, currentId, hoverId, t, onHover, onEdit } = options;
8830
- return results.map((workspace) => ({
8877
+ const { sections, currentId, hoverId, t, onHover, onEdit } = options;
8878
+ const rowOf = (workspace) => ({
8831
8879
  id: workspace.workspaceId,
8832
8880
  icon: /* @__PURE__ */ h(IconFolderOpenRegular, { size: 16 }),
8833
8881
  label: /* @__PURE__ */ h(WorkspaceRowLabel, {
@@ -8840,7 +8888,12 @@ function workspaceMenuItems(options) {
8840
8888
  onEdit: () => onEdit(workspace)
8841
8889
  }) : void 0
8842
8890
  })
8843
- }));
8891
+ });
8892
+ return sections.flatMap((section) => [...section.heading === null ? [] : [{
8893
+ type: "label",
8894
+ id: section.id,
8895
+ text: section.heading
8896
+ }], ...section.items.map(rowOf)]);
8844
8897
  }
8845
8898
  //#endregion
8846
8899
  //#region src/app/layouts/sidebar/ProjectSwitcher.tsx
@@ -8860,7 +8913,12 @@ function ProjectSwitcher({ items, current, onPick, onAdd, onRename, t }) {
8860
8913
  const wasOpenRef = useRef(false);
8861
8914
  const currentId = current?.workspaceId ?? null;
8862
8915
  const results = useMemo(() => matchWorkspaces(items, query), [items, query]);
8863
- const cursorId = cursor === null || results.length === 0 ? null : results[Math.min(cursor, results.length - 1)]?.workspaceId ?? null;
8916
+ const sections = useMemo(() => workspaceSections(results, {
8917
+ workspaces: t("project.group.workspaces"),
8918
+ workshop: t("project.group.workshop")
8919
+ }), [results, t]);
8920
+ const ordered = useMemo(() => flattenSections(sections), [sections]);
8921
+ const cursorId = cursor === null || ordered.length === 0 ? null : ordered[Math.min(cursor, ordered.length - 1)]?.workspaceId ?? null;
8864
8922
  useEffect(() => {
8865
8923
  if (open) {
8866
8924
  wasOpenRef.current = true;
@@ -8883,11 +8941,11 @@ function ProjectSwitcher({ items, current, onPick, onAdd, onRename, t }) {
8883
8941
  setCursor(null);
8884
8942
  },
8885
8943
  onCursor: (step) => {
8886
- if (results.length === 0) return;
8887
- setCursor((at) => at === null ? step === 1 ? 0 : results.length - 1 : (at + step + results.length) % results.length);
8944
+ if (ordered.length === 0) return;
8945
+ setCursor((at) => at === null ? step === 1 ? 0 : ordered.length - 1 : (at + step + ordered.length) % ordered.length);
8888
8946
  },
8889
8947
  onSubmit: () => {
8890
- const pick = results[Math.min(cursor ?? 0, results.length - 1)];
8948
+ const pick = ordered[Math.min(cursor ?? 0, ordered.length - 1)];
8891
8949
  if (pick === void 0) return;
8892
8950
  close();
8893
8951
  onPick(pick.workspaceId);
@@ -8917,7 +8975,7 @@ function ProjectSwitcher({ items, current, onPick, onAdd, onRename, t }) {
8917
8975
  id: "empty",
8918
8976
  text: t("project.search.empty", { query: query.trim() })
8919
8977
  }] : [], ...workspaceMenuItems({
8920
- results,
8978
+ sections,
8921
8979
  currentId,
8922
8980
  hoverId,
8923
8981
  t,
@@ -9638,7 +9696,7 @@ function mount(ctx) {
9638
9696
  disposeDictionary?.();
9639
9697
  };
9640
9698
  }, "dsh-workbench: dictionaries");
9641
- const workshopWorkspace = () => ctx.workspaces.list.getSnapshot().items.find((item) => item.path.endsWith("/workbench/diy"));
9699
+ const workshopWorkspace = () => ctx.workspaces.list.getSnapshot().items.find((item) => isWorkshopPath(item.path));
9642
9700
  const openDiySession = (workspaceId, fallback) => {
9643
9701
  const remoteCreate = sessionCreateOf(ctx.remote);
9644
9702
  try {