@lkzc19/dsh-workspace-virtual-groups 0.2.0 → 0.2.2

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
@@ -13,29 +13,20 @@
13
13
 
14
14
  ## 安装
15
15
 
16
- 发布到 npm 后可以一行安装(包名带 `@lkzc19` scope,不占用公共命名空间):
17
-
18
16
  ```sh
19
17
  dsh plugin --profile desktop add @lkzc19/dsh-workspace-virtual-groups
20
18
  ```
21
19
 
22
- `lib/` 是构建产物,不进仓库。从源码安装时先克隆并构建,再从本地目录安装:
20
+ 从源码安装(`lib/` 是构建产物,不入库,需先构建):
23
21
 
24
22
  ```sh
25
23
  git clone https://github.com/lkzc19/dsh-workspace-virtual-groups.git
26
24
  cd dsh-workspace-virtual-groups
27
- npm install # prepare 会自动执行 npm run build
28
-
29
- # 桌面端
25
+ npm install
30
26
  dsh plugin --profile desktop add "$PWD"
31
-
32
- # Web / CLI
33
- dsh plugin --profile web add "$PWD"
34
27
  ```
35
28
 
36
- 安装后重启桌面端或对应的 `dsh web` 进程。
37
-
38
- > 不要从 GitHub URL 直接安装(`dsh plugin add https://github.com/...`):pnpm 会拒绝为 git 依赖执行 `prepare`(`ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED`),装进来的是一份没有 `lib/` 的源码。
29
+ 安装后重启桌面端或对应的 `dsh web` 进程。git URL 直装不可用:pnpm 不对 git 依赖执行 `prepare`,拿到的源码没有 `lib/`。
39
30
 
40
31
  卸载:
41
32
 
@@ -67,12 +58,12 @@ $DSH_HOME/workspace-virtual-groups.manual.json
67
58
  ## 开发
68
59
 
69
60
  ```sh
70
- npm install # 装依赖并自动构建(prepare)
61
+ npm install # 装依赖并构建
71
62
  npm run build
72
63
  npm test
73
64
  ```
74
65
 
75
- `lib/index.js`(Host)和 `lib/client.js`(Client)是构建产物,已被 `.gitignore` 忽略,不要提交。
66
+ 产物:Host `lib/index.js`,Client `lib/client.js`。`lib/` 已 gitignore。
76
67
 
77
68
  ## 许可证
78
69
 
package/lib/client.js CHANGED
@@ -79,7 +79,7 @@ function moveAfter(list, id, afterId) {
79
79
  return rest;
80
80
  }
81
81
 
82
- // inline-css:/Users/lkzc19/Projects/dsh-plugins/dsh-workspace-virtual-groups/src/client/styles.css
82
+ // inline-css:/home/runner/work/dsh-workspace-virtual-groups/dsh-workspace-virtual-groups/src/client/styles.css
83
83
  var styles_default = "/* Virtual group labels are the ONLY new visual element. Official project and\n session rows keep their own classes, fonts, spacing, icons and actions.\n Menus and dialogs mirror DSH's own section labels, context menu and modal\n metrics instead of inventing a separate visual language. */\n\n.wvg-list {\n display: flex !important;\n flex-direction: column;\n}\n\n/* Section label: same 36px row / 14px tertiary label as the official sidebar\n section header, plus a chevron and a group action button. */\n.wvg-group {\n position: relative;\n display: flex;\n align-items: center;\n gap: 4px;\n height: 36px;\n flex: none;\n padding: 0 8px 0 12px;\n border-radius: 12px;\n color: var(--dsw-alias-label-tertiary, #81858c);\n font: inherit;\n font-size: 14px;\n font-weight: 400;\n line-height: 20px;\n cursor: pointer;\n user-select: none;\n}\n\n.wvg-group[data-wvg-default='0'] {\n margin-top: 4px;\n}\n\n.wvg-group:hover {\n background: var(--dsw-bg-hover, rgba(127, 127, 127, 0.12));\n}\n\n.wvg-group.wvg-drop {\n background: var(--dsw-bg-selected, rgba(74, 144, 217, 0.18));\n}\n\n.wvg-chevron {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 14px;\n height: 14px;\n flex: none;\n color: var(--dsw-alias-label-tertiary, #81858c);\n}\n\n.wvg-chevron svg {\n transition: transform 120ms ease;\n}\n\n.wvg-group[aria-expanded='true'] .wvg-chevron svg {\n transform: rotate(90deg);\n}\n\n.wvg-label {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Match the workspace row's hover-only 24px action button. */\n.wvg-more:not([data-wvg-native-icon]) {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n flex: none;\n margin: 0;\n padding: 0;\n border: none;\n border-radius: var(--dsw-radius-xs, 6px);\n background: transparent;\n color: var(--dsw-alias-label-tertiary, #81858c);\n cursor: pointer;\n}\n\n@media (hover: hover) {\n .wvg-more:not([data-wvg-native-icon]):hover {\n color: var(--dsw-alias-label-primary, #0f1115);\n }\n}\n\n.wvg-group .wvg-more {\n display: none;\n margin: 0;\n}\n\n@media (hover: hover) {\n .wvg-group:hover .wvg-more,\n .wvg-group.wvg-menu-open .wvg-more,\n .wvg-group:focus-within .wvg-more {\n display: inline-flex;\n }\n}\n\n@media (hover: none) {\n .wvg-group .wvg-more {\n display: inline-flex;\n }\n}\n\n.wvg-group[draggable='true'] {\n cursor: grab;\n}\n\n.wvg-group[draggable='true']:active {\n cursor: grabbing;\n}\n\n.wvg-group.wvg-dragging {\n opacity: 0.45;\n}\n\n.wvg-group.wvg-insert-before::before,\n.wvg-group.wvg-insert-after::after {\n content: '';\n position: absolute;\n left: 2px;\n right: 2px;\n height: 2px;\n border-radius: 1px;\n background: var(--dsw-accent, #4a90d9);\n pointer-events: none;\n z-index: 2;\n}\n\n.wvg-group.wvg-insert-before::before {\n top: -2px;\n}\n\n.wvg-group.wvg-insert-after::after {\n bottom: -2px;\n}\n\n/* Context menu: 16px radius surface, 4px padding, 12px rows, 13px labels. */\n.wvg-menu {\n position: fixed;\n z-index: 1100;\n min-width: 144px;\n padding: 4px;\n border: none;\n border-radius: 16px;\n background: var(--dsw-alias-bg-layer-2, var(--dsw-bg-1, #fff));\n box-shadow:\n 0 0 0 0.5px rgba(0, 0, 0, 0.04),\n 0 3px 8px 0 rgba(0, 0, 0, 0.04),\n 0 0 20px 0 rgba(0, 0, 0, 0.05);\n}\n\n.wvg-menu-item {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n min-height: 32px;\n box-sizing: border-box;\n padding: 6px 8px;\n border: none;\n border-radius: 12px;\n background: transparent;\n color: var(--dsw-alias-label-primary, var(--dsw-fg-1, inherit));\n font: inherit;\n font-size: 13px;\n line-height: 20px;\n text-align: left;\n cursor: pointer;\n}\n\n.wvg-menu-item:hover {\n background: var(--dsw-bg-hover, rgba(127, 127, 127, 0.12));\n}\n\n.wvg-menu-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 16px;\n height: 16px;\n flex: none;\n color: var(--dsw-fg-2, inherit);\n}\n\n.wvg-menu-label {\n flex: 1;\n min-width: 0;\n}\n\n.wvg-menu-item.wvg-danger {\n color: var(--dsw-alias-label-danger, #d64545);\n}\n\n.wvg-menu-item.wvg-danger .wvg-menu-icon {\n color: currentColor;\n}\n\n\n.wvg-menu-separator {\n height: 1px;\n margin: 4px 8px;\n background: var(--dsw-alias-border-l2, rgba(127, 127, 127, 0.15));\n}\n\n/* Modal: 380px / 28px radius surface and 36px buttons, matching DSH dialogs. */\n.wvg-overlay {\n position: fixed;\n inset: 0;\n z-index: 1400;\n display: flex;\n align-items: center;\n justify-content: center;\n box-sizing: border-box;\n padding: 24px;\n background: transparent;\n}\n\n.wvg-dialog {\n display: flex;\n flex-direction: column;\n gap: 20px;\n box-sizing: border-box;\n width: 380px;\n max-width: 100%;\n padding: 0 0 24px;\n overflow: hidden;\n border: none;\n border-radius: 28px;\n background: var(--dsw-alias-bg-layer-2, #2c2c2e);\n color: var(--dsw-alias-label-primary, #f9fafb);\n box-shadow:\n 0 0 0 0.5px var(--dsw-alias-border-l4, rgba(255, 255, 255, 0.2)),\n 0 3px 8px 0 rgba(0, 0, 0, 0.04),\n 0 0 20px 0 rgba(0, 0, 0, 0.05);\n font: inherit;\n}\n\n.wvg-dialog-content {\n display: flex;\n flex-direction: column;\n gap: 20px;\n}\n\n.wvg-dialog-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n box-sizing: border-box;\n min-height: 62px;\n padding: 22px 14px 12px 24px;\n}\n\n.wvg-dialog-title {\n min-width: 0;\n margin: 0;\n color: var(--dsw-alias-label-primary, #f9fafb);\n font-size: 16px;\n font-weight: 500;\n line-height: 24px;\n}\n\n.wvg-dialog-close {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n flex: none;\n box-sizing: border-box;\n padding: 1px 6px;\n border: none;\n border-radius: 8px;\n background: transparent;\n color: var(--dsw-alias-label-secondary, #cfd3d6);\n cursor: pointer;\n}\n\n.wvg-dialog-close:hover {\n background: var(--dsw-alias-interactive-bg-hover, rgba(255, 255, 255, 0.08));\n}\n\n.wvg-dialog-body {\n display: flex;\n flex-direction: column;\n padding: 0 24px;\n}\n\n.wvg-dialog-message {\n margin: 0;\n padding: 0;\n color: var(--dsw-alias-label-secondary, #cfd3d6);\n font-size: 14px;\n line-height: 22px;\n}\n\n.wvg-input {\n display: block;\n box-sizing: border-box;\n width: 100%;\n height: 44px;\n margin: 0;\n padding: 7px 14px;\n border: 1px solid var(--dsw-alias-border-l4, rgba(255, 255, 255, 0.2));\n border-radius: 16px;\n outline: none;\n background: transparent;\n color: var(--dsw-alias-label-primary, #f9fafb);\n font: inherit;\n font-size: 14px;\n line-height: 22px;\n}\n\n.wvg-error {\n min-height: 0;\n margin: 8px 0 0;\n color: var(--dsw-alias-state-error-primary, #d64545);\n font-size: 12px;\n line-height: 18px;\n}\n\n.wvg-error:empty {\n display: none;\n margin: 0;\n}\n\n.wvg-dialog-actions {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 8px;\n padding: 0 24px;\n}\n\n.wvg-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n box-sizing: border-box;\n height: 36px;\n padding: 0 14px;\n border: none;\n border-radius: 12px;\n background: transparent;\n color: var(--dsw-alias-label-primary, #f9fafb);\n font: inherit;\n font-size: 14px;\n line-height: 22px;\n cursor: pointer;\n}\n\n.wvg-button.wvg-outline {\n border: 1px solid transparent;\n box-shadow: inset 0 0 0 1px var(--dsw-alias-border-l3, rgba(255, 255, 255, 0.16));\n}\n\n.wvg-button:hover {\n background: var(--dsw-alias-interactive-bg-hover, rgba(255, 255, 255, 0.08));\n}\n\n.wvg-button.wvg-primary {\n background: var(--dsw-alias-label-primary, #f9fafb);\n color: var(--dsw-alias-label-inverse, #0f1115);\n}\n\n.wvg-button.wvg-primary:hover {\n background: var(--dsw-alias-label-primary, #f9fafb);\n opacity: 0.9;\n}\n\n.wvg-button.wvg-danger {\n background: var(--dsw-alias-state-error-primary, #d64545);\n color: #fff;\n}\n\n.wvg-button:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n.wvg-button:focus-visible,\n.wvg-dialog-close:focus-visible {\n outline: 2px solid var(--dsw-alias-state-business-primary, #7aaaff);\n outline-offset: 2px;\n}\n\n.wvg-toast {\n position: fixed;\n bottom: 24px;\n left: 50%;\n z-index: 1500;\n max-width: min(440px, calc(100vw - 32px));\n padding: 8px 12px;\n border-radius: 12px;\n background: var(--dsw-alias-bg-inverse, #222);\n color: var(--dsw-alias-label-inverse, #fff);\n font-size: 13px;\n line-height: 20px;\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.24);\n transform: translateX(-50%);\n}\n\n";
84
84
 
85
85
  // src/client/dom-groups.ts
@@ -259,17 +259,6 @@ function mountWorkspaceVirtualGroups(ctx) {
259
259
  return null;
260
260
  });
261
261
  }
