dsh-superagent-console 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -4,8 +4,31 @@ window.__ModuleLoader__.load({
4
4
  var module = { exports: {} };
5
5
  var exports = module.exports;
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ // \0rolldown/runtime.js
8
+ var __create = Object.create;
9
+ var __defProp = Object.defineProperty;
10
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
11
+ var __getOwnPropNames = Object.getOwnPropertyNames;
12
+ var __getProtoOf = Object.getPrototypeOf;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __copyProps = (to, from, except, desc) => {
15
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
16
+ key = keys[i];
17
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
18
+ get: ((k) => from[k]).bind(null, key),
19
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
20
+ });
21
+ }
22
+ return to;
23
+ };
24
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
25
+ value: mod,
26
+ enumerable: true
27
+ }) : target, mod));
7
28
  let react = require("react");
8
29
  let react_dom = require("react-dom");
30
+ let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
31
+ _deepseek_ai_dsh_client_ui_primitives = __toESM(_deepseek_ai_dsh_client_ui_primitives, 1);
9
32
  let react_jsx_runtime = require("react/jsx-runtime");
10
33
  // src/vendor/navigation.ts
11
34
  /**
@@ -1334,13 +1357,14 @@ window.__ModuleLoader__.load({
1334
1357
  });
1335
1358
  }
1336
1359
  // src/client/plugins-page.tsx
1337
- /**
1338
- * Plugins tab for the official panel, styled after the DSH native plugin
1339
- * manager: a header with refresh and add-plugin actions, an install dialog
1340
- * with guide and examples, classified install failures, an uninstall
1341
- * confirmation, and a restart notice. Backed by the console API
1342
- * (/superagent-console/api/plugins*) and rendered with the panel's own
1343
- * primitives (dt-* classes mirror DSH ui-primitives).
1360
+ /**
1361
+ * Plugins tab for the official panel. The install dialog is rebuilt on the
1362
+ * desktop client's own ui-primitives (required at runtime — kept external by
1363
+ * tsdown, resolved by the ModuleLoader registry exactly like the native
1364
+ * manager does), so the modal shares the native plugin dialog's typography,
1365
+ * colors, focus rings and animation. Page-level dialog rules are ported
1366
+ * verbatim from the native manager's CSS module under sgc-* class names
1367
+ * (widths, gaps, the safety footer, the anchored registry popover).
1344
1368
  *
1345
1369
  * Backend contract (src/plugins.ts): install/remove run
1346
1370
  * dsh plugin --profile <name> add|remove server-side (single-flight,
@@ -1375,26 +1399,75 @@ window.__ModuleLoader__.load({
1375
1399
  if (/\bENOTFOUND\b|\bECONNRESET\b|\bETIMEDOUT\b|\bECONNREFUSED\b|\bEAI_AGAIN\b|FETCH_ERROR|Could not resolve/i.test(log)) return "network";
1376
1400
  return "generic";
1377
1401
  }
1378
- const GUIDE_EXAMPLES = [
1402
+ const GUIDE_ITEMS = [
1379
1403
  {
1380
1404
  key: "Npm",
1381
- example: "dsh-plugin-whale-pet"
1405
+ example: "dsh-plugin-whale-pet",
1406
+ titleKey: "plugins.installGuideNpm",
1407
+ hintKey: "plugins.installGuideNpmHint"
1382
1408
  },
1383
1409
  {
1384
1410
  key: "Git",
1385
- example: "github:owner/dsh-plugin-example"
1411
+ example: "github:owner/dsh-plugin-example",
1412
+ titleKey: "plugins.installGuideGit",
1413
+ hintKey: "plugins.installGuideGitHint"
1386
1414
  },
1387
1415
  {
1388
1416
  key: "Path",
1389
- example: "/Users/me/MyCode/dsh-plugin-example"
1417
+ example: "/Users/me/MyCode/dsh-plugin-example",
1418
+ titleKey: "plugins.installGuidePath",
1419
+ hintKey: "plugins.installGuidePathHint"
1390
1420
  }
1391
1421
  ];
1392
- function GuideText({ kind }) {
1393
- const { t } = usePanel();
1394
- if (kind === "Npm") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: t("plugins.installGuideNpm") });
1395
- if (kind === "Git") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: t("plugins.installGuideGit") });
1396
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: t("plugins.installGuidePath") });
1397
- }
1422
+ /**
1423
+ * Dialog rules ported from the native manager's PluginManagerPage CSS module
1424
+ * (values verbatim; class names renamed to the sgc- prefix so they cannot
1425
+ * collide with either the native or the engine-panel classes).
1426
+ */
1427
+ const SGC_CSS = `
1428
+ .sgc-installDialog{width:min(560px,100%);max-height:min(800px,100%)}
1429
+ .sgc-installContent{min-height:0;overflow-y:auto}
1430
+ .sgc-installBody{flex-direction:column;gap:12px;min-width:0;display:flex}
1431
+ .sgc-installField{flex-direction:column;gap:6px;font-size:13px;display:flex}
1432
+ .sgc-installField input[type=text]{border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-3);height:40px;font:inherit;color:var(--dsw-alias-label-primary);outline:none;padding:0 14px;font-size:13px}
1433
+ .sgc-installField input[type=text]:focus,.sgc-registryCustomField:focus{border-color:var(--dsw-alias-state-business-primary);box-shadow:inset 0 0 0 .5px var(--dsw-alias-state-business-primary)}
1434
+ .sgc-optionsRow{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:8px 12px;display:flex}
1435
+ .sgc-guideToggle{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;align-self:flex-start;align-items:center;gap:4px;margin-top:4px;padding:0;font-size:12.5px;display:inline-flex}
1436
+ .sgc-guideToggle:hover{color:var(--dsw-alias-label-primary)}
1437
+ .sgc-guideChevron{transition:transform .16s}
1438
+ .sgc-guideToggle[aria-expanded=true] .sgc-guideChevron,.sgc-registryToggle[aria-expanded=true] .sgc-guideChevron{transform:rotate(180deg)}
1439
+ .sgc-guide{border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-lg);background:var(--dsw-alias-bg-layer-1);flex-direction:column;gap:10px;padding:8px 14px;display:flex}
1440
+ .sgc-guideList{flex-direction:column;gap:2px;margin:0;padding:0;list-style:none;display:flex}
1441
+ .sgc-guideItem{align-items:flex-start;gap:10px;padding:8px 0;display:flex}
1442
+ .sgc-guideItem>button{align-self:center}
1443
+ .sgc-guideMain{flex-direction:column;flex:1;gap:0;min-width:0;display:flex}
1444
+ .sgc-guideTitle{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500;line-height:20px}
1445
+ .sgc-guideHint{color:var(--dsw-alias-label-tertiary);margin:6px 0 0;font-size:12px;line-height:18px}
1446
+ .sgc-guideExample{color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere;font-size:12px;line-height:20px}
1447
+ .sgc-guideExample code{font-family:var(--ds-font-family-code)}
1448
+ .sgc-guideExampleLabel{color:var(--dsw-alias-label-tertiary)}
1449
+ .sgc-registryToggle{border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-2);height:28px;font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;align-items:center;gap:6px;padding:0 8px 0 10px;font-size:12.5px;display:inline-flex}
1450
+ .sgc-registryToggle:hover:not(:disabled){color:var(--dsw-alias-label-primary)}
1451
+ .sgc-registryToggle:disabled{cursor:default;opacity:.6}
1452
+ .sgc-registryToggle[aria-expanded=true]{border-color:var(--dsw-alias-border-l3);background:var(--dsw-alias-bg-layer-3)}
1453
+ .sgc-registryChosen{color:var(--dsw-alias-label-primary);font-weight:500}
1454
+ .sgc-registry{z-index:1100;box-sizing:border-box;background:var(--dsw-alias-bg-layer-2);border-radius:var(--dsw-radius-lg);width:min(440px,calc(100vw - 24px));box-shadow:var(--dsw-elevation-prominent);border:1px solid var(--dsw-alias-border-l1);flex-direction:column;gap:4px;min-width:0;margin:0;padding:8px;display:flex;position:fixed}
1455
+ .sgc-registryOption{border-radius:var(--dsw-radius-md);cursor:pointer;border:0;flex-direction:column;gap:4px;padding:8px 10px;display:flex}
1456
+ .sgc-registryOption:hover{background:var(--dsw-alias-interactive-bg-hover)}
1457
+ label.sgc-registryOption{flex-direction:row;align-items:flex-start;gap:10px}
1458
+ .sgc-registryOption input[type=radio]{width:16px;height:16px;accent-color:var(--dsw-alias-brand-primary);flex:none;margin:2px 0 0}
1459
+ .sgc-registryTitle{color:var(--dsw-alias-label-primary);flex-wrap:wrap;align-items:center;gap:8px;font-size:13px;line-height:20px;display:flex}
1460
+ .sgc-registryHint{color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere;font-size:12px;line-height:18px}
1461
+ .sgc-registryCustomField{border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-3);height:32px;font:inherit;color:var(--dsw-alias-label-primary);outline:none;margin-left:26px;padding:0 10px;font-size:13px}
1462
+ .sgc-registryCustomError{margin:0 0 0 26px}
1463
+ .sgc-installFooter{flex-direction:column;flex:1;gap:20px;min-width:0;display:flex}
1464
+ .sgc-installSafety{border-radius:var(--dsw-radius-md);background:var(--dsw-alias-state-warn-tertiary);color:var(--dsw-alias-label-secondary);align-items:flex-start;gap:8px;margin:0;padding:10px 12px;font-size:12px;line-height:18px;display:flex}
1465
+ .sgc-installSafety>svg{color:var(--dsw-alias-state-warn-primary);flex:none;margin-top:2px}
1466
+ .sgc-installSafetyText{flex-direction:column;gap:4px;min-width:0;display:flex}
1467
+ .sgc-wide{border-radius:var(--dsw-radius-md);justify-content:center;width:100%;height:40px}
1468
+ .sgc-inputError{color:var(--dsw-alias-state-error-primary);margin:-4px 0 0;font-size:12px;line-height:18px}
1469
+ .sgc-dialogActions{display:flex;justify-content:flex-end;gap:10px}
1470
+ `;
1398
1471
  function OutputBlock({ output }) {
1399
1472
  if (!output) return null;
1400
1473
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
@@ -1406,15 +1479,37 @@ window.__ModuleLoader__.load({
1406
1479
  children: output
1407
1480
  });
1408
1481
  }
