@morehao/dsh-skills-console 0.5.1 → 0.5.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/README.en.md +170 -0
- package/README.md +39 -15
- package/client.js +232 -336
- package/lib/core/intro-store.js +1 -1
- package/lib/core/intro.js +24 -18
- package/package.json +2 -1
package/client.js
CHANGED
|
@@ -304,18 +304,18 @@ var DICT = {
|
|
|
304
304
|
"viewer.info.mode": "状态",
|
|
305
305
|
"viewer.info.status": "更新",
|
|
306
306
|
"viewer.info.none": "无",
|
|
307
|
-
"intro.button": "
|
|
308
|
-
"intro.open": "AI
|
|
309
|
-
"intro.title": "AI
|
|
307
|
+
"intro.button": "简介",
|
|
308
|
+
"intro.open": "AI 简介:这个技能能做什么",
|
|
309
|
+
"intro.title": "AI 简介",
|
|
310
310
|
"intro.loading": "读取中…",
|
|
311
|
-
"intro.empty": "
|
|
312
|
-
"intro.generate": "
|
|
311
|
+
"intro.empty": "还没有生成简介",
|
|
312
|
+
"intro.generate": "生成简介",
|
|
313
313
|
"intro.regenerate": "重新生成",
|
|
314
314
|
"intro.generating": "生成中…",
|
|
315
315
|
"intro.hint": "会把 SKILL.md 正文发送给当前配置的模型提供方",
|
|
316
316
|
"intro.hintRemote": "会把该技能在 {source}@{commit} 的 SKILL.md 发送给当前配置的模型提供方",
|
|
317
317
|
"intro.meta": "{model} · {time} · AI 生成,可能不准确",
|
|
318
|
-
"intro.stale": "
|
|
318
|
+
"intro.stale": "技能内容已更新,简介可能过期",
|
|
319
319
|
"intro.view": "查看技能",
|
|
320
320
|
"intro.unavailable": "当前宿主没有可用的模型服务",
|
|
321
321
|
"intro.error.noModel": "还没有配置可用的模型",
|
|
@@ -1040,7 +1040,7 @@ function FolderIcon({ size = 12 }) {
|
|
|
1040
1040
|
}
|
|
1041
1041
|
|
|
1042
1042
|
// client/section.js
|
|
1043
|
-
var
|
|
1043
|
+
var import_react9 = __toESM(require("react"), 1);
|
|
1044
1044
|
|
|
1045
1045
|
// client/scroll.js
|
|
1046
1046
|
function isScroller(node, tolerance = 4) {
|
|
@@ -1134,6 +1134,19 @@ var CSS = `
|
|
|
1134
1134
|
.dshsc-pop-label em { font-style: normal; font-size: 11px; color: var(--dsw-alias-label-tertiary); }
|
|
1135
1135
|
.dshsc-pop-name { display: inline-flex; align-items: baseline; gap: 6px; }
|
|
1136
1136
|
.dshsc-pop-hint { font-size: 11px; line-height: 1.5; color: var(--dsw-alias-label-tertiary); }
|
|
1137
|
+
/* 与 DSH 自带开关同尺寸/同 token:关是 border-l3 轨道,开是 brand-primary 轨道。
|
|
1138
|
+
0.5.0 换三档 chip 时这组规则被一起删掉,但「侧边栏入口」还在用 .dshsc-switch,补回来。 */
|
|
1139
|
+
.dshsc-switch { box-sizing: border-box; position: relative; flex: none; width: 36px; height: 20px; padding: 2px;
|
|
1140
|
+
border: 0; border-radius: 999px; background: var(--dsw-alias-border-l3); cursor: pointer;
|
|
1141
|
+
transition: background .14s ease; }
|
|
1142
|
+
.dshsc-switch:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary); outline-offset: 2px; }
|
|
1143
|
+
.dshsc-switch:disabled { cursor: not-allowed; opacity: .5; }
|
|
1144
|
+
.dshsc-switch[aria-checked="true"] { background: var(--dsw-alias-brand-primary); }
|
|
1145
|
+
.dshsc-knob { display: block; width: 16px; height: 16px; border-radius: 50%;
|
|
1146
|
+
background: var(--dsw-alias-switch-thumb, #fff); box-shadow: 0 1px 2px rgba(0, 0, 0, .28);
|
|
1147
|
+
transition: transform .12s ease, background .12s ease; }
|
|
1148
|
+
.dshsc-switch[aria-checked="true"] .dshsc-knob { transform: translateX(16px);
|
|
1149
|
+
background: var(--dsw-alias-label-primary-foreground, #fff); }
|
|
1137
1150
|
|
|
1138
1151
|
.dshsc-btn { font: inherit; font-size: 12px; line-height: 1; padding: 7px 11px; border-radius: 8px; cursor: pointer;
|
|
1139
1152
|
white-space: nowrap;
|
|
@@ -1357,10 +1370,15 @@ var CSS = `
|
|
|
1357
1370
|
.dshsc-sub-list { display: flex; flex-direction: column; padding: 0 12px 7px 46px;
|
|
1358
1371
|
background: color-mix(in srgb, var(--dsw-alias-bg-layer-2) 22%, transparent);
|
|
1359
1372
|
border-top: 1px solid var(--dsw-alias-border-l1); }
|
|
1373
|
+
/* 条目操作列 92px 是照两枚按钮预留的,眼下每行只有一枚「安装」(两字 ≈ 43px:0 9px padding + 边框)。
|
|
1374
|
+
表头「操作」和已装行的破折号都按按钮宽度做一个右对齐的居中盒子:三者落在同一条中轴上,
|
|
1375
|
+
既不贴着列右缘,也不用改列宽 / 挪按钮。换语言或字号时盒子只是不再严格等于按钮宽度。 */
|
|
1376
|
+
.dshsc-sub-list { --dshsc-entry-act: 44px; }
|
|
1360
1377
|
.dshsc-entryhead { display: grid; grid-template-columns: minmax(0, 1fr) 110px 92px; gap: 10px;
|
|
1361
1378
|
align-items: center; height: 26px; border-bottom: 1px solid var(--dsw-alias-border-l1);
|
|
1362
1379
|
color: var(--dsw-alias-label-tertiary); font-size: 10px; font-weight: 500; letter-spacing: .02em; }
|
|
1363
|
-
.dshsc-entryhead .right {
|
|
1380
|
+
.dshsc-entryhead .right { justify-self: end; box-sizing: border-box; width: var(--dshsc-entry-act);
|
|
1381
|
+
text-align: center; padding-right: 0; }
|
|
1364
1382
|
.dshsc-sub-entry { display: grid; grid-template-columns: minmax(0, 1fr) 110px 92px; gap: 10px;
|
|
1365
1383
|
align-items: center; font-size: 12px; padding: 6px 0; border-top: 1px solid var(--dsw-alias-border-l1); }
|
|
1366
1384
|
.dshsc-entryhead + .dshsc-sub-entry { border-top: 0; }
|
|
@@ -1374,8 +1392,9 @@ var CSS = `
|
|
|
1374
1392
|
.dshsc-state.ok svg { color: var(--dsw-alias-state-success-primary); }
|
|
1375
1393
|
.dshsc-sub-entry .dshsc-actions { justify-self: end; display: flex; justify-content: flex-end; }
|
|
1376
1394
|
.dshsc-sub-entry .dshsc-btn { height: 24px; padding: 0 9px; font-size: 11.5px; }
|
|
1377
|
-
/*
|
|
1378
|
-
.dshsc-sub-entry .dshsc-noop {
|
|
1395
|
+
/* 已装条目的操作列没有动作:破折号占位,居中在按钮宽度里,和表头、安装按钮同一条中轴 */
|
|
1396
|
+
.dshsc-sub-entry .dshsc-noop { box-sizing: border-box; width: var(--dshsc-entry-act); padding: 0;
|
|
1397
|
+
text-align: center; color: var(--dsw-alias-label-tertiary); font-size: 11.5px; line-height: 24px; }
|
|
1379
1398
|
/* 设置页的真实宽度常常远小于窗口(侧栏 + 页面缩放),视口 media query 不可靠;
|
|
1380
1399
|
以 .dshsc-root 自身宽度为准,窄面板时切两列,避免固定列把 Agent 标签挤成竖排。
|
|
1381
1400
|
断点取 860 而不是贴着面板常见的 ~900:四列网格需要 280 + 150 + 操作列 + 3×14 gap + 中间列,
|
|
@@ -1404,7 +1423,7 @@ var CSS = `
|
|
|
1404
1423
|
}
|
|
1405
1424
|
.dshsc-code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11px; }
|
|
1406
1425
|
|
|
1407
|
-
/* 调用策略 chip
|
|
1426
|
+
/* 调用策略 chip:一个入口显示当前模式;点开三档弹窗。窄列里允许省略,不撑破 102px */
|
|
1408
1427
|
.dshsc-chip { box-sizing: border-box; display: inline-flex; align-items: center; gap: 6px; max-width: 100%;
|
|
1409
1428
|
height: 24px; padding: 0 9px; border: 1px solid var(--dsw-alias-border-l2); border-radius: 999px;
|
|
1410
1429
|
background: var(--dsw-alias-bg-layer-1); color: var(--dsw-alias-label-secondary); font-family: inherit;
|
|
@@ -1428,40 +1447,27 @@ var CSS = `
|
|
|
1428
1447
|
.dshsc-chip.legacy { color: var(--dsw-alias-label-tertiary); background: var(--dsw-alias-bg-layer-2); }
|
|
1429
1448
|
.dshsc-chip.muted { color: var(--dsw-alias-label-tertiary); cursor: default; opacity: .8; }
|
|
1430
1449
|
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
.dshsc-
|
|
1437
|
-
.dshsc-
|
|
1438
|
-
.dshsc-policy-sub { margin: 2px 0 0; font-size: 11px; line-height: 16px; color: var(--dsw-alias-label-tertiary); }
|
|
1439
|
-
.dshsc-policy-close { flex: none; width: 24px; height: 24px; margin: -4px -4px 0 0; border: 0; border-radius: 6px;
|
|
1440
|
-
background: transparent; color: var(--dsw-alias-label-secondary); font-size: 18px; line-height: 1; cursor: pointer; }
|
|
1441
|
-
.dshsc-policy-close:hover { background: var(--dsw-alias-bg-layer-2); color: var(--dsw-alias-label-primary); }
|
|
1442
|
-
.dshsc-policy-options { display: flex; flex-direction: column; gap: 6px; }
|
|
1443
|
-
.dshsc-policyopt { box-sizing: border-box; display: flex; align-items: flex-start; gap: 10px; width: 100%;
|
|
1444
|
-
padding: 9px 10px; text-align: left; border: 1px solid var(--dsw-alias-border-l1); border-radius: 9px;
|
|
1445
|
-
background: var(--dsw-alias-bg-layer-1); color: var(--dsw-alias-label-primary); font-family: inherit; cursor: pointer; }
|
|
1446
|
-
.dshsc-policyopt:hover:not(:disabled) { background: var(--dsw-alias-bg-layer-2); border-color: var(--dsw-alias-border-l2); }
|
|
1447
|
-
.dshsc-policyopt.active { border-color: color-mix(in srgb, var(--dsw-alias-brand-primary) 55%, var(--dsw-alias-border-l1));
|
|
1448
|
-
background: color-mix(in srgb, var(--dsw-alias-brand-primary) 7%, transparent); }
|
|
1450
|
+
/* 调用策略:标准居中弹窗(Overlay)里的单选列表;行距 / 圆点尺寸对齐共享设置的 .dshsc-check */
|
|
1451
|
+
.dshsc-policy-options { max-height: none; overflow: visible; }
|
|
1452
|
+
.dshsc-policyopt { box-sizing: border-box; display: flex; align-items: flex-start; gap: 9px; width: 100%;
|
|
1453
|
+
padding: 5px 8px; text-align: left; border: 0; border-radius: 8px;
|
|
1454
|
+
background: transparent; color: var(--dsw-alias-label-primary); font-family: inherit; cursor: pointer; }
|
|
1455
|
+
.dshsc-policyopt:hover:not(:disabled) { background: var(--dsw-alias-bg-layer-2); }
|
|
1456
|
+
.dshsc-policyopt:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary); outline-offset: -2px; }
|
|
1449
1457
|
.dshsc-policyopt:disabled { cursor: not-allowed; opacity: .55; }
|
|
1450
1458
|
.dshsc-policyopt.static { cursor: default; }
|
|
1451
|
-
.dshsc-policyopt
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1459
|
+
.dshsc-policyopt.static:hover { background: transparent; }
|
|
1460
|
+
.dshsc-policyopt-dot { box-sizing: border-box; flex: none; width: 15px; height: 15px; margin-top: 2px;
|
|
1461
|
+
border-radius: 50%; border: 1px solid var(--dsw-alias-border-l2); background: var(--dsw-alias-bg-layer-1); }
|
|
1462
|
+
.dshsc-policyopt.active .dshsc-policyopt-dot { border: 5px solid var(--dsw-alias-brand-primary); }
|
|
1455
1463
|
.dshsc-policyopt-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
|
|
1456
|
-
.dshsc-policyopt-body b { font-size:
|
|
1464
|
+
.dshsc-policyopt-body b { font-size: 13px; font-weight: 600; }
|
|
1457
1465
|
.dshsc-policyopt-body small { font-size: 11px; line-height: 15px; color: var(--dsw-alias-label-tertiary); }
|
|
1458
1466
|
.dshsc-policy-note { padding: 7px 9px; border-radius: 8px; background: var(--dsw-alias-bg-layer-2);
|
|
1459
1467
|
color: var(--dsw-alias-label-secondary); font-size: 11px; line-height: 15px; }
|
|
1460
1468
|
.dshsc-policy-note.warn, .dshsc-policy-note.author { color: var(--dsw-alias-state-warn-primary);
|
|
1461
1469
|
background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 9%, transparent); }
|
|
1462
1470
|
.dshsc-policy-hidden { display: flex; flex-direction: column; gap: 8px; }
|
|
1463
|
-
.dshsc-policy-restore { align-self: flex-start; }
|
|
1464
|
-
.dshsc-policy-foot { display: flex; justify-content: flex-end; gap: 6px; }
|
|
1465
1471
|
.dshsc-policy-confirm { display: flex; flex-direction: column; gap: 6px; padding: 10px; border-radius: 9px;
|
|
1466
1472
|
border: 1px solid color-mix(in srgb, var(--dsw-alias-state-error-primary) 34%, var(--dsw-alias-border-l1));
|
|
1467
1473
|
background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 7%, transparent); }
|
|
@@ -1647,7 +1653,7 @@ var CSS = `
|
|
|
1647
1653
|
.dshsc-info-sec dd { margin: 0; color: var(--dsw-alias-label-primary); overflow-wrap: anywhere; }
|
|
1648
1654
|
.dshsc-info-sec dd.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11px; }
|
|
1649
1655
|
|
|
1650
|
-
/* 列表行的 ✨
|
|
1656
|
+
/* 列表行的 ✨ 简介入口:只占一个小胶囊,不改变行高与列宽 */
|
|
1651
1657
|
.dshsc-intro-btn { flex: none; display: inline-flex; align-items: center; gap: 3px;
|
|
1652
1658
|
height: 19px; padding: 0 7px; border: 1px solid var(--dsw-alias-border-l2); border-radius: 999px;
|
|
1653
1659
|
background: transparent; color: var(--dsw-alias-label-tertiary); font: inherit; font-size: 11px;
|
|
@@ -1657,15 +1663,15 @@ var CSS = `
|
|
|
1657
1663
|
.dshsc-intro-btn:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary); outline-offset: 2px; }
|
|
1658
1664
|
.dshsc-intro-btn .dshsc-spark { font-size: 10px; color: var(--dsw-alias-brand-primary); }
|
|
1659
1665
|
|
|
1660
|
-
/*
|
|
1661
|
-
.dshsc-intro-
|
|
1666
|
+
/* 简介弹窗:和调用策略一样走标准居中弹窗(Overlay),这里只调内容排版 */
|
|
1667
|
+
.dshsc-intro-sub { margin: 0; font-size: 11px; line-height: 16px; color: var(--dsw-alias-label-tertiary);
|
|
1668
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1662
1669
|
.dshsc-intro-text { margin: 0; font-size: 12.5px; line-height: 1.7; color: var(--dsw-alias-label-primary);
|
|
1663
1670
|
white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
1664
1671
|
.dshsc-intro-note { margin: 0; font-size: 12px; line-height: 1.6; color: var(--dsw-alias-label-tertiary); }
|
|
1665
1672
|
.dshsc-intro-meta { margin: 0; font-size: 11px; line-height: 15px; color: var(--dsw-alias-label-tertiary); }
|
|
1666
1673
|
.dshsc-intro-stale { margin: 0; font-size: 11px; line-height: 15px; color: var(--dsw-alias-state-warn-primary); }
|
|
1667
1674
|
.dshsc-intro-error { margin: 0; font-size: 11.5px; line-height: 16px; color: var(--dsw-alias-state-error-primary); }
|
|
1668
|
-
.dshsc-intro-foot { display: flex; align-items: center; justify-content: flex-end; gap: 8px; margin-top: 3px; }
|
|
1669
1675
|
|
|
1670
1676
|
/* 正文排版:只用 token,不写死深浅色 */
|
|
1671
1677
|
.dshsc-md { font-size: 13.5px; line-height: 1.72; color: var(--dsw-alias-label-primary); }
|
|
@@ -2441,10 +2447,35 @@ function Toolbar({
|
|
|
2441
2447
|
}
|
|
2442
2448
|
|
|
2443
2449
|
// client/views/installed.js
|
|
2444
|
-
var
|
|
2450
|
+
var import_react4 = __toESM(require("react"), 1);
|
|
2445
2451
|
|
|
2446
|
-
// client/views/
|
|
2447
|
-
|
|
2452
|
+
// client/views/overlay.js
|
|
2453
|
+
function Overlay({ t, title, children, footer, onCancel }) {
|
|
2454
|
+
useOverlayLayer(true, onCancel);
|
|
2455
|
+
return portalize(h(
|
|
2456
|
+
"div",
|
|
2457
|
+
{ className: "dshsc-overlay", onClick: (event) => {
|
|
2458
|
+
if (event.target === event.currentTarget) onCancel?.();
|
|
2459
|
+
} },
|
|
2460
|
+
h(
|
|
2461
|
+
"div",
|
|
2462
|
+
{ className: "dshsc-modal", role: "dialog", "aria-modal": "true" },
|
|
2463
|
+
h("button", {
|
|
2464
|
+
className: "dshsc-modal-close",
|
|
2465
|
+
"aria-label": t("action.cancel"),
|
|
2466
|
+
onClick: (event) => {
|
|
2467
|
+
event.stopPropagation();
|
|
2468
|
+
onCancel?.();
|
|
2469
|
+
}
|
|
2470
|
+
}, "×"),
|
|
2471
|
+
h("h3", null, title),
|
|
2472
|
+
children,
|
|
2473
|
+
h("div", { className: "dshsc-modal-foot" }, footer)
|
|
2474
|
+
)
|
|
2475
|
+
), overlayHost());
|
|
2476
|
+
}
|
|
2477
|
+
|
|
2478
|
+
// client/views/intro-modal.js
|
|
2448
2479
|
var ERROR_KEY = {
|
|
2449
2480
|
"error.skills.intro-no-model": "intro.error.noModel",
|
|
2450
2481
|
"error.skills.intro-rate-limit": "intro.error.rateLimit",
|
|
@@ -2470,22 +2501,18 @@ function modelLabel(intro) {
|
|
|
2470
2501
|
if (!intro?.model) return "—";
|
|
2471
2502
|
return intro.provider ? `${intro.provider}/${intro.model}` : intro.model;
|
|
2472
2503
|
}
|
|
2473
|
-
function
|
|
2504
|
+
function IntroModal({
|
|
2474
2505
|
t,
|
|
2475
2506
|
name,
|
|
2476
2507
|
stamp = "",
|
|
2477
2508
|
subtitle = null,
|
|
2478
2509
|
endpoint = null,
|
|
2479
2510
|
capability,
|
|
2480
|
-
anchor,
|
|
2481
|
-
anchorEl,
|
|
2482
2511
|
hint = null,
|
|
2483
2512
|
viewLabel = null,
|
|
2484
2513
|
onClose,
|
|
2485
2514
|
onView = null
|
|
2486
2515
|
}) {
|
|
2487
|
-
const ref = import_react3.default.useRef(null);
|
|
2488
|
-
const [pos, setPos] = import_react3.default.useState({ left: 0, top: 0, visibility: "hidden" });
|
|
2489
2516
|
const enabled = capability?.enabled !== false;
|
|
2490
2517
|
const available = capability?.available !== false;
|
|
2491
2518
|
const intro = useSkillIntro({
|
|
@@ -2495,32 +2522,6 @@ function IntroPopover({
|
|
|
2495
2522
|
enabled,
|
|
2496
2523
|
endpoint
|
|
2497
2524
|
});
|
|
2498
|
-
useOverlayLayer(true, onClose);
|
|
2499
|
-
import_react3.default.useLayoutEffect(() => {
|
|
2500
|
-
const node = ref.current;
|
|
2501
|
-
if (!node || !anchor || typeof window === "undefined") return;
|
|
2502
|
-
const rect = node.getBoundingClientRect();
|
|
2503
|
-
const width = rect.width || 360;
|
|
2504
|
-
const height = rect.height || 220;
|
|
2505
|
-
let left = anchor.left;
|
|
2506
|
-
left = Math.max(8, Math.min(left, window.innerWidth - width - 8));
|
|
2507
|
-
let top = anchor.bottom + 8;
|
|
2508
|
-
if (top + height > window.innerHeight - 8) {
|
|
2509
|
-
top = Math.max(8, anchor.top - height - 8);
|
|
2510
|
-
}
|
|
2511
|
-
setPos({ left, top, visibility: "visible" });
|
|
2512
|
-
}, [anchor, intro.loading, intro.intro?.text, intro.generating, intro.genError]);
|
|
2513
|
-
import_react3.default.useEffect(() => {
|
|
2514
|
-
if (typeof document === "undefined") return void 0;
|
|
2515
|
-
const onPointerDown = (event) => {
|
|
2516
|
-
const target = event?.target;
|
|
2517
|
-
if (ref.current?.contains?.(target)) return;
|
|
2518
|
-
if (anchorEl?.contains?.(target)) return;
|
|
2519
|
-
onClose();
|
|
2520
|
-
};
|
|
2521
|
-
document.addEventListener("pointerdown", onPointerDown, true);
|
|
2522
|
-
return () => document.removeEventListener("pointerdown", onPointerDown, true);
|
|
2523
|
-
}, [anchorEl, onClose]);
|
|
2524
2525
|
const failure = intro.genError || intro.error;
|
|
2525
2526
|
const failureKey = failure?.code ? ERROR_KEY[failure.code] || "intro.error.generic" : null;
|
|
2526
2527
|
const meta = intro.intro ? t("intro.meta").replace("{model}", modelLabel(intro.intro)).replace("{time}", formatWhen(intro.intro.generatedAt) || "—") : "";
|
|
@@ -2554,75 +2555,30 @@ function IntroPopover({
|
|
|
2554
2555
|
}
|
|
2555
2556
|
}, intro.generating ? t("intro.generating") : t("intro.generate")));
|
|
2556
2557
|
}
|
|
2557
|
-
return
|
|
2558
|
-
|
|
2558
|
+
return h(
|
|
2559
|
+
Overlay,
|
|
2559
2560
|
{
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
style: { left: `${pos.left}px`, top: `${pos.top}px`, visibility: pos.visibility }
|
|
2561
|
+
t,
|
|
2562
|
+
title: `✨ ${t("intro.title")}`,
|
|
2563
|
+
onCancel: onClose,
|
|
2564
|
+
footer: actions
|
|
2565
2565
|
},
|
|
2566
|
-
h(
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
),
|
|
2575
|
-
h("button", {
|
|
2576
|
-
type: "button",
|
|
2577
|
-
className: "dshsc-policy-close",
|
|
2578
|
-
"aria-label": t("action.close"),
|
|
2579
|
-
onClick: onClose
|
|
2580
|
-
}, "×")
|
|
2581
|
-
),
|
|
2582
|
-
intro.loading ? h("p", { className: "dshsc-intro-note" }, t("intro.loading")) : intro.intro ? h("p", { className: "dshsc-intro-text" }, intro.intro.text) : h("p", { className: "dshsc-intro-note" }, t("intro.empty")),
|
|
2583
|
-
intro.intro?.stale ? h("p", { className: "dshsc-intro-stale" }, t("intro.stale")) : null,
|
|
2584
|
-
failureKey ? h("p", { className: "dshsc-intro-error" }, t(failureKey)) : null,
|
|
2585
|
-
meta ? h("p", { className: "dshsc-intro-meta" }, meta) : null,
|
|
2586
|
-
available && !intro.intro && !intro.loading ? h("p", { className: "dshsc-intro-meta" }, hint || t("intro.hint")) : null,
|
|
2587
|
-
!available ? h("p", { className: "dshsc-intro-error" }, t("intro.unavailable")) : null,
|
|
2588
|
-
h("div", { className: "dshsc-intro-foot" }, actions)
|
|
2589
|
-
));
|
|
2566
|
+
h("p", { className: "dshsc-intro-sub", key: "sub", title: subtitle || name }, subtitle || name),
|
|
2567
|
+
intro.loading ? h("p", { className: "dshsc-intro-note", key: "loading" }, t("intro.loading")) : intro.intro ? h("p", { className: "dshsc-intro-text", key: "text" }, intro.intro.text) : h("p", { className: "dshsc-intro-note", key: "empty" }, t("intro.empty")),
|
|
2568
|
+
intro.intro?.stale ? h("p", { className: "dshsc-intro-stale", key: "stale" }, t("intro.stale")) : null,
|
|
2569
|
+
failureKey ? h("p", { className: "dshsc-intro-error", key: "error" }, t(failureKey)) : null,
|
|
2570
|
+
meta ? h("p", { className: "dshsc-intro-meta", key: "meta" }, meta) : null,
|
|
2571
|
+
available && !intro.intro && !intro.loading ? h("p", { className: "dshsc-intro-meta", key: "hint" }, hint || t("intro.hint")) : null,
|
|
2572
|
+
!available ? h("p", { className: "dshsc-intro-error", key: "unavailable" }, t("intro.unavailable")) : null
|
|
2573
|
+
);
|
|
2590
2574
|
}
|
|
2591
2575
|
|
|
2592
|
-
// client/views/policy-
|
|
2593
|
-
var
|
|
2594
|
-
function
|
|
2576
|
+
// client/views/policy-modal.js
|
|
2577
|
+
var import_react3 = __toESM(require("react"), 1);
|
|
2578
|
+
function PolicyModal({ t, row, busy, onClose, onSave, onUnpark }) {
|
|
2595
2579
|
const initial = row.mode === "hidden" ? "auto" : row.mode;
|
|
2596
|
-
const [mode, setMode] =
|
|
2597
|
-
const [confirming, setConfirming] =
|
|
2598
|
-
const ref = import_react4.default.useRef(null);
|
|
2599
|
-
const [pos, setPos] = import_react4.default.useState({ left: 0, top: 0, visibility: "hidden" });
|
|
2600
|
-
useOverlayLayer(true, onClose);
|
|
2601
|
-
import_react4.default.useLayoutEffect(() => {
|
|
2602
|
-
const node = ref.current;
|
|
2603
|
-
if (!node || !anchor || typeof window === "undefined") return;
|
|
2604
|
-
const rect = node.getBoundingClientRect();
|
|
2605
|
-
const width = rect.width || 360;
|
|
2606
|
-
const height = rect.height || 320;
|
|
2607
|
-
let left = anchor.left + anchor.width / 2 - width / 2;
|
|
2608
|
-
left = Math.max(8, Math.min(left, window.innerWidth - width - 8));
|
|
2609
|
-
let top = anchor.bottom + 8;
|
|
2610
|
-
if (top + height > window.innerHeight - 8) {
|
|
2611
|
-
top = Math.max(8, anchor.top - height - 8);
|
|
2612
|
-
}
|
|
2613
|
-
setPos({ left, top, visibility: "visible" });
|
|
2614
|
-
}, [anchor, confirming, mode]);
|
|
2615
|
-
import_react4.default.useEffect(() => {
|
|
2616
|
-
if (typeof document === "undefined") return void 0;
|
|
2617
|
-
const onPointerDown = (event) => {
|
|
2618
|
-
const target = event?.target;
|
|
2619
|
-
if (ref.current?.contains?.(target)) return;
|
|
2620
|
-
if (anchorEl?.contains?.(target)) return;
|
|
2621
|
-
onClose();
|
|
2622
|
-
};
|
|
2623
|
-
document.addEventListener("pointerdown", onPointerDown, true);
|
|
2624
|
-
return () => document.removeEventListener("pointerdown", onPointerDown, true);
|
|
2625
|
-
}, [anchorEl, onClose]);
|
|
2580
|
+
const [mode, setMode] = import_react3.default.useState(initial);
|
|
2581
|
+
const [confirming, setConfirming] = import_react3.default.useState(false);
|
|
2626
2582
|
const hidden = row.mode === "hidden";
|
|
2627
2583
|
const author = row.origin === "author" && row.mode === "manual";
|
|
2628
2584
|
const flat = row.kind === "file";
|
|
@@ -2644,7 +2600,8 @@ function PolicyPopover({ t, row, anchor, anchorEl, busy, onClose, onSave, onUnpa
|
|
|
2644
2600
|
type: "button",
|
|
2645
2601
|
key: `policy-${id}`,
|
|
2646
2602
|
className: `dshsc-policyopt${mode === id ? " active" : ""}`,
|
|
2647
|
-
"
|
|
2603
|
+
role: "radio",
|
|
2604
|
+
"aria-checked": String(mode === id),
|
|
2648
2605
|
disabled: busy || flat,
|
|
2649
2606
|
onClick: () => choose(id)
|
|
2650
2607
|
},
|
|
@@ -2656,95 +2613,85 @@ function PolicyPopover({ t, row, anchor, anchorEl, busy, onClose, onSave, onUnpa
|
|
|
2656
2613
|
h("small", null, desc)
|
|
2657
2614
|
)
|
|
2658
2615
|
);
|
|
2659
|
-
|
|
2660
|
-
"
|
|
2616
|
+
const footer = confirming ? [
|
|
2617
|
+
h("button", {
|
|
2618
|
+
type: "button",
|
|
2619
|
+
key: "cancel",
|
|
2620
|
+
className: "dshsc-btn ghost",
|
|
2621
|
+
disabled: busy,
|
|
2622
|
+
onClick: () => setConfirming(false)
|
|
2623
|
+
}, t("action.cancel")),
|
|
2624
|
+
h("button", {
|
|
2625
|
+
type: "button",
|
|
2626
|
+
key: "confirm",
|
|
2627
|
+
className: "dshsc-btn danger",
|
|
2628
|
+
disabled: busy,
|
|
2629
|
+
onClick: () => onSave("hidden")
|
|
2630
|
+
}, busy ? t("policy.saving") : t("policy.hiddenConfirm"))
|
|
2631
|
+
] : hidden ? [
|
|
2632
|
+
h("button", { type: "button", key: "cancel", className: "dshsc-btn ghost", onClick: onClose }, t("action.cancel")),
|
|
2633
|
+
h("button", {
|
|
2634
|
+
type: "button",
|
|
2635
|
+
key: "restore",
|
|
2636
|
+
className: "dshsc-btn primary",
|
|
2637
|
+
disabled: busy,
|
|
2638
|
+
onClick: onUnpark
|
|
2639
|
+
}, t("policy.unpark"))
|
|
2640
|
+
] : [
|
|
2641
|
+
h("button", { type: "button", key: "cancel", className: "dshsc-btn ghost", onClick: onClose }, t("action.cancel")),
|
|
2642
|
+
h("button", {
|
|
2643
|
+
type: "button",
|
|
2644
|
+
key: "save",
|
|
2645
|
+
className: "dshsc-btn primary",
|
|
2646
|
+
disabled: busy || !dirty || flat,
|
|
2647
|
+
onClick: submit
|
|
2648
|
+
}, busy ? t("policy.saving") : t("action.save"))
|
|
2649
|
+
];
|
|
2650
|
+
return h(
|
|
2651
|
+
Overlay,
|
|
2661
2652
|
{
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
style: { left: `${pos.left}px`, top: `${pos.top}px`, visibility: pos.visibility }
|
|
2653
|
+
t,
|
|
2654
|
+
title: t("policy.title"),
|
|
2655
|
+
onCancel: onClose,
|
|
2656
|
+
footer
|
|
2667
2657
|
},
|
|
2668
|
-
h(
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
"div",
|
|
2673
|
-
null,
|
|
2674
|
-
h("div", { className: "dshsc-policy-title" }, t("policy.title")),
|
|
2675
|
-
h("div", { className: "dshsc-policy-sub" }, t("policy.subtitle"))
|
|
2676
|
-
),
|
|
2677
|
-
h("button", {
|
|
2678
|
-
type: "button",
|
|
2679
|
-
className: "dshsc-policy-close",
|
|
2680
|
-
"aria-label": t("action.close"),
|
|
2681
|
-
onClick: onClose
|
|
2682
|
-
}, "×")
|
|
2683
|
-
),
|
|
2684
|
-
author ? h("div", { className: "dshsc-policy-note author" }, t("policy.authorHint")) : null,
|
|
2685
|
-
row.legacy ? h("div", { className: "dshsc-policy-note" }, t("policy.legacyHint")) : null,
|
|
2686
|
-
row.drift ? h("div", { className: "dshsc-policy-note warn" }, t("policy.driftHint")) : null,
|
|
2658
|
+
h("p", { className: "dshsc-sub", key: "sub" }, t("policy.subtitle")),
|
|
2659
|
+
author ? h("div", { className: "dshsc-policy-note author", key: "author" }, t("policy.authorHint")) : null,
|
|
2660
|
+
row.legacy ? h("div", { className: "dshsc-policy-note", key: "legacy" }, t("policy.legacyHint")) : null,
|
|
2661
|
+
row.drift ? h("div", { className: "dshsc-policy-note warn", key: "drift" }, t("policy.driftHint")) : null,
|
|
2687
2662
|
hidden ? h(
|
|
2688
2663
|
"div",
|
|
2689
|
-
{ className: "dshsc-policy-hidden" },
|
|
2664
|
+
{ className: "dshsc-policy-hidden", key: "hidden" },
|
|
2690
2665
|
h(
|
|
2691
2666
|
"div",
|
|
2692
|
-
{ className: "dshsc-
|
|
2693
|
-
h("span", { className: "dshsc-policyopt-dot" }),
|
|
2667
|
+
{ className: "dshsc-panel" },
|
|
2694
2668
|
h(
|
|
2695
|
-
"
|
|
2696
|
-
{ className: "dshsc-policyopt-
|
|
2697
|
-
h("
|
|
2698
|
-
h(
|
|
2669
|
+
"div",
|
|
2670
|
+
{ className: "dshsc-policyopt active static", role: "radio", "aria-checked": "true" },
|
|
2671
|
+
h("span", { className: "dshsc-policyopt-dot", "aria-hidden": "true" }),
|
|
2672
|
+
h(
|
|
2673
|
+
"span",
|
|
2674
|
+
{ className: "dshsc-policyopt-body" },
|
|
2675
|
+
h("b", null, t("policy.hidden")),
|
|
2676
|
+
h("small", null, t("policy.hiddenDesc"))
|
|
2677
|
+
)
|
|
2699
2678
|
)
|
|
2700
2679
|
),
|
|
2701
|
-
h("
|
|
2702
|
-
type: "button",
|
|
2703
|
-
className: "dshsc-btn primary dshsc-policy-restore",
|
|
2704
|
-
disabled: busy,
|
|
2705
|
-
onClick: onUnpark
|
|
2706
|
-
}, t("policy.unpark")),
|
|
2707
|
-
h("p", { className: "dshsc-policy-sub" }, t("policy.hiddenHint"))
|
|
2680
|
+
h("p", { className: "dshsc-sub" }, t("policy.hiddenHint"))
|
|
2708
2681
|
) : h(
|
|
2709
2682
|
"div",
|
|
2710
|
-
{ className: "dshsc-policy-options" },
|
|
2683
|
+
{ className: "dshsc-panel dshsc-policy-options", key: "options", role: "radiogroup", "aria-label": t("policy.title") },
|
|
2711
2684
|
option("auto", t("policy.auto"), t("policy.autoDesc")),
|
|
2712
2685
|
option("manual", t("policy.manual"), t("policy.manualDesc")),
|
|
2713
2686
|
option("hidden", t("policy.hidden"), t("policy.hiddenDesc"))
|
|
2714
2687
|
),
|
|
2715
2688
|
confirming ? h(
|
|
2716
2689
|
"div",
|
|
2717
|
-
{ className: "dshsc-policy-confirm" },
|
|
2690
|
+
{ className: "dshsc-policy-confirm", key: "confirm" },
|
|
2718
2691
|
h("b", null, t("policy.hiddenConfirmTitle")),
|
|
2719
|
-
h("p", null, t("policy.hiddenConfirmBody"))
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
{ className: "dshsc-policy-foot" },
|
|
2723
|
-
h("button", {
|
|
2724
|
-
type: "button",
|
|
2725
|
-
className: "dshsc-btn ghost",
|
|
2726
|
-
disabled: busy,
|
|
2727
|
-
onClick: () => setConfirming(false)
|
|
2728
|
-
}, t("action.cancel")),
|
|
2729
|
-
h("button", {
|
|
2730
|
-
type: "button",
|
|
2731
|
-
className: "dshsc-btn danger",
|
|
2732
|
-
disabled: busy,
|
|
2733
|
-
onClick: () => onSave("hidden")
|
|
2734
|
-
}, busy ? t("policy.saving") : t("policy.hiddenConfirm"))
|
|
2735
|
-
)
|
|
2736
|
-
) : h(
|
|
2737
|
-
"div",
|
|
2738
|
-
{ className: "dshsc-policy-foot" },
|
|
2739
|
-
h("button", { type: "button", className: "dshsc-btn ghost", onClick: onClose }, t("action.cancel")),
|
|
2740
|
-
hidden ? null : h("button", {
|
|
2741
|
-
type: "button",
|
|
2742
|
-
className: "dshsc-btn primary",
|
|
2743
|
-
disabled: busy || !dirty || flat,
|
|
2744
|
-
onClick: submit
|
|
2745
|
-
}, busy ? t("policy.saving") : t("action.save"))
|
|
2746
|
-
)
|
|
2747
|
-
), overlayHost());
|
|
2692
|
+
h("p", null, t("policy.hiddenConfirmBody"))
|
|
2693
|
+
) : null
|
|
2694
|
+
);
|
|
2748
2695
|
}
|
|
2749
2696
|
|
|
2750
2697
|
// client/views/installed.js
|
|
@@ -2766,38 +2713,26 @@ function InstalledTab({
|
|
|
2766
2713
|
actions
|
|
2767
2714
|
}) {
|
|
2768
2715
|
const updatable = (row) => UPDATABLE.has(row.status);
|
|
2769
|
-
const [policy, setPolicy] =
|
|
2770
|
-
const [intro, setIntro] =
|
|
2716
|
+
const [policy, setPolicy] = import_react4.default.useState(null);
|
|
2717
|
+
const [intro, setIntro] = import_react4.default.useState(null);
|
|
2771
2718
|
const sharedTargets = new Set((data?.agents?.detected || []).filter((agent) => agent.kind === "agent" && agent.sharedHome === "yes").map((agent) => agent.target));
|
|
2772
|
-
|
|
2719
|
+
import_react4.default.useEffect(() => {
|
|
2773
2720
|
if (!policy) return;
|
|
2774
2721
|
if (rows.some((row) => row.name === policy.row.name)) return;
|
|
2775
2722
|
setPolicy(null);
|
|
2776
2723
|
}, [policy, rows]);
|
|
2777
|
-
|
|
2724
|
+
import_react4.default.useEffect(() => {
|
|
2778
2725
|
if (!intro) return;
|
|
2779
2726
|
if (rows.some((row) => row.name === intro.row.name)) return;
|
|
2780
2727
|
setIntro(null);
|
|
2781
2728
|
}, [intro, rows]);
|
|
2782
|
-
function openIntro(
|
|
2783
|
-
|
|
2784
|
-
const rect = el.getBoundingClientRect();
|
|
2785
|
-
setIntro({
|
|
2786
|
-
row,
|
|
2787
|
-
anchorEl: el,
|
|
2788
|
-
anchor: { left: rect.left, top: rect.top, bottom: rect.bottom, width: rect.width, height: rect.height }
|
|
2789
|
-
});
|
|
2729
|
+
function openIntro(row) {
|
|
2730
|
+
setIntro({ row });
|
|
2790
2731
|
}
|
|
2791
|
-
function openPolicy(
|
|
2732
|
+
function openPolicy(row) {
|
|
2792
2733
|
if (busy !== null || row.kind === "file") return;
|
|
2793
2734
|
if (row.mode === "manual" && row.origin === "author") return;
|
|
2794
|
-
|
|
2795
|
-
const rect = el.getBoundingClientRect();
|
|
2796
|
-
setPolicy({
|
|
2797
|
-
row,
|
|
2798
|
-
anchorEl: el,
|
|
2799
|
-
anchor: { left: rect.left, top: rect.top, bottom: rect.bottom, width: rect.width, height: rect.height }
|
|
2800
|
-
});
|
|
2735
|
+
setPolicy({ row });
|
|
2801
2736
|
}
|
|
2802
2737
|
function policyChip(row) {
|
|
2803
2738
|
if (row.status === "unmanaged") {
|
|
@@ -2814,7 +2749,7 @@ function InstalledTab({
|
|
|
2814
2749
|
disabled: busy !== null,
|
|
2815
2750
|
title: t("policy.hiddenHint"),
|
|
2816
2751
|
"aria-haspopup": "dialog",
|
|
2817
|
-
onClick: (
|
|
2752
|
+
onClick: () => openPolicy(row)
|
|
2818
2753
|
}, t("policy.hidden"));
|
|
2819
2754
|
}
|
|
2820
2755
|
if (mode === "manual" && row.origin === "author") {
|
|
@@ -2830,7 +2765,7 @@ function InstalledTab({
|
|
|
2830
2765
|
disabled: busy !== null,
|
|
2831
2766
|
title: t("policy.driftHint"),
|
|
2832
2767
|
"aria-haspopup": "dialog",
|
|
2833
|
-
onClick: (
|
|
2768
|
+
onClick: () => openPolicy(row)
|
|
2834
2769
|
}, t("policy.drift"));
|
|
2835
2770
|
}
|
|
2836
2771
|
if (row.legacy === "dsh-off") {
|
|
@@ -2840,7 +2775,7 @@ function InstalledTab({
|
|
|
2840
2775
|
disabled: busy !== null,
|
|
2841
2776
|
title: t("policy.legacyHint"),
|
|
2842
2777
|
"aria-haspopup": "dialog",
|
|
2843
|
-
onClick: (
|
|
2778
|
+
onClick: () => openPolicy(row)
|
|
2844
2779
|
}, t("policy.legacy"));
|
|
2845
2780
|
}
|
|
2846
2781
|
if (mode === "manual") {
|
|
@@ -2850,7 +2785,7 @@ function InstalledTab({
|
|
|
2850
2785
|
disabled: busy !== null,
|
|
2851
2786
|
title: t("policy.manualOnlyHint"),
|
|
2852
2787
|
"aria-haspopup": "dialog",
|
|
2853
|
-
onClick: (
|
|
2788
|
+
onClick: () => openPolicy(row)
|
|
2854
2789
|
}, t("policy.manual"));
|
|
2855
2790
|
}
|
|
2856
2791
|
return h("button", {
|
|
@@ -2859,7 +2794,7 @@ function InstalledTab({
|
|
|
2859
2794
|
disabled: busy !== null,
|
|
2860
2795
|
title: t("policy.open"),
|
|
2861
2796
|
"aria-haspopup": "dialog",
|
|
2862
|
-
onClick: (
|
|
2797
|
+
onClick: () => openPolicy(row)
|
|
2863
2798
|
}, t("policy.auto"));
|
|
2864
2799
|
}
|
|
2865
2800
|
function statusTag(row) {
|
|
@@ -2988,7 +2923,7 @@ function InstalledTab({
|
|
|
2988
2923
|
className: "dshsc-intro-btn",
|
|
2989
2924
|
title: t("intro.open"),
|
|
2990
2925
|
"aria-haspopup": "dialog",
|
|
2991
|
-
onClick: (
|
|
2926
|
+
onClick: () => openIntro(row)
|
|
2992
2927
|
},
|
|
2993
2928
|
h("span", { className: "dshsc-spark", "aria-hidden": "true" }, "✨"),
|
|
2994
2929
|
t("intro.button")
|
|
@@ -3242,12 +3177,10 @@ function InstalledTab({
|
|
|
3242
3177
|
)
|
|
3243
3178
|
);
|
|
3244
3179
|
}
|
|
3245
|
-
const
|
|
3180
|
+
const introModal = intro && data?.intro ? h(IntroModal, {
|
|
3246
3181
|
t,
|
|
3247
3182
|
name: intro.row.name,
|
|
3248
3183
|
stamp: [intro.row.commit || "", intro.row.currentHash || "", intro.row.updatedAt || "", intro.row.status || ""].join("|"),
|
|
3249
|
-
anchor: intro.anchor,
|
|
3250
|
-
anchorEl: intro.anchorEl,
|
|
3251
3184
|
capability: data.intro,
|
|
3252
3185
|
onClose: () => setIntro(null),
|
|
3253
3186
|
onView: () => {
|
|
@@ -3256,11 +3189,9 @@ function InstalledTab({
|
|
|
3256
3189
|
actions.onView(target);
|
|
3257
3190
|
}
|
|
3258
3191
|
}) : null;
|
|
3259
|
-
const
|
|
3192
|
+
const policyModal = policy ? h(PolicyModal, {
|
|
3260
3193
|
t,
|
|
3261
3194
|
row: policy.row,
|
|
3262
|
-
anchor: policy.anchor,
|
|
3263
|
-
anchorEl: policy.anchorEl,
|
|
3264
3195
|
busy: busy !== null,
|
|
3265
3196
|
onClose: () => setPolicy(null),
|
|
3266
3197
|
onSave: (mode) => {
|
|
@@ -3274,7 +3205,7 @@ function InstalledTab({
|
|
|
3274
3205
|
actions.onRequestUnpark(target);
|
|
3275
3206
|
}
|
|
3276
3207
|
}) : null;
|
|
3277
|
-
return h(
|
|
3208
|
+
return h(import_react4.default.Fragment, null, content(), policyModal, introModal);
|
|
3278
3209
|
}
|
|
3279
3210
|
|
|
3280
3211
|
// client/views/agent-tip.js
|
|
@@ -3309,7 +3240,7 @@ function AgentTip({ t, agentTip, agentTipPos, nodeRef }) {
|
|
|
3309
3240
|
}
|
|
3310
3241
|
|
|
3311
3242
|
// client/views/subscriptions.js
|
|
3312
|
-
var
|
|
3243
|
+
var import_react5 = __toESM(require("react"), 1);
|
|
3313
3244
|
function repoOf(source) {
|
|
3314
3245
|
const value = String(source || "").trim().toLowerCase().replace(/\.git$/, "");
|
|
3315
3246
|
return value || null;
|
|
@@ -3331,22 +3262,18 @@ function SubscriptionsTab({
|
|
|
3331
3262
|
onInstall,
|
|
3332
3263
|
onAdd
|
|
3333
3264
|
}) {
|
|
3334
|
-
const [intro, setIntro] =
|
|
3335
|
-
|
|
3265
|
+
const [intro, setIntro] = import_react5.default.useState(null);
|
|
3266
|
+
import_react5.default.useEffect(() => {
|
|
3336
3267
|
if (!intro) return;
|
|
3337
3268
|
const subscription = subscriptions.find((item) => item.id === intro.subscriptionId);
|
|
3338
3269
|
const entries = subscription?.snapshot?.entries || [];
|
|
3339
3270
|
if (entries.some((entry) => entry.name === intro.name)) return;
|
|
3340
3271
|
setIntro(null);
|
|
3341
3272
|
}, [intro, subscriptions]);
|
|
3342
|
-
function openIntro(
|
|
3343
|
-
const el = event.currentTarget;
|
|
3344
|
-
const rect = el.getBoundingClientRect();
|
|
3273
|
+
function openIntro(subscription, entry) {
|
|
3345
3274
|
setIntro({
|
|
3346
3275
|
subscriptionId: subscription.id,
|
|
3347
|
-
name: entry.name
|
|
3348
|
-
anchorEl: el,
|
|
3349
|
-
anchor: { left: rect.left, top: rect.top, bottom: rect.bottom, width: rect.width, height: rect.height }
|
|
3276
|
+
name: entry.name
|
|
3350
3277
|
});
|
|
3351
3278
|
}
|
|
3352
3279
|
function introButton(subscription, entry) {
|
|
@@ -3357,7 +3284,7 @@ function SubscriptionsTab({
|
|
|
3357
3284
|
className: "dshsc-intro-btn",
|
|
3358
3285
|
title: t("intro.open"),
|
|
3359
3286
|
"aria-haspopup": "dialog",
|
|
3360
|
-
onClick: (
|
|
3287
|
+
onClick: () => openIntro(subscription, entry)
|
|
3361
3288
|
},
|
|
3362
3289
|
h("span", { className: "dshsc-spark", "aria-hidden": "true" }, "✨"),
|
|
3363
3290
|
t("intro.button")
|
|
@@ -3365,22 +3292,20 @@ function SubscriptionsTab({
|
|
|
3365
3292
|
}
|
|
3366
3293
|
const introSubscription = intro ? subscriptions.find((item) => item.id === intro.subscriptionId) || null : null;
|
|
3367
3294
|
const introEntry = introSubscription ? (introSubscription.snapshot?.entries || []).find((entry) => entry.name === intro.name) || null : null;
|
|
3368
|
-
const
|
|
3295
|
+
const introModal = intro && introEntry && capability ? (() => {
|
|
3369
3296
|
const commit = introSubscription.snapshot?.commit || "";
|
|
3370
3297
|
const installedAlready = rows.some((row) => {
|
|
3371
3298
|
if (row.name !== introEntry.name) return false;
|
|
3372
3299
|
if (row.subscriptionId === introSubscription.id) return true;
|
|
3373
3300
|
return repoOf(row.source) === repoOf(introSubscription.source);
|
|
3374
3301
|
});
|
|
3375
|
-
return h(
|
|
3302
|
+
return h(IntroModal, {
|
|
3376
3303
|
t,
|
|
3377
3304
|
name: introEntry.name,
|
|
3378
3305
|
stamp: commit,
|
|
3379
3306
|
subtitle: `${introSubscription.source}@${shortCommit(commit)}`,
|
|
3380
3307
|
endpoint: `/subscriptions/${encodeURIComponent(introSubscription.id)}/entries/${encodeURIComponent(introEntry.name)}/intro`,
|
|
3381
3308
|
capability,
|
|
3382
|
-
anchor: intro.anchor,
|
|
3383
|
-
anchorEl: intro.anchorEl,
|
|
3384
3309
|
hint: t("intro.hintRemote").replace("{source}", introSubscription.source).replace("{commit}", shortCommit(commit) || "—"),
|
|
3385
3310
|
viewLabel: t("action.install"),
|
|
3386
3311
|
onView: installedAlready ? null : () => {
|
|
@@ -3412,7 +3337,7 @@ function SubscriptionsTab({
|
|
|
3412
3337
|
);
|
|
3413
3338
|
}
|
|
3414
3339
|
return h(
|
|
3415
|
-
|
|
3340
|
+
import_react5.default.Fragment,
|
|
3416
3341
|
null,
|
|
3417
3342
|
h(
|
|
3418
3343
|
"div",
|
|
@@ -3511,7 +3436,6 @@ function SubscriptionsTab({
|
|
|
3511
3436
|
h(
|
|
3512
3437
|
"span",
|
|
3513
3438
|
{ className: `dshsc-state${installedAlready ? " ok" : ""}` },
|
|
3514
|
-
installedAlready ? "✓ " : "",
|
|
3515
3439
|
installedAlready ? t("installed") : t("notInstalled")
|
|
3516
3440
|
),
|
|
3517
3441
|
h(
|
|
@@ -3530,51 +3454,23 @@ function SubscriptionsTab({
|
|
|
3530
3454
|
})
|
|
3531
3455
|
)
|
|
3532
3456
|
),
|
|
3533
|
-
|
|
3457
|
+
introModal
|
|
3534
3458
|
);
|
|
3535
3459
|
}
|
|
3536
3460
|
|
|
3537
3461
|
// client/views/modals.js
|
|
3538
|
-
var import_react8 = __toESM(require("react"), 1);
|
|
3539
|
-
|
|
3540
|
-
// client/views/add-skill.js
|
|
3541
3462
|
var import_react7 = __toESM(require("react"), 1);
|
|
3542
3463
|
|
|
3543
|
-
// client/views/overlay.js
|
|
3544
|
-
function Overlay({ t, title, children, footer, onCancel }) {
|
|
3545
|
-
useOverlayLayer(true, onCancel);
|
|
3546
|
-
return portalize(h(
|
|
3547
|
-
"div",
|
|
3548
|
-
{ className: "dshsc-overlay", onClick: (event) => {
|
|
3549
|
-
if (event.target === event.currentTarget) onCancel?.();
|
|
3550
|
-
} },
|
|
3551
|
-
h(
|
|
3552
|
-
"div",
|
|
3553
|
-
{ className: "dshsc-modal", role: "dialog", "aria-modal": "true" },
|
|
3554
|
-
h("button", {
|
|
3555
|
-
className: "dshsc-modal-close",
|
|
3556
|
-
"aria-label": t("action.cancel"),
|
|
3557
|
-
onClick: (event) => {
|
|
3558
|
-
event.stopPropagation();
|
|
3559
|
-
onCancel?.();
|
|
3560
|
-
}
|
|
3561
|
-
}, "×"),
|
|
3562
|
-
h("h3", null, title),
|
|
3563
|
-
children,
|
|
3564
|
-
h("div", { className: "dshsc-modal-foot" }, footer)
|
|
3565
|
-
)
|
|
3566
|
-
), overlayHost());
|
|
3567
|
-
}
|
|
3568
|
-
|
|
3569
3464
|
// client/views/add-skill.js
|
|
3465
|
+
var import_react6 = __toESM(require("react"), 1);
|
|
3570
3466
|
function AddSkillModal({ t, onCancel, onProbe, onInstall }) {
|
|
3571
|
-
const [source, setSource] =
|
|
3572
|
-
const [phase, setPhase] =
|
|
3573
|
-
const [options, setOptions] =
|
|
3574
|
-
const [selected, setSelected] =
|
|
3575
|
-
const [manual, setManual] =
|
|
3576
|
-
const [skipped, setSkipped] =
|
|
3577
|
-
const [error, setError] =
|
|
3467
|
+
const [source, setSource] = import_react6.default.useState("");
|
|
3468
|
+
const [phase, setPhase] = import_react6.default.useState("idle");
|
|
3469
|
+
const [options, setOptions] = import_react6.default.useState([]);
|
|
3470
|
+
const [selected, setSelected] = import_react6.default.useState(() => /* @__PURE__ */ new Set());
|
|
3471
|
+
const [manual, setManual] = import_react6.default.useState("");
|
|
3472
|
+
const [skipped, setSkipped] = import_react6.default.useState(0);
|
|
3473
|
+
const [error, setError] = import_react6.default.useState(null);
|
|
3578
3474
|
const reset = (value) => {
|
|
3579
3475
|
setSource(value);
|
|
3580
3476
|
setPhase("idle");
|
|
@@ -3710,7 +3606,7 @@ function AddSkillModal({ t, onCancel, onProbe, onInstall }) {
|
|
|
3710
3606
|
|
|
3711
3607
|
// client/views/modals.js
|
|
3712
3608
|
function FormModal({ t, title, hint, fields, onCancel, onSubmit }) {
|
|
3713
|
-
const [form, setForm] =
|
|
3609
|
+
const [form, setForm] = import_react7.default.useState(() => Object.fromEntries(fields.map((field) => [field.key, ""])));
|
|
3714
3610
|
const incomplete = fields.some(
|
|
3715
3611
|
(field) => field.required !== false && !String(form[field.key] || "").trim()
|
|
3716
3612
|
);
|
|
@@ -3758,7 +3654,7 @@ function ExportsModal({ t, row, agents, onCancel, onSave }) {
|
|
|
3758
3654
|
const candidates = agents.filter(
|
|
3759
3655
|
(agent) => agent.kind === "agent" && agent.exists && agent.exportable
|
|
3760
3656
|
);
|
|
3761
|
-
const [targets, setTargets] =
|
|
3657
|
+
const [targets, setTargets] = import_react7.default.useState(() => {
|
|
3762
3658
|
const available = new Set(candidates.map((agent) => agent.target));
|
|
3763
3659
|
return new Set((row.exports || []).map((item) => item.target).filter((target) => available.has(target)));
|
|
3764
3660
|
});
|
|
@@ -3800,7 +3696,7 @@ function ExportsModal({ t, row, agents, onCancel, onSave }) {
|
|
|
3800
3696
|
);
|
|
3801
3697
|
}
|
|
3802
3698
|
function DefaultExportModal({ t, settings, agents, onCancel, onSave }) {
|
|
3803
|
-
const [targets, setTargets] =
|
|
3699
|
+
const [targets, setTargets] = import_react7.default.useState(() => new Set(
|
|
3804
3700
|
Array.isArray(settings.exportTargets) ? settings.exportTargets : []
|
|
3805
3701
|
));
|
|
3806
3702
|
const toggle = (target) => setTargets((prev) => {
|
|
@@ -3929,7 +3825,7 @@ ${conflicts.map((item) => `${item.label}: ${item.linkPath}`).join("\n")}`,
|
|
|
3929
3825
|
}
|
|
3930
3826
|
|
|
3931
3827
|
// client/views/skill-reader.js
|
|
3932
|
-
var
|
|
3828
|
+
var import_react8 = __toESM(require("react"), 1);
|
|
3933
3829
|
|
|
3934
3830
|
// client/markdown.js
|
|
3935
3831
|
function classifyHref(href) {
|
|
@@ -4426,17 +4322,17 @@ function SkillReader({
|
|
|
4426
4322
|
}) {
|
|
4427
4323
|
const { state, file, loadFile, retry } = useSkillReader(row);
|
|
4428
4324
|
const name = row?.name || "";
|
|
4429
|
-
const [tab, setTab] =
|
|
4430
|
-
const [format, setFormat] =
|
|
4431
|
-
const [docOverride, setDocOverride] =
|
|
4432
|
-
const [fileFormat, setFileFormat] =
|
|
4433
|
-
const [tocActive, setTocActive] =
|
|
4434
|
-
const [descOpen, setDescOpen] =
|
|
4435
|
-
const [descClamped, setDescClamped] =
|
|
4436
|
-
const bodyRef =
|
|
4437
|
-
const dialogRef =
|
|
4438
|
-
const descRef =
|
|
4439
|
-
const scrollRef =
|
|
4325
|
+
const [tab, setTab] = import_react8.default.useState(initialTab);
|
|
4326
|
+
const [format, setFormat] = import_react8.default.useState("rendered");
|
|
4327
|
+
const [docOverride, setDocOverride] = import_react8.default.useState(null);
|
|
4328
|
+
const [fileFormat, setFileFormat] = import_react8.default.useState("rendered");
|
|
4329
|
+
const [tocActive, setTocActive] = import_react8.default.useState(null);
|
|
4330
|
+
const [descOpen, setDescOpen] = import_react8.default.useState(false);
|
|
4331
|
+
const [descClamped, setDescClamped] = import_react8.default.useState(false);
|
|
4332
|
+
const bodyRef = import_react8.default.useRef(null);
|
|
4333
|
+
const dialogRef = import_react8.default.useRef(null);
|
|
4334
|
+
const descRef = import_react8.default.useRef(null);
|
|
4335
|
+
const scrollRef = import_react8.default.useRef(0);
|
|
4440
4336
|
useOverlayLayer(variant === "drawer" || variant === "page", onClose);
|
|
4441
4337
|
const tree = state.tree;
|
|
4442
4338
|
const files = tree?.files || [];
|
|
@@ -4444,18 +4340,18 @@ function SkillReader({
|
|
|
4444
4340
|
const bodyText = docOverride ? docOverride.content : state.body?.content || "";
|
|
4445
4341
|
const description = row?.description || state.body?.meta?.description || "";
|
|
4446
4342
|
const headings = tab === "doc" && bodyText ? parseCached(bodyText).headings : [];
|
|
4447
|
-
|
|
4343
|
+
import_react8.default.useEffect(() => {
|
|
4448
4344
|
setDocOverride(null);
|
|
4449
4345
|
scrollRef.current = 0;
|
|
4450
4346
|
}, [name]);
|
|
4451
|
-
const descOwnerRef =
|
|
4452
|
-
|
|
4347
|
+
const descOwnerRef = import_react8.default.useRef(name);
|
|
4348
|
+
import_react8.default.useEffect(() => {
|
|
4453
4349
|
if (descOwnerRef.current === name) return;
|
|
4454
4350
|
descOwnerRef.current = name;
|
|
4455
4351
|
setDescOpen(false);
|
|
4456
4352
|
setDescClamped(false);
|
|
4457
4353
|
}, [name]);
|
|
4458
|
-
|
|
4354
|
+
import_react8.default.useLayoutEffect(() => {
|
|
4459
4355
|
const node = descRef.current;
|
|
4460
4356
|
if (!node) return void 0;
|
|
4461
4357
|
const measure = () => {
|
|
@@ -4477,16 +4373,16 @@ function SkillReader({
|
|
|
4477
4373
|
window.removeEventListener("resize", measure);
|
|
4478
4374
|
};
|
|
4479
4375
|
}, [description, descOpen]);
|
|
4480
|
-
|
|
4376
|
+
import_react8.default.useEffect(() => {
|
|
4481
4377
|
if (!initialFile) return;
|
|
4482
4378
|
setTab("files");
|
|
4483
4379
|
void loadFile(initialFile);
|
|
4484
4380
|
}, [initialFile, loadFile]);
|
|
4485
|
-
|
|
4381
|
+
import_react8.default.useEffect(() => {
|
|
4486
4382
|
const node = bodyRef.current;
|
|
4487
4383
|
if (node && scrollRef.current) node.scrollTop = scrollRef.current;
|
|
4488
4384
|
});
|
|
4489
|
-
|
|
4385
|
+
import_react8.default.useEffect(() => {
|
|
4490
4386
|
if (variant !== "drawer" || typeof document === "undefined") return void 0;
|
|
4491
4387
|
const previous = document.activeElement;
|
|
4492
4388
|
const node = dialogRef.current;
|
|
@@ -4513,12 +4409,12 @@ function SkillReader({
|
|
|
4513
4409
|
previous?.focus?.();
|
|
4514
4410
|
};
|
|
4515
4411
|
}, [variant, onClose]);
|
|
4516
|
-
|
|
4412
|
+
import_react8.default.useEffect(() => {
|
|
4517
4413
|
if (variant !== "page" || typeof document === "undefined") return;
|
|
4518
4414
|
const node = document.getElementById("dshsc-reader-name");
|
|
4519
4415
|
node?.focus?.();
|
|
4520
4416
|
}, [variant, name]);
|
|
4521
|
-
|
|
4417
|
+
import_react8.default.useEffect(() => {
|
|
4522
4418
|
if (variant !== "page" || typeof window === "undefined") return void 0;
|
|
4523
4419
|
const node = bodyRef.current;
|
|
4524
4420
|
if (!node?.querySelectorAll) return void 0;
|
|
@@ -4956,19 +4852,19 @@ function SkillReader({
|
|
|
4956
4852
|
var UPDATABLE2 = /* @__PURE__ */ new Set(["update-available", "locally-modified", "conflict"]);
|
|
4957
4853
|
function Section({ t, page = false, pickDirectory, onPlacementChange }) {
|
|
4958
4854
|
const { data, error, busy, toast, setToast, setBusy, reload, run } = useSkillsData();
|
|
4959
|
-
const [tab, setTab] =
|
|
4960
|
-
const [view, setView] =
|
|
4961
|
-
const [modal, setModal] =
|
|
4962
|
-
const [expanded, setExpanded] =
|
|
4963
|
-
const [placementBusy, setPlacementBusy] =
|
|
4964
|
-
const [placementOverride, setPlacementOverride] =
|
|
4965
|
-
const [query, setQuery] =
|
|
4966
|
-
const [viewer, setViewer] =
|
|
4967
|
-
const rootRef =
|
|
4968
|
-
const scrollPos =
|
|
4969
|
-
const restoreScroll =
|
|
4970
|
-
const [filter, setFilter] =
|
|
4971
|
-
const [builtinOpen, setBuiltinOpen] =
|
|
4855
|
+
const [tab, setTab] = import_react9.default.useState("installed");
|
|
4856
|
+
const [view, setView] = import_react9.default.useState("flat");
|
|
4857
|
+
const [modal, setModal] = import_react9.default.useState(null);
|
|
4858
|
+
const [expanded, setExpanded] = import_react9.default.useState({});
|
|
4859
|
+
const [placementBusy, setPlacementBusy] = import_react9.default.useState(null);
|
|
4860
|
+
const [placementOverride, setPlacementOverride] = import_react9.default.useState(null);
|
|
4861
|
+
const [query, setQuery] = import_react9.default.useState("");
|
|
4862
|
+
const [viewer, setViewer] = import_react9.default.useState(null);
|
|
4863
|
+
const rootRef = import_react9.default.useRef(null);
|
|
4864
|
+
const scrollPos = import_react9.default.useRef(null);
|
|
4865
|
+
const restoreScroll = import_react9.default.useRef(null);
|
|
4866
|
+
const [filter, setFilter] = import_react9.default.useState("all");
|
|
4867
|
+
const [builtinOpen, setBuiltinOpen] = import_react9.default.useState(false);
|
|
4972
4868
|
const { settingsOpen, settingsRef, settingsPlacement, closeSettings, toggleSettings } = useSettingsPopover();
|
|
4973
4869
|
const { agentTip, agentTipPos, agentTipRef, openAgentTip, closeAgentTip } = useAgentTip();
|
|
4974
4870
|
const rows = data?.installs || [];
|
|
@@ -4991,7 +4887,7 @@ function Section({ t, page = false, pickDirectory, onPlacementChange }) {
|
|
|
4991
4887
|
const agentLabels = row.exports.map((item) => item.label);
|
|
4992
4888
|
return [row.name, row.source || "", ...agentLabels].join(" ").toLowerCase().includes(queryText);
|
|
4993
4889
|
});
|
|
4994
|
-
|
|
4890
|
+
import_react9.default.useEffect(() => {
|
|
4995
4891
|
if (!viewer || !data) return;
|
|
4996
4892
|
if (rows.some((row) => row.name === viewer.name)) return;
|
|
4997
4893
|
setViewer(null);
|
|
@@ -5025,7 +4921,7 @@ function Section({ t, page = false, pickDirectory, onPlacementChange }) {
|
|
|
5025
4921
|
if (viewer?.mode === "page") restoreScroll.current = scrollPos.current;
|
|
5026
4922
|
setViewer(null);
|
|
5027
4923
|
}
|
|
5028
|
-
|
|
4924
|
+
import_react9.default.useLayoutEffect(() => {
|
|
5029
4925
|
if (restoreScroll.current === null) return;
|
|
5030
4926
|
const target = restoreScroll.current;
|
|
5031
4927
|
restoreScroll.current = null;
|
|
@@ -5094,7 +4990,7 @@ function Section({ t, page = false, pickDirectory, onPlacementChange }) {
|
|
|
5094
4990
|
onToast: (next) => setToast(next)
|
|
5095
4991
|
}) : null;
|
|
5096
4992
|
const listNode = h(
|
|
5097
|
-
|
|
4993
|
+
import_react9.default.Fragment,
|
|
5098
4994
|
null,
|
|
5099
4995
|
h(Header, {
|
|
5100
4996
|
t,
|