dsh-memsys 0.2.1 → 0.2.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/client.js +72 -8
- package/package.json +2 -2
- package/src/runtime/summary.mjs +2 -1
- package/test/p6-ui-summary.test.mjs +2 -1
package/client.js
CHANGED
|
@@ -27,19 +27,83 @@ window.__ModuleLoader__.load({
|
|
|
27
27
|
return [state, setState];
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
// Pure CSS/DOM "memory balls on a shelf". No 3D library, no build step,
|
|
31
|
+
// no external resource: gradient + inset shadow + one float keyframe.
|
|
32
|
+
const BALL_CSS = [
|
|
33
|
+
'@keyframes msFloat{0%,100%{transform:translateY(0)}50%{transform:translateY(-3px)}}',
|
|
34
|
+
'.ms-panel{padding:16px;line-height:1.7}',
|
|
35
|
+
'.ms-counts{margin:0 0 12px;opacity:.85}',
|
|
36
|
+
'.ms-shelf{display:flex;gap:16px;align-items:flex-end;padding:12px 16px 8px;margin:0 0 10px;border-radius:12px;',
|
|
37
|
+
'background:linear-gradient(180deg,rgba(127,127,127,.10),rgba(127,127,127,.03));',
|
|
38
|
+
'box-shadow:inset 0 -1px 0 rgba(127,127,127,.28)}',
|
|
39
|
+
'.ms-ball{width:44px;height:44px;border-radius:50%;border:0;padding:0;cursor:pointer;position:relative;',
|
|
40
|
+
'background:radial-gradient(circle at 32% 27%,rgba(255,255,255,.95),rgba(255,255,255,.28) 16%,var(--ms-c) 60%,rgba(0,0,0,.45) 100%);',
|
|
41
|
+
'box-shadow:inset -6px -8px 14px rgba(0,0,0,.30),inset 6px 6px 12px rgba(255,255,255,.22),0 4px 10px rgba(0,0,0,.18);',
|
|
42
|
+
'animation:msFloat 5s ease-in-out infinite;transition:transform .15s ease,box-shadow .15s ease}',
|
|
43
|
+
'.ms-ball:hover,.ms-ball:focus-visible{transform:scale(1.06);',
|
|
44
|
+
'box-shadow:inset -6px -8px 14px rgba(0,0,0,.30),inset 6px 6px 12px rgba(255,255,255,.22),0 0 18px 4px var(--ms-glow);outline:none}',
|
|
45
|
+
'.ms-ball[aria-pressed="true"]{transform:scale(1.06);box-shadow:0 0 18px 4px var(--ms-glow)}',
|
|
46
|
+
'.ms-empty{width:44px;height:44px;border-radius:50%;border:1px dashed rgba(127,127,127,.35);background:rgba(127,127,127,.05)}',
|
|
47
|
+
'.ms-card{margin-top:10px;padding:12px 14px;border-radius:10px;background:rgba(127,127,127,.08);font-size:.95em}',
|
|
48
|
+
'.ms-hint{opacity:.75;margin:6px 0 0}',
|
|
49
|
+
'@media (prefers-reduced-motion:reduce){.ms-ball{animation:none}}',
|
|
50
|
+
].join('');
|
|
51
|
+
const BALL_COLORS = ['#3fa7ff', '#5b8cff', '#7a6bff', '#8f5bd8', '#8450a8'];
|
|
52
|
+
const BALL_GLOWS = [
|
|
53
|
+
'rgba(63,167,255,.45)',
|
|
54
|
+
'rgba(91,140,255,.45)',
|
|
55
|
+
'rgba(122,107,255,.45)',
|
|
56
|
+
'rgba(143,91,216,.45)',
|
|
57
|
+
'rgba(132,80,168,.45)',
|
|
58
|
+
];
|
|
59
|
+
const EMPTY_SLOTS = 5;
|
|
60
|
+
const CARD_TEXT_LIMIT = 120;
|
|
61
|
+
|
|
30
62
|
function MemoryPanel() {
|
|
31
63
|
const [state] = useSummary();
|
|
32
|
-
|
|
33
|
-
if (state
|
|
64
|
+
const [selectedId, setSelectedId] = React.useState(null);
|
|
65
|
+
if (!state) return h('p', { className: 'ms-panel' }, '读取记忆…');
|
|
66
|
+
if (state.error) return h('p', { className: 'ms-panel' }, '暂时读不到记忆数据。');
|
|
67
|
+
const recent = state.recent ?? [];
|
|
68
|
+
const selected = recent.find((memory) => memory.id === selectedId) ?? null;
|
|
69
|
+
// Pad with ghost slots so an almost-empty shelf keeps its shape.
|
|
70
|
+
const shelf = [];
|
|
71
|
+
for (let index = 0; index < Math.max(recent.length, EMPTY_SLOTS); index += 1) shelf.push(recent[index] ?? null);
|
|
34
72
|
return h(
|
|
35
73
|
'div',
|
|
36
|
-
{
|
|
74
|
+
{ className: 'ms-panel' },
|
|
75
|
+
h('style', { dangerouslySetInnerHTML: { __html: BALL_CSS } }),
|
|
37
76
|
h('h2', { style: { margin: '0 0 8px' } }, '记忆'),
|
|
38
|
-
h('p',
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
77
|
+
h('p', { className: 'ms-counts' },
|
|
78
|
+
`记忆 ${state.memories} 条 · 候选 ${state.candidates} 条 · 待确认惦记 ${state.pending_due} 条`),
|
|
79
|
+
h('div', { className: 'ms-shelf', role: 'list', 'aria-label': '最近的记忆' },
|
|
80
|
+
shelf.map((memory, index) => memory === null
|
|
81
|
+
? h('span', { key: `empty-${index}`, className: 'ms-empty', 'aria-hidden': true })
|
|
82
|
+
: h('button', {
|
|
83
|
+
key: memory.id,
|
|
84
|
+
type: 'button',
|
|
85
|
+
role: 'listitem',
|
|
86
|
+
className: 'ms-ball',
|
|
87
|
+
'aria-pressed': selectedId === memory.id,
|
|
88
|
+
'aria-label': memory.text,
|
|
89
|
+
title: memory.text,
|
|
90
|
+
style: {
|
|
91
|
+
'--ms-c': BALL_COLORS[Math.min(index, BALL_COLORS.length - 1)],
|
|
92
|
+
'--ms-glow': BALL_GLOWS[Math.min(index, BALL_GLOWS.length - 1)],
|
|
93
|
+
animationDelay: `${index * 0.35}s`,
|
|
94
|
+
width: `${44 - Math.min(index, 3) * 2}px`,
|
|
95
|
+
height: `${44 - Math.min(index, 3) * 2}px`,
|
|
96
|
+
},
|
|
97
|
+
onClick: () => setSelectedId(selectedId === memory.id ? null : memory.id),
|
|
98
|
+
})),
|
|
99
|
+
),
|
|
100
|
+
recent.length === 0
|
|
101
|
+
? h('p', { className: 'ms-hint' }, '还没有记忆。去跟模型说一句「记住:…」吧')
|
|
102
|
+
: h('p', { className: 'ms-hint' }, '点亮一颗球,看看它装的是什么。'),
|
|
103
|
+
selected
|
|
104
|
+
? h('div', { className: 'ms-card', key: selected.id },
|
|
105
|
+
Array.from(selected.text).slice(0, CARD_TEXT_LIMIT).join(''))
|
|
106
|
+
: null,
|
|
43
107
|
);
|
|
44
108
|
}
|
|
45
109
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
2
|
"name": "dsh-memsys",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "A memory system plugin for DeepSeek Harness: append-only facts, candidates and adjudication, dreams, and budget-aware context injection.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
package/src/runtime/summary.mjs
CHANGED
|
@@ -14,7 +14,8 @@ import {
|
|
|
14
14
|
} from './ui-config.mjs';
|
|
15
15
|
|
|
16
16
|
const RECENT_LIMIT = 5;
|
|
17
|
-
|
|
17
|
+
// 主人要求:卡片展示 120 字(按码点截断,预算保护仍在 host 侧)
|
|
18
|
+
const TEXT_LIMIT = 120;
|
|
18
19
|
const BODY_LIMIT = 4096;
|
|
19
20
|
|
|
20
21
|
const truncate = (text) => Array.from(String(text ?? '')).slice(0, TEXT_LIMIT).join('');
|
|
@@ -283,7 +283,8 @@ test('P6/UI: counts only open candidates, pending-due memories, and truncates re
|
|
|
283
283
|
assert.equal(summary.candidates, 2, 'promoted candidates are history, not "候选"');
|
|
284
284
|
assert.equal(summary.pending_due, 1);
|
|
285
285
|
assert.equal(summary.recent.length, 2);
|
|
286
|
-
|
|
286
|
+
// 主人要求 120 码点(原来是 80);这里改的是需求,不是放宽判据。
|
|
287
|
+
assert.equal(Array.from(summary.recent.find((row) => row.id === dueMemory.id).text).length, 120);
|
|
287
288
|
});
|
|
288
289
|
|
|
289
290
|
test('P6/UI: routes reject off-loopback and cross-origin callers', () => {
|