@norman-else/dsh-claude 0.1.9 → 0.1.11

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/bin.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { i as parseClaudeVersion, n as ensureManagedPreset, r as removeManagedPreset } from "./preset-installer-DUmmDvBm.mjs";
2
+ import { i as parseClaudeVersion, n as ensureManagedPreset, r as removeManagedPreset } from "./preset-installer-B0NrA4Hi.mjs";
3
3
  import { access } from "node:fs/promises";
4
4
  import { delimiter, isAbsolute, join } from "node:path";
5
5
  import { homedir } from "node:os";
package/lib/client.d.ts CHANGED
@@ -35,6 +35,12 @@ window.__ModuleLoader__.load({
35
35
  readonly error: "Doctor failed";
36
36
  readonly security: "Permission boundary";
37
37
  readonly securityBody: "Claude tool permissions are decided through the DSH approval UI. This version does not claim kernel-level write isolation for ~/.claude or paths outside the workspace.";
38
+ readonly globalSettings: "Claude Code global settings";
39
+ readonly globalSettingsBody: "Modify supported ~/.claude/settings.json values while preserving every other field.";
40
+ readonly globalSettingsLoading: "Loading global settings…";
41
+ readonly globalSettingsNewSession: "Output Style changes apply only to new Claude sessions.";
42
+ readonly globalSettingsError: "Global settings save failed";
43
+ readonly outputStyle: "Output Style";
38
44
  readonly pluginUpdate: "Plugin updates";
39
45
  readonly pluginUpdateBody: "Check npm for new releases. Only a uniquely identified npm installation can update in place; local development links are never replaced.";
40
46
  readonly updateNotChecked: "Updates have not been checked yet.";
package/lib/client.js CHANGED
@@ -3,7 +3,7 @@ window.__ModuleLoader__.load({
3
3
  factory: (require) => {
4
4
  var module = { exports: {} };
5
5
  module.exports;
6
- var { useCallback, useEffect, useMemo, useState } = require("react");
6
+ var { useCallback, useEffect, useId, useMemo, useRef, useState } = require("react");
7
7
  var { Fragment, jsx, jsxs } = require("react/jsx-runtime");
8
8
  var { DisclosureRow, IconApiOutline14, IconThinkOutline14, StateDot } = require("@deepseek-ai/dsh-client-ui-primitives");
9
9
  /** Claude's subagent dispatch tools; rendered as plugin-owned group cards
@@ -13,6 +13,7 @@ window.__ModuleLoader__.load({
13
13
  const CLAUDE_UPDATE_CHECK_PATH = "/plugins/dsh-claude/update/check";
14
14
  const CLAUDE_UPDATE_PATH = "/plugins/dsh-claude/update";
15
15
  const CLAUDE_PROJECTION_PATH = "/plugins/dsh-claude/projection";
16
+ const CLAUDE_GLOBAL_SETTINGS_PATH = "/plugins/dsh-claude/settings/global";
16
17
  //#endregion
17
18
  //#region src/client/conversation-sidecar.ts
18
19
  function isTaskActivity(value) {
@@ -309,6 +310,91 @@ window.__ModuleLoader__.load({
309
310
  fontWeight: 500,
310
311
  overflowWrap: "anywhere"
311
312
  };
313
+ const settingSelect = {
314
+ position: "relative",
315
+ minWidth: 0
316
+ };
317
+ const settingSelectTrigger = {
318
+ width: "100%",
319
+ minHeight: 38,
320
+ display: "flex",
321
+ alignItems: "center",
322
+ justifyContent: "space-between",
323
+ gap: 12,
324
+ padding: "7px 11px 7px 13px",
325
+ border: "1px solid var(--dsw-alias-border-l2)",
326
+ borderRadius: 10,
327
+ background: "var(--dsw-alias-bg-layer-2)",
328
+ color: "var(--dsw-alias-label-primary)",
329
+ boxShadow: "0 1px 2px color-mix(in srgb, var(--dsw-alias-label-primary) 5%, transparent)",
330
+ font: "inherit",
331
+ fontSize: 13,
332
+ lineHeight: "20px",
333
+ textAlign: "left",
334
+ cursor: "pointer",
335
+ transition: "border-color 120ms ease, box-shadow 120ms ease, background 120ms ease"
336
+ };
337
+ const settingSelectTriggerOpen = {
338
+ borderColor: "var(--dsw-static-blue-450)",
339
+ boxShadow: "0 0 0 3px color-mix(in srgb, var(--dsw-static-blue-450) 15%, transparent)"
340
+ };
341
+ const settingSelectValue = {
342
+ minWidth: 0,
343
+ flex: 1,
344
+ overflow: "hidden",
345
+ textOverflow: "ellipsis",
346
+ whiteSpace: "nowrap",
347
+ fontWeight: 550
348
+ };
349
+ const settingSelectChevron = {
350
+ flex: "none",
351
+ color: "var(--dsw-alias-label-tertiary)",
352
+ fontSize: 16,
353
+ lineHeight: 1,
354
+ transform: "translateY(-1px)",
355
+ transition: "transform 120ms ease"
356
+ };
357
+ const settingSelectChevronOpen = { transform: "translateY(1px) rotate(180deg)" };
358
+ const settingSelectMenu = {
359
+ position: "absolute",
360
+ zIndex: 20,
361
+ top: "calc(100% + 6px)",
362
+ left: 0,
363
+ right: 0,
364
+ maxHeight: 240,
365
+ overflowY: "auto",
366
+ padding: 5,
367
+ border: "1px solid var(--dsw-alias-border-l2)",
368
+ borderRadius: 11,
369
+ background: "var(--dsw-alias-bg-layer-1)",
370
+ boxShadow: "0 12px 32px color-mix(in srgb, var(--dsw-alias-label-primary) 16%, transparent), 0 2px 8px color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent)"
371
+ };
372
+ const settingSelectOption = {
373
+ width: "100%",
374
+ minHeight: 34,
375
+ display: "flex",
376
+ alignItems: "center",
377
+ gap: 8,
378
+ padding: "6px 9px",
379
+ border: "none",
380
+ borderRadius: 7,
381
+ background: "transparent",
382
+ color: "var(--dsw-alias-label-primary)",
383
+ font: "inherit",
384
+ fontSize: 13,
385
+ lineHeight: "20px",
386
+ textAlign: "left",
387
+ cursor: "pointer"
388
+ };
389
+ const settingSelectOptionActive = { background: "var(--dsw-alias-bg-layer-2)" };
390
+ const settingSelectCheck = {
391
+ width: 14,
392
+ flex: "none",
393
+ color: "var(--dsw-static-blue-450)",
394
+ fontSize: 12,
395
+ fontWeight: 700,
396
+ textAlign: "center"
397
+ };
312
398
  const settingsActions = {
313
399
  display: "flex",
314
400
  flexWrap: "wrap",
@@ -1016,9 +1102,136 @@ window.__ModuleLoader__.load({
1016
1102
  "error"
1017
1103
  ].includes(String(status.state)) && typeof status.canUpdate === "boolean" && typeof status.restartRequired === "boolean" && (status.message === void 0 || typeof status.message === "string");
1018
1104
  }
1105
+ function isGlobalSettingsView(value) {
1106
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
1107
+ const settings = value.settings;
1108
+ return Array.isArray(settings) && settings.every((setting) => {
1109
+ if (typeof setting !== "object" || setting === null || Array.isArray(setting)) return false;
1110
+ const item = setting;
1111
+ return typeof item.key === "string" && item.kind === "select" && typeof item.value === "string" && ["new-session", "restart"].includes(String(item.effect)) && Array.isArray(item.options) && item.options.every((option) => typeof option === "object" && option !== null && typeof option.value === "string" && typeof option.label === "string" && [
1112
+ "built-in",
1113
+ "user",
1114
+ "configured"
1115
+ ].includes(String(option.source)));
1116
+ });
1117
+ }
1019
1118
  function value(status, detail) {
1020
1119
  return detail === void 0 ? status : `${status} · ${detail}`;
1021
1120
  }
1121
+ function GlobalSettingSelect({ setting, disabled, onChange }) {
1122
+ const [open, setOpen] = useState(false);
1123
+ const [activeIndex, setActiveIndex] = useState(0);
1124
+ const rootRef = useRef(null);
1125
+ const triggerRef = useRef(null);
1126
+ const listboxId = useId();
1127
+ const selectedIndex = Math.max(0, setting.options.findIndex((option) => option.value === setting.value));
1128
+ const selectedOption = setting.options[selectedIndex];
1129
+ useEffect(() => {
1130
+ if (!open) return;
1131
+ const closeOnOutsidePointer = (event) => {
1132
+ if (!rootRef.current?.contains(event.target)) setOpen(false);
1133
+ };
1134
+ document.addEventListener("mousedown", closeOnOutsidePointer);
1135
+ return () => document.removeEventListener("mousedown", closeOnOutsidePointer);
1136
+ }, [open]);
1137
+ const openMenu = (index = selectedIndex) => {
1138
+ setActiveIndex(index);
1139
+ setOpen(true);
1140
+ };
1141
+ const choose = (index) => {
1142
+ const option = setting.options[index];
1143
+ if (option === void 0) return;
1144
+ setOpen(false);
1145
+ triggerRef.current?.focus();
1146
+ if (option.value !== setting.value) onChange(option.value);
1147
+ };
1148
+ const move = (offset) => {
1149
+ const count = setting.options.length;
1150
+ if (count === 0) return;
1151
+ setActiveIndex((current) => (current + offset + count) % count);
1152
+ };
1153
+ return /* @__PURE__ */ jsxs("div", {
1154
+ ref: rootRef,
1155
+ style: settingSelect,
1156
+ onBlur: (event) => {
1157
+ if (!event.currentTarget.contains(event.relatedTarget)) setOpen(false);
1158
+ },
1159
+ children: [/* @__PURE__ */ jsxs("button", {
1160
+ ref: triggerRef,
1161
+ type: "button",
1162
+ "aria-haspopup": "listbox",
1163
+ "aria-expanded": open,
1164
+ "aria-controls": open ? listboxId : void 0,
1165
+ "aria-activedescendant": open ? `${listboxId}-${activeIndex}` : void 0,
1166
+ disabled: disabled || setting.options.length === 0,
1167
+ style: {
1168
+ ...settingSelectTrigger,
1169
+ ...open ? settingSelectTriggerOpen : {}
1170
+ },
1171
+ onClick: () => {
1172
+ if (open) setOpen(false);
1173
+ else openMenu();
1174
+ },
1175
+ onKeyDown: (event) => {
1176
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
1177
+ event.preventDefault();
1178
+ if (!open) openMenu(event.key === "ArrowDown" ? selectedIndex : Math.max(0, setting.options.length - 1));
1179
+ else move(event.key === "ArrowDown" ? 1 : -1);
1180
+ } else if (event.key === "Home" && open) {
1181
+ event.preventDefault();
1182
+ setActiveIndex(0);
1183
+ } else if (event.key === "End" && open) {
1184
+ event.preventDefault();
1185
+ setActiveIndex(Math.max(0, setting.options.length - 1));
1186
+ } else if ((event.key === "Enter" || event.key === " ") && open) {
1187
+ event.preventDefault();
1188
+ choose(activeIndex);
1189
+ } else if (event.key === "Escape" && open) {
1190
+ event.preventDefault();
1191
+ setOpen(false);
1192
+ }
1193
+ },
1194
+ children: [/* @__PURE__ */ jsx("span", {
1195
+ style: settingSelectValue,
1196
+ children: selectedOption?.label ?? setting.value
1197
+ }), /* @__PURE__ */ jsx("span", {
1198
+ "aria-hidden": "true",
1199
+ style: {
1200
+ ...settingSelectChevron,
1201
+ ...open ? settingSelectChevronOpen : {}
1202
+ },
1203
+ children: "⌄"
1204
+ })]
1205
+ }), open ? /* @__PURE__ */ jsx("div", {
1206
+ id: listboxId,
1207
+ role: "listbox",
1208
+ "aria-activedescendant": `${listboxId}-${activeIndex}`,
1209
+ style: settingSelectMenu,
1210
+ children: setting.options.map((option, index) => {
1211
+ const selected = option.value === setting.value;
1212
+ const active = index === activeIndex;
1213
+ return /* @__PURE__ */ jsxs("button", {
1214
+ id: `${listboxId}-${index}`,
1215
+ type: "button",
1216
+ role: "option",
1217
+ "aria-selected": selected,
1218
+ style: {
1219
+ ...settingSelectOption,
1220
+ ...active ? settingSelectOptionActive : {}
1221
+ },
1222
+ onMouseEnter: () => setActiveIndex(index),
1223
+ onMouseDown: (event) => event.preventDefault(),
1224
+ onClick: () => choose(index),
1225
+ children: [/* @__PURE__ */ jsx("span", {
1226
+ style: settingSelectCheck,
1227
+ "aria-hidden": "true",
1228
+ children: selected ? "✓" : ""
1229
+ }), /* @__PURE__ */ jsx("span", { children: option.label })]
1230
+ }, `${option.source}:${option.value}`);
1231
+ })
1232
+ }) : null]
1233
+ });
1234
+ }
1022
1235
  function ClaudeCodeSettings({ t }) {
1023
1236
  const [report, setReport] = useState();
1024
1237
  const [error, setError] = useState();
@@ -1026,6 +1239,9 @@ window.__ModuleLoader__.load({
1026
1239
  const [updateStatus, setUpdateStatus] = useState();
1027
1240
  const [updateError, setUpdateError] = useState();
1028
1241
  const [updateBusy, setUpdateBusy] = useState();
1242
+ const [globalSettings, setGlobalSettings] = useState();
1243
+ const [globalSettingsError, setGlobalSettingsError] = useState();
1244
+ const [globalSettingsBusy, setGlobalSettingsBusy] = useState(false);
1029
1245
  const refresh = useCallback(async () => {
1030
1246
  setBusy(true);
1031
1247
  setError(void 0);
@@ -1047,6 +1263,35 @@ window.__ModuleLoader__.load({
1047
1263
  useEffect(() => {
1048
1264
  refresh();
1049
1265
  }, [refresh]);
1266
+ const requestGlobalSettings = useCallback(async (changes) => {
1267
+ setGlobalSettingsBusy(true);
1268
+ setGlobalSettingsError(void 0);
1269
+ try {
1270
+ const response = await fetch(CLAUDE_GLOBAL_SETTINGS_PATH, {
1271
+ method: changes === void 0 ? "GET" : "PATCH",
1272
+ credentials: "same-origin",
1273
+ headers: {
1274
+ accept: "application/json",
1275
+ ...changes === void 0 ? {} : { "content-type": "application/json" }
1276
+ },
1277
+ ...changes === void 0 ? {} : { body: JSON.stringify({ changes }) }
1278
+ });
1279
+ const payload = await response.json();
1280
+ if (!response.ok) {
1281
+ const message = typeof payload === "object" && payload !== null && "error" in payload && typeof payload.error === "string" ? payload.error : `HTTP ${response.status}`;
1282
+ throw new Error(message);
1283
+ }
1284
+ if (!isGlobalSettingsView(payload)) throw new Error("Invalid global settings response");
1285
+ setGlobalSettings(payload);
1286
+ } catch (cause) {
1287
+ setGlobalSettingsError(cause instanceof Error ? cause.message : String(cause));
1288
+ } finally {
1289
+ setGlobalSettingsBusy(false);
1290
+ }
1291
+ }, []);
1292
+ useEffect(() => {
1293
+ requestGlobalSettings();
1294
+ }, [requestGlobalSettings]);
1050
1295
  const requestUpdate = useCallback(async (action) => {
1051
1296
  setUpdateBusy(action);
1052
1297
  setUpdateError(void 0);
@@ -1148,6 +1393,50 @@ window.__ModuleLoader__.load({
1148
1393
  })
1149
1394
  ]
1150
1395
  }),
1396
+ /* @__PURE__ */ jsxs("section", {
1397
+ style: settingsCard,
1398
+ children: [
1399
+ /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("h3", {
1400
+ style: settingsSectionHeading,
1401
+ children: t("globalSettings")
1402
+ }), /* @__PURE__ */ jsx("p", {
1403
+ style: settingsBody,
1404
+ children: t("globalSettingsBody")
1405
+ })] }),
1406
+ globalSettings === void 0 ? /* @__PURE__ */ jsx("p", {
1407
+ style: notice,
1408
+ children: t("globalSettingsLoading")
1409
+ }) : globalSettings.settings.map((setting) => /* @__PURE__ */ jsxs("div", {
1410
+ style: diagnosticGrid,
1411
+ children: [/* @__PURE__ */ jsx("span", {
1412
+ style: diagnosticLabel,
1413
+ children: setting.key === "outputStyle" ? t("outputStyle") : setting.key
1414
+ }), /* @__PURE__ */ jsx(GlobalSettingSelect, {
1415
+ setting,
1416
+ disabled: globalSettingsBusy,
1417
+ onChange: (nextValue) => {
1418
+ requestGlobalSettings({ [setting.key]: nextValue });
1419
+ }
1420
+ })]
1421
+ }, setting.key)),
1422
+ globalSettings?.settings.some((setting) => setting.effect === "new-session") === true ? /* @__PURE__ */ jsx("p", {
1423
+ style: notice,
1424
+ children: t("globalSettingsNewSession")
1425
+ }) : null,
1426
+ globalSettingsError === void 0 ? null : /* @__PURE__ */ jsxs("p", {
1427
+ role: "alert",
1428
+ style: {
1429
+ ...notice,
1430
+ color: "var(--dsw-alias-state-error-primary)"
1431
+ },
1432
+ children: [
1433
+ t("globalSettingsError"),
1434
+ ": ",
1435
+ globalSettingsError
1436
+ ]
1437
+ })
1438
+ ]
1439
+ }),
1151
1440
  /* @__PURE__ */ jsxs("section", {
1152
1441
  style: settingsCard,
1153
1442
  children: [
@@ -1392,6 +1681,12 @@ window.__ModuleLoader__.load({
1392
1681
  error: "诊断失败",
1393
1682
  security: "权限边界",
1394
1683
  securityBody: "Claude 工具权限通过 DSH 审批界面决定;当前版本不宣称对 ~/.claude 与工作区之外路径提供内核级写入隔离。",
1684
+ globalSettings: "Claude Code 全局设置",
1685
+ globalSettingsBody: "修改受支持的 ~/.claude/settings.json 设置;其他字段会被保留。",
1686
+ globalSettingsLoading: "正在加载全局设置…",
1687
+ globalSettingsNewSession: "Output Style 修改仅对新建 Claude 会话生效。",
1688
+ globalSettingsError: "全局设置保存失败",
1689
+ outputStyle: "Output Style",
1395
1690
  pluginUpdate: "插件更新",
1396
1691
  pluginUpdateBody: "检查 npm 上的新版本。只有可唯一识别的 npm 安装才能直接更新;本地开发链接不会被替换。",
1397
1692
  updateNotChecked: "尚未检查更新。",
@@ -1470,6 +1765,12 @@ window.__ModuleLoader__.load({
1470
1765
  error: "Doctor failed",
1471
1766
  security: "Permission boundary",
1472
1767
  securityBody: "Claude tool permissions are decided through the DSH approval UI. This version does not claim kernel-level write isolation for ~/.claude or paths outside the workspace.",
1768
+ globalSettings: "Claude Code global settings",
1769
+ globalSettingsBody: "Modify supported ~/.claude/settings.json values while preserving every other field.",
1770
+ globalSettingsLoading: "Loading global settings…",
1771
+ globalSettingsNewSession: "Output Style changes apply only to new Claude sessions.",
1772
+ globalSettingsError: "Global settings save failed",
1773
+ outputStyle: "Output Style",
1473
1774
  pluginUpdate: "Plugin updates",
1474
1775
  pluginUpdateBody: "Check npm for new releases. Only a uniquely identified npm installation can update in place; local development links are never replaced.",
1475
1776
  updateNotChecked: "Updates have not been checked yet.",