1482
+ const REGISTRY_CHOICES = [
1483
+ {
1484
+ value: "",
1485
+ titleKey: "plugins.registryDefault"
1486
+ },
1487
+ {
1488
+ value: "official",
1489
+ titleKey: "plugins.registryOfficial"
1490
+ },
1491
+ {
1492
+ value: "npmmirror",
1493
+ titleKey: "plugins.registryNpmmirror"
1494
+ },
1495
+ {
1496
+ value: "custom",
1497
+ titleKey: "plugins.registryCustom"
1498
+ }
1499
+ ];
1409
1500
  function InstallModal({ open, onClose, onSettled }) {
1410
1501
  const { t, notify } = usePanel();
1411
1502
  const [spec, setSpec] = (0, react.useState)("");
1412
1503
  const [step, setStep] = (0, react.useState)({ kind: "editing" });
1413
1504
  const [guideOpen, setGuideOpen] = (0, react.useState)(false);
1414
- const [detailsOpen, setDetailsOpen] = (0, react.useState)(false);
1415
- const [lastOp, setLastOp] = (0, react.useState)(null);
1505
+ const [registryOpen, setRegistryOpen] = (0, react.useState)(false);
1416
1506
  const [registry, setRegistry] = (0, react.useState)("");
1417
1507
  const [customRegistry, setCustomRegistry] = (0, react.useState)("");
1508
+ const [customInvalid, setCustomInvalid] = (0, react.useState)(false);
1509
+ const [detailsOpen, setDetailsOpen] = (0, react.useState)(false);
1510
+ const [lastOp, setLastOp] = (0, react.useState)(null);
1511
+ const registryToggleRef = (0, react.useRef)(null);
1512
+ const [registryPos, setRegistryPos] = (0, react.useState)(null);
1418
1513
  const settled = (0, react.useRef)(onSettled);
1419
1514
  settled.current = onSettled;
1420
1515
  (0, react.useEffect)(() => {
@@ -1432,26 +1527,79 @@ window.__ModuleLoader__.load({
1432
1527
  };
1433
1528
  const timer = setInterval(() => {
1434
1529
  tick();
1435
- }, 1500);
1530
+ }, 800);
1436
1531
  tick();
1437
1532
  return () => {
1438
1533
  active = false;
1439
1534
  clearInterval(timer);
1440
1535
  };
1441
1536
  }, [step.kind]);
