dsh-layered-memory 0.9.0 → 0.9.1

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/dist/client.js CHANGED
@@ -1084,6 +1084,9 @@ var __defProp = Object.defineProperty;
1084
1084
  ".dsh-mem-sub::before {",
1085
1085
  " content: ''; position: absolute; left: -10px; top: 0; width: 10px; height: 100%;",
1086
1086
  "}",
1087
+ // 视口翻转态(下方放不下时 JS 加 .flip):底边锚定行底 +5px 向上生长,
1088
+ // 桥接热区随卡片全高仍然成立(横向过桥不受纵向翻转影响)
1089
+ ".dsh-mem-sub.flip { top: auto; bottom: -5px; }",
1087
1090
  ".dsh-mem-subwrap:hover .dsh-mem-sub, .dsh-mem-subwrap:focus-within .dsh-mem-sub { display: block; }",
1088
1091
  '.dsh-mem-subwrap .dsh-mem-pop-opt[aria-hidden="true"] { cursor: default; }',
1089
1092
  // ── 记忆滑条(Codex 式内联展开,spec v2 §4.3):grid-rows 0fr→1fr 原地长高;
@@ -1219,6 +1222,7 @@ var __defProp = Object.defineProperty;
1219
1222
  })();
1220
1223
  var SLIDER_SCOPES = ["chat", "work", "auto"];
1221
1224
  var FLOW_KEYS = ["follow", "rw", "wo", "paused"];
1225
+ var SUB_EST = FLOW_KEYS.length * 40 + 10;
1222
1226
  var scopeLabel = (k) => t("scope." + k);
1223
1227
  var flowLabel = (k) => t("flow." + k);
1224
1228
  function useMaxHeightFallback(ref, cap, signal) {
@@ -1251,8 +1255,11 @@ var __defProp = Object.defineProperty;
1251
1255
  const [menuOpen, setMenuOpen] = (0, import_react2.useState)(false);
1252
1256
  const [sliderOpen, setSliderOpen] = (0, import_react2.useState)(false);
1253
1257
  const [previewScope, setPreviewScope] = (0, import_react2.useState)(null);
1258
+ const [subFlip, setSubFlip] = (0, import_react2.useState)(false);
1259
+ const [subMaxH, setSubMaxH] = (0, import_react2.useState)(null);
1254
1260
  const wrapRef = (0, import_react2.useRef)(null);
1255
1261
  const menuRef = (0, import_react2.useRef)(null);
1262
+ const subWrapRef = (0, import_react2.useRef)(null);
1256
1263
  const seqRef = (0, import_react2.useRef)(0);
1257
1264
  const load = (0, import_react2.useCallback)(() => {
1258
1265
  if (!sessionId || !rpc) return;
@@ -1307,6 +1314,33 @@ var __defProp = Object.defineProperty;
1307
1314
  document.removeEventListener("keydown", onKey);
1308
1315
  };
1309
1316
  }, [menuOpen]);
1317
+ (0, import_react2.useLayoutEffect)(() => {
1318
+ if (!menuOpen) {
1319
+ setSubFlip(false);
1320
+ setSubMaxH(null);
1321
+ return;
1322
+ }
1323
+ const MARGIN = 8;
1324
+ const measure = () => {
1325
+ const el = subWrapRef.current;
1326
+ if (!el) return;
1327
+ const rect = el.getBoundingClientRect();
1328
+ if (rect.height === 0) return;
1329
+ const below = window.innerHeight - MARGIN - (rect.top - 5);
1330
+ const above = rect.bottom + 5 - MARGIN;
1331
+ const flip = above > below;
1332
+ const avail = Math.max(80, flip ? above : below);
1333
+ setSubFlip(flip && below < SUB_EST);
1334
+ setSubMaxH(Math.min(SUB_EST, avail));
1335
+ };
1336
+ measure();
1337
+ window.addEventListener("scroll", measure, true);
1338
+ window.addEventListener("resize", measure);
1339
+ return () => {
1340
+ window.removeEventListener("scroll", measure, true);
1341
+ window.removeEventListener("resize", measure);
1342
+ };
1343
+ }, [menuOpen, sliderOpen]);
1310
1344
  const commitMode = (next) => {
1311
1345
  if (!rpc || !sessionId || mode === null || next === mode) return;
1312
1346
  const prev = mode;
@@ -1367,6 +1401,9 @@ var __defProp = Object.defineProperty;
1367
1401
  };
