sage-mem 0.9.13 → 0.9.14
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 +155 -92
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -112,13 +112,55 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
112
112
|
// 配色:五个色若取高饱和高亮度(#ffd27d / #6fe3ff / #b18cff / #7dffb0),
|
|
113
113
|
// 配上星芒与脉冲环,整体读起来像贴纸。现在统一降饱和、压亮度 ——
|
|
114
114
|
// 深空画布上够辨认,又不抢眼。
|
|
115
|
+
//
|
|
116
|
+
// 两档取值:`color` = 深色档(夜空画布),`light` = 浅色档(纸本设色)。
|
|
117
|
+
// 浅色档不用「深色档降亮度」那种做法 —— 高饱和冷色压在暖纸上会发脏;
|
|
118
|
+
// 换成明度接近的矿物颜料色(赭石/石青/紫墨/石绿/墨),色相错开才分得清类型。
|
|
115
119
|
const KIND_META = {
|
|
116
|
-
user: { label: "用户", color: "#e0c07a" },
|
|
117
|
-
feedback: { label: "反馈", color: "#7ec8da" },
|
|
118
|
-
project: { label: "项目", color: "#ac9edc" },
|
|
119
|
-
reference: { label: "参考", color: "#92ceaa" },
|
|
120
|
-
special: { label: "特殊", color: "#bcc3d0" },
|
|
120
|
+
user: { label: "用户", color: "#e0c07a", light: "#a4652c" },
|
|
121
|
+
feedback: { label: "反馈", color: "#7ec8da", light: "#3a6f8f" },
|
|
122
|
+
project: { label: "项目", color: "#ac9edc", light: "#6a5b8f" },
|
|
123
|
+
reference: { label: "参考", color: "#92ceaa", light: "#4a7c63" },
|
|
124
|
+
special: { label: "特殊", color: "#bcc3d0", light: "#5b5750" },
|
|
121
125
|
};
|
|
126
|
+
/**
|
|
127
|
+
* 宿主主题挂钩 —— 与 ui-theme 同一处:body[data-ds-dark-theme] 存在即深色档。
|
|
128
|
+
*/
|
|
129
|
+
function isDarkTheme() {
|
|
130
|
+
return typeof document !== "undefined" && !!document.body
|
|
131
|
+
&& document.body.hasAttribute("data-ds-dark-theme");
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** 类型色按当前主题取(meta.color / meta.light)。 */
|
|
135
|
+
function kindColor(meta) {
|
|
136
|
+
const m = meta || KIND_META.special;
|
|
137
|
+
return (isDarkTheme() ? m.color : m.light) || m.color;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* 画布调色板:深色档 = 夜空,浅色档 = 暖纸 + 晕染。
|
|
142
|
+
* 两套渲染器(WebGL / canvas2d)都从这里取色,别各写一份。
|
|
143
|
+
* bgGl 是 WebGL shader 用的 0-1 分量(与 bg2d 是同一色,写法不同而已)。
|
|
144
|
+
*/
|
|
145
|
+
function starmapPalette() {
|
|
146
|
+
if (isDarkTheme()) {
|
|
147
|
+
return {
|
|
148
|
+
bg2d: ["#0b1026", "#101731"],
|
|
149
|
+
bgGl: [[0.105, 0.117, 0.145], [0.043, 0.050, 0.065]],
|
|
150
|
+
dust: "#9fb3dd",
|
|
151
|
+
archive: [0.46, 0.50, 0.58],
|
|
152
|
+
vignette: null,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
bg2d: ["#faf6ed", "#e9e0cd"],
|
|
157
|
+
bgGl: [[0.980, 0.965, 0.929], [0.914, 0.878, 0.804]],
|
|
158
|
+
dust: "#c9bfa6",
|
|
159
|
+
archive: [0.62, 0.60, 0.55],
|
|
160
|
+
vignette: { color: "#7a6a48", a: 0.10 },
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
122
164
|
const KIND_ORDER = ["user", "feedback", "project", "reference", "special"];
|
|
123
165
|
const FRESH_MS = 7 * 86400000;
|
|
124
166
|
|
|
@@ -370,79 +412,71 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
370
412
|
// ── 样式(data-plugin-css 约定,幂等注入)──
|
|
371
413
|
const CSS = [
|
|
372
414
|
".smap-footer-btn{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--dsw-alias-border-l1,transparent);background:transparent;color:var(--dsw-alias-label-secondary,inherit);border-radius:8px;padding:4px 10px;font-size:12px;cursor:pointer;font-family:inherit;white-space:nowrap;}",
|
|
373
|
-
".smap-fab{position:fixed;right:0;top:15%;transform:translateY(-50%);z-index:900;width:36px;height:64px;border-radius:12px 0 0 12px;border:1px solid
|
|
374
|
-
".smap-fab:hover{opacity:1;width:46px;background:
|
|
415
|
+
".smap-fab{position:fixed;right:0;top:15%;transform:translateY(-50%);z-index:900;width:36px;height:64px;border-radius:12px 0 0 12px;border:1px solid var(--dsw-alias-border-l1);border-right:none;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-brand-primary);font-size:17px;cursor:pointer;display:flex;align-items:center;justify-content:center;opacity:.5;pointer-events:auto;transition:opacity .15s,background .15s,width .15s;box-shadow:-4px 0 18px rgba(0,0,0,.35);font-family:inherit;}",
|
|
416
|
+
".smap-fab:hover{opacity:1;width:46px;background:var(--dsw-alias-bg-layer-1);}",
|
|
375
417
|
".smap-footer-btn:hover{color:var(--dsw-alias-label-primary,inherit);border-color:var(--dsw-alias-border-l2,#8886);}",
|
|
376
418
|
".smap-footer-icon{font-size:13px;line-height:1;}",
|
|
377
|
-
".smap-veil{position:fixed;inset:0;z-index:1000;background:rgba(
|
|
378
|
-
".smap-modal{width:min(980px,94vw);height:min(680px,92vh);background:
|
|
419
|
+
".smap-veil{position:fixed;inset:0;z-index:1000;background:var(--dsw-alias-bg-mask-1,rgba(0,0,0,.45));backdrop-filter:blur(3px);display:flex;align-items:center;justify-content:center;pointer-events:auto;animation:smap-fade-in .18s ease-out;}",
|
|
420
|
+
".smap-modal{width:min(980px,94vw);height:min(680px,92vh);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);border-radius:16px;box-shadow:0 24px 80px rgba(0,0,0,.55);padding:16px 18px;display:flex;flex-direction:column;overflow:hidden;pointer-events:auto;animation:smap-pop-in .22s cubic-bezier(.2,.9,.3,1.15);}",
|
|
379
421
|
"@keyframes smap-fade-in{from{opacity:0}to{opacity:1}}",
|
|
380
422
|
"@keyframes smap-pop-in{from{opacity:0;transform:scale(.96) translateY(10px)}to{opacity:1;transform:none}}",
|
|
381
|
-
|
|
382
|
-
//
|
|
383
|
-
// 不这么做的下场:宿主切到**浅色主题**后,这个组件里硬编码的
|
|
384
|
-
// 浅色文字(#dbe4f5 系)落在白色卡片上,标题、搜索框、图例几乎看不见 ——
|
|
385
|
-
// 一片「重影」。星图本来就不打算跟随宿主主题(它是一张夜空),
|
|
386
|
-
// 那就把边界画清楚:整块深色,而不是只有中间那半块。
|
|
387
|
-
".smap-card{background:#0c0e12;border-radius:10px;padding:12px 14px;color:#e3e7ef;display:flex;flex-direction:column;flex:1;min-height:0;}",
|
|
423
|
+
".smap-card{background:var(--dsw-alias-bg-layer-1);border-radius:10px;padding:12px 14px;color:var(--dsw-alias-label-primary);display:flex;flex-direction:column;flex:1;min-height:0;}",
|
|
388
424
|
".smap-head{display:flex;align-items:center;gap:10px;margin-bottom:10px;flex-wrap:wrap;}",
|
|
389
425
|
".smap-title{font-size:15px;font-weight:600;letter-spacing:.06em;}",
|
|
390
|
-
".smap-sub{font-size:11px;
|
|
426
|
+
".smap-sub{font-size:11px;color:var(--dsw-alias-label-secondary);}",
|
|
391
427
|
".smap-spacer{flex:1;}",
|
|
392
|
-
".smap-search{background:
|
|
393
|
-
".smap-search:focus{border-color:
|
|
394
|
-
".smap-search::placeholder{color:
|
|
395
|
-
".smap-btn{background:
|
|
396
|
-
".smap-btn:hover{background:
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
".smap-btn.on{border-color:rgba(150,170,230,.78);background:rgba(122,158,255,.20);color:#eef3ff;}",
|
|
400
|
-
'.smap-ar-toggle[aria-pressed="false"]{opacity:.60;}',
|
|
428
|
+
".smap-search{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:8px;color:var(--dsw-alias-label-primary);font-size:12px;padding:4px 9px;outline:none;width:160px;font-family:inherit;}",
|
|
429
|
+
".smap-search:focus{border-color:var(--dsw-alias-border-l2);}",
|
|
430
|
+
".smap-search::placeholder{color:var(--dsw-alias-label-tertiary);}",
|
|
431
|
+
".smap-btn{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:8px;color:var(--dsw-alias-label-primary);font-size:12px;padding:4px 10px;cursor:pointer;font-family:inherit;}",
|
|
432
|
+
".smap-btn:hover{background:var(--dsw-alias-bg-layer-3,var(--dsw-alias-bg-layer-2));}",
|
|
433
|
+
".smap-btn.on{border-color:var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3,var(--dsw-alias-bg-layer-2));color:var(--dsw-alias-label-primary);}",
|
|
434
|
+
".smap-ar-toggle[aria-pressed=\"false\"]{opacity:.60;}",
|
|
401
435
|
".smap-btn:disabled{opacity:.5;cursor:default;}",
|
|
402
|
-
".smap-btn-primary{border-color:
|
|
436
|
+
".smap-btn-primary{border-color:var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3,var(--dsw-alias-bg-layer-2));}",
|
|
403
437
|
".smap-wrap{position:relative;flex:1;min-height:0;}",
|
|
404
438
|
".smap-canvas{position:absolute;inset:0;width:100%;height:100%;display:block;border-radius:10px;cursor:crosshair;}",
|
|
405
|
-
".smap-tip{position:absolute;z-index:5;pointer-events:none;max-width:280px;background:
|
|
439
|
+
".smap-tip{position:absolute;z-index:5;pointer-events:none;max-width:280px;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);border-radius:8px;padding:8px 10px;font-size:12px;line-height:1.55;box-shadow:0 6px 24px rgba(0,0,0,.45);}",
|
|
406
440
|
".smap-tip-title{font-weight:600;margin-bottom:2px;}",
|
|
407
|
-
".smap-tip-kind{
|
|
408
|
-
".smap-tip-desc{
|
|
409
|
-
".smap-panel{position:absolute;top:10px;right:10px;bottom:10px;width:min(400px,54%);background:
|
|
441
|
+
".smap-tip-kind{color:var(--dsw-alias-label-secondary);font-size:11px;margin-bottom:2px;}",
|
|
442
|
+
".smap-tip-desc{color:var(--dsw-alias-label-secondary);}",
|
|
443
|
+
".smap-panel{position:absolute;top:10px;right:10px;bottom:10px;width:min(400px,54%);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;padding:12px 14px;overflow-y:auto;z-index:6;font-size:13px;line-height:1.7;box-shadow:-8px 0 30px rgba(0,0,0,.35);}",
|
|
410
444
|
".smap-panel-head{display:flex;align-items:flex-start;gap:8px;margin-bottom:4px;}",
|
|
411
445
|
".smap-panel-title{font-weight:600;font-size:14px;flex:1;}",
|
|
412
|
-
".smap-panel-meta{font-size:11px;
|
|
413
|
-
".smap-panel-arch{display:flex;flex-direction:column;gap:6px;border:1px solid
|
|
446
|
+
".smap-panel-meta{font-size:11px;color:var(--dsw-alias-label-secondary);margin-bottom:8px;}",
|
|
447
|
+
".smap-panel-arch{display:flex;flex-direction:column;gap:6px;border:1px solid var(--dsw-alias-border-l1);border-radius:8px;padding:8px 10px;background:var(--dsw-alias-bg-layer-2);}",
|
|
414
448
|
".smap-meta-dot{display:inline-block;width:7px;height:7px;border-radius:50%;margin-right:4px;vertical-align:-1px;}",
|
|
415
449
|
".smap-legend{display:flex;flex-wrap:wrap;gap:14px;margin-top:10px;font-size:12px;align-items:center;}",
|
|
416
450
|
".smap-lg-item{cursor:pointer;user-select:none;}",
|
|
417
451
|
".smap-lg-item.off{opacity:.32;}",
|
|
418
452
|
".smap-dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:5px;vertical-align:-1px;}",
|
|
419
|
-
".smap-count{margin-left:auto;
|
|
420
|
-
".smap-status{padding:28px;text-align:center;
|
|
421
|
-
".smap-empty{position:absolute;left:0;right:0;top:46%;text-align:center;color:
|
|
422
|
-
".smap-replay-on{border-color:
|
|
423
|
-
".smap-replay-date{font-size:11px;
|
|
453
|
+
".smap-count{margin-left:auto;color:var(--dsw-alias-label-secondary);font-size:11px;}",
|
|
454
|
+
".smap-status{padding:28px;text-align:center;color:var(--dsw-alias-label-secondary);font-size:13px;}",
|
|
455
|
+
".smap-empty{position:absolute;left:0;right:0;top:46%;text-align:center;color:var(--dsw-alias-label-tertiary);font-size:13px;pointer-events:none;z-index:4;}",
|
|
456
|
+
".smap-replay-on{border-color:var(--dsw-alias-brand-primary)!important;color:var(--dsw-alias-brand-primary)!important;}",
|
|
457
|
+
".smap-replay-date{font-size:11px;color:var(--dsw-alias-brand-primary);font-variant-numeric:tabular-nums;}",
|
|
424
458
|
".smap-md-h{font-weight:600;margin:12px 0 4px;}",
|
|
425
459
|
".smap-md-h1{font-size:16px;}",
|
|
426
460
|
".smap-md-h2{font-size:14.5px;}",
|
|
427
461
|
".smap-md-h3,.smap-md-h4{font-size:13.5px;opacity:.92;}",
|
|
428
462
|
".smap-md-p{margin:4px 0;}",
|
|
429
463
|
".smap-md-list{margin:4px 0;padding-left:20px;}",
|
|
430
|
-
".smap-md-code{background:
|
|
431
|
-
".smap-md-pre{background:
|
|
432
|
-
".smap-md-quote{border-left:3px solid
|
|
433
|
-
".smap-md-wiki{color
|
|
434
|
-
".smap-md-hr{border:none;border-top:1px solid
|
|
435
|
-
".smap-viewtoggle{display:inline-flex;border:1px solid
|
|
436
|
-
".smap-vt-btn{background:transparent;border:none;color:
|
|
437
|
-
".smap-vt-btn.on{background:
|
|
464
|
+
".smap-md-code{background:var(--dsw-alias-bg-layer-3,var(--dsw-alias-bg-layer-2));padding:1px 5px;border-radius:4px;font-size:12px;}",
|
|
465
|
+
".smap-md-pre{background:var(--dsw-alias-bg-layer-2);padding:8px 10px;border-radius:8px;font-size:12px;overflow-x:auto;white-space:pre-wrap;margin:6px 0;}",
|
|
466
|
+
".smap-md-quote{border-left:3px solid var(--dsw-alias-border-l2);margin:6px 0;padding:2px 10px;color:var(--dsw-alias-label-secondary);}",
|
|
467
|
+
".smap-md-wiki{color:var(--dsw-alias-brand-primary);}",
|
|
468
|
+
".smap-md-hr{border:none;border-top:1px solid var(--dsw-alias-border-l1);margin:10px 0;}",
|
|
469
|
+
".smap-viewtoggle{display:inline-flex;border:1px solid var(--dsw-alias-border-l1);border-radius:8px;overflow:hidden;}",
|
|
470
|
+
".smap-vt-btn{background:transparent;border:none;color:var(--dsw-alias-label-secondary);font-size:12px;padding:4px 10px;cursor:pointer;font-family:inherit;}",
|
|
471
|
+
".smap-vt-btn.on{background:var(--dsw-alias-bg-layer-3,var(--dsw-alias-bg-layer-2));color:var(--dsw-alias-label-primary);}",
|
|
438
472
|
".smap-timeline{position:absolute;inset:0;overflow-y:auto;display:flex;flex-direction:column;gap:6px;padding:4px 6px 4px 2px;}",
|
|
439
|
-
".smap-tl-item{display:flex;align-items:baseline;gap:8px;background:
|
|
440
|
-
".smap-tl-item:hover{background:
|
|
473
|
+
".smap-tl-item{display:flex;align-items:baseline;gap:8px;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:8px;padding:7px 10px;cursor:pointer;flex:none;}",
|
|
474
|
+
".smap-tl-item:hover{background:var(--dsw-alias-bg-layer-3,var(--dsw-alias-bg-layer-2));border-color:var(--dsw-alias-border-l1);}",
|
|
441
475
|
".smap-tl-item--arch{opacity:.75;}",
|
|
442
|
-
".smap-ar-badge{font-size:10.5px;
|
|
443
|
-
".smap-tl-date{font-size:11px;
|
|
476
|
+
".smap-ar-badge{font-size:10.5px;color:var(--dsw-alias-label-secondary);border:1px solid var(--dsw-alias-border-l1);border-radius:5px;padding:0 5px;flex:none;align-self:center;}",
|
|
477
|
+
".smap-tl-date{font-size:11px;color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none;}",
|
|
444
478
|
".smap-tl-title{font-size:13px;font-weight:600;flex:none;max-width:40%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}",
|
|
445
|
-
".smap-tl-desc{font-size:12px;
|
|
479
|
+
".smap-tl-desc{font-size:12px;color:var(--dsw-alias-label-secondary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;}",
|
|
446
480
|
].join("\n");
|
|
447
481
|
|
|
448
482
|
function injectCss() {
|
|
@@ -463,14 +497,15 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
463
497
|
}
|
|
464
498
|
|
|
465
499
|
// 归档星(暗星)的用色:把类型色往石板灰上压 —— 它得「还在图上、一眼数得出来」,
|
|
466
|
-
// 又要一眼看出不是活记忆。亮度靠 shader 那一档(0.52
|
|
467
|
-
|
|
500
|
+
// 又要一眼看出不是活记忆。亮度靠 shader 那一档(0.52),这里负责色相:
|
|
501
|
+
// 深色档是冷石板灰;浅色档换成暖灰,否则灰点落在纸上看不见。
|
|
468
502
|
function dimRgb(hex) {
|
|
469
503
|
const c = hexToRgb(hex);
|
|
504
|
+
const g = starmapPalette().archive;
|
|
470
505
|
return [
|
|
471
|
-
c[0] * 0.26 +
|
|
472
|
-
c[1] * 0.26 +
|
|
473
|
-
c[2] * 0.26 +
|
|
506
|
+
c[0] * 0.26 + g[0] * 0.74,
|
|
507
|
+
c[1] * 0.26 + g[1] * 0.74,
|
|
508
|
+
c[2] * 0.26 + g[2] * 0.74,
|
|
474
509
|
];
|
|
475
510
|
}
|
|
476
511
|
function dimCss(hex) {
|
|
@@ -483,17 +518,21 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
483
518
|
// 「会呼吸的彩色底」正是廉价感的一大来源,而且每帧都要重算噪声。
|
|
484
519
|
// 现在只剩一条静态径向渐变:中心略亮的中性深灰,边缘更暗。uTime 保留声明
|
|
485
520
|
// 只为让 uniform 列表不变(用不到时 getUniformLocation 返回 null,调用是安全空操作)。
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
521
|
+
// 底色由调色板给(深色档=夜空深灰,浅色档=暖纸),所以是函数不是常量。
|
|
522
|
+
function smapBgFs(inner, outer) {
|
|
523
|
+
const v = (c) => c.map((x) => x.toFixed(3)).join(", ");
|
|
524
|
+
return [
|
|
525
|
+
"precision highp float;",
|
|
526
|
+
"uniform vec2 uRes;",
|
|
527
|
+
"uniform float uTime;",
|
|
528
|
+
"void main(){",
|
|
529
|
+
" vec2 suv = gl_FragCoord.xy / uRes;",
|
|
530
|
+
" float r = length((suv - vec2(0.5, 0.44)) * vec2(1.0, 1.28));",
|
|
531
|
+
" vec3 col = mix(vec3(" + v(inner) + "), vec3(" + v(outer) + "), clamp(r * 1.30, 0.0, 1.0));",
|
|
532
|
+
" gl_FragColor = vec4(col, 1.0);",
|
|
533
|
+
"}",
|
|
534
|
+
].join("\n");
|
|
535
|
+
}
|
|
497
536
|
|
|
498
537
|
const SMAP_STAR_VS = [
|
|
499
538
|
"attribute vec2 aCorner;",
|
|
@@ -642,7 +681,8 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
642
681
|
canvasEl.width = W * 2;
|
|
643
682
|
canvasEl.height = H * 2;
|
|
644
683
|
gl.viewport(0, 0, canvasEl.width, canvasEl.height);
|
|
645
|
-
const
|
|
684
|
+
const pal = starmapPalette();
|
|
685
|
+
const progBg = glProgram(gl, SMAP_BG_VS, smapBgFs(pal.bgGl[0], pal.bgGl[1]), ["aPos"], ["uRes", "uTime"]);
|
|
646
686
|
const progStar = glProgram(gl, SMAP_STAR_VS, SMAP_STAR_FS, ["aCorner", "aMeta0", "aColor", "aMeta1", "aBirth"], ["uRes", "uTime", "uHoverIdx", "uRevealT"]);
|
|
647
687
|
const progLine = glProgram(gl, SMAP_LINE_VS, SMAP_LINE_FS, ["aPos", "aColor", "aBirth"], ["uRes", "uRevealT"]);
|
|
648
688
|
|
|
@@ -660,7 +700,7 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
660
700
|
const x = rand01(i * 97 + 13) * W;
|
|
661
701
|
const y = rand01(i * 31 + 7) * H;
|
|
662
702
|
const r = 2.4 + rand01(i * 17 + 5) * 2.0;
|
|
663
|
-
pushStarQuad(arr, x, y, r,
|
|
703
|
+
pushStarQuad(arr, x, y, r, hexToRgb(pal.dust), 0.10 + 0.30 * rand01(i * 7 + 3), rand01(i * 11 + 29), 0, -1, -1);
|
|
664
704
|
}
|
|
665
705
|
gl.bindBuffer(gl.ARRAY_BUFFER, dustBuf);
|
|
666
706
|
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(arr), gl.STATIC_DRAW);
|
|
@@ -691,10 +731,10 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
691
731
|
// 负责「深空底上一眼数得出来」——只画一个小点是数不出来的;
|
|
692
732
|
// ② 一颗小核心(走普通分支,fresh 传 -1 = 归档档 0.52),负责
|
|
693
733
|
// 「看得出是颗星」,并且 hover 时整颗点亮 + 出环。
|
|
694
|
-
pushStarQuad(arr, p.x, p.y, p.r * 6.0, dimRgb(meta
|
|
695
|
-
pushStarQuad(arr, p.x, p.y, p.r * 4.4, dimRgb(meta
|
|
734
|
+
pushStarQuad(arr, p.x, p.y, p.r * 6.0, dimRgb(kindColor(meta)), 0.26, p.tw, 0, -1, birth);
|
|
735
|
+
pushStarQuad(arr, p.x, p.y, p.r * 4.4, dimRgb(kindColor(meta)), 1, p.tw, -1, i, birth);
|
|
696
736
|
} else {
|
|
697
|
-
pushStarQuad(arr, p.x, p.y, p.r * 5.4, hexToRgb(meta
|
|
737
|
+
pushStarQuad(arr, p.x, p.y, p.r * 5.4, hexToRgb(kindColor(meta)), 1, p.tw, fresh, i, birth);
|
|
698
738
|
}
|
|
699
739
|
}
|
|
700
740
|
gl.bindBuffer(gl.ARRAY_BUFFER, starBuf);
|
|
@@ -709,7 +749,7 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
709
749
|
const ln = visBox.lines[li];
|
|
710
750
|
const a = layout.pts[ln[0]];
|
|
711
751
|
const b = layout.pts[ln[1]];
|
|
712
|
-
const c = hexToRgb((KIND_META[ln[2]] || KIND_META.special)
|
|
752
|
+
const c = hexToRgb(kindColor(KIND_META[ln[2]] || KIND_META.special));
|
|
713
753
|
const hot = hoverLineIdx >= 0 && (ln[0] === hoverLineIdx || ln[1] === hoverLineIdx);
|
|
714
754
|
const mul = hot ? 1 : 0.72;
|
|
715
755
|
const al = hot ? 0.6 : 0.22;
|
|
@@ -801,16 +841,25 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
801
841
|
const c2d = canvasEl.getContext("2d");
|
|
802
842
|
if (!c2d) return;
|
|
803
843
|
c2d.setTransform(SCALE, 0, 0, SCALE, 0, 0);
|
|
844
|
+
const pal = starmapPalette();
|
|
804
845
|
const bg = c2d.createLinearGradient(0, 0, 0, H);
|
|
805
|
-
bg.addColorStop(0,
|
|
806
|
-
bg.addColorStop(1,
|
|
846
|
+
bg.addColorStop(0, pal.bg2d[0]);
|
|
847
|
+
bg.addColorStop(1, pal.bg2d[1]);
|
|
807
848
|
c2d.fillStyle = bg;
|
|
808
849
|
c2d.fillRect(0, 0, W, H);
|
|
850
|
+
// 晕染:中心透、边缘略深(浅色档才要,深色档的夜空渐变已经够了)
|
|
851
|
+
if (pal.vignette) {
|
|
852
|
+
const vg = c2d.createRadialGradient(W * 0.5, H * 0.46, 0, W * 0.5, H * 0.46, Math.max(W, H) * 0.72);
|
|
853
|
+
vg.addColorStop(0, hexA(pal.vignette.color, 0));
|
|
854
|
+
vg.addColorStop(1, hexA(pal.vignette.color, pal.vignette.a));
|
|
855
|
+
c2d.fillStyle = vg;
|
|
856
|
+
c2d.fillRect(0, 0, W, H);
|
|
857
|
+
}
|
|
809
858
|
for (let i = 0; i < 150; i++) {
|
|
810
859
|
const x = rand01(i * 97 + 13) * W;
|
|
811
860
|
const y = rand01(i * 31 + 7) * H;
|
|
812
861
|
c2d.globalAlpha = 0.08 + 0.22 * rand01(i * 7 + 3);
|
|
813
|
-
c2d.fillStyle =
|
|
862
|
+
c2d.fillStyle = pal.dust;
|
|
814
863
|
c2d.fillRect(x, y, 1, 1);
|
|
815
864
|
}
|
|
816
865
|
c2d.globalAlpha = 1;
|
|
@@ -827,7 +876,7 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
827
876
|
lrv = lt * lt * (3 - 2 * lt);
|
|
828
877
|
if (lrv <= 0) continue;
|
|
829
878
|
}
|
|
830
|
-
c2d.strokeStyle = hexA(meta
|
|
879
|
+
c2d.strokeStyle = hexA(kindColor(meta), (hot ? 0.6 : 0.22) * lrv);
|
|
831
880
|
c2d.lineWidth = 1;
|
|
832
881
|
c2d.beginPath();
|
|
833
882
|
c2d.moveTo(a.x, a.y);
|
|
@@ -849,7 +898,7 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
849
898
|
// 暗星(canvas2d 兜底路径):与 webgl 同一档 —— 核心小而暗、外面一圈很淡的冷辉光。
|
|
850
899
|
// 有辉光才「数得出来」;hover 时整颗点亮 + 描边环,这是它能被发现的关键。
|
|
851
900
|
const hot = hoverBox.idx === i;
|
|
852
|
-
const dc = dimRgb(meta
|
|
901
|
+
const dc = dimRgb(kindColor(meta));
|
|
853
902
|
const rgba = (a) => "rgba(" + Math.round(dc[0] * 255) + "," + Math.round(dc[1] * 255) + "," + Math.round(dc[2] * 255) + "," + a.toFixed(3) + ")";
|
|
854
903
|
const haloR = p.r * 3.4;
|
|
855
904
|
const hg = c2d.createRadialGradient(p.x, p.y, 0, p.x, p.y, haloR);
|
|
@@ -860,13 +909,13 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
860
909
|
c2d.arc(p.x, p.y, haloR, 0, Math.PI * 2);
|
|
861
910
|
c2d.fill();
|
|
862
911
|
c2d.globalAlpha = (hot ? 0.95 : 0.52) * rv;
|
|
863
|
-
c2d.fillStyle = dimCss(meta
|
|
912
|
+
c2d.fillStyle = dimCss(kindColor(meta));
|
|
864
913
|
c2d.beginPath();
|
|
865
914
|
c2d.arc(p.x, p.y, Math.max(1.2, p.r * 0.7), 0, Math.PI * 2);
|
|
866
915
|
c2d.fill();
|
|
867
916
|
c2d.globalAlpha = 1;
|
|
868
917
|
if (hot) {
|
|
869
|
-
c2d.strokeStyle = dimCss(meta
|
|
918
|
+
c2d.strokeStyle = dimCss(kindColor(meta));
|
|
870
919
|
c2d.lineWidth = 1;
|
|
871
920
|
c2d.beginPath();
|
|
872
921
|
c2d.arc(p.x, p.y, p.r + 5, 0, Math.PI * 2);
|
|
@@ -876,7 +925,7 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
876
925
|
}
|
|
877
926
|
if (p.star.mtimeMs > 0 && nowMs - p.star.mtimeMs < FRESH_MS) {
|
|
878
927
|
const pr = p.r + 5 + 3 * (0.5 + 0.5 * Math.sin(phase * 1.4 + p.tw * 6.28));
|
|
879
|
-
c2d.strokeStyle = hexA(meta
|
|
928
|
+
c2d.strokeStyle = hexA(kindColor(meta), 0.4 * rv);
|
|
880
929
|
c2d.lineWidth = 1;
|
|
881
930
|
c2d.beginPath();
|
|
882
931
|
c2d.arc(p.x, p.y, pr, 0, Math.PI * 2);
|
|
@@ -884,20 +933,20 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
884
933
|
}
|
|
885
934
|
const glowR = p.r * 4.2;
|
|
886
935
|
const g = c2d.createRadialGradient(p.x, p.y, 0, p.x, p.y, glowR);
|
|
887
|
-
g.addColorStop(0, hexA(meta
|
|
888
|
-
g.addColorStop(1, hexA(meta
|
|
936
|
+
g.addColorStop(0, hexA(kindColor(meta), 0.5 * tw * rv));
|
|
937
|
+
g.addColorStop(1, hexA(kindColor(meta), 0));
|
|
889
938
|
c2d.fillStyle = g;
|
|
890
939
|
c2d.beginPath();
|
|
891
940
|
c2d.arc(p.x, p.y, glowR, 0, Math.PI * 2);
|
|
892
941
|
c2d.fill();
|
|
893
942
|
c2d.globalAlpha = (0.62 + 0.38 * tw) * rv;
|
|
894
|
-
c2d.fillStyle = meta
|
|
943
|
+
c2d.fillStyle = kindColor(meta);
|
|
895
944
|
c2d.beginPath();
|
|
896
945
|
c2d.arc(p.x, p.y, p.r, 0, Math.PI * 2);
|
|
897
946
|
c2d.fill();
|
|
898
947
|
c2d.globalAlpha = 1;
|
|
899
948
|
if (hoverBox.idx === i) {
|
|
900
|
-
c2d.strokeStyle = hexA(meta
|
|
949
|
+
c2d.strokeStyle = hexA(kindColor(meta), 0.9 * rv);
|
|
901
950
|
c2d.lineWidth = 1;
|
|
902
951
|
c2d.beginPath();
|
|
903
952
|
c2d.arc(p.x, p.y, p.r + 5, 0, Math.PI * 2);
|
|
@@ -978,6 +1027,20 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
978
1027
|
const drawBox = react.useRef({ renderer: null, t0: 0, markDirty: null }).current;
|
|
979
1028
|
const replayBox = react.useRef({ raf: 0, active: false }).current;
|
|
980
1029
|
|
|
1030
|
+
// 宿主主题:切主题要把画布重建一遍(底色/星点色都变了),所以它是个 state,
|
|
1031
|
+
// 并进渲染 effect 的依赖。挂 observer 而不是每次 render 读属性 —— 属性变了要主动重画。
|
|
1032
|
+
const themeKeyS = react.useState(isDarkTheme() ? "dark" : "light");
|
|
1033
|
+
const themeKey = themeKeyS[0];
|
|
1034
|
+
const setThemeKey = themeKeyS[1];
|
|
1035
|
+
react.useEffect(() => {
|
|
1036
|
+
if (typeof document === "undefined" || !document.body) return undefined;
|
|
1037
|
+
const sync = () => setThemeKey(isDarkTheme() ? "dark" : "light");
|
|
1038
|
+
sync();
|
|
1039
|
+
const mo = new MutationObserver(sync);
|
|
1040
|
+
mo.observe(document.body, { attributes: true, attributeFilter: ["data-ds-dark-theme"] });
|
|
1041
|
+
return () => mo.disconnect();
|
|
1042
|
+
}, []);
|
|
1043
|
+
|
|
981
1044
|
// Esc 分层:详情面板开着时先关面板(capture 阶段拦截),再按才落到 overlay 关星空。
|
|
982
1045
|
react.useEffect(() => {
|
|
983
1046
|
if (!selected) return undefined;
|
|
@@ -1077,7 +1140,7 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
1077
1140
|
window.cancelAnimationFrame(replayBox.raf);
|
|
1078
1141
|
if (renderer.destroy) renderer.destroy();
|
|
1079
1142
|
};
|
|
1080
|
-
}, [data, layout, canvasEl]);
|
|
1143
|
+
}, [data, layout, canvasEl, themeKey]);
|
|
1081
1144
|
|
|
1082
1145
|
react.useEffect(() => {
|
|
1083
1146
|
if (!data || !layout || !kindsOn) return;
|
|
@@ -1285,7 +1348,7 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
1285
1348
|
onClick: () => toggleKind(k),
|
|
1286
1349
|
title: "点击显示/隐藏这一类",
|
|
1287
1350
|
},
|
|
1288
|
-
h("span", { className: "smap-dot", style: { background: KIND_META[k]
|
|
1351
|
+
h("span", { className: "smap-dot", style: { background: kindColor(KIND_META[k]) } }),
|
|
1289
1352
|
KIND_META[k].label + " " + n));
|
|
1290
1353
|
}
|
|
1291
1354
|
let totalBytes = 0;
|
|
@@ -1327,7 +1390,7 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
1327
1390
|
h("span", { className: "smap-panel-title" }, selected.star.title),
|
|
1328
1391
|
h("button", { className: "smap-btn", onClick: () => setSelected(null), title: "关闭" }, "×")),
|
|
1329
1392
|
h("div", { key: "meta", className: "smap-panel-meta" },
|
|
1330
|
-
h("span", { className: "smap-meta-dot", style: { background: meta
|
|
1393
|
+
h("span", { className: "smap-meta-dot", style: { background: kindColor(meta) } }),
|
|
1331
1394
|
meta.label + " · " + selected.star.file + " · " + fmtBytes(selected.star.bytes) +
|
|
1332
1395
|
// 归档星的 mtime 是「当年写下」的时间,跟「什么时候收起来的」不是一回事 ——
|
|
1333
1396
|
// 这里只留「已归档」,具体时间在下面的归档块里给。
|
|
@@ -1370,7 +1433,7 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
1370
1433
|
onClick: () => openStar(s),
|
|
1371
1434
|
title: s.archived === true ? "归档案:点开看归档信息并恢复" : "展开全文",
|
|
1372
1435
|
},
|
|
1373
|
-
h("span", { className: "smap-meta-dot", style: { background: s.archived === true ? dimCss((KIND_META[s.kind] || KIND_META.special)
|
|
1436
|
+
h("span", { className: "smap-meta-dot", style: { background: s.archived === true ? dimCss(kindColor(KIND_META[s.kind] || KIND_META.special)) : kindColor(KIND_META[s.kind] || KIND_META.special), flex: "none", alignSelf: "center" } }),
|
|
1374
1437
|
s.archived === true ? h("span", { className: "smap-ar-badge" }, "已归档") : null,
|
|
1375
1438
|
h("span", { className: "smap-tl-date", title: s.mtimeMs ? fmtDate(s.mtimeMs) : "" }, s.mtimeMs ? fmtRel(s.mtimeMs) : "—"),
|
|
1376
1439
|
h("span", { className: "smap-tl-title" }, s.title),
|
|
@@ -2016,7 +2079,7 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
2016
2079
|
// 而独立成屏后如果父容器不是 column flex,`align-items:stretch` 就不会发生。
|
|
2017
2080
|
".smem-tool-panel--full{width:100%;align-self:stretch;box-sizing:border-box;}",
|
|
2018
2081
|
// 已锁定的记忆:卡片上那个按钮要一眼看出「这条现在拒绝归档」,用警示色
|
|
2019
|
-
".smem-btn-locked{border-color:var(--dsw-alias-state-
|
|
2082
|
+
".smem-btn-locked{border-color:var(--dsw-alias-state-warn-primary,#d97706);color:var(--dsw-alias-state-warn-primary,#d97706);}",
|
|
2020
2083
|
// 「自动归档」页签里的天数输入框:窄一点,别让三个数字各占一整行
|
|
2021
2084
|
".smem-num{width:88px;}",
|
|
2022
2085
|
// 体检 / 候选是「翻清单」的面板:定高 + 内部滚动,列表不被顶出首屏。
|
|
@@ -2105,7 +2168,7 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
2105
2168
|
".smem-ad-main{flex:1;min-width:140px;overflow-wrap:anywhere;}",
|
|
2106
2169
|
".smem-ad-sub{color:var(--dsw-alias-label-secondary);font-size:11.5px;overflow-wrap:anywhere;}",
|
|
2107
2170
|
".smem-ad-badge{display:inline-flex;align-items:center;font-size:11px;color:var(--dsw-alias-label-secondary);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;padding:0 5px;white-space:nowrap;}",
|
|
2108
|
-
".smem-ad-badge--warn{color:var(--dsw-alias-state-
|
|
2171
|
+
".smem-ad-badge--warn{color:var(--dsw-alias-state-warn-primary,#d97706);border-color:var(--dsw-alias-state-warn-primary,#d97706);}",
|
|
2109
2172
|
".smem-ad-badge--legacy{opacity:.78;}",
|
|
2110
2173
|
".smem-ad-confirm{border:1px solid var(--dsw-alias-state-error-primary,#ef4444);border-radius:8px;padding:7px 9px;font-size:12px;line-height:1.55;display:flex;flex-direction:column;gap:6px;overflow-wrap:anywhere;}",
|
|
2111
2174
|
".smem-ad-rb{font-size:12px;line-height:1.5;overflow-wrap:anywhere;}",
|
|
@@ -2114,7 +2177,7 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
2114
2177
|
'.smem-ad-run[data-on="1"]{border-color:var(--dsw-alias-brand-primary,#4d76e6);}',
|
|
2115
2178
|
".smem-ad-md{max-height:360px;overflow:auto;border:1px solid var(--dsw-alias-border-l1);border-radius:8px;padding:10px 12px;background:var(--dsw-alias-bg-layer-2);font-size:12px;white-space:pre-wrap;overflow-wrap:anywhere;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;line-height:1.6;}",
|
|
2116
2179
|
// ── 注入参数 / 保留名名单(自动做梦 tab)──
|
|
2117
|
-
".smem-ad-warn{color:var(--dsw-alias-state-
|
|
2180
|
+
".smem-ad-warn{color:var(--dsw-alias-state-warn-primary,#d97706);}",
|
|
2118
2181
|
".smem-chip--fixed{opacity:.72;cursor:default;}",
|
|
2119
2182
|
".smem-chip-x{border:0;background:transparent;color:inherit;cursor:pointer;font-size:11px;font-family:inherit;padding:0 0 0 5px;}",
|
|
2120
2183
|
// ── 更新提示(有新版本才出现的一条窄横幅)──
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sage-mem",
|
|
3
3
|
"description": "File-based memory plugin for DeepSeek Harness (DSH) — cross-session memory in plain markdown files, with a memory star-map view, type/tag filtering, retrieval aliases, and an optional autodream pass that consolidates memories on demand or on a schedule — with per-run rollback points, a per-file change declaration (what changed and why), and a separately selectable model for the consolidation pass",
|
|
4
|
-
"version": "0.9.
|
|
4
|
+
"version": "0.9.14",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"exports": {
|