1537
+ (0, react.useEffect)(() => {
1538
+ if (!registryOpen) return;
1539
+ const place = () => {
1540
+ const el = registryToggleRef.current;
1541
+ if (!el) return;
1542
+ const rect = el.getBoundingClientRect();
1543
+ const width = Math.min(440, window.innerWidth - 24);
1544
+ const left = Math.max(12, Math.min(rect.left, window.innerWidth - width - 12));
1545
+ const top = Math.min(rect.bottom + 6, window.innerHeight - 200);
1546
+ setRegistryPos({
1547
+ left,
1548
+ top
1549
+ });
1550
+ };
1551
+ place();
1552
+ window.addEventListener("resize", place);
1553
+ window.addEventListener("scroll", place, true);
1554
+ return () => {
1555
+ window.removeEventListener("resize", place);
1556
+ window.removeEventListener("scroll", place, true);
1557
+ };
1558
+ }, [registryOpen]);
1559
+ (0, react.useEffect)(() => {
1560
+ if (!registryOpen) return;
1561
+ const onDoc = (event) => {
1562
+ const target = event.target;
1563
+ if (registryToggleRef.current?.contains(target)) return;
1564
+ if (target instanceof Element && target.closest("[data-sgc-registry]")) return;
1565
+ setRegistryOpen(false);
1566
+ };
1567
+ const onKey = (event) => {
1568
+ if (event.key === "Escape") setRegistryOpen(false);
1569
+ };
1570
+ document.addEventListener("mousedown", onDoc);
1571
+ document.addEventListener("keydown", onKey);
1572
+ return () => {
1573
+ document.removeEventListener("mousedown", onDoc);
1574
+ document.removeEventListener("keydown", onKey);
1575
+ };
1576
+ }, [registryOpen]);
1442
1577
  const run = async () => {
1443
1578
  let chosen = null;
1444
1579
  if (registry === "official") chosen = "https://registry.npmjs.org/";
1445
1580
  else if (registry === "npmmirror") chosen = "https://registry.npmmirror.com/";
1446
1581
  else if (registry === "custom") {
1447
- chosen = customRegistry.trim();
1448
- if (!/^https?:\/\/\S+$/.test(chosen)) {
1449
- notify(t("plugins.registryCustomInvalid"));
1582
+ const url = customRegistry.trim();
1583
+ if (!/^https?:\/\/\S+$/.test(url)) {
1584
+ setCustomInvalid(true);
1450
1585
  return;
1451
1586
  }
1587
+ chosen = url;
1452
1588
  }
1589
+ setRegistryOpen(false);
1453
1590
  setDetailsOpen(false);
1454
1591
  setLastOp(null);
1592
+ setStep({ kind: "checking" });
1593
+ try {
1594
+ const { problems } = await post("/superagent-console/api/plugins/check", { spec: spec.trim() });
1595
+ if (problems.length > 0) {
1596
+ setStep({
1597
+ kind: "refused",
1598
+ problems
1599
+ });
1600
+ return;
1601
+ }
1602
+ } catch {}
1455
1603
  setStep({ kind: "running" });
1456
1604
  try {
1457
1605
  await post("/superagent-console/api/plugins/install", {
@@ -1463,224 +1611,283 @@ window.__ModuleLoader__.load({
1463
1611
  setStep({ kind: "editing" });
1464
1612
  }
1465
1613
  };
1466
- const submit = (event) => {
1467
- event.preventDefault();
1468
- if (spec.trim() && step.kind === "editing") run();
1614
+ /** 立即启用: mount every bundle the add introduced, then surface the restart notice. */
1615
+ const enableNow = async () => {
1616
+ const bundles = lastOp?.installed?.filter((item) => item.bundle) ?? [];
1617
+ for (const item of bundles) try {
1618
+ await post("/superagent-console/api/plugins/enable", {
1619
+ name: item.name,
1620
+ enabled: true
1621
+ });
1622
+ } catch (failure) {
1623
+ notify(errorText(failure));
1624
+ return;
1625
+ }
1626
+ settled.current(true);
1627
+ onClose();
1469
1628
  };
1470
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Modal, {
1629
+ const editing = step.kind === "editing";
1630
+ const enableTargets = step.kind === "done" && (lastOp?.installed?.some((item) => item.bundle) ?? false);
1631
+ const chosenTitle = t((REGISTRY_CHOICES.find((choice) => choice.value === registry) ?? REGISTRY_CHOICES[0]).titleKey);
1632
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: SGC_CSS }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
1471
1633
  open,
1472
1634
  onClose,
1473
- className: "dt-plugins-modal",
1635
+ className: "sgc-installDialog",
1636
+ contentClassName: "sgc-installContent",
1474
1637
  title: step.kind === "running" ? t("plugins.installingTitle") : step.kind === "failed" ? t("plugins.installFailedTitle") : t("plugins.installTitle"),
1475
1638
  closeLabel: t("plugins.installClose"),
1476
- description: step.kind === "editing" ? t("plugins.installDescription") : void 0,
1477
- footer: step.kind === "editing" ? void 0 : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
1478
- step.kind === "running" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button$1, {
1479
- variant: "ghost",
1480
- onClick: onClose,
1481
- children: t("plugins.installClose")
1482
- }),
1483
- step.kind === "failed" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button$1, {
1484
- variant: "ghost",
1485
- onClick: onClose,
1486
- children: t("plugins.installClose")
1487
- }),
1488
- step.kind === "failed" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button$1, {
1639
+ description: editing ? t("plugins.installDescription") : void 0,
1640
+ footer: editing ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1641
+ className: "sgc-installFooter",
1642
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
1643
+ className: "sgc-installSafety",
1644
+ role: "note",
1645
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutlineRegular, {
1646
+ size: 14,
1647
+ "aria-hidden": "true"
1648
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1649
+ className: "sgc-installSafetyText",
1650
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("plugins.installGuideSafety") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("plugins.installUpgradeNotice") })]
1651
+ })]
1652
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1489
1653
  variant: "primary",
