living-documentation 7.39.0 → 7.41.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/dist/src/frontend/diagram/clipboard.js +22 -7
- package/dist/src/frontend/diagram/evidence.js +146 -0
- package/dist/src/frontend/diagram/history.js +4 -0
- package/dist/src/frontend/diagram/main.js +4 -0
- package/dist/src/frontend/diagram/network.js +10 -5
- package/dist/src/frontend/diagram/node-rendering.js +1 -0
- package/dist/src/frontend/diagram/persistence.js +5 -0
- package/dist/src/frontend/diagram/ports.js +1 -1
- package/dist/src/frontend/diagram/state.js +2 -0
- package/dist/src/frontend/diagram.html +70 -0
- package/dist/src/frontend/i18n/en.json +9 -0
- package/dist/src/frontend/i18n/fr.json +9 -0
- package/dist/src/mcp/server.d.ts.map +1 -1
- package/dist/src/mcp/server.js +86 -8
- package/dist/src/mcp/server.js.map +1 -1
- package/dist/src/mcp/tools/diagrams.d.ts +56 -18
- package/dist/src/mcp/tools/diagrams.d.ts.map +1 -1
- package/dist/src/mcp/tools/diagrams.js +269 -24
- package/dist/src/mcp/tools/diagrams.js.map +1 -1
- package/package.json +1 -1
|
@@ -48,15 +48,25 @@ async function _selectionToBlob() {
|
|
|
48
48
|
const cropW = Math.ceil(br.x - tl.x + PAD * 2);
|
|
49
49
|
const cropH = Math.ceil(br.y - tl.y + PAD * 2);
|
|
50
50
|
|
|
51
|
-
// Temporarily
|
|
51
|
+
// Temporarily clear every selection state so interactive chrome (selected
|
|
52
|
+
// edge color, label resize handles, anchor handles) does not appear in PNGs.
|
|
52
53
|
const savedNodeIds = [...st.selectedNodeIds];
|
|
54
|
+
const savedEdgeIds = [...(st.selectedEdgeIds || [])];
|
|
55
|
+
const savedNetworkNodeIds = st.network.getSelectedNodes();
|
|
56
|
+
const savedNetworkEdgeIds = st.network.getSelectedEdges();
|
|
53
57
|
st.network.unselectAll();
|
|
54
58
|
st.selectedNodeIds = [];
|
|
59
|
+
st.selectedEdgeIds = [];
|
|
60
|
+
st.exportingPng = true;
|
|
55
61
|
st.network.redraw();
|
|
62
|
+
await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)));
|
|
56
63
|
|
|
57
64
|
const visCanvas = document.querySelector('#vis-canvas canvas');
|
|
58
65
|
if (!visCanvas) {
|
|
59
|
-
st.
|
|
66
|
+
st.exportingPng = false;
|
|
67
|
+
st.selectedNodeIds = savedNodeIds;
|
|
68
|
+
st.selectedEdgeIds = savedEdgeIds;
|
|
69
|
+
st.network.setSelection({ nodes: savedNetworkNodeIds, edges: savedNetworkEdgeIds });
|
|
60
70
|
return null;
|
|
61
71
|
}
|
|
62
72
|
|
|
@@ -73,9 +83,10 @@ async function _selectionToBlob() {
|
|
|
73
83
|
cropX * dpr, cropY * dpr, cropW * dpr, cropH * dpr,
|
|
74
84
|
0, 0, out.width, out.height
|
|
75
85
|
);
|
|
86
|
+
st.exportingPng = false;
|
|
76
87
|
|
|
77
88
|
const blob = await new Promise((res) => out.toBlob(res, 'image/png'));
|
|
78
|
-
return { blob, savedNodeIds };
|
|
89
|
+
return { blob, savedNodeIds, savedEdgeIds, savedNetworkNodeIds, savedNetworkEdgeIds };
|
|
79
90
|
}
|
|
80
91
|
|
|
81
92
|
// ── Copy selection as PNG (to clipboard) ─────────────────────────────────────
|
|
@@ -83,14 +94,16 @@ async function _selectionToBlob() {
|
|
|
83
94
|
export async function copySelectionAsPng() {
|
|
84
95
|
const result = await _selectionToBlob();
|
|
85
96
|
if (!result) return;
|
|
86
|
-
const { blob, savedNodeIds } = result;
|
|
97
|
+
const { blob, savedNodeIds, savedEdgeIds, savedNetworkNodeIds, savedNetworkEdgeIds } = result;
|
|
87
98
|
try {
|
|
88
99
|
await navigator.clipboard.write([new ClipboardItem({ 'image/png': blob })]);
|
|
89
100
|
showToast(t('diagram.toast.png_copied'));
|
|
90
101
|
} catch {
|
|
91
102
|
showToast(t('diagram.toast.png_copy_error'), 'error');
|
|
92
103
|
} finally {
|
|
93
|
-
st.
|
|
104
|
+
st.selectedNodeIds = savedNodeIds;
|
|
105
|
+
st.selectedEdgeIds = savedEdgeIds;
|
|
106
|
+
st.network.setSelection({ nodes: savedNetworkNodeIds, edges: savedNetworkEdgeIds });
|
|
94
107
|
}
|
|
95
108
|
}
|
|
96
109
|
|
|
@@ -99,7 +112,7 @@ export async function copySelectionAsPng() {
|
|
|
99
112
|
export async function saveSelectionAsPng(filename) {
|
|
100
113
|
const result = await _selectionToBlob();
|
|
101
114
|
if (!result) return;
|
|
102
|
-
const { blob, savedNodeIds } = result;
|
|
115
|
+
const { blob, savedNodeIds, savedEdgeIds, savedNetworkNodeIds, savedNetworkEdgeIds } = result;
|
|
103
116
|
try {
|
|
104
117
|
const name = filename.replace(/\.[^.]+$/, ''); // strip extension
|
|
105
118
|
await uploadImageBlob(blob, 'png', name);
|
|
@@ -107,7 +120,9 @@ export async function saveSelectionAsPng(filename) {
|
|
|
107
120
|
} catch {
|
|
108
121
|
showToast(t('diagram.toast.diagram_save_png_error'), 'error');
|
|
109
122
|
} finally {
|
|
110
|
-
st.
|
|
123
|
+
st.selectedNodeIds = savedNodeIds;
|
|
124
|
+
st.selectedEdgeIds = savedEdgeIds;
|
|
125
|
+
st.network.setSelection({ nodes: savedNetworkNodeIds, edges: savedNetworkEdgeIds });
|
|
111
126
|
}
|
|
112
127
|
}
|
|
113
128
|
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// ── Evidence / source consultation mode ───────────────────────────────────────
|
|
2
|
+
// Shows document-provenance markers for nodes and edges that carry `evidence`.
|
|
3
|
+
|
|
4
|
+
import { st } from './state.js';
|
|
5
|
+
import { t } from './t.js';
|
|
6
|
+
|
|
7
|
+
function evidenceItems(item) {
|
|
8
|
+
return Array.isArray(item && item.evidence)
|
|
9
|
+
? item.evidence.filter((e) => e && typeof e.documentId === 'string' && e.documentId.trim())
|
|
10
|
+
: [];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function documentHref(documentId) {
|
|
14
|
+
return '/?doc=' + encodeURIComponent(documentId);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function markerPositionForNode(node) {
|
|
18
|
+
if (!st.network || !node) return null;
|
|
19
|
+
const bodyNode = st.network.body.nodes[node.id];
|
|
20
|
+
if (!bodyNode) return null;
|
|
21
|
+
const w = (bodyNode.shape && bodyNode.shape.width) || node.nodeWidth || 120;
|
|
22
|
+
const h = (bodyNode.shape && bodyNode.shape.height) || node.nodeHeight || 60;
|
|
23
|
+
return st.network.canvasToDOM({ x: bodyNode.x + w / 2 - 8, y: bodyNode.y - h / 2 + 8 });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function markerPositionForEdge(edge) {
|
|
27
|
+
if (!st.network || !edge) return null;
|
|
28
|
+
const bbox = st.edgeLabelBBox && st.edgeLabelBBox[edge.id];
|
|
29
|
+
if (bbox) return st.network.canvasToDOM({ x: bbox.cx, y: bbox.cy });
|
|
30
|
+
|
|
31
|
+
const from = st.network.body.nodes[edge.from];
|
|
32
|
+
const to = edge.to && st.network.body.nodes[edge.to];
|
|
33
|
+
if (!from || !to) return null;
|
|
34
|
+
return st.network.canvasToDOM({ x: (from.x + to.x) / 2, y: (from.y + to.y) / 2 });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function makeMarker(kind, id, pos) {
|
|
38
|
+
const marker = document.createElement('button');
|
|
39
|
+
marker.type = 'button';
|
|
40
|
+
marker.className = 'evidence-marker';
|
|
41
|
+
marker.textContent = '⌘';
|
|
42
|
+
marker.title = t('diagram.evidence.marker_title');
|
|
43
|
+
marker.setAttribute('aria-label', marker.title);
|
|
44
|
+
marker.style.left = `${Math.round(pos.x)}px`;
|
|
45
|
+
marker.style.top = `${Math.round(pos.y)}px`;
|
|
46
|
+
marker.addEventListener('click', (event) => {
|
|
47
|
+
event.preventDefault();
|
|
48
|
+
event.stopPropagation();
|
|
49
|
+
showEvidencePanel(kind, id);
|
|
50
|
+
});
|
|
51
|
+
return marker;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function renderEvidenceMarkers() {
|
|
55
|
+
const layer = document.getElementById('evidenceLayer');
|
|
56
|
+
if (!layer) return;
|
|
57
|
+
layer.replaceChildren();
|
|
58
|
+
layer.classList.toggle('hidden', !st.evidenceMode);
|
|
59
|
+
if (!st.evidenceMode || !st.network || !st.nodes || !st.edges) return;
|
|
60
|
+
|
|
61
|
+
for (const node of st.nodes.get()) {
|
|
62
|
+
if (!evidenceItems(node).length || node.shapeType === 'anchor') continue;
|
|
63
|
+
const pos = markerPositionForNode(node);
|
|
64
|
+
if (pos) layer.appendChild(makeMarker('node', node.id, pos));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
for (const edge of st.edges.get()) {
|
|
68
|
+
if (!evidenceItems(edge).length) continue;
|
|
69
|
+
const pos = markerPositionForEdge(edge);
|
|
70
|
+
if (pos) layer.appendChild(makeMarker('edge', edge.id, pos));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function bindEvidenceModeToNetwork() {
|
|
75
|
+
if (!st.network) return;
|
|
76
|
+
st.network.on('afterDrawing', renderEvidenceMarkers);
|
|
77
|
+
renderEvidenceMarkers();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function initEvidenceMode() {
|
|
81
|
+
window.addEventListener('diagram:network-ready', bindEvidenceModeToNetwork);
|
|
82
|
+
document.getElementById('btnEvidenceClose')?.addEventListener('click', hideEvidencePanel);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function toggleEvidenceMode() {
|
|
86
|
+
st.evidenceMode = !st.evidenceMode;
|
|
87
|
+
const btn = document.getElementById('btnEvidenceMode');
|
|
88
|
+
if (btn) btn.classList.toggle('tool-active', st.evidenceMode);
|
|
89
|
+
if (!st.evidenceMode) hideEvidencePanel();
|
|
90
|
+
renderEvidenceMarkers();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function hideEvidencePanel() {
|
|
94
|
+
const panel = document.getElementById('evidencePanel');
|
|
95
|
+
if (panel) panel.classList.add('hidden');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function showEvidencePanel(kind, id) {
|
|
99
|
+
const panel = document.getElementById('evidencePanel');
|
|
100
|
+
const title = document.getElementById('evidencePanelTitle');
|
|
101
|
+
const body = document.getElementById('evidencePanelBody');
|
|
102
|
+
if (!panel || !title || !body) return;
|
|
103
|
+
|
|
104
|
+
const item = kind === 'node' ? st.nodes.get(id) : st.edges.get(id);
|
|
105
|
+
const entries = evidenceItems(item);
|
|
106
|
+
title.textContent = kind === 'node'
|
|
107
|
+
? t('diagram.evidence.node_title')
|
|
108
|
+
: t('diagram.evidence.edge_title');
|
|
109
|
+
body.replaceChildren();
|
|
110
|
+
|
|
111
|
+
if (!entries.length) {
|
|
112
|
+
const empty = document.createElement('p');
|
|
113
|
+
empty.className = 'text-sm text-gray-500 dark:text-gray-400';
|
|
114
|
+
empty.textContent = t('diagram.evidence.empty');
|
|
115
|
+
body.appendChild(empty);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
for (const entry of entries) {
|
|
119
|
+
const card = document.createElement('article');
|
|
120
|
+
card.className = 'rounded-md border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 p-3 space-y-2';
|
|
121
|
+
|
|
122
|
+
const link = document.createElement('a');
|
|
123
|
+
link.className = 'block text-sm font-semibold text-blue-600 dark:text-blue-400 hover:underline break-all';
|
|
124
|
+
link.href = documentHref(entry.documentId);
|
|
125
|
+
link.textContent = entry.documentId;
|
|
126
|
+
card.appendChild(link);
|
|
127
|
+
|
|
128
|
+
if (entry.section) {
|
|
129
|
+
const section = document.createElement('p');
|
|
130
|
+
section.className = 'text-xs font-medium text-gray-500 dark:text-gray-400';
|
|
131
|
+
section.textContent = `${t('diagram.evidence.section_label')}: ${entry.section}`;
|
|
132
|
+
card.appendChild(section);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (entry.summary) {
|
|
136
|
+
const summary = document.createElement('p');
|
|
137
|
+
summary.className = 'text-sm text-gray-700 dark:text-gray-300 leading-snug';
|
|
138
|
+
summary.textContent = entry.summary;
|
|
139
|
+
card.appendChild(summary);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
body.appendChild(card);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
panel.classList.remove('hidden');
|
|
146
|
+
}
|
|
@@ -21,6 +21,9 @@ function captureState() {
|
|
|
21
21
|
const nodes = st.nodes.get().map((n) => ({
|
|
22
22
|
id: n.id, label: n.label,
|
|
23
23
|
shapeType: n.shapeType || 'box', colorKey: n.colorKey || 'c-gray',
|
|
24
|
+
kind: n.kind || null, renderAs: n.renderAs || null,
|
|
25
|
+
description: n.description || null,
|
|
26
|
+
evidence: Array.isArray(n.evidence) ? n.evidence : null,
|
|
24
27
|
nodeWidth: n.nodeWidth || null, nodeHeight: n.nodeHeight || null,
|
|
25
28
|
fontSize: n.fontSize || null, textAlign: n.textAlign || null, textValign: n.textValign || null,
|
|
26
29
|
bgOpacity: n.bgOpacity ?? null,
|
|
@@ -37,6 +40,7 @@ function captureState() {
|
|
|
37
40
|
fromPort: e.fromPort || null, toPort: e.toPort || null,
|
|
38
41
|
edgeColor: e.edgeColor || null, edgeWidth: e.edgeWidth || null,
|
|
39
42
|
edgeLocked: e.edgeLocked || false,
|
|
43
|
+
evidence: Array.isArray(e.evidence) ? e.evidence : null,
|
|
40
44
|
}));
|
|
41
45
|
return {
|
|
42
46
|
nodes,
|
|
@@ -21,6 +21,7 @@ import { uploadImageBlob } from './image-upload.js';
|
|
|
21
21
|
import { promptImageName } from './image-name-modal.js';
|
|
22
22
|
import { showToast } from './toast.js';
|
|
23
23
|
import { t } from './t.js';
|
|
24
|
+
import { initEvidenceMode, toggleEvidenceMode } from './evidence.js';
|
|
24
25
|
|
|
25
26
|
// ── Tool management ───────────────────────────────────────────────────────────
|
|
26
27
|
|
|
@@ -30,6 +31,7 @@ function setTool(tool, shape) {
|
|
|
30
31
|
|
|
31
32
|
document.querySelectorAll('.tool-btn').forEach((b) => b.classList.remove('tool-active'));
|
|
32
33
|
if (st.alignGuides) document.getElementById('btnAlign').classList.add('tool-active');
|
|
34
|
+
if (st.evidenceMode) document.getElementById('btnEvidenceMode').classList.add('tool-active');
|
|
33
35
|
|
|
34
36
|
const key = tool === 'addNode' ? `addNode:${shape || st.pendingShape}` : tool;
|
|
35
37
|
const btn = document.getElementById(TOOL_BTN_MAP[key]);
|
|
@@ -134,6 +136,7 @@ document.getElementById('btnDelete').addEventListener('click', deleteSelected);
|
|
|
134
136
|
document.getElementById('btnAlign').addEventListener('click', toggleAlignGuides);
|
|
135
137
|
document.getElementById('btnGrid').addEventListener('click', toggleGrid);
|
|
136
138
|
document.getElementById('btnEdgeStraight').addEventListener('click', toggleEdgeStraight);
|
|
139
|
+
document.getElementById('btnEvidenceMode').addEventListener('click', toggleEvidenceMode);
|
|
137
140
|
document.getElementById('btnResizeMode').addEventListener('click', toggleResizeMode);
|
|
138
141
|
|
|
139
142
|
document.getElementById('btnZoomOut').addEventListener('click', () => adjustZoom(-0.2));
|
|
@@ -147,6 +150,7 @@ document.getElementById('btnNewDiagram').addEventListener('click', newDiagram);
|
|
|
147
150
|
|
|
148
151
|
// Dirty state on title edits
|
|
149
152
|
document.getElementById('diagramTitle').addEventListener('input', markDirty);
|
|
153
|
+
initEvidenceMode();
|
|
150
154
|
|
|
151
155
|
// ── Node panel wiring ─────────────────────────────────────────────────────────
|
|
152
156
|
|
|
@@ -52,11 +52,15 @@ export function initNetwork(savedNodes, savedEdges, edgesStraight = false) {
|
|
|
52
52
|
const edgeSmooth = edgesStraight ? { enabled: false } : { type: 'continuous' };
|
|
53
53
|
|
|
54
54
|
st.nodes = new vis.DataSet(
|
|
55
|
-
savedNodes.map((n) =>
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
savedNodes.map((n) => {
|
|
56
|
+
const shapeType = n.shapeType || n.renderAs || 'box';
|
|
57
|
+
return {
|
|
58
|
+
...n,
|
|
59
|
+
shapeType,
|
|
60
|
+
...visNodeProps(shapeType, n.colorKey || 'c-gray', n.nodeWidth, n.nodeHeight, n.fontSize, n.textAlign, n.textValign),
|
|
61
|
+
...(n.locked ? { fixed: { x: true, y: true }, draggable: false } : {}),
|
|
62
|
+
};
|
|
63
|
+
})
|
|
60
64
|
);
|
|
61
65
|
st.edges = new vis.DataSet(
|
|
62
66
|
savedEdges.map((e) => {
|
|
@@ -871,6 +875,7 @@ export function initNetwork(savedNodes, savedEdges, edgesStraight = false) {
|
|
|
871
875
|
}
|
|
872
876
|
st.network.redraw();
|
|
873
877
|
});
|
|
878
|
+
window.dispatchEvent(new CustomEvent('diagram:network-ready'));
|
|
874
879
|
}
|
|
875
880
|
|
|
876
881
|
// ── Anchor snap-to-connect ────────────────────────────────────────────────────
|
|
@@ -70,6 +70,7 @@ export async function openDiagram(id) {
|
|
|
70
70
|
st.edgesStraight = diagram.edgesStraight === true;
|
|
71
71
|
document.getElementById('btnSave').disabled = true;
|
|
72
72
|
document.getElementById('diagramTitle').value = diagram.title || '';
|
|
73
|
+
document.getElementById('evidencePanel')?.classList.add('hidden');
|
|
73
74
|
document.getElementById('btnEdgeStraight').classList.toggle('tool-active', st.edgesStraight);
|
|
74
75
|
applyGridState(diagram.gridEnabled ?? true);
|
|
75
76
|
applyAlignGuidesState(diagram.alignGuides ?? true);
|
|
@@ -125,6 +126,9 @@ export async function saveDiagram() {
|
|
|
125
126
|
.map((n) => ({
|
|
126
127
|
id: n.id, label: n.label,
|
|
127
128
|
shapeType: n.shapeType || 'box', colorKey: n.colorKey || 'c-gray',
|
|
129
|
+
kind: n.kind || null, renderAs: n.renderAs || null,
|
|
130
|
+
description: n.description || null,
|
|
131
|
+
evidence: Array.isArray(n.evidence) ? n.evidence : null,
|
|
128
132
|
nodeWidth: n.nodeWidth || null, nodeHeight: n.nodeHeight || null,
|
|
129
133
|
fontSize: n.fontSize || null, textAlign: n.textAlign || null, textValign: n.textValign || null,
|
|
130
134
|
bgOpacity: n.bgOpacity ?? null,
|
|
@@ -145,6 +149,7 @@ export async function saveDiagram() {
|
|
|
145
149
|
fromPort: e.fromPort || null, toPort: e.toPort || null,
|
|
146
150
|
edgeColor: e.edgeColor || null, edgeWidth: e.edgeWidth || null,
|
|
147
151
|
edgeLocked: e.edgeLocked || false, edgeLabelWidth: e.edgeLabelWidth || null,
|
|
152
|
+
evidence: Array.isArray(e.evidence) ? e.evidence : null,
|
|
148
153
|
}));
|
|
149
154
|
|
|
150
155
|
const title = document.getElementById('diagramTitle').value || t('diagram.toast.untitled');
|
|
@@ -123,6 +123,7 @@ export function getNearestPort(nodeId, canvasPos) {
|
|
|
123
123
|
* `highlightedPort` (if any) is rendered larger and fully orange.
|
|
124
124
|
*/
|
|
125
125
|
export function drawPortDots(ctx, nodeId, highlightedPort) {
|
|
126
|
+
if (st.exportingPng) return;
|
|
126
127
|
for (const key of PORT_KEYS) {
|
|
127
128
|
const p = getPortPosition(nodeId, key);
|
|
128
129
|
if (!p) continue;
|
|
@@ -383,4 +384,3 @@ function _distToSegment(p, a, b) {
|
|
|
383
384
|
const t = Math.max(0, Math.min(1, ((p.x - a.x) * dx + (p.y - a.y) * dy) / len2));
|
|
384
385
|
return Math.hypot(p.x - (a.x + t * dx), p.y - (a.y + t * dy));
|
|
385
386
|
}
|
|
386
|
-
|
|
@@ -31,6 +31,8 @@ export const st = {
|
|
|
31
31
|
edgeLabelCanvasPos: {}, // edgeId → {x, y} canvas coords of last drawn label, used by label editor
|
|
32
32
|
edgeLabelBBox: {}, // edgeId → {cx, cy, w, h, rotation} canvas world coords, used by label resize
|
|
33
33
|
freeArrowFirstPoint: null, // addEdge two-click flow: {x, y} canvas coords of first click, or null
|
|
34
|
+
exportingPng: false, // suppress interactive-only canvas chrome during PNG export
|
|
35
|
+
evidenceMode: false, // when true, show document-provenance markers on nodes/edges
|
|
34
36
|
};
|
|
35
37
|
|
|
36
38
|
export function markDirty() {
|
|
@@ -252,6 +252,40 @@
|
|
|
252
252
|
opacity: 1;
|
|
253
253
|
transform: translateY(0);
|
|
254
254
|
}
|
|
255
|
+
|
|
256
|
+
#evidenceLayer {
|
|
257
|
+
position: absolute;
|
|
258
|
+
inset: 0;
|
|
259
|
+
pointer-events: none;
|
|
260
|
+
z-index: 18;
|
|
261
|
+
overflow: hidden;
|
|
262
|
+
}
|
|
263
|
+
#evidenceLayer.hidden {
|
|
264
|
+
display: none;
|
|
265
|
+
}
|
|
266
|
+
.evidence-marker {
|
|
267
|
+
position: absolute;
|
|
268
|
+
width: 1.25rem;
|
|
269
|
+
height: 1.25rem;
|
|
270
|
+
transform: translate(-50%, -50%);
|
|
271
|
+
display: flex;
|
|
272
|
+
align-items: center;
|
|
273
|
+
justify-content: center;
|
|
274
|
+
border-radius: 999px;
|
|
275
|
+
border: 1px solid #0284c7;
|
|
276
|
+
background: #e0f2fe;
|
|
277
|
+
color: #0369a1;
|
|
278
|
+
font-size: 0.7rem;
|
|
279
|
+
font-weight: 700;
|
|
280
|
+
line-height: 1;
|
|
281
|
+
box-shadow: 0 2px 8px rgba(2, 132, 199, 0.28);
|
|
282
|
+
pointer-events: auto;
|
|
283
|
+
}
|
|
284
|
+
.dark .evidence-marker {
|
|
285
|
+
border-color: #38bdf8;
|
|
286
|
+
background: #082f49;
|
|
287
|
+
color: #e0f2fe;
|
|
288
|
+
}
|
|
255
289
|
</style>
|
|
256
290
|
</head>
|
|
257
291
|
<body
|
|
@@ -490,6 +524,19 @@
|
|
|
490
524
|
<polyline points="8,2 12,2 12,6" />
|
|
491
525
|
</svg>
|
|
492
526
|
</button>
|
|
527
|
+
<button
|
|
528
|
+
id="btnEvidenceMode"
|
|
529
|
+
class="tool-btn"
|
|
530
|
+
data-i18n-title="diagram.toolbar.evidence_mode"
|
|
531
|
+
>
|
|
532
|
+
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round">
|
|
533
|
+
<path d="M4 1.5 H8.5 L11 4 V12.5 H4 Z" />
|
|
534
|
+
<path d="M8.5 1.5 V4 H11" />
|
|
535
|
+
<path d="M1.5 5.5 H6.5" />
|
|
536
|
+
<path d="M1.5 8 H6.5" />
|
|
537
|
+
<path d="M1.5 10.5 H5" />
|
|
538
|
+
</svg>
|
|
539
|
+
</button>
|
|
493
540
|
<button
|
|
494
541
|
id="btnResizeMode"
|
|
495
542
|
class="tool-btn active-tool"
|
|
@@ -597,6 +644,9 @@
|
|
|
597
644
|
<!-- Debug overlay layer -->
|
|
598
645
|
<div id="debugLayer"></div>
|
|
599
646
|
|
|
647
|
+
<!-- Evidence markers layer -->
|
|
648
|
+
<div id="evidenceLayer" class="hidden"></div>
|
|
649
|
+
|
|
600
650
|
<!-- Stamp overlay: intercepts canvas clicks during stamp mode -->
|
|
601
651
|
<div id="stampOverlay" style="position:absolute;inset:0;display:none;z-index:11;cursor:crosshair;"></div>
|
|
602
652
|
|
|
@@ -633,6 +683,26 @@
|
|
|
633
683
|
</div>
|
|
634
684
|
</div>
|
|
635
685
|
|
|
686
|
+
<!-- Evidence panel -->
|
|
687
|
+
<aside
|
|
688
|
+
id="evidencePanel"
|
|
689
|
+
class="hidden absolute top-0 right-0 bottom-0 w-80 max-w-[85vw] z-30 bg-gray-50 dark:bg-gray-900 border-l border-gray-200 dark:border-gray-700 shadow-xl flex flex-col"
|
|
690
|
+
>
|
|
691
|
+
<div class="flex items-center justify-between gap-2 px-4 py-3 border-b border-gray-200 dark:border-gray-700">
|
|
692
|
+
<h2
|
|
693
|
+
id="evidencePanelTitle"
|
|
694
|
+
class="text-sm font-semibold text-gray-800 dark:text-gray-100"
|
|
695
|
+
data-i18n="diagram.evidence.panel_title"
|
|
696
|
+
>Sources</h2>
|
|
697
|
+
<button
|
|
698
|
+
id="btnEvidenceClose"
|
|
699
|
+
class="tool-btn !w-7 !h-7"
|
|
700
|
+
data-i18n-title="diagram.evidence.close"
|
|
701
|
+
>×</button>
|
|
702
|
+
</div>
|
|
703
|
+
<div id="evidencePanelBody" class="flex-1 overflow-y-auto p-3 space-y-3"></div>
|
|
704
|
+
</aside>
|
|
705
|
+
|
|
636
706
|
<!-- Node panel -->
|
|
637
707
|
<div id="nodePanel" class="float-panel hidden">
|
|
638
708
|
<button
|
|
@@ -484,6 +484,15 @@
|
|
|
484
484
|
"diagram.edge_panel.width_increase": "Increase width",
|
|
485
485
|
"diagram.edge_panel.clear_ports": "Clear attachment points",
|
|
486
486
|
|
|
487
|
+
"diagram.toolbar.evidence_mode": "Source consultation mode",
|
|
488
|
+
"diagram.evidence.panel_title": "Sources",
|
|
489
|
+
"diagram.evidence.node_title": "Node sources",
|
|
490
|
+
"diagram.evidence.edge_title": "Relation sources",
|
|
491
|
+
"diagram.evidence.marker_title": "View document sources",
|
|
492
|
+
"diagram.evidence.close": "Close sources",
|
|
493
|
+
"diagram.evidence.empty": "No source attached.",
|
|
494
|
+
"diagram.evidence.section_label": "Section",
|
|
495
|
+
|
|
487
496
|
"diagram.label_input.placeholder": "Label…",
|
|
488
497
|
"diagram.empty_state": "Select or create a diagram",
|
|
489
498
|
|
|
@@ -484,6 +484,15 @@
|
|
|
484
484
|
"diagram.edge_panel.width_increase": "Augmenter l'épaisseur",
|
|
485
485
|
"diagram.edge_panel.clear_ports": "Supprimer les points d'attache",
|
|
486
486
|
|
|
487
|
+
"diagram.toolbar.evidence_mode": "Mode consultation des sources",
|
|
488
|
+
"diagram.evidence.panel_title": "Sources",
|
|
489
|
+
"diagram.evidence.node_title": "Sources du nœud",
|
|
490
|
+
"diagram.evidence.edge_title": "Sources de la relation",
|
|
491
|
+
"diagram.evidence.marker_title": "Voir les sources documentaires",
|
|
492
|
+
"diagram.evidence.close": "Fermer les sources",
|
|
493
|
+
"diagram.evidence.empty": "Aucune source attachée.",
|
|
494
|
+
"diagram.evidence.section_label": "Section",
|
|
495
|
+
|
|
487
496
|
"diagram.label_input.placeholder": "Libellé…",
|
|
488
497
|
"diagram.empty_state": "Sélectionne ou crée un diagramme",
|
|
489
498
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/mcp/server.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/mcp/server.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AAo9CpD,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA4BlD"}
|