dsh-continual-evolve 0.14.1 → 0.14.2

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.md CHANGED
@@ -7,7 +7,7 @@ English | [中文](README.zh.md)
7
7
  [![CI](https://github.com/ZK-Andy/dsh-continual-evolve/actions/workflows/ci.yml/badge.svg)](https://github.com/ZK-Andy/dsh-continual-evolve/actions/workflows/ci.yml)
8
8
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
9
9
  [![Node](https://img.shields.io/badge/node-%5E22.19%20%7C%7C%20%3E%3D24-339933)](package.json)
10
- [![Tests](https://img.shields.io/badge/tests-91%20passing-brightgreen)]()
10
+ [![Tests](https://img.shields.io/badge/tests-92%20passing-brightgreen)]()
11
11
  [![Coverage · statements](https://img.shields.io/badge/coverage_statements-96%25-brightgreen)]()
12
12
  [![Coverage · branches](https://img.shields.io/badge/coverage_branches-93%25-green)]()
13
13
  [![Coverage · functions](https://img.shields.io/badge/coverage_functions-100%25-brightgreen)]()
@@ -66,7 +66,7 @@ Profile patch example:
66
66
 
67
67
  ```bash
68
68
  pnpm install && pnpm build # deps + tsc -> lib/
69
- pnpm test # vitest (91 tests)
69
+ pnpm test # vitest (92 tests)
70
70
  pnpm test:coverage # v8 coverage, CI-enforced thresholds
71
71
  pnpm lint # oxlint src test client
72
72
  ```
package/README.zh.md CHANGED
@@ -7,7 +7,7 @@
7
7
  [![CI](https://github.com/ZK-Andy/dsh-continual-evolve/actions/workflows/ci.yml/badge.svg)](https://github.com/ZK-Andy/dsh-continual-evolve/actions/workflows/ci.yml)
8
8
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
9
9
  [![Node](https://img.shields.io/badge/node-%5E22.19%20%7C%7C%20%3E%3D24-339933)](package.json)
10
- [![Tests](https://img.shields.io/badge/tests-91%20passing-brightgreen)]()
10
+ [![Tests](https://img.shields.io/badge/tests-92%20passing-brightgreen)]()
11
11
  [![Coverage · statements](https://img.shields.io/badge/coverage_statements-96%25-brightgreen)]()
12
12
  [![Coverage · branches](https://img.shields.io/badge/coverage_branches-93%25-green)]()
13
13
  [![Coverage · functions](https://img.shields.io/badge/coverage_functions-100%25-brightgreen)]()
@@ -66,7 +66,7 @@ profile patch 示例:
66
66
 
67
67
  ```bash
68
68
  pnpm install && pnpm build # 依赖 + tsc -> lib/
69
- pnpm test # vitest(91 例)
69
+ pnpm test # vitest(92 例)
70
70
  pnpm test:coverage # v8 覆盖率,CI 强制阈值
71
71
  pnpm lint # oxlint src test client
72
72
  ```
package/client/client.js CHANGED
@@ -76,6 +76,7 @@ window.__ModuleLoader__.load({
76
76
  var createElement = react.createElement;
77
77
  var useState = react.useState;
78
78
  var useEffect = react.useEffect;
79
+ var useRef = react.useRef;
79
80
 
80
81
  var NS = "dsh-continual-evolve";
81
82
  var SLOT = "plugins.bundle.config";
@@ -298,6 +299,7 @@ window.__ModuleLoader__.load({
298
299
  ".dce-hint{color:var(--dsw-alias-label-tertiary,#8b93a1);font-size:12px;line-height:18px}",
299
300
  ".dce-empty{border:1px dashed var(--dsw-alias-border-l3,#d1d9e0);border-radius:10px;padding:16px;text-align:center;color:var(--dsw-alias-label-tertiary,#8b93a1);font-size:13px;line-height:20px}",
300
301
  ".dce-scope-wrap{position:relative;display:inline-flex;min-width:0;max-width:100%;align-self:flex-start}",
302
+ ".dce-scope-pill{border-radius:999px}",
301
303
  ".dce-scope{display:inline-flex;align-items:center;gap:8px;max-width:100%;padding:4px 10px;border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:999px;background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-primary,#1f2328);font:inherit;font-size:12px;line-height:18px;cursor:pointer}",
302
304
  ".dce-scope:hover{border-color:var(--dsw-alias-label-dimmed,#c8ccd4)}",
303
305
  ".dce-scope:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#4f6ef7);outline-offset:1px}",
@@ -307,8 +309,6 @@ window.__ModuleLoader__.load({
307
309
  ".dce-menu-item{display:flex;flex-direction:column;gap:2px;width:100%;padding:6px 10px;border:0;border-radius:8px;background:0 0;font:inherit;color:inherit;text-align:left;cursor:pointer}",
308
310
  ".dce-menu-item:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.03))}",
309
311
  '.dce-menu-item[data-active="true"]{background:var(--dsw-alias-bg-module-platform,#eef1f4)}',
310
- ".dce-menu-label{color:var(--dsw-alias-label-primary,#1f2328);font-size:13px;line-height:18px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
311
- ".dce-menu-path{color:var(--dsw-alias-label-tertiary,#8b93a1);font-size:11px;line-height:16px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
312
312
  ".dce-search{width:100%;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l4,#d8dee4);border-radius:var(--dsw-radius-md,8px);background:var(--dsw-alias-bg-layer-1,#fff);height:32px;color:var(--dsw-alias-label-primary,#1f2328);font:inherit;font-size:12px;padding:0 10px;outline:none}",
313
313
  ".dce-search::placeholder{color:var(--dsw-alias-label-tertiary,#8b93a1)}",
314
314
  ".dce-search:focus-visible{border-color:var(--dsw-alias-brand-primary,#4f6ef7)}",
@@ -321,6 +321,7 @@ window.__ModuleLoader__.load({
321
321
  ".dce-row{display:flex;width:100%;align-items:center;gap:10px;padding:8px 12px;background:0 0;border:0;font:inherit;color:inherit;text-align:left;cursor:pointer}",
322
322
  ".dce-row:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.03))}",
323
323
  ".dce-row:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#4f6ef7);outline-offset:-2px}",
324
+ ".dce-file-icon{display:inline-flex;flex:none;align-items:center;justify-content:center;width:28px;height:28px;border-radius:8px;background:var(--dsw-alias-bg-module-platform,#eef1f4)}",
324
325
  ".dce-file-name{min-width:0;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-primary,#1f2328);font-size:13px;font-weight:500;line-height:18px}",
325
326
  ".dce-file-time{flex:none;color:var(--dsw-alias-label-tertiary,#8b93a1);font-size:11px;line-height:16px}",
326
327
  ".dce-preview{margin:0;padding:8px 12px 10px;background:var(--dsw-alias-bg-module-platform,#eef1f4);border-top:1px solid var(--dsw-alias-border-l2,#e5e7eb)}",
@@ -369,9 +370,20 @@ window.__ModuleLoader__.load({
369
370
  var expanded = props.expanded;
370
371
  var preview = props.preview;
371
372
  var onToggle = props.onToggle;
372
- var rowChildren = [
373
- createElement("span", { key: "name", className: "dce-file-name" }, file.file),
374
- ];
373
+ var UI = props.UI;
374
+ var rowChildren = [];
375
+ if (UI !== null && UI !== undefined && UI.FileIcon !== undefined && UI.FileIcon !== null) {
376
+ // ZCode's memory rows open with a category tile; the host classifies
377
+ // the file itself, so a memory .md gets the markdown glyph.
378
+ rowChildren.push(
379
+ createElement(
380
+ "span",
381
+ { key: "icon", className: "dce-file-icon" },
382
+ createElement(UI.FileIcon, { path: file.file, size: 16 }),
383
+ ),
384
+ );
385
+ }
386
+ rowChildren.push(createElement("span", { key: "name", className: "dce-file-name" }, file.file));
375
387
  var time = formatRelative(file.updatedAt, now, t);
376
388
  if (time !== "") {
377
389
  rowChildren.push(createElement("span", { key: "time", className: "dce-file-time" }, time));
@@ -409,26 +421,31 @@ window.__ModuleLoader__.load({
409
421
  return createElement("li", { className: "dce-item" }, children);
410
422
  }
411
423
 
412
- /** Leading "switch scope" marker from the host primitives, or nothing. */
413
- function ScopeMark() {
414
- if (
415
- primitives !== undefined &&
416
- primitives !== null &&
417
- typeof primitives.IconChevronsUpDownOutlineRegular !== "undefined" &&
418
- primitives.IconChevronsUpDownOutlineRegular !== null
419
- ) {
420
- return createElement(primitives.IconChevronsUpDownOutlineRegular, { size: 14, "aria-hidden": "true" });
424
+ /** The host's folder marker for a workspace, or nothing on an old host. */
425
+ function FolderMark(props) {
426
+ var UI = props.UI;
427
+ if (UI !== null && UI !== undefined && UI.Folder !== undefined && UI.Folder !== null) {
428
+ return createElement(UI.Folder, { size: 16, "aria-hidden": "true" });
421
429
  }
422
430
  return null;
423
431
  }
424
432
 
425
433
  /**
426
434
  * The workspace scope selector, ZCode's Settings → Memory shape: one
427
- * scope at a time behind a dropdown (leading marker + current workspace
428
- * label + chevron), never a row of chips. A chip row can only render
435
+ * scope at a time behind a dropdown (folder marker + current workspace
436
+ * label + chevron), never a row of chips — a chip row can only render
429
437
  * when several workspaces are known, so in the common single-workspace
430
438
  * case it renders nothing at all and the capability reads as missing.
431
- * The menu closes on select, on Escape and on a click outside.
439
+ *
440
+ * The list is the host's own `Menu` primitive whenever the host provides
441
+ * it: the same surface the rest of the settings UI uses, and it owns
442
+ * outside-click/Escape handling. A hand-rolled document listener gets
443
+ * that wrong — React flushes this component's effect synchronously for
444
+ * the discrete click that opens the list, so a listener attached there
445
+ * catches that very click and closes the list again (the button then
446
+ * looks dead). The hand-rolled list stays as the old-host fallback and
447
+ * closes on a pointerdown outside its wrapper, which cannot be the click
448
+ * that opened it.
432
449
  */
433
450
  function WorkspaceScopeMenu(props) {
434
451
  var workspaces = props.workspaces;
@@ -439,27 +456,41 @@ window.__ModuleLoader__.load({
439
456
  var openState = useState(false);
440
457
  var open = openState[0];
441
458
  var setOpen = openState[1];
459
+ var wrapRef = useRef(null);
460
+ var hostMenu =
461
+ UI !== null && UI !== undefined && UI.Menu !== undefined && UI.Menu !== null ? UI.Menu : null;
442
462
  useEffect(
443
463
  function () {
444
- if (!open || typeof document === "undefined") {
464
+ if (hostMenu !== null || !open || typeof document === "undefined") {
445
465
  return undefined;
446
466
  }
447
- var close = function () {
448
- setOpen(false);
467
+ var onPointerDown = function (event) {
468
+ var node = wrapRef.current;
469
+ var target = event.target;
470
+ var inside =
471
+ node !== null &&
472
+ node !== undefined &&
473
+ typeof node.contains === "function" &&
474
+ target !== null &&
475
+ target !== undefined &&
476
+ node.contains(target);
477
+ if (!inside) {
478
+ setOpen(false);
479
+ }
449
480
  };
450
481
  var onKeyDown = function (event) {
451
482
  if (event.key === "Escape") {
452
- close();
483
+ setOpen(false);
453
484
  }
454
485
  };
455
- document.addEventListener("click", close);
486
+ document.addEventListener("pointerdown", onPointerDown);
456
487
  document.addEventListener("keydown", onKeyDown);
457
488
  return function () {
458
- document.removeEventListener("click", close);
489
+ document.removeEventListener("pointerdown", onPointerDown);
459
490
  document.removeEventListener("keydown", onKeyDown);
460
491
  };
461
492
  },
462
- [open],
493
+ [open, hostMenu],
463
494
  );
464
495
  var current = null;
465
496
  for (var index = 0; index < workspaces.length; index++) {
@@ -473,18 +504,11 @@ window.__ModuleLoader__.load({
473
504
  }
474
505
  var label = current === null ? "" : current.label || workspaceLabel(current.root);
475
506
  var scopeChildren = [
476
- createElement(ScopeMark, { key: "mark" }),
477
507
  createElement("span", { key: "label", className: "dce-scope-label" }, label),
478
508
  createElement("span", { key: "caret", className: "dce-scope-caret" }, createElement(Chevron, null)),
479
509
  ];
480
- var triggerProps = {
481
- type: "button",
482
- "aria-haspopup": "menu",
483
- "aria-expanded": open ? "true" : "false",
484
- "aria-label": t("scopeLabel"),
485
- onClick: function () {
486
- setOpen(!open);
487
- },
510
+ var toggle = function () {
511
+ setOpen(!open);
488
512
  };
489
513
  var trigger = null;
490
514
  if (UI !== null && UI !== undefined && typeof UI.Button !== "undefined" && UI.Button !== null) {
@@ -493,12 +517,14 @@ window.__ModuleLoader__.load({
493
517
  {
494
518
  key: "trigger",
495
519
  variant: "outline",
496
- size: "sm",
497
- type: triggerProps.type,
498
- "aria-haspopup": triggerProps["aria-haspopup"],
499
- "aria-expanded": triggerProps["aria-expanded"],
500
- "aria-label": triggerProps["aria-label"],
501
- onClick: triggerProps.onClick,
520
+ size: "md",
521
+ icon: createElement(FolderMark, { UI: UI }),
522
+ className: "dce-scope-pill",
523
+ type: "button",
524
+ "aria-haspopup": "menu",
525
+ "aria-expanded": open ? "true" : "false",
526
+ "aria-label": t("scopeLabel"),
527
+ onClick: toggle,
502
528
  },
503
529
  scopeChildren,
504
530
  );
@@ -507,16 +533,44 @@ window.__ModuleLoader__.load({
507
533
  "button",
508
534
  {
509
535
  key: "trigger",
510
- type: triggerProps.type,
536
+ type: "button",
511
537
  className: "dce-scope",
512
- "aria-haspopup": triggerProps["aria-haspopup"],
513
- "aria-expanded": triggerProps["aria-expanded"],
514
- "aria-label": triggerProps["aria-label"],
515
- onClick: triggerProps.onClick,
538
+ "aria-haspopup": "menu",
539
+ "aria-expanded": open ? "true" : "false",
540
+ "aria-label": t("scopeLabel"),
541
+ onClick: toggle,
516
542
  },
517
543
  scopeChildren,
518
544
  );
519
545
  }
546
+ if (hostMenu !== null) {
547
+ return createElement(hostMenu, {
548
+ key: "scope",
549
+ open: open,
550
+ anchor: trigger,
551
+ items: workspaces.map(function (workspace) {
552
+ return {
553
+ id: workspace.root,
554
+ label: workspace.label || workspaceLabel(workspace.root),
555
+ icon: createElement(FolderMark, { UI: UI }),
556
+ };
557
+ }),
558
+ selectedId: selected,
559
+ onSelect: function (id) {
560
+ setOpen(false);
561
+ if (id !== selected) {
562
+ onSelect(id);
563
+ }
564
+ },
565
+ onClose: function () {
566
+ setOpen(false);
567
+ },
568
+ align: "start",
569
+ side: "bottom",
570
+ portal: true,
571
+ selection: "check",
572
+ });
573
+ }
520
574
  var menu = null;
521
575
  if (open) {
522
576
  menu = createElement(
@@ -539,15 +593,12 @@ window.__ModuleLoader__.load({
539
593
  }
540
594
  },
541
595
  },
542
- // The registry allows duplicate titles, so the root
543
- // disambiguates two workspaces that read the same.
544
- createElement("span", { key: "label", className: "dce-menu-label" }, workspace.label || workspaceLabel(workspace.root)),
545
- createElement("span", { key: "root", className: "dce-menu-path" }, workspace.root),
596
+ workspace.label || workspaceLabel(workspace.root),
546
597
  );
547
598
  }),
548
599
  );
549
600
  }
550
- return createElement("div", { key: "scope", className: "dce-scope-wrap" }, trigger, menu);
601
+ return createElement("div", { key: "scope", className: "dce-scope-wrap", ref: wrapRef }, trigger, menu);
551
602
  }
552
603
 
553
604
  /** One workspace's memory store: stats, drift warnings, searchable file list with previews. */
@@ -669,6 +720,7 @@ window.__ModuleLoader__.load({
669
720
  file: file,
670
721
  now: now,
671
722
  t: t,
723
+ UI: UI,
672
724
  expanded: expandedFile === file.file,
673
725
  preview: preview,
674
726
  onToggle: function () {
@@ -896,7 +948,15 @@ window.__ModuleLoader__.load({
896
948
  }
897
949
  var UI = null;
898
950
  if (primitives !== undefined && primitives !== null && typeof primitives.Button !== "undefined") {
899
- UI = { Button: primitives.Button };
951
+ // The scope selector prefers the host's own Menu and folder icon;
952
+ // each stays optional so a host with only Button still gets the
953
+ // hand-rolled list and a plain trigger.
954
+ UI = {
955
+ Button: primitives.Button,
956
+ Menu: primitives.Menu,
957
+ Folder: primitives.IconFolderOpenOutlineRegular,
958
+ FileIcon: primitives.FileTypeIcon,
959
+ };
900
960
  }
901
961
  ctx.slots.inject(SLOT, function () {
902
962
  return ctx.slots.register(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-continual-evolve",
3
- "version": "0.14.1",
3
+ "version": "0.14.2",
4
4
  "description": "ZCode-style workspace memory for DeepSeek Harness: a per-workspace markdown store with session-start index injection.",
5
5
  "license": "MIT",
6
6
  "repository": {