1654
+ className: "sgc-wide",
1655
+ disabled: !spec.trim(),
1490
1656
  onClick: () => {
1491
1657
  run();
1492
1658
  },
1493
- children: t("plugins.installRetry")
1494
- }),
1495
- step.kind === "done" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button$1, {
1496
- variant: "primary",
1497
- onClick: onClose,
1498
- children: t("plugins.installClose")
1499
- })
1500
- ] }),
1659
+ children: t("plugins.installRun")
1660
+ })]
1661
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1662
+ className: "sgc-dialogActions",
1663
+ children: [
1664
+ (step.kind === "running" || step.kind === "failed") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1665
+ variant: "ghost",
1666
+ onClick: onClose,
1667
+ children: t("plugins.installClose")
1668
+ }),
1669
+ step.kind === "refused" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1670
+ variant: "primary",
1671
+ onClick: () => setStep({ kind: "editing" }),
1672
+ children: t("plugins.installEdit")
1673
+ }),
1674
+ step.kind === "failed" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1675
+ variant: "ghost",
1676
+ onClick: () => setStep({ kind: "editing" }),
1677
+ children: t("plugins.installChangeRegistry")
1678
+ }),
1679
+ step.kind === "failed" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1680
+ variant: "primary",
1681
+ onClick: () => {
1682
+ run();
1683
+ },
1684
+ children: t("plugins.installRetry")
1685
+ }),
1686
+ step.kind === "done" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1687
+ variant: "ghost",
1688
+ onClick: onClose,
1689
+ children: t("plugins.installClose")
1690
+ }),
1691
+ step.kind === "done" && enableTargets && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1692
+ variant: "primary",
1693
+ onClick: () => {
1694
+ enableNow();
1695
+ },
1696
+ children: t("plugins.installEnableNow")
1697
+ })
1698
+ ]
1699
+ }),
1501
1700
  children: [
1502
- step.kind === "editing" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
1503
- onSubmit: submit,
1701
+ editing && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1702
+ className: "sgc-installBody",
1504
1703
  children: [
1505
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Input, {
1506
- type: "text",
1507
- value: spec,
1508
- placeholder: t("plugins.installSpecPlaceholder"),
1509
- "aria-label": t("plugins.installSpecLabel"),
1510
- style: { width: "100%" },
1511
- onChange: (event) => {
1512
- setSpec(event.target.value);
1513
- }
1704
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1705
+ className: "sgc-installField",
1706
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1707
+ type: "text",
1708
+ autoFocus: true,
1709
+ "data-modal-autofocus": true,
1710
+ value: spec,
1711
+ placeholder: t("plugins.installSpecPlaceholder"),
1712
+ "aria-label": t("plugins.installSpecLabel"),
1713
+ onChange: (event) => {
1714
+ setSpec(event.target.value);
1715
+ },
1716
+ onKeyDown: (event) => {
1717
+ if (event.nativeEvent.isComposing) return;
1718
+ if (event.key === "Enter" && spec.trim()) {
1719
+ event.preventDefault();
1720
+ run();
1721
+ }
1722
+ }
1723
+ })
1514
1724
  }),
1515
1725
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1516
- style: {
1517
- display: "flex",
1518
- alignItems: "center",
1519
- justifyContent: "space-between",
1520
- gap: 12,
1521
- marginTop: 12
1522
- },
1726
+ className: "sgc-optionsRow",
1523
1727
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1524
1728
  type: "button",
1525
- className: "dt-textlink",
1729
+ className: "sgc-guideToggle",
1526
1730
  "aria-expanded": guideOpen,
1527
- style: {
1528
- display: "inline-flex",
1529
- alignItems: "center",
1530
- gap: 4
1531
- },
1532
1731
  onClick: () => setGuideOpen(!guideOpen),
