dsh-desktop-kit 0.0.0-stage → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (269) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +152 -2
  3. package/client.js +4101 -0
  4. package/cordis.patch.yml +5 -0
  5. package/docs/ARCHITECTURE.md +114 -0
  6. package/docs/COMPATIBILITY.md +61 -0
  7. package/docs/INSTALL.md +84 -0
  8. package/docs/PRIVACY.md +50 -0
  9. package/docs/ROADMAP.md +57 -0
  10. package/docs/TOKENS.md +118 -0
  11. package/docs/TROUBLESHOOTING.md +68 -0
  12. package/docs/images/hero.png +0 -0
  13. package/index.js +84 -0
  14. package/package.json +70 -4
  15. package/plugins/dsh-gkb-ru-ui/cordis.patch.yml +6 -0
  16. package/plugins/dsh-gkb-ru-ui/lib/client.js +1331 -0
  17. package/plugins/dsh-gkb-ru-ui/lib/index.js +186 -0
  18. package/plugins/dsh-gkb-ru-ui/lib/skills.js +570 -0
  19. package/plugins/dsh-gkb-ru-ui/package.json +28 -0
  20. package/plugins/dsh-gkb-ru-ui/screenshots/01-home-tiles-1440-light.png +0 -0
  21. package/plugins/dsh-gkb-ru-ui/screenshots/02-skills-1440-light.png +0 -0
  22. package/plugins/dsh-gkb-ru-ui/screenshots/03-skills-editor-1440-light.png +0 -0
  23. package/plugins/dsh-gkb-ru-ui/screenshots/04-skills-1440-dark.png +0 -0
  24. package/plugins/dsh-gkb-ru-ui/screenshots/05-skills-390-light.png +0 -0
  25. package/plugins/dsh-gkb-ru-ui/screenshots/06-home-tiles-1440-dark.png +0 -0
  26. package/plugins/dsh-peak-guard/README.md +70 -0
  27. package/plugins/dsh-peak-guard/client.js +741 -0
  28. package/plugins/dsh-peak-guard/cordis.patch.yml +5 -0
  29. package/plugins/dsh-peak-guard/icon.svg +12 -0
  30. package/plugins/dsh-peak-guard/index.js +2 -0
  31. package/plugins/dsh-peak-guard/locale/en.json +6 -0
  32. package/plugins/dsh-peak-guard/locale/ru.json +6 -0
  33. package/plugins/dsh-peak-guard/locale/zh.json +6 -0
  34. package/plugins/dsh-peak-guard/package.json +33 -0
  35. package/plugins/dsh-peak-guard/screenshots/01-real-time-peak-start.png +0 -0
  36. package/plugins/dsh-peak-guard/screenshots/02-real-time-peak-end.png +0 -0
  37. package/plugins/dsh-peak-guard/screenshots/03-peak-card-1440-light.png +0 -0
  38. package/plugins/dsh-peak-guard/screenshots/04-peak-card-1440-dark.png +0 -0
  39. package/plugins/dsh-peak-guard/screenshots/05-peak-card-390-light.png +0 -0
  40. package/plugins/dsh-peak-guard/screenshots/05-peak-card-768-light.png +0 -0
  41. package/plugins/dsh-peak-guard/screenshots/06-peak-card-390-light.png +0 -0
  42. package/plugins/dsh-peak-guard/screenshots/07-live-preview-check.png +0 -0
  43. package/plugins/dsh-peak-guard/screenshots/07-pre-peak-1440-light.png +0 -0
  44. package/plugins/dsh-peak-guard/screenshots/08-peak-start-1440-light.png +0 -0
  45. package/plugins/dsh-peak-guard/screenshots/09-peak-end-1440-light.png +0 -0
  46. package/plugins/dsh-peak-guard/screenshots/10-pre-peak-card-1440-light.png +0 -0
  47. package/plugins/dsh-peak-guard/screenshots/11-pre-peak-card-1440-dark.png +0 -0
  48. package/plugins/dsh-peak-guard/screenshots/12-pre-peak-card-390-light.png +0 -0
  49. package/plugins/dsh-peak-guard/screenshots/qa-report.json +74 -0
  50. package/plugins/dsh-profile-transfer/README.md +62 -0
  51. package/plugins/dsh-profile-transfer/bootstrap/01-INSTALL.cmd +7 -0
  52. package/plugins/dsh-profile-transfer/bootstrap/README-ARCHIVE.md +54 -0
  53. package/plugins/dsh-profile-transfer/bootstrap/README-/320/243/320/241/320/242/320/220/320/235/320/236/320/222/320/232/320/220.md +42 -0
  54. package/plugins/dsh-profile-transfer/bootstrap/install-on-clean-machine.ps1 +119 -0
  55. package/plugins/dsh-profile-transfer/bootstrap//320/243/320/241/320/242/320/220/320/235/320/236/320/222/320/230/320/242/320/254.cmd +5 -0
  56. package/plugins/dsh-profile-transfer/client.js +806 -0
  57. package/plugins/dsh-profile-transfer/cordis.patch.yml +3 -0
  58. package/plugins/dsh-profile-transfer/icon.svg +13 -0
  59. package/plugins/dsh-profile-transfer/index.js +375 -0
  60. package/plugins/dsh-profile-transfer/locale/en.json +6 -0
  61. package/plugins/dsh-profile-transfer/locale/ru.json +6 -0
  62. package/plugins/dsh-profile-transfer/locale/zh.json +6 -0
  63. package/plugins/dsh-profile-transfer/package.json +36 -0
  64. package/plugins/dsh-profile-transfer/screenshots/01-settings-transfer-1440-light.png +0 -0
  65. package/plugins/dsh-profile-transfer/screenshots/02-settings-transfer-preview-1440-light.png +0 -0
  66. package/plugins/dsh-profile-transfer/screenshots/03-settings-transfer-1440-dark.png +0 -0
  67. package/plugins/dsh-profile-transfer/screenshots/04-settings-transfer-390-light.png +0 -0
  68. package/plugins/dsh-profile-transfer/screenshots/05-settings-transfer-768-light.png +0 -0
  69. package/plugins/dsh-profile-transfer/screenshots/06-settings-transfer-390-dark.png +0 -0
  70. package/plugins/dsh-profile-transfer/screenshots/07-settings-transfer-export-result.png +0 -0
  71. package/plugins/dsh-profile-transfer/screenshots/08-settings-transfer-workspaces-1440-light.png +0 -0
  72. package/plugins/dsh-profile-transfer/screenshots/09-settings-transfer-workspaces-1440-dark.png +0 -0
  73. package/plugins/dsh-profile-transfer/screenshots/10-settings-transfer-workspaces-390-light.png +0 -0
  74. package/plugins/dsh-profile-transfer/transfer.js +1004 -0
  75. package/plugins/dsh-project-memory/README.md +71 -0
  76. package/plugins/dsh-project-memory/cordis.patch.yml +4 -0
  77. package/plugins/dsh-project-memory/icon.svg +12 -0
  78. package/plugins/dsh-project-memory/index.js +1240 -0
  79. package/plugins/dsh-project-memory/locale/en.json +6 -0
  80. package/plugins/dsh-project-memory/locale/ru.json +6 -0
  81. package/plugins/dsh-project-memory/locale/zh.json +6 -0
  82. package/plugins/dsh-project-memory/package.json +26 -0
  83. package/plugins/dsh-session-move/README.md +67 -0
  84. package/plugins/dsh-session-move/client.js +719 -0
  85. package/plugins/dsh-session-move/cordis.patch.yml +5 -0
  86. package/plugins/dsh-session-move/icon.svg +14 -0
  87. package/plugins/dsh-session-move/index.js +497 -0
  88. package/plugins/dsh-session-move/locale/en.json +6 -0
  89. package/plugins/dsh-session-move/locale/ru.json +6 -0
  90. package/plugins/dsh-session-move/locale/zh.json +6 -0
  91. package/plugins/dsh-session-move/package.json +31 -0
  92. package/plugins/dsh-session-move/screenshots/01-row-menu-1440-light.png +0 -0
  93. package/plugins/dsh-session-move/screenshots/02-dialog-1440-light.png +0 -0
  94. package/plugins/dsh-session-move/screenshots/03-dialog-390-light.png +0 -0
  95. package/plugins/dsh-session-move/screenshots/04-dialog-1440-dark.png +0 -0
  96. package/plugins/dsh-session-move/screenshots/05-drag-highlight-1440-light.png +0 -0
  97. package/plugins/dsh-session-move/screenshots/06-drop-ungrouped-highlight.png +0 -0
  98. package/plugins/dsh-session-move/screenshots/07-after-drop-ungrouped.png +0 -0
  99. package/plugins/dsh-session-move/screenshots/08-move-dialog.png +0 -0
  100. package/plugins/dsh-session-move/screenshots/09-moved-to-second-workspace.png +0 -0
  101. package/plugins/dsh-session-move/screenshots/qa-report.json +62 -0
  102. package/plugins/dsh-skill-autopilot/cordis.patch.yml +3 -0
  103. package/plugins/dsh-skill-autopilot/index.js +444 -0
  104. package/plugins/dsh-skill-autopilot/package.json +19 -0
  105. package/plugins/dsh-token-lab/cordis.patch.yml +3 -0
  106. package/plugins/dsh-token-lab/lib/client.js +504 -0
  107. package/plugins/dsh-token-lab/lib/index.js +275 -0
  108. package/plugins/dsh-token-lab/package.json +26 -0
  109. package/plugins/dsh-token-lab/screenshots/01-token-lab-1440-light.png +0 -0
  110. package/plugins/dsh-token-lab/screenshots/02-token-lab-1440-dark.png +0 -0
  111. package/skills/code-review-checklist/SKILL.md +55 -0
  112. package/skills/content-writing/README.md +87 -0
  113. package/skills/content-writing/SKILL.md +85 -0
  114. package/skills/content-writing/assets/checklists/fact-check.md +53 -0
  115. package/skills/content-writing/assets/checklists/publish.md +65 -0
  116. package/skills/content-writing/assets/checklists/social.md +40 -0
  117. package/skills/content-writing/assets/templates/article.md +47 -0
  118. package/skills/content-writing/assets/templates/brief.md +44 -0
  119. package/skills/content-writing/assets/templates/doctor-card.md +41 -0
  120. package/skills/content-writing/assets/templates/image-prompts.md +128 -0
  121. package/skills/content-writing/assets/templates/post-telegram.md +36 -0
  122. package/skills/content-writing/assets/templates/post-vk.md +42 -0
  123. package/skills/content-writing/assets/templates/press-release.md +56 -0
  124. package/skills/content-writing/assets/templates/science-pop-note.md +55 -0
  125. package/skills/content-writing/assets/templates/service-card.md +46 -0
  126. package/skills/content-writing/references/anti-ai-style.md +248 -0
  127. package/skills/content-writing/references/editing.md +185 -0
  128. package/skills/content-writing/references/fact-checking.md +205 -0
  129. package/skills/content-writing/references/government-medical.md +246 -0
  130. package/skills/content-writing/references/images.md +212 -0
  131. package/skills/content-writing/references/journalism.md +252 -0
  132. package/skills/content-writing/references/process.md +123 -0
  133. package/skills/content-writing/references/quick-recipes.md +99 -0
  134. package/skills/content-writing/references/rules.md +85 -0
  135. package/skills/content-writing/references/science-popular.md +218 -0
  136. package/skills/content-writing/references/seo-content.md +172 -0
  137. package/skills/content-writing/references/social-media.md +253 -0
  138. package/skills/content-writing/references/sources.md +49 -0
  139. package/skills/content-writing/references/templates.md +291 -0
  140. package/skills/context-economy/SKILL.md +60 -0
  141. package/skills/context-economy/references/long-session.md +35 -0
  142. package/skills/context-economy/references/token-math.md +42 -0
  143. package/skills/context-economy/references/tool-recipes.md +34 -0
  144. package/skills/database-safety/SKILL.md +47 -0
  145. package/skills/debugging-playbook/SKILL.md +42 -0
  146. package/skills/design-critique/SKILL.md +65 -0
  147. package/skills/design-critique/references/a11y.md +25 -0
  148. package/skills/design-critique/references/diagnoses.md +33 -0
  149. package/skills/design-critique/references/quick-tests.md +30 -0
  150. package/skills/design-critique/references/report-template.md +63 -0
  151. package/skills/design-critique/references/rubric-fast.md +73 -0
  152. package/skills/dev-workflow/SKILL.md +48 -0
  153. package/skills/dev-workflow/references/definition-of-done.md +44 -0
  154. package/skills/dev-workflow/references/memory-and-boundaries.md +31 -0
  155. package/skills/dev-workflow/references/stages.md +67 -0
  156. package/skills/git-and-ci/SKILL.md +60 -0
  157. package/skills/pg-upgrade-verify/SKILL.md +67 -0
  158. package/skills/project-deep-dive/SKILL.md +45 -0
  159. package/skills/project-deep-dive/references/recon-commands.md +90 -0
  160. package/skills/project-deep-dive/references/section-templates.md +104 -0
  161. package/skills/project-deep-dive/references/tool-and-process.md +86 -0
  162. package/skills/project-memory-maintenance/SKILL.md +55 -0
  163. package/skills/project-memory-maintenance/references/api-and-checklist.md +99 -0
  164. package/skills/project-memory-maintenance/references/journal-template.md +50 -0
  165. package/skills/project-memory-maintenance/references/section-rules.md +59 -0
  166. package/skills/release-and-deploy/SKILL.md +53 -0
  167. package/skills/requirements-and-planning/SKILL.md +49 -0
  168. package/skills/requirements-and-planning/references/antipatterns-and-checklist.md +20 -0
  169. package/skills/requirements-and-planning/references/process-steps.md +52 -0
  170. package/skills/requirements-and-planning/references/templates-and-estimates.md +29 -0
  171. package/skills/security-review/SKILL.md +40 -0
  172. package/skills/seo/README.md +60 -0
  173. package/skills/seo/SKILL.md +77 -0
  174. package/skills/seo/assets/README.md +66 -0
  175. package/skills/seo/assets/scripts/LICENSE-playwright-core.txt +202 -0
  176. package/skills/seo/assets/scripts/README.md +179 -0
  177. package/skills/seo/assets/scripts/lib/args.mjs +105 -0
  178. package/skills/seo/assets/scripts/lib/browser.mjs +620 -0
  179. package/skills/seo/assets/scripts/lib/zip.mjs +191 -0
  180. package/skills/seo/assets/scripts/seo-audit.mjs +1675 -0
  181. package/skills/seo/assets/snippets/jsonld-breadcrumb.json +30 -0
  182. package/skills/seo/assets/snippets/jsonld-faq.json +39 -0
  183. package/skills/seo/assets/snippets/jsonld-medical-clinic.json +49 -0
  184. package/skills/seo/assets/snippets/jsonld-medical-webpage.json +33 -0
  185. package/skills/seo/assets/snippets/jsonld-organization.json +74 -0
  186. package/skills/seo/assets/snippets/jsonld-physician.json +46 -0
  187. package/skills/seo/assets/snippets/meta.blade.php +61 -0
  188. package/skills/seo/assets/snippets/robots.txt +61 -0
  189. package/skills/seo/assets/snippets/sitemap-urlset-example.xml +72 -0
  190. package/skills/seo/assets/snippets/sitemap.xml +36 -0
  191. package/skills/seo/references/antipatterns.md +66 -0
  192. package/skills/seo/references/audit-checklist.md +319 -0
  193. package/skills/seo/references/content-seo.md +204 -0
  194. package/skills/seo/references/eeat-medical.md +219 -0
  195. package/skills/seo/references/measurement.md +250 -0
  196. package/skills/seo/references/on-page.md +214 -0
  197. package/skills/seo/references/page-templates.md +56 -0
  198. package/skills/seo/references/stack-recipes.md +695 -0
  199. package/skills/seo/references/structured-data.md +487 -0
  200. package/skills/seo/references/technical-seo.md +296 -0
  201. package/skills/service-desk-conventions/SKILL.md +52 -0
  202. package/skills/service-desk-db-safety/SKILL.md +63 -0
  203. package/skills/service-desk-release/SKILL.md +61 -0
  204. package/skills/service-desk-testing/SKILL.md +61 -0
  205. package/skills/service-desk-ui/SKILL.md +52 -0
  206. package/skills/skills-router/SKILL.md +53 -0
  207. package/skills/skills-router/references/applying-skills.md +25 -0
  208. package/skills/skills-router/references/routing-matrix.md +52 -0
  209. package/skills/testing-strategy/SKILL.md +52 -0
  210. package/skills/ui-design-system/SKILL.md +50 -0
  211. package/skills/ui-design-system/references/accessibility-and-responsive.md +50 -0
  212. package/skills/ui-design-system/references/tokens-and-components.md +67 -0
  213. package/skills/ui-design-system/references/visual-qa.md +44 -0
  214. package/skills/web-research/SKILL.md +47 -0
  215. package/skills/web-research/references/answer-and-citations.md +60 -0
  216. package/skills/web-research/references/query-recipes.md +44 -0
  217. package/skills/wow-design/SKILL.md +89 -0
  218. package/skills/wow-design/assets/kit/README.md +195 -0
  219. package/skills/wow-design/assets/kit/assets/css/base.css +158 -0
  220. package/skills/wow-design/assets/kit/assets/css/components.css +1001 -0
  221. package/skills/wow-design/assets/kit/assets/css/fonts.css +73 -0
  222. package/skills/wow-design/assets/kit/assets/css/pages.css +737 -0
  223. package/skills/wow-design/assets/kit/assets/css/tokens.css +218 -0
  224. package/skills/wow-design/assets/kit/assets/fonts/LICENSE-golos-text.txt +93 -0
  225. package/skills/wow-design/assets/kit/assets/fonts/LICENSE-lora.txt +93 -0
  226. package/skills/wow-design/assets/kit/assets/fonts/LICENSE-unbounded.txt +93 -0
  227. package/skills/wow-design/assets/kit/assets/fonts/golos-text-cyrillic-400-normal.woff2 +0 -0
  228. package/skills/wow-design/assets/kit/assets/fonts/golos-text-cyrillic-500-normal.woff2 +0 -0
  229. package/skills/wow-design/assets/kit/assets/fonts/golos-text-cyrillic-600-normal.woff2 +0 -0
  230. package/skills/wow-design/assets/kit/assets/fonts/golos-text-cyrillic-700-normal.woff2 +0 -0
  231. package/skills/wow-design/assets/kit/assets/fonts/golos-text-latin-400-normal.woff2 +0 -0
  232. package/skills/wow-design/assets/kit/assets/fonts/golos-text-latin-500-normal.woff2 +0 -0
  233. package/skills/wow-design/assets/kit/assets/fonts/golos-text-latin-600-normal.woff2 +0 -0
  234. package/skills/wow-design/assets/kit/assets/fonts/golos-text-latin-700-normal.woff2 +0 -0
  235. package/skills/wow-design/assets/kit/assets/fonts/lora-cyrillic-400-italic.woff2 +0 -0
  236. package/skills/wow-design/assets/kit/assets/fonts/lora-cyrillic-400-normal.woff2 +0 -0
  237. package/skills/wow-design/assets/kit/assets/fonts/lora-cyrillic-500-normal.woff2 +0 -0
  238. package/skills/wow-design/assets/kit/assets/fonts/lora-cyrillic-600-normal.woff2 +0 -0
  239. package/skills/wow-design/assets/kit/assets/fonts/lora-latin-400-normal.woff2 +0 -0
  240. package/skills/wow-design/assets/kit/assets/fonts/lora-latin-500-normal.woff2 +0 -0
  241. package/skills/wow-design/assets/kit/assets/fonts/lora-latin-600-normal.woff2 +0 -0
  242. package/skills/wow-design/assets/kit/assets/fonts/unbounded-cyrillic-400-normal.woff2 +0 -0
  243. package/skills/wow-design/assets/kit/assets/fonts/unbounded-cyrillic-500-normal.woff2 +0 -0
  244. package/skills/wow-design/assets/kit/assets/fonts/unbounded-cyrillic-600-normal.woff2 +0 -0
  245. package/skills/wow-design/assets/kit/assets/fonts/unbounded-latin-400-normal.woff2 +0 -0
  246. package/skills/wow-design/assets/kit/assets/fonts/unbounded-latin-500-normal.woff2 +0 -0
  247. package/skills/wow-design/assets/kit/assets/fonts/unbounded-latin-600-normal.woff2 +0 -0
  248. package/skills/wow-design/assets/kit/assets/js/kit.js +550 -0
  249. package/skills/wow-design/assets/kit/pages/dashboard.html +539 -0
  250. package/skills/wow-design/assets/kit/pages/hospital.html +430 -0
  251. package/skills/wow-design/assets/kit/showcase.html +1164 -0
  252. package/skills/wow-design/assets/scripts/LICENSE-axe-core.txt +362 -0
  253. package/skills/wow-design/assets/scripts/LICENSE-playwright-core.txt +202 -0
  254. package/skills/wow-design/assets/scripts/README.md +312 -0
  255. package/skills/wow-design/assets/scripts/audit.mjs +1200 -0
  256. package/skills/wow-design/assets/scripts/capture.mjs +337 -0
  257. package/skills/wow-design/assets/scripts/package.json +30 -0
  258. package/skills/wow-design/references/anti-patterns.md +252 -0
  259. package/skills/wow-design/references/critique-rubric.md +262 -0
  260. package/skills/wow-design/references/design-brief-template.md +48 -0
  261. package/skills/wow-design/references/inspiration.md +203 -0
  262. package/skills/wow-design/references/presets.md +99 -0
  263. package/skills/wow-design/references/principles.md +371 -0
  264. package/skills/wow-design/references/quick-presets.md +50 -0
  265. package/skills/wow-design/references/report-template.md +21 -0
  266. package/skills/wow-design/references/stack-recipes.md +96 -0
  267. package/skills/wow-design/references/visual-qa.md +82 -0
  268. package/skills/wow-design/references/wow-levers.md +43 -0
  269. package/tools/skill-cost.mjs +76 -0
