tink-harness 1.9.5 → 1.9.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/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,19 @@ All notable changes to Tink are tracked here.
|
|
|
6
6
|
|
|
7
7
|
No unreleased changes yet.
|
|
8
8
|
|
|
9
|
+
## [1.9.6] - 2026-06-10
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- The harness map is now fully navigable: wheel zoom toward the cursor (0.4x–5x), drag to pan, +/− and reset controls, and double-click to return to the full view.
|
|
14
|
+
- Nodes render as 3D-style spheres with radial gradients, depth shadows on interactive nodes, and a vignette background for depth.
|
|
15
|
+
- Added a "How to read this map" card to the graph tab's right rail with plain-language explanations of circles, colors, lines, satellites, controls, and edge types.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- Reordered the graph right rail: reading guide → selected node → graph overview.
|
|
20
|
+
- Map help text now mentions zoom and drag controls.
|
|
21
|
+
|
|
9
22
|
## [1.9.5] - 2026-06-10
|
|
10
23
|
|
|
11
24
|
### Added
|
package/VERSIONING.md
CHANGED
package/package.json
CHANGED
|
@@ -48,7 +48,7 @@ const COPY = {
|
|
|
48
48
|
heroText: 'Every visible Tink run, rule, memory reference, and harness relationship mapped into one local dashboard. This report only prepares suggestions and never edits reusable state.',
|
|
49
49
|
generated: 'GENERATED',
|
|
50
50
|
harnessMap: 'HARNESS MAP',
|
|
51
|
-
mapHelp: 'Harnesses, rules, memory, and stages are mapped from visible Tink records.
|
|
51
|
+
mapHelp: 'Harnesses, rules, memory, and stages are mapped from visible Tink records. Scroll to zoom, drag to move, click a node to inspect it.',
|
|
52
52
|
graphControls: 'Graph controls',
|
|
53
53
|
full: 'Full',
|
|
54
54
|
core: 'Core',
|
|
@@ -146,6 +146,30 @@ const COPY = {
|
|
|
146
146
|
runWindow: 'Run window',
|
|
147
147
|
totalRuns: 'Runs',
|
|
148
148
|
refCount: 'References',
|
|
149
|
+
zoomIn: 'Zoom in',
|
|
150
|
+
zoomOut: 'Zoom out',
|
|
151
|
+
zoomReset: 'Reset view',
|
|
152
|
+
mapGuideEyebrow: 'HOW TO READ',
|
|
153
|
+
mapGuideTitle: 'What is this map?',
|
|
154
|
+
mapGuideText: 'Each circle is something Tink knows about: a harness, a rule it loads, or a memory file it reads. The map is drawn only from visible local records.',
|
|
155
|
+
guideItems: [
|
|
156
|
+
['Big circle', 'The more a harness is used, the bigger its circle.'],
|
|
157
|
+
['Color', 'Blue circles are harnesses; gray ones are rules, memory, and stages.'],
|
|
158
|
+
['Line', 'A line means "works together": a harness using a rule, reading memory, or leading to a next step.'],
|
|
159
|
+
['Small dots', 'Tiny satellites around a harness are its usage, evidence, and score signals.']
|
|
160
|
+
],
|
|
161
|
+
controlItems: [
|
|
162
|
+
['Wheel / + −', 'Zoom in and out'],
|
|
163
|
+
['Drag', 'Move around the map'],
|
|
164
|
+
['Double-click', 'Back to full view'],
|
|
165
|
+
['Click a circle', 'See its details below']
|
|
166
|
+
],
|
|
167
|
+
relationTitle: 'What the lines mean',
|
|
168
|
+
relationItems: [
|
|
169
|
+
['uses_rule', 'This harness loads that rule'],
|
|
170
|
+
['uses_memory', 'This harness reads that memory file'],
|
|
171
|
+
['sequence', 'This harness is usually followed by that step']
|
|
172
|
+
],
|
|
149
173
|
groups: [
|
|
150
174
|
['keep', 'Healthy harnesses', 'Ready to keep using'],
|
|
151
175
|
['weave', 'Weave candidates', 'Worth improving next'],
|
|
@@ -206,6 +230,30 @@ COPY.ko = {
|
|
|
206
230
|
runWindow: '기록 기간',
|
|
207
231
|
totalRuns: 'Run 수',
|
|
208
232
|
refCount: '참조 횟수',
|
|
233
|
+
zoomIn: '확대',
|
|
234
|
+
zoomOut: '축소',
|
|
235
|
+
zoomReset: '전체 보기',
|
|
236
|
+
mapGuideEyebrow: '지도 읽는 법',
|
|
237
|
+
mapGuideTitle: '이 지도는 무엇인가요?',
|
|
238
|
+
mapGuideText: '원 하나하나가 Tink가 알고 있는 것입니다: 하네스, 하네스가 쓰는 규칙, 읽는 메모리 파일. 로컬에 보이는 기록만으로 그려집니다.',
|
|
239
|
+
guideItems: [
|
|
240
|
+
['큰 원', '하네스를 많이 쓸수록 원이 커집니다.'],
|
|
241
|
+
['색상', '파란 원이 하네스이고, 회색 계열은 규칙·메모리·단계입니다.'],
|
|
242
|
+
['선', '"함께 일한다"는 뜻입니다. 하네스가 규칙을 쓰거나, 메모리를 읽거나, 다음 단계로 이어질 때 선이 생깁니다.'],
|
|
243
|
+
['작은 점', '하네스 주변의 작은 점들은 사용·근거·점수 신호입니다.']
|
|
244
|
+
],
|
|
245
|
+
controlItems: [
|
|
246
|
+
['휠 / + −', '확대·축소'],
|
|
247
|
+
['드래그', '지도 이동'],
|
|
248
|
+
['더블클릭', '전체 보기로 복귀'],
|
|
249
|
+
['원 클릭', '아래에서 상세 보기']
|
|
250
|
+
],
|
|
251
|
+
relationTitle: '선의 의미',
|
|
252
|
+
relationItems: [
|
|
253
|
+
['uses_rule', '이 하네스가 그 규칙을 불러옵니다'],
|
|
254
|
+
['uses_memory', '이 하네스가 그 메모리 파일을 읽습니다'],
|
|
255
|
+
['sequence', '이 하네스 다음에 그 단계가 자주 이어집니다']
|
|
256
|
+
],
|
|
209
257
|
navLabel: '탐색',
|
|
210
258
|
operator: '작업자',
|
|
211
259
|
online: 'Tink 온라인',
|
|
@@ -214,7 +262,7 @@ COPY.ko = {
|
|
|
214
262
|
heroText: '보이는 Tink run, rule, memory reference, harness 관계를 하나의 로컬 대시보드로 보여줍니다. 이 보고서는 제안만 준비하며 재사용 상태를 직접 수정하지 않습니다.',
|
|
215
263
|
generated: '생성 시각',
|
|
216
264
|
harnessMap: '하네스 지도',
|
|
217
|
-
mapHelp: '보이는 Tink 기록에서 하네스, rule, memory, stage 관계를 그립니다. 노드를 클릭하면 자세히 볼 수 있습니다.',
|
|
265
|
+
mapHelp: '보이는 Tink 기록에서 하네스, rule, memory, stage 관계를 그립니다. 휠로 확대, 드래그로 이동, 노드를 클릭하면 자세히 볼 수 있습니다.',
|
|
218
266
|
graphControls: '그래프 조작',
|
|
219
267
|
full: '전체',
|
|
220
268
|
core: '핵심',
|
|
@@ -638,13 +686,34 @@ function renderGraphCanvas(summary, copy) {
|
|
|
638
686
|
<h2 id="map-title">${escapeHtml(mapTitle)}</h2>
|
|
639
687
|
<p>${escapeHtml(copy.mapHelp)}</p>
|
|
640
688
|
</div>
|
|
641
|
-
<div class="map-controls
|
|
642
|
-
<
|
|
643
|
-
|
|
689
|
+
<div class="map-controls-row">
|
|
690
|
+
<div class="map-controls" aria-label="${escapeAttr(copy.graphControls)}">
|
|
691
|
+
<button class="active" type="button" data-mode="full" aria-pressed="true">${escapeHtml(copy.full)}</button>
|
|
692
|
+
<button type="button" data-mode="core" aria-pressed="false">${escapeHtml(copy.core)}</button>
|
|
693
|
+
</div>
|
|
694
|
+
<div class="map-controls" aria-label="zoom">
|
|
695
|
+
<button type="button" data-zoom="in" aria-label="${escapeAttr(copy.zoomIn || 'Zoom in')}" title="${escapeAttr(copy.zoomIn || 'Zoom in')}">+</button>
|
|
696
|
+
<button type="button" data-zoom="out" aria-label="${escapeAttr(copy.zoomOut || 'Zoom out')}" title="${escapeAttr(copy.zoomOut || 'Zoom out')}">−</button>
|
|
697
|
+
<button type="button" data-zoom="reset" title="${escapeAttr(copy.zoomReset || 'Reset view')}">${escapeHtml(copy.zoomReset || 'Reset')}</button>
|
|
698
|
+
</div>
|
|
644
699
|
</div>
|
|
645
700
|
</div>
|
|
646
701
|
<svg class="graph-canvas" viewBox="0 0 1090 680" role="img" aria-label="Harness health graph">
|
|
647
|
-
<
|
|
702
|
+
<defs>
|
|
703
|
+
<radialGradient id="graph-bg-grad" cx="50%" cy="42%" r="75%">
|
|
704
|
+
<stop offset="0%" style="stop-color: #16181D"/>
|
|
705
|
+
<stop offset="100%" style="stop-color: #0C0D10"/>
|
|
706
|
+
</radialGradient>
|
|
707
|
+
${Object.entries(TYPE_COLORS).map(([type, color]) => `
|
|
708
|
+
<radialGradient id="node-grad-${escapeAttr(type)}" cx="32%" cy="28%" r="78%">
|
|
709
|
+
<stop offset="0%" style="stop-color: #FFFFFF; stop-opacity: 0.42"/>
|
|
710
|
+
<stop offset="38%" style="stop-color: ${escapeAttr(color)}; stop-opacity: 0.98"/>
|
|
711
|
+
<stop offset="100%" style="stop-color: ${escapeAttr(color)}; stop-opacity: 0.78"/>
|
|
712
|
+
</radialGradient>
|
|
713
|
+
`).join('')}
|
|
714
|
+
</defs>
|
|
715
|
+
<rect class="graph-bg" width="1090" height="680" fill="url(#graph-bg-grad)"/>
|
|
716
|
+
<g id="graph-viewport">
|
|
648
717
|
<g class="edges">
|
|
649
718
|
${edges.map((edge, index) => `
|
|
650
719
|
<line
|
|
@@ -686,8 +755,8 @@ function renderGraphCanvas(summary, copy) {
|
|
|
686
755
|
cx="${node.x.toFixed(1)}"
|
|
687
756
|
cy="${node.y.toFixed(1)}"
|
|
688
757
|
r="${node.radius.toFixed(1)}"
|
|
689
|
-
fill="
|
|
690
|
-
fill-opacity="${node.type === 'harness' ? '
|
|
758
|
+
fill="url(#node-grad-${escapeAttr(TYPE_COLORS[node.type] ? node.type : 'unknown')})"
|
|
759
|
+
fill-opacity="${node.type === 'harness' ? '1' : '0.85'}"
|
|
691
760
|
stroke="${escapeAttr('var(--text-secondary)')}"
|
|
692
761
|
stroke-opacity="${node.glow ? '0.9' : '0.18'}"
|
|
693
762
|
stroke-width="${node.glow ? '1.8' : '0.8'}"
|
|
@@ -703,6 +772,7 @@ function renderGraphCanvas(summary, copy) {
|
|
|
703
772
|
<text x="${(node.x + node.radius + 7).toFixed(1)}" y="${(node.y + 4).toFixed(1)}">${escapeHtml(node.label)}</text>
|
|
704
773
|
`).join('')}
|
|
705
774
|
</g>
|
|
775
|
+
</g>
|
|
706
776
|
</svg>
|
|
707
777
|
<div class="graph-tooltip" id="graph-tooltip" role="status" aria-live="polite"></div>
|
|
708
778
|
<div class="map-caption">
|
|
@@ -1008,6 +1078,39 @@ function renderGraphOverview(graph = {}, copy) {
|
|
|
1008
1078
|
`;
|
|
1009
1079
|
}
|
|
1010
1080
|
|
|
1081
|
+
function renderMapGuide(copy) {
|
|
1082
|
+
const guideItems = Array.isArray(copy.guideItems) ? copy.guideItems : [];
|
|
1083
|
+
const controlItems = Array.isArray(copy.controlItems) ? copy.controlItems : [];
|
|
1084
|
+
const relationItems = Array.isArray(copy.relationItems) ? copy.relationItems : [];
|
|
1085
|
+
return `
|
|
1086
|
+
<section class="insight-card map-guide">
|
|
1087
|
+
<div class="panel-title">
|
|
1088
|
+
<p class="eyebrow">${escapeHtml(copy.mapGuideEyebrow || 'HOW TO READ')}</p>
|
|
1089
|
+
<h2>${escapeHtml(copy.mapGuideTitle || 'What is this map?')}</h2>
|
|
1090
|
+
<p>${escapeHtml(copy.mapGuideText || '')}</p>
|
|
1091
|
+
</div>
|
|
1092
|
+
<ul class="guide-list">
|
|
1093
|
+
${guideItems.map(([term, text]) => `
|
|
1094
|
+
<li><strong>${escapeHtml(term)}</strong><span>${escapeHtml(text)}</span></li>
|
|
1095
|
+
`).join('')}
|
|
1096
|
+
</ul>
|
|
1097
|
+
<ul class="guide-list guide-controls">
|
|
1098
|
+
${controlItems.map(([key, text]) => `
|
|
1099
|
+
<li><kbd>${escapeHtml(key)}</kbd><span>${escapeHtml(text)}</span></li>
|
|
1100
|
+
`).join('')}
|
|
1101
|
+
</ul>
|
|
1102
|
+
${relationItems.length ? `
|
|
1103
|
+
<p class="detail-label">${escapeHtml(copy.relationTitle || 'What the lines mean')}</p>
|
|
1104
|
+
<ul class="guide-list guide-relations">
|
|
1105
|
+
${relationItems.map(([type, text]) => `
|
|
1106
|
+
<li><code>${escapeHtml(type)}</code><span>${escapeHtml(text)}</span></li>
|
|
1107
|
+
`).join('')}
|
|
1108
|
+
</ul>
|
|
1109
|
+
` : ''}
|
|
1110
|
+
</section>
|
|
1111
|
+
`;
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1011
1114
|
function renderRoutingCard(copy) {
|
|
1012
1115
|
const rules = Array.isArray(copy.routeRules) ? copy.routeRules : [];
|
|
1013
1116
|
return `
|
|
@@ -1364,6 +1467,76 @@ function renderScript(harnesses, copy) {
|
|
|
1364
1467
|
document.querySelectorAll('[data-mode]').forEach((button) => {
|
|
1365
1468
|
button.addEventListener('click', () => applyMode(button.dataset.mode));
|
|
1366
1469
|
});
|
|
1470
|
+
const graphSvg = document.querySelector('.graph-canvas');
|
|
1471
|
+
const graphViewport = document.getElementById('graph-viewport');
|
|
1472
|
+
if (graphSvg && graphViewport) {
|
|
1473
|
+
const view = { x: 0, y: 0, k: 1 };
|
|
1474
|
+
graphViewport.style.transformOrigin = '0 0';
|
|
1475
|
+
const applyView = () => {
|
|
1476
|
+
graphViewport.style.transform = 'translate(' + view.x + 'px, ' + view.y + 'px) scale(' + view.k + ')';
|
|
1477
|
+
};
|
|
1478
|
+
const svgPoint = (event) => {
|
|
1479
|
+
const pt = graphSvg.createSVGPoint();
|
|
1480
|
+
pt.x = event.clientX;
|
|
1481
|
+
pt.y = event.clientY;
|
|
1482
|
+
return pt.matrixTransform(graphSvg.getScreenCTM().inverse());
|
|
1483
|
+
};
|
|
1484
|
+
const zoomAt = (factor, cx, cy) => {
|
|
1485
|
+
const k = Math.min(5, Math.max(0.4, view.k * factor));
|
|
1486
|
+
const real = k / view.k;
|
|
1487
|
+
view.x = cx - (cx - view.x) * real;
|
|
1488
|
+
view.y = cy - (cy - view.y) * real;
|
|
1489
|
+
view.k = k;
|
|
1490
|
+
applyView();
|
|
1491
|
+
};
|
|
1492
|
+
const resetView = () => {
|
|
1493
|
+
graphViewport.classList.add('is-resetting');
|
|
1494
|
+
view.x = 0; view.y = 0; view.k = 1;
|
|
1495
|
+
applyView();
|
|
1496
|
+
setTimeout(() => graphViewport.classList.remove('is-resetting'), 360);
|
|
1497
|
+
};
|
|
1498
|
+
graphSvg.addEventListener('wheel', (event) => {
|
|
1499
|
+
event.preventDefault();
|
|
1500
|
+
const point = svgPoint(event);
|
|
1501
|
+
zoomAt(event.deltaY < 0 ? 1.15 : 1 / 1.15, point.x, point.y);
|
|
1502
|
+
}, { passive: false });
|
|
1503
|
+
let panState = null;
|
|
1504
|
+
graphSvg.addEventListener('pointerdown', (event) => {
|
|
1505
|
+
if (event.button !== 0) return;
|
|
1506
|
+
panState = { x: event.clientX, y: event.clientY, moved: false };
|
|
1507
|
+
graphSvg.setPointerCapture(event.pointerId);
|
|
1508
|
+
});
|
|
1509
|
+
graphSvg.addEventListener('pointermove', (event) => {
|
|
1510
|
+
if (!panState) return;
|
|
1511
|
+
const dx = event.clientX - panState.x;
|
|
1512
|
+
const dy = event.clientY - panState.y;
|
|
1513
|
+
if (!panState.moved && Math.abs(dx) + Math.abs(dy) < 3) return;
|
|
1514
|
+
panState.moved = true;
|
|
1515
|
+
graphSvg.classList.add('is-panning');
|
|
1516
|
+
const scale = 1090 / graphSvg.clientWidth;
|
|
1517
|
+
view.x += dx * scale;
|
|
1518
|
+
view.y += dy * scale;
|
|
1519
|
+
panState.x = event.clientX;
|
|
1520
|
+
panState.y = event.clientY;
|
|
1521
|
+
applyView();
|
|
1522
|
+
});
|
|
1523
|
+
const endPan = () => {
|
|
1524
|
+
panState = null;
|
|
1525
|
+
graphSvg.classList.remove('is-panning');
|
|
1526
|
+
};
|
|
1527
|
+
graphSvg.addEventListener('pointerup', endPan);
|
|
1528
|
+
graphSvg.addEventListener('pointercancel', endPan);
|
|
1529
|
+
graphSvg.addEventListener('dblclick', (event) => {
|
|
1530
|
+
event.preventDefault();
|
|
1531
|
+
resetView();
|
|
1532
|
+
});
|
|
1533
|
+
document.querySelectorAll('[data-zoom]').forEach((button) => {
|
|
1534
|
+
button.addEventListener('click', () => {
|
|
1535
|
+
if (button.dataset.zoom === 'reset') return resetView();
|
|
1536
|
+
zoomAt(button.dataset.zoom === 'in' ? 1.3 : 1 / 1.3, 545, 340);
|
|
1537
|
+
});
|
|
1538
|
+
});
|
|
1539
|
+
}
|
|
1367
1540
|
const VALID_TABS = ['home', 'harnesses', 'memory', 'graph', 'activity'];
|
|
1368
1541
|
const navLinks = Array.from(document.querySelectorAll('.nav a[data-tab]'));
|
|
1369
1542
|
const pages = Array.from(document.querySelectorAll('.page'));
|
|
@@ -2020,6 +2193,83 @@ function renderStyles() {
|
|
|
2020
2193
|
transition: opacity 160ms ease, transform 160ms ease;
|
|
2021
2194
|
}
|
|
2022
2195
|
|
|
2196
|
+
.map-controls-row {
|
|
2197
|
+
display: flex;
|
|
2198
|
+
gap: var(--space-2);
|
|
2199
|
+
align-items: center;
|
|
2200
|
+
flex-wrap: wrap;
|
|
2201
|
+
}
|
|
2202
|
+
|
|
2203
|
+
.graph-canvas {
|
|
2204
|
+
cursor: grab;
|
|
2205
|
+
touch-action: none;
|
|
2206
|
+
}
|
|
2207
|
+
|
|
2208
|
+
.graph-canvas.is-panning { cursor: grabbing; }
|
|
2209
|
+
|
|
2210
|
+
#graph-viewport {
|
|
2211
|
+
will-change: transform;
|
|
2212
|
+
}
|
|
2213
|
+
|
|
2214
|
+
#graph-viewport.is-resetting {
|
|
2215
|
+
transition: transform 340ms cubic-bezier(0.2, 0.8, 0.2, 1);
|
|
2216
|
+
}
|
|
2217
|
+
|
|
2218
|
+
.graph-node.is-interactive {
|
|
2219
|
+
filter: drop-shadow(0 5px 7px rgba(0, 0, 0, 0.55));
|
|
2220
|
+
}
|
|
2221
|
+
|
|
2222
|
+
.graph-node.is-interactive:hover,
|
|
2223
|
+
.graph-node.is-interactive:focus-visible {
|
|
2224
|
+
filter: drop-shadow(0 9px 14px rgba(0, 0, 0, 0.65));
|
|
2225
|
+
}
|
|
2226
|
+
|
|
2227
|
+
.map-guide .panel-title p:last-child {
|
|
2228
|
+
font-size: 12px;
|
|
2229
|
+
line-height: 1.5;
|
|
2230
|
+
}
|
|
2231
|
+
|
|
2232
|
+
.guide-list {
|
|
2233
|
+
margin: var(--space-3) 0 0;
|
|
2234
|
+
padding: 0;
|
|
2235
|
+
list-style: none;
|
|
2236
|
+
display: grid;
|
|
2237
|
+
gap: var(--space-2);
|
|
2238
|
+
}
|
|
2239
|
+
|
|
2240
|
+
.guide-list li {
|
|
2241
|
+
display: grid;
|
|
2242
|
+
grid-template-columns: 76px 1fr;
|
|
2243
|
+
gap: var(--space-2);
|
|
2244
|
+
align-items: start;
|
|
2245
|
+
font-size: 12px;
|
|
2246
|
+
line-height: 1.45;
|
|
2247
|
+
color: var(--text-secondary);
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
.guide-list strong {
|
|
2251
|
+
color: var(--text-primary);
|
|
2252
|
+
font-size: 12px;
|
|
2253
|
+
font-weight: 500;
|
|
2254
|
+
}
|
|
2255
|
+
|
|
2256
|
+
.guide-list kbd {
|
|
2257
|
+
font-family: var(--font-mono);
|
|
2258
|
+
font-size: 10px;
|
|
2259
|
+
color: var(--accent-text);
|
|
2260
|
+
border: 1px solid var(--border-default);
|
|
2261
|
+
border-radius: var(--radius-sm);
|
|
2262
|
+
background: var(--bg-hover);
|
|
2263
|
+
padding: 2px 5px;
|
|
2264
|
+
text-align: center;
|
|
2265
|
+
white-space: nowrap;
|
|
2266
|
+
}
|
|
2267
|
+
|
|
2268
|
+
.guide-controls { border-top: 1px solid var(--border-default); padding-top: var(--space-3); }
|
|
2269
|
+
|
|
2270
|
+
.guide-relations li { grid-template-columns: 96px 1fr; }
|
|
2271
|
+
.guide-relations code { font-size: 10px; }
|
|
2272
|
+
|
|
2023
2273
|
.map-legend {
|
|
2024
2274
|
display: flex;
|
|
2025
2275
|
gap: var(--space-3);
|
|
@@ -2850,9 +3100,10 @@ function renderReport(summary) {
|
|
|
2850
3100
|
<aside class="right-rail" aria-label="Insights">
|
|
2851
3101
|
<div data-rail="home harnesses memory activity">${renderStats(summary, copy)}</div>
|
|
2852
3102
|
<div data-rail="home harnesses">${renderConfidence(summary, copy)}</div>
|
|
2853
|
-
<div data-rail="graph">${
|
|
2854
|
-
<div data-rail="home harnesses">${renderImportantHarnesses(harnesses, copy)}</div>
|
|
3103
|
+
<div data-rail="graph">${renderMapGuide(copy)}</div>
|
|
2855
3104
|
<div data-rail="graph">${renderSelectedPanel(harnesses, copy)}</div>
|
|
3105
|
+
<div data-rail="home harnesses">${renderImportantHarnesses(harnesses, copy)}</div>
|
|
3106
|
+
<div data-rail="graph">${renderGraphOverview(summary.graph || {}, copy)}</div>
|
|
2856
3107
|
</aside>
|
|
2857
3108
|
</div>
|
|
2858
3109
|
${renderContractMetadata(copy)}
|