1533
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {
1534
- name: guideOpen ? "chevron-up" : "chevron-down",
1535
- size: 14
1536
- }), guideOpen ? t("plugins.installGuideHide") : t("plugins.installGuideToggle")]
1537
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1538
- style: {
1539
- display: "inline-flex",
1540
- alignItems: "center",
1541
- gap: 6,
1542
- border: "0.5px solid var(--dsw-alias-border-l4)",
1543
- borderRadius: 8,
1544
- padding: "0 10px",
1545
- height: 34
1546
- },
1547
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1548
- style: {
1549
- fontSize: 13,
1550
- opacity: .7,
1551
- whiteSpace: "nowrap"
1552
- },
1553
- children: t("plugins.registryTitle")
1554
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
1555
- className: "dt-select",
1556
- style: {
1557
- border: "none",
1558
- background: "transparent",
1559
- width: "auto",
1560
- height: "auto",
1561
- padding: 0
1562
- },
1563
- "aria-label": t("plugins.registryTitle"),
1564
- value: registry,
1565
- onChange: (event) => setRegistry(event.target.value),
1566
- children: [
1567
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1568
- value: "",
1569
- children: t("plugins.registryDefault")
1570
- }),
1571
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1572
- value: "official",
1573
- children: t("plugins.registryOfficial")
1574
- }),
1575
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1576
- value: "npmmirror",
1577
- children: t("plugins.registryNpmmirror")
1578
- }),
1579
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1580
- value: "custom",
1581
- children: t("plugins.registryCustom")
1582
- })
1583
- ]
1584
- })]
1732
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, {
1733
+ className: "sgc-guideChevron",
1734
+ size: 13,
1735
+ "aria-hidden": "true"
1736
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: guideOpen ? t("plugins.installGuideHide") : t("plugins.installGuideToggle") })]
1737
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1738
+ ref: registryToggleRef,
1739
+ type: "button",
1740
+ className: "sgc-registryToggle",
1741
+ "aria-expanded": registryOpen,
1742
+ "aria-haspopup": "dialog",
1743
+ onClick: () => setRegistryOpen(!registryOpen),
1744
+ children: [
1745
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("plugins.registryTitle") }),
1746
+ " ",
1747
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1748
+ className: "sgc-registryChosen",
1749
+ children: chosenTitle
1750
+ }),
1751
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, {
1752
+ className: "sgc-guideChevron",
1753
+ size: 12,
1754
+ "aria-hidden": "true"
1755
+ })
1756
+ ]
1585
1757
  })]
1586
1758
  }),
1587
- registry === "custom" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1588
- style: { marginTop: 10 },
1589
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Input, {
1759
+ guideOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1760
+ className: "sgc-guide",
1761
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ol", {
1762
+ className: "sgc-guideList",
1763
+ children: GUIDE_ITEMS.map(({ key, example, titleKey, hintKey }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
1764
+ className: "sgc-guideItem",
1765
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1766
+ className: "sgc-guideMain",
1767
+ children: [
1768
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1769
+ className: "sgc-guideTitle",
1770
+ children: t(titleKey)
1771
+ }),
1772
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1773
+ className: "sgc-guideHint",
1774
+ children: t(hintKey)
1775
+ }),
1776
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1777
+ className: "sgc-guideExample",
1778
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1779
+ className: "sgc-guideExampleLabel",
1780
+ children: t("plugins.installGuideExampleLabel")
1781
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: example })]
1782
+ })
1783
+ ]
1784
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1785
+ variant: "outline",
1786
+ size: "sm",
1787
+ "aria-label": t("plugins.fillExample", { example }),
1788
+ onClick: () => setSpec(example),
1789
+ children: t("plugins.installGuideFill")
1790
+ })]
1791
+ }, key))
1792
+ })
1793
+ }),
1794
+ registryOpen && (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
1795
+ className: "sgc-registry",
1796
+ "data-sgc-registry": true,
1797
+ "aria-label": t("plugins.registryLegend"),
1798
+ style: registryPos ?? {
1799
+ visibility: "hidden",
1800
+ left: 0,
1801
+ top: 0
1802
+ },
1803
+ children: [REGISTRY_CHOICES.map(({ value, titleKey }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1804
+ className: "sgc-registryOption",
1805
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1806
+ type: "radio",
1807
+ name: "sgc-registry-choice",
1808
+ checked: registry === value,
1809
+ onChange: () => {
1810
+ setRegistry(value);
1811
+ if (value !== "custom") setCustomInvalid(false);
1812
+ }
1813
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1814
+ className: "sgc-registryTitle",
1815
+ children: t(titleKey)
1816
+ })]
1817
+ }, value || "default")), registry === "custom" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1590
1818
  type: "text",
1819
+ className: "sgc-registryCustomField",
1591
1820
  value: customRegistry,
1592
1821
  placeholder: t("plugins.registryCustomPlaceholder"),
1822
+ "aria-invalid": customInvalid || void 0,
1593
1823
  "aria-label": t("plugins.registryCustom"),
1594
- style: { width: "100%" },
1595
1824
  onChange: (event) => {
1596
1825
  setCustomRegistry(event.target.value);
1826
+ setCustomInvalid(false);
1597
1827
  }
1598
1828
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1599
- className: "dt-hint",
1600
- children: t("plugins.registryCustomHint")
1601
- })]
1602
- }),
1603
- guideOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1604
- className: "dt-disclosure-body",
1605
- style: { marginTop: 12 },
1606
- children: GUIDE_EXAMPLES.map(({ key, example }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1607
- className: "dt-row",
1608
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, { state: "idle" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1609
- className: "dt-row-main",
1610
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1611
- className: "dt-row-title dt-line-text",
1612
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GuideText, { kind: key })
1613
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1614
- className: "dt-row-meta",
1615
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
1616
- className: "dt-code",
1617
- children: example
1618
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1619
- type: "button",
1620
- className: "dt-textlink",
1621
- "aria-label": t("plugins.fillExample", { example }),
1622
- onClick: () => setSpec(example),
1623
- children: t("plugins.fill")
1624
- })]
1625
- })]
1626
- })]
1627
- }, key))
1628
- }),
1629
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1630
- className: "dt-banner",
1631
- style: { marginTop: 12 },
1632
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1633
- style: {
1634
- display: "inline-flex",
1635
- alignItems: "flex-start",
1636
- gap: 6
1637
- },
1638
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {
1639
- name: "alert",
1640
- size: 14
1641
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("plugins.installGuideSafety") })]
1642
- })
1643
- }),
1644
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button$1, {
1645
- variant: "primary",
1646
- style: {
1647
- width: "100%",
1648
- marginTop: 14,
1649
- display: "flex",
1650
- alignItems: "center",
1651
- justifyContent: "center"
1652
- },
1653
- disabled: !spec.trim(),
1654
- onClick: () => {
1655
- run();
1656
- },
1657
- children: t("plugins.installRun")
1658
- })
1829
+ className: customInvalid ? "sgc-inputError sgc-registryCustomError" : "sgc-registryHint sgc-registryCustomError",
1830
+ children: customInvalid ? t("plugins.registryCustomInvalid") : t("plugins.registryCustomHint")
1831
+ })] })]
1832
+ }), document.body)
1659
1833
  ]
