@liyuera/dsh-dev-dock 0.0.0-stage → 1.0.3

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 (39) hide show
  1. package/LICENSE +24 -0
  2. package/README.md +42 -2
  3. package/cordis.patch.yml +4 -0
  4. package/lib/client.js +1131 -0
  5. package/lib/index.js +5770 -0
  6. package/lib/invariant.js +24 -0
  7. package/lib/types/actions.d.ts +124 -0
  8. package/lib/types/client/ComposerActions.d.ts +25 -0
  9. package/lib/types/client/DevDockDrawer.d.ts +29 -0
  10. package/lib/types/client/DevDockEntry.d.ts +28 -0
  11. package/lib/types/client/DevDockSettingsPage.d.ts +24 -0
  12. package/lib/types/client/StartTile.d.ts +25 -0
  13. package/lib/types/client/StartWorkButton.d.ts +28 -0
  14. package/lib/types/client/StartWorkModal.d.ts +25 -0
  15. package/lib/types/client/api.d.ts +56 -0
  16. package/lib/types/client/data.d.ts +75 -0
  17. package/lib/types/client/icons.d.ts +40 -0
  18. package/lib/types/client/index.d.ts +27 -0
  19. package/lib/types/client/locales.d.ts +65 -0
  20. package/lib/types/client/pages/ImportPage.d.ts +26 -0
  21. package/lib/types/client/pages/ProjectListPage.d.ts +34 -0
  22. package/lib/types/client/pages/QuickStartPage.d.ts +26 -0
  23. package/lib/types/client/probe.d.ts +3 -0
  24. package/lib/types/client/stores.d.ts +22 -0
  25. package/lib/types/domain.d.ts +17 -0
  26. package/lib/types/editors.d.ts +27 -0
  27. package/lib/types/index.d.ts +31 -0
  28. package/lib/types/invariant.d.ts +16 -0
  29. package/lib/types/platform/editors-darwin.d.ts +25 -0
  30. package/lib/types/platform/editors-win32.d.ts +25 -0
  31. package/lib/types/platform/open-ide.d.ts +23 -0
  32. package/lib/types/platform/open-terminal.d.ts +25 -0
  33. package/lib/types/platform/runner.d.ts +22 -0
  34. package/lib/types/schema.d.ts +52 -0
  35. package/lib/types/tools/actions.d.ts +35 -0
  36. package/lib/types/tools/editors.d.ts +27 -0
  37. package/lib/types/tools/project.d.ts +50 -0
  38. package/lib/types/tools/scan.d.ts +44 -0
  39. package/package.json +85 -4