262
- function restoreLegacyAddButton() {
263
- document.querySelectorAll("button[data-wvg-add-group]").forEach((button) => {
264
- delete button.dataset.wvgAddGroup;
265
- button.setAttribute("aria-label", "\u6DFB\u52A0\u5DE5\u4F5C\u533A");
266
- button.setAttribute("aria-keyshortcuts", "Alt+Meta+O");
267
- const svg = button.querySelector("svg");
268
- const use = button.querySelector("use");
269
- if (svg !== null) svg.setAttribute("data-icon", "dsw-icon-project-add-outline-regular");
270
- if (use !== null) use.setAttribute("href", "#dsw-icon-project-add-outline-regular");
271
- });
272
- }
273
262
  function officialAddButton() {
274
263
  const use = document.querySelector('use[href="#dsw-icon-project-add-outline-regular"]');
275
264
  const button = use?.closest("button");
@@ -294,7 +283,6 @@ function mountWorkspaceVirtualGroups(ctx) {
294
283
  for (const actions of [...headerActionsTracked]) restoreHeaderActions(actions);
295
284
  }
296
285
  function ensureNewGroupButton() {
297
- restoreLegacyAddButton();
298
286
  if (viewMode !== "virtual") {
299
287
  removeNewGroupButtons();
300
288
  return;
@@ -305,16 +293,9 @@ function mountWorkspaceVirtualGroups(ctx) {
305
293
  return;
306
294
  }
307
295
  let button = document.querySelector("button[data-wvg-new-group]");
308
- if (button !== null && button.dataset.wvgVersion !== "2") {
309
- const oldActions = button.parentElement;
310
- button.remove();
311
- restoreHeaderActions(oldActions);
312
- button = null;
313
- }
314
296
  if (button === null || !button.isConnected) {
315
297
  button = add.cloneNode(true);
316
298
  button.dataset.wvgNewGroup = "1";
317
- button.dataset.wvgVersion = "2";
318
299
  add.after(button);
319
300
  }
320
301
  if (button.previousElementSibling !== add) add.after(button);
@@ -1032,7 +1013,6 @@ function mountWorkspaceVirtualGroups(ctx) {
1032
1013
  closeViewOptions();
1033
1014
  closeModal();
1034
1015
  cleanupList();
1035
- restoreLegacyAddButton();
1036
1016
  removeNewGroupButtons();
1037
1017
  document.querySelectorAll("[data-wvg-view-wrap]").forEach((node) => node.remove());
1038
1018
  style.remove();
package/lib/client.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/client/index.ts", "../src/core/types.ts", "../src/core/grouping.ts", "inline-css:/Users/lkzc19/Projects/dsh-plugins/dsh-workspace-virtual-groups/src/client/styles.css", "../src/client/dom-groups.ts"],
4
- "sourcesContent": ["/**\n * dsh-workspace-virtual-groups client half.\n *\n * This is deliberately NOT a replacement for the official Workspace browser.\n * It mounts a thin grouping layer that reorders the official sidebar sections\n * and inserts group labels between them; every official row keeps its markup,\n * styles, hover actions, search and keyboard behavior.\n */\nimport type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\n// Type-only service merges: the Workspace controller and the navigation face\n// that owns the directory picker / session creation.\nimport type {} from '@deepseek-ai/dsh-api-workspace-controller/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-workspace/client'\nimport { mountWorkspaceVirtualGroups } from './dom-groups.ts'\n\n/** Required services: the Workspace controller and its navigation face. */\nexport const inject = ['workspaces', 'uiWorkspace']\n\n/** Mount the grouping layer and remove it with the client fiber. */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(\n () => mountWorkspaceVirtualGroups(ctx as unknown as Parameters<typeof mountWorkspaceVirtualGroups>[0]),\n 'dsh-workspace-virtual-groups: sidebar grouping layer',\n )\n}\n", "/**\n * Shared manual-group types for dsh-workspace-virtual-groups. Pure types (zero\n * runtime deps) so the host half (sidecar persistence) and the browser half\n * (tree derivation) compile against the same contract.\n *\n * Groups are user-created only. The built-in default group catches every\n * workspace that has no explicit assignment.\n */\n\n/** Which grouping surface the sidebar should present. */\nexport type GroupViewMode = 'virtual' | 'official'\n\n/**\n * Runtime-managed grouping overlay: groups created in the sidebar UI plus\n * per-workspace assignments and user-controlled group ordering. Owned by the\n * plugin (JSON sidecar, `$DSH_HOME/workspace-virtual-groups.manual.json`).\n */\nexport interface ManualGroups {\n /** Manually created group names, in creation order. */\n categories: string[]\n /**\n * Per-workspace group assignment, keyed by workspace id. Value is a manual\n * category name, or `null` for the built-in default group. An absent key is\n * also treated as the default group.\n */\n assignments: Record<string, string | null>\n /**\n * Display order of manual groups. The default group is pinned first and is\n * never listed. Absent = manual creation order.\n */\n categoryOrder?: string[]\n /**\n * View mode last selected in the official view-options menu. Absent is\n * treated as `virtual`.\n */\n mode?: GroupViewMode\n}\n\n/**\n * The built-in, non-deletable group that holds every workspace without an\n * explicit group assignment. The name is fixed by product requirement: it is\n * shown verbatim in every UI language and may never be renamed or deleted.\n *\n * Persistence convention: a `null` assignment means \"default group\".\n */\nexport const DEFAULT_GROUP = '\u9ED8\u8BA4'\n", "/**\n * Pure manual-group ordering/assignment helpers shared by the host half\n * (validation) and the browser half (tree derivation).\n *\n * There is no automatic classifier: a workspace is in a manual group when the\n * overlay assigns it there, otherwise it is in the built-in default group.\n */\nimport { DEFAULT_GROUP, type ManualGroups } from './types.ts'\n\n/** One rendered group entry; default is always first. */\nexport interface EffectiveCategory {\n /** Display key (what the tree renders and assignments reference). */\n key: string\n /** Origin: the built-in default group or a manual group. */\n source: 'builtin' | 'manual'\n}\n\n/**\n * Effective manual groups in display order: default first, then manual groups\n * ordered by `categoryOrder` (absent entries keep creation order).\n */\nexport function effectiveCategories(manual: ManualGroups | undefined): EffectiveCategory[] {\n const entries: EffectiveCategory[] = [{ key: DEFAULT_GROUP, source: 'builtin' }]\n const seen = new Set<string>([DEFAULT_GROUP])\n for (const name of manual?.categories ?? []) {\n if (seen.has(name)) continue\n seen.add(name)\n entries.push({ key: name, source: 'manual' })\n }\n const order = manual?.categoryOrder\n if (order === undefined || order.length === 0) return entries\n const position = new Map(order.map((key, index) => [key, index]))\n const [defaultEntry, ...rest] = entries\n rest.sort((a, b) => {\n const pa = position.get(a.key)\n const pb = position.get(b.key)\n if (pa !== undefined && pb !== undefined) return pa - pb\n if (pa !== undefined) return -1\n if (pb !== undefined) return 1\n return 0\n })\n return [defaultEntry!, ...rest]\n}\n\n/** Display keys of all effective groups (default first). */\nexport function displayCategoryKeys(manual: ManualGroups | undefined): string[] {\n return effectiveCategories(manual).map(entry => entry.key)\n}\n\n/** Resolve the group key a workspace renders under. */\nexport function resolveCategory(manual: ManualGroups | undefined, workspaceId: string): string {\n const assignment = manual?.assignments[workspaceId]\n if (assignment === undefined || assignment === null) return DEFAULT_GROUP\n return manual?.categories.includes(assignment) ? assignment : DEFAULT_GROUP\n}\n\n/** Names that may not be used for a new group. */\nexport function takenCategoryNames(manual: ManualGroups | undefined): Set<string> {\n return new Set<string>(displayCategoryKeys(manual))\n}\n\n/** Move `id` before `beforeId` (undefined = append) within an ordered list. */\nexport function moveBefore(list: readonly string[], id: string, beforeId: string | undefined): string[] {\n const rest = list.filter(x => x !== id)\n if (beforeId === undefined) return [...rest, id]\n const index = rest.indexOf(beforeId)\n if (index === -1) return [...rest, id]\n rest.splice(index, 0, id)\n return rest\n}\n\n/** Move `id` after `afterId` (undefined = append) within an ordered list. */\nexport function moveAfter(list: readonly string[], id: string, afterId: string | undefined): string[] {\n const rest = list.filter(x => x !== id)\n if (afterId === undefined) return [...rest, id]\n const index = rest.indexOf(afterId)\n if (index === -1) return [...rest, id]\n rest.splice(index + 1, 0, id)\n return rest\n}\n", "export default \"/* Virtual group labels are the ONLY new visual element. Official project and\\n session rows keep their own classes, fonts, spacing, icons and actions.\\n Menus and dialogs mirror DSH's own section labels, context menu and modal\\n metrics instead of inventing a separate visual language. */\\n\\n.wvg-list {\\n display: flex !important;\\n flex-direction: column;\\n}\\n\\n/* Section label: same 36px row / 14px tertiary label as the official sidebar\\n section header, plus a chevron and a group action button. */\\n.wvg-group {\\n position: relative;\\n display: flex;\\n align-items: center;\\n gap: 4px;\\n height: 36px;\\n flex: none;\\n padding: 0 8px 0 12px;\\n border-radius: 12px;\\n color: var(--dsw-alias-label-tertiary, #81858c);\\n font: inherit;\\n font-size: 14px;\\n font-weight: 400;\\n line-height: 20px;\\n cursor: pointer;\\n user-select: none;\\n}\\n\\n.wvg-group[data-wvg-default='0'] {\\n margin-top: 4px;\\n}\\n\\n.wvg-group:hover {\\n background: var(--dsw-bg-hover, rgba(127, 127, 127, 0.12));\\n}\\n\\n.wvg-group.wvg-drop {\\n background: var(--dsw-bg-selected, rgba(74, 144, 217, 0.18));\\n}\\n\\n.wvg-chevron {\\n display: inline-flex;\\n align-items: center;\\n justify-content: center;\\n width: 14px;\\n height: 14px;\\n flex: none;\\n color: var(--dsw-alias-label-tertiary, #81858c);\\n}\\n\\n.wvg-chevron svg {\\n transition: transform 120ms ease;\\n}\\n\\n.wvg-group[aria-expanded='true'] .wvg-chevron svg {\\n transform: rotate(90deg);\\n}\\n\\n.wvg-label {\\n flex: 1;\\n min-width: 0;\\n overflow: hidden;\\n text-overflow: ellipsis;\\n white-space: nowrap;\\n}\\n\\n/* Match the workspace row's hover-only 24px action button. */\\n.wvg-more:not([data-wvg-native-icon]) {\\n display: inline-flex;\\n align-items: center;\\n justify-content: center;\\n width: 24px;\\n height: 24px;\\n flex: none;\\n margin: 0;\\n padding: 0;\\n border: none;\\n border-radius: var(--dsw-radius-xs, 6px);\\n background: transparent;\\n color: var(--dsw-alias-label-tertiary, #81858c);\\n cursor: pointer;\\n}\\n\\n@media (hover: hover) {\\n .wvg-more:not([data-wvg-native-icon]):hover {\\n color: var(--dsw-alias-label-primary, #0f1115);\\n }\\n}\\n\\n.wvg-group .wvg-more {\\n display: none;\\n margin: 0;\\n}\\n\\n@media (hover: hover) {\\n .wvg-group:hover .wvg-more,\\n .wvg-group.wvg-menu-open .wvg-more,\\n .wvg-group:focus-within .wvg-more {\\n display: inline-flex;\\n }\\n}\\n\\n@media (hover: none) {\\n .wvg-group .wvg-more {\\n display: inline-flex;\\n }\\n}\\n\\n.wvg-group[draggable='true'] {\\n cursor: grab;\\n}\\n\\n.wvg-group[draggable='true']:active {\\n cursor: grabbing;\\n}\\n\\n.wvg-group.wvg-dragging {\\n opacity: 0.45;\\n}\\n\\n.wvg-group.wvg-insert-before::before,\\n.wvg-group.wvg-insert-after::after {\\n content: '';\\n position: absolute;\\n left: 2px;\\n right: 2px;\\n height: 2px;\\n border-radius: 1px;\\n background: var(--dsw-accent, #4a90d9);\\n pointer-events: none;\\n z-index: 2;\\n}\\n\\n.wvg-group.wvg-insert-before::before {\\n top: -2px;\\n}\\n\\n.wvg-group.wvg-insert-after::after {\\n bottom: -2px;\\n}\\n\\n/* Context menu: 16px radius surface, 4px padding, 12px rows, 13px labels. */\\n.wvg-menu {\\n position: fixed;\\n z-index: 1100;\\n min-width: 144px;\\n padding: 4px;\\n border: none;\\n border-radius: 16px;\\n background: var(--dsw-alias-bg-layer-2, var(--dsw-bg-1, #fff));\\n box-shadow:\\n 0 0 0 0.5px rgba(0, 0, 0, 0.04),\\n 0 3px 8px 0 rgba(0, 0, 0, 0.04),\\n 0 0 20px 0 rgba(0, 0, 0, 0.05);\\n}\\n\\n.wvg-menu-item {\\n display: flex;\\n align-items: center;\\n gap: 8px;\\n width: 100%;\\n min-height: 32px;\\n box-sizing: border-box;\\n padding: 6px 8px;\\n border: none;\\n border-radius: 12px;\\n background: transparent;\\n color: var(--dsw-alias-label-primary, var(--dsw-fg-1, inherit));\\n font: inherit;\\n font-size: 13px;\\n line-height: 20px;\\n text-align: left;\\n cursor: pointer;\\n}\\n\\n.wvg-menu-item:hover {\\n background: var(--dsw-bg-hover, rgba(127, 127, 127, 0.12));\\n}\\n\\n.wvg-menu-icon {\\n display: inline-flex;\\n align-items: center;\\n justify-content: center;\\n width: 16px;\\n height: 16px;\\n flex: none;\\n color: var(--dsw-fg-2, inherit);\\n}\\n\\n.wvg-menu-label {\\n flex: 1;\\n min-width: 0;\\n}\\n\\n.wvg-menu-item.wvg-danger {\\n color: var(--dsw-alias-label-danger, #d64545);\\n}\\n\\n.wvg-menu-item.wvg-danger .wvg-menu-icon {\\n color: currentColor;\\n}\\n\\n\\n.wvg-menu-separator {\\n height: 1px;\\n margin: 4px 8px;\\n background: var(--dsw-alias-border-l2, rgba(127, 127, 127, 0.15));\\n}\\n\\n/* Modal: 380px / 28px radius surface and 36px buttons, matching DSH dialogs. */\\n.wvg-overlay {\\n position: fixed;\\n inset: 0;\\n z-index: 1400;\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n box-sizing: border-box;\\n padding: 24px;\\n background: transparent;\\n}\\n\\n.wvg-dialog {\\n display: flex;\\n flex-direction: column;\\n gap: 20px;\\n box-sizing: border-box;\\n width: 380px;\\n max-width: 100%;\\n padding: 0 0 24px;\\n overflow: hidden;\\n border: none;\\n border-radius: 28px;\\n background: var(--dsw-alias-bg-layer-2, #2c2c2e);\\n color: var(--dsw-alias-label-primary, #f9fafb);\\n box-shadow:\\n 0 0 0 0.5px var(--dsw-alias-border-l4, rgba(255, 255, 255, 0.2)),\\n 0 3px 8px 0 rgba(0, 0, 0, 0.04),\\n 0 0 20px 0 rgba(0, 0, 0, 0.05);\\n font: inherit;\\n}\\n\\n.wvg-dialog-content {\\n display: flex;\\n flex-direction: column;\\n gap: 20px;\\n}\\n\\n.wvg-dialog-header {\\n display: flex;\\n align-items: center;\\n justify-content: space-between;\\n gap: 8px;\\n box-sizing: border-box;\\n min-height: 62px;\\n padding: 22px 14px 12px 24px;\\n}\\n\\n.wvg-dialog-title {\\n min-width: 0;\\n margin: 0;\\n color: var(--dsw-alias-label-primary, #f9fafb);\\n font-size: 16px;\\n font-weight: 500;\\n line-height: 24px;\\n}\\n\\n.wvg-dialog-close {\\n display: inline-flex;\\n align-items: center;\\n justify-content: center;\\n width: 28px;\\n height: 28px;\\n flex: none;\\n box-sizing: border-box;\\n padding: 1px 6px;\\n border: none;\\n border-radius: 8px;\\n background: transparent;\\n color: var(--dsw-alias-label-secondary, #cfd3d6);\\n cursor: pointer;\\n}\\n\\n.wvg-dialog-close:hover {\\n background: var(--dsw-alias-interactive-bg-hover, rgba(255, 255, 255, 0.08));\\n}\\n\\n.wvg-dialog-body {\\n display: flex;\\n flex-direction: column;\\n padding: 0 24px;\\n}\\n\\n.wvg-dialog-message {\\n margin: 0;\\n padding: 0;\\n color: var(--dsw-alias-label-secondary, #cfd3d6);\\n font-size: 14px;\\n line-height: 22px;\\n}\\n\\n.wvg-input {\\n display: block;\\n box-sizing: border-box;\\n width: 100%;\\n height: 44px;\\n margin: 0;\\n padding: 7px 14px;\\n border: 1px solid var(--dsw-alias-border-l4, rgba(255, 255, 255, 0.2));\\n border-radius: 16px;\\n outline: none;\\n background: transparent;\\n color: var(--dsw-alias-label-primary, #f9fafb);\\n font: inherit;\\n font-size: 14px;\\n line-height: 22px;\\n}\\n\\n.wvg-error {\\n min-height: 0;\\n margin: 8px 0 0;\\n color: var(--dsw-alias-state-error-primary, #d64545);\\n font-size: 12px;\\n line-height: 18px;\\n}\\n\\n.wvg-error:empty {\\n display: none;\\n margin: 0;\\n}\\n\\n.wvg-dialog-actions {\\n display: flex;\\n align-items: center;\\n justify-content: flex-end;\\n gap: 8px;\\n padding: 0 24px;\\n}\\n\\n.wvg-button {\\n display: inline-flex;\\n align-items: center;\\n justify-content: center;\\n box-sizing: border-box;\\n height: 36px;\\n padding: 0 14px;\\n border: none;\\n border-radius: 12px;\\n background: transparent;\\n color: var(--dsw-alias-label-primary, #f9fafb);\\n font: inherit;\\n font-size: 14px;\\n line-height: 22px;\\n cursor: pointer;\\n}\\n\\n.wvg-button.wvg-outline {\\n border: 1px solid transparent;\\n box-shadow: inset 0 0 0 1px var(--dsw-alias-border-l3, rgba(255, 255, 255, 0.16));\\n}\\n\\n.wvg-button:hover {\\n background: var(--dsw-alias-interactive-bg-hover, rgba(255, 255, 255, 0.08));\\n}\\n\\n.wvg-button.wvg-primary {\\n background: var(--dsw-alias-label-primary, #f9fafb);\\n color: var(--dsw-alias-label-inverse, #0f1115);\\n}\\n\\n.wvg-button.wvg-primary:hover {\\n background: var(--dsw-alias-label-primary, #f9fafb);\\n opacity: 0.9;\\n}\\n\\n.wvg-button.wvg-danger {\\n background: var(--dsw-alias-state-error-primary, #d64545);\\n color: #fff;\\n}\\n\\n.wvg-button:disabled {\\n opacity: 0.4;\\n cursor: default;\\n}\\n\\n.wvg-button:focus-visible,\\n.wvg-dialog-close:focus-visible {\\n outline: 2px solid var(--dsw-alias-state-business-primary, #7aaaff);\\n outline-offset: 2px;\\n}\\n\\n.wvg-toast {\\n position: fixed;\\n bottom: 24px;\\n left: 50%;\\n z-index: 1500;\\n max-width: min(440px, calc(100vw - 32px));\\n padding: 8px 12px;\\n border-radius: 12px;\\n background: var(--dsw-alias-bg-inverse, #222);\\n color: var(--dsw-alias-label-inverse, #fff);\\n font-size: 13px;\\n line-height: 20px;\\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.24);\\n transform: translateX(-50%);\\n}\\n\\n\";", "/**\n * Virtual workspace groups as a thin layer over the official sidebar browser.\n *\n * The official `@deepseek-ai/dsh-client-ui-workspace` browser is left\n * completely intact: its row markup, icons, hover actions, search, keyboard\n * handling and view options are the ones users already know. This module only\n * reorders the official workspace sections (via CSS `order` on a flex list)\n * and inserts group labels between them. No official row is cloned, replaced\n * or restyled.\n *\n * The grouping overlay is the same one the host half serves: manual groups and\n * per-workspace assignments are persisted in\n * `$DSH_HOME/workspace-virtual-groups.manual.json`.\n */\nimport type { ManualGroups } from '../core/types.ts'\nimport { DEFAULT_GROUP } from '../core/types.ts'\nimport {\n effectiveCategories,\n moveAfter,\n moveBefore,\n resolveCategory,\n takenCategoryNames,\n} from '../core/grouping.ts'\nimport css from './styles.css?inline'\n\nconst MIME = 'application/x-dsh-workspace-virtual-groups'\nconst GROUP_MIME = 'application/x-dsh-workspace-virtual-group'\n\n/** Layer glyph used by the new-group button and the view-options item. */\nconst LAYER_ICON = '<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1\" stroke-linejoin=\"round\" data-wvg-layer=\"1\" aria-hidden=\"true\"><path d=\"M8 1.8 14 5 8 8.2 2 5 8 1.8Z\"></path><path d=\"M2 8 8 11.2 14 8\"></path><path d=\"M2 11 8 14.2 14 11\"></path></svg>'\n/** Official grouping-mode icons; stable across UI locales. */\nconst GROUPING = {\n workspace: {\n icon: 'dsw-icon-folder-close-regular',\n labels: ['\u6309\u5DE5\u4F5C\u533A', 'By workspace'],\n },\n tree: {\n icon: 'dsw-icon-workspace-tree-outline-regular',\n labels: ['\u6309\u5DE5\u4F5C\u533A\u6811', 'By workspace tree'],\n },\n flatList: {\n icon: 'dsw-icon-flat-list-outline-regular',\n labels: ['\u5355\u5217\u8868', 'Flat list'],\n },\n} as const\nconst OFFICIAL_GROUPING_ICONS = new Set<string>(\n Object.values(GROUPING).map((entry) => entry.icon),\n)\nconst OFFICIAL_GROUPING_LABELS = new Set<string>(\n Object.values(GROUPING).flatMap((entry) => entry.labels),\n)\nconst MANUAL_ROUTE = '/workspace-virtual-groups/manual'\nconst COLLAPSED_KEY = 'dsh.workspace.virtual-groups.collapsed.v1'\n/** Indentation applied to project sections inside a virtual group (matches the official tree step). */\nconst GROUP_INDENT = '20px'\n\ntype NormalizedManual = Required<ManualGroups>\n\nconst EMPTY_MANUAL: NormalizedManual = {\n categories: [],\n assignments: {},\n categoryOrder: [],\n mode: 'virtual',\n}\n\n/** Materialize optional overlay fields into the fully-populated client shape. */\nfunction normalizeManual(manual: ManualGroups | undefined): NormalizedManual {\n if (manual === undefined) return EMPTY_MANUAL\n const categories = [...new Set(manual.categories)]\n .filter(name => name !== DEFAULT_GROUP)\n const categorySet = new Set(categories)\n const assignments = Object.fromEntries(\n Object.entries(manual.assignments).map(([id, category]): [string, string | null] => [\n id,\n category === null || !categorySet.has(category) ? null : category,\n ]),\n )\n return {\n categories,\n assignments,\n categoryOrder: (manual.categoryOrder ?? []).filter(key => categorySet.has(key)),\n mode: manual.mode === 'official' ? 'official' : 'virtual',\n }\n}\n\n/** Fetch the runtime overlay. */\nasync function loadState(): Promise<NormalizedManual> {\n const response = await fetch(MANUAL_ROUTE, { cache: 'no-cache' })\n if (!response.ok) throw new Error(`manual request failed: ${response.status}`)\n return normalizeManual((await response.json()) as ManualGroups)\n}\n\n/** Persist the whole overlay. The host validates and writes atomically. */\nasync function putManual(manual: NormalizedManual): Promise<void> {\n const response = await fetch(MANUAL_ROUTE, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(manual),\n })\n if (response.ok) return\n let message = `\u4FDD\u5B58\u5931\u8D25\uFF08${response.status}\uFF09`\n try {\n const text = await response.text()\n if (text !== '') message = text\n } catch { /* keep the fallback */ }\n throw new Error(message)\n}\n\nfunction readCollapsed(): Set<string> {\n try {\n const raw = window.localStorage.getItem(COLLAPSED_KEY)\n if (raw === null) return new Set()\n const parsed: unknown = JSON.parse(raw)\n return new Set(Array.isArray(parsed) ? parsed.filter((key): key is string => typeof key === 'string') : [])\n } catch {\n return new Set()\n }\n}\n\nfunction writeCollapsed(collapsed: ReadonlySet<string>): void {\n try {\n window.localStorage.setItem(COLLAPSED_KEY, JSON.stringify([...collapsed]))\n } catch { /* private mode / quota: expansion is only a view preference */ }\n}\n\nfunction svgIcon(id: string, size: number): string {\n return `<svg width=\"${size}\" height=\"${size}\" aria-hidden=\"true\"><use href=\"#${id}\"></use></svg>`\n}\n\n/**\n * Mount the grouping layer over the official Workspace browser.\n *\n * @param ctx - client context carrying the Workspace controller snapshot.\n * @returns the disposer that removes every DOM node, listener and style.\n */\nexport function mountWorkspaceVirtualGroups(ctx: {\n workspaces: {\n create(input: { path: string }): Promise<{ workspaceId: string }>\n }\n uiWorkspace: {\n pickDirectory(): Promise<string | null>\n startSession(workspaceId?: string): void\n }\n}): () => void {\n const style = document.createElement('style')\n style.dataset.plugin = 'dsh-workspace-virtual-groups'\n style.textContent = css\n document.head.append(style)\n\n let disposed = false\n let manual: NormalizedManual = EMPTY_MANUAL\n let loaded = false\n let list: HTMLElement | null = null\n let viewMode: 'virtual' | 'official' = 'virtual'\n let suppressViewModeClick = false\n let lastSelectedExtra: string[] = []\n let lastCheckHTML: string | null = null\n let addingWorkspace = false\n let frame = 0\n let menu: HTMLElement | null = null\n let menuAnchor: HTMLElement | null = null\n let menuPointerClose: ((event: PointerEvent) => void) | null = null\n let menuKeyClose: ((event: KeyboardEvent) => void) | null = null\n let modal: HTMLElement | null = null\n let modalKeyHandler: ((event: KeyboardEvent) => void) | null = null\n let toastTimer = 0\n let dragId: string | null = null\n let draggingGroup: string | null = null\n let saveQueue: Promise<void> = Promise.resolve()\n const groupedHeaders = new Map<string, HTMLElement>()\n const collapsed = readCollapsed()\n /** Official `--dsh-workspace-indent` value before this layer touched a section. */\n const originalIndent = new WeakMap<HTMLElement, string>()\n const viewMenuMeta = new WeakMap<HTMLButtonElement, { baseClass: string; selectedExtra: string[]; check: string | null }>()\n const headerActionsMeta = new WeakMap<HTMLElement, { width: string; minWidth: string; overflow: string }>()\n const headerActionsTracked = new Set<HTMLElement>()\n\n /** Coalesce DOM/state notifications into one animation frame. */\n function schedule(): void {\n if (disposed || frame !== 0) return\n frame = window.requestAnimationFrame(() => {\n frame = 0\n if (!disposed) reconcile()\n })\n }\n\n /** Find the official workspace list (not the search tree). */\n function findList(): HTMLElement | null {\n const row = document.querySelector<HTMLElement>('[data-row-key^=\"workspace:\"]')\n return row?.closest<HTMLElement>('[role=\"tree\"]') ?? null\n }\n\n /** Direct workspace sections of the official list. */\n function sectionsOf(target: HTMLElement): HTMLElement[] {\n return Array.from(target.children).filter((element): element is HTMLElement =>\n element instanceof HTMLElement\n && element.querySelector(':scope [data-row-key^=\"workspace:\"]') !== null,\n )\n }\n\n function sectionWorkspaceId(section: HTMLElement): string | undefined {\n const key = section.querySelector<HTMLElement>('[data-row-key^=\"workspace:\"]')?.getAttribute('data-row-key') ?? ''\n return key.startsWith('workspace:') ? key.slice('workspace:'.length) : undefined\n }\n\n /** The official list is a block container; a flex column lets `order` work. */\n function markList(target: HTMLElement): void {\n target.classList.add('wvg-list')\n target.dataset.wvgList = '1'\n }\n\n function clearSections(target: HTMLElement): void {\n for (const section of sectionsOf(target)) {\n section.style.order = ''\n section.style.display = ''\n const original = originalIndent.get(section)\n // Only undo the indent when it is still the value this layer wrote. If\n // the official tree view changed it in the meantime, leave the official\n // value in place instead of restoring the pre-grouping one.\n if (original !== undefined && section.style.getPropertyValue('--dsh-workspace-indent') === GROUP_INDENT) {\n if (original === '') section.style.removeProperty('--dsh-workspace-indent')\n else section.style.setProperty('--dsh-workspace-indent', original)\n }\n originalIndent.delete(section)\n delete section.dataset.wvgIndent\n }\n }\n\n function clearHeaders(): void {\n for (const header of groupedHeaders.values()) header.remove()\n groupedHeaders.clear()\n }\n\n function cleanupList(): void {\n if (list === null) return\n list.classList.remove('wvg-list')\n delete list.dataset.wvgList\n clearSections(list)\n clearHeaders()\n }\n\n /** Open the create-group dialog. Shared by the repurposed header button. */\n function openCreateGroupDialog(): void {\n openNameDialog('\u65B0\u5EFA\u5206\u7EC4', '', '\u521B\u5EFA', async (name) => {\n const taken = takenCategoryNames(manual)\n if (name === '' || name === DEFAULT_GROUP) return '\u8BE5\u540D\u79F0\u4E0D\u53EF\u7528'\n if (taken.has(name)) return '\u5DF2\u5B58\u5728\u540C\u540D\u5206\u7EC4'\n await persist({ ...manual, categories: [...manual.categories, name] })\n return null\n })\n }\n\n /** Undo the old 0.1.x repurposing of the official add-workspace button. */\n function restoreLegacyAddButton(): void {\n document.querySelectorAll<HTMLButtonElement>('button[data-wvg-add-group]').forEach((button) => {\n delete button.dataset.wvgAddGroup\n button.setAttribute('aria-label', '\u6DFB\u52A0\u5DE5\u4F5C\u533A')\n button.setAttribute('aria-keyshortcuts', 'Alt+Meta+O')\n const svg = button.querySelector('svg')\n const use = button.querySelector('use')\n if (svg !== null) svg.setAttribute('data-icon', 'dsw-icon-project-add-outline-regular')\n if (use !== null) use.setAttribute('href', '#dsw-icon-project-add-outline-regular')\n })\n }\n\n function officialAddButton(): HTMLButtonElement | null {\n const use = document.querySelector<SVGUseElement>('use[href=\"#dsw-icon-project-add-outline-regular\"]')\n const button = use?.closest('button')\n return button instanceof HTMLButtonElement ? button : null\n }\n\n /** Restore the official header-actions width after the extra button goes away. */\n function restoreHeaderActions(actions: HTMLElement | null): void {\n if (actions === null) return\n const original = headerActionsMeta.get(actions)\n if (original === undefined) return\n actions.style.width = original.width\n actions.style.minWidth = original.minWidth\n actions.style.overflow = original.overflow\n headerActionsMeta.delete(actions)\n headerActionsTracked.delete(actions)\n }\n\n function removeNewGroupButtons(): void {\n document.querySelectorAll<HTMLElement>('[data-wvg-new-group]').forEach((node) => {\n const actions = node.parentElement\n node.remove()\n restoreHeaderActions(actions)\n })\n for (const actions of [...headerActionsTracked]) restoreHeaderActions(actions)\n }\n\n /** The layer-icon button next to the official add-workspace button. */\n function ensureNewGroupButton(): void {\n restoreLegacyAddButton()\n if (viewMode !== 'virtual') {\n removeNewGroupButtons()\n return\n }\n const add = officialAddButton()\n if (add === null) {\n removeNewGroupButtons()\n return\n }\n let button = document.querySelector<HTMLButtonElement>('button[data-wvg-new-group]')\n if (button !== null && button.dataset.wvgVersion !== '2') {\n const oldActions = button.parentElement\n button.remove()\n restoreHeaderActions(oldActions)\n button = null\n }\n if (button === null || !button.isConnected) {\n button = add.cloneNode(true) as HTMLButtonElement\n button.dataset.wvgNewGroup = '1'\n button.dataset.wvgVersion = '2'\n add.after(button)\n }\n if (button.previousElementSibling !== add) add.after(button)\n button.setAttribute('aria-label', '\u65B0\u5EFA\u5206\u7EC4')\n button.removeAttribute('aria-keyshortcuts')\n button.removeAttribute('aria-expanded')\n if (button.querySelector('svg[data-wvg-layer=\"1\"]') === null) button.innerHTML = LAYER_ICON\n button.onclick = (event) => {\n event.preventDefault()\n event.stopPropagation()\n openCreateGroupDialog()\n }\n // The official actions container is sized for two 28px buttons and clips\n // overflow. Widen it to fit the layer button; restore it when the button is\n // removed or the plugin unmounts.\n const actions = button.parentElement\n if (actions !== null) {\n if (!headerActionsMeta.has(actions)) {\n headerActionsMeta.set(actions, {\n width: actions.style.width,\n minWidth: actions.style.minWidth,\n overflow: actions.style.overflow,\n })\n headerActionsTracked.add(actions)\n }\n actions.style.width = 'max-content'\n actions.style.minWidth = 'max-content'\n actions.style.overflow = 'visible'\n }\n }\n\n function groupingIcon(button: HTMLButtonElement): string | null {\n const svg = button.querySelector<SVGElement>(':scope > span svg[data-icon]')\n return svg?.getAttribute('data-icon') ?? null\n }\n\n function groupingLabel(button: HTMLButtonElement): string {\n return (button.textContent ?? '').trim()\n }\n\n function isOfficialGroupingButton(button: HTMLButtonElement): boolean {\n const icon = groupingIcon(button)\n if (icon !== null && OFFICIAL_GROUPING_ICONS.has(icon)) return true\n // Fallback for a DSH build that changes the icon names.\n return OFFICIAL_GROUPING_LABELS.has(groupingLabel(button))\n }\n\n function findGroupingButton(\n menu: HTMLElement,\n group: (typeof GROUPING)[keyof typeof GROUPING],\n ): HTMLButtonElement | null {\n const buttons = [...menu.querySelectorAll<HTMLButtonElement>('button[role=\"menuitem\"]')]\n const labels: readonly string[] = group.labels\n return buttons.find((button) => groupingIcon(button) === group.icon)\n ?? buttons.find((button) => labels.includes(groupingLabel(button)))\n ?? null\n }\n\n function virtualGroupingLabel(menu: HTMLElement): string {\n const workspace = findGroupingButton(menu, GROUPING.workspace)\n const label = workspace === null ? '' : groupingLabel(workspace)\n return label.includes('\u6309') || label.includes('\u5DE5\u4F5C\u533A') ? '\u6309\u865A\u62DF\u5206\u7EC4' : 'Virtual groups'\n }\n\n function findViewOptionsMenu(): HTMLElement | null {\n for (const menu of document.querySelectorAll<HTMLElement>('[role=\"menu\"]')) {\n // The CSS-module local name survives hashing and is language-independent.\n if (menu.className.includes('viewOptionsMenu')) return menu\n // Fallback: a menu that contains an official grouping mode.\n if ([...menu.querySelectorAll<HTMLButtonElement>('button[role=\"menuitem\"]')].some(isOfficialGroupingButton)) {\n return menu\n }\n }\n return null\n }\n\n function closeViewOptions(): void {\n if (findViewOptionsMenu() === null) return\n const use = document.querySelector<SVGUseElement>('use[href=\"#dsw-icon-sliders-two-outline-regular\"]')\n const trigger = use?.closest('button')\n if (trigger instanceof HTMLButtonElement) trigger.click()\n else document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }))\n }\n\n /** Add/refresh the \"\u6309\u865A\u62DF\u5206\u7EC4\" item inside the official view-options menu. */\n function ensureViewOptions(): void {\n const menu = findViewOptionsMenu()\n if (menu === null) return\n const isOfficial = isOfficialGroupingButton\n let ours = menu.querySelector<HTMLButtonElement>('button[data-wvg-view-virtual]')\n if (ours === null) {\n const tree = findGroupingButton(menu, GROUPING.tree)\n const wrap = tree?.parentElement\n if (tree === null || wrap === undefined || wrap === null) return\n const official = [...menu.querySelectorAll<HTMLButtonElement>('button[role=\"menuitem\"]')].filter(isOfficial)\n const unselected = official.find((button) => button.querySelector(':scope > svg') === null) ?? tree\n const selected = official.find((button) => button.querySelector(':scope > svg') !== null)\n const baseClass = unselected.className\n const selectedExtra = selected === undefined\n ? []\n : [...selected.classList].filter((name) => !unselected.classList.contains(name))\n const checkHTML = selected?.querySelector(':scope > svg')?.outerHTML ?? null\n if (selectedExtra.length > 0) lastSelectedExtra = selectedExtra\n if (checkHTML !== null) lastCheckHTML = checkHTML\n const clone = wrap.cloneNode(true) as HTMLElement\n clone.dataset.wvgViewWrap = '1'\n const button = clone.querySelector<HTMLButtonElement>('button[role=\"menuitem\"]')\n if (button === null) return\n button.dataset.wvgViewVirtual = '1'\n button.className = baseClass\n const label = [...button.querySelectorAll(':scope > span')]\n .find((span) => span.querySelector('svg') === null && (span.textContent ?? '').trim() !== '')\n if (label !== undefined) label.textContent = virtualGroupingLabel(menu)\n const icon = button.querySelector<HTMLElement>('[class*=\"itemIcon\"]')\n ?? [...button.querySelectorAll('span')].find((span) => span.querySelector('svg') !== null)\n if (icon !== undefined) icon.innerHTML = LAYER_ICON\n button.querySelector(':scope > svg')?.remove()\n wrap.after(clone)\n viewMenuMeta.set(button, {\n baseClass,\n selectedExtra: selectedExtra.length > 0 ? selectedExtra : lastSelectedExtra,\n check: checkHTML ?? lastCheckHTML,\n })\n button.addEventListener('click', (event) => {\n event.preventDefault()\n event.stopPropagation()\n activateVirtualMode(menu)\n })\n ours = button\n }\n\n if (menu.dataset.wvgModeListener !== '1') {\n menu.dataset.wvgModeListener = '1'\n menu.addEventListener('click', (event) => {\n if (suppressViewModeClick) return\n const button = (event.target as Element | null)?.closest<HTMLButtonElement>('button[role=\"menuitem\"]')\n if (button === null || button === undefined) return\n if (!isOfficial(button)) return\n setViewMode('official')\n }, true)\n }\n\n const meta = viewMenuMeta.get(ours)\n ?? { baseClass: ours.className, selectedExtra: lastSelectedExtra, check: lastCheckHTML }\n if (viewMode === 'virtual') {\n for (const button of menu.querySelectorAll<HTMLButtonElement>('button[role=\"menuitem\"]')) {\n if (button === ours || !isOfficial(button)) continue\n if (meta.selectedExtra.length > 0) button.classList.remove(...meta.selectedExtra)\n button.querySelector(':scope > svg')?.remove()\n }\n ours.className = meta.selectedExtra.length > 0\n ? `${meta.baseClass} ${meta.selectedExtra.join(' ')}`\n : meta.baseClass\n if (meta.check !== null && ours.querySelector(':scope > svg') === null) {\n const holder = document.createElement('template')\n holder.innerHTML = meta.check\n const check = holder.content.firstElementChild\n if (check !== null) ours.append(check)\n }\n } else {\n ours.className = meta.baseClass\n ours.querySelector(':scope > svg')?.remove()\n }\n }\n\n function setViewMode(mode: 'virtual' | 'official'): void {\n if (!loaded) return\n if (viewMode === mode) {\n schedule()\n return\n }\n viewMode = mode\n manual = { ...manual, mode }\n schedule()\n void persist(manual).catch((reason) => {\n showToast(reason instanceof Error ? reason.message : String(reason))\n })\n }\n\n function activateVirtualMode(menu: HTMLElement): void {\n setViewMode('virtual')\n const byWorkspace = findGroupingButton(menu, GROUPING.workspace)\n if (byWorkspace === null) {\n closeViewOptions()\n return\n }\n // Selecting virtual grouping also normalizes the official mode to the flat\n // \"\u6309\u5DE5\u4F5C\u533A\" list, otherwise the official tree nesting would fight the\n // virtual sections. Suppress our own official-item listener for this click.\n suppressViewModeClick = true\n byWorkspace.click()\n window.setTimeout(() => {\n suppressViewModeClick = false\n if (menu.isConnected) closeViewOptions()\n }, 0)\n }\n\n function createGroupHeader(key: string): HTMLElement {\n const header = document.createElement('div')\n header.className = 'wvg-group'\n header.dataset.wvgGroup = key\n header.dataset.wvgDefault = key === DEFAULT_GROUP ? '1' : '0'\n header.setAttribute('role', 'treeitem')\n header.setAttribute('aria-expanded', 'true')\n header.setAttribute('aria-label', key)\n header.draggable = key !== DEFAULT_GROUP\n header.innerHTML = `<span class=\"wvg-chevron\">${svgIcon('dsw-icon-triangle-right-fill-regular', 14)}</span><span class=\"wvg-label\"></span><button type=\"button\" class=\"wvg-more\" aria-label=\"\u5206\u7EC4\u64CD\u4F5C\" aria-haspopup=\"menu\">${svgIcon('dsw-icon-ellipsis-outline-regular', 16)}</button>`\n // Group names are user input; never interpolate them into innerHTML.\n header.querySelector<HTMLElement>('.wvg-more')?.setAttribute('aria-label', `\u5206\u7EC4\u201C${key}\u201D\u7684\u64CD\u4F5C`)\n header.addEventListener('dragstart', (event) => {\n if (key === DEFAULT_GROUP) {\n event.preventDefault()\n return\n }\n if ((event.target as HTMLElement | null)?.closest('.wvg-more') !== null) {\n event.preventDefault()\n return\n }\n draggingGroup = key\n try {\n event.dataTransfer?.setData(GROUP_MIME, key)\n if (event.dataTransfer !== null) event.dataTransfer.effectAllowed = 'move'\n } catch { /* synthetic drag */ }\n header.classList.add('wvg-dragging')\n })\n header.addEventListener('dragend', () => { clearDrag() })\n header.addEventListener('click', (event) => {\n if ((event.target as HTMLElement | null)?.closest('.wvg-more') !== null) return\n toggleGroup(key)\n })\n header.addEventListener('contextmenu', (event) => {\n event.preventDefault()\n openGroupMenu(key, header, event.clientX, event.clientY)\n })\n header.addEventListener('dragover', (event) => onGroupDragOver(event, header))\n header.addEventListener('dragleave', (event) => onGroupDragLeave(event, header))\n header.addEventListener('drop', (event) => onGroupDrop(event, key, header))\n header.querySelector<HTMLElement>('.wvg-more')?.addEventListener('click', (event) => {\n event.stopPropagation()\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect()\n openGroupMenu(key, event.currentTarget as HTMLElement, rect.left, rect.bottom + 4)\n })\n return header\n }\n\n /** Give group action buttons the exact native icon-button class. */\n function applyNativeIconClasses(): void {\n const rowAction = document.querySelector<HTMLButtonElement>('[data-row-key^=\"workspace:\"] [data-context-menu-trigger]')\n const nativeClass = rowAction?.className ?? officialAddButton()?.className ?? ''\n if (nativeClass === '') return\n document.querySelectorAll<HTMLButtonElement>('.wvg-more').forEach((button) => {\n const previous = button.dataset.wvgNativeClass ?? ''\n if (previous === nativeClass) return\n for (const name of previous.split(/\\s+/).filter(Boolean)) button.classList.remove(name)\n button.classList.add(...nativeClass.split(/\\s+/).filter(Boolean))\n button.dataset.wvgNativeClass = nativeClass\n button.dataset.wvgNativeIcon = '1'\n })\n }\n\n /** Apply grouping: headers, order, collapse and the new-group button. */\n function reconcile(): void {\n if (disposed) return\n // Do not render or allow edits before the overlay has loaded: persisting an\n // empty placeholder would wipe existing groups if the manual request failed.\n if (!loaded) {\n cleanupList()\n removeNewGroupButtons()\n return\n }\n ensureViewOptions()\n ensureNewGroupButton()\n if (viewMode !== 'virtual') {\n cleanupList()\n return\n }\n const nextList = findList()\n if (nextList !== list) {\n cleanupList()\n list = nextList\n }\n if (list === null) return\n const sections = sectionsOf(list)\n if (sections.length === 0) {\n cleanupList()\n return\n }\n // The official \"workspace tree\" mode nests projects by path. Flattening it\n // into virtual groups would destroy that hierarchy, so leave it untouched.\n // Capture the official indent before overwriting it; sections can be reused\n // across renders, so a WeakMap keeps the original per element.\n for (const section of sections) {\n if (!originalIndent.has(section)) originalIndent.set(section, section.style.getPropertyValue('--dsh-workspace-indent'))\n }\n const inOfficialTree = sections.some((section) => {\n const original = originalIndent.get(section) ?? ''\n if (Number.parseFloat(original) > 0) return true\n // A reused node whose inline indent no longer matches ours was changed by\n // the official tree view between reconciles.\n return section.dataset.wvgIndent === '1'\n && section.style.getPropertyValue('--dsh-workspace-indent') !== GROUP_INDENT\n })\n if (inOfficialTree) {\n cleanupList()\n return\n }\n\n markList(list)\n\n const members = new Map<string, HTMLElement[]>()\n for (const section of sections) {\n const workspaceId = sectionWorkspaceId(section)\n const key = workspaceId === undefined ? DEFAULT_GROUP : resolveCategory(manual, workspaceId)\n const bucket = members.get(key)\n if (bucket === undefined) members.set(key, [section])\n else bucket.push(section)\n }\n\n const groups = effectiveCategories(manual)\n\n const visible = new Set<string>()\n groups.forEach((entry, groupIndex) => {\n visible.add(entry.key)\n let header = groupedHeaders.get(entry.key)\n if (header === undefined || !header.isConnected) {\n header = createGroupHeader(entry.key)\n groupedHeaders.set(entry.key, header)\n list!.append(header)\n }\n header.style.order = String(groupIndex * 100000 - 1000)\n header.setAttribute('aria-expanded', collapsed.has(entry.key) ? 'false' : 'true')\n const label = header.querySelector<HTMLElement>('.wvg-label')\n if (label !== null) label.textContent = entry.key\n const items = members.get(entry.key) ?? []\n items.forEach((section, index) => {\n section.style.order = String(groupIndex * 100000 + index)\n section.style.display = collapsed.has(entry.key) ? 'none' : ''\n section.style.setProperty('--dsh-workspace-indent', GROUP_INDENT)\n section.dataset.wvgIndent = '1'\n })\n })\n for (const [key, header] of groupedHeaders) {\n if (visible.has(key)) continue\n header.remove()\n groupedHeaders.delete(key)\n }\n applyNativeIconClasses()\n }\n\n function toggleGroup(key: string): void {\n if (collapsed.has(key)) collapsed.delete(key)\n else collapsed.add(key)\n writeCollapsed(collapsed)\n schedule()\n }\n\n function onDragStart(event: DragEvent): void {\n const row = (event.target as HTMLElement | null)?.closest<HTMLElement>('[data-row-key^=\"workspace:\"]')\n if (row === null) return\n const key = row.getAttribute('data-row-key') ?? ''\n if (!key.startsWith('workspace:')) return\n dragId = key.slice('workspace:'.length)\n try {\n event.dataTransfer?.setData(MIME, dragId)\n if (event.dataTransfer !== null) event.dataTransfer.effectAllowed = 'move'\n } catch { /* some synthetic drags have no writable dataTransfer */ }\n }\n\n function clearDrag(): void {\n dragId = null\n draggingGroup = null\n document.querySelectorAll<HTMLElement>('.wvg-group.wvg-dragging, .wvg-group.wvg-drop, .wvg-group.wvg-insert-before, .wvg-group.wvg-insert-after')\n .forEach((header) => header.classList.remove('wvg-dragging', 'wvg-drop', 'wvg-insert-before', 'wvg-insert-after'))\n }\n\n function onGroupDragOver(event: DragEvent, header: HTMLElement): void {\n if (draggingGroup !== null) {\n const key = header.dataset.wvgGroup ?? ''\n if (key === DEFAULT_GROUP || key === draggingGroup) return\n event.preventDefault()\n event.stopPropagation()\n if (event.dataTransfer !== null) event.dataTransfer.dropEffect = 'move'\n const rect = header.getBoundingClientRect()\n const before = event.clientY < rect.top + rect.height / 2\n document.querySelectorAll<HTMLElement>('.wvg-group.wvg-insert-before, .wvg-group.wvg-insert-after')\n .forEach((node) => node.classList.remove('wvg-insert-before', 'wvg-insert-after'))\n header.classList.add(before ? 'wvg-insert-before' : 'wvg-insert-after')\n return\n }\n if (dragId === null) return\n event.preventDefault()\n event.stopPropagation()\n if (event.dataTransfer !== null) event.dataTransfer.dropEffect = 'move'\n header.classList.add('wvg-drop')\n }\n\n function onGroupDragLeave(event: DragEvent, header: HTMLElement): void {\n if (event.relatedTarget instanceof Node && header.contains(event.relatedTarget)) return\n header.classList.remove('wvg-drop', 'wvg-insert-before', 'wvg-insert-after')\n }\n\n function onGroupDrop(event: DragEvent, key: string, header: HTMLElement): void {\n if (draggingGroup !== null) {\n if (key === DEFAULT_GROUP || key === draggingGroup) return\n event.preventDefault()\n event.stopPropagation()\n const rect = header.getBoundingClientRect()\n const before = event.clientY < rect.top + rect.height / 2\n const dragged = draggingGroup\n clearDrag()\n void reorderGroup(dragged, key, before)\n return\n }\n if (dragId === null) return\n event.preventDefault()\n event.stopPropagation()\n const workspaceId = dragId\n clearDrag()\n header.classList.remove('wvg-drop')\n void moveToGroup(workspaceId, key)\n }\n\n function onDocumentDragStart(event: DragEvent): void { onDragStart(event) }\n function onDocumentDragEnd(): void { clearDrag() }\n\n async function addWorkspaceToGroup(key: string): Promise<void> {\n if (addingWorkspace) return\n addingWorkspace = true\n try {\n const path = await ctx.uiWorkspace.pickDirectory()\n if (path === null) return\n const workspace = await ctx.workspaces.create({ path })\n collapsed.delete(key)\n writeCollapsed(collapsed)\n await persist({\n ...manual,\n assignments: { ...manual.assignments, [workspace.workspaceId]: key === DEFAULT_GROUP ? null : key },\n })\n try {\n ctx.uiWorkspace.startSession(workspace.workspaceId)\n } catch (reason) {\n showToast(reason instanceof Error ? reason.message : String(reason))\n }\n } catch (reason) {\n showToast(reason instanceof Error ? reason.message : String(reason))\n } finally {\n addingWorkspace = false\n }\n }\n\n async function reorderGroup(draggedKey: string, targetKey: string, before: boolean): Promise<void> {\n if (draggedKey === targetKey || targetKey === DEFAULT_GROUP) return\n const order = effectiveCategories(manual)\n .map(entry => entry.key)\n .filter(key => key !== DEFAULT_GROUP)\n const categoryOrder = before\n ? moveBefore(order, draggedKey, targetKey)\n : moveAfter(order, draggedKey, targetKey)\n try {\n await persist({ ...manual, categoryOrder })\n } catch (reason) {\n showToast(reason instanceof Error ? reason.message : String(reason))\n }\n }\n\n async function moveToGroup(workspaceId: string, key: string): Promise<void> {\n const current = resolveCategory(manual, workspaceId)\n if (current === key) return\n collapsed.delete(key)\n writeCollapsed(collapsed)\n try {\n await persist({\n ...manual,\n assignments: { ...manual.assignments, [workspaceId]: key === DEFAULT_GROUP ? null : key },\n })\n } catch (reason) {\n showToast(reason instanceof Error ? reason.message : String(reason))\n }\n }\n\n /** Serialize overlay writes so two fast drags cannot race each other. */\n function persist(next: NormalizedManual): Promise<void> {\n const run = saveQueue.then(async () => {\n await putManual(next)\n manual = next\n schedule()\n })\n // Keep the queue alive after a rejection; the caller still sees the error.\n saveQueue = run.catch(() => {})\n return run\n }\n\n function requestDeleteGroup(key: string): void {\n openConfirmDialog('\u5220\u9664\u5206\u7EC4', `\u5220\u9664\u540E\uFF0C\u8BE5\u5206\u7EC4\u5185\u7684\u9879\u76EE\u4F1A\u79FB\u5230\u300C${DEFAULT_GROUP}\u300D\u3002`, async () => {\n await deleteGroup(key)\n })\n }\n\n /** Menu row matching DSH's context-menu item (16px icon + 13px label). */\n function menuItem(label: string, icon: string, danger = false): HTMLButtonElement {\n const button = document.createElement('button')\n button.type = 'button'\n button.className = danger ? 'wvg-menu-item wvg-danger' : 'wvg-menu-item'\n button.setAttribute('role', 'menuitem')\n button.innerHTML = `<span class=\"wvg-menu-icon\">${svgIcon(icon, 16)}</span><span class=\"wvg-menu-label\"></span>`\n const text = button.querySelector<HTMLElement>('.wvg-menu-label')\n if (text !== null) text.textContent = label\n return button\n }\n\n function openGroupMenu(key: string, anchor: HTMLElement, x: number, y: number): void {\n closeMenu()\n menuAnchor = anchor\n anchor.closest<HTMLElement>('.wvg-group')?.classList.add('wvg-menu-open')\n const panel = document.createElement('div')\n panel.className = 'wvg-menu'\n panel.setAttribute('role', 'menu')\n panel.setAttribute('aria-label', `\u5206\u7EC4\u201C${key}\u201D\u7684\u64CD\u4F5C`)\n const addWorkspace = menuItem('\u6DFB\u52A0\u5DE5\u4F5C\u533A', 'dsw-icon-project-add-outline-regular')\n addWorkspace.addEventListener('click', () => {\n closeMenu()\n void addWorkspaceToGroup(key)\n })\n panel.append(addWorkspace)\n if (key !== DEFAULT_GROUP) {\n const rename = menuItem('\u91CD\u547D\u540D\u5206\u7EC4', 'dsw-icon-edit-outline-regular')\n rename.addEventListener('click', () => {\n closeMenu()\n openNameDialog('\u91CD\u547D\u540D\u5206\u7EC4', key, '\u91CD\u547D\u540D', async (name) => {\n if (name === '' || name === DEFAULT_GROUP) return '\u8BE5\u540D\u79F0\u4E0D\u53EF\u7528'\n if (name === key) return null\n if (takenCategoryNames(manual).has(name)) return '\u5DF2\u5B58\u5728\u540C\u540D\u5206\u7EC4'\n await renameGroup(key, name)\n return null\n })\n })\n const separator = document.createElement('div')\n separator.className = 'wvg-menu-separator'\n separator.setAttribute('role', 'separator')\n const remove = menuItem('\u5220\u9664\u5206\u7EC4', 'dsw-icon-trash-outline-regular', true)\n remove.addEventListener('click', () => {\n closeMenu()\n requestDeleteGroup(key)\n })\n panel.append(rename, separator, remove)\n }\n document.body.append(panel)\n const rect = panel.getBoundingClientRect()\n panel.style.left = `${Math.max(8, Math.min(x, window.innerWidth - rect.width - 8))}px`\n panel.style.top = `${Math.max(8, Math.min(y, window.innerHeight - rect.height - 8))}px`\n menu = panel\n const close = (event: PointerEvent): void => {\n if (panel.contains(event.target as Node)) return\n closeMenu()\n }\n menuPointerClose = close\n window.setTimeout(() => {\n if (menuPointerClose === close) document.addEventListener('pointerdown', close, true)\n }, 0)\n menuKeyClose = (event: KeyboardEvent): void => {\n if (event.key === 'Escape') closeMenu()\n }\n document.addEventListener('keydown', menuKeyClose, true)\n panel.querySelector<HTMLButtonElement>('.wvg-menu-item')?.focus()\n }\n\n function closeMenu(): void {\n const openHeaders = [...document.querySelectorAll<HTMLElement>('.wvg-group.wvg-menu-open')]\n if (menuPointerClose !== null) document.removeEventListener('pointerdown', menuPointerClose, true)\n if (menuKeyClose !== null) document.removeEventListener('keydown', menuKeyClose, true)\n menuPointerClose = null\n menuKeyClose = null\n const anchor = menuAnchor\n menuAnchor = null\n menu?.remove()\n menu = null\n // Keep the hover-only trigger visible while returning focus to it; once\n // focus lands, :focus-within keeps it visible after the class is removed.\n if (anchor?.isConnected) anchor.focus()\n openHeaders.forEach((header) => header.classList.remove('wvg-menu-open'))\n }\n\n async function renameGroup(from: string, name: string): Promise<void> {\n const assignments = Object.fromEntries(\n Object.entries(manual.assignments).map(([id, category]): [string, string | null] => [\n id,\n category === from ? name : category,\n ]),\n )\n const categoryOrder = manual.categoryOrder.map(key => key === from ? name : key)\n await persist({\n ...manual,\n categories: manual.categories.map(key => key === from ? name : key),\n assignments,\n categoryOrder,\n })\n }\n\n async function deleteGroup(key: string): Promise<void> {\n if (key === DEFAULT_GROUP) return\n const assignments = { ...manual.assignments }\n for (const [workspaceId, category] of Object.entries(assignments)) {\n if (category === key) assignments[workspaceId] = null\n }\n await persist({\n ...manual,\n categories: manual.categories.filter(entry => entry !== key),\n assignments,\n categoryOrder: manual.categoryOrder.filter(entry => entry !== key),\n })\n }\n\n function openNameDialog(title: string, initial: string, confirmLabel: string, submit: (name: string) => Promise<string | null>): void {\n closeModal()\n const overlay = document.createElement('div')\n overlay.className = 'wvg-overlay'\n overlay.innerHTML = `<div class=\"wvg-dialog\" role=\"dialog\" aria-modal=\"true\" tabindex=\"-1\"><div class=\"wvg-dialog-content\"><div class=\"wvg-dialog-header\"><h2 class=\"wvg-dialog-title\"></h2><button type=\"button\" class=\"wvg-dialog-close\" aria-label=\"\u5173\u95ED\">${svgIcon('dsw-icon-close-outline-regular', 14)}</button></div><div class=\"wvg-dialog-body\"><input class=\"wvg-input\" type=\"text\" maxlength=\"80\" aria-label=\"\u5206\u7EC4\u540D\u79F0\" /><div class=\"wvg-error\" role=\"alert\"></div></div></div><div class=\"wvg-dialog-actions\"><button type=\"button\" class=\"wvg-button wvg-outline\" data-wvg-cancel>\u53D6\u6D88</button><button type=\"button\" class=\"wvg-button wvg-primary\" data-wvg-ok></button></div></div>`\n const dialog = overlay.querySelector<HTMLElement>('.wvg-dialog')!\n dialog.setAttribute('aria-label', title)\n dialog.querySelector<HTMLElement>('.wvg-dialog-title')!.textContent = title\n const input = dialog.querySelector<HTMLInputElement>('.wvg-input')!\n input.value = initial\n const error = dialog.querySelector<HTMLElement>('.wvg-error')!\n const ok = dialog.querySelector<HTMLButtonElement>('[data-wvg-ok]')!\n ok.textContent = confirmLabel\n let busy = false\n const initialTrimmed = initial.trim()\n const syncOk = (): void => {\n if (busy) return\n const value = input.value.trim()\n ok.disabled = value === '' || (initialTrimmed !== '' && value === initialTrimmed)\n }\n syncOk()\n const close = (): void => closeModal()\n const confirm = async (): Promise<void> => {\n if (busy || ok.disabled) return\n busy = true\n ok.disabled = true\n error.textContent = ''\n try {\n const message = await submit(input.value.trim())\n if (message !== null) {\n error.textContent = message\n return\n }\n close()\n } catch (reason) {\n error.textContent = reason instanceof Error ? reason.message : String(reason)\n } finally {\n busy = false\n syncOk()\n }\n }\n dialog.querySelector<HTMLElement>('[data-wvg-cancel]')!.addEventListener('click', close)\n dialog.querySelector<HTMLElement>('.wvg-dialog-close')!.addEventListener('click', close)\n input.addEventListener('input', syncOk)\n ok.addEventListener('click', () => { void confirm() })\n input.addEventListener('keydown', (event) => {\n if (event.key === 'Enter' && !event.isComposing) {\n event.preventDefault()\n void confirm()\n }\n })\n overlay.addEventListener('pointerdown', (event) => {\n if (event.target === overlay) close()\n })\n document.body.append(overlay)\n modal = overlay\n modalKeyHandler = (event: KeyboardEvent): void => {\n if (event.key === 'Escape') close()\n }\n document.addEventListener('keydown', modalKeyHandler)\n window.requestAnimationFrame(() => {\n input.focus()\n input.select()\n })\n }\n\n function openConfirmDialog(title: string, message: string, confirm: () => Promise<void>): void {\n closeModal()\n const overlay = document.createElement('div')\n overlay.className = 'wvg-overlay'\n overlay.innerHTML = `<div class=\"wvg-dialog\" role=\"dialog\" aria-modal=\"true\" tabindex=\"-1\"><div class=\"wvg-dialog-content\"><div class=\"wvg-dialog-header\"><h2 class=\"wvg-dialog-title\"></h2><button type=\"button\" class=\"wvg-dialog-close\" aria-label=\"\u5173\u95ED\">${svgIcon('dsw-icon-close-outline-regular', 14)}</button></div><div class=\"wvg-dialog-body\"><div class=\"wvg-dialog-message\"></div><div class=\"wvg-error\" role=\"alert\"></div></div></div><div class=\"wvg-dialog-actions\"><button type=\"button\" class=\"wvg-button wvg-outline\" data-wvg-cancel>\u53D6\u6D88</button><button type=\"button\" class=\"wvg-button wvg-danger\" data-wvg-ok>\u5220\u9664</button></div></div>`\n const dialog = overlay.querySelector<HTMLElement>('.wvg-dialog')!\n dialog.setAttribute('aria-label', title)\n dialog.querySelector<HTMLElement>('.wvg-dialog-title')!.textContent = title\n dialog.querySelector<HTMLElement>('.wvg-dialog-message')!.textContent = message\n const error = dialog.querySelector<HTMLElement>('.wvg-error')!\n const ok = dialog.querySelector<HTMLButtonElement>('[data-wvg-ok]')!\n let busy = false\n const close = (): void => closeModal()\n const run = async (): Promise<void> => {\n if (busy) return\n busy = true\n ok.disabled = true\n error.textContent = ''\n try {\n await confirm()\n close()\n } catch (reason) {\n error.textContent = reason instanceof Error ? reason.message : String(reason)\n } finally {\n busy = false\n ok.disabled = false\n }\n }\n dialog.querySelector<HTMLElement>('[data-wvg-cancel]')!.addEventListener('click', close)\n dialog.querySelector<HTMLElement>('.wvg-dialog-close')!.addEventListener('click', close)\n ok.addEventListener('click', () => { void run() })\n overlay.addEventListener('pointerdown', (event) => {\n if (event.target === overlay) close()\n })\n document.body.append(overlay)\n modal = overlay\n modalKeyHandler = (event: KeyboardEvent): void => {\n if (event.key === 'Escape') close()\n }\n document.addEventListener('keydown', modalKeyHandler)\n window.requestAnimationFrame(() => {\n dialog.querySelector<HTMLButtonElement>('[data-wvg-cancel]')?.focus()\n })\n }\n\n function closeModal(): void {\n if (modalKeyHandler !== null) document.removeEventListener('keydown', modalKeyHandler)\n modalKeyHandler = null\n modal?.remove()\n modal = null\n }\n\n function showToast(message: string): void {\n document.querySelectorAll('.wvg-toast').forEach((node) => node.remove())\n const toast = document.createElement('div')\n toast.className = 'wvg-toast'\n toast.textContent = message\n document.body.append(toast)\n window.clearTimeout(toastTimer)\n toastTimer = window.setTimeout(() => toast.remove(), 3200)\n }\n\n // Initial data, then the official DOM is enhanced whenever it changes.\n void loadState().then((state) => {\n if (disposed) return\n manual = state\n viewMode = manual.mode\n loaded = true\n schedule()\n }).catch((reason: unknown) => {\n if (disposed) return\n loaded = false\n console.warn('workspace virtual groups: manual overlay unavailable:', reason)\n showToast('\u5206\u7EC4\u6570\u636E\u52A0\u8F7D\u5931\u8D25\uFF0C\u865A\u62DF\u5206\u7EC4\u5DF2\u6682\u65F6\u505C\u7528\u3002\u8BF7\u5237\u65B0\u91CD\u8BD5\u3002')\n schedule()\n })\n\n // The conversation streams below the sidebar; observing every mutation would\n // reconcile once per frame for no reason. Only schedule when the sidebar\n // list, its header actions, or an official/injected menu is touched.\n const OBSERVED_SELECTOR = [\n '[role=\"tree\"]',\n '[role=\"menu\"]',\n '[class*=\"viewOptionsMenu\"]',\n '[class*=\"sectionHeader\"]',\n '[class*=\"headerActions\"]',\n '[data-wvg-new-group]',\n '[data-wvg-view-wrap]',\n ].join(',')\n const mutationRelevant = (record: MutationRecord): boolean => {\n if (record.target instanceof Element && record.target.closest(OBSERVED_SELECTOR) !== null) return true\n for (const node of record.addedNodes) {\n if (node instanceof Element && (node.matches(OBSERVED_SELECTOR) || node.querySelector(OBSERVED_SELECTOR) !== null)) return true\n }\n for (const node of record.removedNodes) {\n if (node instanceof Element && (node.matches(OBSERVED_SELECTOR) || node.querySelector(OBSERVED_SELECTOR) !== null)) return true\n }\n return !(record.target instanceof Element)\n }\n const observer = new MutationObserver((records) => {\n if (records.some(mutationRelevant)) schedule()\n })\n observer.observe(document.body, { childList: true, subtree: true })\n document.addEventListener('dragstart', onDocumentDragStart, true)\n document.addEventListener('dragend', onDocumentDragEnd, true)\n schedule()\n\n return () => {\n disposed = true\n observer.disconnect()\n document.removeEventListener('dragstart', onDocumentDragStart, true)\n document.removeEventListener('dragend', onDocumentDragEnd, true)\n if (frame !== 0) window.cancelAnimationFrame(frame)\n window.clearTimeout(toastTimer)\n closeMenu()\n closeViewOptions()\n closeModal()\n cleanupList()\n restoreLegacyAddButton()\n removeNewGroupButtons()\n document.querySelectorAll('[data-wvg-view-wrap]').forEach((node) => node.remove())\n style.remove()\n }\n}\n"],
5
- "mappings": "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",
3
+ "sources": ["../src/client/index.ts", "../src/core/types.ts", "../src/core/grouping.ts", "inline-css:/home/runner/work/dsh-workspace-virtual-groups/dsh-workspace-virtual-groups/src/client/styles.css", "../src/client/dom-groups.ts"],
4
+ "sourcesContent": ["/**\n * dsh-workspace-virtual-groups client half.\n *\n * This is deliberately NOT a replacement for the official Workspace browser.\n * It mounts a thin grouping layer that reorders the official sidebar sections\n * and inserts group labels between them; every official row keeps its markup,\n * styles, hover actions, search and keyboard behavior.\n */\nimport type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\n// Type-only service merges: the Workspace controller and the navigation face\n// that owns the directory picker / session creation.\nimport type {} from '@deepseek-ai/dsh-api-workspace-controller/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-workspace/client'\nimport { mountWorkspaceVirtualGroups } from './dom-groups.ts'\n\n/** Required services: the Workspace controller and its navigation face. */\nexport const inject = ['workspaces', 'uiWorkspace']\n\n/** Mount the grouping layer and remove it with the client fiber. */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(\n () => mountWorkspaceVirtualGroups(ctx as unknown as Parameters<typeof mountWorkspaceVirtualGroups>[0]),\n 'dsh-workspace-virtual-groups: sidebar grouping layer',\n )\n}\n", "/**\n * Shared manual-group types for dsh-workspace-virtual-groups. Pure types (zero\n * runtime deps) so the host half (sidecar persistence) and the browser half\n * (tree derivation) compile against the same contract.\n *\n * Groups are user-created only. The built-in default group catches every\n * workspace that has no explicit assignment.\n */\n\n/** Which grouping surface the sidebar should present. */\nexport type GroupViewMode = 'virtual' | 'official'\n\n/**\n * Runtime-managed grouping overlay: groups created in the sidebar UI plus\n * per-workspace assignments and user-controlled group ordering. Owned by the\n * plugin (JSON sidecar, `$DSH_HOME/workspace-virtual-groups.manual.json`).\n */\nexport interface ManualGroups {\n /** Manually created group names, in creation order. */\n categories: string[]\n /**\n * Per-workspace group assignment, keyed by workspace id. Value is a manual\n * category name, or `null` for the built-in default group. An absent key is\n * also treated as the default group.\n */\n assignments: Record<string, string | null>\n /**\n * Display order of manual groups. The default group is pinned first and is\n * never listed. Absent = manual creation order.\n */\n categoryOrder?: string[]\n /**\n * View mode last selected in the official view-options menu. Absent is\n * treated as `virtual`.\n */\n mode?: GroupViewMode\n}\n\n/**\n * The built-in, non-deletable group that holds every workspace without an\n * explicit group assignment. The name is fixed by product requirement: it is\n * shown verbatim in every UI language and may never be renamed or deleted.\n *\n * Persistence convention: a `null` assignment means \"default group\".\n */\nexport const DEFAULT_GROUP = '\u9ED8\u8BA4'\n", "/**\n * Pure manual-group ordering/assignment helpers shared by the host half\n * (validation) and the browser half (tree derivation).\n *\n * There is no automatic classifier: a workspace is in a manual group when the\n * overlay assigns it there, otherwise it is in the built-in default group.\n */\nimport { DEFAULT_GROUP, type ManualGroups } from './types.ts'\n\n/** One rendered group entry; default is always first. */\nexport interface EffectiveCategory {\n /** Display key (what the tree renders and assignments reference). */\n key: string\n /** Origin: the built-in default group or a manual group. */\n source: 'builtin' | 'manual'\n}\n\n/**\n * Effective manual groups in display order: default first, then manual groups\n * ordered by `categoryOrder` (absent entries keep creation order).\n */\nexport function effectiveCategories(manual: ManualGroups | undefined): EffectiveCategory[] {\n const entries: EffectiveCategory[] = [{ key: DEFAULT_GROUP, source: 'builtin' }]\n const seen = new Set<string>([DEFAULT_GROUP])\n for (const name of manual?.categories ?? []) {\n if (seen.has(name)) continue\n seen.add(name)\n entries.push({ key: name, source: 'manual' })\n }\n const order = manual?.categoryOrder\n if (order === undefined || order.length === 0) return entries\n const position = new Map(order.map((key, index) => [key, index]))\n const [defaultEntry, ...rest] = entries\n rest.sort((a, b) => {\n const pa = position.get(a.key)\n const pb = position.get(b.key)\n if (pa !== undefined && pb !== undefined) return pa - pb\n if (pa !== undefined) return -1\n if (pb !== undefined) return 1\n return 0\n })\n return [defaultEntry!, ...rest]\n}\n\n/** Display keys of all effective groups (default first). */\nexport function displayCategoryKeys(manual: ManualGroups | undefined): string[] {\n return effectiveCategories(manual).map(entry => entry.key)\n}\n\n/** Resolve the group key a workspace renders under. */\nexport function resolveCategory(manual: ManualGroups | undefined, workspaceId: string): string {\n const assignment = manual?.assignments[workspaceId]\n if (assignment === undefined || assignment === null) return DEFAULT_GROUP\n return manual?.categories.includes(assignment) ? assignment : DEFAULT_GROUP\n}\n\n/** Names that may not be used for a new group. */\nexport function takenCategoryNames(manual: ManualGroups | undefined): Set<string> {\n return new Set<string>(displayCategoryKeys(manual))\n}\n\n/** Move `id` before `beforeId` (undefined = append) within an ordered list. */\nexport function moveBefore(list: readonly string[], id: string, beforeId: string | undefined): string[] {\n const rest = list.filter(x => x !== id)\n if (beforeId === undefined) return [...rest, id]\n const index = rest.indexOf(beforeId)\n if (index === -1) return [...rest, id]\n rest.splice(index, 0, id)\n return rest\n}\n\n/** Move `id` after `afterId` (undefined = append) within an ordered list. */\nexport function moveAfter(list: readonly string[], id: string, afterId: string | undefined): string[] {\n const rest = list.filter(x => x !== id)\n if (afterId === undefined) return [...rest, id]\n const index = rest.indexOf(afterId)\n if (index === -1) return [...rest, id]\n rest.splice(index + 1, 0, id)\n return rest\n}\n", "export default \"/* Virtual group labels are the ONLY new visual element. Official project and\\n session rows keep their own classes, fonts, spacing, icons and actions.\\n Menus and dialogs mirror DSH's own section labels, context menu and modal\\n metrics instead of inventing a separate visual language. */\\n\\n.wvg-list {\\n display: flex !important;\\n flex-direction: column;\\n}\\n\\n/* Section label: same 36px row / 14px tertiary label as the official sidebar\\n section header, plus a chevron and a group action button. */\\n.wvg-group {\\n position: relative;\\n display: flex;\\n align-items: center;\\n gap: 4px;\\n height: 36px;\\n flex: none;\\n padding: 0 8px 0 12px;\\n border-radius: 12px;\\n color: var(--dsw-alias-label-tertiary, #81858c);\\n font: inherit;\\n font-size: 14px;\\n font-weight: 400;\\n line-height: 20px;\\n cursor: pointer;\\n user-select: none;\\n}\\n\\n.wvg-group[data-wvg-default='0'] {\\n margin-top: 4px;\\n}\\n\\n.wvg-group:hover {\\n background: var(--dsw-bg-hover, rgba(127, 127, 127, 0.12));\\n}\\n\\n.wvg-group.wvg-drop {\\n background: var(--dsw-bg-selected, rgba(74, 144, 217, 0.18));\\n}\\n\\n.wvg-chevron {\\n display: inline-flex;\\n align-items: center;\\n justify-content: center;\\n width: 14px;\\n height: 14px;\\n flex: none;\\n color: var(--dsw-alias-label-tertiary, #81858c);\\n}\\n\\n.wvg-chevron svg {\\n transition: transform 120ms ease;\\n}\\n\\n.wvg-group[aria-expanded='true'] .wvg-chevron svg {\\n transform: rotate(90deg);\\n}\\n\\n.wvg-label {\\n flex: 1;\\n min-width: 0;\\n overflow: hidden;\\n text-overflow: ellipsis;\\n white-space: nowrap;\\n}\\n\\n/* Match the workspace row's hover-only 24px action button. */\\n.wvg-more:not([data-wvg-native-icon]) {\\n display: inline-flex;\\n align-items: center;\\n justify-content: center;\\n width: 24px;\\n height: 24px;\\n flex: none;\\n margin: 0;\\n padding: 0;\\n border: none;\\n border-radius: var(--dsw-radius-xs, 6px);\\n background: transparent;\\n color: var(--dsw-alias-label-tertiary, #81858c);\\n cursor: pointer;\\n}\\n\\n@media (hover: hover) {\\n .wvg-more:not([data-wvg-native-icon]):hover {\\n color: var(--dsw-alias-label-primary, #0f1115);\\n }\\n}\\n\\n.wvg-group .wvg-more {\\n display: none;\\n margin: 0;\\n}\\n\\n@media (hover: hover) {\\n .wvg-group:hover .wvg-more,\\n .wvg-group.wvg-menu-open .wvg-more,\\n .wvg-group:focus-within .wvg-more {\\n display: inline-flex;\\n }\\n}\\n\\n@media (hover: none) {\\n .wvg-group .wvg-more {\\n display: inline-flex;\\n }\\n}\\n\\n.wvg-group[draggable='true'] {\\n cursor: grab;\\n}\\n\\n.wvg-group[draggable='true']:active {\\n cursor: grabbing;\\n}\\n\\n.wvg-group.wvg-dragging {\\n opacity: 0.45;\\n}\\n\\n.wvg-group.wvg-insert-before::before,\\n.wvg-group.wvg-insert-after::after {\\n content: '';\\n position: absolute;\\n left: 2px;\\n right: 2px;\\n height: 2px;\\n border-radius: 1px;\\n background: var(--dsw-accent, #4a90d9);\\n pointer-events: none;\\n z-index: 2;\\n}\\n\\n.wvg-group.wvg-insert-before::before {\\n top: -2px;\\n}\\n\\n.wvg-group.wvg-insert-after::after {\\n bottom: -2px;\\n}\\n\\n/* Context menu: 16px radius surface, 4px padding, 12px rows, 13px labels. */\\n.wvg-menu {\\n position: fixed;\\n z-index: 1100;\\n min-width: 144px;\\n padding: 4px;\\n border: none;\\n border-radius: 16px;\\n background: var(--dsw-alias-bg-layer-2, var(--dsw-bg-1, #fff));\\n box-shadow:\\n 0 0 0 0.5px rgba(0, 0, 0, 0.04),\\n 0 3px 8px 0 rgba(0, 0, 0, 0.04),\\n 0 0 20px 0 rgba(0, 0, 0, 0.05);\\n}\\n\\n.wvg-menu-item {\\n display: flex;\\n align-items: center;\\n gap: 8px;\\n width: 100%;\\n min-height: 32px;\\n box-sizing: border-box;\\n padding: 6px 8px;\\n border: none;\\n border-radius: 12px;\\n background: transparent;\\n color: var(--dsw-alias-label-primary, var(--dsw-fg-1, inherit));\\n font: inherit;\\n font-size: 13px;\\n line-height: 20px;\\n text-align: left;\\n cursor: pointer;\\n}\\n\\n.wvg-menu-item:hover {\\n background: var(--dsw-bg-hover, rgba(127, 127, 127, 0.12));\\n}\\n\\n.wvg-menu-icon {\\n display: inline-flex;\\n align-items: center;\\n justify-content: center;\\n width: 16px;\\n height: 16px;\\n flex: none;\\n color: var(--dsw-fg-2, inherit);\\n}\\n\\n.wvg-menu-label {\\n flex: 1;\\n min-width: 0;\\n}\\n\\n.wvg-menu-item.wvg-danger {\\n color: var(--dsw-alias-label-danger, #d64545);\\n}\\n\\n.wvg-menu-item.wvg-danger .wvg-menu-icon {\\n color: currentColor;\\n}\\n\\n\\n.wvg-menu-separator {\\n height: 1px;\\n margin: 4px 8px;\\n background: var(--dsw-alias-border-l2, rgba(127, 127, 127, 0.15));\\n}\\n\\n/* Modal: 380px / 28px radius surface and 36px buttons, matching DSH dialogs. */\\n.wvg-overlay {\\n position: fixed;\\n inset: 0;\\n z-index: 1400;\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n box-sizing: border-box;\\n padding: 24px;\\n background: transparent;\\n}\\n\\n.wvg-dialog {\\n display: flex;\\n flex-direction: column;\\n gap: 20px;\\n box-sizing: border-box;\\n width: 380px;\\n max-width: 100%;\\n padding: 0 0 24px;\\n overflow: hidden;\\n border: none;\\n border-radius: 28px;\\n background: var(--dsw-alias-bg-layer-2, #2c2c2e);\\n color: var(--dsw-alias-label-primary, #f9fafb);\\n box-shadow:\\n 0 0 0 0.5px var(--dsw-alias-border-l4, rgba(255, 255, 255, 0.2)),\\n 0 3px 8px 0 rgba(0, 0, 0, 0.04),\\n 0 0 20px 0 rgba(0, 0, 0, 0.05);\\n font: inherit;\\n}\\n\\n.wvg-dialog-content {\\n display: flex;\\n flex-direction: column;\\n gap: 20px;\\n}\\n\\n.wvg-dialog-header {\\n display: flex;\\n align-items: center;\\n justify-content: space-between;\\n gap: 8px;\\n box-sizing: border-box;\\n min-height: 62px;\\n padding: 22px 14px 12px 24px;\\n}\\n\\n.wvg-dialog-title {\\n min-width: 0;\\n margin: 0;\\n color: var(--dsw-alias-label-primary, #f9fafb);\\n font-size: 16px;\\n font-weight: 500;\\n line-height: 24px;\\n}\\n\\n.wvg-dialog-close {\\n display: inline-flex;\\n align-items: center;\\n justify-content: center;\\n width: 28px;\\n height: 28px;\\n flex: none;\\n box-sizing: border-box;\\n padding: 1px 6px;\\n border: none;\\n border-radius: 8px;\\n background: transparent;\\n color: var(--dsw-alias-label-secondary, #cfd3d6);\\n cursor: pointer;\\n}\\n\\n.wvg-dialog-close:hover {\\n background: var(--dsw-alias-interactive-bg-hover, rgba(255, 255, 255, 0.08));\\n}\\n\\n.wvg-dialog-body {\\n display: flex;\\n flex-direction: column;\\n padding: 0 24px;\\n}\\n\\n.wvg-dialog-message {\\n margin: 0;\\n padding: 0;\\n color: var(--dsw-alias-label-secondary, #cfd3d6);\\n font-size: 14px;\\n line-height: 22px;\\n}\\n\\n.wvg-input {\\n display: block;\\n box-sizing: border-box;\\n width: 100%;\\n height: 44px;\\n margin: 0;\\n padding: 7px 14px;\\n border: 1px solid var(--dsw-alias-border-l4, rgba(255, 255, 255, 0.2));\\n border-radius: 16px;\\n outline: none;\\n background: transparent;\\n color: var(--dsw-alias-label-primary, #f9fafb);\\n font: inherit;\\n font-size: 14px;\\n line-height: 22px;\\n}\\n\\n.wvg-error {\\n min-height: 0;\\n margin: 8px 0 0;\\n color: var(--dsw-alias-state-error-primary, #d64545);\\n font-size: 12px;\\n line-height: 18px;\\n}\\n\\n.wvg-error:empty {\\n display: none;\\n margin: 0;\\n}\\n\\n.wvg-dialog-actions {\\n display: flex;\\n align-items: center;\\n justify-content: flex-end;\\n gap: 8px;\\n padding: 0 24px;\\n}\\n\\n.wvg-button {\\n display: inline-flex;\\n align-items: center;\\n justify-content: center;\\n box-sizing: border-box;\\n height: 36px;\\n padding: 0 14px;\\n border: none;\\n border-radius: 12px;\\n background: transparent;\\n color: var(--dsw-alias-label-primary, #f9fafb);\\n font: inherit;\\n font-size: 14px;\\n line-height: 22px;\\n cursor: pointer;\\n}\\n\\n.wvg-button.wvg-outline {\\n border: 1px solid transparent;\\n box-shadow: inset 0 0 0 1px var(--dsw-alias-border-l3, rgba(255, 255, 255, 0.16));\\n}\\n\\n.wvg-button:hover {\\n background: var(--dsw-alias-interactive-bg-hover, rgba(255, 255, 255, 0.08));\\n}\\n\\n.wvg-button.wvg-primary {\\n background: var(--dsw-alias-label-primary, #f9fafb);\\n color: var(--dsw-alias-label-inverse, #0f1115);\\n}\\n\\n.wvg-button.wvg-primary:hover {\\n background: var(--dsw-alias-label-primary, #f9fafb);\\n opacity: 0.9;\\n}\\n\\n.wvg-button.wvg-danger {\\n background: var(--dsw-alias-state-error-primary, #d64545);\\n color: #fff;\\n}\\n\\n.wvg-button:disabled {\\n opacity: 0.4;\\n cursor: default;\\n}\\n\\n.wvg-button:focus-visible,\\n.wvg-dialog-close:focus-visible {\\n outline: 2px solid var(--dsw-alias-state-business-primary, #7aaaff);\\n outline-offset: 2px;\\n}\\n\\n.wvg-toast {\\n position: fixed;\\n bottom: 24px;\\n left: 50%;\\n z-index: 1500;\\n max-width: min(440px, calc(100vw - 32px));\\n padding: 8px 12px;\\n border-radius: 12px;\\n background: var(--dsw-alias-bg-inverse, #222);\\n color: var(--dsw-alias-label-inverse, #fff);\\n font-size: 13px;\\n line-height: 20px;\\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.24);\\n transform: translateX(-50%);\\n}\\n\\n\";", "/**\n * Virtual workspace groups as a thin layer over the official sidebar browser.\n *\n * The official `@deepseek-ai/dsh-client-ui-workspace` browser is left\n * completely intact: its row markup, icons, hover actions, search, keyboard\n * handling and view options are the ones users already know. This module only\n * reorders the official workspace sections (via CSS `order` on a flex list)\n * and inserts group labels between them. No official row is cloned, replaced\n * or restyled.\n *\n * The grouping overlay is the same one the host half serves: manual groups and\n * per-workspace assignments are persisted in\n * `$DSH_HOME/workspace-virtual-groups.manual.json`.\n */\nimport type { ManualGroups } from '../core/types.ts'\nimport { DEFAULT_GROUP } from '../core/types.ts'\nimport {\n effectiveCategories,\n moveAfter,\n moveBefore,\n resolveCategory,\n takenCategoryNames,\n} from '../core/grouping.ts'\nimport css from './styles.css?inline'\n\nconst MIME = 'application/x-dsh-workspace-virtual-groups'\nconst GROUP_MIME = 'application/x-dsh-workspace-virtual-group'\n\n/** Layer glyph used by the new-group button and the view-options item. */\nconst LAYER_ICON = '<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1\" stroke-linejoin=\"round\" data-wvg-layer=\"1\" aria-hidden=\"true\"><path d=\"M8 1.8 14 5 8 8.2 2 5 8 1.8Z\"></path><path d=\"M2 8 8 11.2 14 8\"></path><path d=\"M2 11 8 14.2 14 11\"></path></svg>'\n/** Official grouping-mode icons; stable across UI locales. */\nconst GROUPING = {\n workspace: {\n icon: 'dsw-icon-folder-close-regular',\n labels: ['\u6309\u5DE5\u4F5C\u533A', 'By workspace'],\n },\n tree: {\n icon: 'dsw-icon-workspace-tree-outline-regular',\n labels: ['\u6309\u5DE5\u4F5C\u533A\u6811', 'By workspace tree'],\n },\n flatList: {\n icon: 'dsw-icon-flat-list-outline-regular',\n labels: ['\u5355\u5217\u8868', 'Flat list'],\n },\n} as const\nconst OFFICIAL_GROUPING_ICONS = new Set<string>(\n Object.values(GROUPING).map((entry) => entry.icon),\n)\nconst OFFICIAL_GROUPING_LABELS = new Set<string>(\n Object.values(GROUPING).flatMap((entry) => entry.labels),\n)\nconst MANUAL_ROUTE = '/workspace-virtual-groups/manual'\nconst COLLAPSED_KEY = 'dsh.workspace.virtual-groups.collapsed.v1'\n/** Indentation applied to project sections inside a virtual group (matches the official tree step). */\nconst GROUP_INDENT = '20px'\n\ntype NormalizedManual = Required<ManualGroups>\n\nconst EMPTY_MANUAL: NormalizedManual = {\n categories: [],\n assignments: {},\n categoryOrder: [],\n mode: 'virtual',\n}\n\n/** Materialize optional overlay fields into the fully-populated client shape. */\nfunction normalizeManual(manual: ManualGroups | undefined): NormalizedManual {\n if (manual === undefined) return EMPTY_MANUAL\n const categories = [...new Set(manual.categories)]\n .filter(name => name !== DEFAULT_GROUP)\n const categorySet = new Set(categories)\n const assignments = Object.fromEntries(\n Object.entries(manual.assignments).map(([id, category]): [string, string | null] => [\n id,\n category === null || !categorySet.has(category) ? null : category,\n ]),\n )\n return {\n categories,\n assignments,\n categoryOrder: (manual.categoryOrder ?? []).filter(key => categorySet.has(key)),\n mode: manual.mode === 'official' ? 'official' : 'virtual',\n }\n}\n\n/** Fetch the runtime overlay. */\nasync function loadState(): Promise<NormalizedManual> {\n const response = await fetch(MANUAL_ROUTE, { cache: 'no-cache' })\n if (!response.ok) throw new Error(`manual request failed: ${response.status}`)\n return normalizeManual((await response.json()) as ManualGroups)\n}\n\n/** Persist the whole overlay. The host validates and writes atomically. */\nasync function putManual(manual: NormalizedManual): Promise<void> {\n const response = await fetch(MANUAL_ROUTE, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(manual),\n })\n if (response.ok) return\n let message = `\u4FDD\u5B58\u5931\u8D25\uFF08${response.status}\uFF09`\n try {\n const text = await response.text()\n if (text !== '') message = text\n } catch { /* keep the fallback */ }\n throw new Error(message)\n}\n\nfunction readCollapsed(): Set<string> {\n try {\n const raw = window.localStorage.getItem(COLLAPSED_KEY)\n if (raw === null) return new Set()\n const parsed: unknown = JSON.parse(raw)\n return new Set(Array.isArray(parsed) ? parsed.filter((key): key is string => typeof key === 'string') : [])\n } catch {\n return new Set()\n }\n}\n\nfunction writeCollapsed(collapsed: ReadonlySet<string>): void {\n try {\n window.localStorage.setItem(COLLAPSED_KEY, JSON.stringify([...collapsed]))\n } catch { /* private mode / quota: expansion is only a view preference */ }\n}\n\nfunction svgIcon(id: string, size: number): string {\n return `<svg width=\"${size}\" height=\"${size}\" aria-hidden=\"true\"><use href=\"#${id}\"></use></svg>`\n}\n\n/**\n * Mount the grouping layer over the official Workspace browser.\n *\n * @param ctx - client context carrying the Workspace controller snapshot.\n * @returns the disposer that removes every DOM node, listener and style.\n */\nexport function mountWorkspaceVirtualGroups(ctx: {\n workspaces: {\n create(input: { path: string }): Promise<{ workspaceId: string }>\n }\n uiWorkspace: {\n pickDirectory(): Promise<string | null>\n startSession(workspaceId?: string): void\n }\n}): () => void {\n const style = document.createElement('style')\n style.dataset.plugin = 'dsh-workspace-virtual-groups'\n style.textContent = css\n document.head.append(style)\n\n let disposed = false\n let manual: NormalizedManual = EMPTY_MANUAL\n let loaded = false\n let list: HTMLElement | null = null\n let viewMode: 'virtual' | 'official' = 'virtual'\n let suppressViewModeClick = false\n let lastSelectedExtra: string[] = []\n let lastCheckHTML: string | null = null\n let addingWorkspace = false\n let frame = 0\n let menu: HTMLElement | null = null\n let menuAnchor: HTMLElement | null = null\n let menuPointerClose: ((event: PointerEvent) => void) | null = null\n let menuKeyClose: ((event: KeyboardEvent) => void) | null = null\n let modal: HTMLElement | null = null\n let modalKeyHandler: ((event: KeyboardEvent) => void) | null = null\n let toastTimer = 0\n let dragId: string | null = null\n let draggingGroup: string | null = null\n let saveQueue: Promise<void> = Promise.resolve()\n const groupedHeaders = new Map<string, HTMLElement>()\n const collapsed = readCollapsed()\n /** Official `--dsh-workspace-indent` value before this layer touched a section. */\n const originalIndent = new WeakMap<HTMLElement, string>()\n const viewMenuMeta = new WeakMap<HTMLButtonElement, { baseClass: string; selectedExtra: string[]; check: string | null }>()\n const headerActionsMeta = new WeakMap<HTMLElement, { width: string; minWidth: string; overflow: string }>()\n const headerActionsTracked = new Set<HTMLElement>()\n\n /** Coalesce DOM/state notifications into one animation frame. */\n function schedule(): void {\n if (disposed || frame !== 0) return\n frame = window.requestAnimationFrame(() => {\n frame = 0\n if (!disposed) reconcile()\n })\n }\n\n /** Find the official workspace list (not the search tree). */\n function findList(): HTMLElement | null {\n const row = document.querySelector<HTMLElement>('[data-row-key^=\"workspace:\"]')\n return row?.closest<HTMLElement>('[role=\"tree\"]') ?? null\n }\n\n /** Direct workspace sections of the official list. */\n function sectionsOf(target: HTMLElement): HTMLElement[] {\n return Array.from(target.children).filter((element): element is HTMLElement =>\n element instanceof HTMLElement\n && element.querySelector(':scope [data-row-key^=\"workspace:\"]') !== null,\n )\n }\n\n function sectionWorkspaceId(section: HTMLElement): string | undefined {\n const key = section.querySelector<HTMLElement>('[data-row-key^=\"workspace:\"]')?.getAttribute('data-row-key') ?? ''\n return key.startsWith('workspace:') ? key.slice('workspace:'.length) : undefined\n }\n\n /** The official list is a block container; a flex column lets `order` work. */\n function markList(target: HTMLElement): void {\n target.classList.add('wvg-list')\n target.dataset.wvgList = '1'\n }\n\n function clearSections(target: HTMLElement): void {\n for (const section of sectionsOf(target)) {\n section.style.order = ''\n section.style.display = ''\n const original = originalIndent.get(section)\n // Only undo the indent when it is still the value this layer wrote. If\n // the official tree view changed it in the meantime, leave the official\n // value in place instead of restoring the pre-grouping one.\n if (original !== undefined && section.style.getPropertyValue('--dsh-workspace-indent') === GROUP_INDENT) {\n if (original === '') section.style.removeProperty('--dsh-workspace-indent')\n else section.style.setProperty('--dsh-workspace-indent', original)\n }\n originalIndent.delete(section)\n delete section.dataset.wvgIndent\n }\n }\n\n function clearHeaders(): void {\n for (const header of groupedHeaders.values()) header.remove()\n groupedHeaders.clear()\n }\n\n function cleanupList(): void {\n if (list === null) return\n list.classList.remove('wvg-list')\n delete list.dataset.wvgList\n clearSections(list)\n clearHeaders()\n }\n\n /** Open the create-group dialog. Shared by the repurposed header button. */\n function openCreateGroupDialog(): void {\n openNameDialog('\u65B0\u5EFA\u5206\u7EC4', '', '\u521B\u5EFA', async (name) => {\n const taken = takenCategoryNames(manual)\n if (name === '' || name === DEFAULT_GROUP) return '\u8BE5\u540D\u79F0\u4E0D\u53EF\u7528'\n if (taken.has(name)) return '\u5DF2\u5B58\u5728\u540C\u540D\u5206\u7EC4'\n await persist({ ...manual, categories: [...manual.categories, name] })\n return null\n })\n }\n\n function officialAddButton(): HTMLButtonElement | null {\n const use = document.querySelector<SVGUseElement>('use[href=\"#dsw-icon-project-add-outline-regular\"]')\n const button = use?.closest('button')\n return button instanceof HTMLButtonElement ? button : null\n }\n\n /** Restore the official header-actions width after the extra button goes away. */\n function restoreHeaderActions(actions: HTMLElement | null): void {\n if (actions === null) return\n const original = headerActionsMeta.get(actions)\n if (original === undefined) return\n actions.style.width = original.width\n actions.style.minWidth = original.minWidth\n actions.style.overflow = original.overflow\n headerActionsMeta.delete(actions)\n headerActionsTracked.delete(actions)\n }\n\n function removeNewGroupButtons(): void {\n document.querySelectorAll<HTMLElement>('[data-wvg-new-group]').forEach((node) => {\n const actions = node.parentElement\n node.remove()\n restoreHeaderActions(actions)\n })\n for (const actions of [...headerActionsTracked]) restoreHeaderActions(actions)\n }\n\n /** The layer-icon button next to the official add-workspace button. */\n function ensureNewGroupButton(): void {\n if (viewMode !== 'virtual') {\n removeNewGroupButtons()\n return\n }\n const add = officialAddButton()\n if (add === null) {\n removeNewGroupButtons()\n return\n }\n let button = document.querySelector<HTMLButtonElement>('button[data-wvg-new-group]')\n if (button === null || !button.isConnected) {\n button = add.cloneNode(true) as HTMLButtonElement\n button.dataset.wvgNewGroup = '1'\n add.after(button)\n }\n if (button.previousElementSibling !== add) add.after(button)\n button.setAttribute('aria-label', '\u65B0\u5EFA\u5206\u7EC4')\n button.removeAttribute('aria-keyshortcuts')\n button.removeAttribute('aria-expanded')\n if (button.querySelector('svg[data-wvg-layer=\"1\"]') === null) button.innerHTML = LAYER_ICON\n button.onclick = (event) => {\n event.preventDefault()\n event.stopPropagation()\n openCreateGroupDialog()\n }\n // The official actions container is sized for two 28px buttons and clips\n // overflow. Widen it to fit the layer button; restore it when the button is\n // removed or the plugin unmounts.\n const actions = button.parentElement\n if (actions !== null) {\n if (!headerActionsMeta.has(actions)) {\n headerActionsMeta.set(actions, {\n width: actions.style.width,\n minWidth: actions.style.minWidth,\n overflow: actions.style.overflow,\n })\n headerActionsTracked.add(actions)\n }\n actions.style.width = 'max-content'\n actions.style.minWidth = 'max-content'\n actions.style.overflow = 'visible'\n }\n }\n\n function groupingIcon(button: HTMLButtonElement): string | null {\n const svg = button.querySelector<SVGElement>(':scope > span svg[data-icon]')\n return svg?.getAttribute('data-icon') ?? null\n }\n\n function groupingLabel(button: HTMLButtonElement): string {\n return (button.textContent ?? '').trim()\n }\n\n function isOfficialGroupingButton(button: HTMLButtonElement): boolean {\n const icon = groupingIcon(button)\n if (icon !== null && OFFICIAL_GROUPING_ICONS.has(icon)) return true\n // Fallback for a DSH build that changes the icon names.\n return OFFICIAL_GROUPING_LABELS.has(groupingLabel(button))\n }\n\n function findGroupingButton(\n menu: HTMLElement,\n group: (typeof GROUPING)[keyof typeof GROUPING],\n ): HTMLButtonElement | null {\n const buttons = [...menu.querySelectorAll<HTMLButtonElement>('button[role=\"menuitem\"]')]\n const labels: readonly string[] = group.labels\n return buttons.find((button) => groupingIcon(button) === group.icon)\n ?? buttons.find((button) => labels.includes(groupingLabel(button)))\n ?? null\n }\n\n function virtualGroupingLabel(menu: HTMLElement): string {\n const workspace = findGroupingButton(menu, GROUPING.workspace)\n const label = workspace === null ? '' : groupingLabel(workspace)\n return label.includes('\u6309') || label.includes('\u5DE5\u4F5C\u533A') ? '\u6309\u865A\u62DF\u5206\u7EC4' : 'Virtual groups'\n }\n\n function findViewOptionsMenu(): HTMLElement | null {\n for (const menu of document.querySelectorAll<HTMLElement>('[role=\"menu\"]')) {\n // The CSS-module local name survives hashing and is language-independent.\n if (menu.className.includes('viewOptionsMenu')) return menu\n // Fallback: a menu that contains an official grouping mode.\n if ([...menu.querySelectorAll<HTMLButtonElement>('button[role=\"menuitem\"]')].some(isOfficialGroupingButton)) {\n return menu\n }\n }\n return null\n }\n\n function closeViewOptions(): void {\n if (findViewOptionsMenu() === null) return\n const use = document.querySelector<SVGUseElement>('use[href=\"#dsw-icon-sliders-two-outline-regular\"]')\n const trigger = use?.closest('button')\n if (trigger instanceof HTMLButtonElement) trigger.click()\n else document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }))\n }\n\n /** Add/refresh the \"\u6309\u865A\u62DF\u5206\u7EC4\" item inside the official view-options menu. */\n function ensureViewOptions(): void {\n const menu = findViewOptionsMenu()\n if (menu === null) return\n const isOfficial = isOfficialGroupingButton\n let ours = menu.querySelector<HTMLButtonElement>('button[data-wvg-view-virtual]')\n if (ours === null) {\n const tree = findGroupingButton(menu, GROUPING.tree)\n const wrap = tree?.parentElement\n if (tree === null || wrap === undefined || wrap === null) return\n const official = [...menu.querySelectorAll<HTMLButtonElement>('button[role=\"menuitem\"]')].filter(isOfficial)\n const unselected = official.find((button) => button.querySelector(':scope > svg') === null) ?? tree\n const selected = official.find((button) => button.querySelector(':scope > svg') !== null)\n const baseClass = unselected.className\n const selectedExtra = selected === undefined\n ? []\n : [...selected.classList].filter((name) => !unselected.classList.contains(name))\n const checkHTML = selected?.querySelector(':scope > svg')?.outerHTML ?? null\n if (selectedExtra.length > 0) lastSelectedExtra = selectedExtra\n if (checkHTML !== null) lastCheckHTML = checkHTML\n const clone = wrap.cloneNode(true) as HTMLElement\n clone.dataset.wvgViewWrap = '1'\n const button = clone.querySelector<HTMLButtonElement>('button[role=\"menuitem\"]')\n if (button === null) return\n button.dataset.wvgViewVirtual = '1'\n button.className = baseClass\n const label = [...button.querySelectorAll(':scope > span')]\n .find((span) => span.querySelector('svg') === null && (span.textContent ?? '').trim() !== '')\n if (label !== undefined) label.textContent = virtualGroupingLabel(menu)\n const icon = button.querySelector<HTMLElement>('[class*=\"itemIcon\"]')\n ?? [...button.querySelectorAll('span')].find((span) => span.querySelector('svg') !== null)\n if (icon !== undefined) icon.innerHTML = LAYER_ICON\n button.querySelector(':scope > svg')?.remove()\n wrap.after(clone)\n viewMenuMeta.set(button, {\n baseClass,\n selectedExtra: selectedExtra.length > 0 ? selectedExtra : lastSelectedExtra,\n check: checkHTML ?? lastCheckHTML,\n })\n button.addEventListener('click', (event) => {\n event.preventDefault()\n event.stopPropagation()\n activateVirtualMode(menu)\n })\n ours = button\n }\n\n if (menu.dataset.wvgModeListener !== '1') {\n menu.dataset.wvgModeListener = '1'\n menu.addEventListener('click', (event) => {\n if (suppressViewModeClick) return\n const button = (event.target as Element | null)?.closest<HTMLButtonElement>('button[role=\"menuitem\"]')\n if (button === null || button === undefined) return\n if (!isOfficial(button)) return\n setViewMode('official')\n }, true)\n }\n\n const meta = viewMenuMeta.get(ours)\n ?? { baseClass: ours.className, selectedExtra: lastSelectedExtra, check: lastCheckHTML }\n if (viewMode === 'virtual') {\n for (const button of menu.querySelectorAll<HTMLButtonElement>('button[role=\"menuitem\"]')) {\n if (button === ours || !isOfficial(button)) continue\n if (meta.selectedExtra.length > 0) button.classList.remove(...meta.selectedExtra)\n button.querySelector(':scope > svg')?.remove()\n }\n ours.className = meta.selectedExtra.length > 0\n ? `${meta.baseClass} ${meta.selectedExtra.join(' ')}`\n : meta.baseClass\n if (meta.check !== null && ours.querySelector(':scope > svg') === null) {\n const holder = document.createElement('template')\n holder.innerHTML = meta.check\n const check = holder.content.firstElementChild\n if (check !== null) ours.append(check)\n }\n } else {\n ours.className = meta.baseClass\n ours.querySelector(':scope > svg')?.remove()\n }\n }\n\n function setViewMode(mode: 'virtual' | 'official'): void {\n if (!loaded) return\n if (viewMode === mode) {\n schedule()\n return\n }\n viewMode = mode\n manual = { ...manual, mode }\n schedule()\n void persist(manual).catch((reason) => {\n showToast(reason instanceof Error ? reason.message : String(reason))\n })\n }\n\n function activateVirtualMode(menu: HTMLElement): void {\n setViewMode('virtual')\n const byWorkspace = findGroupingButton(menu, GROUPING.workspace)\n if (byWorkspace === null) {\n closeViewOptions()\n return\n }\n // Selecting virtual grouping also normalizes the official mode to the flat\n // \"\u6309\u5DE5\u4F5C\u533A\" list, otherwise the official tree nesting would fight the\n // virtual sections. Suppress our own official-item listener for this click.\n suppressViewModeClick = true\n byWorkspace.click()\n window.setTimeout(() => {\n suppressViewModeClick = false\n if (menu.isConnected) closeViewOptions()\n }, 0)\n }\n\n function createGroupHeader(key: string): HTMLElement {\n const header = document.createElement('div')\n header.className = 'wvg-group'\n header.dataset.wvgGroup = key\n header.dataset.wvgDefault = key === DEFAULT_GROUP ? '1' : '0'\n header.setAttribute('role', 'treeitem')\n header.setAttribute('aria-expanded', 'true')\n header.setAttribute('aria-label', key)\n header.draggable = key !== DEFAULT_GROUP\n header.innerHTML = `<span class=\"wvg-chevron\">${svgIcon('dsw-icon-triangle-right-fill-regular', 14)}</span><span class=\"wvg-label\"></span><button type=\"button\" class=\"wvg-more\" aria-label=\"\u5206\u7EC4\u64CD\u4F5C\" aria-haspopup=\"menu\">${svgIcon('dsw-icon-ellipsis-outline-regular', 16)}</button>`\n // Group names are user input; never interpolate them into innerHTML.\n header.querySelector<HTMLElement>('.wvg-more')?.setAttribute('aria-label', `\u5206\u7EC4\u201C${key}\u201D\u7684\u64CD\u4F5C`)\n header.addEventListener('dragstart', (event) => {\n if (key === DEFAULT_GROUP) {\n event.preventDefault()\n return\n }\n if ((event.target as HTMLElement | null)?.closest('.wvg-more') !== null) {\n event.preventDefault()\n return\n }\n draggingGroup = key\n try {\n event.dataTransfer?.setData(GROUP_MIME, key)\n if (event.dataTransfer !== null) event.dataTransfer.effectAllowed = 'move'\n } catch { /* synthetic drag */ }\n header.classList.add('wvg-dragging')\n })\n header.addEventListener('dragend', () => { clearDrag() })\n header.addEventListener('click', (event) => {\n if ((event.target as HTMLElement | null)?.closest('.wvg-more') !== null) return\n toggleGroup(key)\n })\n header.addEventListener('contextmenu', (event) => {\n event.preventDefault()\n openGroupMenu(key, header, event.clientX, event.clientY)\n })\n header.addEventListener('dragover', (event) => onGroupDragOver(event, header))\n header.addEventListener('dragleave', (event) => onGroupDragLeave(event, header))\n header.addEventListener('drop', (event) => onGroupDrop(event, key, header))\n header.querySelector<HTMLElement>('.wvg-more')?.addEventListener('click', (event) => {\n event.stopPropagation()\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect()\n openGroupMenu(key, event.currentTarget as HTMLElement, rect.left, rect.bottom + 4)\n })\n return header\n }\n\n /** Give group action buttons the exact native icon-button class. */\n function applyNativeIconClasses(): void {\n const rowAction = document.querySelector<HTMLButtonElement>('[data-row-key^=\"workspace:\"] [data-context-menu-trigger]')\n const nativeClass = rowAction?.className ?? officialAddButton()?.className ?? ''\n if (nativeClass === '') return\n document.querySelectorAll<HTMLButtonElement>('.wvg-more').forEach((button) => {\n const previous = button.dataset.wvgNativeClass ?? ''\n if (previous === nativeClass) return\n for (const name of previous.split(/\\s+/).filter(Boolean)) button.classList.remove(name)\n button.classList.add(...nativeClass.split(/\\s+/).filter(Boolean))\n button.dataset.wvgNativeClass = nativeClass\n button.dataset.wvgNativeIcon = '1'\n })\n }\n\n /** Apply grouping: headers, order, collapse and the new-group button. */\n function reconcile(): void {\n if (disposed) return\n // Do not render or allow edits before the overlay has loaded: persisting an\n // empty placeholder would wipe existing groups if the manual request failed.\n if (!loaded) {\n cleanupList()\n removeNewGroupButtons()\n return\n }\n ensureViewOptions()\n ensureNewGroupButton()\n if (viewMode !== 'virtual') {\n cleanupList()\n return\n }\n const nextList = findList()\n if (nextList !== list) {\n cleanupList()\n list = nextList\n }\n if (list === null) return\n const sections = sectionsOf(list)\n if (sections.length === 0) {\n cleanupList()\n return\n }\n // The official \"workspace tree\" mode nests projects by path. Flattening it\n // into virtual groups would destroy that hierarchy, so leave it untouched.\n // Capture the official indent before overwriting it; sections can be reused\n // across renders, so a WeakMap keeps the original per element.\n for (const section of sections) {\n if (!originalIndent.has(section)) originalIndent.set(section, section.style.getPropertyValue('--dsh-workspace-indent'))\n }\n const inOfficialTree = sections.some((section) => {\n const original = originalIndent.get(section) ?? ''\n if (Number.parseFloat(original) > 0) return true\n // A reused node whose inline indent no longer matches ours was changed by\n // the official tree view between reconciles.\n return section.dataset.wvgIndent === '1'\n && section.style.getPropertyValue('--dsh-workspace-indent') !== GROUP_INDENT\n })\n if (inOfficialTree) {\n cleanupList()\n return\n }\n\n markList(list)\n\n const members = new Map<string, HTMLElement[]>()\n for (const section of sections) {\n const workspaceId = sectionWorkspaceId(section)\n const key = workspaceId === undefined ? DEFAULT_GROUP : resolveCategory(manual, workspaceId)\n const bucket = members.get(key)\n if (bucket === undefined) members.set(key, [section])\n else bucket.push(section)\n }\n\n const groups = effectiveCategories(manual)\n\n const visible = new Set<string>()\n groups.forEach((entry, groupIndex) => {\n visible.add(entry.key)\n let header = groupedHeaders.get(entry.key)\n if (header === undefined || !header.isConnected) {\n header = createGroupHeader(entry.key)\n groupedHeaders.set(entry.key, header)\n list!.append(header)\n }\n header.style.order = String(groupIndex * 100000 - 1000)\n header.setAttribute('aria-expanded', collapsed.has(entry.key) ? 'false' : 'true')\n const label = header.querySelector<HTMLElement>('.wvg-label')\n if (label !== null) label.textContent = entry.key\n const items = members.get(entry.key) ?? []\n items.forEach((section, index) => {\n section.style.order = String(groupIndex * 100000 + index)\n section.style.display = collapsed.has(entry.key) ? 'none' : ''\n section.style.setProperty('--dsh-workspace-indent', GROUP_INDENT)\n section.dataset.wvgIndent = '1'\n })\n })\n for (const [key, header] of groupedHeaders) {\n if (visible.has(key)) continue\n header.remove()\n groupedHeaders.delete(key)\n }\n applyNativeIconClasses()\n }\n\n function toggleGroup(key: string): void {\n if (collapsed.has(key)) collapsed.delete(key)\n else collapsed.add(key)\n writeCollapsed(collapsed)\n schedule()\n }\n\n function onDragStart(event: DragEvent): void {\n const row = (event.target as HTMLElement | null)?.closest<HTMLElement>('[data-row-key^=\"workspace:\"]')\n if (row === null) return\n const key = row.getAttribute('data-row-key') ?? ''\n if (!key.startsWith('workspace:')) return\n dragId = key.slice('workspace:'.length)\n try {\n event.dataTransfer?.setData(MIME, dragId)\n if (event.dataTransfer !== null) event.dataTransfer.effectAllowed = 'move'\n } catch { /* some synthetic drags have no writable dataTransfer */ }\n }\n\n function clearDrag(): void {\n dragId = null\n draggingGroup = null\n document.querySelectorAll<HTMLElement>('.wvg-group.wvg-dragging, .wvg-group.wvg-drop, .wvg-group.wvg-insert-before, .wvg-group.wvg-insert-after')\n .forEach((header) => header.classList.remove('wvg-dragging', 'wvg-drop', 'wvg-insert-before', 'wvg-insert-after'))\n }\n\n function onGroupDragOver(event: DragEvent, header: HTMLElement): void {\n if (draggingGroup !== null) {\n const key = header.dataset.wvgGroup ?? ''\n if (key === DEFAULT_GROUP || key === draggingGroup) return\n event.preventDefault()\n event.stopPropagation()\n if (event.dataTransfer !== null) event.dataTransfer.dropEffect = 'move'\n const rect = header.getBoundingClientRect()\n const before = event.clientY < rect.top + rect.height / 2\n document.querySelectorAll<HTMLElement>('.wvg-group.wvg-insert-before, .wvg-group.wvg-insert-after')\n .forEach((node) => node.classList.remove('wvg-insert-before', 'wvg-insert-after'))\n header.classList.add(before ? 'wvg-insert-before' : 'wvg-insert-after')\n return\n }\n if (dragId === null) return\n event.preventDefault()\n event.stopPropagation()\n if (event.dataTransfer !== null) event.dataTransfer.dropEffect = 'move'\n header.classList.add('wvg-drop')\n }\n\n function onGroupDragLeave(event: DragEvent, header: HTMLElement): void {\n if (event.relatedTarget instanceof Node && header.contains(event.relatedTarget)) return\n header.classList.remove('wvg-drop', 'wvg-insert-before', 'wvg-insert-after')\n }\n\n function onGroupDrop(event: DragEvent, key: string, header: HTMLElement): void {\n if (draggingGroup !== null) {\n if (key === DEFAULT_GROUP || key === draggingGroup) return\n event.preventDefault()\n event.stopPropagation()\n const rect = header.getBoundingClientRect()\n const before = event.clientY < rect.top + rect.height / 2\n const dragged = draggingGroup\n clearDrag()\n void reorderGroup(dragged, key, before)\n return\n }\n if (dragId === null) return\n event.preventDefault()\n event.stopPropagation()\n const workspaceId = dragId\n clearDrag()\n header.classList.remove('wvg-drop')\n void moveToGroup(workspaceId, key)\n }\n\n function onDocumentDragStart(event: DragEvent): void { onDragStart(event) }\n function onDocumentDragEnd(): void { clearDrag() }\n\n async function addWorkspaceToGroup(key: string): Promise<void> {\n if (addingWorkspace) return\n addingWorkspace = true\n try {\n const path = await ctx.uiWorkspace.pickDirectory()\n if (path === null) return\n const workspace = await ctx.workspaces.create({ path })\n collapsed.delete(key)\n writeCollapsed(collapsed)\n await persist({\n ...manual,\n assignments: { ...manual.assignments, [workspace.workspaceId]: key === DEFAULT_GROUP ? null : key },\n })\n try {\n ctx.uiWorkspace.startSession(workspace.workspaceId)\n } catch (reason) {\n showToast(reason instanceof Error ? reason.message : String(reason))\n }\n } catch (reason) {\n showToast(reason instanceof Error ? reason.message : String(reason))\n } finally {\n addingWorkspace = false\n }\n }\n\n async function reorderGroup(draggedKey: string, targetKey: string, before: boolean): Promise<void> {\n if (draggedKey === targetKey || targetKey === DEFAULT_GROUP) return\n const order = effectiveCategories(manual)\n .map(entry => entry.key)\n .filter(key => key !== DEFAULT_GROUP)\n const categoryOrder = before\n ? moveBefore(order, draggedKey, targetKey)\n : moveAfter(order, draggedKey, targetKey)\n try {\n await persist({ ...manual, categoryOrder })\n } catch (reason) {\n showToast(reason instanceof Error ? reason.message : String(reason))\n }\n }\n\n async function moveToGroup(workspaceId: string, key: string): Promise<void> {\n const current = resolveCategory(manual, workspaceId)\n if (current === key) return\n collapsed.delete(key)\n writeCollapsed(collapsed)\n try {\n await persist({\n ...manual,\n assignments: { ...manual.assignments, [workspaceId]: key === DEFAULT_GROUP ? null : key },\n })\n } catch (reason) {\n showToast(reason instanceof Error ? reason.message : String(reason))\n }\n }\n\n /** Serialize overlay writes so two fast drags cannot race each other. */\n function persist(next: NormalizedManual): Promise<void> {\n const run = saveQueue.then(async () => {\n await putManual(next)\n manual = next\n schedule()\n })\n // Keep the queue alive after a rejection; the caller still sees the error.\n saveQueue = run.catch(() => {})\n return run\n }\n\n function requestDeleteGroup(key: string): void {\n openConfirmDialog('\u5220\u9664\u5206\u7EC4', `\u5220\u9664\u540E\uFF0C\u8BE5\u5206\u7EC4\u5185\u7684\u9879\u76EE\u4F1A\u79FB\u5230\u300C${DEFAULT_GROUP}\u300D\u3002`, async () => {\n await deleteGroup(key)\n })\n }\n\n /** Menu row matching DSH's context-menu item (16px icon + 13px label). */\n function menuItem(label: string, icon: string, danger = false): HTMLButtonElement {\n const button = document.createElement('button')\n button.type = 'button'\n button.className = danger ? 'wvg-menu-item wvg-danger' : 'wvg-menu-item'\n button.setAttribute('role', 'menuitem')\n button.innerHTML = `<span class=\"wvg-menu-icon\">${svgIcon(icon, 16)}</span><span class=\"wvg-menu-label\"></span>`\n const text = button.querySelector<HTMLElement>('.wvg-menu-label')\n if (text !== null) text.textContent = label\n return button\n }\n\n function openGroupMenu(key: string, anchor: HTMLElement, x: number, y: number): void {\n closeMenu()\n menuAnchor = anchor\n anchor.closest<HTMLElement>('.wvg-group')?.classList.add('wvg-menu-open')\n const panel = document.createElement('div')\n panel.className = 'wvg-menu'\n panel.setAttribute('role', 'menu')\n panel.setAttribute('aria-label', `\u5206\u7EC4\u201C${key}\u201D\u7684\u64CD\u4F5C`)\n const addWorkspace = menuItem('\u6DFB\u52A0\u5DE5\u4F5C\u533A', 'dsw-icon-project-add-outline-regular')\n addWorkspace.addEventListener('click', () => {\n closeMenu()\n void addWorkspaceToGroup(key)\n })\n panel.append(addWorkspace)\n if (key !== DEFAULT_GROUP) {\n const rename = menuItem('\u91CD\u547D\u540D\u5206\u7EC4', 'dsw-icon-edit-outline-regular')\n rename.addEventListener('click', () => {\n closeMenu()\n openNameDialog('\u91CD\u547D\u540D\u5206\u7EC4', key, '\u91CD\u547D\u540D', async (name) => {\n if (name === '' || name === DEFAULT_GROUP) return '\u8BE5\u540D\u79F0\u4E0D\u53EF\u7528'\n if (name === key) return null\n if (takenCategoryNames(manual).has(name)) return '\u5DF2\u5B58\u5728\u540C\u540D\u5206\u7EC4'\n await renameGroup(key, name)\n return null\n })\n })\n const separator = document.createElement('div')\n separator.className = 'wvg-menu-separator'\n separator.setAttribute('role', 'separator')\n const remove = menuItem('\u5220\u9664\u5206\u7EC4', 'dsw-icon-trash-outline-regular', true)\n remove.addEventListener('click', () => {\n closeMenu()\n requestDeleteGroup(key)\n })\n panel.append(rename, separator, remove)\n }\n document.body.append(panel)\n const rect = panel.getBoundingClientRect()\n panel.style.left = `${Math.max(8, Math.min(x, window.innerWidth - rect.width - 8))}px`\n panel.style.top = `${Math.max(8, Math.min(y, window.innerHeight - rect.height - 8))}px`\n menu = panel\n const close = (event: PointerEvent): void => {\n if (panel.contains(event.target as Node)) return\n closeMenu()\n }\n menuPointerClose = close\n window.setTimeout(() => {\n if (menuPointerClose === close) document.addEventListener('pointerdown', close, true)\n }, 0)\n menuKeyClose = (event: KeyboardEvent): void => {\n if (event.key === 'Escape') closeMenu()\n }\n document.addEventListener('keydown', menuKeyClose, true)\n panel.querySelector<HTMLButtonElement>('.wvg-menu-item')?.focus()\n }\n\n function closeMenu(): void {\n const openHeaders = [...document.querySelectorAll<HTMLElement>('.wvg-group.wvg-menu-open')]\n if (menuPointerClose !== null) document.removeEventListener('pointerdown', menuPointerClose, true)\n if (menuKeyClose !== null) document.removeEventListener('keydown', menuKeyClose, true)\n menuPointerClose = null\n menuKeyClose = null\n const anchor = menuAnchor\n menuAnchor = null\n menu?.remove()\n menu = null\n // Keep the hover-only trigger visible while returning focus to it; once\n // focus lands, :focus-within keeps it visible after the class is removed.\n if (anchor?.isConnected) anchor.focus()\n openHeaders.forEach((header) => header.classList.remove('wvg-menu-open'))\n }\n\n async function renameGroup(from: string, name: string): Promise<void> {\n const assignments = Object.fromEntries(\n Object.entries(manual.assignments).map(([id, category]): [string, string | null] => [\n id,\n category === from ? name : category,\n ]),\n )\n const categoryOrder = manual.categoryOrder.map(key => key === from ? name : key)\n await persist({\n ...manual,\n categories: manual.categories.map(key => key === from ? name : key),\n assignments,\n categoryOrder,\n })\n }\n\n async function deleteGroup(key: string): Promise<void> {\n if (key === DEFAULT_GROUP) return\n const assignments = { ...manual.assignments }\n for (const [workspaceId, category] of Object.entries(assignments)) {\n if (category === key) assignments[workspaceId] = null\n }\n await persist({\n ...manual,\n categories: manual.categories.filter(entry => entry !== key),\n assignments,\n categoryOrder: manual.categoryOrder.filter(entry => entry !== key),\n })\n }\n\n function openNameDialog(title: string, initial: string, confirmLabel: string, submit: (name: string) => Promise<string | null>): void {\n closeModal()\n const overlay = document.createElement('div')\n overlay.className = 'wvg-overlay'\n overlay.innerHTML = `<div class=\"wvg-dialog\" role=\"dialog\" aria-modal=\"true\" tabindex=\"-1\"><div class=\"wvg-dialog-content\"><div class=\"wvg-dialog-header\"><h2 class=\"wvg-dialog-title\"></h2><button type=\"button\" class=\"wvg-dialog-close\" aria-label=\"\u5173\u95ED\">${svgIcon('dsw-icon-close-outline-regular', 14)}</button></div><div class=\"wvg-dialog-body\"><input class=\"wvg-input\" type=\"text\" maxlength=\"80\" aria-label=\"\u5206\u7EC4\u540D\u79F0\" /><div class=\"wvg-error\" role=\"alert\"></div></div></div><div class=\"wvg-dialog-actions\"><button type=\"button\" class=\"wvg-button wvg-outline\" data-wvg-cancel>\u53D6\u6D88</button><button type=\"button\" class=\"wvg-button wvg-primary\" data-wvg-ok></button></div></div>`\n const dialog = overlay.querySelector<HTMLElement>('.wvg-dialog')!\n dialog.setAttribute('aria-label', title)\n dialog.querySelector<HTMLElement>('.wvg-dialog-title')!.textContent = title\n const input = dialog.querySelector<HTMLInputElement>('.wvg-input')!\n input.value = initial\n const error = dialog.querySelector<HTMLElement>('.wvg-error')!\n const ok = dialog.querySelector<HTMLButtonElement>('[data-wvg-ok]')!\n ok.textContent = confirmLabel\n let busy = false\n const initialTrimmed = initial.trim()\n const syncOk = (): void => {\n if (busy) return\n const value = input.value.trim()\n ok.disabled = value === '' || (initialTrimmed !== '' && value === initialTrimmed)\n }\n syncOk()\n const close = (): void => closeModal()\n const confirm = async (): Promise<void> => {\n if (busy || ok.disabled) return\n busy = true\n ok.disabled = true\n error.textContent = ''\n try {\n const message = await submit(input.value.trim())\n if (message !== null) {\n error.textContent = message\n return\n }\n close()\n } catch (reason) {\n error.textContent = reason instanceof Error ? reason.message : String(reason)\n } finally {\n busy = false\n syncOk()\n }\n }\n dialog.querySelector<HTMLElement>('[data-wvg-cancel]')!.addEventListener('click', close)\n dialog.querySelector<HTMLElement>('.wvg-dialog-close')!.addEventListener('click', close)\n input.addEventListener('input', syncOk)\n ok.addEventListener('click', () => { void confirm() })\n input.addEventListener('keydown', (event) => {\n if (event.key === 'Enter' && !event.isComposing) {\n event.preventDefault()\n void confirm()\n }\n })\n overlay.addEventListener('pointerdown', (event) => {\n if (event.target === overlay) close()\n })\n document.body.append(overlay)\n modal = overlay\n modalKeyHandler = (event: KeyboardEvent): void => {\n if (event.key === 'Escape') close()\n }\n document.addEventListener('keydown', modalKeyHandler)\n window.requestAnimationFrame(() => {\n input.focus()\n input.select()\n })\n }\n\n function openConfirmDialog(title: string, message: string, confirm: () => Promise<void>): void {\n closeModal()\n const overlay = document.createElement('div')\n overlay.className = 'wvg-overlay'\n overlay.innerHTML = `<div class=\"wvg-dialog\" role=\"dialog\" aria-modal=\"true\" tabindex=\"-1\"><div class=\"wvg-dialog-content\"><div class=\"wvg-dialog-header\"><h2 class=\"wvg-dialog-title\"></h2><button type=\"button\" class=\"wvg-dialog-close\" aria-label=\"\u5173\u95ED\">${svgIcon('dsw-icon-close-outline-regular', 14)}</button></div><div class=\"wvg-dialog-body\"><div class=\"wvg-dialog-message\"></div><div class=\"wvg-error\" role=\"alert\"></div></div></div><div class=\"wvg-dialog-actions\"><button type=\"button\" class=\"wvg-button wvg-outline\" data-wvg-cancel>\u53D6\u6D88</button><button type=\"button\" class=\"wvg-button wvg-danger\" data-wvg-ok>\u5220\u9664</button></div></div>`\n const dialog = overlay.querySelector<HTMLElement>('.wvg-dialog')!\n dialog.setAttribute('aria-label', title)\n dialog.querySelector<HTMLElement>('.wvg-dialog-title')!.textContent = title\n dialog.querySelector<HTMLElement>('.wvg-dialog-message')!.textContent = message\n const error = dialog.querySelector<HTMLElement>('.wvg-error')!\n const ok = dialog.querySelector<HTMLButtonElement>('[data-wvg-ok]')!\n let busy = false\n const close = (): void => closeModal()\n const run = async (): Promise<void> => {\n if (busy) return\n busy = true\n ok.disabled = true\n error.textContent = ''\n try {\n await confirm()\n close()\n } catch (reason) {\n error.textContent = reason instanceof Error ? reason.message : String(reason)\n } finally {\n busy = false\n ok.disabled = false\n }\n }\n dialog.querySelector<HTMLElement>('[data-wvg-cancel]')!.addEventListener('click', close)\n dialog.querySelector<HTMLElement>('.wvg-dialog-close')!.addEventListener('click', close)\n ok.addEventListener('click', () => { void run() })\n overlay.addEventListener('pointerdown', (event) => {\n if (event.target === overlay) close()\n })\n document.body.append(overlay)\n modal = overlay\n modalKeyHandler = (event: KeyboardEvent): void => {\n if (event.key === 'Escape') close()\n }\n document.addEventListener('keydown', modalKeyHandler)\n window.requestAnimationFrame(() => {\n dialog.querySelector<HTMLButtonElement>('[data-wvg-cancel]')?.focus()\n })\n }\n\n function closeModal(): void {\n if (modalKeyHandler !== null) document.removeEventListener('keydown', modalKeyHandler)\n modalKeyHandler = null\n modal?.remove()\n modal = null\n }\n\n function showToast(message: string): void {\n document.querySelectorAll('.wvg-toast').forEach((node) => node.remove())\n const toast = document.createElement('div')\n toast.className = 'wvg-toast'\n toast.textContent = message\n document.body.append(toast)\n window.clearTimeout(toastTimer)\n toastTimer = window.setTimeout(() => toast.remove(), 3200)\n }\n\n // Initial data, then the official DOM is enhanced whenever it changes.\n void loadState().then((state) => {\n if (disposed) return\n manual = state\n viewMode = manual.mode\n loaded = true\n schedule()\n }).catch((reason: unknown) => {\n if (disposed) return\n loaded = false\n console.warn('workspace virtual groups: manual overlay unavailable:', reason)\n showToast('\u5206\u7EC4\u6570\u636E\u52A0\u8F7D\u5931\u8D25\uFF0C\u865A\u62DF\u5206\u7EC4\u5DF2\u6682\u65F6\u505C\u7528\u3002\u8BF7\u5237\u65B0\u91CD\u8BD5\u3002')\n schedule()\n })\n\n // The conversation streams below the sidebar; observing every mutation would\n // reconcile once per frame for no reason. Only schedule when the sidebar\n // list, its header actions, or an official/injected menu is touched.\n const OBSERVED_SELECTOR = [\n '[role=\"tree\"]',\n '[role=\"menu\"]',\n '[class*=\"viewOptionsMenu\"]',\n '[class*=\"sectionHeader\"]',\n '[class*=\"headerActions\"]',\n '[data-wvg-new-group]',\n '[data-wvg-view-wrap]',\n ].join(',')\n const mutationRelevant = (record: MutationRecord): boolean => {\n if (record.target instanceof Element && record.target.closest(OBSERVED_SELECTOR) !== null) return true\n for (const node of record.addedNodes) {\n if (node instanceof Element && (node.matches(OBSERVED_SELECTOR) || node.querySelector(OBSERVED_SELECTOR) !== null)) return true\n }\n for (const node of record.removedNodes) {\n if (node instanceof Element && (node.matches(OBSERVED_SELECTOR) || node.querySelector(OBSERVED_SELECTOR) !== null)) return true\n }\n return !(record.target instanceof Element)\n }\n const observer = new MutationObserver((records) => {\n if (records.some(mutationRelevant)) schedule()\n })\n observer.observe(document.body, { childList: true, subtree: true })\n document.addEventListener('dragstart', onDocumentDragStart, true)\n document.addEventListener('dragend', onDocumentDragEnd, true)\n schedule()\n\n return () => {\n disposed = true\n observer.disconnect()\n document.removeEventListener('dragstart', onDocumentDragStart, true)\n document.removeEventListener('dragend', onDocumentDragEnd, true)\n if (frame !== 0) window.cancelAnimationFrame(frame)\n window.clearTimeout(toastTimer)\n closeMenu()\n closeViewOptions()\n closeModal()\n cleanupList()\n removeNewGroupButtons()\n document.querySelectorAll('[data-wvg-view-wrap]').forEach((node) => node.remove())\n style.remove()\n }\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC6CO,IAAM,gBAAgB;;;ACxBtB,SAAS,oBAAoB,QAAuD;AACzF,QAAM,UAA+B,CAAC,EAAE,KAAK,eAAe,QAAQ,UAAU,CAAC;AAC/E,QAAM,OAAO,oBAAI,IAAY,CAAC,aAAa,CAAC;AAC5C,aAAW,QAAQ,QAAQ,cAAc,CAAC,GAAG;AAC3C,QAAI,KAAK,IAAI,IAAI,EAAG;AACpB,SAAK,IAAI,IAAI;AACb,YAAQ,KAAK,EAAE,KAAK,MAAM,QAAQ,SAAS,CAAC;AAAA,EAC9C;AACA,QAAM,QAAQ,QAAQ;AACtB,MAAI,UAAU,UAAa,MAAM,WAAW,EAAG,QAAO;AACtD,QAAM,WAAW,IAAI,IAAI,MAAM,IAAI,CAAC,KAAK,UAAU,CAAC,KAAK,KAAK,CAAC,CAAC;AAChE,QAAM,CAAC,cAAc,GAAG,IAAI,IAAI;AAChC,OAAK,KAAK,CAAC,GAAG,MAAM;AAClB,UAAM,KAAK,SAAS,IAAI,EAAE,GAAG;AAC7B,UAAM,KAAK,SAAS,IAAI,EAAE,GAAG;AAC7B,QAAI,OAAO,UAAa,OAAO,OAAW,QAAO,KAAK;AACtD,QAAI,OAAO,OAAW,QAAO;AAC7B,QAAI,OAAO,OAAW,QAAO;AAC7B,WAAO;AAAA,EACT,CAAC;AACD,SAAO,CAAC,cAAe,GAAG,IAAI;AAChC;AAGO,SAAS,oBAAoB,QAA4C;AAC9E,SAAO,oBAAoB,MAAM,EAAE,IAAI,WAAS,MAAM,GAAG;AAC3D;AAGO,SAAS,gBAAgB,QAAkC,aAA6B;AAC7F,QAAM,aAAa,QAAQ,YAAY,WAAW;AAClD,MAAI,eAAe,UAAa,eAAe,KAAM,QAAO;AAC5D,SAAO,QAAQ,WAAW,SAAS,UAAU,IAAI,aAAa;AAChE;AAGO,SAAS,mBAAmB,QAA+C;AAChF,SAAO,IAAI,IAAY,oBAAoB,MAAM,CAAC;AACpD;AAGO,SAAS,WAAW,MAAyB,IAAY,UAAwC;AACtG,QAAM,OAAO,KAAK,OAAO,OAAK,MAAM,EAAE;AACtC,MAAI,aAAa,OAAW,QAAO,CAAC,GAAG,MAAM,EAAE;AAC/C,QAAM,QAAQ,KAAK,QAAQ,QAAQ;AACnC,MAAI,UAAU,GAAI,QAAO,CAAC,GAAG,MAAM,EAAE;AACrC,OAAK,OAAO,OAAO,GAAG,EAAE;AACxB,SAAO;AACT;AAGO,SAAS,UAAU,MAAyB,IAAY,SAAuC;AACpG,QAAM,OAAO,KAAK,OAAO,OAAK,MAAM,EAAE;AACtC,MAAI,YAAY,OAAW,QAAO,CAAC,GAAG,MAAM,EAAE;AAC9C,QAAM,QAAQ,KAAK,QAAQ,OAAO;AAClC,MAAI,UAAU,GAAI,QAAO,CAAC,GAAG,MAAM,EAAE;AACrC,OAAK,OAAO,QAAQ,GAAG,GAAG,EAAE;AAC5B,SAAO;AACT;;;AC/EA,IAAO,iBAAQ;;;ACyBf,IAAM,OAAO;AACb,IAAM,aAAa;AAGnB,IAAM,aAAa;AAEnB,IAAM,WAAW;AAAA,EACf,WAAW;AAAA,IACT,MAAM;AAAA,IACN,QAAQ,CAAC,4BAAQ,cAAc;AAAA,EACjC;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ,CAAC,kCAAS,mBAAmB;AAAA,EACvC;AAAA,EACA,UAAU;AAAA,IACR,MAAM;AAAA,IACN,QAAQ,CAAC,sBAAO,WAAW;AAAA,EAC7B;AACF;AACA,IAAM,0BAA0B,IAAI;AAAA,EAClC,OAAO,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU,MAAM,IAAI;AACnD;AACA,IAAM,2BAA2B,IAAI;AAAA,EACnC,OAAO,OAAO,QAAQ,EAAE,QAAQ,CAAC,UAAU,MAAM,MAAM;AACzD;AACA,IAAM,eAAe;AACrB,IAAM,gBAAgB;AAEtB,IAAM,eAAe;AAIrB,IAAM,eAAiC;AAAA,EACrC,YAAY,CAAC;AAAA,EACb,aAAa,CAAC;AAAA,EACd,eAAe,CAAC;AAAA,EAChB,MAAM;AACR;AAGA,SAAS,gBAAgB,QAAoD;AAC3E,MAAI,WAAW,OAAW,QAAO;AACjC,QAAM,aAAa,CAAC,GAAG,IAAI,IAAI,OAAO,UAAU,CAAC,EAC9C,OAAO,UAAQ,SAAS,aAAa;AACxC,QAAM,cAAc,IAAI,IAAI,UAAU;AACtC,QAAM,cAAc,OAAO;AAAA,IACzB,OAAO,QAAQ,OAAO,WAAW,EAAE,IAAI,CAAC,CAAC,IAAI,QAAQ,MAA+B;AAAA,MAClF;AAAA,MACA,aAAa,QAAQ,CAAC,YAAY,IAAI,QAAQ,IAAI,OAAO;AAAA,IAC3D,CAAC;AAAA,EACH;AACA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,gBAAgB,OAAO,iBAAiB,CAAC,GAAG,OAAO,SAAO,YAAY,IAAI,GAAG,CAAC;AAAA,IAC9E,MAAM,OAAO,SAAS,aAAa,aAAa;AAAA,EAClD;AACF;AAGA,eAAe,YAAuC;AACpD,QAAM,WAAW,MAAM,MAAM,cAAc,EAAE,OAAO,WAAW,CAAC;AAChE,MAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,0BAA0B,SAAS,MAAM,EAAE;AAC7E,SAAO,gBAAiB,MAAM,SAAS,KAAK,CAAkB;AAChE;AAGA,eAAe,UAAU,QAAyC;AAChE,QAAM,WAAW,MAAM,MAAM,cAAc;AAAA,IACzC,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C,MAAM,KAAK,UAAU,MAAM;AAAA,EAC7B,CAAC;AACD,MAAI,SAAS,GAAI;AACjB,MAAI,UAAU,iCAAQ,SAAS,MAAM;AACrC,MAAI;AACF,UAAM,OAAO,MAAM,SAAS,KAAK;AACjC,QAAI,SAAS,GAAI,WAAU;AAAA,EAC7B,QAAQ;AAAA,EAA0B;AAClC,QAAM,IAAI,MAAM,OAAO;AACzB;AAEA,SAAS,gBAA6B;AACpC,MAAI;AACF,UAAM,MAAM,OAAO,aAAa,QAAQ,aAAa;AACrD,QAAI,QAAQ,KAAM,QAAO,oBAAI,IAAI;AACjC,UAAM,SAAkB,KAAK,MAAM,GAAG;AACtC,WAAO,IAAI,IAAI,MAAM,QAAQ,MAAM,IAAI,OAAO,OAAO,CAAC,QAAuB,OAAO,QAAQ,QAAQ,IAAI,CAAC,CAAC;AAAA,EAC5G,QAAQ;AACN,WAAO,oBAAI,IAAI;AAAA,EACjB;AACF;AAEA,SAAS,eAAe,WAAsC;AAC5D,MAAI;AACF,WAAO,aAAa,QAAQ,eAAe,KAAK,UAAU,CAAC,GAAG,SAAS,CAAC,CAAC;AAAA,EAC3E,QAAQ;AAAA,EAAkE;AAC5E;AAEA,SAAS,QAAQ,IAAY,MAAsB;AACjD,SAAO,eAAe,IAAI,aAAa,IAAI,oCAAoC,EAAE;AACnF;AAQO,SAAS,4BAA4B,KAQ7B;AACb,QAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,QAAM,QAAQ,SAAS;AACvB,QAAM,cAAc;AACpB,WAAS,KAAK,OAAO,KAAK;AAE1B,MAAI,WAAW;AACf,MAAI,SAA2B;AAC/B,MAAI,SAAS;AACb,MAAI,OAA2B;AAC/B,MAAI,WAAmC;AACvC,MAAI,wBAAwB;AAC5B,MAAI,oBAA8B,CAAC;AACnC,MAAI,gBAA+B;AACnC,MAAI,kBAAkB;AACtB,MAAI,QAAQ;AACZ,MAAI,OAA2B;AAC/B,MAAI,aAAiC;AACrC,MAAI,mBAA2D;AAC/D,MAAI,eAAwD;AAC5D,MAAI,QAA4B;AAChC,MAAI,kBAA2D;AAC/D,MAAI,aAAa;AACjB,MAAI,SAAwB;AAC5B,MAAI,gBAA+B;AACnC,MAAI,YAA2B,QAAQ,QAAQ;AAC/C,QAAM,iBAAiB,oBAAI,IAAyB;AACpD,QAAM,YAAY,cAAc;AAEhC,QAAM,iBAAiB,oBAAI,QAA6B;AACxD,QAAM,eAAe,oBAAI,QAAiG;AAC1H,QAAM,oBAAoB,oBAAI,QAA4E;AAC1G,QAAM,uBAAuB,oBAAI,IAAiB;AAGlD,WAAS,WAAiB;AACxB,QAAI,YAAY,UAAU,EAAG;AAC7B,YAAQ,OAAO,sBAAsB,MAAM;AACzC,cAAQ;AACR,UAAI,CAAC,SAAU,WAAU;AAAA,IAC3B,CAAC;AAAA,EACH;AAGA,WAAS,WAA+B;AACtC,UAAM,MAAM,SAAS,cAA2B,8BAA8B;AAC9E,WAAO,KAAK,QAAqB,eAAe,KAAK;AAAA,EACvD;AAGA,WAAS,WAAW,QAAoC;AACtD,WAAO,MAAM,KAAK,OAAO,QAAQ,EAAE;AAAA,MAAO,CAAC,YACzC,mBAAmB,eAChB,QAAQ,cAAc,qCAAqC,MAAM;AAAA,IACtE;AAAA,EACF;AAEA,WAAS,mBAAmB,SAA0C;AACpE,UAAM,MAAM,QAAQ,cAA2B,8BAA8B,GAAG,aAAa,cAAc,KAAK;AAChH,WAAO,IAAI,WAAW,YAAY,IAAI,IAAI,MAAM,aAAa,MAAM,IAAI;AAAA,EACzE;AAGA,WAAS,SAAS,QAA2B;AAC3C,WAAO,UAAU,IAAI,UAAU;AAC/B,WAAO,QAAQ,UAAU;AAAA,EAC3B;AAEA,WAAS,cAAc,QAA2B;AAChD,eAAW,WAAW,WAAW,MAAM,GAAG;AACxC,cAAQ,MAAM,QAAQ;AACtB,cAAQ,MAAM,UAAU;AACxB,YAAM,WAAW,eAAe,IAAI,OAAO;AAI3C,UAAI,aAAa,UAAa,QAAQ,MAAM,iBAAiB,wBAAwB,MAAM,cAAc;AACvG,YAAI,aAAa,GAAI,SAAQ,MAAM,eAAe,wBAAwB;AAAA,YACrE,SAAQ,MAAM,YAAY,0BAA0B,QAAQ;AAAA,MACnE;AACA,qBAAe,OAAO,OAAO;AAC7B,aAAO,QAAQ,QAAQ;AAAA,IACzB;AAAA,EACF;AAEA,WAAS,eAAqB;AAC5B,eAAW,UAAU,eAAe,OAAO,EAAG,QAAO,OAAO;AAC5D,mBAAe,MAAM;AAAA,EACvB;AAEA,WAAS,cAAoB;AAC3B,QAAI,SAAS,KAAM;AACnB,SAAK,UAAU,OAAO,UAAU;AAChC,WAAO,KAAK,QAAQ;AACpB,kBAAc,IAAI;AAClB,iBAAa;AAAA,EACf;AAGA,WAAS,wBAA8B;AACrC,mBAAe,4BAAQ,IAAI,gBAAM,OAAO,SAAS;AAC/C,YAAM,QAAQ,mBAAmB,MAAM;AACvC,UAAI,SAAS,MAAM,SAAS,cAAe,QAAO;AAClD,UAAI,MAAM,IAAI,IAAI,EAAG,QAAO;AAC5B,YAAM,QAAQ,EAAE,GAAG,QAAQ,YAAY,CAAC,GAAG,OAAO,YAAY,IAAI,EAAE,CAAC;AACrE,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAEA,WAAS,oBAA8C;AACrD,UAAM,MAAM,SAAS,cAA6B,mDAAmD;AACrG,UAAM,SAAS,KAAK,QAAQ,QAAQ;AACpC,WAAO,kBAAkB,oBAAoB,SAAS;AAAA,EACxD;AAGA,WAAS,qBAAqB,SAAmC;AAC/D,QAAI,YAAY,KAAM;AACtB,UAAM,WAAW,kBAAkB,IAAI,OAAO;AAC9C,QAAI,aAAa,OAAW;AAC5B,YAAQ,MAAM,QAAQ,SAAS;AAC/B,YAAQ,MAAM,WAAW,SAAS;AAClC,YAAQ,MAAM,WAAW,SAAS;AAClC,sBAAkB,OAAO,OAAO;AAChC,yBAAqB,OAAO,OAAO;AAAA,EACrC;AAEA,WAAS,wBAA8B;AACrC,aAAS,iBAA8B,sBAAsB,EAAE,QAAQ,CAAC,SAAS;AAC/E,YAAM,UAAU,KAAK;AACrB,WAAK,OAAO;AACZ,2BAAqB,OAAO;AAAA,IAC9B,CAAC;AACD,eAAW,WAAW,CAAC,GAAG,oBAAoB,EAAG,sBAAqB,OAAO;AAAA,EAC/E;AAGA,WAAS,uBAA6B;AACpC,QAAI,aAAa,WAAW;AAC1B,4BAAsB;AACtB;AAAA,IACF;AACA,UAAM,MAAM,kBAAkB;AAC9B,QAAI,QAAQ,MAAM;AAChB,4BAAsB;AACtB;AAAA,IACF;AACA,QAAI,SAAS,SAAS,cAAiC,4BAA4B;AACnF,QAAI,WAAW,QAAQ,CAAC,OAAO,aAAa;AAC1C,eAAS,IAAI,UAAU,IAAI;AAC3B,aAAO,QAAQ,cAAc;AAC7B,UAAI,MAAM,MAAM;AAAA,IAClB;AACA,QAAI,OAAO,2BAA2B,IAAK,KAAI,MAAM,MAAM;AAC3D,WAAO,aAAa,cAAc,0BAAM;AACxC,WAAO,gBAAgB,mBAAmB;AAC1C,WAAO,gBAAgB,eAAe;AACtC,QAAI,OAAO,cAAc,yBAAyB,MAAM,KAAM,QAAO,YAAY;AACjF,WAAO,UAAU,CAAC,UAAU;AAC1B,YAAM,eAAe;AACrB,YAAM,gBAAgB;AACtB,4BAAsB;AAAA,IACxB;AAIA,UAAM,UAAU,OAAO;AACvB,QAAI,YAAY,MAAM;AACpB,UAAI,CAAC,kBAAkB,IAAI,OAAO,GAAG;AACnC,0BAAkB,IAAI,SAAS;AAAA,UAC7B,OAAO,QAAQ,MAAM;AAAA,UACrB,UAAU,QAAQ,MAAM;AAAA,UACxB,UAAU,QAAQ,MAAM;AAAA,QAC1B,CAAC;AACD,6BAAqB,IAAI,OAAO;AAAA,MAClC;AACA,cAAQ,MAAM,QAAQ;AACtB,cAAQ,MAAM,WAAW;AACzB,cAAQ,MAAM,WAAW;AAAA,IAC3B;AAAA,EACF;AAEA,WAAS,aAAa,QAA0C;AAC9D,UAAM,MAAM,OAAO,cAA0B,8BAA8B;AAC3E,WAAO,KAAK,aAAa,WAAW,KAAK;AAAA,EAC3C;AAEA,WAAS,cAAc,QAAmC;AACxD,YAAQ,OAAO,eAAe,IAAI,KAAK;AAAA,EACzC;AAEA,WAAS,yBAAyB,QAAoC;AACpE,UAAM,OAAO,aAAa,MAAM;AAChC,QAAI,SAAS,QAAQ,wBAAwB,IAAI,IAAI,EAAG,QAAO;AAE/D,WAAO,yBAAyB,IAAI,cAAc,MAAM,CAAC;AAAA,EAC3D;AAEA,WAAS,mBACPA,OACA,OAC0B;AAC1B,UAAM,UAAU,CAAC,GAAGA,MAAK,iBAAoC,yBAAyB,CAAC;AACvF,UAAM,SAA4B,MAAM;AACxC,WAAO,QAAQ,KAAK,CAAC,WAAW,aAAa,MAAM,MAAM,MAAM,IAAI,KAC9D,QAAQ,KAAK,CAAC,WAAW,OAAO,SAAS,cAAc,MAAM,CAAC,CAAC,KAC/D;AAAA,EACP;AAEA,WAAS,qBAAqBA,OAA2B;AACvD,UAAM,YAAY,mBAAmBA,OAAM,SAAS,SAAS;AAC7D,UAAM,QAAQ,cAAc,OAAO,KAAK,cAAc,SAAS;AAC/D,WAAO,MAAM,SAAS,QAAG,KAAK,MAAM,SAAS,oBAAK,IAAI,mCAAU;AAAA,EAClE;AAEA,WAAS,sBAA0C;AACjD,eAAWA,SAAQ,SAAS,iBAA8B,eAAe,GAAG;AAE1E,UAAIA,MAAK,UAAU,SAAS,iBAAiB,EAAG,QAAOA;AAEvD,UAAI,CAAC,GAAGA,MAAK,iBAAoC,yBAAyB,CAAC,EAAE,KAAK,wBAAwB,GAAG;AAC3G,eAAOA;AAAA,MACT;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,WAAS,mBAAyB;AAChC,QAAI,oBAAoB,MAAM,KAAM;AACpC,UAAM,MAAM,SAAS,cAA6B,mDAAmD;AACrG,UAAM,UAAU,KAAK,QAAQ,QAAQ;AACrC,QAAI,mBAAmB,kBAAmB,SAAQ,MAAM;AAAA,QACnD,UAAS,cAAc,IAAI,cAAc,WAAW,EAAE,KAAK,UAAU,SAAS,KAAK,CAAC,CAAC;AAAA,EAC5F;AAGA,WAAS,oBAA0B;AACjC,UAAMA,QAAO,oBAAoB;AACjC,QAAIA,UAAS,KAAM;AACnB,UAAM,aAAa;AACnB,QAAI,OAAOA,MAAK,cAAiC,+BAA+B;AAChF,QAAI,SAAS,MAAM;AACjB,YAAM,OAAO,mBAAmBA,OAAM,SAAS,IAAI;AACnD,YAAM,OAAO,MAAM;AACnB,UAAI,SAAS,QAAQ,SAAS,UAAa,SAAS,KAAM;AAC1D,YAAM,WAAW,CAAC,GAAGA,MAAK,iBAAoC,yBAAyB,CAAC,EAAE,OAAO,UAAU;AAC3G,YAAM,aAAa,SAAS,KAAK,CAACC,YAAWA,QAAO,cAAc,cAAc,MAAM,IAAI,KAAK;AAC/F,YAAM,WAAW,SAAS,KAAK,CAACA,YAAWA,QAAO,cAAc,cAAc,MAAM,IAAI;AACxF,YAAM,YAAY,WAAW;AAC7B,YAAM,gBAAgB,aAAa,SAC/B,CAAC,IACD,CAAC,GAAG,SAAS,SAAS,EAAE,OAAO,CAAC,SAAS,CAAC,WAAW,UAAU,SAAS,IAAI,CAAC;AACjF,YAAM,YAAY,UAAU,cAAc,cAAc,GAAG,aAAa;AACxE,UAAI,cAAc,SAAS,EAAG,qBAAoB;AAClD,UAAI,cAAc,KAAM,iBAAgB;AACxC,YAAM,QAAQ,KAAK,UAAU,IAAI;AACjC,YAAM,QAAQ,cAAc;AAC5B,YAAM,SAAS,MAAM,cAAiC,yBAAyB;AAC/E,UAAI,WAAW,KAAM;AACrB,aAAO,QAAQ,iBAAiB;AAChC,aAAO,YAAY;AACnB,YAAM,QAAQ,CAAC,GAAG,OAAO,iBAAiB,eAAe,CAAC,EACvD,KAAK,CAAC,SAAS,KAAK,cAAc,KAAK,MAAM,SAAS,KAAK,eAAe,IAAI,KAAK,MAAM,EAAE;AAC9F,UAAI,UAAU,OAAW,OAAM,cAAc,qBAAqBD,KAAI;AACtE,YAAM,OAAO,OAAO,cAA2B,qBAAqB,KAC/D,CAAC,GAAG,OAAO,iBAAiB,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,KAAK,cAAc,KAAK,MAAM,IAAI;AAC3F,UAAI,SAAS,OAAW,MAAK,YAAY;AACzC,aAAO,cAAc,cAAc,GAAG,OAAO;AAC7C,WAAK,MAAM,KAAK;AAChB,mBAAa,IAAI,QAAQ;AAAA,QACvB;AAAA,QACA,eAAe,cAAc,SAAS,IAAI,gBAAgB;AAAA,QAC1D,OAAO,aAAa;AAAA,MACtB,CAAC;AACD,aAAO,iBAAiB,SAAS,CAAC,UAAU;AAC1C,cAAM,eAAe;AACrB,cAAM,gBAAgB;AACtB,4BAAoBA,KAAI;AAAA,MAC1B,CAAC;AACD,aAAO;AAAA,IACT;AAEA,QAAIA,MAAK,QAAQ,oBAAoB,KAAK;AACxC,MAAAA,MAAK,QAAQ,kBAAkB;AAC/B,MAAAA,MAAK,iBAAiB,SAAS,CAAC,UAAU;AACxC,YAAI,sBAAuB;AAC3B,cAAM,SAAU,MAAM,QAA2B,QAA2B,yBAAyB;AACrG,YAAI,WAAW,QAAQ,WAAW,OAAW;AAC7C,YAAI,CAAC,WAAW,MAAM,EAAG;AACzB,oBAAY,UAAU;AAAA,MACxB,GAAG,IAAI;AAAA,IACT;AAEA,UAAM,OAAO,aAAa,IAAI,IAAI,KAC7B,EAAE,WAAW,KAAK,WAAW,eAAe,mBAAmB,OAAO,cAAc;AACzF,QAAI,aAAa,WAAW;AAC1B,iBAAW,UAAUA,MAAK,iBAAoC,yBAAyB,GAAG;AACxF,YAAI,WAAW,QAAQ,CAAC,WAAW,MAAM,EAAG;AAC5C,YAAI,KAAK,cAAc,SAAS,EAAG,QAAO,UAAU,OAAO,GAAG,KAAK,aAAa;AAChF,eAAO,cAAc,cAAc,GAAG,OAAO;AAAA,MAC/C;AACA,WAAK,YAAY,KAAK,cAAc,SAAS,IACzC,GAAG,KAAK,SAAS,IAAI,KAAK,cAAc,KAAK,GAAG,CAAC,KACjD,KAAK;AACT,UAAI,KAAK,UAAU,QAAQ,KAAK,cAAc,cAAc,MAAM,MAAM;AACtE,cAAM,SAAS,SAAS,cAAc,UAAU;AAChD,eAAO,YAAY,KAAK;AACxB,cAAM,QAAQ,OAAO,QAAQ;AAC7B,YAAI,UAAU,KAAM,MAAK,OAAO,KAAK;AAAA,MACvC;AAAA,IACF,OAAO;AACL,WAAK,YAAY,KAAK;AACtB,WAAK,cAAc,cAAc,GAAG,OAAO;AAAA,IAC7C;AAAA,EACF;AAEA,WAAS,YAAY,MAAoC;AACvD,QAAI,CAAC,OAAQ;AACb,QAAI,aAAa,MAAM;AACrB,eAAS;AACT;AAAA,IACF;AACA,eAAW;AACX,aAAS,EAAE,GAAG,QAAQ,KAAK;AAC3B,aAAS;AACT,SAAK,QAAQ,MAAM,EAAE,MAAM,CAAC,WAAW;AACrC,gBAAU,kBAAkB,QAAQ,OAAO,UAAU,OAAO,MAAM,CAAC;AAAA,IACrE,CAAC;AAAA,EACH;AAEA,WAAS,oBAAoBA,OAAyB;AACpD,gBAAY,SAAS;AACrB,UAAM,cAAc,mBAAmBA,OAAM,SAAS,SAAS;AAC/D,QAAI,gBAAgB,MAAM;AACxB,uBAAiB;AACjB;AAAA,IACF;AAIA,4BAAwB;AACxB,gBAAY,MAAM;AAClB,WAAO,WAAW,MAAM;AACtB,8BAAwB;AACxB,UAAIA,MAAK,YAAa,kBAAiB;AAAA,IACzC,GAAG,CAAC;AAAA,EACN;AAEA,WAAS,kBAAkB,KAA0B;AACnD,UAAM,SAAS,SAAS,cAAc,KAAK;AAC3C,WAAO,YAAY;AACnB,WAAO,QAAQ,WAAW;AAC1B,WAAO,QAAQ,aAAa,QAAQ,gBAAgB,MAAM;AAC1D,WAAO,aAAa,QAAQ,UAAU;AACtC,WAAO,aAAa,iBAAiB,MAAM;AAC3C,WAAO,aAAa,cAAc,GAAG;AACrC,WAAO,YAAY,QAAQ;AAC3B,WAAO,YAAY,6BAA6B,QAAQ,wCAAwC,EAAE,CAAC,2IAAuH,QAAQ,qCAAqC,EAAE,CAAC;AAE1Q,WAAO,cAA2B,WAAW,GAAG,aAAa,cAAc,qBAAM,GAAG,0BAAM;AAC1F,WAAO,iBAAiB,aAAa,CAAC,UAAU;AAC9C,UAAI,QAAQ,eAAe;AACzB,cAAM,eAAe;AACrB;AAAA,MACF;AACA,UAAK,MAAM,QAA+B,QAAQ,WAAW,MAAM,MAAM;AACvE,cAAM,eAAe;AACrB;AAAA,MACF;AACA,sBAAgB;AAChB,UAAI;AACF,cAAM,cAAc,QAAQ,YAAY,GAAG;AAC3C,YAAI,MAAM,iBAAiB,KAAM,OAAM,aAAa,gBAAgB;AAAA,MACtE,QAAQ;AAAA,MAAuB;AAC/B,aAAO,UAAU,IAAI,cAAc;AAAA,IACrC,CAAC;AACD,WAAO,iBAAiB,WAAW,MAAM;AAAE,gBAAU;AAAA,IAAE,CAAC;AACxD,WAAO,iBAAiB,SAAS,CAAC,UAAU;AAC1C,UAAK,MAAM,QAA+B,QAAQ,WAAW,MAAM,KAAM;AACzE,kBAAY,GAAG;AAAA,IACjB,CAAC;AACD,WAAO,iBAAiB,eAAe,CAAC,UAAU;AAChD,YAAM,eAAe;AACrB,oBAAc,KAAK,QAAQ,MAAM,SAAS,MAAM,OAAO;AAAA,IACzD,CAAC;AACD,WAAO,iBAAiB,YAAY,CAAC,UAAU,gBAAgB,OAAO,MAAM,CAAC;AAC7E,WAAO,iBAAiB,aAAa,CAAC,UAAU,iBAAiB,OAAO,MAAM,CAAC;AAC/E,WAAO,iBAAiB,QAAQ,CAAC,UAAU,YAAY,OAAO,KAAK,MAAM,CAAC;AAC1E,WAAO,cAA2B,WAAW,GAAG,iBAAiB,SAAS,CAAC,UAAU;AACnF,YAAM,gBAAgB;AACtB,YAAM,OAAQ,MAAM,cAA8B,sBAAsB;AACxE,oBAAc,KAAK,MAAM,eAA8B,KAAK,MAAM,KAAK,SAAS,CAAC;AAAA,IACnF,CAAC;AACD,WAAO;AAAA,EACT;AAGA,WAAS,yBAA+B;AACtC,UAAM,YAAY,SAAS,cAAiC,0DAA0D;AACtH,UAAM,cAAc,WAAW,aAAa,kBAAkB,GAAG,aAAa;AAC9E,QAAI,gBAAgB,GAAI;AACxB,aAAS,iBAAoC,WAAW,EAAE,QAAQ,CAAC,WAAW;AAC5E,YAAM,WAAW,OAAO,QAAQ,kBAAkB;AAClD,UAAI,aAAa,YAAa;AAC9B,iBAAW,QAAQ,SAAS,MAAM,KAAK,EAAE,OAAO,OAAO,EAAG,QAAO,UAAU,OAAO,IAAI;AACtF,aAAO,UAAU,IAAI,GAAG,YAAY,MAAM,KAAK,EAAE,OAAO,OAAO,CAAC;AAChE,aAAO,QAAQ,iBAAiB;AAChC,aAAO,QAAQ,gBAAgB;AAAA,IACjC,CAAC;AAAA,EACH;AAGA,WAAS,YAAkB;AACzB,QAAI,SAAU;AAGd,QAAI,CAAC,QAAQ;AACX,kBAAY;AACZ,4BAAsB;AACtB;AAAA,IACF;AACA,sBAAkB;AAClB,yBAAqB;AACrB,QAAI,aAAa,WAAW;AAC1B,kBAAY;AACZ;AAAA,IACF;AACA,UAAM,WAAW,SAAS;AAC1B,QAAI,aAAa,MAAM;AACrB,kBAAY;AACZ,aAAO;AAAA,IACT;AACA,QAAI,SAAS,KAAM;AACnB,UAAM,WAAW,WAAW,IAAI;AAChC,QAAI,SAAS,WAAW,GAAG;AACzB,kBAAY;AACZ;AAAA,IACF;AAKA,eAAW,WAAW,UAAU;AAC9B,UAAI,CAAC,eAAe,IAAI,OAAO,EAAG,gBAAe,IAAI,SAAS,QAAQ,MAAM,iBAAiB,wBAAwB,CAAC;AAAA,IACxH;AACA,UAAM,iBAAiB,SAAS,KAAK,CAAC,YAAY;AAChD,YAAM,WAAW,eAAe,IAAI,OAAO,KAAK;AAChD,UAAI,OAAO,WAAW,QAAQ,IAAI,EAAG,QAAO;AAG5C,aAAO,QAAQ,QAAQ,cAAc,OAChC,QAAQ,MAAM,iBAAiB,wBAAwB,MAAM;AAAA,IACpE,CAAC;AACD,QAAI,gBAAgB;AAClB,kBAAY;AACZ;AAAA,IACF;AAEA,aAAS,IAAI;AAEb,UAAM,UAAU,oBAAI,IAA2B;AAC/C,eAAW,WAAW,UAAU;AAC9B,YAAM,cAAc,mBAAmB,OAAO;AAC9C,YAAM,MAAM,gBAAgB,SAAY,gBAAgB,gBAAgB,QAAQ,WAAW;AAC3F,YAAM,SAAS,QAAQ,IAAI,GAAG;AAC9B,UAAI,WAAW,OAAW,SAAQ,IAAI,KAAK,CAAC,OAAO,CAAC;AAAA,UAC/C,QAAO,KAAK,OAAO;AAAA,IAC1B;AAEA,UAAM,SAAS,oBAAoB,MAAM;AAEzC,UAAM,UAAU,oBAAI,IAAY;AAChC,WAAO,QAAQ,CAAC,OAAO,eAAe;AACpC,cAAQ,IAAI,MAAM,GAAG;AACrB,UAAI,SAAS,eAAe,IAAI,MAAM,GAAG;AACzC,UAAI,WAAW,UAAa,CAAC,OAAO,aAAa;AAC/C,iBAAS,kBAAkB,MAAM,GAAG;AACpC,uBAAe,IAAI,MAAM,KAAK,MAAM;AACpC,aAAM,OAAO,MAAM;AAAA,MACrB;AACA,aAAO,MAAM,QAAQ,OAAO,aAAa,MAAS,GAAI;AACtD,aAAO,aAAa,iBAAiB,UAAU,IAAI,MAAM,GAAG,IAAI,UAAU,MAAM;AAChF,YAAM,QAAQ,OAAO,cAA2B,YAAY;AAC5D,UAAI,UAAU,KAAM,OAAM,cAAc,MAAM;AAC9C,YAAM,QAAQ,QAAQ,IAAI,MAAM,GAAG,KAAK,CAAC;AACzC,YAAM,QAAQ,CAAC,SAAS,UAAU;AAChC,gBAAQ,MAAM,QAAQ,OAAO,aAAa,MAAS,KAAK;AACxD,gBAAQ,MAAM,UAAU,UAAU,IAAI,MAAM,GAAG,IAAI,SAAS;AAC5D,gBAAQ,MAAM,YAAY,0BAA0B,YAAY;AAChE,gBAAQ,QAAQ,YAAY;AAAA,MAC9B,CAAC;AAAA,IACH,CAAC;AACD,eAAW,CAAC,KAAK,MAAM,KAAK,gBAAgB;AAC1C,UAAI,QAAQ,IAAI,GAAG,EAAG;AACtB,aAAO,OAAO;AACd,qBAAe,OAAO,GAAG;AAAA,IAC3B;AACA,2BAAuB;AAAA,EACzB;AAEA,WAAS,YAAY,KAAmB;AACtC,QAAI,UAAU,IAAI,GAAG,EAAG,WAAU,OAAO,GAAG;AAAA,QACvC,WAAU,IAAI,GAAG;AACtB,mBAAe,SAAS;AACxB,aAAS;AAAA,EACX;AAEA,WAAS,YAAY,OAAwB;AAC3C,UAAM,MAAO,MAAM,QAA+B,QAAqB,8BAA8B;AACrG,QAAI,QAAQ,KAAM;AAClB,UAAM,MAAM,IAAI,aAAa,cAAc,KAAK;AAChD,QAAI,CAAC,IAAI,WAAW,YAAY,EAAG;AACnC,aAAS,IAAI,MAAM,aAAa,MAAM;AACtC,QAAI;AACF,YAAM,cAAc,QAAQ,MAAM,MAAM;AACxC,UAAI,MAAM,iBAAiB,KAAM,OAAM,aAAa,gBAAgB;AAAA,IACtE,QAAQ;AAAA,IAA2D;AAAA,EACrE;AAEA,WAAS,YAAkB;AACzB,aAAS;AACT,oBAAgB;AAChB,aAAS,iBAA8B,yGAAyG,EAC7I,QAAQ,CAAC,WAAW,OAAO,UAAU,OAAO,gBAAgB,YAAY,qBAAqB,kBAAkB,CAAC;AAAA,EACrH;AAEA,WAAS,gBAAgB,OAAkB,QAA2B;AACpE,QAAI,kBAAkB,MAAM;AAC1B,YAAM,MAAM,OAAO,QAAQ,YAAY;AACvC,UAAI,QAAQ,iBAAiB,QAAQ,cAAe;AACpD,YAAM,eAAe;AACrB,YAAM,gBAAgB;AACtB,UAAI,MAAM,iBAAiB,KAAM,OAAM,aAAa,aAAa;AACjE,YAAM,OAAO,OAAO,sBAAsB;AAC1C,YAAM,SAAS,MAAM,UAAU,KAAK,MAAM,KAAK,SAAS;AACxD,eAAS,iBAA8B,2DAA2D,EAC/F,QAAQ,CAAC,SAAS,KAAK,UAAU,OAAO,qBAAqB,kBAAkB,CAAC;AACnF,aAAO,UAAU,IAAI,SAAS,sBAAsB,kBAAkB;AACtE;AAAA,IACF;AACA,QAAI,WAAW,KAAM;AACrB,UAAM,eAAe;AACrB,UAAM,gBAAgB;AACtB,QAAI,MAAM,iBAAiB,KAAM,OAAM,aAAa,aAAa;AACjE,WAAO,UAAU,IAAI,UAAU;AAAA,EACjC;AAEA,WAAS,iBAAiB,OAAkB,QAA2B;AACrE,QAAI,MAAM,yBAAyB,QAAQ,OAAO,SAAS,MAAM,aAAa,EAAG;AACjF,WAAO,UAAU,OAAO,YAAY,qBAAqB,kBAAkB;AAAA,EAC7E;AAEA,WAAS,YAAY,OAAkB,KAAa,QAA2B;AAC7E,QAAI,kBAAkB,MAAM;AAC1B,UAAI,QAAQ,iBAAiB,QAAQ,cAAe;AACpD,YAAM,eAAe;AACrB,YAAM,gBAAgB;AACtB,YAAM,OAAO,OAAO,sBAAsB;AAC1C,YAAM,SAAS,MAAM,UAAU,KAAK,MAAM,KAAK,SAAS;AACxD,YAAM,UAAU;AAChB,gBAAU;AACV,WAAK,aAAa,SAAS,KAAK,MAAM;AACtC;AAAA,IACF;AACA,QAAI,WAAW,KAAM;AACrB,UAAM,eAAe;AACrB,UAAM,gBAAgB;AACtB,UAAM,cAAc;AACpB,cAAU;AACV,WAAO,UAAU,OAAO,UAAU;AAClC,SAAK,YAAY,aAAa,GAAG;AAAA,EACnC;AAEA,WAAS,oBAAoB,OAAwB;AAAE,gBAAY,KAAK;AAAA,EAAE;AAC1E,WAAS,oBAA0B;AAAE,cAAU;AAAA,EAAE;AAEjD,iBAAe,oBAAoB,KAA4B;AAC7D,QAAI,gBAAiB;AACrB,sBAAkB;AAClB,QAAI;AACF,YAAM,OAAO,MAAM,IAAI,YAAY,cAAc;AACjD,UAAI,SAAS,KAAM;AACnB,YAAM,YAAY,MAAM,IAAI,WAAW,OAAO,EAAE,KAAK,CAAC;AACtD,gBAAU,OAAO,GAAG;AACpB,qBAAe,SAAS;AACxB,YAAM,QAAQ;AAAA,QACZ,GAAG;AAAA,QACH,aAAa,EAAE,GAAG,OAAO,aAAa,CAAC,UAAU,WAAW,GAAG,QAAQ,gBAAgB,OAAO,IAAI;AAAA,MACpG,CAAC;AACD,UAAI;AACF,YAAI,YAAY,aAAa,UAAU,WAAW;AAAA,MACpD,SAAS,QAAQ;AACf,kBAAU,kBAAkB,QAAQ,OAAO,UAAU,OAAO,MAAM,CAAC;AAAA,MACrE;AAAA,IACF,SAAS,QAAQ;AACf,gBAAU,kBAAkB,QAAQ,OAAO,UAAU,OAAO,MAAM,CAAC;AAAA,IACrE,UAAE;AACA,wBAAkB;AAAA,IACpB;AAAA,EACF;AAEA,iBAAe,aAAa,YAAoB,WAAmB,QAAgC;AACjG,QAAI,eAAe,aAAa,cAAc,cAAe;AAC7D,UAAM,QAAQ,oBAAoB,MAAM,EACrC,IAAI,WAAS,MAAM,GAAG,EACtB,OAAO,SAAO,QAAQ,aAAa;AACtC,UAAM,gBAAgB,SAClB,WAAW,OAAO,YAAY,SAAS,IACvC,UAAU,OAAO,YAAY,SAAS;AAC1C,QAAI;AACF,YAAM,QAAQ,EAAE,GAAG,QAAQ,cAAc,CAAC;AAAA,IAC5C,SAAS,QAAQ;AACf,gBAAU,kBAAkB,QAAQ,OAAO,UAAU,OAAO,MAAM,CAAC;AAAA,IACrE;AAAA,EACF;AAEA,iBAAe,YAAY,aAAqB,KAA4B;AAC1E,UAAM,UAAU,gBAAgB,QAAQ,WAAW;AACnD,QAAI,YAAY,IAAK;AACrB,cAAU,OAAO,GAAG;AACpB,mBAAe,SAAS;AACxB,QAAI;AACF,YAAM,QAAQ;AAAA,QACZ,GAAG;AAAA,QACH,aAAa,EAAE,GAAG,OAAO,aAAa,CAAC,WAAW,GAAG,QAAQ,gBAAgB,OAAO,IAAI;AAAA,MAC1F,CAAC;AAAA,IACH,SAAS,QAAQ;AACf,gBAAU,kBAAkB,QAAQ,OAAO,UAAU,OAAO,MAAM,CAAC;AAAA,IACrE;AAAA,EACF;AAGA,WAAS,QAAQ,MAAuC;AACtD,UAAM,MAAM,UAAU,KAAK,YAAY;AACrC,YAAM,UAAU,IAAI;AACpB,eAAS;AACT,eAAS;AAAA,IACX,CAAC;AAED,gBAAY,IAAI,MAAM,MAAM;AAAA,IAAC,CAAC;AAC9B,WAAO;AAAA,EACT;AAEA,WAAS,mBAAmB,KAAmB;AAC7C,sBAAkB,4BAAQ,6FAAkB,aAAa,gBAAM,YAAY;AACzE,YAAM,YAAY,GAAG;AAAA,IACvB,CAAC;AAAA,EACH;AAGA,WAAS,SAAS,OAAe,MAAc,SAAS,OAA0B;AAChF,UAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,WAAO,OAAO;AACd,WAAO,YAAY,SAAS,6BAA6B;AACzD,WAAO,aAAa,QAAQ,UAAU;AACtC,WAAO,YAAY,+BAA+B,QAAQ,MAAM,EAAE,CAAC;AACnE,UAAM,OAAO,OAAO,cAA2B,iBAAiB;AAChE,QAAI,SAAS,KAAM,MAAK,cAAc;AACtC,WAAO;AAAA,EACT;AAEA,WAAS,cAAc,KAAa,QAAqB,GAAW,GAAiB;AACnF,cAAU;AACV,iBAAa;AACb,WAAO,QAAqB,YAAY,GAAG,UAAU,IAAI,eAAe;AACxE,UAAM,QAAQ,SAAS,cAAc,KAAK;AAC1C,UAAM,YAAY;AAClB,UAAM,aAAa,QAAQ,MAAM;AACjC,UAAM,aAAa,cAAc,qBAAM,GAAG,0BAAM;AAChD,UAAM,eAAe,SAAS,kCAAS,sCAAsC;AAC7E,iBAAa,iBAAiB,SAAS,MAAM;AAC3C,gBAAU;AACV,WAAK,oBAAoB,GAAG;AAAA,IAC9B,CAAC;AACD,UAAM,OAAO,YAAY;AACzB,QAAI,QAAQ,eAAe;AACzB,YAAM,SAAS,SAAS,kCAAS,+BAA+B;AAChE,aAAO,iBAAiB,SAAS,MAAM;AACrC,kBAAU;AACV,uBAAe,kCAAS,KAAK,sBAAO,OAAO,SAAS;AAClD,cAAI,SAAS,MAAM,SAAS,cAAe,QAAO;AAClD,cAAI,SAAS,IAAK,QAAO;AACzB,cAAI,mBAAmB,MAAM,EAAE,IAAI,IAAI,EAAG,QAAO;AACjD,gBAAM,YAAY,KAAK,IAAI;AAC3B,iBAAO;AAAA,QACT,CAAC;AAAA,MACH,CAAC;AACD,YAAM,YAAY,SAAS,cAAc,KAAK;AAC9C,gBAAU,YAAY;AACtB,gBAAU,aAAa,QAAQ,WAAW;AAC1C,YAAM,SAAS,SAAS,4BAAQ,kCAAkC,IAAI;AACtE,aAAO,iBAAiB,SAAS,MAAM;AACrC,kBAAU;AACV,2BAAmB,GAAG;AAAA,MACxB,CAAC;AACD,YAAM,OAAO,QAAQ,WAAW,MAAM;AAAA,IACxC;AACA,aAAS,KAAK,OAAO,KAAK;AAC1B,UAAM,OAAO,MAAM,sBAAsB;AACzC,UAAM,MAAM,OAAO,GAAG,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,OAAO,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC;AAClF,UAAM,MAAM,MAAM,GAAG,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,OAAO,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC;AACnF,WAAO;AACP,UAAM,QAAQ,CAAC,UAA8B;AAC3C,UAAI,MAAM,SAAS,MAAM,MAAc,EAAG;AAC1C,gBAAU;AAAA,IACZ;AACA,uBAAmB;AACnB,WAAO,WAAW,MAAM;AACtB,UAAI,qBAAqB,MAAO,UAAS,iBAAiB,eAAe,OAAO,IAAI;AAAA,IACtF,GAAG,CAAC;AACJ,mBAAe,CAAC,UAA+B;AAC7C,UAAI,MAAM,QAAQ,SAAU,WAAU;AAAA,IACxC;AACA,aAAS,iBAAiB,WAAW,cAAc,IAAI;AACvD,UAAM,cAAiC,gBAAgB,GAAG,MAAM;AAAA,EAClE;AAEA,WAAS,YAAkB;AACzB,UAAM,cAAc,CAAC,GAAG,SAAS,iBAA8B,0BAA0B,CAAC;AAC1F,QAAI,qBAAqB,KAAM,UAAS,oBAAoB,eAAe,kBAAkB,IAAI;AACjG,QAAI,iBAAiB,KAAM,UAAS,oBAAoB,WAAW,cAAc,IAAI;AACrF,uBAAmB;AACnB,mBAAe;AACf,UAAM,SAAS;AACf,iBAAa;AACb,UAAM,OAAO;AACb,WAAO;AAGP,QAAI,QAAQ,YAAa,QAAO,MAAM;AACtC,gBAAY,QAAQ,CAAC,WAAW,OAAO,UAAU,OAAO,eAAe,CAAC;AAAA,EAC1E;AAEA,iBAAe,YAAY,MAAc,MAA6B;AACpE,UAAM,cAAc,OAAO;AAAA,MACzB,OAAO,QAAQ,OAAO,WAAW,EAAE,IAAI,CAAC,CAAC,IAAI,QAAQ,MAA+B;AAAA,QAClF;AAAA,QACA,aAAa,OAAO,OAAO;AAAA,MAC7B,CAAC;AAAA,IACH;AACA,UAAM,gBAAgB,OAAO,cAAc,IAAI,SAAO,QAAQ,OAAO,OAAO,GAAG;AAC/E,UAAM,QAAQ;AAAA,MACZ,GAAG;AAAA,MACH,YAAY,OAAO,WAAW,IAAI,SAAO,QAAQ,OAAO,OAAO,GAAG;AAAA,MAClE;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAEA,iBAAe,YAAY,KAA4B;AACrD,QAAI,QAAQ,cAAe;AAC3B,UAAM,cAAc,EAAE,GAAG,OAAO,YAAY;AAC5C,eAAW,CAAC,aAAa,QAAQ,KAAK,OAAO,QAAQ,WAAW,GAAG;AACjE,UAAI,aAAa,IAAK,aAAY,WAAW,IAAI;AAAA,IACnD;AACA,UAAM,QAAQ;AAAA,MACZ,GAAG;AAAA,MACH,YAAY,OAAO,WAAW,OAAO,WAAS,UAAU,GAAG;AAAA,MAC3D;AAAA,MACA,eAAe,OAAO,cAAc,OAAO,WAAS,UAAU,GAAG;AAAA,IACnE,CAAC;AAAA,EACH;AAEA,WAAS,eAAe,OAAe,SAAiB,cAAsB,QAAwD;AACpI,eAAW;AACX,UAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,YAAQ,YAAY;AACpB,YAAQ,YAAY,mPAAyO,QAAQ,kCAAkC,EAAE,CAAC;AAC1S,UAAM,SAAS,QAAQ,cAA2B,aAAa;AAC/D,WAAO,aAAa,cAAc,KAAK;AACvC,WAAO,cAA2B,mBAAmB,EAAG,cAAc;AACtE,UAAM,QAAQ,OAAO,cAAgC,YAAY;AACjE,UAAM,QAAQ;AACd,UAAM,QAAQ,OAAO,cAA2B,YAAY;AAC5D,UAAM,KAAK,OAAO,cAAiC,eAAe;AAClE,OAAG,cAAc;AACjB,QAAI,OAAO;AACX,UAAM,iBAAiB,QAAQ,KAAK;AACpC,UAAM,SAAS,MAAY;AACzB,UAAI,KAAM;AACV,YAAM,QAAQ,MAAM,MAAM,KAAK;AAC/B,SAAG,WAAW,UAAU,MAAO,mBAAmB,MAAM,UAAU;AAAA,IACpE;AACA,WAAO;AACP,UAAM,QAAQ,MAAY,WAAW;AACrC,UAAM,UAAU,YAA2B;AACzC,UAAI,QAAQ,GAAG,SAAU;AACzB,aAAO;AACP,SAAG,WAAW;AACd,YAAM,cAAc;AACpB,UAAI;AACF,cAAM,UAAU,MAAM,OAAO,MAAM,MAAM,KAAK,CAAC;AAC/C,YAAI,YAAY,MAAM;AACpB,gBAAM,cAAc;AACpB;AAAA,QACF;AACA,cAAM;AAAA,MACR,SAAS,QAAQ;AACf,cAAM,cAAc,kBAAkB,QAAQ,OAAO,UAAU,OAAO,MAAM;AAAA,MAC9E,UAAE;AACA,eAAO;AACP,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO,cAA2B,mBAAmB,EAAG,iBAAiB,SAAS,KAAK;AACvF,WAAO,cAA2B,mBAAmB,EAAG,iBAAiB,SAAS,KAAK;AACvF,UAAM,iBAAiB,SAAS,MAAM;AACtC,OAAG,iBAAiB,SAAS,MAAM;AAAE,WAAK,QAAQ;AAAA,IAAE,CAAC;AACrD,UAAM,iBAAiB,WAAW,CAAC,UAAU;AAC3C,UAAI,MAAM,QAAQ,WAAW,CAAC,MAAM,aAAa;AAC/C,cAAM,eAAe;AACrB,aAAK,QAAQ;AAAA,MACf;AAAA,IACF,CAAC;AACD,YAAQ,iBAAiB,eAAe,CAAC,UAAU;AACjD,UAAI,MAAM,WAAW,QAAS,OAAM;AAAA,IACtC,CAAC;AACD,aAAS,KAAK,OAAO,OAAO;AAC5B,YAAQ;AACR,sBAAkB,CAAC,UAA+B;AAChD,UAAI,MAAM,QAAQ,SAAU,OAAM;AAAA,IACpC;AACA,aAAS,iBAAiB,WAAW,eAAe;AACpD,WAAO,sBAAsB,MAAM;AACjC,YAAM,MAAM;AACZ,YAAM,OAAO;AAAA,IACf,CAAC;AAAA,EACH;AAEA,WAAS,kBAAkB,OAAe,SAAiB,SAAoC;AAC7F,eAAW;AACX,UAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,YAAQ,YAAY;AACpB,YAAQ,YAAY,mPAAyO,QAAQ,kCAAkC,EAAE,CAAC;AAC1S,UAAM,SAAS,QAAQ,cAA2B,aAAa;AAC/D,WAAO,aAAa,cAAc,KAAK;AACvC,WAAO,cAA2B,mBAAmB,EAAG,cAAc;AACtE,WAAO,cAA2B,qBAAqB,EAAG,cAAc;AACxE,UAAM,QAAQ,OAAO,cAA2B,YAAY;AAC5D,UAAM,KAAK,OAAO,cAAiC,eAAe;AAClE,QAAI,OAAO;AACX,UAAM,QAAQ,MAAY,WAAW;AACrC,UAAM,MAAM,YAA2B;AACrC,UAAI,KAAM;AACV,aAAO;AACP,SAAG,WAAW;AACd,YAAM,cAAc;AACpB,UAAI;AACF,cAAM,QAAQ;AACd,cAAM;AAAA,MACR,SAAS,QAAQ;AACf,cAAM,cAAc,kBAAkB,QAAQ,OAAO,UAAU,OAAO,MAAM;AAAA,MAC9E,UAAE;AACA,eAAO;AACP,WAAG,WAAW;AAAA,MAChB;AAAA,IACF;AACA,WAAO,cAA2B,mBAAmB,EAAG,iBAAiB,SAAS,KAAK;AACvF,WAAO,cAA2B,mBAAmB,EAAG,iBAAiB,SAAS,KAAK;AACvF,OAAG,iBAAiB,SAAS,MAAM;AAAE,WAAK,IAAI;AAAA,IAAE,CAAC;AACjD,YAAQ,iBAAiB,eAAe,CAAC,UAAU;AACjD,UAAI,MAAM,WAAW,QAAS,OAAM;AAAA,IACtC,CAAC;AACD,aAAS,KAAK,OAAO,OAAO;AAC5B,YAAQ;AACR,sBAAkB,CAAC,UAA+B;AAChD,UAAI,MAAM,QAAQ,SAAU,OAAM;AAAA,IACpC;AACA,aAAS,iBAAiB,WAAW,eAAe;AACpD,WAAO,sBAAsB,MAAM;AACjC,aAAO,cAAiC,mBAAmB,GAAG,MAAM;AAAA,IACtE,CAAC;AAAA,EACH;AAEA,WAAS,aAAmB;AAC1B,QAAI,oBAAoB,KAAM,UAAS,oBAAoB,WAAW,eAAe;AACrF,sBAAkB;AAClB,WAAO,OAAO;AACd,YAAQ;AAAA,EACV;AAEA,WAAS,UAAU,SAAuB;AACxC,aAAS,iBAAiB,YAAY,EAAE,QAAQ,CAAC,SAAS,KAAK,OAAO,CAAC;AACvE,UAAM,QAAQ,SAAS,cAAc,KAAK;AAC1C,UAAM,YAAY;AAClB,UAAM,cAAc;AACpB,aAAS,KAAK,OAAO,KAAK;AAC1B,WAAO,aAAa,UAAU;AAC9B,iBAAa,OAAO,WAAW,MAAM,MAAM,OAAO,GAAG,IAAI;AAAA,EAC3D;AAGA,OAAK,UAAU,EAAE,KAAK,CAAC,UAAU;AAC/B,QAAI,SAAU;AACd,aAAS;AACT,eAAW,OAAO;AAClB,aAAS;AACT,aAAS;AAAA,EACX,CAAC,EAAE,MAAM,CAAC,WAAoB;AAC5B,QAAI,SAAU;AACd,aAAS;AACT,YAAQ,KAAK,yDAAyD,MAAM;AAC5E,cAAU,wJAA2B;AACrC,aAAS;AAAA,EACX,CAAC;AAKD,QAAM,oBAAoB;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,GAAG;AACV,QAAM,mBAAmB,CAAC,WAAoC;AAC5D,QAAI,OAAO,kBAAkB,WAAW,OAAO,OAAO,QAAQ,iBAAiB,MAAM,KAAM,QAAO;AAClG,eAAW,QAAQ,OAAO,YAAY;AACpC,UAAI,gBAAgB,YAAY,KAAK,QAAQ,iBAAiB,KAAK,KAAK,cAAc,iBAAiB,MAAM,MAAO,QAAO;AAAA,IAC7H;AACA,eAAW,QAAQ,OAAO,cAAc;AACtC,UAAI,gBAAgB,YAAY,KAAK,QAAQ,iBAAiB,KAAK,KAAK,cAAc,iBAAiB,MAAM,MAAO,QAAO;AAAA,IAC7H;AACA,WAAO,EAAE,OAAO,kBAAkB;AAAA,EACpC;AACA,QAAM,WAAW,IAAI,iBAAiB,CAAC,YAAY;AACjD,QAAI,QAAQ,KAAK,gBAAgB,EAAG,UAAS;AAAA,EAC/C,CAAC;AACD,WAAS,QAAQ,SAAS,MAAM,EAAE,WAAW,MAAM,SAAS,KAAK,CAAC;AAClE,WAAS,iBAAiB,aAAa,qBAAqB,IAAI;AAChE,WAAS,iBAAiB,WAAW,mBAAmB,IAAI;AAC5D,WAAS;AAET,SAAO,MAAM;AACX,eAAW;AACX,aAAS,WAAW;AACpB,aAAS,oBAAoB,aAAa,qBAAqB,IAAI;AACnE,aAAS,oBAAoB,WAAW,mBAAmB,IAAI;AAC/D,QAAI,UAAU,EAAG,QAAO,qBAAqB,KAAK;AAClD,WAAO,aAAa,UAAU;AAC9B,cAAU;AACV,qBAAiB;AACjB,eAAW;AACX,gBAAY;AACZ,0BAAsB;AACtB,aAAS,iBAAiB,sBAAsB,EAAE,QAAQ,CAAC,SAAS,KAAK,OAAO,CAAC;AACjF,UAAM,OAAO;AAAA,EACf;AACF;;;AJtjCO,IAAM,SAAS,CAAC,cAAc,aAAa;AAG3C,SAAS,MAAM,KAA0B;AAC9C,MAAI;AAAA,IACF,MAAM,4BAA4B,GAAmE;AAAA,IACrG;AAAA,EACF;AACF;",
6
6
  "names": ["menu", "button"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lkzc19/dsh-workspace-virtual-groups",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Virtual workspace groups for the DSH web sidebar: a built-in default group, group create/rename/delete, drag-and-drop project grouping and ordering.",
5
5
  "keywords": [
6
6
  "dsh",