1660
1834
  }),
1835
+ step.kind === "checking" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1836
+ className: "dt-row",
1837
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, { state: "ongoing" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1838
+ className: "dt-row-main",
1839
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1840
+ className: "dt-row-title",
1841
+ children: t("plugins.installChecking")
1842
+ })
1843
+ })]
1844
+ }),
1845
+ step.kind === "refused" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1846
+ className: "dt-row",
1847
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, { state: "error" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1848
+ className: "dt-row-main",
1849
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1850
+ className: "dt-row-title",
1851
+ children: t("plugins.installConfirmedRefused")
1852
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1853
+ className: "dt-row-meta",
1854
+ children: step.problems.map((problem, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
1855
+ problem.kind === "invalid" && t("plugins.installProblemInvalid", { reason: problem.detail ?? "" }),
1856
+ problem.kind === "installed" && t("plugins.installProblemInstalled"),
1857
+ problem.kind === "links" && t("plugins.problemLinks")
1858
+ ] }, index))
1859
+ })]
1860
+ })]
1861
+ }) }),
1661
1862
  step.kind === "running" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1662
1863
  className: "dt-row",
1663
1864
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, { state: "ongoing" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1664
1865
  className: "dt-row-main",
1665
1866
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1666
1867
  className: "dt-row-title",
1667
- children: t("plugins.installingTitle")
1868
+ children: (lastOp?.output ?? "") === "" ? t("plugins.installStarting") : t("plugins.installingTitle")
1668
1869
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1669
1870
  className: "dt-row-meta",
1670
1871
  children: t("plugins.installBackground")
1671
1872
  })]
1672
1873
  })]
1673
1874
  }), lastOp?.output && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OutputBlock, { output: lastOp.output })] }),
1674
- step.kind === "done" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1875
+ step.kind === "done" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1675
1876
  className: "dt-row",
1676
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, { state: "done" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1877
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, { state: "done" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1677
1878
  className: "dt-row-main",
1678
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1879
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1679
1880
  className: "dt-row-title",
1680
- children: t("plugins.installDoneRestart")
1681
- })
1881
+ children: t("plugins.installedTitle")
1882
+ }), (lastOp?.installed?.length ?? 0) > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1883
+ className: "dt-row-meta",
1884
+ children: lastOp.installed.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [item.name, item.version ? " · " + t("plugins.installVersion", { version: item.version }) : ""] }, item.name))
1885
+ })]
1682
1886
  })]
1683
- }),
1887
+ }), lastOp?.profileDir && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1888
+ className: "dt-row-meta",
1889
+ children: t("plugins.installLocation", { dir: lastOp.profileDir })
1890
+ })] }),
1684
1891
  step.kind === "failed" && lastOp && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1685
1892
  className: "dt-row",
1686
1893
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, { state: "error" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -1705,7 +1912,7 @@ window.__ModuleLoader__.load({
1705
1912
  })]
1706
1913
  }) })
1707
1914
  ]
1708
- });
1915
+ })] });
1709
1916
  }
