dsh-sessions-manager 3.7.4 → 3.7.6
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 +2 -2
- package/README.md +2 -2
- package/assets/screenshot-dark-controls.png +0 -0
- package/assets/screenshot-id-copied.png +0 -0
- package/assets/screenshot-id-idle.png +0 -0
- package/lib/client.js +101 -10
- package/lib/client.js.map +2 -2
- package/lib/index.js +34 -3
- package/lib/index.js.map +2 -2
- package/package.json +1 -1
- package/src/client/index.jsx +98 -9
- package/src/index.js +46 -2
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-sessions-manager",
|
|
3
3
|
"description": "DSH 设置面板会话管理器:归档 / 恢复 / 彻底删除 / 移动到其他工作区,带工作区标签与会话日期;统一「会话管理」面板。Session manager for the DeepSeek Harness settings panel — archive / restore / permanently delete / move sessions across workspaces, with workspace tags & session dates in one unified panel.",
|
|
4
|
-
"version": "3.7.
|
|
4
|
+
"version": "3.7.6",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"exports": {
|
package/src/client/index.jsx
CHANGED
|
@@ -21,10 +21,26 @@ function loadPanelPrefs() {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
const CSS = `
|
|
24
|
+
/* 兼容 shim(2026-09-30 对 0.2.0-rc.2 实测):当前 runtime 的设计令牌不再提供
|
|
25
|
+
fill-elevated / fill-subtle / fill-secondary / label-inverted / state-warning-primary
|
|
26
|
+
这几个 alias,缺位会让输入框、下拉框、卡片、徽标整体回退成透明。这里把它们重映射
|
|
27
|
+
到 runtime 现有 token(官方 Input/Pill 分别用 bg-layer-1 / bg-layer-2)。选择器用
|
|
28
|
+
:where(:root)(零特异性):runtime 将来恢复官方定义时自动胜出,shim 只在缺位时兜底。
|
|
29
|
+
state-warning-primary 是旧名,现名为 state-warn-primary。 */
|
|
30
|
+
:where(:root){--dsw-alias-fill-elevated:var(--dsw-alias-bg-layer-2);--dsw-alias-fill-subtle:var(--dsw-alias-bg-layer-1);--dsw-alias-fill-secondary:var(--dsw-alias-bg-layer-2);--dsw-alias-label-inverted:var(--dsw-alias-label-primary-foreground);--dsw-alias-state-warning-primary:var(--dsw-alias-state-warn-primary)}
|
|
31
|
+
/* 原生 <select> 的弹出列表由 Chromium 按 color-scheme 绘制,不声明时暗色主题下
|
|
32
|
+
仍是白底,叠加未适配的文字色导致不可读;跟随 DSH 的暗色主题标记切换。 */
|
|
33
|
+
.archv{color-scheme:light}
|
|
34
|
+
body[data-ds-dark-theme] .archv{color-scheme:dark}
|
|
35
|
+
/* 兜底:原生弹层行的配色直接写在 option 上(Chromium 按 option 自身的
|
|
36
|
+
background/color 绘制弹层),即使 color-scheme 判定被扩展或环境干扰,
|
|
37
|
+
暗色下也不会出现白底配浅色文字的不可读组合。 */
|
|
38
|
+
.archv option{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary)}
|
|
24
39
|
.archv{--dsm-radius-tag:9px;--dsm-radius-ctl:9px;--dsm-radius-sheet:10px;--dsm-radius-card:12px;display:flex;flex-direction:column;gap:4px;max-width:800px;padding:8px 2px 28px}
|
|
25
40
|
.archv-head{display:flex;align-items:center;gap:10px;margin:0 0 2px}
|
|
26
41
|
.archv-title{font-size:16px;font-weight:650;color:var(--dsw-alias-label-primary);letter-spacing:-0.01em;margin:0}
|
|
27
|
-
.archv-stamp{font-size:11px;font-weight:500;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-fill-secondary);border:1px solid var(--dsw-alias-border-l1);border-radius:999px;padding:2px 8px;white-space:nowrap;cursor:
|
|
42
|
+
.archv-stamp{font-size:11px;font-weight:500;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-fill-secondary);border:1px solid var(--dsw-alias-border-l1);border-radius:999px;padding:2px 8px;white-space:nowrap;cursor:pointer;transition:color .15s ease,border-color .15s ease}
|
|
43
|
+
.archv-stamp:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l3)}
|
|
28
44
|
.archv-count{font-size:11px;font-weight:600;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-fill-subtle);border:1px solid var(--dsw-alias-border-l2);border-radius:999px;padding:1px 8px;flex:none}
|
|
29
45
|
.archv-sub{font-size:12px;line-height:1.55;color:var(--dsw-alias-label-tertiary);margin:0 0 12px;max-width:64ch}
|
|
30
46
|
.archv-err{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:9px 12px;border:1px solid color-mix(in srgb,var(--dsw-alias-state-error-primary) 40%,transparent);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 8%,transparent);border-radius:var(--dsm-radius-sheet);color:var(--dsw-alias-state-error-primary);font-size:12px;margin-bottom:10px}
|
|
@@ -49,8 +65,11 @@ const CSS = `
|
|
|
49
65
|
.sess-fbtn-on{background:var(--dsw-alias-interactive-bg-active);color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l3)}
|
|
50
66
|
.sess-tools{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:8px;margin:0 0 8px}
|
|
51
67
|
.sess-tools-4{grid-template-columns:repeat(auto-fit,minmax(132px,1fr))}
|
|
52
|
-
/* T4 起搜索行有 5
|
|
53
|
-
|
|
68
|
+
/* T4 起搜索行有 5 个字段(搜索/工作区/标签/排序/分组):固定 5 列 + minmax(0,1fr)
|
|
69
|
+
一行排满——auto-fit(minmax(124px,…)) 在面板宽度不足 5×124+gap 时会把最后
|
|
70
|
+
一列挤到第二行(分组孤行)。minmax(0,1fr) 允许列均分收缩,select/input
|
|
71
|
+
各自截断;<640px 仍由下方断点收成单列。 */
|
|
72
|
+
.sess-tools-5{grid-template-columns:repeat(5,minmax(0,1fr))}
|
|
54
73
|
.dsm-kids{display:flex;flex-direction:column;gap:6px;margin:10px 0 2px;margin-left:43px;padding-left:10px;border-left:2px solid var(--dsw-alias-border-l3)}
|
|
55
74
|
.dsm-kid .dsm-kids{margin-left:8px;margin-top:6px}
|
|
56
75
|
.dsm-kids-toggle{appearance:none;min-height:22px;padding:0 9px;border:1px solid color-mix(in srgb,var(--dsw-alias-state-business-primary) 40%,transparent);background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 10%,transparent);color:var(--dsw-alias-state-business-primary);border-radius:var(--dsm-radius-tag);font:inherit;font-size:11px;font-weight:500;cursor:pointer;flex:none;white-space:nowrap}
|
|
@@ -93,7 +112,9 @@ const CSS = `
|
|
|
93
112
|
分支(绿)/空白(灰),名字超长省略号;+N 计数 chip 中性色。 */
|
|
94
113
|
.dsm-tagchip{display:inline-flex;align-items:center;flex:none;max-width:9em;min-height:22px;padding:0 8px;border:1px solid color-mix(in srgb,var(--dsw-alias-state-warn-primary,#EAB308) 45%,transparent);border-radius:var(--dsm-radius-tag);background:color-mix(in srgb,var(--dsw-alias-state-warn-primary,#EAB308) 10%,transparent);color:var(--dsw-alias-label-secondary);font-size:11px;font-weight:500;line-height:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
95
114
|
.dsm-tagchip-more{max-width:none;color:var(--dsw-alias-label-tertiary);border-color:var(--dsw-alias-border-l2);background:var(--dsw-alias-fill-subtle)}
|
|
96
|
-
.archv-id{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:10.5px;color:var(--dsw-alias-label-tertiary);flex:none;margin-left:auto;white-space:nowrap}
|
|
115
|
+
.archv-id{appearance:none;border:none;background:none;padding:0;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:10.5px;color:var(--dsw-alias-label-tertiary);flex:none;margin-left:auto;white-space:nowrap;cursor:pointer;transition:color .12s ease}
|
|
116
|
+
.archv-id:hover{color:var(--dsw-alias-label-secondary)}
|
|
117
|
+
.archv-id-copied{color:var(--dsw-alias-state-business-primary)}
|
|
97
118
|
.archv-dot{color:var(--dsw-alias-border-l3);flex:none}
|
|
98
119
|
.archv-check{width:15px;height:15px;accent-color:var(--dsw-alias-state-business-primary);flex:none;cursor:pointer}
|
|
99
120
|
.archv-star{appearance:none;width:22px;height:22px;flex:none;display:inline-flex;align-items:center;justify-content:center;border:none;background:0 0;padding:0;line-height:0;color:var(--dsw-alias-label-tertiary);cursor:pointer;border-radius:50%;transition:color .15s ease,transform .12s ease}
|
|
@@ -886,6 +907,68 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
886
907
|
timer.current = setTimeout(() => setToast(null), toastDurationFor(msg, kind))
|
|
887
908
|
}
|
|
888
909
|
|
|
910
|
+
// 版本徽章点击复制完整 buildStamp。webview 里 navigator.clipboard 可能缺失
|
|
911
|
+
// 或被权限拒绝(此前失败被静默吞掉,用户以为按钮坏了)——失败落
|
|
912
|
+
// execCommand 兜底;两条路都不通时至少把完整版本号亮在 toast 里。
|
|
913
|
+
const copyBuildStamp = async () => {
|
|
914
|
+
const full = String(capabilities && capabilities.buildStamp || '')
|
|
915
|
+
if (!full) return
|
|
916
|
+
try {
|
|
917
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
918
|
+
await navigator.clipboard.writeText(full)
|
|
919
|
+
showToast('已复制完整版本号 ' + full, 'ok')
|
|
920
|
+
return
|
|
921
|
+
}
|
|
922
|
+
throw new Error('clipboard unavailable')
|
|
923
|
+
} catch (e) {
|
|
924
|
+
try {
|
|
925
|
+
const ta = document.createElement('textarea')
|
|
926
|
+
ta.value = full
|
|
927
|
+
ta.style.position = 'fixed'
|
|
928
|
+
ta.style.opacity = '0'
|
|
929
|
+
document.body.appendChild(ta)
|
|
930
|
+
ta.select()
|
|
931
|
+
const ok = document.execCommand('copy')
|
|
932
|
+
document.body.removeChild(ta)
|
|
933
|
+
if (!ok) throw new Error('execCommand failed')
|
|
934
|
+
showToast('已复制完整版本号 ' + full, 'ok')
|
|
935
|
+
} catch (e2) {
|
|
936
|
+
showToast('复制失败,完整版本号:' + full, 'err')
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
// 点击行尾短 ID 复制完整会话 ID。clipboard API 优先(desktop/web 的页面都来自
|
|
942
|
+
// localhost,属安全上下文),失败回退 execCommand;成功把短 ID 短暂换成「已复制」
|
|
943
|
+
// 作就近反馈,两条路径全失败时把完整 ID 亮进 toast 供手抄(与上方徽章的失败终点
|
|
944
|
+
// 同一思路)。
|
|
945
|
+
const copiedTimer = useRef(null)
|
|
946
|
+
const [copiedId, setCopiedId] = useState(null)
|
|
947
|
+
const copySessionId = (id) => {
|
|
948
|
+
const markCopied = () => {
|
|
949
|
+
setCopiedId(id)
|
|
950
|
+
if (copiedTimer.current) clearTimeout(copiedTimer.current)
|
|
951
|
+
copiedTimer.current = setTimeout(() => setCopiedId(null), 1400)
|
|
952
|
+
}
|
|
953
|
+
const viaExecCommand = () => {
|
|
954
|
+
try {
|
|
955
|
+
const ta = document.createElement('textarea')
|
|
956
|
+
ta.value = id
|
|
957
|
+
ta.setAttribute('readonly', '')
|
|
958
|
+
ta.style.cssText = 'position:fixed;left:-9999px;top:0;opacity:0'
|
|
959
|
+
document.body.appendChild(ta)
|
|
960
|
+
ta.select()
|
|
961
|
+
const ok = document.execCommand('copy')
|
|
962
|
+
ta.remove()
|
|
963
|
+
if (ok) markCopied()
|
|
964
|
+
else showToast('复制失败,完整 ID:' + id, 'err')
|
|
965
|
+
} catch (e) { showToast('复制失败,完整 ID:' + id, 'err') }
|
|
966
|
+
}
|
|
967
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
968
|
+
navigator.clipboard.writeText(id).then(markCopied).catch(viaExecCommand)
|
|
969
|
+
} else viaExecCommand()
|
|
970
|
+
}
|
|
971
|
+
|
|
889
972
|
const refresh = () => {
|
|
890
973
|
setError(null)
|
|
891
974
|
Promise.all([
|
|
@@ -1608,7 +1691,13 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
1608
1691
|
<div className="archv-head">
|
|
1609
1692
|
<h2 className="archv-title">会话管理</h2>
|
|
1610
1693
|
{capabilities && capabilities.buildStamp
|
|
1611
|
-
? <span
|
|
1694
|
+
? <span
|
|
1695
|
+
className="archv-stamp"
|
|
1696
|
+
role="button"
|
|
1697
|
+
aria-label="复制完整版本号"
|
|
1698
|
+
title={capabilities.buildStamp}
|
|
1699
|
+
onClick={copyBuildStamp}
|
|
1700
|
+
>v{String(capabilities.buildStamp).split('+')[0]}</span>
|
|
1612
1701
|
: null}
|
|
1613
1702
|
{sessions !== null && <span className="archv-count" aria-label={`${sessions.length} 个会话`}>{sessions.length}</span>}
|
|
1614
1703
|
</div>
|
|
@@ -1895,8 +1984,8 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
1895
1984
|
<div className="sess-field">
|
|
1896
1985
|
<label htmlFor="dsm-group">分组</label>
|
|
1897
1986
|
<select id="dsm-group" value={groupByLineage ? 'lineage' : 'flat'} onChange={(e) => setGroupByLineage(e.target.value === 'lineage')} title="血缘分组:子代理折叠、分支聚拢成组;平铺:与 DSH 原生一致,全部并列">
|
|
1898
|
-
<option value="lineage"
|
|
1899
|
-
<option value="flat"
|
|
1987
|
+
<option value="lineage">血缘(折叠)</option>
|
|
1988
|
+
<option value="flat">平铺(并列)</option>
|
|
1900
1989
|
</select>
|
|
1901
1990
|
</div>
|
|
1902
1991
|
</div>
|
|
@@ -2106,7 +2195,7 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
2106
2195
|
{tagChips(it.sessionId)}
|
|
2107
2196
|
{kidsBadge(it.sessionId)}
|
|
2108
2197
|
{branchGroupBadge(it.sessionId)}
|
|
2109
|
-
<
|
|
2198
|
+
<button type="button" className={'archv-id' + (copiedId === it.sessionId ? ' archv-id-copied' : '')} title={it.sessionId + '(点击复制完整 ID)'} aria-label={'复制会话 ID ' + it.sessionId} onClick={(e) => { e.stopPropagation(); copySessionId(it.sessionId) }}>{copiedId === it.sessionId ? '已复制' : shortId(it.sessionId)}</button>
|
|
2110
2199
|
</div>
|
|
2111
2200
|
<div className="archv-meta">
|
|
2112
2201
|
{it.archived ? <span className="archv-wtag archv-wgone">已归档</span> : <span className="archv-wtag archv-active">活动</span>}
|
|
@@ -2390,7 +2479,7 @@ const SIDEBAR_AUG_CSS = `
|
|
|
2390
2479
|
.dsm-toast{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);z-index:2147483601;background:Canvas;color:CanvasText;border:1px solid color-mix(in srgb,CanvasText 25%,transparent);padding:9px 16px;border-radius:999px;font-size:12px;box-shadow:0 8px 24px rgb(0 0 0/.25);max-width:min(92vw,460px);cursor:pointer}
|
|
2391
2480
|
.dsm-toast-long{border-radius:14px;text-align:left;line-height:18px}
|
|
2392
2481
|
.dsm-toast-err{background:#4A1D1D;color:#FFD9D9;border:1px solid var(--dsw-alias-state-error-primary)}
|
|
2393
|
-
.dsm-sub{position:fixed;z-index:1100;box-sizing:border-box;min-width:190px;max-width:320px;padding:4px;display:flex;flex-direction:column;gap:0;background:var(--dsw-specific-menu);border:1px solid var(--dsw-alias-border-inverted);border-radius:12px;box-shadow:var(--dsw-shadow-lv3)}
|
|
2482
|
+
.dsm-sub{position:fixed;z-index:1100;box-sizing:border-box;min-width:190px;max-width:320px;padding:4px;display:flex;flex-direction:column;gap:0;background:var(--dsw-specific-menu);backdrop-filter:var(--dsw-menu-backdrop-filter,blur(40px) saturate(150%));border:1px solid var(--dsw-alias-border-inverted);border-radius:12px;box-shadow:var(--dsw-shadow-lv3)}
|
|
2394
2483
|
.dsm-sub-loading,.dsm-sub-empty{font-size:12px;color:var(--dsw-alias-label-tertiary);padding:6px 10px}
|
|
2395
2484
|
.dsm-sub-err{font-size:12px;color:var(--dsw-alias-state-error-primary);padding:6px 10px}
|
|
2396
2485
|
.dsm-sub-item{display:flex;align-items:center;gap:8px;width:100%;min-height:36px;padding:6px 10px;border:none;border-radius:8px;background:transparent;cursor:pointer;font-size:13px;line-height:18px;color:var(--dsw-alias-label-primary);text-align:left}
|
package/src/index.js
CHANGED
|
@@ -251,6 +251,31 @@ export function apply(ctx) {
|
|
|
251
251
|
return result
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
+
// —— 0.2.0+ 官方 title 投影快路径(2026-09-29 审计 P2)————————————————
|
|
255
|
+
// dsh-session-title(0.2.0 起 dsh-base 默认组合挂载)把标题折叠官方化为
|
|
256
|
+
// O(1) 投影(key='title',state 为 string|null),注册表对已 materialize 的
|
|
257
|
+
// cell 提供 cachedSnapshot——只读 watermark 缓存,**零折叠、零磁盘 I/O**
|
|
258
|
+
// (对比 readTitleSnapshots 每条整本解码,issue #8)。命中即免预热。
|
|
259
|
+
// 红线:
|
|
260
|
+
// 1. 只信 cachedSnapshot(绝不调 stateOf——那会对内存日志做同步折叠,
|
|
261
|
+
// 违反「请求路径零重活」纪律);
|
|
262
|
+
// 2. 服务不存在(0.1.7)/会话未实例化/任何异常 → 静默返回 null,行为与
|
|
263
|
+
// 3.7.4 完全一致;
|
|
264
|
+
// 3. cachedSnapshot 的 title=null 只说明「该 cell 还没有非空标题」(可能
|
|
265
|
+
// 会话从未打开、cell 未折叠)——一律当**未知**交回预热队列,绝不冒充
|
|
266
|
+
// 「会话真的无标题」(empty 三态纪律,v3.7.0 T1)。
|
|
267
|
+
function projectionTitleOf(liveStore, id) {
|
|
268
|
+
try {
|
|
269
|
+
const reg = ctx.get('sessionProjections')
|
|
270
|
+
if (!reg || typeof reg.cachedSnapshot !== 'function') return null
|
|
271
|
+
const session = liveStore && liveStore.get && liveStore.get(id)
|
|
272
|
+
if (!session) return null
|
|
273
|
+
const snap = reg.cachedSnapshot(session, ['title'])
|
|
274
|
+
const title = snap && snap.values ? snap.values.title : null
|
|
275
|
+
return typeof title === 'string' && title.length > 0 ? title : null
|
|
276
|
+
} catch (e) { return null }
|
|
277
|
+
}
|
|
278
|
+
|
|
254
279
|
async function archivedState() {
|
|
255
280
|
const d = dom()
|
|
256
281
|
if (!d) throw new Error('workspace domain is not open')
|
|
@@ -1478,7 +1503,26 @@ export function apply(ctx) {
|
|
|
1478
1503
|
createdAt: (header && header.createdAt != null) ? header.createdAt : meta.createdAt,
|
|
1479
1504
|
})
|
|
1480
1505
|
}
|
|
1481
|
-
const
|
|
1506
|
+
const stillMissing0 = missing.filter((id) => !persisted.has(id))
|
|
1507
|
+
// 0.2.0+ 官方投影快路径:先把「已实例化且 cell 已 materialize」的会话从
|
|
1508
|
+
// 预热队列里捞回来(cachedSnapshot 零 I/O),剩下的才进后台预热。
|
|
1509
|
+
// 注意:metaCache.set 在指纹不可算(无 stat / revision 缺失)时**拒绝写入**,
|
|
1510
|
+
// 所以命中结果另存 projectionMeta,本请求的渲染链直接可用——缓存只是增益。
|
|
1511
|
+
const projectionMeta = new Map()
|
|
1512
|
+
const stillMissing = []
|
|
1513
|
+
for (const id of stillMissing0) {
|
|
1514
|
+
const title = projectionTitleOf(live, id)
|
|
1515
|
+
if (title == null) { stillMissing.push(id); continue }
|
|
1516
|
+
const entry = entryById.get(id)
|
|
1517
|
+
const header = entry && entry.header ? entry.header : null
|
|
1518
|
+
const meta = {
|
|
1519
|
+
title,
|
|
1520
|
+
cwd: (header && typeof header.cwd === 'string' && header.cwd) ? header.cwd : null,
|
|
1521
|
+
createdAt: (header && header.createdAt != null) ? header.createdAt : null,
|
|
1522
|
+
}
|
|
1523
|
+
projectionMeta.set(id, meta)
|
|
1524
|
+
metaCache.set(id, statsById.get(id), meta)
|
|
1525
|
+
}
|
|
1482
1526
|
// issue #8:缺失部分交后台预热(分批 + 让步),请求路径零投影、零日志解码。
|
|
1483
1527
|
if (stillMissing.length) enqueueWarm(stillMissing, statsById, entryById)
|
|
1484
1528
|
const items = []
|
|
@@ -1487,7 +1531,7 @@ export function apply(ctx) {
|
|
|
1487
1531
|
// metaCache(persisted.get 里的 cwd 可能因移动会话而陈旧)。
|
|
1488
1532
|
// 完全未命中的会话也不能丢 cwd/createdAt——它们来自 list header,零成本
|
|
1489
1533
|
// 且是工作区归属/创建时间的权威来源;只有标题允许占位等后台预热(issue #8)。
|
|
1490
|
-
let meta = metaCache.get(id, statsById.get(id)) || persisted.get(id) || null
|
|
1534
|
+
let meta = metaCache.get(id, statsById.get(id)) || persisted.get(id) || projectionMeta.get(id) || null
|
|
1491
1535
|
if (!meta) {
|
|
1492
1536
|
meta = { title: null, cwd: null, createdAt: null }
|
|
1493
1537
|
const entry = entryById.get(id)
|