@dong-victor/dsh-better-sidebar-starter 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +161 -6
- package/lib/client.js.map +1 -1
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -100,6 +100,14 @@ window.__ModuleLoader__.load({
|
|
|
100
100
|
function ClipboardIcon({ size }) {
|
|
101
101
|
return svg(size, `<path d="M19 3h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm2 14H7v-2h7v2zm3-4H7v-2h10v2zm0-4H7V7h10v2z" fill="currentColor"/>`);
|
|
102
102
|
}
|
|
103
|
+
/** 📄 Copy — duplicate a run config. */
|
|
104
|
+
function CopyIcon({ size }) {
|
|
105
|
+
return svg(size, `<path d="M21 8c0-1.1-.9-2-2-2h-9c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h9c1.1 0 2-.9 2-2V8z" fill="currentColor"/><path d="M3 16V4c0-1.1.9-2 2-2h10v2H5v12H3z" fill="currentColor" opacity="0.6"/>`);
|
|
106
|
+
}
|
|
107
|
+
/** ✈️ Send — send selected text to the conversation composer. */
|
|
108
|
+
function SendToChatIcon({ size }) {
|
|
109
|
+
return svg(size, `<path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" fill="currentColor"/>`);
|
|
110
|
+
}
|
|
103
111
|
/** Get the icon for a config type. */
|
|
104
112
|
function typeIcon(type, size) {
|
|
105
113
|
switch (type) {
|
|
@@ -141,7 +149,7 @@ window.__ModuleLoader__.load({
|
|
|
141
149
|
}
|
|
142
150
|
/** The config tree component. */
|
|
143
151
|
function ConfigTree(props) {
|
|
144
|
-
const { configs, instances, selectedInstanceId, selectedConfigId, onSelectConfig, onSelectInstance, onStartConfig, onStopInstance, onEditConfig } = props;
|
|
152
|
+
const { configs, instances, selectedInstanceId, selectedConfigId, onSelectConfig, onSelectInstance, onStartConfig, onStopInstance, onEditConfig, onDuplicateConfig } = props;
|
|
145
153
|
const [collapsed, setCollapsed] = (0, react.useState)(/* @__PURE__ */ new Set());
|
|
146
154
|
const toggleGroup = (type) => {
|
|
147
155
|
setCollapsed((prev) => {
|
|
@@ -201,7 +209,14 @@ window.__ModuleLoader__.load({
|
|
|
201
209
|
e.stopPropagation();
|
|
202
210
|
onEditConfig(config.id);
|
|
203
211
|
}
|
|
204
|
-
}, GearIcon({ size: 12 })))
|
|
212
|
+
}, GearIcon({ size: 12 })), (0, react.createElement)("button", {
|
|
213
|
+
className: "sts-icon-btn dup",
|
|
214
|
+
title: "复制(生成副本配置)",
|
|
215
|
+
onClick: (e) => {
|
|
216
|
+
e.stopPropagation();
|
|
217
|
+
onDuplicateConfig(config.id);
|
|
218
|
+
}
|
|
219
|
+
}, CopyIcon({ size: 12 }))))];
|
|
205
220
|
for (const inst of configInstances) {
|
|
206
221
|
const instSelected = selectedInstanceId === inst.id;
|
|
207
222
|
rows.push((0, react.createElement)("div", {
|
|
@@ -324,6 +339,74 @@ window.__ModuleLoader__.load({
|
|
|
324
339
|
return ANSI_COLORS[seg.bg] ?? null;
|
|
325
340
|
}
|
|
326
341
|
//#endregion
|
|
342
|
+
//#region src/client/conversationDraft.ts
|
|
343
|
+
/**
|
|
344
|
+
* Append `text` to the session's composer draft (space-separated, like the
|
|
345
|
+
* @-mentions). Returns false — and warns — when the conversation service
|
|
346
|
+
* or the session scope is unavailable.
|
|
347
|
+
*/
|
|
348
|
+
function appendToDraft(ctx, sessionId, text) {
|
|
349
|
+
try {
|
|
350
|
+
const actx = ctx.sessions?.scope(sessionId);
|
|
351
|
+
if (actx === void 0) return false;
|
|
352
|
+
const conversation = ctx.get.bind(ctx)("conversation");
|
|
353
|
+
if (conversation === void 0 || conversation === null) return false;
|
|
354
|
+
const input = conversation.input.for(actx);
|
|
355
|
+
if (input === void 0) return false;
|
|
356
|
+
const draft = input.state.getSnapshot().draft ?? "";
|
|
357
|
+
input.setDraft(draft.trim() === "" ? text : `${draft} ${text}`);
|
|
358
|
+
return true;
|
|
359
|
+
} catch (error) {
|
|
360
|
+
console.warn("[dsh-better-sidebar-starter] draft insert failed:", error);
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
//#endregion
|
|
365
|
+
//#region src/client/utils.ts
|
|
366
|
+
/**
|
|
367
|
+
* Shared utility functions for the starter plugin.
|
|
368
|
+
* @module dsh-better-sidebar-starter/client/utils
|
|
369
|
+
*/
|
|
370
|
+
let toastEl = null;
|
|
371
|
+
let toastTimer = null;
|
|
372
|
+
/** Show a small floating toast notification. */
|
|
373
|
+
function showToast(message, success) {
|
|
374
|
+
if (toastTimer !== null) {
|
|
375
|
+
clearTimeout(toastTimer);
|
|
376
|
+
toastTimer = null;
|
|
377
|
+
}
|
|
378
|
+
if (toastEl !== null) {
|
|
379
|
+
document.body.removeChild(toastEl);
|
|
380
|
+
toastEl = null;
|
|
381
|
+
}
|
|
382
|
+
const el = document.createElement("div");
|
|
383
|
+
el.textContent = message;
|
|
384
|
+
el.style.cssText = `
|
|
385
|
+
position: fixed;
|
|
386
|
+
top: 16px;
|
|
387
|
+
left: 50%;
|
|
388
|
+
transform: translateX(-50%);
|
|
389
|
+
padding: 8px 16px;
|
|
390
|
+
background: ${success ? "rgba(78,154,6,0.9)" : "rgba(235,87,87,0.9)"};
|
|
391
|
+
color: #fff;
|
|
392
|
+
font-size: 12px;
|
|
393
|
+
border-radius: 6px;
|
|
394
|
+
z-index: 999999;
|
|
395
|
+
box-shadow: 0 2px 12px rgba(0,0,0,0.4);
|
|
396
|
+
pointer-events: none;
|
|
397
|
+
font-family: ui-monospace, SFMono-Regular, "Cascadia Mono", "Cascadia Code", Menlo, Consolas, monospace;
|
|
398
|
+
`;
|
|
399
|
+
document.body.appendChild(el);
|
|
400
|
+
toastEl = el;
|
|
401
|
+
toastTimer = setTimeout(() => {
|
|
402
|
+
if (toastEl !== null) {
|
|
403
|
+
document.body.removeChild(toastEl);
|
|
404
|
+
toastEl = null;
|
|
405
|
+
}
|
|
406
|
+
toastTimer = null;
|
|
407
|
+
}, 2e3);
|
|
408
|
+
}
|
|
409
|
+
//#endregion
|
|
327
410
|
//#region src/client/LogView.tsx
|
|
328
411
|
/**
|
|
329
412
|
* Log view: the right pane showing real-time log output from a running
|
|
@@ -350,13 +433,34 @@ window.__ModuleLoader__.load({
|
|
|
350
433
|
});
|
|
351
434
|
}
|
|
352
435
|
/** The log view component. */
|
|
353
|
-
function LogView({ instance, onStop, onRestart }) {
|
|
436
|
+
function LogView({ instance, ctx, sessionId, onStop, onRestart }) {
|
|
354
437
|
const [logLines, setLogLines] = (0, react.useState)([]);
|
|
355
438
|
const [autoScroll, setAutoScroll] = (0, react.useState)(true);
|
|
356
439
|
const [exitInfo, setExitInfo] = (0, react.useState)(null);
|
|
440
|
+
const [selLength, setSelLength] = (0, react.useState)(0);
|
|
357
441
|
const contentRef = (0, react.useRef)(null);
|
|
358
442
|
const wsRef = (0, react.useRef)(null);
|
|
359
443
|
const lineKeyCounter = (0, react.useRef)(0);
|
|
444
|
+
(0, react.useEffect)(() => {
|
|
445
|
+
const updateSel = () => {
|
|
446
|
+
const sel = window.getSelection();
|
|
447
|
+
const len = sel !== null && !sel.isCollapsed ? sel.toString().trim().length : 0;
|
|
448
|
+
setSelLength(len);
|
|
449
|
+
};
|
|
450
|
+
document.addEventListener("selectionchange", updateSel);
|
|
451
|
+
return () => document.removeEventListener("selectionchange", updateSel);
|
|
452
|
+
}, []);
|
|
453
|
+
/** Send the currently selected log text into the conversation composer. */
|
|
454
|
+
const handleSendSelection = () => {
|
|
455
|
+
const sel = window.getSelection();
|
|
456
|
+
const text = sel !== null && !sel.isCollapsed ? sel.toString().trim() : "";
|
|
457
|
+
if (text === "") {
|
|
458
|
+
showToast("请先在日志中选中文本", false);
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
if (appendToDraft(ctx, sessionId, text)) showToast(`已将选中文本(${text.length} 字符)发送到对话`, true);
|
|
462
|
+
else showToast("发送失败:对话服务不可用", false);
|
|
463
|
+
};
|
|
360
464
|
const lineBuffer = (0, react.useRef)({ current: [] });
|
|
361
465
|
/** Flush the buffer into a new log line. */
|
|
362
466
|
const flushLine = (0, react.useCallback)(() => {
|
|
@@ -446,7 +550,11 @@ window.__ModuleLoader__.load({
|
|
|
446
550
|
className: "sts-icon-btn",
|
|
447
551
|
title: "清空",
|
|
448
552
|
onClick: handleClear
|
|
449
|
-
}, TrashIcon({ size: 14 }), " 清空"), (0, react.createElement)("
|
|
553
|
+
}, TrashIcon({ size: 14 }), " 清空"), (0, react.createElement)("button", {
|
|
554
|
+
className: "sts-icon-btn send",
|
|
555
|
+
title: selLength > 0 ? `发送选中的 ${selLength} 字符到对话` : "发送选中的文本到对话",
|
|
556
|
+
onClick: handleSendSelection
|
|
557
|
+
}, SendToChatIcon({ size: 14 }), selLength > 0 ? ` 发送选中(${selLength})` : " 发送选中"), (0, react.createElement)("label", {
|
|
450
558
|
className: "sts-log-toolbar-label",
|
|
451
559
|
onClick: () => setAutoScroll((v) => !v)
|
|
452
560
|
}, autoScroll ? "☑" : "☐", " 自动滚动")), (0, react.createElement)("div", {
|
|
@@ -487,7 +595,7 @@ window.__ModuleLoader__.load({
|
|
|
487
595
|
custom: "Custom"
|
|
488
596
|
};
|
|
489
597
|
/** The detail card component. */
|
|
490
|
-
function ConfigDetail({ config, onStart, onEdit, onDelete }) {
|
|
598
|
+
function ConfigDetail({ config, onStart, onEdit, onDuplicate, onDelete }) {
|
|
491
599
|
const envEntries = Object.entries(config.env);
|
|
492
600
|
const dateFmt = (ts) => {
|
|
493
601
|
if (ts === null) return "—";
|
|
@@ -500,6 +608,10 @@ window.__ModuleLoader__.load({
|
|
|
500
608
|
className: "sts-detail-btn danger",
|
|
501
609
|
onClick: onDelete
|
|
502
610
|
}, (0, react.createElement)(TrashIcon, { size: 14 }), " 删除"), (0, react.createElement)("button", {
|
|
611
|
+
className: "sts-detail-btn",
|
|
612
|
+
onClick: onDuplicate,
|
|
613
|
+
title: "复制为副本(保留全部信息)"
|
|
614
|
+
}, (0, react.createElement)(CopyIcon, { size: 14 }), " 复制"), (0, react.createElement)("button", {
|
|
503
615
|
className: "sts-detail-btn",
|
|
504
616
|
onClick: onEdit
|
|
505
617
|
}, (0, react.createElement)(GearIcon, { size: 14 }), " 编辑"), (0, react.createElement)("button", {
|
|
@@ -1324,6 +1436,17 @@ window.__ModuleLoader__.load({
|
|
|
1324
1436
|
.sts-icon-btn.stop { color: #eb5757; }
|
|
1325
1437
|
.sts-icon-btn.gear { color: var(--dsw-alias-label-secondary, #b0b0b0); }
|
|
1326
1438
|
.sts-icon-btn.trash { color: #eb5757; }
|
|
1439
|
+
.sts-icon-btn.dup { color: var(--dsw-alias-label-secondary, #b0b0b0); }
|
|
1440
|
+
.sts-icon-btn.send { color: #4a90d9; }
|
|
1441
|
+
/* Toolbar buttons (log view) are always visible and fit icon + text. */
|
|
1442
|
+
.sts-log-toolbar .sts-icon-btn {
|
|
1443
|
+
width: auto;
|
|
1444
|
+
height: 26px;
|
|
1445
|
+
padding: 0 8px;
|
|
1446
|
+
gap: 3px;
|
|
1447
|
+
font-size: 11px;
|
|
1448
|
+
opacity: 1;
|
|
1449
|
+
}
|
|
1327
1450
|
|
|
1328
1451
|
/* ===== Instance row ===== */
|
|
1329
1452
|
.sts-instance-row {
|
|
@@ -2168,6 +2291,34 @@ window.__ModuleLoader__.load({
|
|
|
2168
2291
|
});
|
|
2169
2292
|
setShowModal(true);
|
|
2170
2293
|
};
|
|
2294
|
+
/**
|
|
2295
|
+
* Duplicate a config: open the editor prefilled with ALL of the original
|
|
2296
|
+
* config's info under an auto-generated "xxx-副本" name (deduped against
|
|
2297
|
+
* existing names). Saving it (id omitted) creates a brand-new config.
|
|
2298
|
+
*/
|
|
2299
|
+
const handleDuplicateConfig = (configId) => {
|
|
2300
|
+
const config = configs.find((c) => c.id === configId);
|
|
2301
|
+
if (config === void 0) return;
|
|
2302
|
+
const stem = config.name.endsWith("-副本") ? config.name.slice(0, -3) : config.name;
|
|
2303
|
+
const taken = new Set(configs.map((c) => c.name));
|
|
2304
|
+
let dupName = `${stem}-副本`;
|
|
2305
|
+
let suffix = 2;
|
|
2306
|
+
while (taken.has(dupName)) {
|
|
2307
|
+
dupName = `${stem}-副本${suffix}`;
|
|
2308
|
+
suffix++;
|
|
2309
|
+
}
|
|
2310
|
+
setEditingConfig({
|
|
2311
|
+
name: dupName,
|
|
2312
|
+
type: config.type,
|
|
2313
|
+
command: config.command,
|
|
2314
|
+
cwd: config.cwd,
|
|
2315
|
+
env: config.env,
|
|
2316
|
+
jvmArgs: config.jvmArgs,
|
|
2317
|
+
args: config.args,
|
|
2318
|
+
runtime: config.runtime
|
|
2319
|
+
});
|
|
2320
|
+
setShowModal(true);
|
|
2321
|
+
};
|
|
2171
2322
|
/** Save a config (create or update). */
|
|
2172
2323
|
const handleSaveConfig = (0, react.useCallback)(async (config) => {
|
|
2173
2324
|
try {
|
|
@@ -2260,12 +2411,15 @@ window.__ModuleLoader__.load({
|
|
|
2260
2411
|
onStopInstance: (id) => {
|
|
2261
2412
|
handleStop(id);
|
|
2262
2413
|
},
|
|
2263
|
-
onEditConfig: handleEditConfig
|
|
2414
|
+
onEditConfig: handleEditConfig,
|
|
2415
|
+
onDuplicateConfig: handleDuplicateConfig
|
|
2264
2416
|
})), (0, react.createElement)("div", {
|
|
2265
2417
|
className: `sts-split-gutter${dragging ? " dragging" : ""}${isHorizontal ? "" : " sts-split-gutter-v"}`,
|
|
2266
2418
|
onMouseDown: handleMouseDown
|
|
2267
2419
|
}), (0, react.createElement)("div", { className: `sts-split-second${isHorizontal ? "" : " sts-split-second-v"}` }, selectedInstance !== null ? (0, react.createElement)(LogView, {
|
|
2268
2420
|
instance: selectedInstance,
|
|
2421
|
+
ctx: props.ctx,
|
|
2422
|
+
sessionId: scope.sessionId ?? store.getSnapshot().sessionId ?? "",
|
|
2269
2423
|
onStop: () => {
|
|
2270
2424
|
handleStop(selectedInstance.id);
|
|
2271
2425
|
},
|
|
@@ -2278,6 +2432,7 @@ window.__ModuleLoader__.load({
|
|
|
2278
2432
|
handleStart(selectedConfig.id);
|
|
2279
2433
|
},
|
|
2280
2434
|
onEdit: () => handleEditConfig(selectedConfig.id),
|
|
2435
|
+
onDuplicate: () => handleDuplicateConfig(selectedConfig.id),
|
|
2281
2436
|
onDelete: () => {
|
|
2282
2437
|
handleDeleteConfig(selectedConfig.id);
|
|
2283
2438
|
}
|
package/lib/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["createElement","TYPE_LABELS","useState","useMemo","createElement","createElement","useState","useRef","useCallback","TYPE_LABELS","createElement","fsTree","useState","useRef","createElement","createElement","useState","createElement","useState","useRef","useCallback","createElement","useState","createElement","createElement"],"sources":["../src/client/icons.tsx","../src/client/ConfigTree.tsx","../src/client/ansi.ts","../src/client/LogView.tsx","../src/client/ConfigDetail.tsx","../src/client/DirChooser.tsx","../src/client/PathPicker.tsx","../src/client/ConfigEditorModal.tsx","../src/client/styles.ts","../src/client/ServicesTab.tsx","../src/client/SettingsPanel.tsx","../src/client/index.tsx"],"sourcesContent":["/**\n * SVG icons for the starter plugin. Each icon is a React component that\n * takes a size prop and renders an inline SVG. Monochrome, currentColor fill.\n * @module dsh-better-sidebar-starter/client/icons\n */\n\nimport { createElement, type ReactNode } from 'react'\n\ninterface IconProps {\n size: number\n}\n\nfunction svg(size: number, pathChildren: string): ReactNode {\n const svgHtml = `<svg width=\"${size}\" height=\"${size}\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">${pathChildren}</svg>`\n return createElement('span', {\n style: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: size, height: size },\n dangerouslySetInnerHTML: { __html: svgHtml },\n })\n}\n\n/** 🚀 Rocket — the tab icon. */\nexport function RocketIcon({ size }: IconProps): ReactNode {\n return svg(size, `<path d=\"M9.19 8.35c.34-.05.67-.1 1-.13 1.2-1.49 2.85-2.49 4.85-3.02 2-.53 4.16-.42 6.5.33.75 2.34.86 4.5.33 6.5-.53 2-1.53 3.65-3.02 4.85-.04.33-.08.66-.13 1-.13.87-.32 1.71-.57 2.5-.42 1.3-1.02 2.5-1.78 3.6-.42.6-1.1.96-1.86.96-.72 0-1.4-.34-1.84-.93l-2.06-2.76c-.2-.27-.5-.43-.82-.43H7.68c-.32 0-.62-.16-.82-.43l-2.06-2.76C4.36 19.34 4 18.66 4 17.94c0-.76.36-1.44.96-1.86 1.1-.76 2.3-1.36 3.6-1.78.79-.25 1.63-.44 2.5-.57l.13-1c.05-.34.1-.67.13-1z\" fill=\"currentColor\" opacity=\"0.85\"/><circle cx=\"15.5\" cy=\"8.5\" r=\"1.5\" fill=\"currentColor\" opacity=\"0.6\"/><path d=\"M5 14c-1 0-2 .5-2 2 0 1 .5 2 .5 2s1-.5 1.5-.5 1.5.5 1.5.5 1-.5 1-2-.5-2-2-2z\" fill=\"currentColor\" opacity=\"0.5\"/>`)\n}\n\n/** ▶ Play — start button. */\nexport function PlayIcon({ size }: IconProps): ReactNode {\n return svg(size, `<path d=\"M8 5v14l11-7z\" fill=\"currentColor\"/>`)\n}\n\n/** ⏹ Stop — stop button. */\nexport function StopIcon({ size }: IconProps): ReactNode {\n return svg(size, `<rect x=\"6\" y=\"6\" width=\"12\" height=\"12\" rx=\"1\" fill=\"currentColor\"/>`)\n}\n\n/** 🔄 Restart — restart button. */\nexport function RestartIcon({ size }: IconProps): ReactNode {\n return svg(size, `<path d=\"M12 5V2L7 7l5 5V8c2.76 0 5 2.24 5 5s-2.24 5-5 5-5-2.24-5-5H5c0 3.87 3.13 7 7 7s7-3.13 7-7-3.13-7-7-7z\" fill=\"currentColor\"/>`)\n}\n\n/** ⚙ Gear — edit button. */\nexport function GearIcon({ size }: IconProps): ReactNode {\n return svg(size, `<path d=\"M12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm8.94 3l-1.97-.39c-.11-.37-.26-.72-.44-1.06l1.14-1.64c.26-.37.22-.88-.09-1.2l-1.28-1.28c-.32-.31-.83-.35-1.2-.09l-1.64 1.14c-.34-.18-.69-.33-1.06-.44L14.94 4c-.1-.42-.47-.72-.9-.72h-1.8c-.43 0-.8.3-.9.72l-.39 1.97c-.37.11-.72.26-1.06.44L7.25 5.27c-.37-.26-.88-.22-1.2.09L4.77 6.64c-.31.32-.35.83-.09 1.2l1.14 1.64c-.18.34-.33.69-.44 1.06L3.4 11c-.42.1-.72.47-.72.9v1.8c0 .43.3.8.72.9l1.97.39c.11.37.26.72.44 1.06l-1.14 1.64c-.26.37-.22.88.09 1.2l1.28 1.28c.32.31.83.35 1.2.09l1.64-1.14c.34.18.69.33 1.06.44l.39 1.97c.1.42.47.72.9.72h1.8c.43 0 .8-.3.9-.72l.39-1.97c.37-.11.72-.26 1.06-.44l1.64 1.14c.37.26.88.22 1.2-.09l1.28-1.28c.31-.32.35-.83.09-1.2l-1.14-1.64c.18-.34.33-.69.44-1.06L20.6 14c.42-.1.72-.47.72-.9v-1.8c0-.43-.3-.8-.72-.9z\" fill=\"currentColor\"/>`)\n}\n\n/** 🗑 Trash — delete button. */\nexport function TrashIcon({ size }: IconProps): ReactNode {\n return svg(size, `<path d=\"M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z\" fill=\"currentColor\"/>`)\n}\n\n/** ➕ Plus — add new config. */\nexport function PlusIcon({ size }: IconProps): ReactNode {\n return svg(size, `<path d=\"M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\" fill=\"currentColor\"/>`)\n}\n\n/** ✕ Close — modal cancel. */\nexport function CloseIcon({ size }: IconProps): ReactNode {\n return svg(size, `<path d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\" fill=\"currentColor\"/>`)\n}\n\n/** ▾ Chevron down — tree expand. */\nexport function ChevronDownIcon({ size }: IconProps): ReactNode {\n return svg(size, `<path d=\"M7 10l5 5 5-5z\" fill=\"currentColor\"/>`)\n}\n\n/** ▸ Chevron right — tree collapse. */\nexport function ChevronRightIcon({ size }: IconProps): ReactNode {\n return svg(size, `<path d=\"M10 7l5 5-5 5z\" fill=\"currentColor\"/>`)\n}\n\n/** 🟢 Dot — running status. */\nexport function RunningDotIcon({ size }: IconProps): ReactNode {\n return svg(size, `<circle cx=\"12\" cy=\"12\" r=\"5\" fill=\"#4e9a06\"/>`)\n}\n\n/** ✅ Check — completed status. */\nexport function CheckIcon({ size }: IconProps): ReactNode {\n return svg(size, `<path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\" fill=\"#4e9a06\"/>`)\n}\n\n/** 🔴 Error dot — failed status. */\nexport function ErrorDotIcon({ size }: IconProps): ReactNode {\n return svg(size, `<circle cx=\"12\" cy=\"12\" r=\"5\" fill=\"#cc0000\"/>`)\n}\n\n/** 📋 Log — download log. */\nexport function DownloadIcon({ size }: IconProps): ReactNode {\n return svg(size, `<path d=\"M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z\" fill=\"currentColor\"/>`)\n}\n\n/** 📦 npm package icon. */\nexport function NpmIcon({ size }: IconProps): ReactNode {\n return svg(size, `<rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/><path d=\"M7 17V8h4v6h2V8h4v9h-4v-3h-2v3H7z\" fill=\"currentColor\"/>`)\n}\n\n/** ☕ springboot icon. */\nexport function SpringIcon({ size }: IconProps): ReactNode {\n return svg(size, `<path d=\"M12 2C7.5 2 4 5.5 4 10c0 3 2 5.5 4.5 7 .5-2 1.5-3.5 3-4.5 1.5 1 2.5 2.5 3 4.5 2.5-1.5 4.5-4 4.5-7 0-4.5-3.5-8-7-8z\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/><path d=\"M8 10c1-1 2.5-1.5 4-1.5s3 .5 4 1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>`)\n}\n\n/** 🐍 python icon. */\nexport function PythonIcon({ size }: IconProps): ReactNode {\n return svg(size, `<path d=\"M12 4c-2 0-4 1-4 3v2h4v1H6c-1 0-2 1-2 3s1 3 2 3h2v-2c0-2 2-3 4-3h2c1 0 2-1 2-2V7c0-2-2-3-4-3z\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/><circle cx=\"10.5\" cy=\"6.5\" r=\"0.8\" fill=\"currentColor\"/><path d=\"M12 20c2 0 4-1 4-3v-2h-4v-1h4c1 0 2-1 2-3s-1-3-2-3h-2v2c0 2-2 3-4 3h-2c-1 0-2 1-2 2v3c0 2 2 3 4 3z\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/><circle cx=\"13.5\" cy=\"17.5\" r=\"0.8\" fill=\"currentColor\"/>`)\n}\n\n/** ⚡ custom icon. */\nexport function CustomIcon({ size }: IconProps): ReactNode {\n return svg(size, `<path d=\"M11 21h-1l1-7H7.5c-.6 0-.8-.4-.5-.9 1.5-2.6 3.7-6.2 6.5-10.1h1l-1 7h3.5c.6 0 .8.4.5.9C14.5 14.7 12.3 18.1 11 21z\" fill=\"currentColor\"/>`)\n}\n\n/** 📁 Folder — directory tree icon. */\nexport function FolderIcon({ size }: IconProps): ReactNode {\n return svg(size, `<path d=\"M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z\" fill=\"currentColor\" opacity=\"0.7\"/>`)\n}\n\n/** 📋 Clipboard — bulk paste env. */\nexport function ClipboardIcon({ size }: IconProps): ReactNode {\n return svg(size, `<path d=\"M19 3h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm2 14H7v-2h7v2zm3-4H7v-2h10v2zm0-4H7V7h10v2z\" fill=\"currentColor\"/>`)\n}\n\n/** Get the icon for a config type. */\nexport function typeIcon(type: string, size: number): ReactNode {\n switch (type) {\n case 'npm': return NpmIcon({ size })\n case 'springboot': return SpringIcon({ size })\n case 'python': return PythonIcon({ size })\n default: return CustomIcon({ size })\n }\n}\n","/**\n * Config tree: the left pane showing all run configurations grouped by type,\n * with running instances as child nodes.\n * @module dsh-better-sidebar-starter/client/ConfigTree\n */\n\nimport { createElement, useState, useMemo, type ReactNode } from 'react'\nimport type { RunConfig, RunInstance } from './types.ts'\nimport {\n PlayIcon, StopIcon, GearIcon,\n ChevronDownIcon, ChevronRightIcon,\n RunningDotIcon, CheckIcon, ErrorDotIcon,\n typeIcon,\n} from './icons.tsx'\n\n/** Props for the config tree. */\nexport interface ConfigTreeProps {\n configs: RunConfig[]\n instances: RunInstance[]\n selectedInstanceId: string | null\n selectedConfigId: string | null\n onSelectConfig: (configId: string) => void\n onSelectInstance: (instanceId: string) => void\n onStartConfig: (configId: string) => void\n onStopInstance: (instanceId: string) => void\n onEditConfig: (configId: string) => void\n}\n\n/** Type group order. */\nconst TYPE_ORDER: string[] = ['npm', 'springboot', 'python', 'custom']\nconst TYPE_LABELS: Record<string, string> = {\n npm: 'npm', springboot: 'Spring Boot', python: 'Python', custom: 'Custom',\n}\n\n/** Status icon for an instance. */\nfunction statusIcon(status: string, size: number): ReactNode {\n switch (status) {\n case 'running': return RunningDotIcon({ size })\n case 'exited': return CheckIcon({ size })\n case 'killed':\n case 'error': return ErrorDotIcon({ size })\n default: return null\n }\n}\n\n/** The config tree component. */\nexport function ConfigTree(props: ConfigTreeProps): ReactNode {\n const { configs, instances, selectedInstanceId, selectedConfigId,\n onSelectConfig, onSelectInstance, onStartConfig, onStopInstance, onEditConfig } = props\n\n const [collapsed, setCollapsed] = useState<Set<string>>(new Set())\n\n const toggleGroup = (type: string): void => {\n setCollapsed((prev) => {\n const next = new Set(prev)\n if (next.has(type)) next.delete(type)\n else next.add(type)\n return next\n })\n }\n\n // Group configs by type, ordered by TYPE_ORDER, sorted by lastRunAt desc.\n const groups = useMemo(() => {\n const map = new Map<string, RunConfig[]>()\n for (const config of configs) {\n const arr = map.get(config.type) ?? []\n arr.push(config)\n map.set(config.type, arr)\n }\n for (const arr of map.values()) {\n arr.sort((a, b) => (b.lastRunAt ?? 0) - (a.lastRunAt ?? 0))\n }\n return TYPE_ORDER\n .filter((type) => map.has(type) && map.get(type)!.length > 0)\n .map((type) => ({ type, items: map.get(type)! }))\n }, [configs])\n\n if (configs.length === 0) {\n return createElement('div', { className: 'sts-empty' },\n '暂无运行配置',\n createElement('br'),\n '点击右上角 + 新建配置',\n )\n }\n\n return createElement('div', { className: 'sts-tree' },\n ...groups.map((group) => {\n const isCollapsed = collapsed.has(group.type)\n const label = TYPE_LABELS[group.type] ?? group.type\n const runningCount = instances.filter(\n (inst) => inst.configType === group.type && inst.status === 'running',\n ).length\n\n return createElement('div', { key: group.type },\n // Group header\n createElement('div', {\n className: 'sts-group-header',\n onClick: () => toggleGroup(group.type),\n },\n isCollapsed\n ? createElement(ChevronRightIcon, { size: 12 })\n : createElement(ChevronDownIcon, { size: 12 }),\n typeIcon(group.type, 14),\n label,\n createElement('span', { className: 'sts-group-count' },\n ` (${group.items.length}${runningCount > 0 ? `, ${runningCount} 运行中` : ''})`,\n ),\n ),\n // Group children\n createElement('div', {\n className: `sts-group-children${isCollapsed ? ' collapsed' : ''}`,\n },\n ...group.items.flatMap((config) => {\n const configInstances = instances.filter((inst) => inst.configId === config.id)\n const isRunning = configInstances.some((inst) => inst.status === 'running')\n const isSelected = selectedConfigId === config.id && selectedInstanceId === null\n\n const rows: ReactNode[] = [\n // Config row\n createElement('div', {\n key: config.id,\n className: `sts-config-row${isSelected ? ' selected' : ''}${isRunning ? ' running' : ''}`,\n onClick: () => onSelectConfig(config.id),\n },\n isRunning\n ? RunningDotIcon({ size: 12 })\n : typeIcon(config.type, 12),\n createElement('div', { className: 'sts-config-name' }, config.name),\n createElement('div', { className: 'sts-config-cmd' }, config.command),\n createElement('div', { className: 'sts-config-actions' },\n isRunning\n ? createElement('button', {\n className: 'sts-icon-btn stop',\n title: '停止全部',\n onClick: (e: MouseEvent) => {\n e.stopPropagation()\n for (const inst of configInstances) {\n if (inst.status === 'running') onStopInstance(inst.id)\n }\n },\n }, StopIcon({ size: 14 }))\n : createElement('button', {\n className: 'sts-icon-btn play',\n title: '启动',\n onClick: (e: MouseEvent) => {\n e.stopPropagation()\n onStartConfig(config.id)\n },\n }, PlayIcon({ size: 14 })),\n createElement('button', {\n className: 'sts-icon-btn gear',\n title: '编辑',\n onClick: (e: MouseEvent) => {\n e.stopPropagation()\n onEditConfig(config.id)\n },\n }, GearIcon({ size: 12 })),\n ),\n ),\n ]\n\n // Instance rows (children)\n for (const inst of configInstances) {\n const instSelected = selectedInstanceId === inst.id\n rows.push(\n createElement('div', {\n key: inst.id,\n className: `sts-instance-row${instSelected ? ' selected' : ''}`,\n onClick: () => onSelectInstance(inst.id),\n },\n statusIcon(inst.status, 10),\n `#${inst.id.slice(0, 8)}`,\n createElement('span', { className: 'sts-instance-pid' },\n inst.status === 'running' ? ` pid:${inst.pid}` : ` ${inst.status}`,\n ),\n inst.status === 'running'\n ? createElement('button', {\n className: 'sts-icon-btn stop',\n title: '停止',\n onClick: (e: MouseEvent) => {\n e.stopPropagation()\n onStopInstance(inst.id)\n },\n }, StopIcon({ size: 12 }))\n : null,\n ),\n )\n }\n\n return rows\n }),\n ),\n )\n }),\n )\n}\n","/**\n * ANSI escape code parser: converts raw terminal output with ANSI SGR\n * sequences into an array of styled text segments for React rendering.\n * Covers the common color codes (30-37, 40-47, 90-97, 100-107, bold, dim,\n * italic, underline, reset). Non-SGR escape sequences are stripped.\n * @module dsh-better-sidebar-starter/client/ansi\n */\n\n/** One styled text segment. */\nexport interface AnsiSegment {\n text: string\n bold: boolean\n dim: boolean\n italic: boolean\n underline: boolean\n fg: number | null\n bg: number | null\n}\n\ninterface ParseState {\n bold: boolean\n dim: boolean\n italic: boolean\n underline: boolean\n fg: number | null\n bg: number | null\n}\n\nfunction cloneState(s: ParseState): AnsiSegment {\n return {\n text: '',\n bold: s.bold,\n dim: s.dim,\n italic: s.italic,\n underline: s.underline,\n fg: s.fg,\n bg: s.bg,\n }\n}\n\n/** Apply one SGR parameter to the parse state. */\nfunction applySgr(state: ParseState, code: number): void {\n if (code === 0) {\n state.bold = false; state.dim = false; state.italic = false\n state.underline = false; state.fg = null; state.bg = null\n } else if (code === 1) {\n state.bold = true\n } else if (code === 2) {\n state.dim = true\n } else if (code === 3) {\n state.italic = true\n } else if (code === 4) {\n state.underline = true\n } else if (code === 22) {\n state.bold = false; state.dim = false\n } else if (code === 23) {\n state.italic = false\n } else if (code === 24) {\n state.underline = false\n } else if (code >= 30 && code <= 37) {\n state.fg = code - 30\n } else if (code === 39) {\n state.fg = null\n } else if (code >= 40 && code <= 47) {\n state.bg = code - 40\n } else if (code === 49) {\n state.bg = null\n } else if (code >= 90 && code <= 97) {\n state.fg = code - 90 + 8\n } else if (code >= 100 && code <= 107) {\n state.bg = code - 100 + 8\n }\n}\n\n/**\n * Parse a string with ANSI escape codes into styled segments.\n * Returns an array of AnsiSegment objects ready for React rendering.\n */\nexport function parseAnsi(input: string): AnsiSegment[] {\n const state: ParseState = {\n bold: false, dim: false, italic: false, underline: false, fg: null, bg: null,\n }\n const segments: AnsiSegment[] = []\n let current = cloneState(state)\n\n // Regex: match CSI sequences (ESC [ ... letter) and non-CSI text.\n const regex = /\\x1b\\[([0-9;]*)m|\\x1b\\][^\\x07]*\\x07|\\x1b\\[[0-9;]*[A-Za-z]/g\n let lastIndex = 0\n let match: RegExpExecArray | null\n\n while ((match = regex.exec(input)) !== null) {\n // Text before this escape sequence.\n if (match.index > lastIndex) {\n current.text += input.slice(lastIndex, match.index)\n }\n // Process the escape sequence.\n if (match[1] !== undefined) {\n // SGR sequence: ESC [ ... m\n if (current.text !== '') {\n segments.push(current)\n }\n const params = match[1] === '' ? '0' : match[1]\n for (const codeStr of params.split(';')) {\n const code = codeStr === '' ? 0 : Number(codeStr)\n if (!Number.isNaN(code)) applySgr(state, code)\n }\n current = cloneState(state)\n }\n // Non-SGR sequences (OSC, other CSI) are simply stripped.\n lastIndex = regex.lastIndex\n }\n\n // Remaining text after the last escape.\n if (lastIndex < input.length) {\n current.text += input.slice(lastIndex)\n }\n if (current.text !== '') {\n segments.push(current)\n }\n\n return segments\n}\n\n/** Map an ANSI color index (0-15) to a CSS color string. */\nconst ANSI_COLORS: readonly string[] = [\n // 0-7: standard colors\n '#000000', '#cc0000', '#4e9a06', '#c4a000', '#3465a4', '#75507b', '#06989a', '#d3d7cf',\n // 8-15: bright colors\n '#555753', '#ef2929', '#8ae234', '#fce94f', '#729fcf', '#ad7fa8', '#34e2e2', '#eeeeec',\n]\n\n/** Resolve a segment's foreground color to a CSS string, or null for default. */\nexport function segmentFgColor(seg: AnsiSegment): string | null {\n if (seg.fg === null) return null\n return ANSI_COLORS[seg.fg] ?? null\n}\n\n/** Resolve a segment's background color to a CSS string, or null for default. */\nexport function segmentBgColor(seg: AnsiSegment): string | null {\n if (seg.bg === null) return null\n return ANSI_COLORS[seg.bg] ?? null\n}\n","/**\n * Log view: the right pane showing real-time log output from a running\n * instance. Connects to the WebSocket endpoint, parses ANSI colors, and\n * renders log lines with auto-scroll.\n * @module dsh-better-sidebar-starter/client/LogView\n */\n\nimport { createElement, useState, useEffect, useRef, useCallback, type ReactNode } from 'react'\nimport type { LogEntry, RunInstance } from './types.ts'\nimport type { WsMessage } from './types.ts'\nimport { parseAnsi, segmentFgColor, segmentBgColor, type AnsiSegment } from './ansi.ts'\nimport { StopIcon, RestartIcon, TrashIcon } from './icons.tsx'\n\n/** Props for the log view. */\nexport interface LogViewProps {\n instance: RunInstance\n onStop: () => void\n onRestart: () => void\n}\n\n/** One rendered log line. */\ninterface LogLine {\n entries: LogEntry[]\n key: number\n}\n\n/** Render ANSI-colored text segments. */\nfunction renderAnsiLine(text: string, keyBase: string): ReactNode {\n const segments = parseAnsi(text)\n return segments.map((seg: AnsiSegment, i: number) => {\n const style: Record<string, string> = {}\n const fg = segmentFgColor(seg)\n const bg = segmentBgColor(seg)\n if (fg !== null) style.color = fg\n if (bg !== null) style.backgroundColor = bg\n if (seg.bold) style.fontWeight = 'bold'\n if (seg.dim) style.opacity = '0.6'\n if (seg.italic) style.fontStyle = 'italic'\n if (seg.underline) style.textDecoration = 'underline'\n return createElement('span', { key: `${keyBase}-${i}`, style }, seg.text)\n })\n}\n\n/** The log view component. */\nexport function LogView({ instance, onStop, onRestart }: LogViewProps): ReactNode {\n const [logLines, setLogLines] = useState<LogLine[]>([])\n const [autoScroll, setAutoScroll] = useState(true)\n const [exitInfo, setExitInfo] = useState<{ status: string; exitCode: number | null } | null>(null)\n const contentRef = useRef<HTMLDivElement>(null)\n const wsRef = useRef<WebSocket | null>(null)\n const lineKeyCounter = useRef(0)\n\n // Buffer accumulation: accumulate text entries into complete lines.\n const lineBuffer = useRef<{ current: LogEntry[] }>({ current: [] })\n\n /** Flush the buffer into a new log line. */\n const flushLine = useCallback((): void => {\n const entries = lineBuffer.current.current\n if (entries.length === 0) return\n lineBuffer.current.current = []\n setLogLines((prev) => {\n const key = lineKeyCounter.current++\n // Cap at 5000 lines to avoid memory issues.\n const next = [...prev, { entries, key }]\n if (next.length > 5000) next.splice(0, next.length - 5000)\n return next\n })\n }, [])\n\n /** Add an entry to the current line buffer, splitting on newlines. */\n const addEntry = useCallback((entry: LogEntry): void => {\n const parts = entry.text.split('\\n')\n for (let i = 0; i < parts.length; i++) {\n if (i > 0) {\n // Newline boundary: flush the current line.\n flushLine()\n }\n if (parts[i] !== '') {\n lineBuffer.current.current.push({\n stream: entry.stream,\n text: parts[i],\n ts: entry.ts,\n })\n }\n }\n // If the entry ends with \\n, flush.\n if (parts.length > 1 && parts[parts.length - 1] === '') {\n flushLine()\n }\n }, [flushLine])\n\n // Connect WebSocket.\n useEffect(() => {\n const url = `/api/dsh-better-sidebar-starter/logs?instanceId=${encodeURIComponent(instance.id)}`\n const ws = new WebSocket(url)\n wsRef.current = ws\n\n ws.onmessage = (event: MessageEvent): void => {\n try {\n const msg = JSON.parse(event.data as string) as WsMessage\n if (msg.type === 'history') {\n setLogLines([])\n lineBuffer.current.current = []\n for (const entry of msg.entries) {\n addEntry(entry)\n }\n flushLine()\n } else if (msg.type === 'log') {\n addEntry(msg.entry)\n } else if (msg.type === 'status') {\n if (msg.status !== 'running') {\n flushLine()\n setExitInfo({ status: msg.status, exitCode: msg.exitCode })\n }\n }\n } catch { /* ignore parse errors */ }\n }\n\n return () => {\n ws.close()\n wsRef.current = null\n }\n }, [instance.id, addEntry, flushLine])\n\n // Auto-scroll to bottom when new lines arrive.\n useEffect(() => {\n if (autoScroll && contentRef.current !== null) {\n contentRef.current.scrollTop = contentRef.current.scrollHeight\n }\n }, [logLines, autoScroll])\n\n const handleClear = (): void => {\n setLogLines([])\n lineBuffer.current.current = []\n }\n\n return createElement('div', { style: { display: 'flex', flexDirection: 'column', height: '100%', minHeight: 0 } },\n // Toolbar\n createElement('div', { className: 'sts-log-toolbar' },\n instance.configName,\n createElement('span', { style: { opacity: 0.5, marginLeft: '4px' } }, `#${instance.id.slice(0, 8)}`),\n createElement('div', { className: 'sts-log-toolbar-spacer' }),\n instance.status === 'running'\n ? createElement('button', {\n className: 'sts-icon-btn stop',\n title: '停止',\n onClick: onStop,\n }, StopIcon({ size: 14 }), ' 停止')\n : null,\n createElement('button', {\n className: 'sts-icon-btn',\n title: '重启',\n onClick: onRestart,\n }, RestartIcon({ size: 14 }), ' 重启'),\n createElement('button', {\n className: 'sts-icon-btn',\n title: '清空',\n onClick: handleClear,\n }, TrashIcon({ size: 14 }), ' 清空'),\n createElement('label', {\n className: 'sts-log-toolbar-label',\n onClick: () => setAutoScroll((v) => !v),\n },\n autoScroll ? '☑' : '☐',\n ' 自动滚动',\n ),\n ),\n // Log content\n createElement('div', { ref: contentRef, className: 'sts-log-content' },\n ...logLines.map((line) => {\n const keyStr = `line-${line.key}`\n return createElement('div', { key: keyStr, className: 'sts-log-line' },\n ...line.entries.map((entry, i) => {\n const segKey = `${keyStr}-e${i}`\n return createElement('span', {\n key: segKey,\n className: entry.stream === 'stderr' ? 'sts-log-stderr' : '',\n }, renderAnsiLine(entry.text, segKey))\n }),\n )\n }),\n // Exit info\n exitInfo !== null\n ? createElement('div', {\n key: 'exit-info',\n style: {\n color: exitInfo.status === 'exited' && exitInfo.exitCode === 0 ? '#4e9a06' : '#cc0000',\n fontWeight: 'bold',\n marginTop: '4px',\n },\n },\n `\\n进程${exitInfo.status === 'exited' ? '退出' : exitInfo.status === 'killed' ? '被终止' : '出错'}`,\n exitInfo.exitCode !== null ? `,退出码: ${exitInfo.exitCode}` : '',\n )\n : null,\n ),\n )\n}\n","/**\n * Config detail card: shown in the right pane when an idle config is selected.\n * Displays the config's full info and offers start / edit / delete buttons.\n * @module dsh-better-sidebar-starter/client/ConfigDetail\n */\n\nimport { createElement } from 'react'\nimport type { ReactNode } from 'react'\nimport type { RunConfig } from './types.ts'\nimport { PlayIcon, GearIcon, TrashIcon, typeIcon } from './icons.tsx'\n\n/** Props for the detail card. */\nexport interface ConfigDetailProps {\n config: RunConfig\n onStart: () => void\n onEdit: () => void\n onDelete: () => void\n}\n\nconst TYPE_LABELS: Record<string, string> = {\n npm: 'npm',\n springboot: 'Spring Boot',\n python: 'Python',\n custom: 'Custom',\n}\n\n/** The detail card component. */\nexport function ConfigDetail({ config, onStart, onEdit, onDelete }: ConfigDetailProps): ReactNode {\n const envEntries = Object.entries(config.env)\n const dateFmt = (ts: number | null): string => {\n if (ts === null) return '—'\n return new Date(ts).toLocaleString('zh-CN', { hour12: false })\n }\n\n return createElement('div', { className: 'sts-detail' },\n createElement('div', { className: 'sts-detail-card' },\n // Title\n createElement('div', { className: 'sts-detail-title' },\n typeIcon(config.type, 20),\n config.name,\n ),\n // Type\n createElement('div', { className: 'sts-detail-row' },\n createElement('div', { className: 'sts-detail-label' }, '类型'),\n createElement('div', { className: 'sts-detail-value' }, TYPE_LABELS[config.type] ?? config.type),\n ),\n // Command\n createElement('div', { className: 'sts-detail-row' },\n createElement('div', { className: 'sts-detail-label' }, '命令'),\n createElement('div', { className: 'sts-detail-value' }, config.command),\n ),\n // Cwd\n createElement('div', { className: 'sts-detail-row' },\n createElement('div', { className: 'sts-detail-label' }, '工作目录'),\n createElement('div', { className: 'sts-detail-value' }, config.cwd),\n ),\n // JVM args (springboot only)\n config.type === 'springboot' && config.jvmArgs\n ? createElement('div', { className: 'sts-detail-row' },\n createElement('div', { className: 'sts-detail-label' }, 'JVM参数'),\n createElement('div', { className: 'sts-detail-value' }, config.jvmArgs),\n )\n : null,\n // Program args\n config.args\n ? createElement('div', { className: 'sts-detail-row' },\n createElement('div', { className: 'sts-detail-label' }, '程序实参'),\n createElement('div', { className: 'sts-detail-value' }, config.args),\n )\n : null,\n // Env\n envEntries.length > 0\n ? createElement('div', { className: 'sts-detail-row' },\n createElement('div', { className: 'sts-detail-label' }, '环境变量'),\n createElement('div', { style: { flex: 1 } },\n ...envEntries.map(([key, value]) =>\n createElement('div', { key, className: 'sts-detail-value' },\n `${key} = ${value}`,\n ),\n ),\n ),\n )\n : null,\n // Timestamps\n createElement('div', { className: 'sts-detail-row' },\n createElement('div', { className: 'sts-detail-label' }, '创建时间'),\n createElement('div', { className: 'sts-detail-value' }, dateFmt(config.createdAt)),\n ),\n config.lastRunAt !== null\n ? createElement('div', { className: 'sts-detail-row' },\n createElement('div', { className: 'sts-detail-label' }, '最近运行'),\n createElement('div', { className: 'sts-detail-value' }, dateFmt(config.lastRunAt)),\n )\n : null,\n // Actions\n createElement('div', { className: 'sts-detail-actions' },\n createElement('button', { className: 'sts-detail-btn danger', onClick: onDelete },\n createElement(TrashIcon, { size: 14 }), ' 删除',\n ),\n createElement('button', { className: 'sts-detail-btn', onClick: onEdit },\n createElement(GearIcon, { size: 14 }), ' 编辑',\n ),\n createElement('button', {\n className: 'sts-detail-btn primary',\n onClick: onStart,\n style: {\n background: '#1a1a1e',\n border: '1px solid #3a3a3e',\n color: '#fff',\n borderRadius: '6px',\n padding: '6px 14px',\n cursor: 'pointer',\n fontSize: '12px',\n },\n },\n createElement(PlayIcon, { size: 14 }), ' 启动',\n ),\n ),\n ),\n )\n}\n","/**\n * Shared directory chooser: a filesystem folder tree (backed by the sidebar\n * fs.tree API, which can list any absolute path) plus a browse button that\n * opens a modal popup. Browsing starts at the user's home directory and can\n * navigate up to any drive/root, so runtime tool paths (JDK/Maven/...) that\n * live outside the workspace can be selected.\n * @module dsh-better-sidebar-starter/client/DirChooser\n */\n\nimport { createElement, useState, useEffect, useRef, type ReactNode } from 'react'\nimport type { SessionScope } from 'dsh-better-sidebar/client/service'\nimport { FolderIcon, ChevronRightIcon, ChevronDownIcon, CloseIcon, CheckIcon } from './icons.tsx'\n\n/** One directory entry from the sidebar fs.tree API. */\nexport interface FsEntry {\n name: string\n path: string\n isDir: boolean\n hidden: boolean\n isSymlink: boolean\n broken: boolean\n}\n\n/** Call the sidebar fs.tree API directly via fetch. Returns directories only. */\nexport async function fsTree(scope: SessionScope, path: string): Promise<FsEntry[]> {\n const payload: Record<string, string> = { sessionId: scope.sessionId, path }\n if (scope.cwd !== undefined && scope.cwd !== '') payload.cwd = scope.cwd\n const resp = await fetch('/sidebar/api/fs.tree', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!resp.ok) throw new Error(`fs.tree ${path}: ${resp.status}`)\n const parsed = await resp.json() as { ok?: boolean; value?: { entries: FsEntry[] }; error?: { message: string } }\n if (!parsed.ok || parsed.value === undefined) {\n throw new Error(`fs.tree ${path}: ${parsed.error?.message ?? 'unknown error'}`)\n }\n return parsed.value.entries.filter((e) => e.isDir && !e.hidden)\n}\n\n/** Fetch the user's home directory from the host. Falls back to cwd. */\nasync function fetchHomeDefault(scope: SessionScope): Promise<string> {\n try {\n const r = await fetch('/api/dsh-better-sidebar-starter/detect-env', { method: 'GET' })\n if (r.ok) {\n const data = await r.json() as { home?: string }\n if (data.home && data.home !== '') return data.home\n }\n } catch { /* ignore */ }\n return scope.cwd ?? '.'\n}\n\n/** Normalize a path, returning its parent or null if at the root. */\nfunction parentOf(p: string): string | null {\n const trimmed = p.replace(/[\\\\/]+$/, '')\n const lastSep = Math.max(trimmed.lastIndexOf('/'), trimmed.lastIndexOf('\\\\'))\n if (lastSep <= 0) return null\n return trimmed.substring(0, lastSep)\n}\n\n/** The folder tree panel: shows the current dir's subdirectories with up-nav. */\nexport function DirTree(props: {\n scope: SessionScope\n selectedPath: string\n onPick: (path: string) => void\n}): ReactNode {\n const { scope, selectedPath, onPick } = props\n const [cur, setCur] = useState<string>('.') // current directory being browsed\n const [entries, setEntries] = useState<FsEntry[] | null>(null)\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const [expanded, setExpanded] = useState<Set<string>>(new Set())\n\n // Stash scope in a ref so the init effect only runs once regardless of the\n // parent re-rendering with a fresh scope object each frame.\n const scopeRef = useRef(scope)\n const initDoneRef = useRef(false)\n // Remember the initial selected value so the popup opens at, or as close to\n // as possible, the already-configured path.\n const initialValueRef = useRef(selectedPath)\n\n // Initialize browsing root: prefer an existing selected path, else home.\n useEffect(() => {\n if (initDoneRef.current) return\n initDoneRef.current = true\n let cancelled = false\n const s = scopeRef.current\n const startPath = initialValueRef.current?.trim() && initialValueRef.current !== '.' && initialValueRef.current !== './'\n ? initialValueRef.current.trim()\n : null\n if (startPath !== null) {\n setCur(startPath)\n loadDir(s, startPath)\n // If the start path points at a file dir inside a parent, still fine.\n } else {\n fetchHomeDefault(s).then((home) => {\n if (cancelled) return\n setCur(home)\n loadDir(s, home)\n })\n }\n return () => { cancelled = true }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n const loadDir = (s: SessionScope, path: string): void => {\n setLoading(true)\n setError(null)\n fsTree(s, path)\n .then((dirs) => {\n setEntries(dirs)\n setLoading(false)\n })\n .catch((err) => {\n setError(err instanceof Error ? err.message : String(err))\n setLoading(false)\n })\n }\n\n const goUp = (): void => {\n const parent = parentOf(cur)\n if (parent !== null) {\n setCur(parent)\n loadDir(scopeRef.current, parent)\n }\n }\n\n const navigateInto = (path: string): void => {\n setCur(path)\n loadDir(scopeRef.current, path)\n }\n\n const toggleExpand = (path: string): void => {\n setExpanded((prev) => {\n const next = new Set(prev)\n if (next.has(path)) next.delete(path)\n else next.add(path)\n return next\n })\n }\n\n const isSelected = (p: string): boolean => selectedPath !== '' && selectedPath !== '.' && selectedPath === p\n\n return createElement('div', { className: 'sts-dirtree' },\n // Navigation bar: current path + up button\n createElement('div', { className: 'sts-dirtree-nav' },\n createElement('button', {\n className: 'sts-dirtree-up',\n onClick: goUp,\n disabled: parentOf(cur) === null,\n title: '\\u4e0a\\u7ea7\\u76ee\\u5f55',\n }, '\\u2191'),\n createElement('button', {\n className: 'sts-dirtree-home',\n onClick: () => loadDir(scopeRef.current, cur), // refresh current dir\n title: '\\u5237\\u65b0',\n }, '\\u21bb'),\n createElement('span', { className: 'sts-dirtree-cur', title: cur }, cur || '(.)'),\n ),\n // Current directory selection row + subdir tree\n createElement('div', {\n className: `sts-tree-row${isSelected(cur) ? ' selected' : ''}`,\n style: { paddingLeft: '8px' },\n onClick: () => onPick(cur),\n },\n createElement('span', { className: 'sts-tree-icon' },\n createElement(FolderIcon, { size: 14 }),\n ),\n createElement('span', { className: 'sts-tree-label' }, cur || '(.)'),\n createElement('span', { className: 'sts-tree-select-hint' }, '\\u25cf \\u9009\\u62e9\\u6b64\\u76ee\\u5f55'),\n ),\n loading\n ? createElement('div', { className: 'sts-tree-loading', style: { paddingLeft: '16px' } }, '\\u52a0\\u8f7d\\u4e2d...')\n : error !== null\n ? createElement('div', { className: 'sts-tree-error', style: { paddingLeft: '16px' } }, error)\n : entries !== null && entries.length > 0\n ? createElement('div', { className: 'sts-dirtree-sub' },\n ...entries.map((entry) => createElement(DirRow, {\n key: entry.path,\n scope,\n entry,\n depth: 1,\n selectedPath,\n expanded,\n onToggleExpand: toggleExpand,\n onPick,\n onNavigate: navigateInto,\n })),\n )\n : createElement('div', { className: 'sts-tree-empty', style: { paddingLeft: '16px' } }, '\\u65e0\\u5b50\\u76ee\\u5f55'),\n )\n}\n\n/** One directory row with recursive expansion + click into. */\nfunction DirRow(props: {\n scope: SessionScope\n entry: FsEntry\n depth: number\n selectedPath: string\n expanded: Set<string>\n onToggleExpand: (p: string) => void\n onPick: (p: string) => void\n onNavigate: (p: string) => void\n}): ReactNode {\n const { scope, entry, depth, selectedPath, expanded, onToggleExpand, onPick, onNavigate } = props\n const [children, setChildren] = useState<FsEntry[] | null>(null)\n const [loading, setLoading] = useState(false)\n const isExpanded = expanded.has(entry.path)\n const isSelected = selectedPath === entry.path\n\n const handleClick = (): void => {\n // Clicking a row selects it AND expands to reveal children (unless already\n // expanded, in which case a second click collapses).\n onPick(entry.path)\n if (isExpanded) {\n onToggleExpand(entry.path)\n return\n }\n onToggleExpand(entry.path)\n if (children === null) {\n setLoading(true)\n fsTree(scope, entry.path)\n .then((dirs) => { setChildren(dirs); setLoading(false) })\n .catch(() => { setChildren([]); setLoading(false) })\n }\n }\n\n return createElement('div', null,\n createElement('div', {\n className: `sts-tree-row${isSelected ? ' selected' : ''}`,\n style: { paddingLeft: `${depth * 16 + 8}px` },\n onClick: handleClick,\n },\n createElement('span', { className: `sts-tree-chevron${isExpanded ? ' expanded' : ''}` },\n createElement(isExpanded ? ChevronDownIcon : ChevronRightIcon, { size: 12 }),\n ),\n createElement('span', { className: 'sts-tree-icon' },\n createElement(FolderIcon, { size: 14 }),\n ),\n createElement('span', { className: 'sts-tree-label' }, entry.name),\n ),\n isExpanded\n ? loading\n ? createElement('div', { className: 'sts-tree-loading', style: { paddingLeft: `${(depth + 1) * 16 + 8}px` } }, '\\u52a0\\u8f7d\\u4e2d...')\n : children !== null && children.length > 0\n ? createElement('div', null,\n ...children.map((child) =>\n createElement(DirRow, {\n key: child.path,\n scope,\n entry: child,\n depth: depth + 1,\n selectedPath,\n expanded,\n onToggleExpand: onToggleExpand,\n onPick,\n onNavigate: props.onNavigate,\n }),\n ),\n )\n : null\n : null,\n )\n}\n\n/** Props for the browse button + modal chooser. */\nexport interface DirChooserProps {\n scope: SessionScope\n value: string\n onChange: (v: string) => void\n /** Button/input presentation. */\n compact?: boolean\n /** Placeholder for the value input. */\n placeholder?: string\n}\n\n/** A read-only path input + browse button that opens a modal folder tree. */\nexport function DirChooser(props: DirChooserProps): ReactNode {\n const { scope, value, onChange, compact, placeholder } = props\n const [open, setOpen] = useState(false)\n\n return createElement('div', { className: 'sts-settings-path-row' },\n createElement('span', { className: 'sts-settings-path-icon' },\n createElement(FolderIcon, { size: 14 }),\n ),\n createElement('input', {\n className: compact ? 'sts-modal-input mono' : 'sts-settings-input',\n value: value,\n readOnly: true,\n placeholder: placeholder ?? '',\n title: value,\n onClick: () => setOpen(true),\n }),\n createElement('button', {\n className: 'sts-settings-browse',\n onClick: () => setOpen(true),\n title: '\\u9009\\u62e9\\u6587\\u4ef6\\u5939',\n }, '\\u6d4f\\u89c8'),\n open\n ? createElement(DirChooserModal, {\n scope,\n value,\n onConfirm: (p) => { onChange(p); setOpen(false) },\n onCancel: () => setOpen(false),\n })\n : null,\n )\n}\n\n/** Modal popup hosting the folder tree with confirm/cancel. */\nfunction DirChooserModal(props: {\n scope: SessionScope\n value: string\n onConfirm: (path: string) => void\n onCancel: () => void\n}): ReactNode {\n const { scope, value, onConfirm, onCancel } = props\n const [sel, setSel] = useState(value)\n\n return createElement('div', {\n className: 'sts-modal-overlay',\n onClick: (e: MouseEvent) => { if (e.target === e.currentTarget) onCancel() },\n },\n createElement('div', { className: 'sts-dir-modal' },\n createElement('div', { className: 'sts-modal-header' },\n '\\u9009\\u62e9\\u6587\\u4ef6\\u5939',\n createElement('button', { className: 'sts-icon-btn', onClick: onCancel },\n createElement(CloseIcon, { size: 16 }),\n ),\n ),\n createElement('div', { className: 'sts-dir-modal-tree' },\n createElement(DirTree, { scope, selectedPath: sel, onPick: setSel }),\n ),\n createElement('div', { className: 'sts-modal-footer' },\n createElement('button', { className: 'sts-modal-btn', onClick: onCancel }, '\\u53d6\\u6d88'),\n createElement('button', {\n className: 'sts-modal-btn primary',\n onClick: () => onConfirm(sel),\n style: {\n background: '#1a1a1e',\n border: '1px solid #3a3a3e',\n color: '#fff',\n borderRadius: '6px',\n padding: '8px 18px',\n cursor: 'pointer',\n fontSize: '13px',\n },\n },\n createElement(CheckIcon, { size: 14 }), ' \\u786e\\u5b9a'),\n ),\n ),\n )\n}\n","/**\n * Path picker: a read-only input + browse button that opens a folder tree\n * modal (DirChooser). Shared by the settings panel and the config editor.\n * @module dsh-better-sidebar-starter/client/PathPicker\n */\n\nimport { createElement, type ReactNode } from 'react'\nimport type { SessionScope } from 'dsh-better-sidebar/client/service'\nimport { DirChooser } from './DirChooser.tsx'\n\nexport type PathKind = 'java' | 'node' | 'python' | 'mvn'\n\n/** Props for the path picker. */\nexport interface PathPickerProps {\n scope: SessionScope\n kind: PathKind\n value: string\n onChange: (v: string) => void\n /** Compact mode (no label/desc wrapper), used inside the modal. */\n compact?: boolean\n desc?: string\n}\n\n/** A folder-browse input with a hosted modal tree chooser. */\nexport function PathPicker(props: PathPickerProps): ReactNode {\n const { scope, value, onChange, compact, desc } = props\n return createElement(DirChooser, {\n scope,\n value,\n onChange,\n compact,\n placeholder: desc,\n })\n}\n","/**\n * Config editor modal dialog: create or edit a run configuration.\n * Lets the user set name, type, command, cwd (tree picker), jvmArgs, args,\n * and environment variables.\n * @module dsh-better-sidebar-starter/client/ConfigEditorModal\n */\n\nimport { createElement, useState, useEffect, type ReactNode } from 'react'\nimport type { SessionScope } from 'dsh-better-sidebar/client/service'\nimport { CloseIcon, PlusIcon, TrashIcon, ChevronRightIcon, FolderIcon, ClipboardIcon } from './icons.tsx'\nimport { PathPicker } from './PathPicker.tsx'\n\n/** One directory entry from the sidebar fs.tree API. */\ninterface FsEntry {\n name: string\n path: string\n isDir: boolean\n hidden: boolean\n isSymlink: boolean\n broken: boolean\n}\n\n/** Call the sidebar fs.tree API directly via fetch. Returns directories only.\n * If path is omitted, the server uses the session cwd (root listing).\n * The sidebar API wraps responses in { ok: true, value: ... } envelope. */\nasync function fsTree(scope: SessionScope, path?: string): Promise<FsEntry[]> {\n const payload: Record<string, string> = { sessionId: scope.sessionId }\n if (scope.cwd !== undefined && scope.cwd !== '') payload.cwd = scope.cwd\n if (path !== undefined) payload.path = path\n const resp = await fetch('/sidebar/api/fs.tree', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!resp.ok) throw new Error(`fs.tree ${path ?? '(root)'}: ${resp.status}`)\n const parsed = await resp.json() as { ok?: boolean; value?: { entries: FsEntry[] }; error?: { message: string } }\n if (!parsed.ok || parsed.value === undefined) {\n throw new Error(`fs.tree ${path ?? '(root)'}: ${parsed.error?.message ?? 'unknown error'}`)\n }\n return parsed.value.entries.filter((e) => e.isDir && !e.hidden)\n}\n\n/** Recursive tree node: loads its own children on expand. */\nfunction TreeNode(props: {\n scope: SessionScope\n entry: FsEntry\n depth: number\n selectedPath: string\n onSelect: (path: string) => void\n}): ReactNode {\n const { scope, entry, depth, selectedPath, onSelect } = props\n const [expanded, setExpanded] = useState(false)\n const [children, setChildren] = useState<FsEntry[] | null>(null)\n const [loading, setLoading] = useState(false)\n\n const handleToggle = (): void => {\n if (!expanded && children === null) {\n setLoading(true)\n fsTree(scope, entry.path)\n .then((dirs) => { setChildren(dirs); setLoading(false) })\n .catch(() => { setChildren([]); setLoading(false) })\n }\n setExpanded(!expanded)\n }\n\n const isSelected = selectedPath === entry.path\n\n return createElement('div', null,\n createElement('div', {\n className: `sts-tree-row${isSelected ? ' selected' : ''}`,\n style: { paddingLeft: `${depth * 16 + 8}px` },\n onClick: () => { onSelect(entry.path); handleToggle() },\n },\n createElement('span', { className: `sts-tree-chevron${expanded ? ' expanded' : ''}` },\n createElement(ChevronRightIcon, { size: 12 }),\n ),\n createElement('span', { className: 'sts-tree-icon' },\n createElement(FolderIcon, { size: 14 }),\n ),\n createElement('span', { className: 'sts-tree-label' }, entry.name),\n ),\n expanded\n ? loading\n ? createElement('div', { className: 'sts-tree-loading', style: { paddingLeft: `${(depth + 1) * 16 + 8}px` } }, '\\u52a0\\u8f7d\\u4e2d...')\n : children !== null && children.length > 0\n ? createElement('div', null,\n ...children.map((child) =>\n createElement(TreeNode, {\n key: child.path,\n scope,\n entry: child,\n depth: depth + 1,\n selectedPath,\n onSelect,\n }),\n ),\n )\n : createElement('div', { className: 'sts-tree-empty', style: { paddingLeft: `${(depth + 1) * 16 + 8}px` } }, '\\u65e0\\u5b50\\u76ee\\u5f55')\n : null,\n )\n}\n\n/** Directory tree picker: shows a collapsible tree rooted at session cwd. */\nfunction DirTreePicker(props: { scope: SessionScope; value: string; onSelect: (path: string) => void }): ReactNode {\n const { scope, value, onSelect } = props\n const [expanded, setExpanded] = useState(false)\n const [children, setChildren] = useState<FsEntry[] | null>(null)\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const handleToggle = (): void => {\n if (!expanded && children === null) {\n setLoading(true)\n setError(null)\n fsTree(scope)\n .then((dirs) => { setChildren(dirs); setLoading(false) })\n .catch((err) => { setError(err instanceof Error ? err.message : String(err)); setLoading(false) })\n }\n setExpanded(!expanded)\n }\n\n const rootSelected = value === '.' || value === '' || value === './'\n\n return createElement('div', { className: 'sts-tree-picker' },\n createElement('div', {\n className: `sts-tree-row${rootSelected ? ' selected' : ''}`,\n style: { paddingLeft: '8px' },\n onClick: () => { onSelect('.'); handleToggle() },\n },\n createElement('span', { className: `sts-tree-chevron${expanded ? ' expanded' : ''}` },\n createElement(ChevronRightIcon, { size: 12 }),\n ),\n createElement('span', { className: 'sts-tree-icon' },\n createElement(FolderIcon, { size: 14 }),\n ),\n createElement('span', { className: 'sts-tree-label' }, '(\\u5de5\\u4f5c\\u7a7a\\u95f4\\u6839\\u76ee\\u5f55)'),\n ),\n expanded\n ? loading\n ? createElement('div', { className: 'sts-tree-loading', style: { paddingLeft: '24px' } }, '\\u52a0\\u8f7d\\u4e2d...')\n : error !== null\n ? createElement('div', { className: 'sts-tree-error', style: { paddingLeft: '24px' } }, error)\n : children !== null && children.length > 0\n ? createElement('div', null,\n ...children.map((child) =>\n createElement(TreeNode, {\n key: child.path,\n scope,\n entry: child,\n depth: 1,\n selectedPath: value,\n onSelect,\n }),\n ),\n )\n : createElement('div', { className: 'sts-tree-empty', style: { paddingLeft: '24px' } }, '\\u65e0\\u5b50\\u76ee\\u5f55')\n : null,\n )\n}\n\n/** One env var row. */\ninterface EnvRow {\n key: string\n value: string\n}\n\n/** Props for the modal. */\nexport interface ConfigEditorModalProps {\n /** Existing config to edit, or null for a new config. */\n config: {\n id?: string\n name: string\n type: string\n command: string\n cwd: string\n env: Record<string, string>\n jvmArgs?: string\n args?: string\n runtime?: { java?: string; node?: string; python?: string; mvn?: string }\n } | null\n /** Session scope for fsTree calls. */\n scope: SessionScope\n /** Called when the user saves. */\n onSave: (config: {\n id?: string\n name: string\n type: string\n command: string\n cwd: string\n env: Record<string, string>\n jvmArgs: string\n args: string\n runtime: Record<string, string>\n }) => void\n /** Called when the user cancels. */\n onCancel: () => void\n}\n\n/** Type \\u2192 default command mapping. */\nconst TYPE_DEFAULTS: Record<string, string> = {\n npm: 'npm run dev',\n springboot: 'mvn spring-boot:run',\n python: 'python main.py',\n custom: '',\n}\n\nconst TYPE_LABELS: Record<string, string> = {\n npm: 'npm',\n springboot: 'Spring Boot',\n python: 'Python',\n custom: 'Custom',\n}\n\n/** The modal dialog component. */\nexport function ConfigEditorModal({ config, scope, onSave, onCancel }: ConfigEditorModalProps): ReactNode {\n const [name, setName] = useState(config?.name ?? '')\n const [type, setType] = useState(config?.type ?? 'custom')\n const [command, setCommand] = useState(config?.command ?? '')\n const [cwd, setCwd] = useState(config?.cwd ?? '.')\n const [jvmArgs, setJvmArgs] = useState(config?.jvmArgs ?? '')\n const [args, setArgs] = useState(config?.args ?? '')\n const [envRows, setEnvRows] = useState<EnvRow[]>(\n config\n ? Object.entries(config.env).map(([key, value]) => ({ key, value }))\n : [],\n )\n const [showTree, setShowTree] = useState(false)\n const [showRuntime, setShowRuntime] = useState(false)\n const [rtJava, setRtJava] = useState(config?.runtime?.java ?? '')\n const [rtNode, setRtNode] = useState(config?.runtime?.node ?? '')\n const [rtPython, setRtPython] = useState(config?.runtime?.python ?? '')\n const [rtMvn, setRtMvn] = useState(config?.runtime?.mvn ?? '')\n\n // When type changes and command is empty (new config), auto-fill template.\n useEffect(() => {\n if (command === '' && TYPE_DEFAULTS[type] !== '') {\n setCommand(TYPE_DEFAULTS[type])\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [type])\n\n const handleSave = (): void => {\n const env: Record<string, string> = {}\n for (const row of envRows) {\n if (row.key.trim() !== '') {\n env[row.key.trim()] = row.value\n }\n }\n const runtime: Record<string, string> = {}\n if (rtJava.trim() !== '') runtime.java = rtJava.trim()\n if (rtNode.trim() !== '') runtime.node = rtNode.trim()\n if (rtPython.trim() !== '') runtime.python = rtPython.trim()\n if (rtMvn.trim() !== '') runtime.mvn = rtMvn.trim()\n onSave({\n id: config?.id,\n name: name.trim(),\n type,\n command: command.trim(),\n cwd: cwd.trim() === '' ? '.' : cwd.trim(),\n env,\n jvmArgs: jvmArgs.trim(),\n args: args.trim(),\n runtime,\n })\n }\n\n const updateEnvRow = (idx: number, field: 'key' | 'value', val: string): void => {\n setEnvRows((prev) => prev.map((row, i) => i === idx ? { ...row, [field]: val } : row))\n }\n const addEnvRow = (): void => setEnvRows((prev) => [...prev, { key: '', value: '' }])\n const removeEnvRow = (idx: number): void => setEnvRows((prev) => prev.filter((_, i) => i !== idx))\n\n // Bulk paste state.\n const [showEnvPaste, setShowEnvPaste] = useState(false)\n const [envPasteText, setEnvPasteText] = useState('')\n\n /** Parse \"K=V;K=V,...\" (or newline/comma separated) into env rows, merging\n * into the existing rows (later values override earlier for duplicate keys). */\n const applyEnvPaste = (): void => {\n const existing = new Map(envRows.filter((r) => r.key.trim() !== '').map((r) => [r.key.trim(), r.value]))\n const text = envPasteText.trim()\n if (text === '') return\n // Split on semicolons, commas, or newlines.\n const pairs = text.split(/[;,\\n]+/)\n for (const pair of pairs) {\n const p = pair.trim()\n if (p === '') continue\n const eq = p.indexOf('=')\n if (eq <= 0) continue // ignore malformed entries\n const key = p.substring(0, eq).trim()\n const value = p.substring(eq + 1).trim()\n if (key !== '') existing.set(key, value)\n }\n setEnvRows(Array.from(existing, ([key, value]) => ({ key, value })))\n setEnvPasteText('')\n setShowEnvPaste(false)\n }\n\n const isSpringboot = type === 'springboot'\n\n return createElement('div', {\n className: 'sts-modal-overlay',\n onClick: (e: MouseEvent) => { if (e.target === e.currentTarget) onCancel() },\n },\n createElement('div', { className: 'sts-modal' },\n // Header\n createElement('div', { className: 'sts-modal-header' },\n config?.id ? '\\u7f16\\u8f91\\u8fd0\\u884c\\u914d\\u7f6e' : '\\u65b0\\u5efa\\u8fd0\\u884c\\u914d\\u7f6e',\n createElement('button', {\n className: 'sts-icon-btn',\n onClick: onCancel,\n }, createElement(CloseIcon, { size: 16 })),\n ),\n // Body\n createElement('div', { className: 'sts-modal-body' },\n // Name\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, '\\u540d\\u79f0'),\n createElement('input', {\n className: 'sts-modal-input',\n value: name,\n onChange: (e: { target: { value: string } }) => setName(e.target.value),\n placeholder: '\\u914d\\u7f6e\\u540d\\u79f0',\n }),\n ),\n // Type\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, '\\u7c7b\\u578b'),\n createElement('select', {\n className: 'sts-modal-select',\n value: type,\n onChange: (e: { target: { value: string } }) => setType(e.target.value),\n },\n ...Object.entries(TYPE_LABELS).map(([val, label]) =>\n createElement('option', { key: val, value: val }, label),\n ),\n ),\n ),\n // Command\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, '\\u547d\\u4ee4'),\n createElement('input', {\n className: 'sts-modal-input mono',\n value: command,\n onChange: (e: { target: { value: string } }) => setCommand(e.target.value),\n placeholder: '\\u8981\\u6267\\u884c\\u7684\\u547d\\u4ee4',\n }),\n ),\n // Working directory (tree picker)\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, '\\u5de5\\u4f5c\\u76ee\\u5f55'),\n createElement('div', { className: 'sts-cwd-display', onClick: () => setShowTree(!showTree) },\n createElement('span', { className: 'sts-cwd-icon' }, createElement(FolderIcon, { size: 14 })),\n createElement('span', { className: 'sts-cwd-value' }, cwd),\n ),\n showTree\n ? createElement('div', { className: 'sts-tree-picker-container' },\n createElement(DirTreePicker, { scope, value: cwd, onSelect: (p) => setCwd(p) }),\n )\n : null,\n ),\n // JVM args (springboot only)\n isSpringboot\n ? createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, '\\u865a\\u62df\\u673a\\u53c2\\u6570 (JVM Args)'),\n createElement('input', {\n className: 'sts-modal-input mono',\n value: jvmArgs,\n onChange: (e: { target: { value: string } }) => setJvmArgs(e.target.value),\n placeholder: '\\u4f8b: -Xmx512m -Dspring.profiles.active=dev',\n }),\n )\n : null,\n // Program args (all types)\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, '\\u7a0b\\u5e8f\\u5b9e\\u53c2'),\n createElement('input', {\n className: 'sts-modal-input mono',\n value: args,\n onChange: (e: { target: { value: string } }) => setArgs(e.target.value),\n placeholder: '\\u4f8b: --server.port=8080 --debug',\n }),\n ),\n // Runtime environment (collapsible advanced section)\n createElement('div', { className: 'sts-modal-field' },\n createElement('div', {\n className: `sts-collapse-header${showRuntime ? ' open' : ''}`,\n onClick: () => setShowRuntime(!showRuntime),\n },\n createElement('span', { className: `sts-collapse-chevron${showRuntime ? ' expanded' : ''}` },\n createElement(ChevronRightIcon, { size: 12 }),\n ),\n createElement('span', null, '\\u8fd0\\u884c\\u65f6\\u73af\\u5883 (\\u9ad8\\u7ea7)'),\n createElement('span', { className: 'sts-collapse-hint' }, '\\u9ed8\\u8ba4\\u7528\\u5168\\u5c40\\u914d\\u7f6e'),\n ),\n showRuntime\n ? createElement('div', { className: 'sts-collapse-body' },\n createElement('div', { className: 'sts-modal-label' }, '\\u7559\\u7a7a\\u5219\\u7528\\u5168\\u5c40/\\u7cfb\\u7edf\\u914d\\u7f6e'),\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, 'Java \\u8def\\u5f84'),\n createElement(PathPicker, {\n scope,\n kind: 'java',\n value: rtJava,\n onChange: setRtJava,\n compact: true,\n desc: '\\u9009\\u62e9 JDK \\u76ee\\u5f55',\n }),\n ),\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, 'Node.js \\u8def\\u5f84'),\n createElement(PathPicker, {\n scope,\n kind: 'node',\n value: rtNode,\n onChange: setRtNode,\n compact: true,\n desc: '\\u9009\\u62e9 Node \\u76ee\\u5f55',\n }),\n ),\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, 'Python \\u8def\\u5f84'),\n createElement(PathPicker, {\n scope,\n kind: 'python',\n value: rtPython,\n onChange: setRtPython,\n compact: true,\n desc: '\\u9009\\u62e9 Python \\u76ee\\u5f55',\n }),\n ),\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, 'Maven \\u8def\\u5f84 (MVN)'),\n createElement(PathPicker, {\n scope,\n kind: 'mvn',\n value: rtMvn,\n onChange: setRtMvn,\n compact: true,\n desc: '\\u9009\\u62e9 Maven \\u76ee\\u5f55',\n }),\n ),\n )\n : null,\n ),\n // Environment variables\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, '\\u73af\\u5883\\u53d8\\u91cf'),\n ...envRows.map((row, idx) =>\n createElement('div', { key: idx, className: 'sts-env-row' },\n createElement('input', {\n className: 'sts-modal-input sts-env-key',\n value: row.key,\n onChange: (e: { target: { value: string } }) => updateEnvRow(idx, 'key', e.target.value),\n placeholder: '\\u53d8\\u91cf\\u540d (\\u5982 JAVA_HOME)',\n }),\n createElement('span', { className: 'sts-env-sep' }, '='),\n createElement('input', {\n className: 'sts-modal-input sts-env-val',\n value: row.value,\n onChange: (e: { target: { value: string } }) => updateEnvRow(idx, 'value', e.target.value),\n placeholder: '\\u53d8\\u91cf\\u503c',\n }),\n createElement('button', {\n className: 'sts-icon-btn trash',\n onClick: () => removeEnvRow(idx),\n }, createElement(TrashIcon, { size: 14 })),\n ),\n ),\n createElement('div', { className: 'sts-env-actions' },\n createElement('button', {\n className: 'sts-env-add', onClick: addEnvRow,\n },\n createElement(PlusIcon, { size: 12 }), ' \\u6dfb\\u52a0\\u73af\\u5883\\u53d8\\u91cf',\n ),\n createElement('button', {\n className: 'sts-env-add', onClick: () => setShowEnvPaste(!showEnvPaste),\n },\n createElement(ClipboardIcon, { size: 12 }), showEnvPaste ? ' \\u53d6\\u6d88' : ' \\u6279\\u91cf\\u7c98\\u8d34',\n ),\n ),\n showEnvPaste\n ? createElement('div', { className: 'sts-env-paste' },\n createElement('textarea', {\n className: 'sts-env-paste-input',\n value: envPasteText,\n onChange: (e: { target: { value: string } }) => setEnvPasteText(e.target.value),\n placeholder: '\\u7c98\\u8d34 KEY=VALUE;KEY=VALUE;\\u6216\\u4ee5\\u6362\\u884c/\\u9017\\u53f7\\u5206\\u9694\\uff0c\\u5982\\uff1a\\nDB_DATABASE=cassiopeia_ap;DB_HOST=10.18.18.18;DB_PORT=3306;MQ_HOST=10.18.18.18;MQ_PASSWORD=admin123;MQ_PORT=5672;MQ_USERNAME=admin;MQ_VIRTUAL_HOST=/;REDIS_HOST=10.18.18.17;REDIS_PASSWORD=Qaz!wsx2;REDIS_PORT=6379',\n rows: 4,\n }),\n createElement('div', { className: 'sts-env-paste-footer' },\n createElement('button', {\n className: 'sts-env-paste-apply', onClick: applyEnvPaste,\n },\n '\\u786e\\u5b9a\\u5e76\\u751f\\u6210\\u73af\\u5883\\u53d8\\u91cf'),\n createElement('span', { className: 'sts-env-paste-hint' }, '\\u91cd\\u590d\\u5c06\\u8986\\u76d6'),\n ),\n )\n : null,\n ),\n ),\n // Footer\n createElement('div', { className: 'sts-modal-footer' },\n createElement('button', { className: 'sts-modal-btn', onClick: onCancel }, '\\u53d6\\u6d88'),\n createElement('button', {\n className: 'sts-modal-btn primary',\n onClick: handleSave,\n disabled: name.trim() === '',\n style: {\n background: '#1a1a1e',\n border: '1px solid #3a3a3e',\n color: '#fff',\n borderRadius: '6px',\n padding: '8px 18px',\n cursor: name.trim() === '' ? 'not-allowed' : 'pointer',\n fontSize: '13px',\n opacity: name.trim() === '' ? 0.5 : 1,\n },\n }, '\\u4fdd\\u5b58'),\n ),\n ),\n )\n}\n","/**\n * CSS styles for the starter plugin panel. Uses .sts- prefixed class names\n * (scoped enough to avoid collisions with the host shell).\n * Uses --dsw-alias-* CSS variables from the DSH design system to follow the\n * active theme automatically (dark/light).\n * @module dsh-better-sidebar-starter/client/styles\n */\n\nexport const STYLES_CSS = `\n/* ===== Root container ===== */\n.sts-root {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n font-size: 13px;\n color: var(--dsw-alias-label-primary, #e6e6e6);\n background: var(--dsw-alias-bg-base, #1a1a1e);\n}\n\n/* ===== Toolbar ===== */\n.sts-toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0 12px;\n height: 40px;\n border-bottom: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\n flex-shrink: 0;\n}\n.sts-toolbar-title {\n display: flex;\n align-items: center;\n gap: 8px;\n font: var(--dsw-font-m-14, 500 14px/20px sans-serif);\n color: var(--dsw-alias-label-primary, #e6e6e6);\n}\n.sts-toolbar-btn {\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 4px 10px;\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-label-secondary, #b0b0b0);\n cursor: pointer;\n font-size: 12px;\n transition: background 0.12s, border-color 0.12s;\n}\n.sts-toolbar-btn:hover {\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.06));\n border-color: var(--dsw-alias-border-l3, #3a3a3e);\n color: var(--dsw-alias-label-primary, #e6e6e6);\n}\n\n/* ===== Split pane (horizontal: default for right panel) ===== */\n.sts-split {\n display: flex;\n flex: 1;\n min-height: 0;\n}\n.sts-split-first {\n overflow-y: auto;\n overflow-x: hidden;\n border-right: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\n flex-shrink: 0;\n}\n.sts-split-gutter {\n width: 1px;\n background: var(--dsw-alias-border-l2, #2a2a2e);\n cursor: col-resize;\n flex-shrink: 0;\n transition: width 0.12s, background 0.12s;\n}\n.sts-split-gutter:hover,\n.sts-split-gutter.dragging {\n width: 3px;\n background: var(--dsw-alias-interactive-bg-hover-accent, #007acc);\n}\n.sts-split-second {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-width: 0;\n min-height: 0;\n}\n\n/* ===== Split pane (vertical: for bottom panel) ===== */\n.sts-split-vertical {\n flex-direction: column;\n}\n.sts-split-first-v {\n border-right: none;\n border-bottom: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\n width: 100% !important;\n overflow-y: auto;\n overflow-x: hidden;\n flex-shrink: 0;\n}\n.sts-split-gutter-v {\n width: auto;\n height: 1px;\n cursor: row-resize;\n transition: height 0.12s, background 0.12s;\n}\n.sts-split-gutter-v:hover,\n.sts-split-gutter-v.dragging {\n width: auto;\n height: 3px;\n background: var(--dsw-alias-interactive-bg-hover-accent, #007acc);\n}\n.sts-split-second-v {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-width: 0;\n min-height: 0;\n}\n\n/* ===== Config tree ===== */\n.sts-tree {\n padding: 4px 4px 8px;\n user-select: none;\n}\n.sts-group-header {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 8px;\n font: var(--dsw-font-xxxs-11, 500 11px/16px sans-serif);\n color: var(--dsw-alias-label-tertiary, #888);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n cursor: pointer;\n border-radius: 6px;\n}\n.sts-group-header:hover {\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.04));\n}\n.sts-group-count {\n color: rgba(255,255,255,0.4);\n font-weight: 400;\n font-variant-numeric: tabular-nums;\n}\n.sts-group-children {\n overflow: hidden;\n}\n.sts-group-children.collapsed {\n display: none;\n}\n\n/* ===== Config row ===== */\n.sts-config-row {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 5px 8px 5px 24px;\n cursor: pointer;\n font-size: 13px;\n border-radius: 6px;\n margin: 1px 2px;\n transition: background 0.1s;\n color: var(--dsw-alias-label-primary, #e6e6e6);\n}\n.sts-config-row:hover {\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.04));\n}\n.sts-config-row.selected {\n background: var(--dsw-alias-interactive-bg-active, rgba(0,122,204,0.12));\n}\n.sts-config-name {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-weight: 400;\n}\n.sts-config-cmd {\n font-size: 10px;\n color: rgba(255,255,255,0.4);\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, \"DejaVu Sans Mono\", \"Liberation Mono\", monospace;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n max-width: 100px;\n}\n.sts-config-actions {\n display: flex;\n align-items: center;\n gap: 1px;\n flex-shrink: 0;\n}\n.sts-icon-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 22px;\n height: 22px;\n border: none;\n border-radius: 4px;\n background: transparent;\n color: var(--dsw-alias-label-secondary, #b0b0b0);\n cursor: pointer;\n opacity: 0;\n transition: opacity 0.1s, background 0.1s;\n}\n.sts-config-row:hover .sts-icon-btn {\n opacity: 0.7;\n}\n.sts-icon-btn:hover {\n opacity: 1 !important;\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.08));\n}\n.sts-icon-btn.play { color: #6ab04c; }\n.sts-icon-btn.stop { color: #eb5757; }\n.sts-icon-btn.gear { color: var(--dsw-alias-label-secondary, #b0b0b0); }\n.sts-icon-btn.trash { color: #eb5757; }\n\n/* ===== Instance row ===== */\n.sts-instance-row {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 3px 8px 3px 40px;\n font-size: 11px;\n cursor: pointer;\n border-radius: 6px;\n margin: 1px 2px;\n color: var(--dsw-alias-label-tertiary, #888);\n transition: background 0.1s;\n}\n.sts-instance-row:hover {\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.04));\n}\n.sts-instance-row.selected {\n background: var(--dsw-alias-interactive-bg-active, rgba(0,122,204,0.12));\n color: var(--dsw-alias-label-primary, #e6e6e6);\n}\n.sts-instance-pid {\n color: rgba(255,255,255,0.4);\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, \"DejaVu Sans Mono\", \"Liberation Mono\", monospace;\n font-variant-numeric: tabular-nums;\n}\n\n/* ===== Empty states ===== */\n.sts-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: rgba(255,255,255,0.4);\n font-size: 12px;\n padding: 24px;\n text-align: center;\n gap: 8px;\n line-height: 20px;\n}\n\n/* ===== Log view ===== */\n.sts-log-toolbar {\n display: flex;\n align-items: center;\n gap: 2px;\n padding: 0 8px;\n height: 36px;\n border-bottom: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\n flex-shrink: 0;\n color: var(--dsw-alias-label-secondary, #b0b0b0);\n font-size: 12px;\n}\n.sts-log-toolbar-spacer {\n flex: 1;\n}\n.sts-log-toolbar-label {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n cursor: pointer;\n color: var(--dsw-alias-label-tertiary, #888);\n padding: 2px 6px;\n border-radius: 4px;\n}\n.sts-log-toolbar-label:hover {\n color: var(--dsw-alias-label-primary, #e6e6e6);\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.04));\n}\n.sts-log-content {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n padding: 8px 12px;\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, \"DejaVu Sans Mono\", \"Liberation Mono\", monospace;\n font-size: 12px;\n line-height: 1.6;\n white-space: pre-wrap;\n word-break: break-all;\n color: var(--dsw-alias-label-primary, #e6e6e6);\n background: var(--dsw-alias-bg-base, #1a1a1e);\n}\n.sts-log-line {\n min-height: 1em;\n}\n\n/* ===== Config detail ===== */\n.sts-detail {\n padding: 20px;\n overflow-y: auto;\n flex: 1;\n}\n.sts-detail-card {\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\n background: var(--dsw-alias-bg-layer-2, #202024);\n border-radius: 12px;\n padding: 20px;\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n.sts-detail-title {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 16px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary, #e6e6e6);\n padding-bottom: 12px;\n border-bottom: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\n}\n.sts-detail-row {\n display: flex;\n gap: 12px;\n font-size: 13px;\n align-items: baseline;\n}\n.sts-detail-label {\n width: 72px;\n flex-shrink: 0;\n color: var(--dsw-alias-label-tertiary, #888);\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n.sts-detail-value {\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, \"DejaVu Sans Mono\", \"Liberation Mono\", monospace;\n word-break: break-all;\n color: var(--dsw-alias-label-primary, #e6e6e6);\n}\n.sts-detail-actions {\n display: flex;\n gap: 8px;\n justify-content: flex-end;\n margin-top: 12px;\n padding-top: 16px;\n border-top: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\n}\n.sts-detail-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 14px;\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-label-secondary, #b0b0b0);\n cursor: pointer;\n font-size: 12px;\n transition: background 0.12s, color 0.12s;\n}\n.sts-detail-btn:hover {\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.06));\n color: var(--dsw-alias-label-primary, #e6e6e6);\n}\n.sts-detail-btn.danger {\n color: #eb5757;\n border-color: rgba(235,87,87,0.4);\n}\n.sts-detail-btn.danger:hover {\n background: rgba(235,87,87,0.1);\n}\n\n/* ===== Modal ===== */\n.sts-modal-overlay {\n position: fixed;\n top: 0; left: 0; right: 0; bottom: 0;\n background: rgba(0,0,0,0.45);\n backdrop-filter: blur(2px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10000;\n}\n.sts-modal {\n background: var(--dsw-alias-bg-layer-3, #242428);\n border: 1px solid var(--dsw-alias-border-l3, #3a3a3e);\n border-radius: 12px;\n width: 480px;\n max-width: 90vw;\n max-height: 80vh;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n box-shadow: 0 8px 32px rgba(0,0,0,0.3);\n}\n.sts-modal-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 14px 20px;\n font-size: 15px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary, #e6e6e6);\n border-bottom: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\n}\n.sts-modal-body {\n padding: 20px;\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n.sts-modal-field {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.sts-modal-label {\n font-size: 11px;\n color: var(--dsw-alias-label-tertiary, #888);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n.sts-modal-input,\n.sts-modal-select {\n padding: 8px 10px;\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-1, #1a1a1e);\n color: var(--dsw-alias-label-primary, #e6e6e6);\n font-size: 13px;\n font-family: inherit;\n outline: none;\n transition: border-color 0.12s, background 0.12s;\n}\n.sts-modal-input:focus,\n.sts-modal-select:focus {\n border-color: var(--dsw-alias-button-primary-fill, #007acc);\n background: var(--dsw-alias-bg-base, #1a1a1e);\n}\n.sts-modal-input.mono {\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, \"DejaVu Sans Mono\", \"Liberation Mono\", monospace;\n}\n.sts-env-row {\n display: flex;\n gap: 6px;\n align-items: center;\n}\n.sts-env-key {\n flex: 1;\n}\n.sts-env-val {\n flex: 2;\n}\n.sts-env-sep {\n color: var(--dsw-alias-label-dimmed, #555);\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, \"DejaVu Sans Mono\", \"Liberation Mono\", monospace;\n}\n.sts-env-add {\n font-size: 12px;\n padding: 6px 10px;\n border: 1px dashed var(--dsw-alias-border-l3, #3a3a3e);\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-label-tertiary, #888);\n cursor: pointer;\n align-self: flex-start;\n transition: color 0.12s, border-color 0.12s;\n}\n.sts-env-add:hover {\n color: var(--dsw-alias-label-primary, #e6e6e6);\n border-color: var(--dsw-alias-button-primary-fill, #007acc);\n}\n.sts-modal-footer {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n padding: 14px 20px;\n border-top: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\n}\n.sts-modal-btn {\n padding: 8px 18px;\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-label-secondary, #b0b0b0);\n cursor: pointer;\n font-size: 13px;\n transition: background 0.12s, color 0.12s;\n}\n.sts-modal-btn:hover {\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.06));\n color: var(--dsw-alias-label-primary, #e6e6e6);\n}\n\n/* ===== Directory tree picker ===== */\n.sts-cwd-display {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 10px;\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-1, #1a1a1e);\n cursor: pointer;\n transition: border-color 0.12s;\n font-size: 13px;\n}\n.sts-cwd-display:hover {\n border-color: var(--dsw-alias-border-l3, #3a3a3e);\n}\n.sts-cwd-icon {\n display: flex;\n align-items: center;\n color: var(--dsw-alias-label-secondary, #b0b0b0);\n}\n.sts-cwd-value {\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, monospace;\n color: var(--dsw-alias-label-primary, #e6e6e6);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.sts-tree-picker-container {\n margin-top: 4px;\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-1, #1a1a1e);\n max-height: 240px;\n overflow-y: auto;\n padding: 4px;\n}\n.sts-tree-row {\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 4px 8px;\n cursor: pointer;\n border-radius: 4px;\n font-size: 13px;\n color: var(--dsw-alias-label-secondary, #b0b0b0);\n transition: background 0.08s;\n white-space: nowrap;\n}\n.sts-tree-row:hover {\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.04));\n color: var(--dsw-alias-label-primary, #e6e6e6);\n}\n.sts-tree-row.selected {\n background: var(--dsw-alias-interactive-bg-active, rgba(0,122,204,0.12));\n color: var(--dsw-alias-label-primary, #e6e6e6);\n}\n.sts-tree-chevron {\n display: flex;\n align-items: center;\n transition: transform 0.1s;\n flex-shrink: 0;\n}\n.sts-tree-chevron.expanded {\n transform: rotate(90deg);\n}\n.sts-tree-icon {\n display: flex;\n align-items: center;\n flex-shrink: 0;\n color: var(--dsw-alias-label-tertiary, #888);\n}\n.sts-tree-label {\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.sts-tree-loading,\n.sts-tree-error,\n.sts-tree-empty {\n padding: 8px 12px;\n font-size: 12px;\n color: rgba(255,255,255,0.4);\n}\n.sts-tree-error {\n color: #eb5757;\n}\n\n/* ===== Settings panel ===== */\n.sts-settings-panel {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding: 8px;\n min-width: 320px;\n}\n.sts-settings-row {\n padding: 4px 0;\n}\n.sts-settings-switch-row {\n display: flex;\n align-items: center;\n gap: 8px;\n cursor: pointer;\n font-size: 13px;\n color: var(--dsw-alias-label-primary, #e6e6e6);\n}\n.sts-settings-switch {\n width: 16px;\n height: 16px;\n cursor: pointer;\n}\n.sts-settings-sep {\n margin: 8px 0 4px;\n padding: 0 4px;\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: rgba(255,255,255,0.4);\n}\n.sts-settings-path {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding: 4px 0;\n}\n.sts-settings-path-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n}\n.sts-settings-label {\n font-size: 12px;\n color: var(--dsw-alias-label-secondary, #b0b0b0);\n}\n.sts-settings-badge {\n display: flex;\n align-items: center;\n gap: 2px;\n font-size: 10px;\n padding: 1px 6px;\n border-radius: 10px;\n}\n.sts-settings-badge.ok {\n color: #4e9a06;\n background: rgba(78,154,6,0.12);\n}\n.sts-settings-badge.err {\n color: #eb5757;\n background: rgba(235,87,87,0.12);\n}\n.sts-settings-path-row {\n display: flex;\n align-items: center;\n gap: 4px;\n}\n.sts-settings-path-icon {\n display: flex;\n align-items: center;\n color: var(--dsw-alias-label-tertiary, #888);\n flex-shrink: 0;\n}\n.sts-settings-input {\n flex: 1;\n padding: 6px 8px;\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\n border-radius: 4px;\n background: var(--dsw-alias-bg-layer-1, #1a1a1e);\n color: var(--dsw-alias-label-primary, #e6e6e6);\n font-size: 12px;\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, monospace;\n outline: none;\n min-width: 0;\n}\n.sts-settings-input:focus {\n border-color: var(--dsw-alias-border-l3, #3a3a3e);\n}\n.sts-settings-input::placeholder {\n color: rgba(255,255,255,0.3);\n}\n.sts-settings-browse {\n padding: 6px 10px;\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\n border-radius: 4px;\n background: transparent;\n color: var(--dsw-alias-label-secondary, #b0b0b0);\n cursor: pointer;\n font-size: 12px;\n flex-shrink: 0;\n transition: background 0.12s, color 0.12s;\n}\n.sts-settings-browse:hover {\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.06));\n color: var(--dsw-alias-label-primary, #e6e6e6);\n}\n.sts-settings-msg {\n font-size: 10px;\n padding: 0 4px;\n color: rgba(255,255,255,0.4);\n}\n.sts-settings-msg.valid {\n color: #4e9a06;\n}\n.sts-settings-msg.invalid {\n color: #eb5757;\n}\n\n/* ===== Collapsible advanced section ===== */\n.sts-collapse-header {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 10px;\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\n border-radius: 6px;\n cursor: pointer;\n font-size: 13px;\n color: var(--dsw-alias-label-primary, #e6e6e6);\n background: var(--dsw-alias-bg-layer-1, #1a1a1e);\n transition: border-color 0.12s;\n user-select: none;\n}\n.sts-collapse-header:hover {\n border-color: var(--dsw-alias-border-l3, #3a3a3e);\n}\n.sts-collapse-chevron {\n display: flex;\n align-items: center;\n transition: transform 0.12s;\n color: var(--dsw-alias-label-secondary, #b0b0b0);\n}\n.sts-collapse-chevron.expanded {\n transform: rotate(90deg);\n}\n.sts-collapse-hint {\n margin-left: auto;\n font-size: 10px;\n color: rgba(255,255,255,0.4);\n}\n.sts-collapse-body {\n margin-top: 8px;\n padding: 12px;\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-1, #1a1a1e);\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n/* ===== Settings picker rows & dir chooser modal ===== */\n.sts-settings-picker-row {\n margin: 6px 0;\n}\n.sts-settings-picker-row .sts-settings-label {\n display: block;\n margin-bottom: 4px;\n font-size: 11px;\n}\n.sts-dir-modal {\n background: var(--dsw-alias-bg-layer-3, #242428);\n border: 1px solid var(--dsw-alias-border-l3, #3a3a3e);\n border-radius: 12px;\n width: 420px;\n max-width: 90vw;\n max-height: 70vh;\n display: flex;\n flex-direction: column;\n box-shadow: 0 8px 32px rgba(0,0,0,0.3);\n}\n.sts-dir-modal-tree {\n flex: 1;\n overflow-y: auto;\n padding: 8px;\n min-height: 160px;\n}\n.sts-dirtree {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n.sts-dirtree-nav {\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 4px 4px 8px;\n border-bottom: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\n margin-bottom: 4px;\n}\n.sts-dirtree-up,\n.sts-dirtree-home {\n width: 24px;\n height: 24px;\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\n border-radius: 4px;\n background: transparent;\n color: var(--dsw-alias-label-secondary, #b0b0b0);\n cursor: pointer;\n font-size: 13px;\n flex-shrink: 0;\n transition: background 0.12s, color 0.12s;\n}\n.sts-dirtree-up:hover,\n.sts-dirtree-home:hover {\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.06));\n color: var(--dsw-alias-label-primary, #e6e6e6);\n}\n.sts-dirtree-up:disabled {\n opacity: 0.4;\n cursor: default;\n}\n.sts-dirtree-cur {\n flex: 1;\n font-size: 11px;\n color: var(--dsw-alias-label-tertiary, #888);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n direction: rtl;\n text-align: left;\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, monospace;\n}\n.sts-tree-select-hint {\n margin-left: auto;\n font-size: 10px;\n color: rgba(255,255,255,0.4);\n}\n.sts-dirtree-sub {\n border-top: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\n margin-top: 2px;\n padding-top: 2px;\n}\n\n/* ===== Env bulk paste ===== */\n.sts-env-actions {\n display: flex;\n gap: 6px;\n align-items: center;\n margin-top: 2px;\n}\n.sts-env-paste {\n margin-top: 8px;\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.sts-env-paste-input {\n width: 100%;\n padding: 8px 10px;\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-1, #1a1a1e);\n color: var(--dsw-alias-label-primary, #e6e6e6);\n font-size: 12px;\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, monospace;\n outline: none;\n resize: vertical;\n min-height: 80px;\n box-sizing: border-box;\n}\n.sts-env-paste-input:focus {\n border-color: var(--dsw-alias-border-l3, #3a3a3e);\n}\n.sts-env-paste-input::placeholder {\n color: rgba(255,255,255,0.3);\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, monospace;\n}\n.sts-env-paste-footer {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n.sts-env-paste-apply {\n padding: 6px 14px;\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-label-primary, #e6e6e6);\n cursor: pointer;\n font-size: 12px;\n transition: background 0.12s;\n}\n.sts-env-paste-apply:hover {\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.06));\n}\n.sts-env-paste-hint {\n font-size: 10px;\n color: rgba(255,255,255,0.4);\n}\n`\n","/**\n * Services tab: the main sidebar panel component. Left 1/3 config tree,\n * right 2/3 log view or config detail. Manages config CRUD via the host\n * REST API and tracks running instances.\n * @module dsh-better-sidebar-starter/client/ServicesTab\n */\n\nimport { createElement, useState, useEffect, useRef, useCallback, type ReactNode } from 'react'\nimport type { TabComponentProps } from 'dsh-better-sidebar/client/service'\nimport type { RunConfig, RunInstance } from './types.ts'\nimport { ConfigTree } from './ConfigTree.tsx'\nimport { LogView } from './LogView.tsx'\nimport { ConfigDetail } from './ConfigDetail.tsx'\nimport { ConfigEditorModal, type ConfigEditorModalProps } from './ConfigEditorModal.tsx'\nimport { RocketIcon, PlusIcon } from './icons.tsx'\nimport { STYLES_CSS } from './styles.ts'\n\n/** API base. */\nconst API_BASE = '/api/dsh-better-sidebar-starter'\n\n/** Instance poll interval (ms). */\nconst POLL_INTERVAL = 3000\n\n/** Inject styles once. */\nlet STYLES_INJECTED = false\nfunction injectStyles(): void {\n if (STYLES_INJECTED) return\n STYLES_INJECTED = true\n const tag = document.createElement('style')\n tag.id = 'dsh-better-sidebar-starter-styles'\n tag.setAttribute('data-plugin', '@dong-victor/dsh-better-sidebar-starter')\n tag.textContent = STYLES_CSS\n document.head.appendChild(tag)\n}\n\n/** The main services tab component. */\nexport function ServicesTab(props: TabComponentProps): ReactNode {\n injectStyles()\n\n const { scope, store } = props\n const [configs, setConfigs] = useState<RunConfig[]>([])\n const [instances, setInstances] = useState<RunInstance[]>([])\n const [selectedConfigId, setSelectedConfigId] = useState<string | null>(null)\n const [selectedInstanceId, setSelectedInstanceId] = useState<string | null>(null)\n const [editingConfig, setEditingConfig] = useState<ConfigEditorModalProps['config'] | null>(null)\n const [showModal, setShowModal] = useState(false)\n const [splitRatio, setSplitRatio] = useState(0.33)\n const [dragging, setDragging] = useState(false)\n const [isHorizontal, setIsHorizontal] = useState(true) // true = left/right split (right panel), false = top/bottom (bottom panel)\n\n const containerRef = useRef<HTMLDivElement>(null)\n\n // Detect panel orientation via ResizeObserver (aspect ratio).\n useEffect(() => {\n const el = containerRef.current\n if (el === null) return\n const check = (): void => {\n const rect = el.getBoundingClientRect()\n // If wider than tall → horizontal split (left/right). If taller → vertical (top/bottom).\n setIsHorizontal(rect.width > rect.height * 1.2)\n }\n check()\n const ro = new ResizeObserver(check)\n ro.observe(el)\n return () => ro.disconnect()\n }, [])\n\n // Read split ratio from plugin settings on mount.\n useEffect(() => {\n const blob = store.getPrefs()?.pluginSettings?.['dsh-better-sidebar-starter:services'] ?? {}\n if (typeof blob.splitRatio === 'number') {\n setSplitRatio(Math.max(0.2, Math.min(0.5, blob.splitRatio)))\n }\n }, [store])\n\n /** Persist split ratio to plugin settings. */\n const persistSplitRatio = useCallback((ratio: number): void => {\n const service = (store as any).service\n if (service !== undefined && typeof service.updateTab === 'function') {\n // Use pluginSettings persistence if available.\n }\n }, [store])\n\n /** Build a query string for the session scope. */\n const sessionQuery = useCallback((): string => {\n const params = new URLSearchParams({ sessionId: scope.sessionId })\n if (scope.cwd !== undefined) params.set('cwd', scope.cwd)\n return params.toString()\n }, [scope])\n\n /** Fetch configs from the host. */\n const fetchConfigs = useCallback(async (): Promise<void> => {\n try {\n const resp = await fetch(`${API_BASE}/configs?${sessionQuery()}`)\n if (resp.ok) {\n const data = await resp.json() as { configs: RunConfig[] }\n setConfigs(data.configs)\n }\n } catch { /* network error — ignore */ }\n }, [sessionQuery])\n\n /** Fetch instances from the host. */\n const fetchInstances = useCallback(async (): Promise<void> => {\n try {\n const resp = await fetch(`${API_BASE}/instances?${sessionQuery()}`)\n if (resp.ok) {\n const data = await resp.json() as { instances: RunInstance[] }\n setInstances(data.instances)\n }\n } catch { /* network error — ignore */ }\n }, [sessionQuery])\n\n // Initial load.\n useEffect(() => {\n void fetchConfigs()\n void fetchInstances()\n }, [fetchConfigs, fetchInstances])\n\n // Poll instances.\n useEffect(() => {\n const timer = setInterval(() => void fetchInstances(), POLL_INTERVAL)\n return () => clearInterval(timer)\n }, [fetchInstances])\n\n /** Start a config. */\n const handleStart = useCallback(async (configId: string): Promise<void> => {\n try {\n // Read global env paths from plugin settings.\n const blob = store.getPrefs()?.pluginSettings?.['dsh-better-sidebar-starter:services'] ?? {}\n const globalEnv: Record<string, string> = {}\n if (typeof blob.javaHome === 'string' && blob.javaHome.trim() !== '') globalEnv.JAVA_HOME = blob.javaHome.trim()\n if (typeof blob.nodePath === 'string' && blob.nodePath.trim() !== '') globalEnv.NODE_PATH = blob.nodePath.trim()\n if (typeof blob.pythonPath === 'string' && blob.pythonPath.trim() !== '') globalEnv.PYTHON_PATH = blob.pythonPath.trim()\n if (typeof blob.mvnPath === 'string' && blob.mvnPath.trim() !== '') globalEnv.MVN_PATH = blob.mvnPath.trim()\n\n const resp = await fetch(`${API_BASE}/run?${sessionQuery()}`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ configId, globalEnv }),\n })\n if (resp.ok) {\n const data = await resp.json() as { instance: RunInstance }\n setSelectedInstanceId(data.instance.id)\n setSelectedConfigId(null)\n void fetchInstances()\n void fetchConfigs() // lastRunAt update\n }\n } catch { /* ignore */ }\n }, [sessionQuery, fetchInstances, fetchConfigs, store])\n\n /** Stop an instance. */\n const handleStop = useCallback(async (instanceId: string): Promise<void> => {\n try {\n await fetch(`${API_BASE}/stop`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ instanceId }),\n })\n void fetchInstances()\n } catch { /* ignore */ }\n }, [fetchInstances])\n\n /** Restart = stop + start the same config. */\n const handleRestart = useCallback(async (instanceId: string): Promise<void> => {\n const inst = instances.find((i) => i.id === instanceId)\n if (inst === undefined) return\n await handleStop(instanceId)\n // Small delay to let the process die.\n await new Promise((r) => setTimeout(r, 200))\n await handleStart(inst.configId)\n }, [instances, handleStop, handleStart])\n\n /** Open the new config modal. */\n const handleNewConfig = (): void => {\n setEditingConfig(null)\n setShowModal(true)\n }\n\n /** Open the edit config modal. */\n const handleEditConfig = (configId: string): void => {\n const config = configs.find((c) => c.id === configId)\n if (config === undefined) return\n setEditingConfig({\n id: config.id,\n name: config.name,\n type: config.type,\n command: config.command,\n cwd: config.cwd,\n env: config.env,\n jvmArgs: config.jvmArgs,\n args: config.args,\n runtime: config.runtime,\n })\n setShowModal(true)\n }\n\n /** Save a config (create or update). */\n const handleSaveConfig = useCallback(async (config: ConfigEditorModalProps['config']): Promise<void> => {\n try {\n const resp = await fetch(`${API_BASE}/configs?${sessionQuery()}`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ config }),\n })\n if (resp.ok) {\n setShowModal(false)\n setEditingConfig(null)\n void fetchConfigs()\n }\n } catch { /* ignore */ }\n }, [sessionQuery, fetchConfigs])\n\n /** Delete a config. */\n const handleDeleteConfig = useCallback(async (configId: string): Promise<void> => {\n // Stop any running instances first.\n for (const inst of instances) {\n if (inst.configId === configId && inst.status === 'running') {\n await handleStop(inst.id)\n }\n }\n try {\n const resp = await fetch(`${API_BASE}/configs?${sessionQuery()}&id=${encodeURIComponent(configId)}`, {\n method: 'DELETE',\n })\n if (resp.ok) {\n if (selectedConfigId === configId) setSelectedConfigId(null)\n void fetchConfigs()\n void fetchInstances()\n }\n } catch { /* ignore */ }\n }, [instances, handleStop, sessionQuery, fetchConfigs, fetchInstances, selectedConfigId])\n\n /** Select a config (deselect any instance). */\n const handleSelectConfig = (configId: string): void => {\n setSelectedConfigId(configId)\n setSelectedInstanceId(null)\n }\n\n /** Select an instance. */\n const handleSelectInstance = (instanceId: string): void => {\n setSelectedInstanceId(instanceId)\n setSelectedConfigId(null)\n }\n\n // -- Split pane drag handling --\n const handleMouseDown = (e: MouseEvent): void => {\n e.preventDefault()\n setDragging(true)\n }\n\n useEffect(() => {\n if (!dragging) return\n const handleMove = (e: MouseEvent): void => {\n if (containerRef.current === null) return\n const rect = containerRef.current.getBoundingClientRect()\n const ratio = isHorizontal\n ? (e.clientX - rect.left) / rect.width\n : (e.clientY - rect.top) / rect.height\n const clamped = Math.max(0.2, Math.min(0.5, ratio))\n setSplitRatio(clamped)\n persistSplitRatio(clamped)\n }\n const handleUp = (): void => setDragging(false)\n document.addEventListener('mousemove', handleMove)\n document.addEventListener('mouseup', handleUp)\n return () => {\n document.removeEventListener('mousemove', handleMove)\n document.removeEventListener('mouseup', handleUp)\n }\n }, [dragging, isHorizontal, persistSplitRatio])\n\n // Resolve what to show in the right pane.\n const selectedInstance = selectedInstanceId !== null\n ? instances.find((i) => i.id === selectedInstanceId) ?? null\n : null\n const selectedConfig = selectedConfigId !== null\n ? configs.find((c) => c.id === selectedConfigId) ?? null\n : null\n\n return createElement('div', { ref: containerRef, className: 'sts-root' },\n // Toolbar\n createElement('div', { className: 'sts-toolbar' },\n createElement('div', { className: 'sts-toolbar-title' },\n createElement(RocketIcon, { size: 16 }),\n '服务',\n ),\n createElement('button', {\n className: 'sts-toolbar-btn',\n onClick: handleNewConfig,\n },\n createElement(PlusIcon, { size: 13 }), '新建配置'),\n ),\n // Split pane (horizontal or vertical based on panel position)\n createElement('div', { className: `sts-split${isHorizontal ? '' : ' sts-split-vertical'}` },\n // First pane: config tree\n createElement('div', {\n className: `sts-split-first${isHorizontal ? '' : ' sts-split-first-v'}`,\n style: isHorizontal\n ? { width: `${splitRatio * 100}%` }\n : { height: `${splitRatio * 100}%` },\n },\n configs.length === 0\n ? createElement('div', { className: 'sts-empty' }, '暂无运行配置,点击右上角 + 新建')\n : createElement(ConfigTree, {\n configs,\n instances,\n selectedInstanceId,\n selectedConfigId,\n onSelectConfig: handleSelectConfig,\n onSelectInstance: handleSelectInstance,\n onStartConfig: (id: string) => { void handleStart(id) },\n onStopInstance: (id: string) => { void handleStop(id) },\n onEditConfig: handleEditConfig,\n }),\n ),\n // Gutter\n createElement('div', {\n className: `sts-split-gutter${dragging ? ' dragging' : ''}${isHorizontal ? '' : ' sts-split-gutter-v'}`,\n onMouseDown: handleMouseDown,\n }),\n // Second pane: log view or config detail or empty\n createElement('div', { className: `sts-split-second${isHorizontal ? '' : ' sts-split-second-v'}` },\n selectedInstance !== null\n ? createElement(LogView, {\n instance: selectedInstance,\n onStop: () => { void handleStop(selectedInstance.id) },\n onRestart: () => { void handleRestart(selectedInstance.id) },\n })\n : selectedConfig !== null\n ? createElement(ConfigDetail, {\n config: selectedConfig,\n onStart: () => { void handleStart(selectedConfig.id) },\n onEdit: () => handleEditConfig(selectedConfig.id),\n onDelete: () => { void handleDeleteConfig(selectedConfig.id) },\n })\n : createElement('div', { className: 'sts-empty' }, '选择左侧配置查看详情或日志'),\n ),\n ),\n // Modal\n showModal\n ? createElement(ConfigEditorModal, {\n config: editingConfig,\n scope: { sessionId: scope.sessionId ?? store.getSnapshot().sessionId ?? '', cwd: scope.cwd },\n onSave: (c) => { void handleSaveConfig(c) },\n onCancel: () => { setShowModal(false); setEditingConfig(null) },\n })\n : null,\n )\n}\n","/**\n * Custom settings panel for the \"服务\" tab: renders switch toggles plus\n * validated directory pickers for Java/Node/Python/Maven paths.\n * @module dsh-better-sidebar-starter/client/SettingsPanel\n */\n\nimport { createElement, useState, useEffect, type ReactNode } from 'react'\nimport type { SidebarSettingsRenderProps, SessionScope } from 'dsh-better-sidebar/client/service'\nimport { PathPicker } from './PathPicker.tsx'\n\n/** The custom settings panel rendered in the gear popup. */\nexport function SettingsPanel(props: SidebarSettingsRenderProps): ReactNode {\n const { store, pluginSettings, updatePluginSetting } = props\n const scope: SessionScope = { sessionId: store.getSnapshot().sessionId ?? '' }\n\n const get = (key: string): string =>\n typeof pluginSettings[key] === 'string' ? pluginSettings[key] as string : ''\n const getBool = (key: string): boolean =>\n pluginSettings[key] === true\n\n const [autoSelect, setAutoSelect] = useState(getBool('autoSelectOnStart'))\n const [autoScroll, setAutoScroll] = useState(getBool('autoScroll'))\n const [javaHome, setJavaHome] = useState(get('javaHome'))\n const [nodePath, setNodePath] = useState(get('nodePath'))\n const [pythonPath, setPythonPath] = useState(get('pythonPath'))\n const [mvnPath, setMvnPath] = useState(get('mvnPath'))\n\n // Auto-detect environment paths on first open if not yet configured.\n useEffect(() => {\n const needDetect = get('javaHome') === '' && get('nodePath') === '' && get('pythonPath') === '' && get('mvnPath') === ''\n if (!needDetect) return\n let cancelled = false\n fetch('/api/dsh-better-sidebar-starter/detect-env')\n .then((r) => r.ok ? r.json() as Promise<{ javaHome?: string; nodePath?: string; pythonPath?: string; mvnPath?: string }> : null)\n .then((detected) => {\n if (cancelled || detected === null) return\n if (detected.javaHome !== undefined && detected.javaHome !== '') {\n setJavaHome(detected.javaHome)\n updatePluginSetting('javaHome', detected.javaHome)\n }\n if (detected.nodePath !== undefined && detected.nodePath !== '') {\n setNodePath(detected.nodePath)\n updatePluginSetting('nodePath', detected.nodePath)\n }\n if (detected.pythonPath !== undefined && detected.pythonPath !== '') {\n setPythonPath(detected.pythonPath)\n updatePluginSetting('pythonPath', detected.pythonPath)\n }\n if (detected.mvnPath !== undefined && detected.mvnPath !== '') {\n setMvnPath(detected.mvnPath)\n updatePluginSetting('mvnPath', detected.mvnPath)\n }\n })\n .catch(() => { /* ignore */ })\n return () => { cancelled = true }\n }, [])\n\n const handleSwitch = (key: string, setter: (v: boolean) => void) => (v: boolean): void => {\n setter(v)\n updatePluginSetting(key, v)\n }\n const handlePath = (key: string, setter: (v: string) => void) => (v: string): void => {\n setter(v)\n updatePluginSetting(key, v)\n }\n\n return createElement('div', { className: 'sts-settings-panel' },\n // Switches\n createElement('div', { className: 'sts-settings-row' },\n createElement('label', { className: 'sts-settings-switch-row' },\n createElement('input', {\n type: 'checkbox',\n className: 'sts-settings-switch',\n checked: autoSelect,\n onChange: (e: { target: { checked: boolean } }) => handleSwitch('autoSelectOnStart', setAutoSelect)(e.target.checked),\n }),\n createElement('span', null, '\\u542f\\u52a8\\u540e\\u81ea\\u52a8\\u9009\\u4e2d\\u65e5\\u5fd7'),\n ),\n ),\n createElement('div', { className: 'sts-settings-row' },\n createElement('label', { className: 'sts-settings-switch-row' },\n createElement('input', {\n type: 'checkbox',\n className: 'sts-settings-switch',\n checked: autoScroll,\n onChange: (e: { target: { checked: boolean } }) => handleSwitch('autoScroll', setAutoScroll)(e.target.checked),\n }),\n createElement('span', null, '\\u81ea\\u52a8\\u6eda\\u52a8\\u5230\\u6700\\u65b0\\u65e5\\u5fd7'),\n ),\n ),\n // Separator\n createElement('div', { className: 'sts-settings-sep' }, '\\u8fd0\\u884c\\u73af\\u5883\\u8def\\u5f84'),\n // Path pickers (each wrapped with a label row)\n createElement(PathPickerRow, {\n label: 'Java \\u4e3b\\u76ee\\u5f55 (JAVA_HOME)',\n scope,\n kind: 'java',\n value: javaHome,\n onChange: handlePath('javaHome', setJavaHome),\n desc: 'Spring Boot \\u7528\\u7684 JDK\\uff0c\\u7559\\u7a7a\\u5219\\u81ea\\u52a8\\u67e5\\u627e',\n }),\n createElement(PathPickerRow, {\n label: 'Node.js \\u8def\\u5f84',\n scope,\n kind: 'node',\n value: nodePath,\n onChange: handlePath('nodePath', setNodePath),\n desc: 'npm \\u7528\\u7684 Node\\uff0c\\u7559\\u7a7a\\u5219\\u7528\\u7cfb\\u7edf\\u9ed8\\u8ba4',\n }),\n createElement(PathPickerRow, {\n label: 'Python \\u8def\\u5f84',\n scope,\n kind: 'python',\n value: pythonPath,\n onChange: handlePath('pythonPath', setPythonPath),\n desc: 'Python \\u89e3\\u91ca\\u5668\\uff0c\\u7559\\u7a7a\\u5219\\u7528\\u7cfb\\u7edf\\u9ed8\\u8ba4',\n }),\n createElement(PathPickerRow, {\n label: 'Maven \\u8def\\u5f84 (MVN)',\n scope,\n kind: 'mvn',\n value: mvnPath,\n onChange: handlePath('mvnPath', setMvnPath),\n desc: 'springboot \\u7528\\u7684 Maven\\uff0c\\u7559\\u7a7a\\u5219\\u81ea\\u52a8\\u67e5\\u627e',\n }),\n )\n}\n\n/** A labeled path picker row for the settings panel. */\nfunction PathPickerRow(props: {\n label: string\n scope: SessionScope\n kind: 'java' | 'node' | 'python' | 'mvn'\n value: string\n onChange: (v: string) => void\n desc: string\n}): ReactNode {\n return createElement('div', { className: 'sts-settings-picker-row' },\n createElement('label', { className: 'sts-settings-label' }, props.label),\n createElement(PathPicker, {\n scope: props.scope,\n kind: props.kind,\n value: props.value,\n onChange: props.onChange,\n desc: props.desc,\n }),\n )\n}\n","/**\n * Client half of the dsh-better-sidebar-starter plugin: registers the\n * \"服务\" (Services) sidebar tab through the `ctx.betterSidebar` service.\n * The tab renders a split-pane panel: left config tree + right log/detail.\n * The host half (same package) owns the session-scoped config/process routes.\n * @module dsh-better-sidebar-starter/client\n */\n\nimport { createElement, type ReactNode } from 'react'\nimport type { Context } from 'cordis'\n// Triggers the ctx.betterSidebar type augmentation (erased at build).\nimport type {} from 'dsh-better-sidebar'\nimport { ServicesTab } from './ServicesTab.tsx'\nimport { SettingsPanel } from './SettingsPanel.tsx'\nimport { RocketIcon } from './icons.tsx'\n\n/** Services required before mounting: the betterSidebar registry (provided\n * by dsh-better-sidebar's own client half) and the session store. */\nexport const inject = ['betterSidebar', 'sessions']\n\n/** Plugin body. */\nexport function apply(ctx: Context): void {\n // The \"服务\" sidebar tab.\n ctx.effect(() =>\n ctx.betterSidebar.registerTab({\n id: 'dsh-better-sidebar-starter:services',\n title: () => '服务',\n icon: (size: number): ReactNode => createElement(RocketIcon, { size }),\n order: 50,\n single: true,\n settings: {\n pluginToggles: [\n { key: 'autoSelectOnStart', type: 'switch', title: '启动后自动选中日志' },\n { key: 'autoScroll', type: 'switch', title: '自动滚动到最新日志' },\n { key: 'javaHome', type: 'text', title: 'Java 主目录 (JAVA_HOME)', desc: 'Spring Boot 用的 JDK 路径,留空则自动查找' },\n { key: 'nodePath', type: 'text', title: 'Node.js 路径', desc: 'npm 命令用的 Node 路径,留空则用系统默认' },\n { key: 'pythonPath', type: 'text', title: 'Python 路径', desc: 'Python 命令用的解释器路径,留空则用系统默认' },\n ],\n render: (props): ReactNode => createElement(SettingsPanel, props),\n },\n component: (props): ReactNode => createElement(ServicesTab, props),\n }),\n 'dsh-better-sidebar-starter: services tab',\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["createElement","TYPE_LABELS","useState","useMemo","createElement","createElement","useState","useRef","useCallback","TYPE_LABELS","createElement","fsTree","useState","useRef","createElement","createElement","useState","createElement","useState","useRef","useCallback","createElement","useState","createElement","createElement"],"sources":["../src/client/icons.tsx","../src/client/ConfigTree.tsx","../src/client/ansi.ts","../src/client/conversationDraft.ts","../src/client/utils.ts","../src/client/LogView.tsx","../src/client/ConfigDetail.tsx","../src/client/DirChooser.tsx","../src/client/PathPicker.tsx","../src/client/ConfigEditorModal.tsx","../src/client/styles.ts","../src/client/ServicesTab.tsx","../src/client/SettingsPanel.tsx","../src/client/index.tsx"],"sourcesContent":["/**\r\n * SVG icons for the starter plugin. Each icon is a React component that\r\n * takes a size prop and renders an inline SVG. Monochrome, currentColor fill.\r\n * @module dsh-better-sidebar-starter/client/icons\r\n */\r\n\r\nimport { createElement, type ReactNode } from 'react'\r\n\r\ninterface IconProps {\r\n size: number\r\n}\r\n\r\nfunction svg(size: number, pathChildren: string): ReactNode {\r\n const svgHtml = `<svg width=\"${size}\" height=\"${size}\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">${pathChildren}</svg>`\r\n return createElement('span', {\r\n style: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: size, height: size },\r\n dangerouslySetInnerHTML: { __html: svgHtml },\r\n })\r\n}\r\n\r\n/** 🚀 Rocket — the tab icon. */\r\nexport function RocketIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M9.19 8.35c.34-.05.67-.1 1-.13 1.2-1.49 2.85-2.49 4.85-3.02 2-.53 4.16-.42 6.5.33.75 2.34.86 4.5.33 6.5-.53 2-1.53 3.65-3.02 4.85-.04.33-.08.66-.13 1-.13.87-.32 1.71-.57 2.5-.42 1.3-1.02 2.5-1.78 3.6-.42.6-1.1.96-1.86.96-.72 0-1.4-.34-1.84-.93l-2.06-2.76c-.2-.27-.5-.43-.82-.43H7.68c-.32 0-.62-.16-.82-.43l-2.06-2.76C4.36 19.34 4 18.66 4 17.94c0-.76.36-1.44.96-1.86 1.1-.76 2.3-1.36 3.6-1.78.79-.25 1.63-.44 2.5-.57l.13-1c.05-.34.1-.67.13-1z\" fill=\"currentColor\" opacity=\"0.85\"/><circle cx=\"15.5\" cy=\"8.5\" r=\"1.5\" fill=\"currentColor\" opacity=\"0.6\"/><path d=\"M5 14c-1 0-2 .5-2 2 0 1 .5 2 .5 2s1-.5 1.5-.5 1.5.5 1.5.5 1-.5 1-2-.5-2-2-2z\" fill=\"currentColor\" opacity=\"0.5\"/>`)\r\n}\r\n\r\n/** ▶ Play — start button. */\r\nexport function PlayIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M8 5v14l11-7z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** ⏹ Stop — stop button. */\r\nexport function StopIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<rect x=\"6\" y=\"6\" width=\"12\" height=\"12\" rx=\"1\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** 🔄 Restart — restart button. */\r\nexport function RestartIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M12 5V2L7 7l5 5V8c2.76 0 5 2.24 5 5s-2.24 5-5 5-5-2.24-5-5H5c0 3.87 3.13 7 7 7s7-3.13 7-7-3.13-7-7-7z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** ⚙ Gear — edit button. */\r\nexport function GearIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm8.94 3l-1.97-.39c-.11-.37-.26-.72-.44-1.06l1.14-1.64c.26-.37.22-.88-.09-1.2l-1.28-1.28c-.32-.31-.83-.35-1.2-.09l-1.64 1.14c-.34-.18-.69-.33-1.06-.44L14.94 4c-.1-.42-.47-.72-.9-.72h-1.8c-.43 0-.8.3-.9.72l-.39 1.97c-.37.11-.72.26-1.06.44L7.25 5.27c-.37-.26-.88-.22-1.2.09L4.77 6.64c-.31.32-.35.83-.09 1.2l1.14 1.64c-.18.34-.33.69-.44 1.06L3.4 11c-.42.1-.72.47-.72.9v1.8c0 .43.3.8.72.9l1.97.39c.11.37.26.72.44 1.06l-1.14 1.64c-.26.37-.22.88.09 1.2l1.28 1.28c.32.31.83.35 1.2.09l1.64-1.14c.34.18.69.33 1.06.44l.39 1.97c.1.42.47.72.9.72h1.8c.43 0 .8-.3.9-.72l.39-1.97c.37-.11.72-.26 1.06-.44l1.64 1.14c.37.26.88.22 1.2-.09l1.28-1.28c.31-.32.35-.83.09-1.2l-1.14-1.64c.18-.34.33-.69.44-1.06L20.6 14c.42-.1.72-.47.72-.9v-1.8c0-.43-.3-.8-.72-.9z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** 🗑 Trash — delete button. */\r\nexport function TrashIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** ➕ Plus — add new config. */\r\nexport function PlusIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** ✕ Close — modal cancel. */\r\nexport function CloseIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** ▾ Chevron down — tree expand. */\r\nexport function ChevronDownIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M7 10l5 5 5-5z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** ▸ Chevron right — tree collapse. */\r\nexport function ChevronRightIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M10 7l5 5-5 5z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** 🟢 Dot — running status. */\r\nexport function RunningDotIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<circle cx=\"12\" cy=\"12\" r=\"5\" fill=\"#4e9a06\"/>`)\r\n}\r\n\r\n/** ✅ Check — completed status. */\r\nexport function CheckIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\" fill=\"#4e9a06\"/>`)\r\n}\r\n\r\n/** 🔴 Error dot — failed status. */\r\nexport function ErrorDotIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<circle cx=\"12\" cy=\"12\" r=\"5\" fill=\"#cc0000\"/>`)\r\n}\r\n\r\n/** 📋 Log — download log. */\r\nexport function DownloadIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** 📦 npm package icon. */\r\nexport function NpmIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/><path d=\"M7 17V8h4v6h2V8h4v9h-4v-3h-2v3H7z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** ☕ springboot icon. */\r\nexport function SpringIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M12 2C7.5 2 4 5.5 4 10c0 3 2 5.5 4.5 7 .5-2 1.5-3.5 3-4.5 1.5 1 2.5 2.5 3 4.5 2.5-1.5 4.5-4 4.5-7 0-4.5-3.5-8-7-8z\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/><path d=\"M8 10c1-1 2.5-1.5 4-1.5s3 .5 4 1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>`)\r\n}\r\n\r\n/** 🐍 python icon. */\r\nexport function PythonIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M12 4c-2 0-4 1-4 3v2h4v1H6c-1 0-2 1-2 3s1 3 2 3h2v-2c0-2 2-3 4-3h2c1 0 2-1 2-2V7c0-2-2-3-4-3z\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/><circle cx=\"10.5\" cy=\"6.5\" r=\"0.8\" fill=\"currentColor\"/><path d=\"M12 20c2 0 4-1 4-3v-2h-4v-1h4c1 0 2-1 2-3s-1-3-2-3h-2v2c0 2-2 3-4 3h-2c-1 0-2 1-2 2v3c0 2 2 3 4 3z\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/><circle cx=\"13.5\" cy=\"17.5\" r=\"0.8\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** ⚡ custom icon. */\r\nexport function CustomIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M11 21h-1l1-7H7.5c-.6 0-.8-.4-.5-.9 1.5-2.6 3.7-6.2 6.5-10.1h1l-1 7h3.5c.6 0 .8.4.5.9C14.5 14.7 12.3 18.1 11 21z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** 📁 Folder — directory tree icon. */\r\nexport function FolderIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z\" fill=\"currentColor\" opacity=\"0.7\"/>`)\r\n}\r\n\r\n/** 📋 Clipboard — bulk paste env. */\r\nexport function ClipboardIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M19 3h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm2 14H7v-2h7v2zm3-4H7v-2h10v2zm0-4H7V7h10v2z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** 📄 Copy — duplicate a run config. */\r\nexport function CopyIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M21 8c0-1.1-.9-2-2-2h-9c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h9c1.1 0 2-.9 2-2V8z\" fill=\"currentColor\"/><path d=\"M3 16V4c0-1.1.9-2 2-2h10v2H5v12H3z\" fill=\"currentColor\" opacity=\"0.6\"/>`)\r\n}\r\n\r\n/** ✈️ Send — send selected text to the conversation composer. */\r\nexport function SendToChatIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M2.01 21L23 12 2.01 3 2 10l15 2-15 2z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** Get the icon for a config type. */\r\nexport function typeIcon(type: string, size: number): ReactNode {\r\n switch (type) {\r\n case 'npm': return NpmIcon({ size })\r\n case 'springboot': return SpringIcon({ size })\r\n case 'python': return PythonIcon({ size })\r\n default: return CustomIcon({ size })\r\n }\r\n}\r\n","/**\r\n * Config tree: the left pane showing all run configurations grouped by type,\r\n * with running instances as child nodes.\r\n * @module dsh-better-sidebar-starter/client/ConfigTree\r\n */\r\n\r\nimport { createElement, useState, useMemo, type ReactNode } from 'react'\r\nimport type { RunConfig, RunInstance } from './types.ts'\r\nimport {\r\n PlayIcon, StopIcon, GearIcon, CopyIcon,\r\n ChevronDownIcon, ChevronRightIcon,\r\n RunningDotIcon, CheckIcon, ErrorDotIcon,\r\n typeIcon,\r\n} from './icons.tsx'\r\n\r\n/** Props for the config tree. */\r\nexport interface ConfigTreeProps {\r\n configs: RunConfig[]\r\n instances: RunInstance[]\r\n selectedInstanceId: string | null\r\n selectedConfigId: string | null\r\n onSelectConfig: (configId: string) => void\r\n onSelectInstance: (instanceId: string) => void\r\n onStartConfig: (configId: string) => void\r\n onStopInstance: (instanceId: string) => void\r\n onEditConfig: (configId: string) => void\r\n /** Duplicate a config: opens the editor prefilled with all its info under a new name. */\r\n onDuplicateConfig: (configId: string) => void\r\n}\r\n\r\n/** Type group order. */\r\nconst TYPE_ORDER: string[] = ['npm', 'springboot', 'python', 'custom']\r\nconst TYPE_LABELS: Record<string, string> = {\r\n npm: 'npm', springboot: 'Spring Boot', python: 'Python', custom: 'Custom',\r\n}\r\n\r\n/** Status icon for an instance. */\r\nfunction statusIcon(status: string, size: number): ReactNode {\r\n switch (status) {\r\n case 'running': return RunningDotIcon({ size })\r\n case 'exited': return CheckIcon({ size })\r\n case 'killed':\r\n case 'error': return ErrorDotIcon({ size })\r\n default: return null\r\n }\r\n}\r\n\r\n/** The config tree component. */\r\nexport function ConfigTree(props: ConfigTreeProps): ReactNode {\r\n const { configs, instances, selectedInstanceId, selectedConfigId,\r\n onSelectConfig, onSelectInstance, onStartConfig, onStopInstance, onEditConfig, onDuplicateConfig } = props\r\n\r\n const [collapsed, setCollapsed] = useState<Set<string>>(new Set())\r\n\r\n const toggleGroup = (type: string): void => {\r\n setCollapsed((prev) => {\r\n const next = new Set(prev)\r\n if (next.has(type)) next.delete(type)\r\n else next.add(type)\r\n return next\r\n })\r\n }\r\n\r\n // Group configs by type, ordered by TYPE_ORDER, sorted by lastRunAt desc.\r\n const groups = useMemo(() => {\r\n const map = new Map<string, RunConfig[]>()\r\n for (const config of configs) {\r\n const arr = map.get(config.type) ?? []\r\n arr.push(config)\r\n map.set(config.type, arr)\r\n }\r\n for (const arr of map.values()) {\r\n arr.sort((a, b) => (b.lastRunAt ?? 0) - (a.lastRunAt ?? 0))\r\n }\r\n return TYPE_ORDER\r\n .filter((type) => map.has(type) && map.get(type)!.length > 0)\r\n .map((type) => ({ type, items: map.get(type)! }))\r\n }, [configs])\r\n\r\n if (configs.length === 0) {\r\n return createElement('div', { className: 'sts-empty' },\r\n '暂无运行配置',\r\n createElement('br'),\r\n '点击右上角 + 新建配置',\r\n )\r\n }\r\n\r\n return createElement('div', { className: 'sts-tree' },\r\n ...groups.map((group) => {\r\n const isCollapsed = collapsed.has(group.type)\r\n const label = TYPE_LABELS[group.type] ?? group.type\r\n const runningCount = instances.filter(\r\n (inst) => inst.configType === group.type && inst.status === 'running',\r\n ).length\r\n\r\n return createElement('div', { key: group.type },\r\n // Group header\r\n createElement('div', {\r\n className: 'sts-group-header',\r\n onClick: () => toggleGroup(group.type),\r\n },\r\n isCollapsed\r\n ? createElement(ChevronRightIcon, { size: 12 })\r\n : createElement(ChevronDownIcon, { size: 12 }),\r\n typeIcon(group.type, 14),\r\n label,\r\n createElement('span', { className: 'sts-group-count' },\r\n ` (${group.items.length}${runningCount > 0 ? `, ${runningCount} 运行中` : ''})`,\r\n ),\r\n ),\r\n // Group children\r\n createElement('div', {\r\n className: `sts-group-children${isCollapsed ? ' collapsed' : ''}`,\r\n },\r\n ...group.items.flatMap((config) => {\r\n const configInstances = instances.filter((inst) => inst.configId === config.id)\r\n const isRunning = configInstances.some((inst) => inst.status === 'running')\r\n const isSelected = selectedConfigId === config.id && selectedInstanceId === null\r\n\r\n const rows: ReactNode[] = [\r\n // Config row\r\n createElement('div', {\r\n key: config.id,\r\n className: `sts-config-row${isSelected ? ' selected' : ''}${isRunning ? ' running' : ''}`,\r\n onClick: () => onSelectConfig(config.id),\r\n },\r\n isRunning\r\n ? RunningDotIcon({ size: 12 })\r\n : typeIcon(config.type, 12),\r\n createElement('div', { className: 'sts-config-name' }, config.name),\r\n createElement('div', { className: 'sts-config-cmd' }, config.command),\r\n createElement('div', { className: 'sts-config-actions' },\r\n isRunning\r\n ? createElement('button', {\r\n className: 'sts-icon-btn stop',\r\n title: '停止全部',\r\n onClick: (e: MouseEvent) => {\r\n e.stopPropagation()\r\n for (const inst of configInstances) {\r\n if (inst.status === 'running') onStopInstance(inst.id)\r\n }\r\n },\r\n }, StopIcon({ size: 14 }))\r\n : createElement('button', {\r\n className: 'sts-icon-btn play',\r\n title: '启动',\r\n onClick: (e: MouseEvent) => {\r\n e.stopPropagation()\r\n onStartConfig(config.id)\r\n },\r\n }, PlayIcon({ size: 14 })),\r\n createElement('button', {\r\n className: 'sts-icon-btn gear',\r\n title: '编辑',\r\n onClick: (e: MouseEvent) => {\r\n e.stopPropagation()\r\n onEditConfig(config.id)\r\n },\r\n }, GearIcon({ size: 12 })),\r\n createElement('button', {\r\n className: 'sts-icon-btn dup',\r\n title: '复制(生成副本配置)',\r\n onClick: (e: MouseEvent) => {\r\n e.stopPropagation()\r\n onDuplicateConfig(config.id)\r\n },\r\n }, CopyIcon({ size: 12 })),\r\n ),\r\n ),\r\n ]\r\n\r\n // Instance rows (children)\r\n for (const inst of configInstances) {\r\n const instSelected = selectedInstanceId === inst.id\r\n rows.push(\r\n createElement('div', {\r\n key: inst.id,\r\n className: `sts-instance-row${instSelected ? ' selected' : ''}`,\r\n onClick: () => onSelectInstance(inst.id),\r\n },\r\n statusIcon(inst.status, 10),\r\n `#${inst.id.slice(0, 8)}`,\r\n createElement('span', { className: 'sts-instance-pid' },\r\n inst.status === 'running' ? ` pid:${inst.pid}` : ` ${inst.status}`,\r\n ),\r\n inst.status === 'running'\r\n ? createElement('button', {\r\n className: 'sts-icon-btn stop',\r\n title: '停止',\r\n onClick: (e: MouseEvent) => {\r\n e.stopPropagation()\r\n onStopInstance(inst.id)\r\n },\r\n }, StopIcon({ size: 12 }))\r\n : null,\r\n ),\r\n )\r\n }\r\n\r\n return rows\r\n }),\r\n ),\r\n )\r\n }),\r\n )\r\n}\r\n","/**\n * ANSI escape code parser: converts raw terminal output with ANSI SGR\n * sequences into an array of styled text segments for React rendering.\n * Covers the common color codes (30-37, 40-47, 90-97, 100-107, bold, dim,\n * italic, underline, reset). Non-SGR escape sequences are stripped.\n * @module dsh-better-sidebar-starter/client/ansi\n */\n\n/** One styled text segment. */\nexport interface AnsiSegment {\n text: string\n bold: boolean\n dim: boolean\n italic: boolean\n underline: boolean\n fg: number | null\n bg: number | null\n}\n\ninterface ParseState {\n bold: boolean\n dim: boolean\n italic: boolean\n underline: boolean\n fg: number | null\n bg: number | null\n}\n\nfunction cloneState(s: ParseState): AnsiSegment {\n return {\n text: '',\n bold: s.bold,\n dim: s.dim,\n italic: s.italic,\n underline: s.underline,\n fg: s.fg,\n bg: s.bg,\n }\n}\n\n/** Apply one SGR parameter to the parse state. */\nfunction applySgr(state: ParseState, code: number): void {\n if (code === 0) {\n state.bold = false; state.dim = false; state.italic = false\n state.underline = false; state.fg = null; state.bg = null\n } else if (code === 1) {\n state.bold = true\n } else if (code === 2) {\n state.dim = true\n } else if (code === 3) {\n state.italic = true\n } else if (code === 4) {\n state.underline = true\n } else if (code === 22) {\n state.bold = false; state.dim = false\n } else if (code === 23) {\n state.italic = false\n } else if (code === 24) {\n state.underline = false\n } else if (code >= 30 && code <= 37) {\n state.fg = code - 30\n } else if (code === 39) {\n state.fg = null\n } else if (code >= 40 && code <= 47) {\n state.bg = code - 40\n } else if (code === 49) {\n state.bg = null\n } else if (code >= 90 && code <= 97) {\n state.fg = code - 90 + 8\n } else if (code >= 100 && code <= 107) {\n state.bg = code - 100 + 8\n }\n}\n\n/**\n * Parse a string with ANSI escape codes into styled segments.\n * Returns an array of AnsiSegment objects ready for React rendering.\n */\nexport function parseAnsi(input: string): AnsiSegment[] {\n const state: ParseState = {\n bold: false, dim: false, italic: false, underline: false, fg: null, bg: null,\n }\n const segments: AnsiSegment[] = []\n let current = cloneState(state)\n\n // Regex: match CSI sequences (ESC [ ... letter) and non-CSI text.\n const regex = /\\x1b\\[([0-9;]*)m|\\x1b\\][^\\x07]*\\x07|\\x1b\\[[0-9;]*[A-Za-z]/g\n let lastIndex = 0\n let match: RegExpExecArray | null\n\n while ((match = regex.exec(input)) !== null) {\n // Text before this escape sequence.\n if (match.index > lastIndex) {\n current.text += input.slice(lastIndex, match.index)\n }\n // Process the escape sequence.\n if (match[1] !== undefined) {\n // SGR sequence: ESC [ ... m\n if (current.text !== '') {\n segments.push(current)\n }\n const params = match[1] === '' ? '0' : match[1]\n for (const codeStr of params.split(';')) {\n const code = codeStr === '' ? 0 : Number(codeStr)\n if (!Number.isNaN(code)) applySgr(state, code)\n }\n current = cloneState(state)\n }\n // Non-SGR sequences (OSC, other CSI) are simply stripped.\n lastIndex = regex.lastIndex\n }\n\n // Remaining text after the last escape.\n if (lastIndex < input.length) {\n current.text += input.slice(lastIndex)\n }\n if (current.text !== '') {\n segments.push(current)\n }\n\n return segments\n}\n\n/** Map an ANSI color index (0-15) to a CSS color string. */\nconst ANSI_COLORS: readonly string[] = [\n // 0-7: standard colors\n '#000000', '#cc0000', '#4e9a06', '#c4a000', '#3465a4', '#75507b', '#06989a', '#d3d7cf',\n // 8-15: bright colors\n '#555753', '#ef2929', '#8ae234', '#fce94f', '#729fcf', '#ad7fa8', '#34e2e2', '#eeeeec',\n]\n\n/** Resolve a segment's foreground color to a CSS string, or null for default. */\nexport function segmentFgColor(seg: AnsiSegment): string | null {\n if (seg.fg === null) return null\n return ANSI_COLORS[seg.fg] ?? null\n}\n\n/** Resolve a segment's background color to a CSS string, or null for default. */\nexport function segmentBgColor(seg: AnsiSegment): string | null {\n if (seg.bg === null) return null\n return ANSI_COLORS[seg.bg] ?? null\n}\n","/**\n * Send text into the DSH conversation composer draft. Mirrors the internal\n * `appendToDraft` of dsh-better-sidebar (not exported from that package):\n * the shared path behind the explorer's @-reference button / viewer\n * selection popup. The conversation service is resolved lazily through\n * `ctx.get`; a missing service, session scope, or activity scope degrades\n * to a logged no-op — never a crash.\n * @module dsh-better-sidebar-starter/client/conversationDraft\n */\n\nimport type { Context } from 'cordis'\n\n/** Minimal structural face of the composer input handle we touch. */\ninterface DraftInputLike {\n state: { getSnapshot(): { draft?: string } }\n setDraft(draft: string): void\n}\n\n/** Minimal structural face of the ui-conversation service (composer). */\ninterface ConversationLike {\n input: { for(actx: unknown): DraftInputLike | undefined }\n}\n\n/** Structural face of the client runtime sessions service. */\ninterface SessionsLike {\n scope(sessionId: string): unknown\n}\n\n/**\n * Append `text` to the session's composer draft (space-separated, like the\n * @-mentions). Returns false — and warns — when the conversation service\n * or the session scope is unavailable.\n */\nexport function appendToDraft(ctx: Context, sessionId: string, text: string): boolean {\n try {\n const sessions = (ctx as { sessions?: SessionsLike }).sessions\n const actx = sessions?.scope(sessionId)\n if (actx === undefined) return false\n const getService = (ctx as unknown as { get: (name: string) => unknown }).get.bind(ctx)\n const conversation = getService('conversation') as ConversationLike | undefined\n if (conversation === undefined || conversation === null) return false\n const input = conversation.input.for(actx)\n if (input === undefined) return false\n const draft = input.state.getSnapshot().draft ?? ''\n input.setDraft(draft.trim() === '' ? text : `${draft} ${text}`)\n return true\n } catch (error) {\n console.warn('[dsh-better-sidebar-starter] draft insert failed:', error)\n return false\n }\n}","/**\n * Shared utility functions for the starter plugin.\n * @module dsh-better-sidebar-starter/client/utils\n */\n\nlet toastEl: HTMLDivElement | null = null\nlet toastTimer: ReturnType<typeof setTimeout> | null = null\n\n/** Show a small floating toast notification. */\nexport function showToast(message: string, success: boolean): void {\n // Dismiss previous toast.\n if (toastTimer !== null) {\n clearTimeout(toastTimer)\n toastTimer = null\n }\n if (toastEl !== null) {\n document.body.removeChild(toastEl)\n toastEl = null\n }\n\n const el = document.createElement('div')\n el.textContent = message\n el.style.cssText = `\n position: fixed;\n top: 16px;\n left: 50%;\n transform: translateX(-50%);\n padding: 8px 16px;\n background: ${success ? 'rgba(78,154,6,0.9)' : 'rgba(235,87,87,0.9)'};\n color: #fff;\n font-size: 12px;\n border-radius: 6px;\n z-index: 999999;\n box-shadow: 0 2px 12px rgba(0,0,0,0.4);\n pointer-events: none;\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, monospace;\n `\n document.body.appendChild(el)\n toastEl = el\n toastTimer = setTimeout(() => {\n if (toastEl !== null) {\n document.body.removeChild(toastEl)\n toastEl = null\n }\n toastTimer = null\n }, 2000)\n}\n\n/** Copy text to clipboard with fallback for sandboxed iframes. Shows toast feedback. */\nexport function copyToClipboard(text: string): boolean {\n try {\n // Try modern clipboard API.\n if (navigator.clipboard && (window as any).clipboardEvent !== undefined) {\n void navigator.clipboard.writeText(text)\n showToast('已复制到剪贴板', true)\n return true\n }\n } catch { /* fallback below */ }\n\n // Fallback: create textarea, select, execCommand.\n try {\n const ta = document.createElement('textarea')\n ta.value = text\n ta.style.position = 'fixed'\n ta.style.left = '-9999px'\n ta.style.top = '0'\n ta.style.opacity = '0'\n ta.style.width = '1px'\n ta.style.height = '1px'\n document.body.appendChild(ta)\n ta.focus()\n ta.select()\n const ok = document.execCommand('copy')\n document.body.removeChild(ta)\n if (ok) {\n showToast('已复制到剪贴板', true)\n return true\n }\n } catch { /* last resort below */ }\n\n // Last resort.\n try {\n void navigator.clipboard?.writeText(text)\n showToast('已复制到剪贴板', true)\n return true\n } catch {\n showToast('复制失败', false)\n return false\n }\n}","/**\r\n * Log view: the right pane showing real-time log output from a running\r\n * instance. Connects to the WebSocket endpoint, parses ANSI colors, and\r\n * renders log lines with auto-scroll.\r\n * @module dsh-better-sidebar-starter/client/LogView\r\n */\r\n\r\nimport { createElement, useState, useEffect, useRef, useCallback, type ReactNode } from 'react'\r\nimport type { Context } from 'cordis'\r\nimport type { LogEntry, RunInstance } from './types.ts'\r\nimport type { WsMessage } from './types.ts'\r\nimport { parseAnsi, segmentFgColor, segmentBgColor, type AnsiSegment } from './ansi.ts'\r\nimport { StopIcon, RestartIcon, TrashIcon, SendToChatIcon } from './icons.tsx'\r\nimport { appendToDraft } from './conversationDraft.ts'\r\nimport { showToast } from './utils.ts'\r\n\r\n/** Props for the log view. */\r\nexport interface LogViewProps {\r\n instance: RunInstance\r\n /** The sidebar tab's cordis context (used to reach the conversation service). */\r\n ctx: Context\r\n /** The session to send selected text into. */\r\n sessionId: string\r\n onStop: () => void\r\n onRestart: () => void\r\n}\r\n\r\n/** One rendered log line. */\r\ninterface LogLine {\r\n entries: LogEntry[]\r\n key: number\r\n}\r\n\r\n/** Render ANSI-colored text segments. */\r\nfunction renderAnsiLine(text: string, keyBase: string): ReactNode {\r\n const segments = parseAnsi(text)\r\n return segments.map((seg: AnsiSegment, i: number) => {\r\n const style: Record<string, string> = {}\r\n const fg = segmentFgColor(seg)\r\n const bg = segmentBgColor(seg)\r\n if (fg !== null) style.color = fg\r\n if (bg !== null) style.backgroundColor = bg\r\n if (seg.bold) style.fontWeight = 'bold'\r\n if (seg.dim) style.opacity = '0.6'\r\n if (seg.italic) style.fontStyle = 'italic'\r\n if (seg.underline) style.textDecoration = 'underline'\r\n return createElement('span', { key: `${keyBase}-${i}`, style }, seg.text)\r\n })\r\n}\r\n\r\n/** The log view component. */\r\nexport function LogView({ instance, ctx, sessionId, onStop, onRestart }: LogViewProps): ReactNode {\r\n const [logLines, setLogLines] = useState<LogLine[]>([])\r\n const [autoScroll, setAutoScroll] = useState(true)\r\n const [exitInfo, setExitInfo] = useState<{ status: string; exitCode: number | null } | null>(null)\r\n const [selLength, setSelLength] = useState(0)\r\n const contentRef = useRef<HTMLDivElement>(null)\r\n const wsRef = useRef<WebSocket | null>(null)\r\n const lineKeyCounter = useRef(0)\r\n\r\n // Track the length of the current text selection inside this panel.\r\n useEffect(() => {\r\n const updateSel = (): void => {\r\n const sel = window.getSelection()\r\n const len = sel !== null && !sel.isCollapsed ? sel.toString().trim().length : 0\r\n setSelLength(len)\r\n }\r\n document.addEventListener('selectionchange', updateSel)\r\n return () => document.removeEventListener('selectionchange', updateSel)\r\n }, [])\r\n\r\n /** Send the currently selected log text into the conversation composer. */\r\n const handleSendSelection = (): void => {\r\n const sel = window.getSelection()\r\n const text = sel !== null && !sel.isCollapsed ? sel.toString().trim() : ''\r\n if (text === '') {\r\n showToast('请先在日志中选中文本', false)\r\n return\r\n }\r\n if (appendToDraft(ctx, sessionId, text)) {\r\n showToast(`已将选中文本(${text.length} 字符)发送到对话`, true)\r\n } else {\r\n showToast('发送失败:对话服务不可用', false)\r\n }\r\n }\r\n\r\n // Buffer accumulation: accumulate text entries into complete lines.\r\n const lineBuffer = useRef<{ current: LogEntry[] }>({ current: [] })\r\n\r\n /** Flush the buffer into a new log line. */\r\n const flushLine = useCallback((): void => {\r\n const entries = lineBuffer.current.current\r\n if (entries.length === 0) return\r\n lineBuffer.current.current = []\r\n setLogLines((prev) => {\r\n const key = lineKeyCounter.current++\r\n // Cap at 5000 lines to avoid memory issues.\r\n const next = [...prev, { entries, key }]\r\n if (next.length > 5000) next.splice(0, next.length - 5000)\r\n return next\r\n })\r\n }, [])\r\n\r\n /** Add an entry to the current line buffer, splitting on newlines. */\r\n const addEntry = useCallback((entry: LogEntry): void => {\r\n const parts = entry.text.split('\\n')\r\n for (let i = 0; i < parts.length; i++) {\r\n if (i > 0) {\r\n // Newline boundary: flush the current line.\r\n flushLine()\r\n }\r\n if (parts[i] !== '') {\r\n lineBuffer.current.current.push({\r\n stream: entry.stream,\r\n text: parts[i],\r\n ts: entry.ts,\r\n })\r\n }\r\n }\r\n // If the entry ends with \\n, flush.\r\n if (parts.length > 1 && parts[parts.length - 1] === '') {\r\n flushLine()\r\n }\r\n }, [flushLine])\r\n\r\n // Connect WebSocket.\r\n useEffect(() => {\r\n const url = `/api/dsh-better-sidebar-starter/logs?instanceId=${encodeURIComponent(instance.id)}`\r\n const ws = new WebSocket(url)\r\n wsRef.current = ws\r\n\r\n ws.onmessage = (event: MessageEvent): void => {\r\n try {\r\n const msg = JSON.parse(event.data as string) as WsMessage\r\n if (msg.type === 'history') {\r\n setLogLines([])\r\n lineBuffer.current.current = []\r\n for (const entry of msg.entries) {\r\n addEntry(entry)\r\n }\r\n flushLine()\r\n } else if (msg.type === 'log') {\r\n addEntry(msg.entry)\r\n } else if (msg.type === 'status') {\r\n if (msg.status !== 'running') {\r\n flushLine()\r\n setExitInfo({ status: msg.status, exitCode: msg.exitCode })\r\n }\r\n }\r\n } catch { /* ignore parse errors */ }\r\n }\r\n\r\n return () => {\r\n ws.close()\r\n wsRef.current = null\r\n }\r\n }, [instance.id, addEntry, flushLine])\r\n\r\n // Auto-scroll to bottom when new lines arrive.\r\n useEffect(() => {\r\n if (autoScroll && contentRef.current !== null) {\r\n contentRef.current.scrollTop = contentRef.current.scrollHeight\r\n }\r\n }, [logLines, autoScroll])\r\n\r\n const handleClear = (): void => {\r\n setLogLines([])\r\n lineBuffer.current.current = []\r\n }\r\n\r\n return createElement('div', { style: { display: 'flex', flexDirection: 'column', height: '100%', minHeight: 0 } },\r\n // Toolbar\r\n createElement('div', { className: 'sts-log-toolbar' },\r\n instance.configName,\r\n createElement('span', { style: { opacity: 0.5, marginLeft: '4px' } }, `#${instance.id.slice(0, 8)}`),\r\n createElement('div', { className: 'sts-log-toolbar-spacer' }),\r\n instance.status === 'running'\r\n ? createElement('button', {\r\n className: 'sts-icon-btn stop',\r\n title: '停止',\r\n onClick: onStop,\r\n }, StopIcon({ size: 14 }), ' 停止')\r\n : null,\r\n createElement('button', {\r\n className: 'sts-icon-btn',\r\n title: '重启',\r\n onClick: onRestart,\r\n }, RestartIcon({ size: 14 }), ' 重启'),\r\n createElement('button', {\r\n className: 'sts-icon-btn',\r\n title: '清空',\r\n onClick: handleClear,\r\n }, TrashIcon({ size: 14 }), ' 清空'),\r\n createElement('button', {\r\n className: 'sts-icon-btn send',\r\n title: selLength > 0 ? `发送选中的 ${selLength} 字符到对话` : '发送选中的文本到对话',\r\n onClick: handleSendSelection,\r\n }, SendToChatIcon({ size: 14 }), selLength > 0 ? ` 发送选中(${selLength})` : ' 发送选中'),\r\n createElement('label', {\r\n className: 'sts-log-toolbar-label',\r\n onClick: () => setAutoScroll((v) => !v),\r\n },\r\n autoScroll ? '☑' : '☐',\r\n ' 自动滚动',\r\n ),\r\n ),\r\n // Log content\r\n createElement('div', { ref: contentRef, className: 'sts-log-content' },\r\n ...logLines.map((line) => {\r\n const keyStr = `line-${line.key}`\r\n return createElement('div', { key: keyStr, className: 'sts-log-line' },\r\n ...line.entries.map((entry, i) => {\r\n const segKey = `${keyStr}-e${i}`\r\n return createElement('span', {\r\n key: segKey,\r\n className: entry.stream === 'stderr' ? 'sts-log-stderr' : '',\r\n }, renderAnsiLine(entry.text, segKey))\r\n }),\r\n )\r\n }),\r\n // Exit info\r\n exitInfo !== null\r\n ? createElement('div', {\r\n key: 'exit-info',\r\n style: {\r\n color: exitInfo.status === 'exited' && exitInfo.exitCode === 0 ? '#4e9a06' : '#cc0000',\r\n fontWeight: 'bold',\r\n marginTop: '4px',\r\n },\r\n },\r\n `\\n进程${exitInfo.status === 'exited' ? '退出' : exitInfo.status === 'killed' ? '被终止' : '出错'}`,\r\n exitInfo.exitCode !== null ? `,退出码: ${exitInfo.exitCode}` : '',\r\n )\r\n : null,\r\n ),\r\n )\r\n}\r\n","/**\r\n * Config detail card: shown in the right pane when an idle config is selected.\r\n * Displays the config's full info and offers start / edit / delete buttons.\r\n * @module dsh-better-sidebar-starter/client/ConfigDetail\r\n */\r\n\r\nimport { createElement } from 'react'\r\nimport type { ReactNode } from 'react'\r\nimport type { RunConfig } from './types.ts'\r\nimport { PlayIcon, GearIcon, TrashIcon, CopyIcon, typeIcon } from './icons.tsx'\r\n\r\n/** Props for the detail card. */\r\nexport interface ConfigDetailProps {\r\n config: RunConfig\r\n onStart: () => void\r\n onEdit: () => void\r\n onDuplicate: () => void\r\n onDelete: () => void\r\n}\r\n\r\nconst TYPE_LABELS: Record<string, string> = {\r\n npm: 'npm',\r\n springboot: 'Spring Boot',\r\n python: 'Python',\r\n custom: 'Custom',\r\n}\r\n\r\n/** The detail card component. */\r\nexport function ConfigDetail({ config, onStart, onEdit, onDuplicate, onDelete }: ConfigDetailProps): ReactNode {\r\n const envEntries = Object.entries(config.env)\r\n const dateFmt = (ts: number | null): string => {\r\n if (ts === null) return '—'\r\n return new Date(ts).toLocaleString('zh-CN', { hour12: false })\r\n }\r\n\r\n return createElement('div', { className: 'sts-detail' },\r\n createElement('div', { className: 'sts-detail-card' },\r\n // Title\r\n createElement('div', { className: 'sts-detail-title' },\r\n typeIcon(config.type, 20),\r\n config.name,\r\n ),\r\n // Type\r\n createElement('div', { className: 'sts-detail-row' },\r\n createElement('div', { className: 'sts-detail-label' }, '类型'),\r\n createElement('div', { className: 'sts-detail-value' }, TYPE_LABELS[config.type] ?? config.type),\r\n ),\r\n // Command\r\n createElement('div', { className: 'sts-detail-row' },\r\n createElement('div', { className: 'sts-detail-label' }, '命令'),\r\n createElement('div', { className: 'sts-detail-value' }, config.command),\r\n ),\r\n // Cwd\r\n createElement('div', { className: 'sts-detail-row' },\r\n createElement('div', { className: 'sts-detail-label' }, '工作目录'),\r\n createElement('div', { className: 'sts-detail-value' }, config.cwd),\r\n ),\r\n // JVM args (springboot only)\r\n config.type === 'springboot' && config.jvmArgs\r\n ? createElement('div', { className: 'sts-detail-row' },\r\n createElement('div', { className: 'sts-detail-label' }, 'JVM参数'),\r\n createElement('div', { className: 'sts-detail-value' }, config.jvmArgs),\r\n )\r\n : null,\r\n // Program args\r\n config.args\r\n ? createElement('div', { className: 'sts-detail-row' },\r\n createElement('div', { className: 'sts-detail-label' }, '程序实参'),\r\n createElement('div', { className: 'sts-detail-value' }, config.args),\r\n )\r\n : null,\r\n // Env\r\n envEntries.length > 0\r\n ? createElement('div', { className: 'sts-detail-row' },\r\n createElement('div', { className: 'sts-detail-label' }, '环境变量'),\r\n createElement('div', { style: { flex: 1 } },\r\n ...envEntries.map(([key, value]) =>\r\n createElement('div', { key, className: 'sts-detail-value' },\r\n `${key} = ${value}`,\r\n ),\r\n ),\r\n ),\r\n )\r\n : null,\r\n // Timestamps\r\n createElement('div', { className: 'sts-detail-row' },\r\n createElement('div', { className: 'sts-detail-label' }, '创建时间'),\r\n createElement('div', { className: 'sts-detail-value' }, dateFmt(config.createdAt)),\r\n ),\r\n config.lastRunAt !== null\r\n ? createElement('div', { className: 'sts-detail-row' },\r\n createElement('div', { className: 'sts-detail-label' }, '最近运行'),\r\n createElement('div', { className: 'sts-detail-value' }, dateFmt(config.lastRunAt)),\r\n )\r\n : null,\r\n // Actions\r\n createElement('div', { className: 'sts-detail-actions' },\r\n createElement('button', { className: 'sts-detail-btn danger', onClick: onDelete },\r\n createElement(TrashIcon, { size: 14 }), ' 删除',\r\n ),\r\n createElement('button', { className: 'sts-detail-btn', onClick: onDuplicate, title: '复制为副本(保留全部信息)' },\r\n createElement(CopyIcon, { size: 14 }), ' 复制',\r\n ),\r\n createElement('button', { className: 'sts-detail-btn', onClick: onEdit },\r\n createElement(GearIcon, { size: 14 }), ' 编辑',\r\n ),\r\n createElement('button', {\r\n className: 'sts-detail-btn primary',\r\n onClick: onStart,\r\n style: {\r\n background: '#1a1a1e',\r\n border: '1px solid #3a3a3e',\r\n color: '#fff',\r\n borderRadius: '6px',\r\n padding: '6px 14px',\r\n cursor: 'pointer',\r\n fontSize: '12px',\r\n },\r\n },\r\n createElement(PlayIcon, { size: 14 }), ' 启动',\r\n ),\r\n ),\r\n ),\r\n )\r\n}\r\n","/**\n * Shared directory chooser: a filesystem folder tree (backed by the sidebar\n * fs.tree API, which can list any absolute path) plus a browse button that\n * opens a modal popup. Browsing starts at the user's home directory and can\n * navigate up to any drive/root, so runtime tool paths (JDK/Maven/...) that\n * live outside the workspace can be selected.\n * @module dsh-better-sidebar-starter/client/DirChooser\n */\n\nimport { createElement, useState, useEffect, useRef, type ReactNode } from 'react'\nimport type { SessionScope } from 'dsh-better-sidebar/client/service'\nimport { FolderIcon, ChevronRightIcon, ChevronDownIcon, CloseIcon, CheckIcon } from './icons.tsx'\n\n/** One directory entry from the sidebar fs.tree API. */\nexport interface FsEntry {\n name: string\n path: string\n isDir: boolean\n hidden: boolean\n isSymlink: boolean\n broken: boolean\n}\n\n/** Call the sidebar fs.tree API directly via fetch. Returns directories only. */\nexport async function fsTree(scope: SessionScope, path: string): Promise<FsEntry[]> {\n const payload: Record<string, string> = { sessionId: scope.sessionId, path }\n if (scope.cwd !== undefined && scope.cwd !== '') payload.cwd = scope.cwd\n const resp = await fetch('/sidebar/api/fs.tree', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!resp.ok) throw new Error(`fs.tree ${path}: ${resp.status}`)\n const parsed = await resp.json() as { ok?: boolean; value?: { entries: FsEntry[] }; error?: { message: string } }\n if (!parsed.ok || parsed.value === undefined) {\n throw new Error(`fs.tree ${path}: ${parsed.error?.message ?? 'unknown error'}`)\n }\n return parsed.value.entries.filter((e) => e.isDir && !e.hidden)\n}\n\n/** Fetch the user's home directory from the host. Falls back to cwd. */\nasync function fetchHomeDefault(scope: SessionScope): Promise<string> {\n try {\n const r = await fetch('/api/dsh-better-sidebar-starter/detect-env', { method: 'GET' })\n if (r.ok) {\n const data = await r.json() as { home?: string }\n if (data.home && data.home !== '') return data.home\n }\n } catch { /* ignore */ }\n return scope.cwd ?? '.'\n}\n\n/** Normalize a path, returning its parent or null if at the root. */\nfunction parentOf(p: string): string | null {\n const trimmed = p.replace(/[\\\\/]+$/, '')\n const lastSep = Math.max(trimmed.lastIndexOf('/'), trimmed.lastIndexOf('\\\\'))\n if (lastSep <= 0) return null\n return trimmed.substring(0, lastSep)\n}\n\n/** The folder tree panel: shows the current dir's subdirectories with up-nav. */\nexport function DirTree(props: {\n scope: SessionScope\n selectedPath: string\n onPick: (path: string) => void\n}): ReactNode {\n const { scope, selectedPath, onPick } = props\n const [cur, setCur] = useState<string>('.') // current directory being browsed\n const [entries, setEntries] = useState<FsEntry[] | null>(null)\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const [expanded, setExpanded] = useState<Set<string>>(new Set())\n\n // Stash scope in a ref so the init effect only runs once regardless of the\n // parent re-rendering with a fresh scope object each frame.\n const scopeRef = useRef(scope)\n const initDoneRef = useRef(false)\n // Remember the initial selected value so the popup opens at, or as close to\n // as possible, the already-configured path.\n const initialValueRef = useRef(selectedPath)\n\n // Initialize browsing root: prefer an existing selected path, else home.\n useEffect(() => {\n if (initDoneRef.current) return\n initDoneRef.current = true\n let cancelled = false\n const s = scopeRef.current\n const startPath = initialValueRef.current?.trim() && initialValueRef.current !== '.' && initialValueRef.current !== './'\n ? initialValueRef.current.trim()\n : null\n if (startPath !== null) {\n setCur(startPath)\n loadDir(s, startPath)\n // If the start path points at a file dir inside a parent, still fine.\n } else {\n fetchHomeDefault(s).then((home) => {\n if (cancelled) return\n setCur(home)\n loadDir(s, home)\n })\n }\n return () => { cancelled = true }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n const loadDir = (s: SessionScope, path: string): void => {\n setLoading(true)\n setError(null)\n fsTree(s, path)\n .then((dirs) => {\n setEntries(dirs)\n setLoading(false)\n })\n .catch((err) => {\n setError(err instanceof Error ? err.message : String(err))\n setLoading(false)\n })\n }\n\n const goUp = (): void => {\n const parent = parentOf(cur)\n if (parent !== null) {\n setCur(parent)\n loadDir(scopeRef.current, parent)\n }\n }\n\n const navigateInto = (path: string): void => {\n setCur(path)\n loadDir(scopeRef.current, path)\n }\n\n const toggleExpand = (path: string): void => {\n setExpanded((prev) => {\n const next = new Set(prev)\n if (next.has(path)) next.delete(path)\n else next.add(path)\n return next\n })\n }\n\n const isSelected = (p: string): boolean => selectedPath !== '' && selectedPath !== '.' && selectedPath === p\n\n return createElement('div', { className: 'sts-dirtree' },\n // Navigation bar: current path + up button\n createElement('div', { className: 'sts-dirtree-nav' },\n createElement('button', {\n className: 'sts-dirtree-up',\n onClick: goUp,\n disabled: parentOf(cur) === null,\n title: '\\u4e0a\\u7ea7\\u76ee\\u5f55',\n }, '\\u2191'),\n createElement('button', {\n className: 'sts-dirtree-home',\n onClick: () => loadDir(scopeRef.current, cur), // refresh current dir\n title: '\\u5237\\u65b0',\n }, '\\u21bb'),\n createElement('span', { className: 'sts-dirtree-cur', title: cur }, cur || '(.)'),\n ),\n // Current directory selection row + subdir tree\n createElement('div', {\n className: `sts-tree-row${isSelected(cur) ? ' selected' : ''}`,\n style: { paddingLeft: '8px' },\n onClick: () => onPick(cur),\n },\n createElement('span', { className: 'sts-tree-icon' },\n createElement(FolderIcon, { size: 14 }),\n ),\n createElement('span', { className: 'sts-tree-label' }, cur || '(.)'),\n createElement('span', { className: 'sts-tree-select-hint' }, '\\u25cf \\u9009\\u62e9\\u6b64\\u76ee\\u5f55'),\n ),\n loading\n ? createElement('div', { className: 'sts-tree-loading', style: { paddingLeft: '16px' } }, '\\u52a0\\u8f7d\\u4e2d...')\n : error !== null\n ? createElement('div', { className: 'sts-tree-error', style: { paddingLeft: '16px' } }, error)\n : entries !== null && entries.length > 0\n ? createElement('div', { className: 'sts-dirtree-sub' },\n ...entries.map((entry) => createElement(DirRow, {\n key: entry.path,\n scope,\n entry,\n depth: 1,\n selectedPath,\n expanded,\n onToggleExpand: toggleExpand,\n onPick,\n onNavigate: navigateInto,\n })),\n )\n : createElement('div', { className: 'sts-tree-empty', style: { paddingLeft: '16px' } }, '\\u65e0\\u5b50\\u76ee\\u5f55'),\n )\n}\n\n/** One directory row with recursive expansion + click into. */\nfunction DirRow(props: {\n scope: SessionScope\n entry: FsEntry\n depth: number\n selectedPath: string\n expanded: Set<string>\n onToggleExpand: (p: string) => void\n onPick: (p: string) => void\n onNavigate: (p: string) => void\n}): ReactNode {\n const { scope, entry, depth, selectedPath, expanded, onToggleExpand, onPick, onNavigate } = props\n const [children, setChildren] = useState<FsEntry[] | null>(null)\n const [loading, setLoading] = useState(false)\n const isExpanded = expanded.has(entry.path)\n const isSelected = selectedPath === entry.path\n\n const handleClick = (): void => {\n // Clicking a row selects it AND expands to reveal children (unless already\n // expanded, in which case a second click collapses).\n onPick(entry.path)\n if (isExpanded) {\n onToggleExpand(entry.path)\n return\n }\n onToggleExpand(entry.path)\n if (children === null) {\n setLoading(true)\n fsTree(scope, entry.path)\n .then((dirs) => { setChildren(dirs); setLoading(false) })\n .catch(() => { setChildren([]); setLoading(false) })\n }\n }\n\n return createElement('div', null,\n createElement('div', {\n className: `sts-tree-row${isSelected ? ' selected' : ''}`,\n style: { paddingLeft: `${depth * 16 + 8}px` },\n onClick: handleClick,\n },\n createElement('span', { className: `sts-tree-chevron${isExpanded ? ' expanded' : ''}` },\n createElement(isExpanded ? ChevronDownIcon : ChevronRightIcon, { size: 12 }),\n ),\n createElement('span', { className: 'sts-tree-icon' },\n createElement(FolderIcon, { size: 14 }),\n ),\n createElement('span', { className: 'sts-tree-label' }, entry.name),\n ),\n isExpanded\n ? loading\n ? createElement('div', { className: 'sts-tree-loading', style: { paddingLeft: `${(depth + 1) * 16 + 8}px` } }, '\\u52a0\\u8f7d\\u4e2d...')\n : children !== null && children.length > 0\n ? createElement('div', null,\n ...children.map((child) =>\n createElement(DirRow, {\n key: child.path,\n scope,\n entry: child,\n depth: depth + 1,\n selectedPath,\n expanded,\n onToggleExpand: onToggleExpand,\n onPick,\n onNavigate: props.onNavigate,\n }),\n ),\n )\n : null\n : null,\n )\n}\n\n/** Props for the browse button + modal chooser. */\nexport interface DirChooserProps {\n scope: SessionScope\n value: string\n onChange: (v: string) => void\n /** Button/input presentation. */\n compact?: boolean\n /** Placeholder for the value input. */\n placeholder?: string\n}\n\n/** A read-only path input + browse button that opens a modal folder tree. */\nexport function DirChooser(props: DirChooserProps): ReactNode {\n const { scope, value, onChange, compact, placeholder } = props\n const [open, setOpen] = useState(false)\n\n return createElement('div', { className: 'sts-settings-path-row' },\n createElement('span', { className: 'sts-settings-path-icon' },\n createElement(FolderIcon, { size: 14 }),\n ),\n createElement('input', {\n className: compact ? 'sts-modal-input mono' : 'sts-settings-input',\n value: value,\n readOnly: true,\n placeholder: placeholder ?? '',\n title: value,\n onClick: () => setOpen(true),\n }),\n createElement('button', {\n className: 'sts-settings-browse',\n onClick: () => setOpen(true),\n title: '\\u9009\\u62e9\\u6587\\u4ef6\\u5939',\n }, '\\u6d4f\\u89c8'),\n open\n ? createElement(DirChooserModal, {\n scope,\n value,\n onConfirm: (p) => { onChange(p); setOpen(false) },\n onCancel: () => setOpen(false),\n })\n : null,\n )\n}\n\n/** Modal popup hosting the folder tree with confirm/cancel. */\nfunction DirChooserModal(props: {\n scope: SessionScope\n value: string\n onConfirm: (path: string) => void\n onCancel: () => void\n}): ReactNode {\n const { scope, value, onConfirm, onCancel } = props\n const [sel, setSel] = useState(value)\n\n return createElement('div', {\n className: 'sts-modal-overlay',\n onClick: (e: MouseEvent) => { if (e.target === e.currentTarget) onCancel() },\n },\n createElement('div', { className: 'sts-dir-modal' },\n createElement('div', { className: 'sts-modal-header' },\n '\\u9009\\u62e9\\u6587\\u4ef6\\u5939',\n createElement('button', { className: 'sts-icon-btn', onClick: onCancel },\n createElement(CloseIcon, { size: 16 }),\n ),\n ),\n createElement('div', { className: 'sts-dir-modal-tree' },\n createElement(DirTree, { scope, selectedPath: sel, onPick: setSel }),\n ),\n createElement('div', { className: 'sts-modal-footer' },\n createElement('button', { className: 'sts-modal-btn', onClick: onCancel }, '\\u53d6\\u6d88'),\n createElement('button', {\n className: 'sts-modal-btn primary',\n onClick: () => onConfirm(sel),\n style: {\n background: '#1a1a1e',\n border: '1px solid #3a3a3e',\n color: '#fff',\n borderRadius: '6px',\n padding: '8px 18px',\n cursor: 'pointer',\n fontSize: '13px',\n },\n },\n createElement(CheckIcon, { size: 14 }), ' \\u786e\\u5b9a'),\n ),\n ),\n )\n}\n","/**\n * Path picker: a read-only input + browse button that opens a folder tree\n * modal (DirChooser). Shared by the settings panel and the config editor.\n * @module dsh-better-sidebar-starter/client/PathPicker\n */\n\nimport { createElement, type ReactNode } from 'react'\nimport type { SessionScope } from 'dsh-better-sidebar/client/service'\nimport { DirChooser } from './DirChooser.tsx'\n\nexport type PathKind = 'java' | 'node' | 'python' | 'mvn'\n\n/** Props for the path picker. */\nexport interface PathPickerProps {\n scope: SessionScope\n kind: PathKind\n value: string\n onChange: (v: string) => void\n /** Compact mode (no label/desc wrapper), used inside the modal. */\n compact?: boolean\n desc?: string\n}\n\n/** A folder-browse input with a hosted modal tree chooser. */\nexport function PathPicker(props: PathPickerProps): ReactNode {\n const { scope, value, onChange, compact, desc } = props\n return createElement(DirChooser, {\n scope,\n value,\n onChange,\n compact,\n placeholder: desc,\n })\n}\n","/**\n * Config editor modal dialog: create or edit a run configuration.\n * Lets the user set name, type, command, cwd (tree picker), jvmArgs, args,\n * and environment variables.\n * @module dsh-better-sidebar-starter/client/ConfigEditorModal\n */\n\nimport { createElement, useState, useEffect, type ReactNode } from 'react'\nimport type { SessionScope } from 'dsh-better-sidebar/client/service'\nimport { CloseIcon, PlusIcon, TrashIcon, ChevronRightIcon, FolderIcon, ClipboardIcon } from './icons.tsx'\nimport { PathPicker } from './PathPicker.tsx'\n\n/** One directory entry from the sidebar fs.tree API. */\ninterface FsEntry {\n name: string\n path: string\n isDir: boolean\n hidden: boolean\n isSymlink: boolean\n broken: boolean\n}\n\n/** Call the sidebar fs.tree API directly via fetch. Returns directories only.\n * If path is omitted, the server uses the session cwd (root listing).\n * The sidebar API wraps responses in { ok: true, value: ... } envelope. */\nasync function fsTree(scope: SessionScope, path?: string): Promise<FsEntry[]> {\n const payload: Record<string, string> = { sessionId: scope.sessionId }\n if (scope.cwd !== undefined && scope.cwd !== '') payload.cwd = scope.cwd\n if (path !== undefined) payload.path = path\n const resp = await fetch('/sidebar/api/fs.tree', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!resp.ok) throw new Error(`fs.tree ${path ?? '(root)'}: ${resp.status}`)\n const parsed = await resp.json() as { ok?: boolean; value?: { entries: FsEntry[] }; error?: { message: string } }\n if (!parsed.ok || parsed.value === undefined) {\n throw new Error(`fs.tree ${path ?? '(root)'}: ${parsed.error?.message ?? 'unknown error'}`)\n }\n return parsed.value.entries.filter((e) => e.isDir && !e.hidden)\n}\n\n/** Recursive tree node: loads its own children on expand. */\nfunction TreeNode(props: {\n scope: SessionScope\n entry: FsEntry\n depth: number\n selectedPath: string\n onSelect: (path: string) => void\n}): ReactNode {\n const { scope, entry, depth, selectedPath, onSelect } = props\n const [expanded, setExpanded] = useState(false)\n const [children, setChildren] = useState<FsEntry[] | null>(null)\n const [loading, setLoading] = useState(false)\n\n const handleToggle = (): void => {\n if (!expanded && children === null) {\n setLoading(true)\n fsTree(scope, entry.path)\n .then((dirs) => { setChildren(dirs); setLoading(false) })\n .catch(() => { setChildren([]); setLoading(false) })\n }\n setExpanded(!expanded)\n }\n\n const isSelected = selectedPath === entry.path\n\n return createElement('div', null,\n createElement('div', {\n className: `sts-tree-row${isSelected ? ' selected' : ''}`,\n style: { paddingLeft: `${depth * 16 + 8}px` },\n onClick: () => { onSelect(entry.path); handleToggle() },\n },\n createElement('span', { className: `sts-tree-chevron${expanded ? ' expanded' : ''}` },\n createElement(ChevronRightIcon, { size: 12 }),\n ),\n createElement('span', { className: 'sts-tree-icon' },\n createElement(FolderIcon, { size: 14 }),\n ),\n createElement('span', { className: 'sts-tree-label' }, entry.name),\n ),\n expanded\n ? loading\n ? createElement('div', { className: 'sts-tree-loading', style: { paddingLeft: `${(depth + 1) * 16 + 8}px` } }, '\\u52a0\\u8f7d\\u4e2d...')\n : children !== null && children.length > 0\n ? createElement('div', null,\n ...children.map((child) =>\n createElement(TreeNode, {\n key: child.path,\n scope,\n entry: child,\n depth: depth + 1,\n selectedPath,\n onSelect,\n }),\n ),\n )\n : createElement('div', { className: 'sts-tree-empty', style: { paddingLeft: `${(depth + 1) * 16 + 8}px` } }, '\\u65e0\\u5b50\\u76ee\\u5f55')\n : null,\n )\n}\n\n/** Directory tree picker: shows a collapsible tree rooted at session cwd. */\nfunction DirTreePicker(props: { scope: SessionScope; value: string; onSelect: (path: string) => void }): ReactNode {\n const { scope, value, onSelect } = props\n const [expanded, setExpanded] = useState(false)\n const [children, setChildren] = useState<FsEntry[] | null>(null)\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const handleToggle = (): void => {\n if (!expanded && children === null) {\n setLoading(true)\n setError(null)\n fsTree(scope)\n .then((dirs) => { setChildren(dirs); setLoading(false) })\n .catch((err) => { setError(err instanceof Error ? err.message : String(err)); setLoading(false) })\n }\n setExpanded(!expanded)\n }\n\n const rootSelected = value === '.' || value === '' || value === './'\n\n return createElement('div', { className: 'sts-tree-picker' },\n createElement('div', {\n className: `sts-tree-row${rootSelected ? ' selected' : ''}`,\n style: { paddingLeft: '8px' },\n onClick: () => { onSelect('.'); handleToggle() },\n },\n createElement('span', { className: `sts-tree-chevron${expanded ? ' expanded' : ''}` },\n createElement(ChevronRightIcon, { size: 12 }),\n ),\n createElement('span', { className: 'sts-tree-icon' },\n createElement(FolderIcon, { size: 14 }),\n ),\n createElement('span', { className: 'sts-tree-label' }, '(\\u5de5\\u4f5c\\u7a7a\\u95f4\\u6839\\u76ee\\u5f55)'),\n ),\n expanded\n ? loading\n ? createElement('div', { className: 'sts-tree-loading', style: { paddingLeft: '24px' } }, '\\u52a0\\u8f7d\\u4e2d...')\n : error !== null\n ? createElement('div', { className: 'sts-tree-error', style: { paddingLeft: '24px' } }, error)\n : children !== null && children.length > 0\n ? createElement('div', null,\n ...children.map((child) =>\n createElement(TreeNode, {\n key: child.path,\n scope,\n entry: child,\n depth: 1,\n selectedPath: value,\n onSelect,\n }),\n ),\n )\n : createElement('div', { className: 'sts-tree-empty', style: { paddingLeft: '24px' } }, '\\u65e0\\u5b50\\u76ee\\u5f55')\n : null,\n )\n}\n\n/** One env var row. */\ninterface EnvRow {\n key: string\n value: string\n}\n\n/** Props for the modal. */\nexport interface ConfigEditorModalProps {\n /** Existing config to edit, or null for a new config. */\n config: {\n id?: string\n name: string\n type: string\n command: string\n cwd: string\n env: Record<string, string>\n jvmArgs?: string\n args?: string\n runtime?: { java?: string; node?: string; python?: string; mvn?: string }\n } | null\n /** Session scope for fsTree calls. */\n scope: SessionScope\n /** Called when the user saves. */\n onSave: (config: {\n id?: string\n name: string\n type: string\n command: string\n cwd: string\n env: Record<string, string>\n jvmArgs: string\n args: string\n runtime: Record<string, string>\n }) => void\n /** Called when the user cancels. */\n onCancel: () => void\n}\n\n/** Type \\u2192 default command mapping. */\nconst TYPE_DEFAULTS: Record<string, string> = {\n npm: 'npm run dev',\n springboot: 'mvn spring-boot:run',\n python: 'python main.py',\n custom: '',\n}\n\nconst TYPE_LABELS: Record<string, string> = {\n npm: 'npm',\n springboot: 'Spring Boot',\n python: 'Python',\n custom: 'Custom',\n}\n\n/** The modal dialog component. */\nexport function ConfigEditorModal({ config, scope, onSave, onCancel }: ConfigEditorModalProps): ReactNode {\n const [name, setName] = useState(config?.name ?? '')\n const [type, setType] = useState(config?.type ?? 'custom')\n const [command, setCommand] = useState(config?.command ?? '')\n const [cwd, setCwd] = useState(config?.cwd ?? '.')\n const [jvmArgs, setJvmArgs] = useState(config?.jvmArgs ?? '')\n const [args, setArgs] = useState(config?.args ?? '')\n const [envRows, setEnvRows] = useState<EnvRow[]>(\n config\n ? Object.entries(config.env).map(([key, value]) => ({ key, value }))\n : [],\n )\n const [showTree, setShowTree] = useState(false)\n const [showRuntime, setShowRuntime] = useState(false)\n const [rtJava, setRtJava] = useState(config?.runtime?.java ?? '')\n const [rtNode, setRtNode] = useState(config?.runtime?.node ?? '')\n const [rtPython, setRtPython] = useState(config?.runtime?.python ?? '')\n const [rtMvn, setRtMvn] = useState(config?.runtime?.mvn ?? '')\n\n // When type changes and command is empty (new config), auto-fill template.\n useEffect(() => {\n if (command === '' && TYPE_DEFAULTS[type] !== '') {\n setCommand(TYPE_DEFAULTS[type])\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [type])\n\n const handleSave = (): void => {\n const env: Record<string, string> = {}\n for (const row of envRows) {\n if (row.key.trim() !== '') {\n env[row.key.trim()] = row.value\n }\n }\n const runtime: Record<string, string> = {}\n if (rtJava.trim() !== '') runtime.java = rtJava.trim()\n if (rtNode.trim() !== '') runtime.node = rtNode.trim()\n if (rtPython.trim() !== '') runtime.python = rtPython.trim()\n if (rtMvn.trim() !== '') runtime.mvn = rtMvn.trim()\n onSave({\n id: config?.id,\n name: name.trim(),\n type,\n command: command.trim(),\n cwd: cwd.trim() === '' ? '.' : cwd.trim(),\n env,\n jvmArgs: jvmArgs.trim(),\n args: args.trim(),\n runtime,\n })\n }\n\n const updateEnvRow = (idx: number, field: 'key' | 'value', val: string): void => {\n setEnvRows((prev) => prev.map((row, i) => i === idx ? { ...row, [field]: val } : row))\n }\n const addEnvRow = (): void => setEnvRows((prev) => [...prev, { key: '', value: '' }])\n const removeEnvRow = (idx: number): void => setEnvRows((prev) => prev.filter((_, i) => i !== idx))\n\n // Bulk paste state.\n const [showEnvPaste, setShowEnvPaste] = useState(false)\n const [envPasteText, setEnvPasteText] = useState('')\n\n /** Parse \"K=V;K=V,...\" (or newline/comma separated) into env rows, merging\n * into the existing rows (later values override earlier for duplicate keys). */\n const applyEnvPaste = (): void => {\n const existing = new Map(envRows.filter((r) => r.key.trim() !== '').map((r) => [r.key.trim(), r.value]))\n const text = envPasteText.trim()\n if (text === '') return\n // Split on semicolons, commas, or newlines.\n const pairs = text.split(/[;,\\n]+/)\n for (const pair of pairs) {\n const p = pair.trim()\n if (p === '') continue\n const eq = p.indexOf('=')\n if (eq <= 0) continue // ignore malformed entries\n const key = p.substring(0, eq).trim()\n const value = p.substring(eq + 1).trim()\n if (key !== '') existing.set(key, value)\n }\n setEnvRows(Array.from(existing, ([key, value]) => ({ key, value })))\n setEnvPasteText('')\n setShowEnvPaste(false)\n }\n\n const isSpringboot = type === 'springboot'\n\n return createElement('div', {\n className: 'sts-modal-overlay',\n onClick: (e: MouseEvent) => { if (e.target === e.currentTarget) onCancel() },\n },\n createElement('div', { className: 'sts-modal' },\n // Header\n createElement('div', { className: 'sts-modal-header' },\n config?.id ? '\\u7f16\\u8f91\\u8fd0\\u884c\\u914d\\u7f6e' : '\\u65b0\\u5efa\\u8fd0\\u884c\\u914d\\u7f6e',\n createElement('button', {\n className: 'sts-icon-btn',\n onClick: onCancel,\n }, createElement(CloseIcon, { size: 16 })),\n ),\n // Body\n createElement('div', { className: 'sts-modal-body' },\n // Name\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, '\\u540d\\u79f0'),\n createElement('input', {\n className: 'sts-modal-input',\n value: name,\n onChange: (e: { target: { value: string } }) => setName(e.target.value),\n placeholder: '\\u914d\\u7f6e\\u540d\\u79f0',\n }),\n ),\n // Type\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, '\\u7c7b\\u578b'),\n createElement('select', {\n className: 'sts-modal-select',\n value: type,\n onChange: (e: { target: { value: string } }) => setType(e.target.value),\n },\n ...Object.entries(TYPE_LABELS).map(([val, label]) =>\n createElement('option', { key: val, value: val }, label),\n ),\n ),\n ),\n // Command\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, '\\u547d\\u4ee4'),\n createElement('input', {\n className: 'sts-modal-input mono',\n value: command,\n onChange: (e: { target: { value: string } }) => setCommand(e.target.value),\n placeholder: '\\u8981\\u6267\\u884c\\u7684\\u547d\\u4ee4',\n }),\n ),\n // Working directory (tree picker)\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, '\\u5de5\\u4f5c\\u76ee\\u5f55'),\n createElement('div', { className: 'sts-cwd-display', onClick: () => setShowTree(!showTree) },\n createElement('span', { className: 'sts-cwd-icon' }, createElement(FolderIcon, { size: 14 })),\n createElement('span', { className: 'sts-cwd-value' }, cwd),\n ),\n showTree\n ? createElement('div', { className: 'sts-tree-picker-container' },\n createElement(DirTreePicker, { scope, value: cwd, onSelect: (p) => setCwd(p) }),\n )\n : null,\n ),\n // JVM args (springboot only)\n isSpringboot\n ? createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, '\\u865a\\u62df\\u673a\\u53c2\\u6570 (JVM Args)'),\n createElement('input', {\n className: 'sts-modal-input mono',\n value: jvmArgs,\n onChange: (e: { target: { value: string } }) => setJvmArgs(e.target.value),\n placeholder: '\\u4f8b: -Xmx512m -Dspring.profiles.active=dev',\n }),\n )\n : null,\n // Program args (all types)\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, '\\u7a0b\\u5e8f\\u5b9e\\u53c2'),\n createElement('input', {\n className: 'sts-modal-input mono',\n value: args,\n onChange: (e: { target: { value: string } }) => setArgs(e.target.value),\n placeholder: '\\u4f8b: --server.port=8080 --debug',\n }),\n ),\n // Runtime environment (collapsible advanced section)\n createElement('div', { className: 'sts-modal-field' },\n createElement('div', {\n className: `sts-collapse-header${showRuntime ? ' open' : ''}`,\n onClick: () => setShowRuntime(!showRuntime),\n },\n createElement('span', { className: `sts-collapse-chevron${showRuntime ? ' expanded' : ''}` },\n createElement(ChevronRightIcon, { size: 12 }),\n ),\n createElement('span', null, '\\u8fd0\\u884c\\u65f6\\u73af\\u5883 (\\u9ad8\\u7ea7)'),\n createElement('span', { className: 'sts-collapse-hint' }, '\\u9ed8\\u8ba4\\u7528\\u5168\\u5c40\\u914d\\u7f6e'),\n ),\n showRuntime\n ? createElement('div', { className: 'sts-collapse-body' },\n createElement('div', { className: 'sts-modal-label' }, '\\u7559\\u7a7a\\u5219\\u7528\\u5168\\u5c40/\\u7cfb\\u7edf\\u914d\\u7f6e'),\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, 'Java \\u8def\\u5f84'),\n createElement(PathPicker, {\n scope,\n kind: 'java',\n value: rtJava,\n onChange: setRtJava,\n compact: true,\n desc: '\\u9009\\u62e9 JDK \\u76ee\\u5f55',\n }),\n ),\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, 'Node.js \\u8def\\u5f84'),\n createElement(PathPicker, {\n scope,\n kind: 'node',\n value: rtNode,\n onChange: setRtNode,\n compact: true,\n desc: '\\u9009\\u62e9 Node \\u76ee\\u5f55',\n }),\n ),\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, 'Python \\u8def\\u5f84'),\n createElement(PathPicker, {\n scope,\n kind: 'python',\n value: rtPython,\n onChange: setRtPython,\n compact: true,\n desc: '\\u9009\\u62e9 Python \\u76ee\\u5f55',\n }),\n ),\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, 'Maven \\u8def\\u5f84 (MVN)'),\n createElement(PathPicker, {\n scope,\n kind: 'mvn',\n value: rtMvn,\n onChange: setRtMvn,\n compact: true,\n desc: '\\u9009\\u62e9 Maven \\u76ee\\u5f55',\n }),\n ),\n )\n : null,\n ),\n // Environment variables\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, '\\u73af\\u5883\\u53d8\\u91cf'),\n ...envRows.map((row, idx) =>\n createElement('div', { key: idx, className: 'sts-env-row' },\n createElement('input', {\n className: 'sts-modal-input sts-env-key',\n value: row.key,\n onChange: (e: { target: { value: string } }) => updateEnvRow(idx, 'key', e.target.value),\n placeholder: '\\u53d8\\u91cf\\u540d (\\u5982 JAVA_HOME)',\n }),\n createElement('span', { className: 'sts-env-sep' }, '='),\n createElement('input', {\n className: 'sts-modal-input sts-env-val',\n value: row.value,\n onChange: (e: { target: { value: string } }) => updateEnvRow(idx, 'value', e.target.value),\n placeholder: '\\u53d8\\u91cf\\u503c',\n }),\n createElement('button', {\n className: 'sts-icon-btn trash',\n onClick: () => removeEnvRow(idx),\n }, createElement(TrashIcon, { size: 14 })),\n ),\n ),\n createElement('div', { className: 'sts-env-actions' },\n createElement('button', {\n className: 'sts-env-add', onClick: addEnvRow,\n },\n createElement(PlusIcon, { size: 12 }), ' \\u6dfb\\u52a0\\u73af\\u5883\\u53d8\\u91cf',\n ),\n createElement('button', {\n className: 'sts-env-add', onClick: () => setShowEnvPaste(!showEnvPaste),\n },\n createElement(ClipboardIcon, { size: 12 }), showEnvPaste ? ' \\u53d6\\u6d88' : ' \\u6279\\u91cf\\u7c98\\u8d34',\n ),\n ),\n showEnvPaste\n ? createElement('div', { className: 'sts-env-paste' },\n createElement('textarea', {\n className: 'sts-env-paste-input',\n value: envPasteText,\n onChange: (e: { target: { value: string } }) => setEnvPasteText(e.target.value),\n placeholder: '\\u7c98\\u8d34 KEY=VALUE;KEY=VALUE;\\u6216\\u4ee5\\u6362\\u884c/\\u9017\\u53f7\\u5206\\u9694\\uff0c\\u5982\\uff1a\\nDB_DATABASE=cassiopeia_ap;DB_HOST=10.18.18.18;DB_PORT=3306;MQ_HOST=10.18.18.18;MQ_PASSWORD=admin123;MQ_PORT=5672;MQ_USERNAME=admin;MQ_VIRTUAL_HOST=/;REDIS_HOST=10.18.18.17;REDIS_PASSWORD=Qaz!wsx2;REDIS_PORT=6379',\n rows: 4,\n }),\n createElement('div', { className: 'sts-env-paste-footer' },\n createElement('button', {\n className: 'sts-env-paste-apply', onClick: applyEnvPaste,\n },\n '\\u786e\\u5b9a\\u5e76\\u751f\\u6210\\u73af\\u5883\\u53d8\\u91cf'),\n createElement('span', { className: 'sts-env-paste-hint' }, '\\u91cd\\u590d\\u5c06\\u8986\\u76d6'),\n ),\n )\n : null,\n ),\n ),\n // Footer\n createElement('div', { className: 'sts-modal-footer' },\n createElement('button', { className: 'sts-modal-btn', onClick: onCancel }, '\\u53d6\\u6d88'),\n createElement('button', {\n className: 'sts-modal-btn primary',\n onClick: handleSave,\n disabled: name.trim() === '',\n style: {\n background: '#1a1a1e',\n border: '1px solid #3a3a3e',\n color: '#fff',\n borderRadius: '6px',\n padding: '8px 18px',\n cursor: name.trim() === '' ? 'not-allowed' : 'pointer',\n fontSize: '13px',\n opacity: name.trim() === '' ? 0.5 : 1,\n },\n }, '\\u4fdd\\u5b58'),\n ),\n ),\n )\n}\n","/**\r\n * CSS styles for the starter plugin panel. Uses .sts- prefixed class names\r\n * (scoped enough to avoid collisions with the host shell).\r\n * Uses --dsw-alias-* CSS variables from the DSH design system to follow the\r\n * active theme automatically (dark/light).\r\n * @module dsh-better-sidebar-starter/client/styles\r\n */\r\n\r\nexport const STYLES_CSS = `\r\n/* ===== Root container ===== */\r\n.sts-root {\r\n display: flex;\r\n flex-direction: column;\r\n height: 100%;\r\n min-height: 0;\r\n font-size: 13px;\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n background: var(--dsw-alias-bg-base, #1a1a1e);\r\n}\r\n\r\n/* ===== Toolbar ===== */\r\n.sts-toolbar {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 0 12px;\r\n height: 40px;\r\n border-bottom: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n flex-shrink: 0;\r\n}\r\n.sts-toolbar-title {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n font: var(--dsw-font-m-14, 500 14px/20px sans-serif);\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-toolbar-btn {\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n padding: 4px 10px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 6px;\r\n background: transparent;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n cursor: pointer;\r\n font-size: 12px;\r\n transition: background 0.12s, border-color 0.12s;\r\n}\r\n.sts-toolbar-btn:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.06));\r\n border-color: var(--dsw-alias-border-l3, #3a3a3e);\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n\r\n/* ===== Split pane (horizontal: default for right panel) ===== */\r\n.sts-split {\r\n display: flex;\r\n flex: 1;\r\n min-height: 0;\r\n}\r\n.sts-split-first {\r\n overflow-y: auto;\r\n overflow-x: hidden;\r\n border-right: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n flex-shrink: 0;\r\n}\r\n.sts-split-gutter {\r\n width: 1px;\r\n background: var(--dsw-alias-border-l2, #2a2a2e);\r\n cursor: col-resize;\r\n flex-shrink: 0;\r\n transition: width 0.12s, background 0.12s;\r\n}\r\n.sts-split-gutter:hover,\r\n.sts-split-gutter.dragging {\r\n width: 3px;\r\n background: var(--dsw-alias-interactive-bg-hover-accent, #007acc);\r\n}\r\n.sts-split-second {\r\n flex: 1;\r\n display: flex;\r\n flex-direction: column;\r\n min-width: 0;\r\n min-height: 0;\r\n}\r\n\r\n/* ===== Split pane (vertical: for bottom panel) ===== */\r\n.sts-split-vertical {\r\n flex-direction: column;\r\n}\r\n.sts-split-first-v {\r\n border-right: none;\r\n border-bottom: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n width: 100% !important;\r\n overflow-y: auto;\r\n overflow-x: hidden;\r\n flex-shrink: 0;\r\n}\r\n.sts-split-gutter-v {\r\n width: auto;\r\n height: 1px;\r\n cursor: row-resize;\r\n transition: height 0.12s, background 0.12s;\r\n}\r\n.sts-split-gutter-v:hover,\r\n.sts-split-gutter-v.dragging {\r\n width: auto;\r\n height: 3px;\r\n background: var(--dsw-alias-interactive-bg-hover-accent, #007acc);\r\n}\r\n.sts-split-second-v {\r\n flex: 1;\r\n display: flex;\r\n flex-direction: column;\r\n min-width: 0;\r\n min-height: 0;\r\n}\r\n\r\n/* ===== Config tree ===== */\r\n.sts-tree {\r\n padding: 4px 4px 8px;\r\n user-select: none;\r\n}\r\n.sts-group-header {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n padding: 6px 8px;\r\n font: var(--dsw-font-xxxs-11, 500 11px/16px sans-serif);\r\n color: var(--dsw-alias-label-tertiary, #888);\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n cursor: pointer;\r\n border-radius: 6px;\r\n}\r\n.sts-group-header:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.04));\r\n}\r\n.sts-group-count {\r\n color: rgba(255,255,255,0.4);\r\n font-weight: 400;\r\n font-variant-numeric: tabular-nums;\r\n}\r\n.sts-group-children {\r\n overflow: hidden;\r\n}\r\n.sts-group-children.collapsed {\r\n display: none;\r\n}\r\n\r\n/* ===== Config row ===== */\r\n.sts-config-row {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n padding: 5px 8px 5px 24px;\r\n cursor: pointer;\r\n font-size: 13px;\r\n border-radius: 6px;\r\n margin: 1px 2px;\r\n transition: background 0.1s;\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-config-row:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.04));\r\n}\r\n.sts-config-row.selected {\r\n background: var(--dsw-alias-interactive-bg-active, rgba(0,122,204,0.12));\r\n}\r\n.sts-config-name {\r\n flex: 1;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n font-weight: 400;\r\n}\r\n.sts-config-cmd {\r\n font-size: 10px;\r\n color: rgba(255,255,255,0.4);\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, \"DejaVu Sans Mono\", \"Liberation Mono\", monospace;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n max-width: 100px;\r\n}\r\n.sts-config-actions {\r\n display: flex;\r\n align-items: center;\r\n gap: 1px;\r\n flex-shrink: 0;\r\n}\r\n.sts-icon-btn {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 22px;\r\n height: 22px;\r\n border: none;\r\n border-radius: 4px;\r\n background: transparent;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n cursor: pointer;\r\n opacity: 0;\r\n transition: opacity 0.1s, background 0.1s;\r\n}\r\n.sts-config-row:hover .sts-icon-btn {\r\n opacity: 0.7;\r\n}\r\n.sts-icon-btn:hover {\r\n opacity: 1 !important;\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.08));\r\n}\r\n.sts-icon-btn.play { color: #6ab04c; }\r\n.sts-icon-btn.stop { color: #eb5757; }\r\n.sts-icon-btn.gear { color: var(--dsw-alias-label-secondary, #b0b0b0); }\r\n.sts-icon-btn.trash { color: #eb5757; }\r\n.sts-icon-btn.dup { color: var(--dsw-alias-label-secondary, #b0b0b0); }\r\n.sts-icon-btn.send { color: #4a90d9; }\r\n/* Toolbar buttons (log view) are always visible and fit icon + text. */\r\n.sts-log-toolbar .sts-icon-btn {\r\n width: auto;\r\n height: 26px;\r\n padding: 0 8px;\r\n gap: 3px;\r\n font-size: 11px;\r\n opacity: 1;\r\n}\r\n\r\n/* ===== Instance row ===== */\r\n.sts-instance-row {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n padding: 3px 8px 3px 40px;\r\n font-size: 11px;\r\n cursor: pointer;\r\n border-radius: 6px;\r\n margin: 1px 2px;\r\n color: var(--dsw-alias-label-tertiary, #888);\r\n transition: background 0.1s;\r\n}\r\n.sts-instance-row:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.04));\r\n}\r\n.sts-instance-row.selected {\r\n background: var(--dsw-alias-interactive-bg-active, rgba(0,122,204,0.12));\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-instance-pid {\r\n color: rgba(255,255,255,0.4);\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, \"DejaVu Sans Mono\", \"Liberation Mono\", monospace;\r\n font-variant-numeric: tabular-nums;\r\n}\r\n\r\n/* ===== Empty states ===== */\r\n.sts-empty {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: center;\r\n height: 100%;\r\n color: rgba(255,255,255,0.4);\r\n font-size: 12px;\r\n padding: 24px;\r\n text-align: center;\r\n gap: 8px;\r\n line-height: 20px;\r\n}\r\n\r\n/* ===== Log view ===== */\r\n.sts-log-toolbar {\r\n display: flex;\r\n align-items: center;\r\n gap: 2px;\r\n padding: 0 8px;\r\n height: 36px;\r\n border-bottom: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n flex-shrink: 0;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n font-size: 12px;\r\n}\r\n.sts-log-toolbar-spacer {\r\n flex: 1;\r\n}\r\n.sts-log-toolbar-label {\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n font-size: 11px;\r\n cursor: pointer;\r\n color: var(--dsw-alias-label-tertiary, #888);\r\n padding: 2px 6px;\r\n border-radius: 4px;\r\n}\r\n.sts-log-toolbar-label:hover {\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.04));\r\n}\r\n.sts-log-content {\r\n flex: 1;\r\n overflow-y: auto;\r\n overflow-x: hidden;\r\n padding: 8px 12px;\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, \"DejaVu Sans Mono\", \"Liberation Mono\", monospace;\r\n font-size: 12px;\r\n line-height: 1.6;\r\n white-space: pre-wrap;\r\n word-break: break-all;\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n background: var(--dsw-alias-bg-base, #1a1a1e);\r\n}\r\n.sts-log-line {\r\n min-height: 1em;\r\n}\r\n\r\n/* ===== Config detail ===== */\r\n.sts-detail {\r\n padding: 20px;\r\n overflow-y: auto;\r\n flex: 1;\r\n}\r\n.sts-detail-card {\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n background: var(--dsw-alias-bg-layer-2, #202024);\r\n border-radius: 12px;\r\n padding: 20px;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 16px;\r\n}\r\n.sts-detail-title {\r\n display: flex;\r\n align-items: center;\r\n gap: 10px;\r\n font-size: 16px;\r\n font-weight: 500;\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n padding-bottom: 12px;\r\n border-bottom: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n}\r\n.sts-detail-row {\r\n display: flex;\r\n gap: 12px;\r\n font-size: 13px;\r\n align-items: baseline;\r\n}\r\n.sts-detail-label {\r\n width: 72px;\r\n flex-shrink: 0;\r\n color: var(--dsw-alias-label-tertiary, #888);\r\n font-size: 11px;\r\n text-transform: uppercase;\r\n letter-spacing: 0.3px;\r\n}\r\n.sts-detail-value {\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, \"DejaVu Sans Mono\", \"Liberation Mono\", monospace;\r\n word-break: break-all;\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-detail-actions {\r\n display: flex;\r\n gap: 8px;\r\n justify-content: flex-end;\r\n margin-top: 12px;\r\n padding-top: 16px;\r\n border-top: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n}\r\n.sts-detail-btn {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n padding: 6px 14px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 6px;\r\n background: transparent;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n cursor: pointer;\r\n font-size: 12px;\r\n transition: background 0.12s, color 0.12s;\r\n}\r\n.sts-detail-btn:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.06));\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-detail-btn.danger {\r\n color: #eb5757;\r\n border-color: rgba(235,87,87,0.4);\r\n}\r\n.sts-detail-btn.danger:hover {\r\n background: rgba(235,87,87,0.1);\r\n}\r\n\r\n/* ===== Modal ===== */\r\n.sts-modal-overlay {\r\n position: fixed;\r\n top: 0; left: 0; right: 0; bottom: 0;\r\n background: rgba(0,0,0,0.45);\r\n backdrop-filter: blur(2px);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n z-index: 10000;\r\n}\r\n.sts-modal {\r\n background: var(--dsw-alias-bg-layer-3, #242428);\r\n border: 1px solid var(--dsw-alias-border-l3, #3a3a3e);\r\n border-radius: 12px;\r\n width: 480px;\r\n max-width: 90vw;\r\n max-height: 80vh;\r\n overflow-y: auto;\r\n display: flex;\r\n flex-direction: column;\r\n box-shadow: 0 8px 32px rgba(0,0,0,0.3);\r\n}\r\n.sts-modal-header {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 14px 20px;\r\n font-size: 15px;\r\n font-weight: 500;\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n border-bottom: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n}\r\n.sts-modal-body {\r\n padding: 20px;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 16px;\r\n}\r\n.sts-modal-field {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 6px;\r\n}\r\n.sts-modal-label {\r\n font-size: 11px;\r\n color: var(--dsw-alias-label-tertiary, #888);\r\n text-transform: uppercase;\r\n letter-spacing: 0.3px;\r\n}\r\n.sts-modal-input,\r\n.sts-modal-select {\r\n padding: 8px 10px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 6px;\r\n background: var(--dsw-alias-bg-layer-1, #1a1a1e);\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n font-size: 13px;\r\n font-family: inherit;\r\n outline: none;\r\n transition: border-color 0.12s, background 0.12s;\r\n}\r\n.sts-modal-input:focus,\r\n.sts-modal-select:focus {\r\n border-color: var(--dsw-alias-button-primary-fill, #007acc);\r\n background: var(--dsw-alias-bg-base, #1a1a1e);\r\n}\r\n.sts-modal-input.mono {\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, \"DejaVu Sans Mono\", \"Liberation Mono\", monospace;\r\n}\r\n.sts-env-row {\r\n display: flex;\r\n gap: 6px;\r\n align-items: center;\r\n}\r\n.sts-env-key {\r\n flex: 1;\r\n}\r\n.sts-env-val {\r\n flex: 2;\r\n}\r\n.sts-env-sep {\r\n color: var(--dsw-alias-label-dimmed, #555);\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, \"DejaVu Sans Mono\", \"Liberation Mono\", monospace;\r\n}\r\n.sts-env-add {\r\n font-size: 12px;\r\n padding: 6px 10px;\r\n border: 1px dashed var(--dsw-alias-border-l3, #3a3a3e);\r\n border-radius: 6px;\r\n background: transparent;\r\n color: var(--dsw-alias-label-tertiary, #888);\r\n cursor: pointer;\r\n align-self: flex-start;\r\n transition: color 0.12s, border-color 0.12s;\r\n}\r\n.sts-env-add:hover {\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n border-color: var(--dsw-alias-button-primary-fill, #007acc);\r\n}\r\n.sts-modal-footer {\r\n display: flex;\r\n justify-content: flex-end;\r\n gap: 8px;\r\n padding: 14px 20px;\r\n border-top: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n}\r\n.sts-modal-btn {\r\n padding: 8px 18px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 6px;\r\n background: transparent;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n cursor: pointer;\r\n font-size: 13px;\r\n transition: background 0.12s, color 0.12s;\r\n}\r\n.sts-modal-btn:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.06));\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n\r\n/* ===== Directory tree picker ===== */\r\n.sts-cwd-display {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n padding: 8px 10px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 6px;\r\n background: var(--dsw-alias-bg-layer-1, #1a1a1e);\r\n cursor: pointer;\r\n transition: border-color 0.12s;\r\n font-size: 13px;\r\n}\r\n.sts-cwd-display:hover {\r\n border-color: var(--dsw-alias-border-l3, #3a3a3e);\r\n}\r\n.sts-cwd-icon {\r\n display: flex;\r\n align-items: center;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n}\r\n.sts-cwd-value {\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, monospace;\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n.sts-tree-picker-container {\r\n margin-top: 4px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 6px;\r\n background: var(--dsw-alias-bg-layer-1, #1a1a1e);\r\n max-height: 240px;\r\n overflow-y: auto;\r\n padding: 4px;\r\n}\r\n.sts-tree-row {\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n padding: 4px 8px;\r\n cursor: pointer;\r\n border-radius: 4px;\r\n font-size: 13px;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n transition: background 0.08s;\r\n white-space: nowrap;\r\n}\r\n.sts-tree-row:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.04));\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-tree-row.selected {\r\n background: var(--dsw-alias-interactive-bg-active, rgba(0,122,204,0.12));\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-tree-chevron {\r\n display: flex;\r\n align-items: center;\r\n transition: transform 0.1s;\r\n flex-shrink: 0;\r\n}\r\n.sts-tree-chevron.expanded {\r\n transform: rotate(90deg);\r\n}\r\n.sts-tree-icon {\r\n display: flex;\r\n align-items: center;\r\n flex-shrink: 0;\r\n color: var(--dsw-alias-label-tertiary, #888);\r\n}\r\n.sts-tree-label {\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n}\r\n.sts-tree-loading,\r\n.sts-tree-error,\r\n.sts-tree-empty {\r\n padding: 8px 12px;\r\n font-size: 12px;\r\n color: rgba(255,255,255,0.4);\r\n}\r\n.sts-tree-error {\r\n color: #eb5757;\r\n}\r\n\r\n/* ===== Settings panel ===== */\r\n.sts-settings-panel {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 4px;\r\n padding: 8px;\r\n min-width: 320px;\r\n}\r\n.sts-settings-row {\r\n padding: 4px 0;\r\n}\r\n.sts-settings-switch-row {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n cursor: pointer;\r\n font-size: 13px;\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-settings-switch {\r\n width: 16px;\r\n height: 16px;\r\n cursor: pointer;\r\n}\r\n.sts-settings-sep {\r\n margin: 8px 0 4px;\r\n padding: 0 4px;\r\n font-size: 10px;\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n color: rgba(255,255,255,0.4);\r\n}\r\n.sts-settings-path {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 4px;\r\n padding: 4px 0;\r\n}\r\n.sts-settings-path-header {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n}\r\n.sts-settings-label {\r\n font-size: 12px;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n}\r\n.sts-settings-badge {\r\n display: flex;\r\n align-items: center;\r\n gap: 2px;\r\n font-size: 10px;\r\n padding: 1px 6px;\r\n border-radius: 10px;\r\n}\r\n.sts-settings-badge.ok {\r\n color: #4e9a06;\r\n background: rgba(78,154,6,0.12);\r\n}\r\n.sts-settings-badge.err {\r\n color: #eb5757;\r\n background: rgba(235,87,87,0.12);\r\n}\r\n.sts-settings-path-row {\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n}\r\n.sts-settings-path-icon {\r\n display: flex;\r\n align-items: center;\r\n color: var(--dsw-alias-label-tertiary, #888);\r\n flex-shrink: 0;\r\n}\r\n.sts-settings-input {\r\n flex: 1;\r\n padding: 6px 8px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 4px;\r\n background: var(--dsw-alias-bg-layer-1, #1a1a1e);\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n font-size: 12px;\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, monospace;\r\n outline: none;\r\n min-width: 0;\r\n}\r\n.sts-settings-input:focus {\r\n border-color: var(--dsw-alias-border-l3, #3a3a3e);\r\n}\r\n.sts-settings-input::placeholder {\r\n color: rgba(255,255,255,0.3);\r\n}\r\n.sts-settings-browse {\r\n padding: 6px 10px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 4px;\r\n background: transparent;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n cursor: pointer;\r\n font-size: 12px;\r\n flex-shrink: 0;\r\n transition: background 0.12s, color 0.12s;\r\n}\r\n.sts-settings-browse:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.06));\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-settings-msg {\r\n font-size: 10px;\r\n padding: 0 4px;\r\n color: rgba(255,255,255,0.4);\r\n}\r\n.sts-settings-msg.valid {\r\n color: #4e9a06;\r\n}\r\n.sts-settings-msg.invalid {\r\n color: #eb5757;\r\n}\r\n\r\n/* ===== Collapsible advanced section ===== */\r\n.sts-collapse-header {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n padding: 8px 10px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 6px;\r\n cursor: pointer;\r\n font-size: 13px;\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n background: var(--dsw-alias-bg-layer-1, #1a1a1e);\r\n transition: border-color 0.12s;\r\n user-select: none;\r\n}\r\n.sts-collapse-header:hover {\r\n border-color: var(--dsw-alias-border-l3, #3a3a3e);\r\n}\r\n.sts-collapse-chevron {\r\n display: flex;\r\n align-items: center;\r\n transition: transform 0.12s;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n}\r\n.sts-collapse-chevron.expanded {\r\n transform: rotate(90deg);\r\n}\r\n.sts-collapse-hint {\r\n margin-left: auto;\r\n font-size: 10px;\r\n color: rgba(255,255,255,0.4);\r\n}\r\n.sts-collapse-body {\r\n margin-top: 8px;\r\n padding: 12px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 6px;\r\n background: var(--dsw-alias-bg-layer-1, #1a1a1e);\r\n display: flex;\r\n flex-direction: column;\r\n gap: 10px;\r\n}\r\n\r\n/* ===== Settings picker rows & dir chooser modal ===== */\r\n.sts-settings-picker-row {\r\n margin: 6px 0;\r\n}\r\n.sts-settings-picker-row .sts-settings-label {\r\n display: block;\r\n margin-bottom: 4px;\r\n font-size: 11px;\r\n}\r\n.sts-dir-modal {\r\n background: var(--dsw-alias-bg-layer-3, #242428);\r\n border: 1px solid var(--dsw-alias-border-l3, #3a3a3e);\r\n border-radius: 12px;\r\n width: 420px;\r\n max-width: 90vw;\r\n max-height: 70vh;\r\n display: flex;\r\n flex-direction: column;\r\n box-shadow: 0 8px 32px rgba(0,0,0,0.3);\r\n}\r\n.sts-dir-modal-tree {\r\n flex: 1;\r\n overflow-y: auto;\r\n padding: 8px;\r\n min-height: 160px;\r\n}\r\n.sts-dirtree {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 2px;\r\n}\r\n.sts-dirtree-nav {\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n padding: 4px 4px 8px;\r\n border-bottom: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n margin-bottom: 4px;\r\n}\r\n.sts-dirtree-up,\r\n.sts-dirtree-home {\r\n width: 24px;\r\n height: 24px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 4px;\r\n background: transparent;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n cursor: pointer;\r\n font-size: 13px;\r\n flex-shrink: 0;\r\n transition: background 0.12s, color 0.12s;\r\n}\r\n.sts-dirtree-up:hover,\r\n.sts-dirtree-home:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.06));\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-dirtree-up:disabled {\r\n opacity: 0.4;\r\n cursor: default;\r\n}\r\n.sts-dirtree-cur {\r\n flex: 1;\r\n font-size: 11px;\r\n color: var(--dsw-alias-label-tertiary, #888);\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n direction: rtl;\r\n text-align: left;\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, monospace;\r\n}\r\n.sts-tree-select-hint {\r\n margin-left: auto;\r\n font-size: 10px;\r\n color: rgba(255,255,255,0.4);\r\n}\r\n.sts-dirtree-sub {\r\n border-top: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n margin-top: 2px;\r\n padding-top: 2px;\r\n}\r\n\r\n/* ===== Env bulk paste ===== */\r\n.sts-env-actions {\r\n display: flex;\r\n gap: 6px;\r\n align-items: center;\r\n margin-top: 2px;\r\n}\r\n.sts-env-paste {\r\n margin-top: 8px;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 6px;\r\n}\r\n.sts-env-paste-input {\r\n width: 100%;\r\n padding: 8px 10px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 6px;\r\n background: var(--dsw-alias-bg-layer-1, #1a1a1e);\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n font-size: 12px;\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, monospace;\r\n outline: none;\r\n resize: vertical;\r\n min-height: 80px;\r\n box-sizing: border-box;\r\n}\r\n.sts-env-paste-input:focus {\r\n border-color: var(--dsw-alias-border-l3, #3a3a3e);\r\n}\r\n.sts-env-paste-input::placeholder {\r\n color: rgba(255,255,255,0.3);\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, monospace;\r\n}\r\n.sts-env-paste-footer {\r\n display: flex;\r\n align-items: center;\r\n gap: 10px;\r\n}\r\n.sts-env-paste-apply {\r\n padding: 6px 14px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 6px;\r\n background: transparent;\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n cursor: pointer;\r\n font-size: 12px;\r\n transition: background 0.12s;\r\n}\r\n.sts-env-paste-apply:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.06));\r\n}\r\n.sts-env-paste-hint {\r\n font-size: 10px;\r\n color: rgba(255,255,255,0.4);\r\n}\r\n`\r\n","/**\n * Services tab: the main sidebar panel component. Left 1/3 config tree,\n * right 2/3 log view or config detail. Manages config CRUD via the host\n * REST API and tracks running instances.\n * @module dsh-better-sidebar-starter/client/ServicesTab\n */\n\nimport { createElement, useState, useEffect, useRef, useCallback, type ReactNode } from 'react'\nimport type { TabComponentProps } from 'dsh-better-sidebar/client/service'\nimport type { RunConfig, RunInstance } from './types.ts'\nimport { ConfigTree } from './ConfigTree.tsx'\nimport { LogView } from './LogView.tsx'\nimport { ConfigDetail } from './ConfigDetail.tsx'\nimport { ConfigEditorModal, type ConfigEditorModalProps } from './ConfigEditorModal.tsx'\nimport { RocketIcon, PlusIcon } from './icons.tsx'\nimport { STYLES_CSS } from './styles.ts'\n\n/** API base. */\nconst API_BASE = '/api/dsh-better-sidebar-starter'\n\n/** Instance poll interval (ms). */\nconst POLL_INTERVAL = 3000\n\n/** Inject styles once. */\nlet STYLES_INJECTED = false\nfunction injectStyles(): void {\n if (STYLES_INJECTED) return\n STYLES_INJECTED = true\n const tag = document.createElement('style')\n tag.id = 'dsh-better-sidebar-starter-styles'\n tag.setAttribute('data-plugin', '@dong-victor/dsh-better-sidebar-starter')\n tag.textContent = STYLES_CSS\n document.head.appendChild(tag)\n}\n\n/** The main services tab component. */\nexport function ServicesTab(props: TabComponentProps): ReactNode {\n injectStyles()\n\n const { scope, store } = props\n const [configs, setConfigs] = useState<RunConfig[]>([])\n const [instances, setInstances] = useState<RunInstance[]>([])\n const [selectedConfigId, setSelectedConfigId] = useState<string | null>(null)\n const [selectedInstanceId, setSelectedInstanceId] = useState<string | null>(null)\n const [editingConfig, setEditingConfig] = useState<ConfigEditorModalProps['config'] | null>(null)\n const [showModal, setShowModal] = useState(false)\n const [splitRatio, setSplitRatio] = useState(0.33)\n const [dragging, setDragging] = useState(false)\n const [isHorizontal, setIsHorizontal] = useState(true) // true = left/right split (right panel), false = top/bottom (bottom panel)\n\n const containerRef = useRef<HTMLDivElement>(null)\n\n // Detect panel orientation via ResizeObserver (aspect ratio).\n useEffect(() => {\n const el = containerRef.current\n if (el === null) return\n const check = (): void => {\n const rect = el.getBoundingClientRect()\n // If wider than tall → horizontal split (left/right). If taller → vertical (top/bottom).\n setIsHorizontal(rect.width > rect.height * 1.2)\n }\n check()\n const ro = new ResizeObserver(check)\n ro.observe(el)\n return () => ro.disconnect()\n }, [])\n\n // Read split ratio from plugin settings on mount.\n useEffect(() => {\n const blob = store.getPrefs()?.pluginSettings?.['dsh-better-sidebar-starter:services'] ?? {}\n if (typeof blob.splitRatio === 'number') {\n setSplitRatio(Math.max(0.2, Math.min(0.5, blob.splitRatio)))\n }\n }, [store])\n\n /** Persist split ratio to plugin settings. */\n const persistSplitRatio = useCallback((ratio: number): void => {\n const service = (store as any).service\n if (service !== undefined && typeof service.updateTab === 'function') {\n // Use pluginSettings persistence if available.\n }\n }, [store])\n\n /** Build a query string for the session scope. */\n const sessionQuery = useCallback((): string => {\n const params = new URLSearchParams({ sessionId: scope.sessionId })\n if (scope.cwd !== undefined) params.set('cwd', scope.cwd)\n return params.toString()\n }, [scope])\n\n /** Fetch configs from the host. */\n const fetchConfigs = useCallback(async (): Promise<void> => {\n try {\n const resp = await fetch(`${API_BASE}/configs?${sessionQuery()}`)\n if (resp.ok) {\n const data = await resp.json() as { configs: RunConfig[] }\n setConfigs(data.configs)\n }\n } catch { /* network error — ignore */ }\n }, [sessionQuery])\n\n /** Fetch instances from the host. */\n const fetchInstances = useCallback(async (): Promise<void> => {\n try {\n const resp = await fetch(`${API_BASE}/instances?${sessionQuery()}`)\n if (resp.ok) {\n const data = await resp.json() as { instances: RunInstance[] }\n setInstances(data.instances)\n }\n } catch { /* network error — ignore */ }\n }, [sessionQuery])\n\n // Initial load.\n useEffect(() => {\n void fetchConfigs()\n void fetchInstances()\n }, [fetchConfigs, fetchInstances])\n\n // Poll instances.\n useEffect(() => {\n const timer = setInterval(() => void fetchInstances(), POLL_INTERVAL)\n return () => clearInterval(timer)\n }, [fetchInstances])\n\n /** Start a config. */\n const handleStart = useCallback(async (configId: string): Promise<void> => {\n try {\n // Read global env paths from plugin settings.\n const blob = store.getPrefs()?.pluginSettings?.['dsh-better-sidebar-starter:services'] ?? {}\n const globalEnv: Record<string, string> = {}\n if (typeof blob.javaHome === 'string' && blob.javaHome.trim() !== '') globalEnv.JAVA_HOME = blob.javaHome.trim()\n if (typeof blob.nodePath === 'string' && blob.nodePath.trim() !== '') globalEnv.NODE_PATH = blob.nodePath.trim()\n if (typeof blob.pythonPath === 'string' && blob.pythonPath.trim() !== '') globalEnv.PYTHON_PATH = blob.pythonPath.trim()\n if (typeof blob.mvnPath === 'string' && blob.mvnPath.trim() !== '') globalEnv.MVN_PATH = blob.mvnPath.trim()\n\n const resp = await fetch(`${API_BASE}/run?${sessionQuery()}`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ configId, globalEnv }),\n })\n if (resp.ok) {\n const data = await resp.json() as { instance: RunInstance }\n setSelectedInstanceId(data.instance.id)\n setSelectedConfigId(null)\n void fetchInstances()\n void fetchConfigs() // lastRunAt update\n }\n } catch { /* ignore */ }\n }, [sessionQuery, fetchInstances, fetchConfigs, store])\n\n /** Stop an instance. */\n const handleStop = useCallback(async (instanceId: string): Promise<void> => {\n try {\n await fetch(`${API_BASE}/stop`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ instanceId }),\n })\n void fetchInstances()\n } catch { /* ignore */ }\n }, [fetchInstances])\n\n /** Restart = stop + start the same config. */\n const handleRestart = useCallback(async (instanceId: string): Promise<void> => {\n const inst = instances.find((i) => i.id === instanceId)\n if (inst === undefined) return\n await handleStop(instanceId)\n // Small delay to let the process die.\n await new Promise((r) => setTimeout(r, 200))\n await handleStart(inst.configId)\n }, [instances, handleStop, handleStart])\n\n /** Open the new config modal. */\n const handleNewConfig = (): void => {\n setEditingConfig(null)\n setShowModal(true)\n }\n\n /** Open the edit config modal. */\n const handleEditConfig = (configId: string): void => {\n const config = configs.find((c) => c.id === configId)\n if (config === undefined) return\n setEditingConfig({\n id: config.id,\n name: config.name,\n type: config.type,\n command: config.command,\n cwd: config.cwd,\n env: config.env,\n jvmArgs: config.jvmArgs,\n args: config.args,\n runtime: config.runtime,\n })\n setShowModal(true)\n }\n\n /**\n * Duplicate a config: open the editor prefilled with ALL of the original\n * config's info under an auto-generated \"xxx-副本\" name (deduped against\n * existing names). Saving it (id omitted) creates a brand-new config.\n */\n const handleDuplicateConfig = (configId: string): void => {\n const config = configs.find((c) => c.id === configId)\n if (config === undefined) return\n // Derive a unique copy name: strip a trailing \"-副本\", then append\n // \"-副本\" (or \"-副本2\", \"-副本3\", …) while avoiding collisions.\n const stem = config.name.endsWith('-副本') ? config.name.slice(0, -3) : config.name\n const taken = new Set(configs.map((c) => c.name))\n let dupName = `${stem}-副本`\n let suffix = 2\n while (taken.has(dupName)) {\n dupName = `${stem}-副本${suffix}`\n suffix++\n }\n setEditingConfig({\n // No id → saving creates a new config instead of updating the source.\n name: dupName,\n type: config.type,\n command: config.command,\n cwd: config.cwd,\n env: config.env,\n jvmArgs: config.jvmArgs,\n args: config.args,\n runtime: config.runtime,\n })\n setShowModal(true)\n }\n\n /** Save a config (create or update). */\n const handleSaveConfig = useCallback(async (config: ConfigEditorModalProps['config']): Promise<void> => {\n try {\n const resp = await fetch(`${API_BASE}/configs?${sessionQuery()}`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ config }),\n })\n if (resp.ok) {\n setShowModal(false)\n setEditingConfig(null)\n void fetchConfigs()\n }\n } catch { /* ignore */ }\n }, [sessionQuery, fetchConfigs])\n\n /** Delete a config. */\n const handleDeleteConfig = useCallback(async (configId: string): Promise<void> => {\n // Stop any running instances first.\n for (const inst of instances) {\n if (inst.configId === configId && inst.status === 'running') {\n await handleStop(inst.id)\n }\n }\n try {\n const resp = await fetch(`${API_BASE}/configs?${sessionQuery()}&id=${encodeURIComponent(configId)}`, {\n method: 'DELETE',\n })\n if (resp.ok) {\n if (selectedConfigId === configId) setSelectedConfigId(null)\n void fetchConfigs()\n void fetchInstances()\n }\n } catch { /* ignore */ }\n }, [instances, handleStop, sessionQuery, fetchConfigs, fetchInstances, selectedConfigId])\n\n /** Select a config (deselect any instance). */\n const handleSelectConfig = (configId: string): void => {\n setSelectedConfigId(configId)\n setSelectedInstanceId(null)\n }\n\n /** Select an instance. */\n const handleSelectInstance = (instanceId: string): void => {\n setSelectedInstanceId(instanceId)\n setSelectedConfigId(null)\n }\n\n // -- Split pane drag handling --\n const handleMouseDown = (e: MouseEvent): void => {\n e.preventDefault()\n setDragging(true)\n }\n\n useEffect(() => {\n if (!dragging) return\n const handleMove = (e: MouseEvent): void => {\n if (containerRef.current === null) return\n const rect = containerRef.current.getBoundingClientRect()\n const ratio = isHorizontal\n ? (e.clientX - rect.left) / rect.width\n : (e.clientY - rect.top) / rect.height\n const clamped = Math.max(0.2, Math.min(0.5, ratio))\n setSplitRatio(clamped)\n persistSplitRatio(clamped)\n }\n const handleUp = (): void => setDragging(false)\n document.addEventListener('mousemove', handleMove)\n document.addEventListener('mouseup', handleUp)\n return () => {\n document.removeEventListener('mousemove', handleMove)\n document.removeEventListener('mouseup', handleUp)\n }\n }, [dragging, isHorizontal, persistSplitRatio])\n\n // Resolve what to show in the right pane.\n const selectedInstance = selectedInstanceId !== null\n ? instances.find((i) => i.id === selectedInstanceId) ?? null\n : null\n const selectedConfig = selectedConfigId !== null\n ? configs.find((c) => c.id === selectedConfigId) ?? null\n : null\n\n return createElement('div', { ref: containerRef, className: 'sts-root' },\n // Toolbar\n createElement('div', { className: 'sts-toolbar' },\n createElement('div', { className: 'sts-toolbar-title' },\n createElement(RocketIcon, { size: 16 }),\n '服务',\n ),\n createElement('button', {\n className: 'sts-toolbar-btn',\n onClick: handleNewConfig,\n },\n createElement(PlusIcon, { size: 13 }), '新建配置'),\n ),\n // Split pane (horizontal or vertical based on panel position)\n createElement('div', { className: `sts-split${isHorizontal ? '' : ' sts-split-vertical'}` },\n // First pane: config tree\n createElement('div', {\n className: `sts-split-first${isHorizontal ? '' : ' sts-split-first-v'}`,\n style: isHorizontal\n ? { width: `${splitRatio * 100}%` }\n : { height: `${splitRatio * 100}%` },\n },\n configs.length === 0\n ? createElement('div', { className: 'sts-empty' }, '暂无运行配置,点击右上角 + 新建')\n : createElement(ConfigTree, {\n configs,\n instances,\n selectedInstanceId,\n selectedConfigId,\n onSelectConfig: handleSelectConfig,\n onSelectInstance: handleSelectInstance,\n onStartConfig: (id: string) => { void handleStart(id) },\n onStopInstance: (id: string) => { void handleStop(id) },\n onEditConfig: handleEditConfig,\n onDuplicateConfig: handleDuplicateConfig,\n }),\n ),\n // Gutter\n createElement('div', {\n className: `sts-split-gutter${dragging ? ' dragging' : ''}${isHorizontal ? '' : ' sts-split-gutter-v'}`,\n onMouseDown: handleMouseDown,\n }),\n // Second pane: log view or config detail or empty\n createElement('div', { className: `sts-split-second${isHorizontal ? '' : ' sts-split-second-v'}` },\n selectedInstance !== null\n ? createElement(LogView, {\n instance: selectedInstance,\n ctx: props.ctx,\n sessionId: scope.sessionId ?? store.getSnapshot().sessionId ?? '',\n onStop: () => { void handleStop(selectedInstance.id) },\n onRestart: () => { void handleRestart(selectedInstance.id) },\n })\n : selectedConfig !== null\n ? createElement(ConfigDetail, {\n config: selectedConfig,\n onStart: () => { void handleStart(selectedConfig.id) },\n onEdit: () => handleEditConfig(selectedConfig.id),\n onDuplicate: () => handleDuplicateConfig(selectedConfig.id),\n onDelete: () => { void handleDeleteConfig(selectedConfig.id) },\n })\n : createElement('div', { className: 'sts-empty' }, '选择左侧配置查看详情或日志'),\n ),\n ),\n // Modal\n showModal\n ? createElement(ConfigEditorModal, {\n config: editingConfig,\n scope: { sessionId: scope.sessionId ?? store.getSnapshot().sessionId ?? '', cwd: scope.cwd },\n onSave: (c) => { void handleSaveConfig(c) },\n onCancel: () => { setShowModal(false); setEditingConfig(null) },\n })\n : null,\n )\n}\n","/**\n * Custom settings panel for the \"服务\" tab: renders switch toggles plus\n * validated directory pickers for Java/Node/Python/Maven paths.\n * @module dsh-better-sidebar-starter/client/SettingsPanel\n */\n\nimport { createElement, useState, useEffect, type ReactNode } from 'react'\nimport type { SidebarSettingsRenderProps, SessionScope } from 'dsh-better-sidebar/client/service'\nimport { PathPicker } from './PathPicker.tsx'\n\n/** The custom settings panel rendered in the gear popup. */\nexport function SettingsPanel(props: SidebarSettingsRenderProps): ReactNode {\n const { store, pluginSettings, updatePluginSetting } = props\n const scope: SessionScope = { sessionId: store.getSnapshot().sessionId ?? '' }\n\n const get = (key: string): string =>\n typeof pluginSettings[key] === 'string' ? pluginSettings[key] as string : ''\n const getBool = (key: string): boolean =>\n pluginSettings[key] === true\n\n const [autoSelect, setAutoSelect] = useState(getBool('autoSelectOnStart'))\n const [autoScroll, setAutoScroll] = useState(getBool('autoScroll'))\n const [javaHome, setJavaHome] = useState(get('javaHome'))\n const [nodePath, setNodePath] = useState(get('nodePath'))\n const [pythonPath, setPythonPath] = useState(get('pythonPath'))\n const [mvnPath, setMvnPath] = useState(get('mvnPath'))\n\n // Auto-detect environment paths on first open if not yet configured.\n useEffect(() => {\n const needDetect = get('javaHome') === '' && get('nodePath') === '' && get('pythonPath') === '' && get('mvnPath') === ''\n if (!needDetect) return\n let cancelled = false\n fetch('/api/dsh-better-sidebar-starter/detect-env')\n .then((r) => r.ok ? r.json() as Promise<{ javaHome?: string; nodePath?: string; pythonPath?: string; mvnPath?: string }> : null)\n .then((detected) => {\n if (cancelled || detected === null) return\n if (detected.javaHome !== undefined && detected.javaHome !== '') {\n setJavaHome(detected.javaHome)\n updatePluginSetting('javaHome', detected.javaHome)\n }\n if (detected.nodePath !== undefined && detected.nodePath !== '') {\n setNodePath(detected.nodePath)\n updatePluginSetting('nodePath', detected.nodePath)\n }\n if (detected.pythonPath !== undefined && detected.pythonPath !== '') {\n setPythonPath(detected.pythonPath)\n updatePluginSetting('pythonPath', detected.pythonPath)\n }\n if (detected.mvnPath !== undefined && detected.mvnPath !== '') {\n setMvnPath(detected.mvnPath)\n updatePluginSetting('mvnPath', detected.mvnPath)\n }\n })\n .catch(() => { /* ignore */ })\n return () => { cancelled = true }\n }, [])\n\n const handleSwitch = (key: string, setter: (v: boolean) => void) => (v: boolean): void => {\n setter(v)\n updatePluginSetting(key, v)\n }\n const handlePath = (key: string, setter: (v: string) => void) => (v: string): void => {\n setter(v)\n updatePluginSetting(key, v)\n }\n\n return createElement('div', { className: 'sts-settings-panel' },\n // Switches\n createElement('div', { className: 'sts-settings-row' },\n createElement('label', { className: 'sts-settings-switch-row' },\n createElement('input', {\n type: 'checkbox',\n className: 'sts-settings-switch',\n checked: autoSelect,\n onChange: (e: { target: { checked: boolean } }) => handleSwitch('autoSelectOnStart', setAutoSelect)(e.target.checked),\n }),\n createElement('span', null, '\\u542f\\u52a8\\u540e\\u81ea\\u52a8\\u9009\\u4e2d\\u65e5\\u5fd7'),\n ),\n ),\n createElement('div', { className: 'sts-settings-row' },\n createElement('label', { className: 'sts-settings-switch-row' },\n createElement('input', {\n type: 'checkbox',\n className: 'sts-settings-switch',\n checked: autoScroll,\n onChange: (e: { target: { checked: boolean } }) => handleSwitch('autoScroll', setAutoScroll)(e.target.checked),\n }),\n createElement('span', null, '\\u81ea\\u52a8\\u6eda\\u52a8\\u5230\\u6700\\u65b0\\u65e5\\u5fd7'),\n ),\n ),\n // Separator\n createElement('div', { className: 'sts-settings-sep' }, '\\u8fd0\\u884c\\u73af\\u5883\\u8def\\u5f84'),\n // Path pickers (each wrapped with a label row)\n createElement(PathPickerRow, {\n label: 'Java \\u4e3b\\u76ee\\u5f55 (JAVA_HOME)',\n scope,\n kind: 'java',\n value: javaHome,\n onChange: handlePath('javaHome', setJavaHome),\n desc: 'Spring Boot \\u7528\\u7684 JDK\\uff0c\\u7559\\u7a7a\\u5219\\u81ea\\u52a8\\u67e5\\u627e',\n }),\n createElement(PathPickerRow, {\n label: 'Node.js \\u8def\\u5f84',\n scope,\n kind: 'node',\n value: nodePath,\n onChange: handlePath('nodePath', setNodePath),\n desc: 'npm \\u7528\\u7684 Node\\uff0c\\u7559\\u7a7a\\u5219\\u7528\\u7cfb\\u7edf\\u9ed8\\u8ba4',\n }),\n createElement(PathPickerRow, {\n label: 'Python \\u8def\\u5f84',\n scope,\n kind: 'python',\n value: pythonPath,\n onChange: handlePath('pythonPath', setPythonPath),\n desc: 'Python \\u89e3\\u91ca\\u5668\\uff0c\\u7559\\u7a7a\\u5219\\u7528\\u7cfb\\u7edf\\u9ed8\\u8ba4',\n }),\n createElement(PathPickerRow, {\n label: 'Maven \\u8def\\u5f84 (MVN)',\n scope,\n kind: 'mvn',\n value: mvnPath,\n onChange: handlePath('mvnPath', setMvnPath),\n desc: 'springboot \\u7528\\u7684 Maven\\uff0c\\u7559\\u7a7a\\u5219\\u81ea\\u52a8\\u67e5\\u627e',\n }),\n )\n}\n\n/** A labeled path picker row for the settings panel. */\nfunction PathPickerRow(props: {\n label: string\n scope: SessionScope\n kind: 'java' | 'node' | 'python' | 'mvn'\n value: string\n onChange: (v: string) => void\n desc: string\n}): ReactNode {\n return createElement('div', { className: 'sts-settings-picker-row' },\n createElement('label', { className: 'sts-settings-label' }, props.label),\n createElement(PathPicker, {\n scope: props.scope,\n kind: props.kind,\n value: props.value,\n onChange: props.onChange,\n desc: props.desc,\n }),\n )\n}\n","/**\n * Client half of the dsh-better-sidebar-starter plugin: registers the\n * \"服务\" (Services) sidebar tab through the `ctx.betterSidebar` service.\n * The tab renders a split-pane panel: left config tree + right log/detail.\n * The host half (same package) owns the session-scoped config/process routes.\n * @module dsh-better-sidebar-starter/client\n */\n\nimport { createElement, type ReactNode } from 'react'\nimport type { Context } from 'cordis'\n// Triggers the ctx.betterSidebar type augmentation (erased at build).\nimport type {} from 'dsh-better-sidebar'\nimport { ServicesTab } from './ServicesTab.tsx'\nimport { SettingsPanel } from './SettingsPanel.tsx'\nimport { RocketIcon } from './icons.tsx'\n\n/** Services required before mounting: the betterSidebar registry (provided\n * by dsh-better-sidebar's own client half) and the session store. */\nexport const inject = ['betterSidebar', 'sessions']\n\n/** Plugin body. */\nexport function apply(ctx: Context): void {\n // The \"服务\" sidebar tab.\n ctx.effect(() =>\n ctx.betterSidebar.registerTab({\n id: 'dsh-better-sidebar-starter:services',\n title: () => '服务',\n icon: (size: number): ReactNode => createElement(RocketIcon, { size }),\n order: 50,\n single: true,\n settings: {\n pluginToggles: [\n { key: 'autoSelectOnStart', type: 'switch', title: '启动后自动选中日志' },\n { key: 'autoScroll', type: 'switch', title: '自动滚动到最新日志' },\n { key: 'javaHome', type: 'text', title: 'Java 主目录 (JAVA_HOME)', desc: 'Spring Boot 用的 JDK 路径,留空则自动查找' },\n { key: 'nodePath', type: 'text', title: 'Node.js 路径', desc: 'npm 命令用的 Node 路径,留空则用系统默认' },\n { key: 'pythonPath', type: 'text', title: 'Python 路径', desc: 'Python 命令用的解释器路径,留空则用系统默认' },\n ],\n render: (props): ReactNode => createElement(SettingsPanel, props),\n },\n component: (props): ReactNode => createElement(ServicesTab, props),\n }),\n 'dsh-better-sidebar-starter: services tab',\n )\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dong-victor/dsh-better-sidebar-starter",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "dsh-better-sidebar 的运行配置插件:模仿 IntelliJ IDEA 运行/调试配置,支持 npm/springboot/python/custom 命令执行、历史保存、快捷启动、实时日志查看,单 Tab 内分栏布局(左配置树 + 右日志流)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|