1710
1917
  function PluginItem({ row, busy, onUninstall, onToggle }) {
1711
1918
  const { t } = usePanel();
@@ -1810,7 +2017,6 @@ window.__ModuleLoader__.load({
1810
2017
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1811
2018
  className: "dt-form",
1812
2019
  children: [
1813
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: ".dt-plugins-modal.dt-modal { width: min(680px, 100%); }" }),
1814
2020
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1815
2021
  className: "dt-status-head",
1816
2022
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h2", {
@@ -4895,7 +5101,7 @@ window.__ModuleLoader__.load({
4895
5101
  "plugins.uninstallTitle": ["卸载「{name}」?", "Uninstall \"{name}\"?"],
4896
5102
  "plugins.uninstallDescription": ["卸载后它提供的功能会消失,重启引擎后生效。", "Its features disappear; the change applies when the engine restarts."],
4897
5103
  "plugins.installTitle": ["添加插件", "Add plugin"],
4898
- "plugins.installDescription": ["输入插件的包名、Git 仓库地址或本地目录路径。", "Enter the plugin's package name, Git repository, or local directory path."],
5104
+ "plugins.installDescription": ["输入插件的包名、GitHub 仓库地址或本地目录路径。", "Enter the plugin's package name, GitHub repository address, or local directory path."],
4899
5105
  "plugins.installSpecLabel": ["包名或地址", "Package or address"],
4900
5106
  "plugins.installSpecPlaceholder": ["例如 dsh-plugin-whale-pet", "e.g. dsh-plugin-whale-pet"],
4901
5107
  "plugins.registryTitle": ["安装源", "Registry"],
@@ -4904,16 +5110,22 @@ window.__ModuleLoader__.load({
4904
5110
  "plugins.registryNpmmirror": ["中国大陆镜像源", "China mirror registry"],
4905
5111
  "plugins.registryCustom": ["自定义地址", "Custom URL"],
4906
5112
  "plugins.registryCustomPlaceholder": ["https://npm.example.com/", "https://npm.example.com/"],
4907
- "plugins.registryCustomHint": ["自定义源需要登录时,把凭据放在本机的 ~/.npmrc 里。", "For a registry that requires login, keep its credentials in the local ~/.npmrc."],
5113
+ "plugins.registryCustomHint": ["填写内网或私有 npm 源地址,以 http:// 或 https:// 开头。若为需要登录的源,请把凭据放在本机的 ~/.npmrc 里。", "Enter an internal or private npm registry address starting with http:// or https://. If it requires a login, store the credentials in ~/.npmrc on this machine."],
4908
5114
  "plugins.registryCustomInvalid": ["请输入以 http:// 或 https:// 开头的地址", "Enter an address starting with http:// or https://"],
4909
5115
  "plugins.installGuideToggle": ["插件安装引导和示例", "Install guide and examples"],
4910
5116
  "plugins.installGuideHide": ["收起引导", "Hide guide"],
4911
- "plugins.installGuideNpm": ["npm 包名:插件包名即 npm 包名(如 dsh-xxx 或 @作者/插件名)。", "npm package: the plugin name is its npm name (dsh-xxx or @author/plugin)."],
4912
- "plugins.installGuideGit": ["Git 仓库:github:owner/repo、git URL 或 .git 地址。", "Git repository: github:owner/repo, a git URL, or a .git address."],
4913
- "plugins.installGuidePath": ["本地目录:本机插件 checkout 的绝对路径(以 link 方式安装)。", "Local directory: an absolute path to a local checkout (installed as a link)."],
4914
- "plugins.fill": ["填入", "Fill in"],
5117
+ "plugins.installGuideNpm": ["npm 包名", "npm package name"],
5118
+ "plugins.installGuideNpmHint": ["插件包名即 npm 包名(如 dsh-xxx 或 @作者/插件名)。", "The plugin name is its npm name (dsh-xxx or @author/plugin)."],
5119
+ "plugins.installGuideGit": ["Git 仓库", "Git repository"],
5120
+ "plugins.installGuideGitHint": ["github:owner/repo、git URL 或 .git 地址。", "github:owner/repo, a git URL, or a .git address."],
5121
+ "plugins.installGuidePath": ["本地目录", "Local directory"],
5122
+ "plugins.installGuidePathHint": ["本机插件 checkout 的绝对路径(以 link 方式安装)。", "An absolute path to a local checkout (installed as a link)."],
5123
+ "plugins.installGuideExampleLabel": ["示例:", "Example:"],
5124
+ "plugins.installGuideFill": ["填入示例", "Use example"],
4915
5125
  "plugins.fillExample": ["填入示例 {example}", "Fill in the example {example}"],
4916
- "plugins.installGuideSafety": ["请确认插件来源可信。插件以你的权限在本机运行,来源不明的插件可能损坏引擎,或读取和泄露你的数据。", "Only add plugins you trust. Plugins run locally with your permissions; untrusted plugins can damage the engine or read and leak your data."],
5126
+ "plugins.installGuideSafety": ["请确认插件来源可信。插件在本机以你的权限运行,来源不明的插件可能损坏 DeepSeek Harness,或读取和泄露你的数据。", "Install only plugins you trust: they run with your permissions and can damage DeepSeek Harness or leak your data."],
5127
+ "plugins.installUpgradeNotice": ["按需安装的官方插件会在版本与 DSH 不同时提供“更新”。其他插件请先卸载再安装新版。", "On-demand Official plugins offer Update when their version differs from DSH. To update other plugins, uninstall and reinstall them."],
5128
+ "plugins.registryLegend": ["从哪个 npm 源下载插件", "The npm registry the plugin is downloaded from"],
4917
5129
  "plugins.installRun": ["安装", "Install"],
4918
5130
  "plugins.installingTitle": ["插件安装中…", "Installing plugin…"],
4919
5131
  "plugins.installBackground": ["可以关闭此窗口,安装会在后台继续;进度显示在列表上方的横幅里。", "You can close this dialog; the install continues in the background and its progress shows in the banner above the list."],
@@ -4923,6 +5135,18 @@ window.__ModuleLoader__.load({
4923
5135
  "plugins.installDetailsHide": ["收起安装详情", "Hide install details"],
4924
5136
  "plugins.installRetry": ["重试", "Retry"],
4925
5137
  "plugins.installClose": ["完成", "Done"],
5138
+ "plugins.installChecking": ["正在检查…", "Checking…"],
5139
+ "plugins.installStarting": ["正在准备安装…", "Preparing installation…"],
5140
+ "plugins.installedTitle": ["已安装", "Installed"],
5141
+ "plugins.installEnableNow": ["立即启用", "Enable now"],
5142
+ "plugins.installVersion": ["版本 {version}", "Version {version}"],
5143
+ "plugins.installLocation": ["安装位置:{dir}", "Installs into {dir}"],
5144
+ "plugins.installChangeRegistry": ["更换安装源", "Change registry"],
5145
+ "plugins.installConfirmedRefused": ["无法安装此插件", "This plugin cannot be installed"],
5146
+ "plugins.installProblemInstalled": ["该插件已安装。如需升级,请卸载后重新安装", "This plugin is already installed. To upgrade it, uninstall it and install it again"],
5147
+ "plugins.installProblemInvalid": ["无法识别这个包名或地址:{reason}", "This is not a package name or address that can be installed: {reason}"],
5148
+ "plugins.installEdit": ["编辑", "Edit"],
5149
+ "plugins.problemLinks": ["引擎 profile 的 link: 依赖目标不存在,请先修复后再安装。", "A link dependency of the engine profile points to a missing target; repair it before installing."],
4926
5150
  "plugins.failure.network": ["网络连接失败,请检查网络或代理后重试", "Network failed; check connectivity or proxy, then retry"],
4927
5151
  "plugins.failure.notFound": ["未找到相关插件", "Plugin not found"],
4928
5152
  "plugins.failure.version": ["没有匹配的版本", "No matching version"],
@@ -7852,7 +8076,12 @@ window.__ModuleLoader__.load({
7852
8076
  children: props.label
7853
8077
  });
7854
8078
  }
7855
- function PrereqList({ prereqs, title }) {
8079
+ function PrereqList({ prereqs, title, st }) {
8080
+ const copy = (text) => {
8081
+ const clip = navigator.clipboard;
8082
+ if (!clip) return;
8083
+ clip.writeText(text).catch(() => void 0);
8084
+ };
7856
8085
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7857
8086
  style: { marginTop: 16 },
7858
8087
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -7865,13 +8094,45 @@ window.__ModuleLoader__.load({
7865
8094
  },
7866
8095
  children: prereqs.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
7867
8096
  style: { color: item.ok ? "inherit" : "var(--dsh-danger, #d64545)" },
7868
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
7869
- style: monoStyle,
7870
- children: item.name
7871
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
7872
- style: { opacity: .75 },
7873
- children: [" — ", item.detail]
7874
- })]
8097
+ children: [
8098
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
8099
+ style: monoStyle,
8100
+ children: item.name
8101
+ }),
8102
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
8103
+ style: { opacity: .75 },
8104
+ children: [" — ", item.detail]
8105
+ }),
8106
+ !item.ok && item.hint && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
8107
+ style: {
8108
+ display: "inline-flex",
8109
+ alignItems: "center",
8110
+ gap: 6,
8111
+ marginLeft: 8
8112
+ },
8113
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
8114
+ style: {
8115
+ ...monoStyle,
8116
+ opacity: .85
8117
+ },
8118
+ children: item.hint
8119
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8120
+ type: "button",
8121
+ onClick: () => {
8122
+ copy(item.hint ?? "");
8123
+ },
8124
+ style: {
8125
+ ...buttonStyle,
8126
+ padding: "1px 8px",
8127
+ fontSize: 11,
8128
+ borderRadius: 6,
8129
+ color: "inherit",
8130
+ borderColor: "var(--dsh-border, rgba(128,128,128,.4))"
8131
+ },
8132
+ children: st("copyCommand")
8133
+ })]
8134
+ })
8135
+ ]
7875
8136
  }, item.name))
