@dong-victor/dsh-better-sidebar-starter 0.1.7 → 0.1.9

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 CHANGED
@@ -161,7 +161,7 @@ window.__ModuleLoader__.load({
161
161
  }
162
162
  /** The config tree component. */
163
163
  function ConfigTree(props) {
164
- const { configs, instances, selectedInstanceId, selectedConfigId, onSelectConfig, onSelectInstance, onStartConfig, onStopInstance, onEditConfig, onDuplicateConfig } = props;
164
+ const { configs, instances, selectedInstanceId, selectedConfigId, onSelectConfig, onSelectInstance, onStartConfig, onStopInstance, onEditConfig, onDuplicateConfig, onDeleteConfig } = props;
165
165
  const [collapsed, setCollapsed] = (0, react.useState)(/* @__PURE__ */ new Set());
166
166
  const toggleGroup = (type) => {
167
167
  setCollapsed((prev) => {
@@ -228,7 +228,14 @@ window.__ModuleLoader__.load({
228
228
  e.stopPropagation();
229
229
  onDuplicateConfig(config.id);
230
230
  }
231
- }, CopyIcon({ size: 12 }))))];
231
+ }, CopyIcon({ size: 12 })), (0, react.createElement)("button", {
232
+ className: "sts-icon-btn trash",
233
+ title: "删除配置",
234
+ onClick: (e) => {
235
+ e.stopPropagation();
236
+ onDeleteConfig(config.id);
237
+ }
238
+ }, TrashIcon({ size: 12 }))))];
232
239
  for (const inst of configInstances) {
233
240
  const instSelected = selectedInstanceId === inst.id;
234
241
  rows.push((0, react.createElement)("div", {
@@ -1082,29 +1089,28 @@ window.__ModuleLoader__.load({
1082
1089
  //#endregion
1083
1090
  //#region src/client/DirChooser.tsx
1084
1091
  /**
1085
- * Shared directory chooser: a filesystem folder tree (backed by the sidebar
1086
- * fs.tree API, which can list any absolute path) plus a browse button that
1087
- * opens a modal popup. Browsing starts at the user's home directory and can
1088
- * navigate up to any drive/root, so runtime tool paths (JDK/Maven/...) that
1089
- * live outside the workspace can be selected.
1092
+ * Shared directory chooser: a filesystem folder tree (backed by the starter
1093
+ * plugin's own fs-dirs host route) plus a browse button that opens a modal
1094
+ * popup. Browsing starts at the user's home directory and can navigate up to
1095
+ * any drive/root, so runtime tool paths (JDK/Maven/...) that live outside the
1096
+ * workspace can be selected. The sidebar fs.tree API cannot be used here — it
1097
+ * is workspace-confined since dsh-better-sidebar 0.16 (403 outside the
1098
+ * session workspace).
1090
1099
  * @module dsh-better-sidebar-starter/client/DirChooser
1091
1100
  */
1092
- /** Call the sidebar fs.tree API directly via fetch. Returns directories only. */
1093
- async function fsTree$1(scope, path) {
1094
- const payload = {
1095
- sessionId: scope.sessionId,
1096
- path
1097
- };
1098
- if (scope.cwd !== void 0 && scope.cwd !== "") payload.cwd = scope.cwd;
1099
- const resp = await fetch("/sidebar/api/fs.tree", {
1101
+ /** Call the starter fs-dirs host route via fetch. Returns directories only. */
1102
+ async function fsTree(scope, path) {
1103
+ const resp = await fetch("/api/dsh-better-sidebar-starter/fs-dirs", {
1100
1104
  method: "POST",
1101
1105
  headers: { "content-type": "application/json" },
1102
- body: JSON.stringify(payload)
1106
+ body: JSON.stringify({
1107
+ sessionId: scope.sessionId,
1108
+ path
1109
+ })
1103
1110
  });
1104
- if (!resp.ok) throw new Error(`fs.tree ${path}: ${resp.status}`);
1105
- const parsed = await resp.json();
1106
- if (!parsed.ok || parsed.value === void 0) throw new Error(`fs.tree ${path}: ${parsed.error?.message ?? "unknown error"}`);
1107
- return parsed.value.entries.filter((e) => e.isDir && !e.hidden);
1111
+ const parsed = await resp.json().catch(() => void 0);
1112
+ if (!resp.ok) throw new Error(`fs-dirs ${path}: ${parsed?.error ?? resp.status}`);
1113
+ return (parsed?.entries ?? []).filter((e) => e.isDir && !e.hidden);
1108
1114
  }
1109
1115
  /** Fetch the user's home directory from the host. Falls back to cwd. */
1110
1116
  async function fetchHomeDefault(scope) {
@@ -1156,7 +1162,7 @@ window.__ModuleLoader__.load({
1156
1162
  const loadDir = (s, path) => {
1157
1163
  setLoading(true);
1158
1164
  setError(null);
1159
- fsTree$1(s, path).then((dirs) => {
1165
+ fsTree(s, path).then((dirs) => {
1160
1166
  setEntries(dirs);
1161
1167
  setLoading(false);
1162
1168
  }).catch((err) => {
@@ -1237,7 +1243,7 @@ window.__ModuleLoader__.load({
1237
1243
  onToggleExpand(entry.path);
1238
1244
  if (children === null) {
1239
1245
  setLoading(true);
1240
- fsTree$1(scope, entry.path).then((dirs) => {
1246
+ fsTree(scope, entry.path).then((dirs) => {
1241
1247
  setChildren(dirs);
1242
1248
  setLoading(false);
1243
1249
  }).catch(() => {
@@ -1349,23 +1355,6 @@ window.__ModuleLoader__.load({
1349
1355
  * and environment variables.
1350
1356
  * @module dsh-better-sidebar-starter/client/ConfigEditorModal
1351
1357
  */
1352
- /** Call the sidebar fs.tree API directly via fetch. Returns directories only.
1353
- * If path is omitted, the server uses the session cwd (root listing).
1354
- * The sidebar API wraps responses in { ok: true, value: ... } envelope. */
1355
- async function fsTree(scope, path) {
1356
- const payload = { sessionId: scope.sessionId };
1357
- if (scope.cwd !== void 0 && scope.cwd !== "") payload.cwd = scope.cwd;
1358
- if (path !== void 0) payload.path = path;
1359
- const resp = await fetch("/sidebar/api/fs.tree", {
1360
- method: "POST",
1361
- headers: { "content-type": "application/json" },
1362
- body: JSON.stringify(payload)
1363
- });
1364
- if (!resp.ok) throw new Error(`fs.tree ${path ?? "(root)"}: ${resp.status}`);
1365
- const parsed = await resp.json();
1366
- if (!parsed.ok || parsed.value === void 0) throw new Error(`fs.tree ${path ?? "(root)"}: ${parsed.error?.message ?? "unknown error"}`);
1367
- return parsed.value.entries.filter((e) => e.isDir && !e.hidden);
1368
- }
1369
1358
  /** Recursive tree node: loads its own children on expand. */
1370
1359
  function TreeNode(props) {
1371
1360
  const { scope, entry, depth, selectedPath, onSelect } = props;
@@ -1419,7 +1408,8 @@ window.__ModuleLoader__.load({
1419
1408
  if (!expanded && children === null) {
1420
1409
  setLoading(true);
1421
1410
  setError(null);
1422
- fsTree(scope).then((dirs) => {
1411
+ const root = scope.cwd ?? "";
1412
+ (root === "" ? Promise.reject(/* @__PURE__ */ new Error("fs-dirs: 会话工作目录不可用")) : fsTree(scope, root)).then((dirs) => {
1423
1413
  setChildren(dirs);
1424
1414
  setLoading(false);
1425
1415
  }).catch((err) => {
@@ -2851,7 +2841,10 @@ window.__ModuleLoader__.load({
2851
2841
  }
2852
2842
  } catch {}
2853
2843
  }, [sessionQuery, fetchConfigs]);
2854
- (0, react.useCallback)(async (configId) => {
2844
+ /** Delete a config. */
2845
+ const handleDeleteConfig = (0, react.useCallback)(async (configId) => {
2846
+ const target = configs.find((c) => c.id === configId);
2847
+ if (!globalThis.confirm(`确定删除配置「${target?.name ?? configId}」?运行中的实例将被一并停止。`)) return;
2855
2848
  for (const inst of instances) if (inst.configId === configId && inst.status === "running") await handleStop(inst.id);
2856
2849
  try {
2857
2850
  if ((await fetch(`${API_BASE}/configs?${sessionQuery()}&id=${encodeURIComponent(configId)}`, { method: "DELETE" })).ok) {
@@ -2861,6 +2854,7 @@ window.__ModuleLoader__.load({
2861
2854
  }
2862
2855
  } catch {}
2863
2856
  }, [
2857
+ configs,
2864
2858
  instances,
2865
2859
  handleStop,
2866
2860
  sessionQuery,
@@ -2929,7 +2923,10 @@ window.__ModuleLoader__.load({
2929
2923
  handleStop(id);
2930
2924
  },
2931
2925
  onEditConfig: handleEditConfig,
2932
- onDuplicateConfig: handleDuplicateConfig
2926
+ onDuplicateConfig: handleDuplicateConfig,
2927
+ onDeleteConfig: (id) => {
2928
+ handleDeleteConfig(id);
2929
+ }
2933
2930
  })), (0, react.createElement)("div", {
2934
2931
  className: `sts-split-gutter${dragging ? " dragging" : ""}${isHorizontal ? "" : " sts-split-gutter-v"}`,
2935
2932
  onMouseDown: handleMouseDown
@@ -3090,39 +3087,7 @@ window.__ModuleLoader__.load({
3090
3087
  icon: (size) => (0, react.createElement)(RocketIcon, { size }),
3091
3088
  order: 50,
3092
3089
  single: true,
3093
- settings: {
3094
- pluginToggles: [
3095
- {
3096
- key: "autoSelectOnStart",
3097
- type: "switch",
3098
- title: "启动后自动选中日志"
3099
- },
3100
- {
3101
- key: "autoScroll",
3102
- type: "switch",
3103
- title: "自动滚动到最新日志"
3104
- },
3105
- {
3106
- key: "javaHome",
3107
- type: "text",
3108
- title: "Java 主目录 (JAVA_HOME)",
3109
- desc: "Spring Boot 用的 JDK 路径,留空则自动查找"
3110
- },
3111
- {
3112
- key: "nodePath",
3113
- type: "text",
3114
- title: "Node.js 路径",
3115
- desc: "npm 命令用的 Node 路径,留空则用系统默认"
3116
- },
3117
- {
3118
- key: "pythonPath",
3119
- type: "text",
3120
- title: "Python 路径",
3121
- desc: "Python 命令用的解释器路径,留空则用系统默认"
3122
- }
3123
- ],
3124
- render: (props) => (0, react.createElement)(SettingsPanel, props)
3125
- },
3090
+ settings: { render: (props) => (0, react.createElement)(SettingsPanel, props) },
3126
3091
  component: (props) => (0, react.createElement)(ServicesTab, props)
3127
3092
  }), "dsh-better-sidebar-starter: services tab");
3128
3093
  }