1368
1402
  if (!sessionId || !rpc) return null;
1369
1403
  ensureThemeStyle();
1404
+ const MENU_MAX_H = 480;
1405
+ const menuMaxH = useMenuMaxHeight(menuRef, MENU_MAX_H, menuOpen);
1406
+ const menuClamped = menuMaxH < MENU_MAX_H;
1370
1407
  const paused = mode === "off";
1371
1408
  const scope = paused ? resumeScope ?? "auto" : mode ?? "auto";
1372
1409
  const flow = paused ? "paused" : recall === false ? "wo" : recall === true ? "rw" : "follow";
@@ -1394,80 +1431,104 @@ var __defProp = Object.defineProperty;
1394
1431
  ]
1395
1432
  }
1396
1433
  ),
1397
- menuOpen ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-mem-menu", ref: menuRef, role: "menu", "aria-label": t("chip.base"), children: [
1398
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1399
- "button",
1400
- {
1401
- type: "button",
1402
- className: "dsh-mem-pop-opt dsh-mem-sl-row" + (sliderOpen ? " on" : ""),
1403
- disabled: paused,
1404
- "aria-expanded": sliderOpen,
1405
- onClick: () => setSliderOpen(!sliderOpen),
1406
- children: [
1407
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-mem-pop-opt-label", children: t("row.scope") }),
1408
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-mem-subval", children: scopeLabel(shownScope) }),
1409
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-mem-subchev", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NIconChevronRight14, {}) })
1410
- ]
1411
- }
1412
- ),
1413
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-mem-sl-reveal" + (sliderOpen ? " open" : ""), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-mem-sl-inner", children: [
1414
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-mem-sl-labels", "aria-hidden": true, children: [
1415
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: scopeLabel("chat") }),
1416
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: scopeLabel("work") }),
1417
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: scopeLabel("auto") })
1418
- ] }),
1419
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1420
- ScopeSlider,
1421
- {
1422
- scope,
1423
- disabled: paused,
1424
- onPreview: (k) => setPreviewScope(k),
1425
- onCommit: (k) => {
1426
- setPreviewScope(null);
1427
- commitMode(k);
1434
+ menuOpen ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1435
+ "div",
1436
+ {
1437
+ className: "dsh-mem-menu",
1438
+ ref: menuRef,
1439
+ role: "menu",
1440
+ "aria-label": t("chip.base"),
1441
+ style: menuClamped ? { maxHeight: menuMaxH, overflowY: "auto" } : void 0,
1442
+ children: [
1443
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1444
+ "button",
1445
+ {
1446
+ type: "button",
1447
+ className: "dsh-mem-pop-opt dsh-mem-sl-row" + (sliderOpen ? " on" : ""),
1448
+ disabled: paused,
1449
+ "aria-expanded": sliderOpen,
1450
+ onClick: () => setSliderOpen(!sliderOpen),
1451
+ children: [
1452
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-mem-pop-opt-label", children: t("row.scope") }),
1453
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-mem-subval", children: scopeLabel(shownScope) }),
1454
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-mem-subchev", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NIconChevronRight14, {}) })
1455
+ ]
1428
1456
  }
1429
- }
1430
- )
1431
- ] }) }),
1432
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-mem-subwrap", hidden: sliderOpen, children: [
1433
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1434
- "button",
1435
- {
1436
- type: "button",
1437
- className: "dsh-mem-pop-opt",
1438
- tabIndex: 0,
1439
- "aria-haspopup": "menu",
1440
- onMouseDown: (e) => {
1441
- e.preventDefault();
1442
- },
1443
- children: [
1444
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-mem-pop-opt-label", children: t("row.flow") }),
1445
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-mem-subval", children: flowLabel(flow) }),
1446
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-mem-subchev", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NIconChevronRight14, {}) })
1447
- ]
1448
- }
1449
- ),
1450
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-mem-sub", role: "menu", "aria-label": t("row.flow"), children: FLOW_KEYS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1451
- "button",
1452
- {
1453
- type: "button",
1454
- role: "menuitemradio",
1455
- "aria-checked": flow === k,
1456
- className: "dsh-mem-pop-opt" + (flow === k ? " on" : ""),
1457
- onClick: () => {
1458
- applyFlow(k);
1459
- setMenuOpen(false);
1460
- setSliderOpen(false);
1461
- },
1462
- children: [
1463
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-mem-pop-opt-label", children: flowLabel(k) }),
1464
- flow === k ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-mem-pop-check", children: "✓" }) : null
1465
- ]
1466
- },
1467
- k
1468
- )) })
1469
- ] })
1470
- ] }) : null,
1457
+ ),
1458
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-mem-sl-reveal" + (sliderOpen ? " open" : ""), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-mem-sl-inner", children: [
1459
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-mem-sl-labels", "aria-hidden": true, children: [
1460
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: scopeLabel("chat") }),
1461
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: scopeLabel("work") }),
1462
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: scopeLabel("auto") })
1463
+ ] }),
1464
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1465
+ ScopeSlider,
1466
+ {
1467
+ scope,
1468
+ disabled: paused,
1469
+ onPreview: (k) => setPreviewScope(k),
1470
+ onCommit: (k) => {
1471
+ setPreviewScope(null);
1472
+ commitMode(k);
1473
+ }
1474
+ }
1475
+ )
1476
+ ] }) }),
1477
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-mem-subwrap", ref: subWrapRef, hidden: sliderOpen, children: [
1478
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1479
+ "button",
1480
+ {
1481
+ type: "button",
1482
+ className: "dsh-mem-pop-opt",
1483
+ tabIndex: 0,
1484
+ "aria-haspopup": "menu",
1485
+ onMouseDown: (e) => {
1486
+ e.preventDefault();
1487
+ },
1488
+ children: [
1489
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-mem-pop-opt-label", children: t("row.flow") }),
1490
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-mem-subval", children: flowLabel(flow) }),
1491
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-mem-subchev", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NIconChevronRight14, {}) })
1492
+ ]
1493
+ }
1494
+ ),
1495
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1496
+ "div",
1497
+ {
1498
+ className: "dsh-mem-sub" + (subFlip ? " flip" : ""),
1499
+ role: "menu",
1500
+ "aria-label": t("row.flow"),
1501
+ style: {
1502
+ maxHeight: subMaxH ?? void 0,
1503
+ // 只在 maxHeight 真正夹紧(< 设计高)时才开滚动:overflow 会把
1504
+ // 卡外左侧的 ::before 桥接热区裁掉,hover 链在 6px 空隙处断裂
1505
+ overflowY: subMaxH != null && subMaxH < SUB_EST ? "auto" : void 0
1506
+ },
1507
+ children: FLOW_KEYS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1508
+ "button",
1509
+ {
1510
+ type: "button",
1511
+ role: "menuitemradio",
1512
+ "aria-checked": flow === k,
1513
+ className: "dsh-mem-pop-opt" + (flow === k ? " on" : ""),
1514
+ onClick: () => {
1515
+ applyFlow(k);
1516
+ setMenuOpen(false);
1517
+ setSliderOpen(false);
1518
+ },
1519
+ children: [
1520
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-mem-pop-opt-label", children: flowLabel(k) }),
1521
+ flow === k ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-mem-pop-check", children: "✓" }) : null
1522
+ ]
1523
+ },
1524
+ k
1525
+ ))
1526
+ }
1527
+ )
1528
+ ] })
1529
+ ]
1530
+ }
1531
+ ) : null,
1471
1532
  error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { role: "alert", style: { position: "absolute", top: "100%", left: 0, fontSize: 11, color: "var(--dsh-mem-danger)", whiteSpace: "nowrap", zIndex: 2 }, children: t("err.load") }) : null
1472
1533
  ] });
1473
1534
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-layered-memory",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "L0~L3 分层蒸馏记忆插件 for DeepSeek Harness:自动捕获对话(L0)、抽取原子记忆(L1)、整合场景块(L2)、蒸馏核心画像/团队方法论(L3),并在模型步骤前自动召回注入。移植自 MemoryCore (TencentDB Agent Memory) 的管线设计。",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",