7876
8137
  })]
7877
8138
  });
@@ -8084,7 +8345,8 @@ window.__ModuleLoader__.load({
8084
8345
  }),
8085
8346
  (status.state === "not-installed" || status.state === "prereq-missing" || installing) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PrereqList, {
8086
8347
  prereqs: status.prereqs,
8087
- title: st("prereqsTitle")
8348
+ title: st("prereqsTitle"),
8349
+ st
8088
8350
  }),
8089
8351
  (installing || status.setup.current || STAGES.some((id) => status.setup.stages[id]?.status !== "pending")) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StageList, {
8090
8352
  setup: status.setup,
@@ -8166,7 +8428,7 @@ window.__ModuleLoader__.load({
8166
8428
  descStopped: "引擎服务处于停止状态,可以一键拉起(tmux 会话由生命周期脚本托管)。",
8167
8429
  descUnhealthy: "引擎进程存在但管理 API 不可达或组件报错,可尝试重启。",
8168
8430
  descNotInstalled: "引擎运行在独立的 DSH profile 中。点击安装后,控制台会在线拉取官方源码、自动构建并部署到独立 profile(npm 上无此包,源码构建是官方唯一安装路径)。",
8169
- descPrereq: "在线安装需要以下工具,请先补齐后再安装(凭据类操作仍需本人完成)。",
8431
+ descPrereq: "在线安装需要以下工具,红项按提示命令补齐后点「重试」(凭据类操作仍需本人完成)。",
8170
8432
  install: "在线安装引擎",
8171
8433
  installResume: "继续安装",
8172
8434
  installing: "正在安装…",
@@ -8176,6 +8438,7 @@ window.__ModuleLoader__.load({
8176
8438
  starting: "正在启动…",
8177
8439
  restarting: "正在重启…",
8178
8440
  prereqsTitle: "前置依赖",
8441
+ copyCommand: "复制",
8179
8442
  stageTitle: "安装进度",
8180
8443
  setupWait: "引擎已启动,正在等待首次配置(Source 设置需在引擎面板完成)。",
8181
8444
  manuallyStopped: "引擎带有手动停止标记,已按需忽略。",
@@ -8196,7 +8459,7 @@ window.__ModuleLoader__.load({
8196
8459
  descStopped: "The engine service is stopped. Start it with one click (the tmux session is managed by the lifecycle script).",
8197
8460
  descUnhealthy: "The engine process exists but the management API is unreachable or a component reports an error. Try a restart.",
8198
8461
  descNotInstalled: "The engine runs in its own dedicated DSH profile. Installing fetches the official source online, builds it, and deploys it into that profile (the package is not published to npm; source build + link is the only official path).",
8199
- descPrereq: "The online installer needs the tools below. Install them first (credential steps stay yours).",
8462
+ descPrereq: "The online installer needs the tools below — fix failing entries with the suggested command, then Retry (credential steps stay yours).",
8200
8463
  install: "Install engine online",
8201
8464
  installResume: "Resume install",
8202
8465
  installing: "Installing…",
@@ -8206,6 +8469,7 @@ window.__ModuleLoader__.load({
8206
8469
  starting: "Starting…",
8207
8470
  restarting: "Restarting…",
8208
8471
  prereqsTitle: "Prerequisites",
8472
+ copyCommand: "Copy",
8209
8473
  stageTitle: "Install progress",
8210
8474
  setupWait: "Engine is up and waiting for first-time setup (Source configuration happens in the engine panel).",
8211
8475
  manuallyStopped: "The engine carries a manual-stop marker; honored on demand.",