package/lib/client.js ADDED
@@ -0,0 +1,1131 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@liyuera/dsh-dev-dock",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let _deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
8
+ let react_jsx_runtime = require("react/jsx-runtime");
9
+ let react = require("react");
10
+ let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
11
+ //#region lib/types/client/data.js
12
+ /**
13
+ * devDock browser data layer v2: mirrors the plugin's document from the host
14
+ * through the same-origin `/dev-dock/state` route and exposes the
15
+ * desktop-action bridge. Both are plain HTTP because static client bundles
16
+ * have no package-private RPC channel (host.call is a dynamic-plugin
17
+ * builtin); the host half owns the storage domain behind that route and runs
18
+ * the deterministic desktop actions.
19
+ * @module @liyuera/dsh-dev-dock/client/data
20
+ */
21
+ /** Canonical editor names across platforms (union for stable UI display). */
22
+ const EDITOR_NAMES = [
23
+ "WebStorm",
24
+ "VS Code",
25
+ "IntelliJ IDEA",
26
+ "Cursor",
27
+ "Sublime Text",
28
+ "HBuilderX"
29
+ ];
30
+ /**
31
+ * Create the devDock data layer for one client plugin fiber.
32
+ * @param ctx - client root context, used to own the mirror's effects.
33
+ * @returns the document mirror and the action facade.
34
+ */
35
+ function createDevDockData(ctx) {
36
+ const store = (0, _deepseek_ai_dsh_client_store.createSnapshotStore)({
37
+ ready: false,
38
+ settings: void 0
39
+ });
40
+ const adopt = (raw) => {
41
+ const settings = decodeSettings(raw);
42
+ if (settings === void 0) return;
43
+ store.set({
44
+ ready: true,
45
+ settings
46
+ });
47
+ };
48
+ const refresh = async () => {
49
+ try {
50
+ const answer = await (await fetch("/dev-dock/state", { method: "GET" })).json();
51
+ if (answer.ok === true) adopt(answer.settings);
52
+ } catch {}
53
+ };
54
+ refresh();
55
+ ctx.effect(() => {
56
+ const onFocus = () => {
57
+ refresh();
58
+ };
59
+ window.addEventListener("focus", onFocus);
60
+ return () => {
61
+ window.removeEventListener("focus", onFocus);
62
+ };
63
+ }, "dev-dock: focus refresh");
64
+ const write = async (field, value) => {
65
+ const snapshot = store.getSnapshot().settings;
66
+ if (snapshot !== void 0) store.set({
67
+ ready: true,
68
+ settings: {
69
+ ...snapshot,
70
+ [field]: value
71
+ }
72
+ });
73
+ try {
74
+ const answer = await (await fetch("/dev-dock/state", {
75
+ method: "POST",
76
+ headers: { "content-type": "application/json" },
77
+ body: JSON.stringify({
78
+ field,
79
+ value
80
+ })
81
+ })).json();
82
+ if (answer.ok === true) adopt(answer.settings);
83
+ } catch {}
84
+ };
85
+ return {
86
+ store,
87
+ actions: {
88
+ async setWorkspacePref(workspaceId, editor) {
89
+ const current = store.getSnapshot().settings;
90
+ if (current === void 0) return;
91
+ const rest = current.workspacePrefs.filter((p) => p.workspaceId !== workspaceId);
92
+ await write("workspacePrefs", editor === "" ? rest : [...rest, {
93
+ workspaceId,
94
+ editor
95
+ }]);
96
+ },
97
+ async setEditorManualPath(name, manualPath) {
98
+ const current = store.getSnapshot().settings;
99
+ if (current === void 0) return;
100
+ await write("editors", current.editors.find((e) => e.name === name) === void 0 ? [...current.editors, {
101
+ name,
102
+ manualPath
103
+ }] : current.editors.map((e) => e.name === name ? {
104
+ ...e,
105
+ manualPath
106
+ } : e));
107
+ },
108
+ async setTerminalApp(app) {
109
+ await write("terminalApp", app);
110
+ },
111
+ async setStartWork(workspaceIds) {
112
+ await write("startWork", workspaceIds);
113
+ },
114
+ listEditors: () => rpcAction("list-editors"),
115
+ openIde: (workspaceId) => rpcAction("open-ide", { workspaceId }),
116
+ openTerminal: (workspaceId) => rpcAction("open-terminal", { workspaceId }),
117
+ start: (workspaceId) => rpcAction("start", { workspaceId }),
118
+ startWork: (workspaceIds) => rpcAction("start-work", { workspaceIds })
119
+ }
120
+ };
121
+ }
122
+ /** Decode the wire section into the settings document (lenient cast). */
123
+ function decodeSettings(section) {
124
+ if (section === null || typeof section !== "object") return void 0;
125
+ const doc = section;
126
+ if (!Array.isArray(doc.workspacePrefs) || !Array.isArray(doc.editors) || !Array.isArray(doc.startWork)) return;
127
+ return {
128
+ workspacePrefs: doc.workspacePrefs,
129
+ editors: doc.editors,
130
+ terminalApp: doc.terminalApp === "iterm" ? "iterm" : "default",
131
+ startWork: doc.startWork
132
+ };
133
+ }
134
+ /**
135
+ * POST one action to the plugin's host route.
136
+ * @param action - action name.
137
+ * @param extra - additional JSON fields.
138
+ * @returns the host answer, or a fetch-failure answer when the wire failed.
139
+ */
140
+ async function rpcAction(action, extra = {}) {
141
+ try {
142
+ return await (await fetch("/dev-dock/action", {
143
+ method: "POST",
144
+ headers: { "content-type": "application/json" },
145
+ body: JSON.stringify({
146
+ action,
147
+ ...extra
148
+ })
149
+ })).json();
150
+ } catch (error) {
151
+ return {
152
+ ok: false,
153
+ error: `devDock action route unreachable: ${error instanceof Error ? error.message : String(error)}`
154
+ };
155
+ }
156
+ }
157
+ //#endregion
158
+ //#region lib/types/client/stores.js
159
+ /**
160
+ * devDock panel viewing store: the start-work dialog open state. Shared by
161
+ * the sidebar footer button and entry row so the dialog opens from either
162
+ * and survives entry/overlay remounts.
163
+ * @module @liyuera/dsh-dev-dock/client/stores
164
+ */
165
+ /**
166
+ * Create the devDock viewing store.
167
+ * @returns the store handle (spec + type + identity + factory in one).
168
+ */
169
+ function createDevDockStore() {
170
+ return (0, _deepseek_ai_dsh_client_store.defineStore)({
171
+ init: () => ({ open: false }),
172
+ actions: { setOpen: (draft, open) => {
173
+ draft.open = open;
174
+ } }
175
+ });
176
+ }
177
+ //#endregion
178
+ //#region lib/types/client/icons.js
179
+ /** Terminal glyph: `>_` command prompt. */
180
+ const TERMINAL_STROKE = {
181
+ stroke: "currentColor",
182
+ strokeWidth: 1.4,
183
+ strokeLinecap: "round",
184
+ strokeLinejoin: "round"
185
+ };
186
+ /**
187
+ * IDE glyph: `</>` code brackets.
188
+ * @param props - icon size and optional class.
189
+ * @returns the 16px editor svg.
190
+ */
191
+ function DevIdeIcon({ size = 16, className }) {
192
+ return (0, react_jsx_runtime.jsxs)("svg", {
193
+ width: size,
194
+ height: size,
195
+ viewBox: "0 0 16 16",
196
+ fill: "none",
197
+ stroke: "currentColor",
198
+ strokeWidth: "1.4",
199
+ strokeLinecap: "round",
200
+ strokeLinejoin: "round",
201
+ className,
202
+ xmlns: "http://www.w3.org/2000/svg",
203
+ children: [
204
+ (0, react_jsx_runtime.jsx)("path", { d: "M5.0 4.8 2.6 8 5.0 11.2" }),
205
+ (0, react_jsx_runtime.jsx)("path", { d: "M11.0 4.8 13.4 8 11.0 11.2" }),
206
+ (0, react_jsx_runtime.jsx)("path", { d: "M8.7 4.6 7.3 11.4" })
207
+ ]
208
+ });
209
+ }
210
+ /**
211
+ * Terminal glyph: `>_` prompt.
212
+ * @param props - icon size and optional class.
213
+ * @returns the 16px terminal svg.
214
+ */
215
+ function DevTerminalIcon({ size = 16, className }) {
216
+ return (0, react_jsx_runtime.jsxs)("svg", {
217
+ ...TERMINAL_STROKE,
218
+ width: size,
219
+ height: size,
220
+ viewBox: "0 0 16 16",
221
+ fill: "none",
222
+ className,
223
+ xmlns: "http://www.w3.org/2000/svg",
224
+ children: [(0, react_jsx_runtime.jsx)("path", { d: "M4.4 4.8 7.6 8 4.4 11.2" }), (0, react_jsx_runtime.jsx)("path", { d: "M8.6 11.2H11.8" })]
225
+ });
226
+ }
227
+ /**
228
+ * App-icon-style terminal fallback: a dark rounded square with a light `>_`
229
+ * prompt, used where the real app icon is unavailable (Assets.car-only
230
+ * bundles, other platforms).
231
+ * @param props - icon size and optional class.
232
+ * @returns the 16px filled terminal svg.
233
+ */
234
+ function DevTerminalAppIcon({ size = 16, className }) {
235
+ return (0, react_jsx_runtime.jsxs)("svg", {
236
+ width: size,
237
+ height: size,
238
+ viewBox: "0 0 16 16",
239
+ fill: "none",
240
+ className,
241
+ xmlns: "http://www.w3.org/2000/svg",
242
+ children: [
243
+ (0, react_jsx_runtime.jsx)("rect", {
244
+ x: "1.6",
245
+ y: "1.6",
246
+ width: "12.8",
247
+ height: "12.8",
248
+ rx: "3.4",
249
+ fill: "#23272E"
250
+ }),
251
+ (0, react_jsx_runtime.jsx)("path", {
252
+ d: "M4.7 5.2 7.4 8 4.7 10.8",
253
+ stroke: "#E8EAED",
254
+ strokeWidth: "1.4",
255
+ strokeLinecap: "round",
256
+ strokeLinejoin: "round"
257
+ }),
258
+ (0, react_jsx_runtime.jsx)("path", {
259
+ d: "M8.1 10.9H11.5",
260
+ stroke: "#E8EAED",
261
+ strokeWidth: "1.4",
262
+ strokeLinecap: "round"
263
+ })
264
+ ]
265
+ });
266
+ }
267
+ /**
268
+ * App-icon-style editor fallback: a blue rounded square with a white `</>`
269
+ * mark, used where the real app icon is unavailable.
270
+ * @param props - icon size and optional class.
271
+ * @returns the 16px filled editor svg.
272
+ */
273
+ function DevIdeAppIcon({ size = 16, className }) {
274
+ return (0, react_jsx_runtime.jsxs)("svg", {
275
+ width: size,
276
+ height: size,
277
+ viewBox: "0 0 16 16",
278
+ fill: "none",
279
+ className,
280
+ xmlns: "http://www.w3.org/2000/svg",
281
+ children: [
282
+ (0, react_jsx_runtime.jsx)("rect", {
283
+ x: "1.6",
284
+ y: "1.6",
285
+ width: "12.8",
286
+ height: "12.8",
287
+ rx: "3.4",
288
+ fill: "#3D8BFF"
289
+ }),
290
+ (0, react_jsx_runtime.jsx)("path", {
291
+ d: "M5.6 5.4 3.3 8 5.6 10.6",
292
+ stroke: "#FFFFFF",
293
+ strokeWidth: "1.4",
294
+ strokeLinecap: "round",
295
+ strokeLinejoin: "round"
296
+ }),
297
+ (0, react_jsx_runtime.jsx)("path", {
298
+ d: "M10.4 5.4 12.7 8 10.4 10.6",
299
+ stroke: "#FFFFFF",
300
+ strokeWidth: "1.4",
301
+ strokeLinecap: "round",
302
+ strokeLinejoin: "round"
303
+ }),
304
+ (0, react_jsx_runtime.jsx)("path", {
305
+ d: "M9.1 5.1 7.5 10.9",
306
+ stroke: "#FFFFFF",
307
+ strokeWidth: "1.4",
308
+ strokeLinecap: "round"
309
+ })
310
+ ]
311
+ });
312
+ }
313
+ //#endregion
314
+ //#region \0dsh-css:/Users/liyu/Documents/www/DeepSeek/deepseek-harness/deepseek-harness-plugin/dev-dock/src/client/StartTile.module.css.mjs
315
+ const css$4 = ".Im9i9a_tile{border-radius:4px;width:16px;height:16px;display:block;position:relative;overflow:hidden}.Im9i9a_halfTerminal{clip-path:polygon(0 0,100% 0,0 100%);justify-content:flex-start;align-items:flex-start;display:flex;position:absolute;inset:0}.Im9i9a_halfIde{clip-path:polygon(100% 0,100% 100%,0 100%);justify-content:flex-end;align-items:flex-end;display:flex;position:absolute;inset:0}.Im9i9a_img{object-fit:cover;width:100%;height:100%;display:block}.Im9i9a_tile:after{content:\"\";pointer-events:none;background:linear-gradient(to bottom right, transparent 40%, color-mix(in srgb, var(--dsw-alias-bg-layer-1) 55%, transparent) 50%, transparent 60%);position:absolute;inset:0}";
316
+ const tagId$4 = "@liyuera/dsh-dev-dock/StartTile.module.css";
317
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$4) + "]") === null) {
318
+ const tag = document.createElement("style");
319
+ tag.dataset.plugin = "@liyuera/dsh-dev-dock";
320
+ tag.dataset.pluginCss = tagId$4;
321
+ tag.textContent = css$4;
322
+ document.head.appendChild(tag);
323
+ }
324
+ var StartTile_module_css_default = {
325
+ "halfIde": "Im9i9a_halfIde",
326
+ "halfTerminal": "Im9i9a_halfTerminal",
327
+ "img": "Im9i9a_img",
328
+ "tile": "Im9i9a_tile"
329
+ };
330
+ //#endregion
331
+ //#region lib/types/client/StartTile.js
332
+ /**
333
+ * Start-work tile: a 16px rounded square split from top-right to
334
+ * bottom-left — the terminal icon in the upper-left half, the editor icon in
335
+ * the lower-right half, no border or padding, with a soft diagonal seam
336
+ * blending the two halves. Used by the session-header start button and the
337
+ * sidebar start-work button, with app icons when resolvable and
338
+ * app-icon-style fallbacks otherwise.
339
+ * @module @liyuera/dsh-dev-dock/client/StartTile
340
+ */
341
+ /**
342
+ * Render the combined start-work tile.
343
+ * @param props - optional app-icon URLs per half and the tile edge.
344
+ * @returns the split tile.
345
+ */
346
+ function StartTile({ ideSrc, termSrc, size = 16 }) {
347
+ const [failed, setFailed] = (0, react.useState)({
348
+ ide: false,
349
+ term: false
350
+ });
351
+ (0, react.useEffect)(() => {
352
+ setFailed({
353
+ ide: false,
354
+ term: false
355
+ });
356
+ }, [ideSrc, termSrc]);
357
+ return (0, react_jsx_runtime.jsxs)("span", {
358
+ className: StartTile_module_css_default.tile,
359
+ style: {
360
+ width: size,
361
+ height: size
362
+ },
363
+ "aria-hidden": "true",
364
+ children: [(0, react_jsx_runtime.jsx)("span", {
365
+ className: StartTile_module_css_default.halfTerminal,
366
+ children: failed.term || termSrc === void 0 ? (0, react_jsx_runtime.jsx)(DevTerminalAppIcon, {
367
+ size,
368
+ className: StartTile_module_css_default.img
369
+ }) : (0, react_jsx_runtime.jsx)("img", {
370
+ className: StartTile_module_css_default.img,
371
+ src: termSrc,
372
+ alt: "",
373
+ onError: () => {
374
+ setFailed((current) => ({
375
+ ...current,
376
+ term: true
377
+ }));
378
+ }
379
+ })
380
+ }), (0, react_jsx_runtime.jsx)("span", {
381
+ className: StartTile_module_css_default.halfIde,
382
+ children: failed.ide || ideSrc === void 0 ? (0, react_jsx_runtime.jsx)(DevIdeAppIcon, {
383
+ size,
384
+ className: StartTile_module_css_default.img
385
+ }) : (0, react_jsx_runtime.jsx)("img", {
386
+ className: StartTile_module_css_default.img,
387
+ src: ideSrc,
388
+ alt: "",
389
+ onError: () => {
390
+ setFailed((current) => ({
391
+ ...current,
392
+ ide: true
393
+ }));
394
+ }
395
+ })
396
+ })]
397
+ });
398
+ }
399
+ //#endregion
400
+ //#region \0dsh-css:/Users/liyu/Documents/www/DeepSeek/deepseek-harness/deepseek-harness-plugin/dev-dock/src/client/StartWorkButton.module.css.mjs
401
+ const css$3 = "._6acG6G_row{box-sizing:border-box;width:calc(100% + 4px);height:42px;color:var(--dsw-alias-label-primary);cursor:pointer;text-align:left;transition:background var(--ds-transition-duration-fast) var(--ds-ease-in-out);background:0 0;border:none;border-radius:12px;align-items:center;gap:8px;margin:4px -2px;padding:0 10px 0 8px;display:flex;overflow:hidden}._6acG6G_row:hover{background:var(--dsw-alias-interactive-bg-hover)}._6acG6G_row:active{background:var(--dsw-alias-interactive-bg-active)}._6acG6G_label{white-space:nowrap;font-size:14px;line-height:22px;overflow:hidden}._6acG6G_railButton{width:36px;height:36px;color:var(--dsw-alias-label-secondary);cursor:pointer;transition:background var(--ds-transition-duration-fast) var(--ds-ease-in-out);background:0 0;border:none;border-radius:50%;justify-content:center;align-items:center;margin:8px 0 10px;padding:0;display:flex}._6acG6G_railButton:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}";
402
+ const tagId$3 = "@liyuera/dsh-dev-dock/StartWorkButton.module.css";
403
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
404
+ const tag = document.createElement("style");
405
+ tag.dataset.plugin = "@liyuera/dsh-dev-dock";
406
+ tag.dataset.pluginCss = tagId$3;
407
+ tag.textContent = css$3;
408
+ document.head.appendChild(tag);
409
+ }
410
+ var StartWorkButton_module_css_default = {
411
+ "label": "_6acG6G_label",
412
+ "railButton": "_6acG6G_railButton",
413
+ "row": "_6acG6G_row"
414
+ };
415
+ //#endregion
416
+ //#region lib/types/client/StartWorkButton.js
417
+ /**
418
+ * The sidebar footer start-work button.
419
+ * @param props - footer owner state, view store, settings mirror, translator.
420
+ * @returns the button row.
421
+ */
422
+ function StartWorkButton({ wide, useWorkspaces, useDevDockData, actions, t }) {
423
+ const label = t("start.button");
424
+ const settings = useDevDockData((data) => data.settings);
425
+ const firstWorkspaceId = useWorkspaces((state) => state.items[0]?.workspaceId);
426
+ const editorPref = settings?.workspacePrefs.find((p) => p.workspaceId === firstWorkspaceId)?.editor ?? "";
427
+ const ideSrc = firstWorkspaceId === void 0 ? void 0 : `/dev-dock/workspace-editor-icon?workspaceId=${encodeURIComponent(firstWorkspaceId)}&v=${encodeURIComponent(editorPref)}`;
428
+ const termSrc = `/dev-dock/terminal-icon?app=${encodeURIComponent(settings?.terminalApp ?? "default")}`;
429
+ if (!wide) return (0, react_jsx_runtime.jsx)("button", {
430
+ type: "button",
431
+ className: StartWorkButton_module_css_default.railButton,
432
+ title: label,
433
+ "aria-label": label,
434
+ onClick: () => {
435
+ actions.setOpen(true);
436
+ },
437
+ children: (0, react_jsx_runtime.jsx)(StartTile, {
438
+ ideSrc,
439
+ termSrc,
440
+ size: 18
441
+ })
442
+ });
443
+ return (0, react_jsx_runtime.jsxs)("button", {
444
+ type: "button",
445
+ className: StartWorkButton_module_css_default.row,
446
+ title: label,
447
+ onClick: () => {
448
+ actions.setOpen(true);
449
+ },
450
+ children: [(0, react_jsx_runtime.jsx)(StartTile, {
451
+ ideSrc,
452
+ termSrc
453
+ }), (0, react_jsx_runtime.jsx)("span", {
454
+ className: StartWorkButton_module_css_default.label,
455
+ children: label
456
+ })]
457
+ });
458
+ }
459
+ //#endregion
460
+ //#region \0dsh-css:/Users/liyu/Documents/www/DeepSeek/deepseek-harness/deepseek-harness-plugin/dev-dock/src/client/StartWork.module.css.mjs
461
+ const css$2 = ".G6Ne1q_dialog{width:min(570px,100%)}.G6Ne1q_actionBtn{min-width:96px;padding:0 20px}.G6Ne1q_list{flex-direction:column;gap:4px;max-height:426px;margin:0;padding:0;list-style:none;display:flex;overflow-y:auto}.G6Ne1q_row{cursor:pointer;border-radius:8px;align-items:center;gap:8px;padding:8px 10px;display:flex}.G6Ne1q_row:hover{background:var(--dsw-alias-interactive-bg-hover)}.G6Ne1q_row input{flex:none}.G6Ne1q_rowIcon{flex:none;display:block}.G6Ne1q_name{min-width:0;color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;flex:auto;font-size:13px;font-weight:600}.G6Ne1q_empty{color:var(--dsw-alias-label-secondary);margin:0;font-size:13px}.G6Ne1q_error{color:var(--dsw-alias-state-error,var(--dsw-alias-label-error));margin:10px 0 0;font-size:12.5px}";
462
+ const tagId$2 = "@liyuera/dsh-dev-dock/StartWork.module.css";
463
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
464
+ const tag = document.createElement("style");
465
+ tag.dataset.plugin = "@liyuera/dsh-dev-dock";
466
+ tag.dataset.pluginCss = tagId$2;
467
+ tag.textContent = css$2;
468
+ document.head.appendChild(tag);
469
+ }
470
+ var StartWork_module_css_default = {
471
+ "actionBtn": "G6Ne1q_actionBtn",
472
+ "dialog": "G6Ne1q_dialog",
473
+ "empty": "G6Ne1q_empty",
474
+ "error": "G6Ne1q_error",
475
+ "list": "G6Ne1q_list",
476
+ "name": "G6Ne1q_name",
477
+ "row": "G6Ne1q_row",
478
+ "rowIcon": "G6Ne1q_rowIcon"
479
+ };
480
+ //#endregion
481
+ //#region lib/types/client/StartWorkModal.js
482
+ /**
483
+ * Start-work dialog: check the workspace projects to launch, confirming
484
+ * starts each selected project's editor + system terminal. The selection is
485
+ * remembered in settings and prefilled next time.
486
+ */
487
+ /**
488
+ * The start-work dialog.
489
+ * @param props - overlay runtime, view store, data hook, actions, translator.
490
+ * @returns the modal, or null when closed.
491
+ */
492
+ function StartWorkModal({ useStore, actions, useWorkspaces, useDevDockData, dataActions, t }) {
493
+ const { open } = useStore((state) => state);
494
+ const workspaces = useWorkspaces((state) => state.items);
495
+ const settings = useDevDockData((data) => data.settings);
496
+ const [selection, setSelection] = (0, react.useState)(/* @__PURE__ */ new Set());
497
+ const [phase, setPhase] = (0, react.useState)("idle");
498
+ const [error, setError] = (0, react.useState)("");
499
+ (0, react.useEffect)(() => {
500
+ if (!open) return;
501
+ const remembered = settings?.startWork ?? [];
502
+ const available = new Set(workspaces.map((w) => w.workspaceId));
503
+ setSelection(new Set(remembered.filter((id) => available.has(id))));
504
+ setPhase("idle");
505
+ setError("");
506
+ }, [
507
+ open,
508
+ settings,
509
+ workspaces
510
+ ]);
511
+ if (!open) return null;
512
+ const toggle = (workspaceId) => {
513
+ setSelection((current) => {
514
+ const next = new Set(current);
515
+ if (next.has(workspaceId)) next.delete(workspaceId);
516
+ else next.add(workspaceId);
517
+ return next;
518
+ });
519
+ };
520
+ const confirm = async () => {
521
+ const ids = workspaces.map((w) => w.workspaceId).filter((id) => selection.has(id));
522
+ if (ids.length === 0) return;
523
+ setPhase("running");
524
+ const answer = await dataActions.startWork(ids);
525
+ await dataActions.setStartWork(ids);
526
+ if (answer.ok) {
527
+ actions.setOpen(false);
528
+ return;
529
+ }
530
+ setError("fetchError" in answer ? answer.error ?? String(answer.fetchError) : answer.error ?? "start failed");
531
+ setPhase("error");
532
+ };
533
+ const close = () => {
534
+ actions.setOpen(false);
535
+ };
536
+ return (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
537
+ open: true,
538
+ onClose: close,
539
+ title: t("start.button"),
540
+ closeLabel: t("start.cancel"),
541
+ description: t("start.notice"),
542
+ className: StartWork_module_css_default.dialog,
543
+ footer: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
544
+ size: "md",
545
+ variant: "outline",
546
+ className: StartWork_module_css_default.actionBtn,
547
+ onClick: close,
548
+ disabled: phase === "running",
549
+ children: t("start.cancel")
550
+ }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
551
+ size: "md",
552
+ variant: "primary",
553
+ className: StartWork_module_css_default.actionBtn,
554
+ onClick: () => {
555
+ confirm();
556
+ },
557
+ disabled: selection.size === 0 || phase === "running",
558
+ children: phase === "running" ? t("start.running") : t("start.confirm")
559
+ })] }),
560
+ children: [workspaces.length === 0 ? (0, react_jsx_runtime.jsx)("p", {
561
+ className: StartWork_module_css_default.empty,
562
+ children: t("start.empty")
563
+ }) : (0, react_jsx_runtime.jsx)("ul", {
564
+ className: StartWork_module_css_default.list,
565
+ "aria-label": t("start.button"),
566
+ children: workspaces.map((workspace) => {
567
+ const pref = settings?.workspacePrefs.find((p) => p.workspaceId === workspace.workspaceId)?.editor ?? "";
568
+ const ideSrc = `/dev-dock/workspace-editor-icon?workspaceId=${encodeURIComponent(workspace.workspaceId)}&v=${encodeURIComponent(pref)}`;
569
+ const termSrc = `/dev-dock/terminal-icon?app=${encodeURIComponent(settings?.terminalApp ?? "default")}`;
570
+ return (0, react_jsx_runtime.jsx)("li", { children: (0, react_jsx_runtime.jsxs)("label", {
571
+ className: StartWork_module_css_default.row,
572
+ children: [
573
+ (0, react_jsx_runtime.jsx)("input", {
574
+ type: "checkbox",
575
+ checked: selection.has(workspace.workspaceId),
576
+ onChange: () => {
577
+ toggle(workspace.workspaceId);
578
+ },
579
+ disabled: phase === "running"
580
+ }),
581
+ (0, react_jsx_runtime.jsx)("span", {
582
+ className: StartWork_module_css_default.rowIcon,
583
+ children: (0, react_jsx_runtime.jsx)(StartTile, {
584
+ ideSrc,
585
+ termSrc
586
+ })
587
+ }),
588
+ (0, react_jsx_runtime.jsx)("span", {
589
+ className: StartWork_module_css_default.name,
590
+ children: workspace.title || workspace.path
591
+ })
592
+ ]
593
+ }) }, workspace.workspaceId);
594
+ })
595
+ }), phase === "error" && (0, react_jsx_runtime.jsx)("p", {
596
+ className: StartWork_module_css_default.error,
597
+ role: "alert",
598
+ children: error
599
+ })]
600
+ });
601
+ }
602
+ //#endregion
603
+ //#region \0dsh-css:/Users/liyu/Documents/www/DeepSeek/deepseek-harness/deepseek-harness-plugin/dev-dock/src/client/SessionActions.module.css.mjs
604
+ const css$1 = ".DLfkuW_button{min-width:28px;min-height:28px;color:var(--dsw-alias-label-secondary);cursor:pointer;transition:background var(--ds-transition-duration-fast) var(--ds-ease-in-out);background:0 0;border:0;border-radius:6px;justify-content:center;align-items:center;padding:4px;display:inline-flex}.DLfkuW_appIcon{border-radius:4px;width:16px;height:16px}.DLfkuW_group{align-items:center;gap:2px;display:inline-flex}.DLfkuW_group .DLfkuW_button{min-width:24px;min-height:24px;padding:3px}.DLfkuW_button:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.DLfkuW_button:disabled,.DLfkuW_button[aria-disabled=true]{opacity:.5;cursor:default}.DLfkuW_error{color:var(--dsw-alias-state-error,var(--dsw-alias-label-error))}";
605
+ const tagId$1 = "@liyuera/dsh-dev-dock/SessionActions.module.css";
606
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
607
+ const tag = document.createElement("style");
608
+ tag.dataset.plugin = "@liyuera/dsh-dev-dock";
609
+ tag.dataset.pluginCss = tagId$1;
610
+ tag.textContent = css$1;
611
+ document.head.appendChild(tag);
612
+ }
613
+ var SessionActions_module_css_default = {
614
+ "appIcon": "DLfkuW_appIcon",
615
+ "button": "DLfkuW_button",
616
+ "error": "DLfkuW_error",
617
+ "group": "DLfkuW_group"
618
+ };
619
+ //#endregion
620
+ //#region lib/types/client/ComposerActions.js
621
+ /**
622
+ * Composer actions: the three devDock buttons (IDE / terminal / start) in
623
+ * the input tool row's left seat, bound to the current session's workspace.
624
+ * Same resolution and feedback as the session-header actions, in a compact
625
+ * group beside the resident chrome.
626
+ */
627
+ /** Locale key per action kind. */
628
+ const KIND_KEY = {
629
+ ide: "header.ide",
630
+ terminal: "header.terminal",
631
+ start: "header.start"
632
+ };
633
+ /**
634
+ * The composer tool-row action group.
635
+ * @param props - input-zone runtime, settings mirror, actions, translator.
636
+ * @returns the three buttons, or null when the session has no workspace.
637
+ */
638
+ function ComposerActions({ sessionId, useWorkspaces, useDevDockData, dataActions, t }) {
639
+ const workspace = useWorkspaces((state) => state.items.find((w) => w.sessionIds.includes(sessionId)));
640
+ const settings = useDevDockData((data) => data.settings);
641
+ const [states, setStates] = (0, react.useState)({
642
+ ide: "idle",
643
+ terminal: "idle",
644
+ start: "idle"
645
+ });
646
+ const [errors, setErrors] = (0, react.useState)({});
647
+ const [iconFailed, setIconFailed] = (0, react.useState)({});
648
+ const timer = (0, react.useRef)(null);
649
+ (0, react.useEffect)(() => () => {
650
+ if (timer.current !== null) clearTimeout(timer.current);
651
+ }, []);
652
+ const editorPref = settings?.workspacePrefs.find((p) => p.workspaceId === workspace?.workspaceId)?.editor ?? "";
653
+ const terminalApp = settings?.terminalApp ?? "default";
654
+ const editorIconUrl = workspace === void 0 ? "" : `/dev-dock/workspace-editor-icon?workspaceId=${encodeURIComponent(workspace.workspaceId)}&v=${encodeURIComponent(editorPref)}`;
655
+ const terminalIconUrl = `/dev-dock/terminal-icon?app=${encodeURIComponent(terminalApp)}`;
656
+ (0, react.useEffect)(() => {
657
+ setIconFailed({});
658
+ }, [editorIconUrl, terminalIconUrl]);
659
+ if (workspace === void 0) return null;
660
+ const run = async (kind) => {
661
+ if (states[kind] === "busy") return;
662
+ setStates((current) => ({
663
+ ...current,
664
+ [kind]: "busy"
665
+ }));
666
+ const answer = kind === "ide" ? await dataActions.openIde(workspace.workspaceId) : kind === "terminal" ? await dataActions.openTerminal(workspace.workspaceId) : await dataActions.start(workspace.workspaceId);
667
+ setStates((current) => ({
668
+ ...current,
669
+ [kind]: answer.ok ? "ok" : "error"
670
+ }));
671
+ if (!answer.ok) setErrors((current) => ({
672
+ ...current,
673
+ [kind]: answer.error ?? "unknown error"
674
+ }));
675
+ if (timer.current !== null) clearTimeout(timer.current);
676
+ timer.current = setTimeout(() => {
677
+ setStates({
678
+ ide: "idle",
679
+ terminal: "idle",
680
+ start: "idle"
681
+ });
682
+ }, answer.ok ? 1500 : 4e3);
683
+ };
684
+ const kindIcon = (kind) => {
685
+ if (kind === "ide") return iconFailed.ide === true ? (0, react_jsx_runtime.jsx)(DevIdeAppIcon, { size: 16 }) : (0, react_jsx_runtime.jsx)("img", {
686
+ className: SessionActions_module_css_default.appIcon,
687
+ src: editorIconUrl,
688
+ alt: "",
689
+ onError: () => {
690
+ setIconFailed((c) => ({
691
+ ...c,
692
+ ide: true
693
+ }));
694
+ }
695
+ });
696
+ if (kind === "terminal") return iconFailed.terminal === true ? (0, react_jsx_runtime.jsx)(DevTerminalAppIcon, { size: 16 }) : (0, react_jsx_runtime.jsx)("img", {
697
+ className: SessionActions_module_css_default.appIcon,
698
+ src: terminalIconUrl,
699
+ alt: "",
700
+ onError: () => {
701
+ setIconFailed((c) => ({
702
+ ...c,
703
+ terminal: true
704
+ }));
705
+ }
706
+ });
707
+ return (0, react_jsx_runtime.jsx)(StartTile, {
708
+ ideSrc: editorIconUrl,
709
+ termSrc: terminalIconUrl
710
+ });
711
+ };
712
+ return (0, react_jsx_runtime.jsx)("span", {
713
+ className: SessionActions_module_css_default.group,
714
+ children: [
715
+ "ide",
716
+ "terminal",
717
+ "start"
718
+ ].map((kind) => {
719
+ const state = states[kind];
720
+ const title = state === "error" ? t("header.error", { error: errors[kind] ?? "" }) : t(KIND_KEY[kind]);
721
+ return (0, react_jsx_runtime.jsx)("button", {
722
+ type: "button",
723
+ className: `${SessionActions_module_css_default.button} ${state === "error" ? SessionActions_module_css_default.error : ""}`,
724
+ title,
725
+ "aria-label": title,
726
+ "aria-disabled": state === "busy",
727
+ onClick: () => {
728
+ run(kind);
729
+ },
730
+ children: state === "ok" ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCheckOutlineRegular, { size: 16 }) : kindIcon(kind)
731
+ }, kind);
732
+ })
733
+ });
734
+ }
735
+ //#endregion
736
+ //#region \0dsh-css:/Users/liyu/Documents/www/DeepSeek/deepseek-harness/deepseek-harness-plugin/dev-dock/src/client/DevDockSettingsPage.module.css.mjs
737
+ const css = "._5fyaeq_page{flex-direction:column;gap:8px;display:flex}._5fyaeq_heading{color:var(--dsw-alias-label-primary);margin:12px 0 0;font-size:14px;font-weight:600}._5fyaeq_hint{color:var(--dsw-alias-label-secondary);margin:0 0 8px;font-size:12px;line-height:18px}._5fyaeq_rows{flex-direction:column;gap:6px;margin:0 0 8px;padding:0;list-style:none;display:flex}._5fyaeq_row{justify-content:space-between;align-items:center;gap:12px;display:flex}._5fyaeq_name{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:12.5px;font-weight:600;overflow:hidden}._5fyaeq_selector{background:var(--dsw-alias-bg-module-platform);height:36px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:18px;align-items:center;gap:4px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}._5fyaeq_selector:hover{background:var(--dsw-alias-interactive-bg-hover)}._5fyaeq_chevron{color:var(--dsw-alias-label-secondary);transition:transform var(--ds-transition-duration-fast) var(--ds-ease-in-out);flex:none;display:inline-flex}._5fyaeq_chevronOpen{transform:rotate(180deg)}._5fyaeq_editorRow{align-items:center;gap:10px;margin:0 0 8px;display:flex}._5fyaeq_editorName{flex:none;align-items:center;gap:8px;width:150px;display:flex}._5fyaeq_editorIcon{border-radius:5px;flex:none;width:20px;height:20px}._5fyaeq_editorLabel{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}._5fyaeq_editorInput{flex:auto;min-width:0}._5fyaeq_editorSave{flex:none}._5fyaeq_refreshRow{align-items:center;gap:10px;margin-bottom:8px;display:flex}._5fyaeq_radioRow{color:var(--dsw-alias-label-primary);cursor:pointer;align-items:center;gap:8px;margin-bottom:6px;font-size:12.5px;display:flex}._5fyaeq_error{color:var(--dsw-alias-state-error,var(--dsw-alias-label-error));font-size:12px}";
738
+ const tagId = "@liyuera/dsh-dev-dock/DevDockSettingsPage.module.css";
739
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
740
+ const tag = document.createElement("style");
741
+ tag.dataset.plugin = "@liyuera/dsh-dev-dock";
742
+ tag.dataset.pluginCss = tagId;
743
+ tag.textContent = css;
744
+ document.head.appendChild(tag);
745
+ }
746
+ var DevDockSettingsPage_module_css_default = {
747
+ "chevron": "_5fyaeq_chevron",
748
+ "chevronOpen": "_5fyaeq_chevronOpen",
749
+ "editorIcon": "_5fyaeq_editorIcon",
750
+ "editorInput": "_5fyaeq_editorInput",
751
+ "editorLabel": "_5fyaeq_editorLabel",
752
+ "editorName": "_5fyaeq_editorName",
753
+ "editorRow": "_5fyaeq_editorRow",
754
+ "editorSave": "_5fyaeq_editorSave",
755
+ "error": "_5fyaeq_error",
756
+ "heading": "_5fyaeq_heading",
757
+ "hint": "_5fyaeq_hint",
758
+ "name": "_5fyaeq_name",
759
+ "page": "_5fyaeq_page",
760
+ "radioRow": "_5fyaeq_radioRow",
761
+ "refreshRow": "_5fyaeq_refreshRow",
762
+ "row": "_5fyaeq_row",
763
+ "rows": "_5fyaeq_rows",
764
+ "selector": "_5fyaeq_selector"
765
+ };
766
+ //#endregion
767
+ //#region lib/types/client/DevDockSettingsPage.js
768
+ /**
769
+ * devDock settings page (`settings.section`): per-workspace editor
770
+ * preference, editor manual paths + detection refresh, and the terminal
771
+ * preference. Data rides the settings namespace mirror; desktop detection
772
+ * goes through the host route.
773
+ */
774
+ /** Stable union of editor names for the preference selects. */
775
+ const EDITOR_OPTIONS = EDITOR_NAMES;
776
+ /**
777
+ * dsh-style editor pill: rounded-chrome trigger plus the primitives Menu
778
+ * trailing-check popup (same pattern as the permission preset selector).
779
+ * @param props - current editor, available options, change callback.
780
+ * @returns the pill and its menu.
781
+ */
782
+ function WorkspaceEditorSelect({ value, options, onChange }) {
783
+ const [open, setOpen] = (0, react.useState)(false);
784
+ const items = [{
785
+ id: "",
786
+ label: "自动"
787
+ }, ...options.map((name) => ({
788
+ id: name,
789
+ label: name
790
+ }))];
791
+ const display = value === "" ? "自动" : value;
792
+ return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
793
+ open,
794
+ items,
795
+ selectedId: value === "" ? "" : value,
796
+ onSelect: (id) => {
797
+ setOpen(false);
798
+ onChange(id);
799
+ },
800
+ onClose: () => {
801
+ setOpen(false);
802
+ },
803
+ align: "end",
804
+ side: "bottom",
805
+ anchor: (0, react_jsx_runtime.jsxs)("button", {
806
+ type: "button",
807
+ className: DevDockSettingsPage_module_css_default.selector,
808
+ onClick: () => {
809
+ setOpen(!open);
810
+ },
811
+ children: [(0, react_jsx_runtime.jsx)("span", { children: display }), (0, react_jsx_runtime.jsx)("span", {
812
+ className: `${DevDockSettingsPage_module_css_default.chevron} ${open ? DevDockSettingsPage_module_css_default.chevronOpen : ""}`,
813
+ "aria-hidden": true,
814
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, {})
815
+ })]
816
+ })
817
+ });
818
+ }
819
+ /**
820
+ * Render the devDock settings page.
821
+ * @param props - settings runtime, data hook, actions, translator.
822
+ * @returns the settings sections.
823
+ */
824
+ function DevDockSettingsPage({ useWorkspaces, useDevDockData, dataActions, t }) {
825
+ const workspaces = useWorkspaces((state) => state.items);
826
+ const settings = useDevDockData((data) => data.settings);
827
+ const [drafts, setDrafts] = (0, react.useState)({});
828
+ const [detecting, setDetecting] = (0, react.useState)(false);
829
+ const [detectError, setDetectError] = (0, react.useState)("");
830
+ const [iconFailed, setIconFailed] = (0, react.useState)({});
831
+ const prefs = settings?.workspacePrefs ?? [];
832
+ const editors = settings?.editors ?? [];
833
+ const editorNames = [...new Set([...EDITOR_OPTIONS, ...editors.map((e) => e.name)])];
834
+ const prefOf = (workspaceId) => prefs.find((p) => p.workspaceId === workspaceId)?.editor ?? "";
835
+ const setPref = async (workspaceId, editor) => {
836
+ await dataActions.setWorkspacePref(workspaceId, editor);
837
+ };
838
+ const saveManual = async (name, value) => {
839
+ await dataActions.setEditorManualPath(name, value);
840
+ };
841
+ const refresh = async () => {
842
+ setDetecting(true);
843
+ setDetectError("");
844
+ const answer = await dataActions.listEditors();
845
+ setDetecting(false);
846
+ if (!answer.ok) setDetectError(answer.error ?? "detection failed");
847
+ };
848
+ return (0, react_jsx_runtime.jsxs)("div", {
849
+ className: DevDockSettingsPage_module_css_default.page,
850
+ children: [
851
+ (0, react_jsx_runtime.jsx)("h2", {
852
+ className: DevDockSettingsPage_module_css_default.heading,
853
+ children: t("settings.workspacePref.title")
854
+ }),
855
+ (0, react_jsx_runtime.jsx)("p", {
856
+ className: DevDockSettingsPage_module_css_default.hint,
857
+ children: t("settings.workspacePref.hint")
858
+ }),
859
+ workspaces.length === 0 && (0, react_jsx_runtime.jsx)("p", {
860
+ className: DevDockSettingsPage_module_css_default.hint,
861
+ children: t("start.noWorkspace")
862
+ }),
863
+ (0, react_jsx_runtime.jsx)("ul", {
864
+ className: DevDockSettingsPage_module_css_default.rows,
865
+ children: workspaces.map((workspace) => (0, react_jsx_runtime.jsxs)("li", {
866
+ className: DevDockSettingsPage_module_css_default.row,
867
+ children: [(0, react_jsx_runtime.jsx)("span", {
868
+ className: DevDockSettingsPage_module_css_default.name,
869
+ children: workspace.title || workspace.path
870
+ }), (0, react_jsx_runtime.jsx)(WorkspaceEditorSelect, {
871
+ value: prefOf(workspace.workspaceId),
872
+ options: editorNames,
873
+ onChange: (editor) => {
874
+ setPref(workspace.workspaceId, editor);
875
+ }
876
+ })]
877
+ }, workspace.workspaceId))
878
+ }),
879
+ (0, react_jsx_runtime.jsx)("h2", {
880
+ className: DevDockSettingsPage_module_css_default.heading,
881
+ children: t("settings.editors.title")
882
+ }),
883
+ (0, react_jsx_runtime.jsx)("p", {
884
+ className: DevDockSettingsPage_module_css_default.hint,
885
+ children: t("settings.editors.hint")
886
+ }),
887
+ (0, react_jsx_runtime.jsxs)("div", {
888
+ className: DevDockSettingsPage_module_css_default.refreshRow,
889
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
890
+ size: "sm",
891
+ variant: "outline",
892
+ onClick: () => {
893
+ refresh();
894
+ },
895
+ disabled: detecting,
896
+ children: detecting ? `${t("settings.editors.refresh")}…` : t("settings.editors.refresh")
897
+ }), detectError !== "" && (0, react_jsx_runtime.jsx)("span", {
898
+ className: DevDockSettingsPage_module_css_default.error,
899
+ role: "alert",
900
+ children: detectError
901
+ })]
902
+ }),
903
+ (0, react_jsx_runtime.jsx)("ul", {
904
+ className: DevDockSettingsPage_module_css_default.rows,
905
+ children: editorNames.map((name) => {
906
+ const editor = editors.find((e) => e.name === name);
907
+ const manual = editor?.manualPath;
908
+ const detected = editor?.detectedPath ?? "";
909
+ const draft = drafts[name] ?? manual ?? detected;
910
+ return (0, react_jsx_runtime.jsxs)("li", {
911
+ className: DevDockSettingsPage_module_css_default.editorRow,
912
+ children: [
913
+ (0, react_jsx_runtime.jsxs)("span", {
914
+ className: `${DevDockSettingsPage_module_css_default.name} ${DevDockSettingsPage_module_css_default.editorName}`,
915
+ children: [iconFailed[name] ? (0, react_jsx_runtime.jsx)(DevIdeIcon, { size: 16 }) : (0, react_jsx_runtime.jsx)("img", {
916
+ className: DevDockSettingsPage_module_css_default.editorIcon,
917
+ src: `/dev-dock/editor-icon?editor=${encodeURIComponent(name)}`,
918
+ alt: "",
919
+ onError: () => {
920
+ setIconFailed((current) => ({
921
+ ...current,
922
+ [name]: true
923
+ }));
924
+ }
925
+ }), (0, react_jsx_runtime.jsx)("span", {
926
+ className: DevDockSettingsPage_module_css_default.editorLabel,
927
+ children: name
928
+ })]
929
+ }),
930
+ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
931
+ className: DevDockSettingsPage_module_css_default.editorInput,
932
+ value: draft,
933
+ placeholder: t("settings.editors.manual"),
934
+ onChange: (event) => {
935
+ setDrafts((current) => ({
936
+ ...current,
937
+ [name]: event.target.value
938
+ }));
939
+ }
940
+ }),
941
+ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
942
+ size: "sm",
943
+ variant: "outline",
944
+ className: DevDockSettingsPage_module_css_default.editorSave,
945
+ onClick: () => {
946
+ saveManual(name, draft);
947
+ },
948
+ children: t("settings.editors.save")
949
+ })
950
+ ]
951
+ }, name);
952
+ })
953
+ }),
954
+ (0, react_jsx_runtime.jsx)("h2", {
955
+ className: DevDockSettingsPage_module_css_default.heading,
956
+ children: t("settings.terminal.title")
957
+ }),
958
+ TERMINAL_APPS.map(({ key, labelKey }) => {
959
+ const failed = iconFailed[key];
960
+ return (0, react_jsx_runtime.jsxs)("label", {
961
+ className: DevDockSettingsPage_module_css_default.radioRow,
962
+ children: [
963
+ (0, react_jsx_runtime.jsx)("input", {
964
+ type: "radio",
965
+ name: "dev-dock-terminal",
966
+ checked: (settings?.terminalApp ?? "default") === key,
967
+ onChange: () => {
968
+ dataActions.setTerminalApp(key);
969
+ }
970
+ }),
971
+ failed ? (0, react_jsx_runtime.jsx)(DevTerminalIcon, { size: 16 }) : (0, react_jsx_runtime.jsx)("img", {
972
+ className: DevDockSettingsPage_module_css_default.editorIcon,
973
+ src: `/dev-dock/terminal-icon?app=${key}`,
974
+ alt: "",
975
+ onError: () => {
976
+ setIconFailed((current) => ({
977
+ ...current,
978
+ [key]: true
979
+ }));
980
+ }
981
+ }),
982
+ (0, react_jsx_runtime.jsx)("span", { children: t(labelKey) })
983
+ ]
984
+ }, key);
985
+ })
986
+ ]
987
+ });
988
+ }
989
+ /** Terminal preference options with their icon route keys. */
990
+ const TERMINAL_APPS = [{
991
+ key: "default",
992
+ labelKey: "settings.terminal.default"
993
+ }, {
994
+ key: "iterm",
995
+ labelKey: "settings.terminal.iterm"
996
+ }];
997
+ //#endregion
998
+ //#region lib/types/client/locales.js
999
+ /**
1000
+ * devDock v2 browser copy: entry row, start-work dialog, per-session header
1001
+ * actions (IDE / terminal / start), and the settings page. Simplified from
1002
+ * v1: projects are dsh workspaces; desktop actions are deterministic.
1003
+ * @module @liyuera/dsh-dev-dock/client/locales
1004
+ */
1005
+ /** devDock dictionary keys. */
1006
+ const NS = "devDock";
1007
+ /** zh dict. */
1008
+ const zh = {
1009
+ "start.button": "开始上班",
1010
+ "start.notice": "选择要启动的工作区项目(记住上次选择)",
1011
+ "start.confirm": "启动",
1012
+ "start.cancel": "取消",
1013
+ "start.running": "正在启动…",
1014
+ "start.done": "已打开 {opened} 个编辑器、{started} 个终端窗口",
1015
+ "start.empty": "没有可用的工作区项目",
1016
+ "start.noWorkspace": "请先在工作区添加项目",
1017
+ "header.ide": "用 IDE 打开",
1018
+ "header.terminal": "打开系统终端",
1019
+ "header.start": "启动(IDE + 终端)",
1020
+ "header.noWorkspace": "当前会话没有关联工作区",
1021
+ "header.error": "操作失败:{error}",
1022
+ "settings.title": "devDock",
1023
+ "settings.workspacePref.title": "工作区编辑器偏好",
1024
+ "settings.workspacePref.hint": "给每个工作区选择打开用的 IDE;不选则按内容自动检测(uni-app → HBuilderX,其它 → WebStorm)。",
1025
+ "settings.editors.title": "编辑器",
1026
+ "settings.editors.refresh": "重新检测",
1027
+ "settings.editors.manual": "手动路径",
1028
+ "settings.editors.save": "保存",
1029
+ "settings.editors.hint": "检测不到或需要指定时,可手动填写(如 HBuilderX)。",
1030
+ "settings.terminal.title": "终端",
1031
+ "settings.terminal.default": "Terminal.app(默认)",
1032
+ "settings.terminal.iterm": "iTerm2"
1033
+ };
1034
+ /** en dict. */
1035
+ const en = {
1036
+ "start.button": "Start Work",
1037
+ "start.notice": "Select workspace projects to start (last selection remembered)",
1038
+ "start.confirm": "Start",
1039
+ "start.cancel": "Cancel",
1040
+ "start.running": "Starting…",
1041
+ "start.done": "Opened {opened} editor(s) and {started} terminal(s)",
1042
+ "start.empty": "No workspace projects available",
1043
+ "start.noWorkspace": "Add projects in the workspace area first",
1044
+ "header.ide": "Open in IDE",
1045
+ "header.terminal": "Open system terminal",
1046
+ "header.start": "Start (IDE + terminal)",
1047
+ "header.noWorkspace": "This session has no associated workspace",
1048
+ "header.error": "Action failed: {error}",
1049
+ "settings.title": "devDock",
1050
+ "settings.workspacePref.title": "Workspace editor preference",
1051
+ "settings.workspacePref.hint": "Pick the IDE each workspace opens with; auto-detected when unset (uni-app → HBuilderX, other → WebStorm).",
1052
+ "settings.editors.title": "Editors",
1053
+ "settings.editors.refresh": "Re-detect",
1054
+ "settings.editors.manual": "Manual path",
1055
+ "settings.editors.save": "Save",
1056
+ "settings.editors.hint": "Set a manual path when detection misses (e.g. HBuilderX).",
1057
+ "settings.terminal.title": "Terminal",
1058
+ "settings.terminal.default": "Terminal.app (default)",
1059
+ "settings.terminal.iterm": "iTerm2"
1060
+ };
1061
+ //#endregion
1062
+ //#region lib/types/client/index.js
1063
+ /** Required services for data binding and slot contributions. */
1064
+ const inject = ["slots", "locale"];
1065
+ /**
1066
+ * Client plugin body: register dictionaries and every surface entry.
1067
+ * @param ctx - client root context.
1068
+ */
1069
+ function apply(ctx) {
1070
+ ctx.effect(() => ctx.locale.register(NS, {
1071
+ zh,
1072
+ en
1073
+ }), "dev-dock: dictionaries");
1074
+ const t = ctx.locale.bind(NS);
1075
+ const view = createDevDockStore();
1076
+ const data = createDevDockData(ctx);
1077
+ const dataHandle = data.store;
1078
+ const dataActions = data.actions;
1079
+ const modalInjected = () => ({
1080
+ hooks: { devDockData: dataHandle },
1081
+ dataActions
1082
+ });
1083
+ const footerInjected = () => ({ hooks: { devDockData: dataHandle } });
1084
+ ctx.slots.inject("sidebar.footer.action", () => ctx.slots.register({
1085
+ name: "sidebar.footer.action",
1086
+ id: "dev-dock-start",
1087
+ order: 5,
1088
+ locale: NS,
1089
+ store: view,
1090
+ inject: footerInjected
1091
+ }, StartWorkButton));
1092
+ const composerInjected = () => ({
1093
+ dataActions,
1094
+ hooks: { devDockData: dataHandle }
1095
+ });
1096
+ ctx.slots.inject("conversation.input.right", () => ctx.slots.register({
1097
+ name: "conversation.input.right",
1098
+ id: "dev-dock-actions",
1099
+ order: 10,
1100
+ locale: NS,
1101
+ inject: composerInjected
1102
+ }, ComposerActions));
1103
+ ctx.slots.inject("shell.overlay", () => ctx.slots.register({
1104
+ name: "shell.overlay",
1105
+ id: "dev-dock-start-modal",
1106
+ order: 300,
1107
+ locale: NS,
1108
+ store: view,
1109
+ inject: modalInjected
1110
+ }, StartWorkModal));
1111
+ const settingsInjected = () => ({
1112
+ hooks: { devDockData: dataHandle },
1113
+ dataActions
1114
+ });
1115
+ ctx.slots.inject("settings.section", () => ctx.slots.register({
1116
+ name: "settings.section",
1117
+ id: "dev-dock",
1118
+ order: 12,
1119
+ label: () => t("settings.title"),
1120
+ locale: NS,
1121
+ inject: settingsInjected
1122
+ }, DevDockSettingsPage));
1123
+ }
1124
+ //#endregion
1125
+ exports.apply = apply;
1126
+ exports.inject = inject;
1127
+ return module.exports;
1128
+ }
1129
+ });
1130
+
1131
+ //# sourceMappingURL=client.js.map