@co-engram/viewer 0.1.0
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 +38 -0
- package/dist/brand-logos.d.ts +9 -0
- package/dist/brand-logos.d.ts.map +1 -0
- package/dist/brand-logos.js +10 -0
- package/dist/brand-logos.js.map +1 -0
- package/dist/html.d.ts +21 -0
- package/dist/html.d.ts.map +1 -0
- package/dist/html.js +299 -0
- package/dist/html.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/runtime/app.d.ts +11 -0
- package/dist/runtime/app.d.ts.map +1 -0
- package/dist/runtime/app.js +437 -0
- package/dist/runtime/app.js.map +1 -0
- package/dist/runtime/decay.d.ts +16 -0
- package/dist/runtime/decay.d.ts.map +1 -0
- package/dist/runtime/decay.js +108 -0
- package/dist/runtime/decay.js.map +1 -0
- package/dist/runtime/graph.d.ts +13 -0
- package/dist/runtime/graph.d.ts.map +1 -0
- package/dist/runtime/graph.js +313 -0
- package/dist/runtime/graph.js.map +1 -0
- package/dist/runtime/i18n.d.ts +16 -0
- package/dist/runtime/i18n.d.ts.map +1 -0
- package/dist/runtime/i18n.js +76 -0
- package/dist/runtime/i18n.js.map +1 -0
- package/dist/runtime/tabs.d.ts +8 -0
- package/dist/runtime/tabs.d.ts.map +1 -0
- package/dist/runtime/tabs.js +1783 -0
- package/dist/runtime/tabs.js.map +1 -0
- package/dist/server.d.ts +73 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +985 -0
- package/dist/server.js.map +1 -0
- package/dist/styles.d.ts +13 -0
- package/dist/styles.d.ts.map +1 -0
- package/dist/styles.js +1632 -0
- package/dist/styles.js.map +1 -0
- package/dist/vendor/dompurify-source.d.ts +11 -0
- package/dist/vendor/dompurify-source.d.ts.map +1 -0
- package/dist/vendor/dompurify-source.js +15 -0
- package/dist/vendor/dompurify-source.js.map +1 -0
- package/dist/vendor/marked-source.d.ts +11 -0
- package/dist/vendor/marked-source.d.ts.map +1 -0
- package/dist/vendor/marked-source.js +18 -0
- package/dist/vendor/marked-source.js.map +1 -0
- package/dist/vendor/vis-network-source.d.ts +11 -0
- package/dist/vendor/vis-network-source.d.ts.map +1 -0
- package/dist/vendor/vis-network-source.js +46 -0
- package/dist/vendor/vis-network-source.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Viewer v2 runtime — Graph tab(记忆突触)。
|
|
3
|
+
*
|
|
4
|
+
* 用 vis-network(已内联到 window.vis)渲染力导向图谱。节点按 EngramKind 着色,
|
|
5
|
+
* 边按 SynapseFamily 着色,contradicts 边红色虚线 + resolutionStatus badge。
|
|
6
|
+
*
|
|
7
|
+
* 点击节点 → 节点详情(记忆印迹)
|
|
8
|
+
* 点击边 → 突触详情(可编辑/删除,通过 CO_ENGRAM_SYNAPSES)
|
|
9
|
+
*
|
|
10
|
+
* @module @co-engram/claude-code/viewer/runtime/graph
|
|
11
|
+
*/
|
|
12
|
+
export const GRAPH_RUNTIME = `
|
|
13
|
+
CO_ENGRAM.on('graph', async function renderGraph() {
|
|
14
|
+
const container = document.getElementById('graph-canvas');
|
|
15
|
+
if (!container) return;
|
|
16
|
+
try {
|
|
17
|
+
await renderGraphInner(container);
|
|
18
|
+
} catch (e) {
|
|
19
|
+
console.error('[co-engram] graph render failed:', e);
|
|
20
|
+
container.innerHTML = '<div class="empty"><div class="icon">⚠️</div>渲染失败:' + CO_ENGRAM.escapeHtml(String(e && e.message || e)) + '</div>';
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
async function renderGraphInner(container) {
|
|
25
|
+
if (typeof vis === 'undefined' || !vis.Network) {
|
|
26
|
+
container.innerHTML = '<div class="empty"><div class="icon">⚠️</div>vis-network 加载失败</div>';
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (CO_ENGRAM._graphState && CO_ENGRAM._graphState.initialized) {
|
|
30
|
+
// 已初始化,只 refit
|
|
31
|
+
setTimeout(() => CO_ENGRAM._graphState.network.fit(), 30);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let graph;
|
|
36
|
+
try {
|
|
37
|
+
graph = await CO_ENGRAM.apiGet('/api/graph');
|
|
38
|
+
} catch (e) {
|
|
39
|
+
container.innerHTML = '<div class="empty"><div class="icon">⚠️</div>加载失败:' + CO_ENGRAM.escapeHtml(e.message) + '</div>';
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!graph.nodes || graph.nodes.length === 0) {
|
|
44
|
+
container.innerHTML = '<div class="empty"><div class="icon">🕳️</div>暂无记忆印迹</div>';
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const L = CO_ENGRAM_LABELS;
|
|
49
|
+
const FAMILY_LABEL = { structural: '结构', causal: '因果', evidential: '证据', temporal: '时间', modulatory: '调节' };
|
|
50
|
+
|
|
51
|
+
// === 状态:filter ===
|
|
52
|
+
// showKinds: engram 类型(节点过滤);showSynapseKinds: synapse 类型(边过滤,与编辑器一致)
|
|
53
|
+
const ALL_SYNAPSE_KINDS = ['extends', 'part_of', 'similar_to', 'depends_on', 'causes', 'follows', 'derives_from', 'contradicts', 'exemplifies', 'supersedes', 'consolidates', 'contextualizes'];
|
|
54
|
+
const state = {
|
|
55
|
+
showKinds: { fact: true, observation: true, pattern: true, procedure: true, hypothesis: true },
|
|
56
|
+
showSynapseKinds: Object.fromEntries(ALL_SYNAPSE_KINDS.map(k => [k, true])),
|
|
57
|
+
physicsEnabled: true
|
|
58
|
+
};
|
|
59
|
+
CO_ENGRAM._graphState = { initialized: false, network: null, data: graph, state };
|
|
60
|
+
|
|
61
|
+
// === 节点 / 边构建 ===
|
|
62
|
+
function buildNodes() {
|
|
63
|
+
return graph.nodes
|
|
64
|
+
.filter(n => state.showKinds[n.kind] !== false)
|
|
65
|
+
.map(n => {
|
|
66
|
+
const importance = (n.importance != null ? n.importance : 0.5);
|
|
67
|
+
const size = 10 + importance * 18;
|
|
68
|
+
const kindLabel = L.kind[n.kind] || n.kind;
|
|
69
|
+
const kindTip = (CO_ENGRAM.TOOLTIPS && CO_ENGRAM.TOOLTIPS.kind && CO_ENGRAM.TOOLTIPS.kind[n.kind]) || '';
|
|
70
|
+
const tipText = n.title + '\\n[' + kindLabel + ' / ' + n.kind + ']\\n标签:' + (n.domainTags || []).join(', ') + (kindTip ? '\\n\\n' + kindTip : '');
|
|
71
|
+
return {
|
|
72
|
+
id: n.id,
|
|
73
|
+
label: n.title.length > 32 ? n.title.slice(0, 30) + '…' : n.title,
|
|
74
|
+
title: tipText,
|
|
75
|
+
group: n.kind,
|
|
76
|
+
color: {
|
|
77
|
+
background: CO_ENGRAM.kindColor(n.kind),
|
|
78
|
+
border: CO_ENGRAM.kindColor(n.kind),
|
|
79
|
+
highlight: { background: CO_ENGRAM.kindColor(n.kind), border: '#000' },
|
|
80
|
+
hover: { background: CO_ENGRAM.kindColor(n.kind), border: '#fff' }
|
|
81
|
+
},
|
|
82
|
+
size,
|
|
83
|
+
font: { color: getComputedStyle(document.body).color, size: 11, face: 'sans-serif' },
|
|
84
|
+
shape: 'dot',
|
|
85
|
+
_raw: n
|
|
86
|
+
};
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function buildEdges() {
|
|
91
|
+
const out = [];
|
|
92
|
+
for (const e of graph.edges) {
|
|
93
|
+
// 按 12 kind 过滤(与编辑器一致)
|
|
94
|
+
if (state.showSynapseKinds[e.kind] === false) continue;
|
|
95
|
+
|
|
96
|
+
const isContra = e.kind === 'contradicts';
|
|
97
|
+
const color = CO_ENGRAM.edgeColor(e.kind);
|
|
98
|
+
const kindLabel = L.synapse[e.kind] || e.kind;
|
|
99
|
+
const family = CO_ENGRAM.synapseFamily(e.kind);
|
|
100
|
+
const familyLabel = FAMILY_LABEL[family] || family;
|
|
101
|
+
const synTip = (CO_ENGRAM.TOOLTIPS && CO_ENGRAM.TOOLTIPS.synapse && CO_ENGRAM.TOOLTIPS.synapse[e.kind]) || '';
|
|
102
|
+
const resLabel = e.resolutionStatus ? ' · 裁决:' + (L.resolution[e.resolutionStatus] || e.resolutionStatus) : '';
|
|
103
|
+
const tipText = kindLabel + ' (' + e.kind + ') · ' + familyLabel + '族\\n权重 ' + (e.weight != null ? e.weight.toFixed(2) : '?') + ' · 证据 ' + (e.evidenceCount || 0) + resLabel + '\\n方向:' + (e.direction || 'directional') + (synTip ? '\\n\\n' + synTip : '') + '\\n\\n[点击编辑此突触]';
|
|
104
|
+
out.push({
|
|
105
|
+
id: e.id,
|
|
106
|
+
from: e.from,
|
|
107
|
+
to: e.to,
|
|
108
|
+
label: '',
|
|
109
|
+
title: tipText,
|
|
110
|
+
color: { color, highlight: color, hover: color, opacity: 0.85 },
|
|
111
|
+
width: 1 + (e.weight || 0.5) * 3,
|
|
112
|
+
dashes: isContra,
|
|
113
|
+
arrows: e.direction === 'bidirectional' ? { to: { enabled: true }, from: { enabled: true } } : { to: { enabled: true, scaleFactor: 0.6 } },
|
|
114
|
+
smooth: { enabled: true, type: 'continuous', roundness: 0.5 },
|
|
115
|
+
_raw: e
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return out;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// === 初始化 vis-network ===
|
|
122
|
+
const nodesDataset = new vis.DataSet(buildNodes());
|
|
123
|
+
const edgesDataset = new vis.DataSet(buildEdges());
|
|
124
|
+
|
|
125
|
+
const options = {
|
|
126
|
+
autoResize: true,
|
|
127
|
+
height: '100%',
|
|
128
|
+
width: '100%',
|
|
129
|
+
nodes: { borderWidth: 2, shadow: { enabled: true, size: 6, x: 0, y: 1 } },
|
|
130
|
+
edges: { smooth: { type: 'continuous' } },
|
|
131
|
+
physics: {
|
|
132
|
+
enabled: true,
|
|
133
|
+
solver: 'forceAtlas2Based',
|
|
134
|
+
forceAtlas2Based: { gravitationalConstant: -65, centralGravity: 0.01, springLength: 110, springConstant: 0.08, damping: 0.4, avoidOverlap: 0.5 },
|
|
135
|
+
stabilization: { iterations: 80, fit: true }
|
|
136
|
+
},
|
|
137
|
+
interaction: { hover: true, tooltipDelay: 100, navigationButtons: false, keyboard: false, selectConnectedEdges: false }
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const network = new vis.Network(container, { nodes: nodesDataset, edges: edgesDataset }, options);
|
|
141
|
+
CO_ENGRAM._graphState.network = network;
|
|
142
|
+
CO_ENGRAM._graphState.initialized = true;
|
|
143
|
+
CO_ENGRAM._graphState.nodes = nodesDataset;
|
|
144
|
+
CO_ENGRAM._graphState.edges = edgesDataset;
|
|
145
|
+
|
|
146
|
+
// === 交互 ===
|
|
147
|
+
network.on('click', (params) => {
|
|
148
|
+
// 优先处理边点击(突触详情)
|
|
149
|
+
if (params.edges && params.edges.length > 0 && (!params.nodes || params.nodes.length === 0)) {
|
|
150
|
+
const edgeId = params.edges[0];
|
|
151
|
+
const edge = edgesDataset.get(edgeId);
|
|
152
|
+
if (edge && edge._raw) {
|
|
153
|
+
CO_ENGRAM_SYNAPSES.open(edge._raw.id);
|
|
154
|
+
}
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (!params.nodes || params.nodes.length === 0) {
|
|
158
|
+
// 点空白:取消高亮
|
|
159
|
+
resetHighlight();
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const id = params.nodes[0];
|
|
163
|
+
focusNode(id);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
function resetHighlight() {
|
|
167
|
+
const allNodes = nodesDataset.get();
|
|
168
|
+
const allEdges = edgesDataset.get();
|
|
169
|
+
nodesDataset.update(allNodes.map(n => ({ id: n.id, opacity: 1.0 })));
|
|
170
|
+
edgesDataset.update(allEdges.map(e => ({ id: e.id, color: e.color })));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function focusNode(id) {
|
|
174
|
+
const connectedNodeIds = new Set([id]);
|
|
175
|
+
const connectedEdgeIds = new Set();
|
|
176
|
+
for (const e of graph.edges) {
|
|
177
|
+
if (e.from === id) { connectedNodeIds.add(e.to); connectedEdgeIds.add(e.id); }
|
|
178
|
+
if (e.to === id) { connectedNodeIds.add(e.from); connectedEdgeIds.add(e.id); }
|
|
179
|
+
}
|
|
180
|
+
const allNodes = nodesDataset.get();
|
|
181
|
+
const allEdges = edgesDataset.get();
|
|
182
|
+
nodesDataset.update(allNodes.map(n => ({
|
|
183
|
+
id: n.id,
|
|
184
|
+
opacity: connectedNodeIds.has(n.id) ? 1.0 : 0.15
|
|
185
|
+
})));
|
|
186
|
+
edgesDataset.update(allEdges.map(e => ({
|
|
187
|
+
id: e.id,
|
|
188
|
+
opacity: connectedEdgeIds.has(e.id) ? 1.0 : 0.05
|
|
189
|
+
})));
|
|
190
|
+
showNodeDetail(id);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function showNodeDetail(id) {
|
|
194
|
+
let detail;
|
|
195
|
+
try {
|
|
196
|
+
detail = await CO_ENGRAM.apiGet('/api/engrams/' + encodeURIComponent(id));
|
|
197
|
+
} catch (e) {
|
|
198
|
+
CO_ENGRAM.openDrawer('<h2>' + CO_ENGRAM.escapeHtml(id) + '</h2><div class="empty">加载失败:' + CO_ENGRAM.escapeHtml(e.message) + '</div>');
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// 找该节点的所有 synapse
|
|
203
|
+
const outgoing = graph.edges.filter(e => e.from === id);
|
|
204
|
+
const incoming = graph.edges.filter(e => e.to === id);
|
|
205
|
+
|
|
206
|
+
const familyGroup = (list, label) => {
|
|
207
|
+
if (!list.length) return '';
|
|
208
|
+
const grouped = {};
|
|
209
|
+
for (const e of list) {
|
|
210
|
+
const fam = CO_ENGRAM.synapseFamily(e.kind);
|
|
211
|
+
(grouped[fam] = grouped[fam] || []).push(e);
|
|
212
|
+
}
|
|
213
|
+
let html = '<h3>' + label + ' (' + list.length + ')</h3>';
|
|
214
|
+
for (const fam of Object.keys(grouped)) {
|
|
215
|
+
html += '<div class="field"><span class="chip dot" style="color:' + CO_ENGRAM.familyColor(fam) + '">' + (FAMILY_LABEL[fam] || fam) + '</span></div>';
|
|
216
|
+
for (const e of grouped[fam]) {
|
|
217
|
+
const other = e.from === id ? e.to : e.from;
|
|
218
|
+
const kindLabel = L.synapse[e.kind] || e.kind;
|
|
219
|
+
html += '<div class="field" style="padding-left:0.5rem">'
|
|
220
|
+
+ '<span class="chip synapse-link" data-synapse-id="' + CO_ENGRAM.escapeHtml(e.id) + '" style="background:' + CO_ENGRAM.edgeColor(e.kind) + '22;color:' + CO_ENGRAM.edgeColor(e.kind) + ';cursor:pointer">' + kindLabel + '</span> '
|
|
221
|
+
+ '<span class="engram-link" data-engram-id="' + CO_ENGRAM.escapeHtml(other) + '">' + CO_ENGRAM.escapeHtml(other) + '</span>'
|
|
222
|
+
+ (e.resolutionStatus ? ' <span class="chip" style="background:rgba(239,68,68,.15);color:#ef4444">' + (L.resolution[e.resolutionStatus] || e.resolutionStatus) + '</span>' : '')
|
|
223
|
+
+ '</div>';
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return html;
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const body = [
|
|
230
|
+
'<div class="edit-banner" style="display:flex;gap:.5rem;align-items:center"><strong style="margin-right:auto">节点详情</strong>'
|
|
231
|
+
+ '<button class="btn" onclick="CO_ENGRAM.showTab(\\'engrams\\');setTimeout(()=>CO_ENGRAM_ENGRAMS.open(\\'' + CO_ENGRAM.escapeHtml(detail.id) + '\\'),50)">在记忆印迹中编辑</button>'
|
|
232
|
+
+ '</div>',
|
|
233
|
+
'<h2>' + CO_ENGRAM.escapeHtml(detail.title || detail.id) + '</h2>',
|
|
234
|
+
'<div class="field"><span class="chip kind-' + detail.kind + '">' + (L.kind[detail.kind] || detail.kind) + '</span> '
|
|
235
|
+
+ CO_ENGRAM.importanceBar(detail.importance) + ' <span class="kpi-sub">重要性 ' + (detail.importance || 0).toFixed(2) + '</span></div>',
|
|
236
|
+
'<div class="field"><span class="field-label">ID:</span><code>' + CO_ENGRAM.escapeHtml(detail.id) + '</code></div>',
|
|
237
|
+
(detail.domainTags && detail.domainTags.length
|
|
238
|
+
? '<div class="field"><span class="field-label">领域标签:</span>' + detail.domainTags.map(t => '<span class="chip">' + CO_ENGRAM.escapeHtml(t) + '</span>').join(' ') + '</div>'
|
|
239
|
+
: ''),
|
|
240
|
+
(detail.summary ? '<h3>摘要</h3><div class="field">' + CO_ENGRAM.escapeHtml(detail.summary) + '</div>' : ''),
|
|
241
|
+
'<h3>统计</h3>',
|
|
242
|
+
'<div class="field"><span class="field-label">检索:</span>' + (detail.retrievalCount || 0)
|
|
243
|
+
+ ' <span class="field-label">有效:</span>' + (detail.effectiveRetrievals || 0)
|
|
244
|
+
+ ' <span class="field-label">失败:</span>' + (detail.failedUses || 0) + '</div>',
|
|
245
|
+
'<div class="field"><span class="field-label">创建者:</span>' + CO_ENGRAM.escapeHtml(detail.createdBy || '')
|
|
246
|
+
+ ' <span class="field-label">时间:</span>' + CO_ENGRAM.escapeHtml(detail.createdAt || '') + '</div>',
|
|
247
|
+
familyGroup(outgoing, '发出突触'),
|
|
248
|
+
familyGroup(incoming, '接收突触')
|
|
249
|
+
].join('\\n');
|
|
250
|
+
CO_ENGRAM.openDrawer(body);
|
|
251
|
+
|
|
252
|
+
// drawer 内的 engram / synapse 链接点击 → focus / open
|
|
253
|
+
setTimeout(() => {
|
|
254
|
+
document.querySelectorAll('#detail-drawer .engram-link').forEach(el => {
|
|
255
|
+
el.onclick = () => {
|
|
256
|
+
const targetId = el.getAttribute('data-engram-id');
|
|
257
|
+
if (targetId) {
|
|
258
|
+
network.focus(targetId, { scale: 1.2, animation: { duration: 400, easingFunction: 'easeInOutQuad' } });
|
|
259
|
+
network.selectNodes([targetId]);
|
|
260
|
+
focusNode(targetId);
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
});
|
|
264
|
+
document.querySelectorAll('#detail-drawer .synapse-link').forEach(el => {
|
|
265
|
+
el.onclick = () => {
|
|
266
|
+
const sid = el.getAttribute('data-synapse-id');
|
|
267
|
+
if (sid) CO_ENGRAM_SYNAPSES.open(sid);
|
|
268
|
+
};
|
|
269
|
+
});
|
|
270
|
+
}, 50);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// === 工具栏交互 ===
|
|
274
|
+
CO_ENGRAM._graphState.applyFilters = function() {
|
|
275
|
+
nodesDataset.clear();
|
|
276
|
+
edgesDataset.clear();
|
|
277
|
+
nodesDataset.add(buildNodes());
|
|
278
|
+
edgesDataset.add(buildEdges());
|
|
279
|
+
};
|
|
280
|
+
CO_ENGRAM._graphState.togglePhysics = function() {
|
|
281
|
+
state.physicsEnabled = !state.physicsEnabled;
|
|
282
|
+
network.setOptions({ physics: { enabled: state.physicsEnabled } });
|
|
283
|
+
};
|
|
284
|
+
CO_ENGRAM._graphState.fit = function() { network.fit({ animation: true }); };
|
|
285
|
+
CO_ENGRAM._graphState.resetView = function() {
|
|
286
|
+
state.showKinds = { fact: true, observation: true, pattern: true, procedure: true, hypothesis: true };
|
|
287
|
+
state.showSynapseKinds = Object.fromEntries(ALL_SYNAPSE_KINDS.map(k => [k, true]));
|
|
288
|
+
document.querySelectorAll('.graph-toolbar input[type=checkbox]').forEach(c => c.checked = true);
|
|
289
|
+
CO_ENGRAM._graphState.applyFilters();
|
|
290
|
+
network.fit({ animation: true });
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// 工具栏点击处理(从 onclick 调用)
|
|
295
|
+
window.CO_ENGRAM_GRAPH = {
|
|
296
|
+
toggleSynapseKind(kind, checked) {
|
|
297
|
+
const s = CO_ENGRAM._graphState;
|
|
298
|
+
if (!s) return;
|
|
299
|
+
s.state.showSynapseKinds[kind] = checked;
|
|
300
|
+
s.applyFilters();
|
|
301
|
+
},
|
|
302
|
+
toggleKind(kind, checked) {
|
|
303
|
+
const s = CO_ENGRAM._graphState;
|
|
304
|
+
if (!s) return;
|
|
305
|
+
s.state.showKinds[kind] = checked;
|
|
306
|
+
s.applyFilters();
|
|
307
|
+
},
|
|
308
|
+
togglePhysics() { CO_ENGRAM._graphState && CO_ENGRAM._graphState.togglePhysics(); },
|
|
309
|
+
fit() { CO_ENGRAM._graphState && CO_ENGRAM._graphState.fit(); },
|
|
310
|
+
reset() { CO_ENGRAM._graphState && CO_ENGRAM._graphState.resetView(); }
|
|
311
|
+
};
|
|
312
|
+
`;
|
|
313
|
+
//# sourceMappingURL=graph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph.js","sourceRoot":"","sources":["../../src/runtime/graph.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4S5B,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Viewer runtime i18n helper
|
|
3
|
+
*
|
|
4
|
+
* 浏览器端翻译函数,从 window.CO_ENGRAM_I18N({zh, en}) 读取翻译表,
|
|
5
|
+
* 当前语言由 window.CO_ENGRAM_LANG 决定。提供:
|
|
6
|
+
* - CO_ENGRAM_T.t(key, vars?):通用翻译
|
|
7
|
+
* - CO_ENGRAM_T.enumLabel(category, value):枚举显示(如 'kind', 'fact')
|
|
8
|
+
* - CO_ENGRAM_T.fieldLabel(name):字段标签(如 'time', 'id')
|
|
9
|
+
* - CO_ENGRAM_T.decayLabel(days):衰退倒计时文案
|
|
10
|
+
*
|
|
11
|
+
* 与 core 的 t() 语义一致:fallback 顺序为目标语言 → 英文 → key 本身。
|
|
12
|
+
*
|
|
13
|
+
* @module @co-engram/viewer/runtime/i18n
|
|
14
|
+
*/
|
|
15
|
+
export declare const I18N_RUNTIME = "\n// ============================================================\n// Co-Engram Viewer i18n helper(\u6D4F\u89C8\u5668\u7AEF,\u6302\u5728 window.CO_ENGRAM_T)\n// ============================================================\nwindow.CO_ENGRAM_T = (function() {\n 'use strict';\n\n const DICTS = (window.CO_ENGRAM_I18N || { zh: {}, en: {} });\n const LANG = window.CO_ENGRAM_LANG || 'en';\n\n function dictFor(lang) {\n return DICTS[lang] || {};\n }\n\n function t(key, vars) {\n const mainDict = dictFor(LANG);\n const enDict = dictFor('en');\n const template = mainDict[key] || enDict[key] || key;\n if (!vars) return template;\n return template.replace(/\\$\\{(\\w+)\\}/g, function(_, name) {\n return vars[name] !== undefined ? String(vars[name]) : '\\ + name + ';\n });\n }\n\n function enumLabel(category, value) {\n if (!value) return t('common.unknown');\n return t('enum.' + category + '.' + value);\n }\n\n function fieldLabel(name) {\n return t('field.label.' + name);\n }\n\n function sectionLabel(name) {\n return t('section.' + name);\n }\n\n function actionLabel(name) {\n return t('action.' + name);\n }\n\n function decayLabel(days) {\n if (days === null || days === undefined) return t('decay.forgotten');\n return t('decay.daysToNext', { days: days });\n }\n\n function currentLang() {\n return LANG;\n }\n\n return {\n t: t,\n enumLabel: enumLabel,\n fieldLabel: fieldLabel,\n sectionLabel: sectionLabel,\n actionLabel: actionLabel,\n decayLabel: decayLabel,\n currentLang: currentLang,\n };\n})();\n";
|
|
16
|
+
//# sourceMappingURL=i18n.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/runtime/i18n.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,eAAO,MAAM,YAAY,6kDA4DxB,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Viewer runtime i18n helper
|
|
3
|
+
*
|
|
4
|
+
* 浏览器端翻译函数,从 window.CO_ENGRAM_I18N({zh, en}) 读取翻译表,
|
|
5
|
+
* 当前语言由 window.CO_ENGRAM_LANG 决定。提供:
|
|
6
|
+
* - CO_ENGRAM_T.t(key, vars?):通用翻译
|
|
7
|
+
* - CO_ENGRAM_T.enumLabel(category, value):枚举显示(如 'kind', 'fact')
|
|
8
|
+
* - CO_ENGRAM_T.fieldLabel(name):字段标签(如 'time', 'id')
|
|
9
|
+
* - CO_ENGRAM_T.decayLabel(days):衰退倒计时文案
|
|
10
|
+
*
|
|
11
|
+
* 与 core 的 t() 语义一致:fallback 顺序为目标语言 → 英文 → key 本身。
|
|
12
|
+
*
|
|
13
|
+
* @module @co-engram/viewer/runtime/i18n
|
|
14
|
+
*/
|
|
15
|
+
export const I18N_RUNTIME = `
|
|
16
|
+
// ============================================================
|
|
17
|
+
// Co-Engram Viewer i18n helper(浏览器端,挂在 window.CO_ENGRAM_T)
|
|
18
|
+
// ============================================================
|
|
19
|
+
window.CO_ENGRAM_T = (function() {
|
|
20
|
+
'use strict';
|
|
21
|
+
|
|
22
|
+
const DICTS = (window.CO_ENGRAM_I18N || { zh: {}, en: {} });
|
|
23
|
+
const LANG = window.CO_ENGRAM_LANG || 'en';
|
|
24
|
+
|
|
25
|
+
function dictFor(lang) {
|
|
26
|
+
return DICTS[lang] || {};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function t(key, vars) {
|
|
30
|
+
const mainDict = dictFor(LANG);
|
|
31
|
+
const enDict = dictFor('en');
|
|
32
|
+
const template = mainDict[key] || enDict[key] || key;
|
|
33
|
+
if (!vars) return template;
|
|
34
|
+
return template.replace(/\\$\\{(\\w+)\\}/g, function(_, name) {
|
|
35
|
+
return vars[name] !== undefined ? String(vars[name]) : '\\${" + name + "}';
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function enumLabel(category, value) {
|
|
40
|
+
if (!value) return t('common.unknown');
|
|
41
|
+
return t('enum.' + category + '.' + value);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function fieldLabel(name) {
|
|
45
|
+
return t('field.label.' + name);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function sectionLabel(name) {
|
|
49
|
+
return t('section.' + name);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function actionLabel(name) {
|
|
53
|
+
return t('action.' + name);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function decayLabel(days) {
|
|
57
|
+
if (days === null || days === undefined) return t('decay.forgotten');
|
|
58
|
+
return t('decay.daysToNext', { days: days });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function currentLang() {
|
|
62
|
+
return LANG;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
t: t,
|
|
67
|
+
enumLabel: enumLabel,
|
|
68
|
+
fieldLabel: fieldLabel,
|
|
69
|
+
sectionLabel: sectionLabel,
|
|
70
|
+
actionLabel: actionLabel,
|
|
71
|
+
decayLabel: decayLabel,
|
|
72
|
+
currentLang: currentLang,
|
|
73
|
+
};
|
|
74
|
+
})();
|
|
75
|
+
`;
|
|
76
|
+
//# sourceMappingURL=i18n.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18n.js","sourceRoot":"","sources":["../../src/runtime/i18n.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;kEAoBsC,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwC7E,CAAC"}
|