@@ -0,0 +1,719 @@
1
+ /**
2
+ * dsh-session-move — browser half.
3
+ *
4
+ * Adds two ways to re-file a chat into another Workspace:
5
+ * 1. drag a Session row from one sidebar group and drop it on another group /
6
+ * Workspace header (a result toast reports the outcome);
7
+ * 2. the Session row's "…" menu gains "Move to workspace…" with a picker
8
+ * dialog (keyboard-accessible fallback for the drag gesture).
9
+ *
10
+ * The Host half (index.js) owns the actual move; this module only renders the
11
+ * interaction and calls its authenticated Connection RPC route family.
12
+ */
13
+
14
+ window.__ModuleLoader__.load({
15
+ id: "dsh-session-move",
16
+ factory: (require) => {
17
+ var module = { exports: {} };
18
+ var exports = module.exports;
19
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
20
+ const React = require("react");
21
+ const h = React.createElement;
22
+ const useState = React.useState;
23
+ const useEffect = React.useEffect;
24
+ const useSyncExternalStore = React.useSyncExternalStore;
25
+
26
+ const NS = "session-move";
27
+ /** Host wire namespace; must match ROUTE_BASE/RPC_NAMESPACE in index.js. */
28
+ const RPC_NS = "dsh-session-move";
29
+
30
+ const EN = {
31
+ "menu.move": "Move to workspace…",
32
+ "dialog.title": "Move chat to a workspace",
33
+ "dialog.subtitle": "The chat will belong to the chosen workspace and keep working in its folder.",
34
+ "dialog.ungrouped": "No project (Ungrouped)",
35
+ "dialog.current": "Current",
36
+ "dialog.cancel": "Cancel",
37
+ "dialog.retry": "Retry",
38
+ "dialog.loading": "Loading workspaces…",
39
+ "dialog.error": "Could not load workspaces: {message}",
40
+ "dialog.empty": "No workspaces registered yet.",
41
+ "toast.busy": "Moving chat…",
42
+ "toast.moved": "Moved to “{target}”",
43
+ "toast.movedUngrouped": "Moved out of projects",
44
+ "toast.alreadyThere": "This chat already belongs there.",
45
+ "toast.failed": "Could not move the chat: {message}",
46
+ "toast.workingDirSkipped": "Workspace changed; the working directory stays the same for now.",
47
+ "drop.hint": "Move to “{target}”",
48
+ "drop.ungrouped": "Move out of projects",
49
+ "error.session-unknown": "This chat is not available.",
50
+ "error.workspace-unknown": "This workspace no longer exists.",
51
+ "error.workspace-missing-dir": "The workspace folder is missing.",
52
+ "error.subagent": "Subagent chats cannot be moved.",
53
+ "error.busy": "This chat is already being moved.",
54
+ "error.bad-request": "The move request was rejected as invalid.",
55
+ "error.internal": "Internal error: {message}",
56
+ "menu.icon": "Move",
57
+ };
58
+ const RU = {
59
+ "menu.move": "Переместить в рабочую область…",
60
+ "dialog.title": "Переместить чат в рабочую область",
61
+ "dialog.subtitle": "Чат будет относиться к выбранной рабочей области и продолжит работать в её папке.",
62
+ "dialog.ungrouped": "Без проекта",
63
+ "dialog.current": "Текущая",
64
+ "dialog.cancel": "Отмена",
65
+ "dialog.retry": "Повторить",
66
+ "dialog.loading": "Загружаю рабочие области…",
67
+ "dialog.error": "Не удалось загрузить рабочие области: {message}",
68
+ "dialog.empty": "Рабочих областей пока нет.",
69
+ "toast.busy": "Перемещаю чат…",
70
+ "toast.moved": "Перемещено в «{target}»",
71
+ "toast.movedUngrouped": "Чат убран из проектов",
72
+ "toast.alreadyThere": "Этот чат уже находится там.",
73
+ "toast.failed": "Не удалось переместить чат: {message}",
74
+ "toast.workingDirSkipped": "Рабочая область изменена; рабочая директория пока осталась прежней.",
75
+ "drop.hint": "Переместить в «{target}»",
76
+ "drop.ungrouped": "Убрать из проектов",
77
+ "error.session-unknown": "Чат недоступен.",
78
+ "error.workspace-unknown": "Рабочая область больше не существует.",
79
+ "error.workspace-missing-dir": "Папка рабочей области недоступна.",
80
+ "error.subagent": "Чаты подагентов нельзя перемещать.",
81
+ "error.busy": "Этот чат уже перемещается.",
82
+ "error.bad-request": "Запрос на перемещение отклонён как некорректный.",
83
+ "error.internal": "Внутренняя ошибка: {message}",
84
+ "menu.icon": "Переместить",
85
+ };
86
+ const ZH = {
87
+ "menu.move": "移动到工作区…",
88
+ "dialog.title": "将对话移动到工作区",
89
+ "dialog.subtitle": "该对话将归属于所选工作区,并在其文件夹中继续工作。",
90
+ "dialog.ungrouped": "无项目(未分组)",
91
+ "dialog.current": "当前",
92
+ "dialog.cancel": "取消",
93
+ "dialog.retry": "重试",
94
+ "dialog.loading": "正在加载工作区…",
95
+ "dialog.error": "无法加载工作区:{message}",
96
+ "dialog.empty": "尚未注册任何工作区。",
97
+ "toast.busy": "正在移动对话…",
98
+ "toast.moved": "已移动到“{target}”",
99
+ "toast.movedUngrouped": "已从项目中移出",
100
+ "toast.alreadyThere": "该对话已经在那里了。",
101
+ "toast.failed": "无法移动对话:{message}",
102
+ "toast.workingDirSkipped": "工作区已更改;工作目录暂时保持不变。",
103
+ "drop.hint": "移动到“{target}”",
104
+ "drop.ungrouped": "从项目中移出",
105
+ "error.session-unknown": "该对话不可用。",
106
+ "error.workspace-unknown": "该工作区已不存在。",
107
+ "error.workspace-missing-dir": "工作区文件夹不存在。",
108
+ "error.subagent": "子代理对话无法移动。",
109
+ "error.busy": "该对话正在移动中。",
110
+ "error.bad-request": "移动请求无效。",
111
+ "error.internal": "内部错误:{message}",
112
+ "menu.icon": "移动",
113
+ };
114
+
115
+ const inject = ["slots", "locale", "connection"];
116
+
117
+ const TOAST_TIMEOUT_MS = 4600;
118
+ const ROW_SELECTOR = "[data-row-key]";
119
+ const WORKSPACE_PREFIX = "workspace:";
120
+ const SESSION_PREFIX = "session:";
121
+
122
+ function formatTemplate(template, params) {
123
+ return String(template).replace(/\{(\w+)\}/g, (match, name) =>
124
+ params && Object.prototype.hasOwnProperty.call(params, name) ? String(params[name]) : match,
125
+ );
126
+ }
127
+
128
+ function createStore(initial) {
129
+ let snapshot = initial;
130
+ const listeners = new Set();
131
+ return {
132
+ get: () => snapshot,
133
+ set(patch) {
134
+ const next = typeof patch === "function" ? patch(snapshot) : { ...snapshot, ...patch };
135
+ if (next === snapshot) return;
136
+ snapshot = next;
137
+ for (const listener of [...listeners]) {
138
+ try {
139
+ listener();
140
+ } catch (error) {
141
+ console.error("[session-move] store listener failed", error);
142
+ }
143
+ }
144
+ },
145
+ subscribe(listener) {
146
+ listeners.add(listener);
147
+ return () => listeners.delete(listener);
148
+ },
149
+ };
150
+ }
151
+
152
+ function useStore(store) {
153
+ return useSyncExternalStore(store.subscribe, store.get, store.get);
154
+ }
155
+
156
+ function apply(ctx) {
157
+ const t = ctx.locale.bind(NS);
158
+
159
+ /* ---------------- locale ---------------- */
160
+ ctx.effect(() => {
161
+ try {
162
+ return ctx.locale.register(NS, { en: EN, zh: ZH });
163
+ } catch (error) {
164
+ console.error("[session-move] dictionaries", error);
165
+ return () => {};
166
+ }
167
+ }, "session-move: dictionaries");
168
+ /* Do not addLanguage("ru") here: a Russian language pack (dsh-gkb-ru-ui)
169
+ owns that definition, and adding the same id twice throws. Registering
170
+ the dictionary is enough — an absent language simply never activates
171
+ these strings. */
172
+ ctx.effect(() => {
173
+ try {
174
+ return ctx.locale.register(NS, "ru", RU);
175
+ } catch {
176
+ return () => {};
177
+ }
178
+ }, "session-move: ru dictionary");
179
+
180
+ /* ---------------- styles ---------------- */
181
+ ctx.effect(() => {
182
+ const style = document.createElement("style");
183
+ style.id = "dsh-session-move-style";
184
+ style.textContent = `
185
+ .dsh-sm-dialog-backdrop{position:fixed;inset:0;z-index:2147483000;display:flex;align-items:center;justify-content:center;padding:24px;background:rgba(15,17,21,.42)}
186
+ .dsh-sm-dialog{width:min(460px,100%);max-height:min(560px,calc(100vh - 48px));display:flex;flex-direction:column;gap:0;border-radius:16px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));background:var(--dsw-alias-bg-overlay,#fff);color:var(--dsw-alias-label-primary,#111);box-shadow:0 24px 64px rgba(0,0,0,.28);overflow:hidden;animation:dsh-sm-pop .16s ease-out}
187
+ .dsh-sm-dialog-head{padding:18px 20px 12px;display:flex;flex-direction:column;gap:6px}
188
+ .dsh-sm-dialog-title{font-size:16px;font-weight:650;line-height:22px}
189
+ .dsh-sm-dialog-sub{font-size:12.5px;line-height:18px;color:var(--dsw-alias-label-secondary,#666)}
190
+ .dsh-sm-dialog-body{padding:4px 10px 10px;overflow:auto;display:flex;flex-direction:column;gap:2px}
191
+ .dsh-sm-dialog-foot{display:flex;justify-content:flex-end;gap:8px;padding:10px 16px 16px}
192
+ .dsh-sm-row{display:flex;align-items:center;gap:10px;width:100%;box-sizing:border-box;text-align:left;padding:9px 10px;border-radius:10px;border:1px solid transparent;background:transparent;color:var(--dsw-alias-label-primary,#111);font:inherit;font-size:13px;line-height:18px;cursor:pointer}
193
+ .dsh-sm-row:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.055))}
194
+ .dsh-sm-row[data-current="true"]{border-color:var(--dsw-alias-border-l2,rgba(0,0,0,.16));background:var(--dsw-alias-bg-layer-1,rgba(0,0,0,.03))}
195
+ .dsh-sm-row[data-busy="true"]{opacity:.6;cursor:progress}
196
+ .dsh-sm-row-text{display:flex;flex-direction:column;gap:1px;min-width:0;flex:1}
197
+ .dsh-sm-row-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
198
+ .dsh-sm-row-path{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px;line-height:15px;color:var(--dsw-alias-label-secondary,#777)}
199
+ .dsh-sm-row-mark{flex:none;display:flex;align-items:center;justify-content:center;width:18px;height:18px;color:var(--dsw-alias-brand-primary,#2f7df6)}
200
+ .dsh-sm-btn{border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.16));background:transparent;color:var(--dsw-alias-label-primary,#111);font:inherit;font-size:13px;line-height:18px;padding:7px 14px;border-radius:9px;cursor:pointer}
201
+ .dsh-sm-btn:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.05))}
202
+ .dsh-sm-btn:focus-visible,.dsh-sm-row:focus-visible,.dsh-sm-menu-item:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#2f7df6);outline-offset:1px}
203
+ .dsh-sm-menu-item{display:flex;align-items:center;gap:8px;width:100%;box-sizing:border-box;padding:7px 10px;border:none;border-radius:6px;background:transparent;color:var(--dsw-alias-label-primary,#111);font:inherit;font-size:13px;line-height:18px;text-align:left;cursor:pointer}
204
+ .dsh-sm-menu-item:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.06))}
205
+ .dsh-sm-menu-icon{flex:none;display:flex;align-items:center;justify-content:center;width:16px;height:16px;opacity:.8}
206
+ .dsh-sm-toast{position:fixed;left:50%;bottom:26px;transform:translateX(-50%);z-index:2147483001;display:flex;align-items:flex-start;gap:10px;padding:10px 14px;border-radius:12px;background:var(--dsw-alias-bg-overlay,#fff);border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));box-shadow:0 14px 36px rgba(0,0,0,.2);color:var(--dsw-alias-label-primary,#111);font-size:13px;line-height:18px;max-width:min(540px,calc(100vw - 40px));animation:dsh-sm-toast-in .18s ease-out}
207
+ .dsh-sm-toast-dot{flex:none;width:8px;height:8px;margin-top:5px;border-radius:50%}
208
+ .dsh-sm-toast-body{display:flex;flex-direction:column;gap:2px;min-width:0}
209
+ .dsh-sm-toast-detail{font-size:11.5px;line-height:16px;color:var(--dsw-alias-label-secondary,#666)}
210
+ @keyframes dsh-sm-toast-in{from{opacity:0;transform:translate(-50%,6px)}to{opacity:1;transform:translate(-50%,0)}}
211
+ @keyframes dsh-sm-pop{from{opacity:0;transform:scale(.985)}to{opacity:1;transform:scale(1)}}
212
+ @media (prefers-reduced-motion: reduce){.dsh-sm-toast,.dsh-sm-dialog{animation:none;transition:none}}
213
+ `;
214
+ document.head.appendChild(style);
215
+ return () => style.remove();
216
+ }, "session-move: styles");
217
+
218
+ /* ---------------- store + rpc ---------------- */
219
+ const store = createStore({
220
+ drag: null,
221
+ toast: null,
222
+ dialog: null,
223
+ });
224
+ let toastTimer = null;
225
+ let workspacesCache = [];
226
+
227
+ function showToast(toast) {
228
+ if (toastTimer !== null) {
229
+ clearTimeout(toastTimer);
230
+ toastTimer = null;
231
+ }
232
+ store.set({ toast: { ...toast, id: Date.now() } });
233
+ if (toast.persist !== true) {
234
+ toastTimer = setTimeout(() => {
235
+ toastTimer = null;
236
+ store.set({ toast: null });
237
+ }, TOAST_TIMEOUT_MS);
238
+ }
239
+ }
240
+
241
+ async function call(operation, payload) {
242
+ let signal;
243
+ try {
244
+ signal = typeof AbortSignal !== "undefined" && typeof AbortSignal.timeout === "function"
245
+ ? AbortSignal.timeout(30000)
246
+ : undefined;
247
+ } catch {
248
+ signal = undefined;
249
+ }
250
+ return await ctx.connection.rpc.call("/api", `${RPC_NS}/${operation}`, payload ?? {}, signal);
251
+ }
252
+
253
+ function describeError(error) {
254
+ const code = typeof error?.code === "string" ? error.code : "";
255
+ const known = {
256
+ "session-move/session-unknown": t("error.session-unknown"),
257
+ "session-move/workspace-unknown": t("error.workspace-unknown"),
258
+ "session-move/workspace-missing-dir": t("error.workspace-missing-dir"),
259
+ "session-move/subagent": t("error.subagent"),
260
+ "session-move/busy": t("error.busy"),
261
+ "session-move/bad-request": t("error.bad-request"),
262
+ };
263
+ if (Object.prototype.hasOwnProperty.call(known, code)) return known[code];
264
+ if (code === "session-move/internal") return t("error.internal", { message: error?.message ?? "" });
265
+ return error?.message ?? String(error);
266
+ }
267
+
268
+ function workspaceLabel(workspaceId) {
269
+ if (workspaceId === null || workspaceId === undefined) return "";
270
+ const found = workspacesCache.find((workspace) => workspace.workspaceId === workspaceId);
271
+ return found ? found.title : "";
272
+ }
273
+
274
+ function refreshWorkspaces() {
275
+ call("state", {})
276
+ .then((result) => {
277
+ if (result?.ok && Array.isArray(result.value?.workspaces)) {
278
+ workspacesCache = result.value.workspaces;
279
+ }
280
+ })
281
+ .catch(() => {});
282
+ }
283
+
284
+ /* ---------------- move + dialog ---------------- */
285
+ function closeDialog() {
286
+ store.set({ dialog: null });
287
+ }
288
+
289
+ async function openDialog(sessionId) {
290
+ store.set({ dialog: { phase: "loading", sessionId, workspaces: [], currentWorkspaceId: null, error: null, busy: false } });
291
+ try {
292
+ const result = await call("state", { sessionId });
293
+ if (!result?.ok) {
294
+ store.set({ dialog: { phase: "error", sessionId, workspaces: [], currentWorkspaceId: null, error: describeError(result?.error), busy: false } });
295
+ return;
296
+ }
297
+ workspacesCache = Array.isArray(result.value?.workspaces) ? result.value.workspaces : [];
298
+ store.set({
299
+ dialog: {
300
+ phase: "ready",
301
+ sessionId,
302
+ workspaces: workspacesCache,
303
+ currentWorkspaceId: result.value?.session?.workspaceId ?? null,
304
+ error: null,
305
+ busy: false,
306
+ },
307
+ });
308
+ } catch (error) {
309
+ store.set({ dialog: { phase: "error", sessionId, workspaces: [], currentWorkspaceId: null, error: describeError(error), busy: false } });
310
+ }
311
+ }
312
+
313
+ function dialogMove(sessionId, workspaceId, displayTitle) {
314
+ closeDialog();
315
+ void performMove(sessionId, workspaceId, displayTitle, null);
316
+ }
317
+
318
+ async function performMove(sessionId, workspaceId, targetLabel, targetHeader, options) {
319
+ const label = targetLabel || workspaceLabel(workspaceId);
320
+ showToast({ tone: "info", text: t("toast.busy"), persist: true });
321
+ try {
322
+ const result = await call("move", { sessionId, workspaceId });
323
+ if (!result?.ok) {
324
+ showToast({ tone: "error", text: t("toast.failed", { message: describeError(result?.error) }) });
325
+ return false;
326
+ }
327
+ const value = result.value ?? {};
328
+ if (value.changed === false) {
329
+ showToast({ tone: "info", text: t("toast.alreadyThere") });
330
+ return true;
331
+ }
332
+ const detail = typeof value.workingDirectory === "string" && value.workingDirectory.startsWith("skipped")
333
+ ? t("toast.workingDirSkipped")
334
+ : undefined;
335
+ if (workspaceId === null) showToast({ tone: "success", text: t("toast.movedUngrouped"), detail });
336
+ else showToast({ tone: "success", text: t("toast.moved", { target: label || workspaceLabel(workspaceId) || "" }), detail });
337
+ if (targetHeader && targetHeader.getAttribute && targetHeader.getAttribute("aria-expanded") === "false") {
338
+ try {
339
+ targetHeader.click();
340
+ } catch {
341
+ /* the header may have been replaced mid-animation */
342
+ }
343
+ }
344
+ refreshWorkspaces();
345
+ if (typeof options?.onDone === "function") options.onDone(value);
346
+ return true;
347
+ } catch (error) {
348
+ showToast({ tone: "error", text: t("toast.failed", { message: describeError(error) }) });
349
+ return false;
350
+ }
351
+ }
352
+
353
+ /* ---------------- components ---------------- */
354
+ function ToastView() {
355
+ const snapshot = useStore(store);
356
+ const toast = snapshot.toast;
357
+ if (toast === null) return null;
358
+ const dotColor = toast.tone === "error"
359
+ ? "var(--dsw-alias-state-error-primary,#d64545)"
360
+ : toast.tone === "success"
361
+ ? "var(--dsw-alias-state-success-primary,#2e9e5b)"
362
+ : "var(--dsw-alias-brand-primary,#2f7df6)";
363
+ return h(
364
+ "div",
365
+ { className: "dsh-sm-toast", role: "status", "aria-live": "polite" },
366
+ h("span", { className: "dsh-sm-toast-dot", style: { background: dotColor } }),
367
+ h(
368
+ "div",
369
+ { className: "dsh-sm-toast-body" },
370
+ h("span", null, toast.text),
371
+ toast.detail ? h("span", { className: "dsh-sm-toast-detail" }, toast.detail) : null,
372
+ ),
373
+ );
374
+ }
375
+
376
+ function CheckMark() {
377
+ return h(
378
+ "svg",
379
+ { viewBox: "0 0 16 16", width: 16, height: 16, "aria-hidden": "true" },
380
+ h("path", {
381
+ d: "M3.2 8.6 6.4 11.8 12.9 4.9",
382
+ fill: "none",
383
+ stroke: "currentColor",
384
+ strokeWidth: 1.8,
385
+ strokeLinecap: "round",
386
+ strokeLinejoin: "round",
387
+ }),
388
+ );
389
+ }
390
+
391
+ function MoveDialog() {
392
+ const snapshot = useStore(store);
393
+ const dialog = snapshot.dialog;
394
+ const dialogOpen = dialog !== null;
395
+
396
+ useEffect(() => {
397
+ if (!dialogOpen) return undefined;
398
+ const onKeyDown = (event) => {
399
+ if (event.key === "Escape") {
400
+ event.stopPropagation();
401
+ closeDialog();
402
+ }
403
+ };
404
+ document.addEventListener("keydown", onKeyDown, true);
405
+ return () => document.removeEventListener("keydown", onKeyDown, true);
406
+ }, [dialogOpen]);
407
+
408
+ if (dialog === null) return null;
409
+
410
+ const rows = [];
411
+ if (dialog.phase === "ready") {
412
+ for (const workspace of dialog.workspaces) {
413
+ const current = workspace.workspaceId === dialog.currentWorkspaceId;
414
+ rows.push(
415
+ h(
416
+ "button",
417
+ {
418
+ key: workspace.workspaceId,
419
+ type: "button",
420
+ className: "dsh-sm-row",
421
+ "data-current": current ? "true" : "false",
422
+ "data-busy": dialog.busy ? "true" : "false",
423
+ disabled: dialog.busy || current,
424
+ onClick: () => dialogMove(dialog.sessionId, workspace.workspaceId, workspace.title),
425
+ title: workspace.path,
426
+ },
427
+ h(
428
+ "span",
429
+ { className: "dsh-sm-row-text" },
430
+ h("span", { className: "dsh-sm-row-title" }, workspace.title),
431
+ h("span", { className: "dsh-sm-row-path" }, workspace.path),
432
+ ),
433
+ h("span", { className: "dsh-sm-row-mark" }, current ? null : h(CheckMark)),
434
+ ),
435
+ );
436
+ }
437
+ if (dialog.currentWorkspaceId !== null) {
438
+ rows.push(
439
+ h(
440
+ "button",
441
+ {
442
+ key: "__ungrouped__",
443
+ type: "button",
444
+ className: "dsh-sm-row",
445
+ "data-current": "false",
446
+ "data-busy": dialog.busy ? "true" : "false",
447
+ disabled: dialog.busy,
448
+ onClick: () => dialogMove(dialog.sessionId, null, null),
449
+ },
450
+ h(
451
+ "span",
452
+ { className: "dsh-sm-row-text" },
453
+ h("span", { className: "dsh-sm-row-title" }, t("dialog.ungrouped")),
454
+ ),
455
+ ),
456
+ );
457
+ }
458
+ if (rows.length === 0) {
459
+ rows.push(h("div", { key: "__empty__", className: "dsh-sm-row-path", style: { padding: "10px 12px" } }, t("dialog.empty")));
460
+ }
461
+ }
462
+
463
+ let body;
464
+ if (dialog.phase === "loading") body = h("div", { className: "dsh-sm-row-path", style: { padding: "10px 12px" } }, t("dialog.loading"));
465
+ else if (dialog.phase === "error") body = h("div", { className: "dsh-sm-row-path", style: { padding: "10px 12px", color: "var(--dsw-alias-state-error-primary,#d64545)" } }, t("dialog.error", { message: dialog.error ?? "" }));
466
+ else body = rows;
467
+
468
+ return h(
469
+ "div",
470
+ {
471
+ className: "dsh-sm-dialog-backdrop",
472
+ role: "presentation",
473
+ onMouseDown: (event) => {
474
+ if (event.target === event.currentTarget) closeDialog();
475
+ },
476
+ },
477
+ h(
478
+ "div",
479
+ { className: "dsh-sm-dialog", role: "dialog", "aria-modal": "true", "aria-label": t("dialog.title") },
480
+ h(
481
+ "div",
482
+ { className: "dsh-sm-dialog-head" },
483
+ h("div", { className: "dsh-sm-dialog-title" }, t("dialog.title")),
484
+ h("div", { className: "dsh-sm-dialog-sub" }, t("dialog.subtitle")),
485
+ ),
486
+ h("div", { className: "dsh-sm-dialog-body" }, body),
487
+ h(
488
+ "div",
489
+ { className: "dsh-sm-dialog-foot" },
490
+ dialog.phase === "error"
491
+ ? h("button", { type: "button", className: "dsh-sm-btn", onClick: () => void openDialog(dialog.sessionId) }, t("dialog.retry"))
492
+ : null,
493
+ h("button", { type: "button", className: "dsh-sm-btn", onClick: closeDialog }, t("dialog.cancel")),
494
+ ),
495
+ ),
496
+ );
497
+ }
498
+
499
+ function MoveIcon() {
500
+ return h(
501
+ "svg",
502
+ { viewBox: "0 0 16 16", width: 16, height: 16, "aria-hidden": "true", focusable: "false" },
503
+ h("path", {
504
+ d: "M2.5 4.2c0-.7.6-1.3 1.3-1.3h2.6c.4 0 .8.2 1 .5l.7.8h4c.7 0 1.3.6 1.3 1.3v6.1c0 .7-.6 1.3-1.3 1.3H3.8c-.7 0-1.3-.6-1.3-1.3V4.2Z",
505
+ fill: "none",
506
+ stroke: "currentColor",
507
+ strokeWidth: 1.2,
508
+ strokeLinejoin: "round",
509
+ }),
510
+ h("path", {
511
+ d: "M8.6 7.1v3.2M7.2 8.7l1.4 1.6 1.4-1.6",
512
+ fill: "none",
513
+ stroke: "currentColor",
514
+ strokeWidth: 1.2,
515
+ strokeLinecap: "round",
516
+ strokeLinejoin: "round",
517
+ }),
518
+ );
519
+ }
520
+
521
+ function MoveMenuItem(props) {
522
+ const sessionId = props?.sessionId;
523
+ const useMenuOpenState = props?.useMenuOpenState;
524
+ let setMenuOpen = null;
525
+ try {
526
+ const pair = typeof useMenuOpenState === "function" ? useMenuOpenState() : null;
527
+ if (Array.isArray(pair) && typeof pair[1] === "function") setMenuOpen = pair[1];
528
+ } catch {
529
+ setMenuOpen = null;
530
+ }
531
+ return h(
532
+ "button",
533
+ {
534
+ type: "button",
535
+ role: "menuitem",
536
+ className: "dsh-sm-menu-item",
537
+ onClick: () => {
538
+ try {
539
+ if (setMenuOpen) setMenuOpen(false);
540
+ } catch {
541
+ /* menu already closed */
542
+ }
543
+ void openDialog(sessionId);
544
+ },
545
+ },
546
+ h("span", { className: "dsh-sm-menu-icon" }, h(MoveIcon)),
547
+ h("span", null, t("menu.move")),
548
+ );
549
+ }
550
+
551
+ /* ---------------- drag and drop ---------------- */
552
+ function setupDragAndDrop() {
553
+ let drag = null;
554
+
555
+
556
+ function rowElementOf(target) {
557
+ if (!target || typeof target.closest !== "function") return null;
558
+ return target.closest(ROW_SELECTOR);
559
+ }
560
+
561
+ function workspaceHeaderFor(row) {
562
+ if (row === null) return null;
563
+ const own = row.getAttribute("data-row-key") || "";
564
+ if (own.startsWith(WORKSPACE_PREFIX)) return row;
565
+ let node = row.parentElement;
566
+ while (node !== null && node !== document.body) {
567
+ for (const child of node.children) {
568
+ if (typeof child.hasAttribute !== "function") continue;
569
+ if (child.hasAttribute("data-row-key")) {
570
+ if ((child.getAttribute("data-row-key") || "").startsWith(WORKSPACE_PREFIX)) return child;
571
+ continue;
572
+ }
573
+ const nested = child.querySelector?.(`[data-row-key^="${WORKSPACE_PREFIX}"]`);
574
+ if (nested !== undefined && nested !== null) return nested;
575
+ }
576
+ node = node.parentElement;
577
+ }
578
+ return null;
579
+ }
580
+
581
+ function dropTargetAt(event) {
582
+ const row = rowElementOf(event.target);
583
+ if (row === null) return null;
584
+ const header = workspaceHeaderFor(row);
585
+ if (header === null) return null;
586
+ const attr = header.getAttribute("data-row-key") || "";
587
+ if (!attr.startsWith(WORKSPACE_PREFIX)) return null;
588
+ return { key: attr.slice(WORKSPACE_PREFIX.length), header };
589
+ }
590
+
591
+ function titleOfHeader(header, key) {
592
+ const cached = workspaceLabel(key);
593
+ if (cached) return cached;
594
+ const title = header.querySelector(".wnFoaW_title");
595
+ const raw = title !== null ? title.textContent : header.textContent;
596
+ return (raw || "").trim();
597
+ }
598
+
599
+ function onDragStart(event) {
600
+ const row = rowElementOf(event.target);
601
+ if (row === null) return;
602
+ const attr = row.getAttribute("data-row-key") || "";
603
+ if (!attr.startsWith(SESSION_PREFIX)) return;
604
+ const header = workspaceHeaderFor(row);
605
+ const fromHeaderAttr = header === null ? "" : header.getAttribute("data-row-key") || "";
606
+ drag = {
607
+ sessionId: attr.slice(SESSION_PREFIX.length),
608
+ fromKey: fromHeaderAttr.startsWith(WORKSPACE_PREFIX) ? fromHeaderAttr.slice(WORKSPACE_PREFIX.length) : undefined,
609
+ };
610
+ store.set({ drag: { sessionId: drag.sessionId } });
611
+ }
612
+
613
+ function onDragOver(event) {
614
+ if (drag === null) return;
615
+ const target = dropTargetAt(event);
616
+ if (target === null || target.key === drag.fromKey) return;
617
+ event.preventDefault();
618
+ try {
619
+ if (event.dataTransfer !== null) event.dataTransfer.dropEffect = "move";
620
+ } catch {
621
+ /* some synthetic drag events expose a read-only DataTransfer */
622
+ }
623
+ }
624
+
625
+ function onDrop(event) {
626
+ if (drag === null) return;
627
+ const target = dropTargetAt(event);
628
+ if (target === null || target.key === drag.fromKey) return;
629
+ event.preventDefault();
630
+ const sessionId = drag.sessionId;
631
+ const key = target.key;
632
+ const title = key === "" ? "" : titleOfHeader(target.header, key);
633
+ drag = null;
634
+ store.set({ drag: null });
635
+ void performMove(sessionId, key === "" ? null : key, title, target.header);
636
+ }
637
+
638
+ // Never stop dragend: the shipped sidebar owns its own drag state and
639
+ // clears the blue insertion indicator when this event reaches it.
640
+ function onDragEnd() {
641
+ drag = null;
642
+ if (store.get().drag !== null) store.set({ drag: null });
643
+ }
644
+
645
+ function onWindowBlur() {
646
+ if (drag === null) return;
647
+ drag = null;
648
+ store.set({ drag: null });
649
+ }
650
+
651
+ document.addEventListener("dragstart", onDragStart, true);
652
+ document.addEventListener("dragover", onDragOver, true);
653
+ document.addEventListener("drop", onDrop, true);
654
+ document.addEventListener("dragend", onDragEnd, true);
655
+ window.addEventListener("blur", onWindowBlur);
656
+ return () => {
657
+ document.removeEventListener("dragstart", onDragStart, true);
658
+ document.removeEventListener("dragover", onDragOver, true);
659
+ document.removeEventListener("drop", onDrop, true);
660
+ document.removeEventListener("dragend", onDragEnd, true);
661
+ window.removeEventListener("blur", onWindowBlur);
662
+ drag = null;
663
+ store.set({ drag: null });
664
+ };
665
+ }
666
+
667
+ ctx.effect(() => setupDragAndDrop(), "session-move: drag and drop");
668
+
669
+ /* ---------------- slots ---------------- */
670
+ ctx.effect(() => ctx.slots.inject("shell.overlay", () => ctx.slots.register({
671
+ name: "shell.overlay",
672
+ id: "session-move-toast",
673
+ order: 901,
674
+ }, ToastView)), "session-move: toast");
675
+ ctx.effect(() => ctx.slots.inject("shell.overlay", () => ctx.slots.register({
676
+ name: "shell.overlay",
677
+ id: "session-move-dialog",
678
+ order: 902,
679
+ }, MoveDialog)), "session-move: dialog");
680
+ ctx.effect(() => ctx.slots.inject("sidebar.workspaces.session.menu.item", () => ctx.slots.register({
681
+ name: "sidebar.workspaces.session.menu.item",
682
+ id: "session-move.menu",
683
+ order: 500,
684
+ locale: NS,
685
+ }, MoveMenuItem)), "session-move: session menu item");
686
+
687
+ refreshWorkspaces();
688
+
689
+ /* Small, namespaced diagnostic hook: no data or authority beyond the
690
+ same authenticated RPC the UI already uses. */
691
+ try {
692
+ window.__DSH_SESSION_MOVE__ = {
693
+ call,
694
+ state: (sessionId) => call("state", sessionId === undefined ? {} : { sessionId }),
695
+ move: (sessionId, workspaceId) => call("move", { sessionId, workspaceId }),
696
+ diag: () => call("diag", {}),
697
+ openDialog: (sessionId) => openDialog(sessionId),
698
+ dialogState: () => store.get().dialog,
699
+ storeGet: () => store.get(),
700
+ };
701
+ ctx.effect(() => () => {
702
+ try {
703
+ delete window.__DSH_SESSION_MOVE__;
704
+ } catch {
705
+ /* ignore */
706
+ }
707
+ }, "session-move: debug hook");
708
+ } catch {
709
+ /* a hardened page may seal window */
710
+ }
711
+
712
+ console.info("[session-move] ready");
713
+ }
714
+
715
+ exports.apply = apply;
716
+ exports.inject = inject;
717
+ return module.exports;
718
+ },
719
+ });