graphlin 0.1.2 → 0.1.3
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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/runtime/web/app.js +147 -26
- package/runtime/web/index.html +88 -80
- package/runtime/web/style.css +103 -142
- package/scripts/control.mjs +1 -1
package/package.json
CHANGED
package/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
3
|
"name": "graphlin",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.3",
|
|
5
5
|
"description": "Local architecture and activity diagrams from observable coding-agent work.",
|
|
6
6
|
"extensions": {
|
|
7
7
|
"com.openai": {
|
package/runtime/web/app.js
CHANGED
|
@@ -157,10 +157,10 @@ export function liveNodeChanges(previous, next, eligible) {
|
|
|
157
157
|
};
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
-
export function filterDiagram(graph, query) {
|
|
161
|
-
if (!query) return graph;
|
|
160
|
+
export function filterDiagram(graph, query = '', kinds = null) {
|
|
161
|
+
if (!query && kinds === null) return graph;
|
|
162
162
|
const needle = query.toLowerCase();
|
|
163
|
-
const nodes = graph.nodes.filter(node => node.label.toLowerCase().includes(needle));
|
|
163
|
+
const nodes = graph.nodes.filter(node => (kinds === null || kinds.has(node.kind)) && node.label.toLowerCase().includes(needle));
|
|
164
164
|
const visible = new Set(nodes.map(node => node.id));
|
|
165
165
|
return { ...graph, nodes, edges: graph.edges.filter(edge => visible.has(edge.source) && visible.has(edge.target)) };
|
|
166
166
|
}
|
|
@@ -842,6 +842,7 @@ export function startDashboardInfo({ load = signal => request('/api/about', { si
|
|
|
842
842
|
throw new Error('invalid_dashboard_info');
|
|
843
843
|
}
|
|
844
844
|
$('project-path').textContent = safeText(info.projectRoot, 4096) || 'Path unavailable';
|
|
845
|
+
$('project-path').title = $('project-path').textContent;
|
|
845
846
|
$('graphlin-version').textContent = info.version;
|
|
846
847
|
$('version-update-steps').textContent = info.mode === 'demo'
|
|
847
848
|
? 'Press Ctrl+C in the demo terminal, then run this command to restart the updated offline demo.'
|
|
@@ -851,9 +852,14 @@ export function startDashboardInfo({ load = signal => request('/api/about', { si
|
|
|
851
852
|
? safeText(branch.name, 1024) || 'Branch unavailable'
|
|
852
853
|
: branch?.status === 'detached' ? `Detached HEAD${branch.commit ? ` · ${safeText(branch.commit, 12)}` : ''}`
|
|
853
854
|
: branch?.status === 'not_git' ? 'Not a Git repository' : 'Branch unavailable';
|
|
855
|
+
$('project-branch').title = $('project-branch').textContent;
|
|
854
856
|
const latest = typeof info.update?.latest === 'string' &&
|
|
855
857
|
/^\d+\.\d+\.\d+$/.test(info.update.latest) ? info.update.latest : null;
|
|
856
858
|
const available = info.update?.status === 'available' && latest;
|
|
859
|
+
$('version-update-indicator').hidden = !available;
|
|
860
|
+
$('version-update-indicator').textContent = available ? `${latest} available` : 'Update available';
|
|
861
|
+
$('version-update-indicator').setAttribute('aria-label', available
|
|
862
|
+
? `Graphlin ${latest} is available. View update instructions.` : 'View update instructions');
|
|
857
863
|
$('version-update-status').textContent = available ? `Graphlin ${latest} is available`
|
|
858
864
|
: info.update?.status === 'current' ? 'No newer release found'
|
|
859
865
|
: 'Update check unavailable';
|
|
@@ -884,6 +890,7 @@ export function startDashboardInfo({ load = signal => request('/api/about', { si
|
|
|
884
890
|
if ($('project-path').textContent === 'Checking…') $('project-path').textContent = 'Path unavailable';
|
|
885
891
|
if ($('graphlin-version').textContent === 'Checking…') $('graphlin-version').textContent = 'Version unavailable';
|
|
886
892
|
$('version-update-status').textContent = 'Update check unavailable';
|
|
893
|
+
$('version-update-indicator').hidden = true;
|
|
887
894
|
$('version-update-guide').hidden = true;
|
|
888
895
|
$('version-update-command').textContent = '';
|
|
889
896
|
command = '';
|
|
@@ -1556,6 +1563,7 @@ export function startViewer() {
|
|
|
1556
1563
|
viewport: null, fitBounds: null, zoom: 1, followFit: true, lastGraphSignature: '', inspectorSignature: '',
|
|
1557
1564
|
nodeElements: new Map(), edgeElements: new Map(), activityElements: new Map(),
|
|
1558
1565
|
views: new Map(), viewKey: null, view: null, displayGraph: null, searchQuery: '',
|
|
1566
|
+
nodeTypes: null, nodeTypeButtons: new Map(),
|
|
1559
1567
|
effects: new Map(), liveReady: false, motionReady: false, movement: null, closed: false, projectName: '',
|
|
1560
1568
|
};
|
|
1561
1569
|
const motionPreference = window.matchMedia?.('(prefers-reduced-motion: reduce)');
|
|
@@ -1630,7 +1638,7 @@ export function startViewer() {
|
|
|
1630
1638
|
$('onboarding-action').dataset.action = progress.next.action;
|
|
1631
1639
|
// Preserve a manual text selection while live snapshots arrive.
|
|
1632
1640
|
if ($('orientation-prompt').textContent !== ORIENTATION_PROMPT) $('orientation-prompt').textContent = ORIENTATION_PROMPT;
|
|
1633
|
-
$('orientation').hidden = Boolean(state.searchQuery || state.replayFrame || state.snapshot?.mode === 'demo' || state.snapshot?.mode === 'replay');
|
|
1641
|
+
$('orientation').hidden = Boolean(state.searchQuery || state.nodeTypes !== null || state.replayFrame || state.snapshot?.mode === 'demo' || state.snapshot?.mode === 'replay');
|
|
1634
1642
|
}
|
|
1635
1643
|
function currentGraph() { return state.replayFrame?.graph || state.snapshot?.graph; }
|
|
1636
1644
|
function applyTheme() {
|
|
@@ -1789,10 +1797,14 @@ export function startViewer() {
|
|
|
1789
1797
|
// initial/session snapshots establish it without focusing an old arrival.
|
|
1790
1798
|
const live = streamed && state.liveReady && !switched && !state.replayFrame &&
|
|
1791
1799
|
snapshot.mode !== 'replay' && state.snapshot?.mode !== 'replay';
|
|
1792
|
-
const
|
|
1800
|
+
const visible = new Set(filterDiagram(snapshot.graph, state.searchQuery, state.nodeTypes).nodes.map(node => node.id));
|
|
1801
|
+
const focusNodeId = liveNodeChanges(state.snapshot?.graph, snapshot.graph, live).added.filter(id => visible.has(id)).at(-1);
|
|
1793
1802
|
const before = state.displayGraph;
|
|
1794
1803
|
cancelMovement({ fit: !focusNodeId });
|
|
1795
|
-
if (switched)
|
|
1804
|
+
if (switched) {
|
|
1805
|
+
resetView();
|
|
1806
|
+
state.nodeTypes = null;
|
|
1807
|
+
}
|
|
1796
1808
|
state.snapshot = snapshot;
|
|
1797
1809
|
state.epoch += 1;
|
|
1798
1810
|
state.frames = historyFrames(snapshot);
|
|
@@ -1926,14 +1938,18 @@ export function startViewer() {
|
|
|
1926
1938
|
}
|
|
1927
1939
|
}
|
|
1928
1940
|
function revealInspector() {
|
|
1941
|
+
revealDetailsSection($('inspector-body')?.parentElement);
|
|
1942
|
+
}
|
|
1943
|
+
function revealDetailsSection(panel) {
|
|
1944
|
+
setWorkspacePanel('details', true);
|
|
1929
1945
|
const container = $('live-sidebar');
|
|
1930
|
-
const panel = $('inspector-body')?.parentElement;
|
|
1931
1946
|
if (!container?.scrollTo || !container.getBoundingClientRect || !panel?.getBoundingClientRect ||
|
|
1932
1947
|
!container.contains(panel)) return;
|
|
1933
1948
|
const region = container.getBoundingClientRect();
|
|
1934
1949
|
const evidence = panel.getBoundingClientRect();
|
|
1935
1950
|
if (!Number.isFinite(region.top) || !Number.isFinite(evidence.top) || !(region.height > 0)) return;
|
|
1936
|
-
const
|
|
1951
|
+
const headingHeight = $('details-heading')?.getBoundingClientRect?.().height || 0;
|
|
1952
|
+
const offset = evidence.top - region.top - (container.clientTop || 0) - headingHeight;
|
|
1937
1953
|
if (Math.abs(offset) < 1) return;
|
|
1938
1954
|
container.scrollTo({
|
|
1939
1955
|
top: Math.max(0, (container.scrollTop || 0) + offset),
|
|
@@ -2016,14 +2032,40 @@ export function startViewer() {
|
|
|
2016
2032
|
state.followFit = false;
|
|
2017
2033
|
setViewBox();
|
|
2018
2034
|
}
|
|
2019
|
-
function
|
|
2035
|
+
function renderNodeTypeFilters(canonical) {
|
|
2036
|
+
const container = $('node-type-filters');
|
|
2037
|
+
if (!container) return;
|
|
2038
|
+
// Discover kinds from the current canonical canvas, never search results.
|
|
2039
|
+
const kinds = [...new Set(canonical.nodes.map(node => node.kind))].sort();
|
|
2040
|
+
for (const [kind, button] of state.nodeTypeButtons) {
|
|
2041
|
+
if (!kinds.includes(kind)) {
|
|
2042
|
+
button.remove();
|
|
2043
|
+
state.nodeTypeButtons.delete(kind);
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
kinds.forEach((kind, index) => {
|
|
2047
|
+
let button = state.nodeTypeButtons.get(kind);
|
|
2048
|
+
if (!button) {
|
|
2049
|
+
button = html('button', upperFirst(kind), 'node-type-filter');
|
|
2050
|
+
button.setAttribute('type', 'button');
|
|
2051
|
+
button.dataset.kind = kind;
|
|
2052
|
+
state.nodeTypeButtons.set(kind, button);
|
|
2053
|
+
}
|
|
2054
|
+
button.setAttribute('aria-pressed', String(state.nodeTypes === null || state.nodeTypes.has(kind)));
|
|
2055
|
+
if (container.children[index] !== button) container.insertBefore(button, container.children[index] || null);
|
|
2056
|
+
});
|
|
2057
|
+
$('node-types-all')?.setAttribute('aria-pressed', String(state.nodeTypes === null));
|
|
2058
|
+
$('node-types-none')?.setAttribute('aria-pressed', String(state.nodeTypes?.size === 0));
|
|
2059
|
+
}
|
|
2060
|
+
function renderGraph({ forceFit = false, arrange = false, focusNodeId } = {}) {
|
|
2020
2061
|
const canonical = currentGraph();
|
|
2021
2062
|
if (!canonical) return;
|
|
2022
2063
|
const view = presentation();
|
|
2023
2064
|
applyTheme();
|
|
2024
|
-
|
|
2025
|
-
//
|
|
2026
|
-
|
|
2065
|
+
renderNodeTypeFilters(canonical);
|
|
2066
|
+
// Lay out only visible nodes so hidden components leave no empty slots.
|
|
2067
|
+
// Evidence, exports and live arrival detection retain the canonical graph.
|
|
2068
|
+
const graph = projectPresentation(filterDiagram(canonical, state.searchQuery, state.nodeTypes), view, { arrange });
|
|
2027
2069
|
state.displayGraph = graph;
|
|
2028
2070
|
const routes = graphEdgeRoutes(graph);
|
|
2029
2071
|
const bounds = graphBounds(graph, routes);
|
|
@@ -2144,7 +2186,7 @@ export function startViewer() {
|
|
|
2144
2186
|
$('diagram-title').textContent = `${state.replayFrame ? 'Historical' : 'Live'} architecture, revision ${graph.revision}`;
|
|
2145
2187
|
$('diagram-desc').textContent = `${graph.nodes.length} components and ${graph.edges.length} relationships. Code interpretation does not establish runtime connectivity. Use Tab and Enter to inspect a component or relationship. With the diagram focused, use plus and minus to zoom, arrow keys to pan, and 0 to fit.`;
|
|
2146
2188
|
$('graph-count').textContent = `${graph.nodes.length} components · ${graph.edges.length} relationships`;
|
|
2147
|
-
$('diagram-search-status').textContent = state.searchQuery
|
|
2189
|
+
$('diagram-search-status').textContent = state.searchQuery || state.nodeTypes !== null
|
|
2148
2190
|
? `${graph.nodes.length} of ${canonical.nodes.length} components shown` : '';
|
|
2149
2191
|
$('diagram-search-clear').hidden = !state.searchQuery;
|
|
2150
2192
|
$('empty-canvas').hidden = graph.nodes.length > 0 || removalBounds().length > 0;
|
|
@@ -2156,8 +2198,12 @@ export function startViewer() {
|
|
|
2156
2198
|
unavailable: ['Waiting for classification.', 'The classifier is unavailable. Safe activity continues below; supported architecture will appear when classification recovers.'],
|
|
2157
2199
|
timeout: ['Evidence needs another moment.', 'Classification exceeded its deadline. Activity still appears below, and no unsupported components are added.'],
|
|
2158
2200
|
};
|
|
2159
|
-
const message = state.
|
|
2160
|
-
? ['No
|
|
2201
|
+
const message = state.nodeTypes?.size === 0
|
|
2202
|
+
? ['No component types selected.', 'Choose a type or All types to show components.']
|
|
2203
|
+
: state.searchQuery
|
|
2204
|
+
? ['No matching components.', `No selected components match “${state.searchQuery}”. Try another search or press Esc to clear the search.`]
|
|
2205
|
+
: state.nodeTypes !== null && canonical.nodes.length
|
|
2206
|
+
? ['No matching components.', 'Choose another type or All types to show components.']
|
|
2161
2207
|
: state.replayFrame
|
|
2162
2208
|
? ['No components in this revision.', 'Move through the recent revisions or return to Live to follow the current map.']
|
|
2163
2209
|
: emptyMessages[classifier] || ['Your architecture starts here.', 'Work in a connected agent session. Components appear when approved evidence supports them; activity can arrive first.'];
|
|
@@ -2227,8 +2273,7 @@ export function startViewer() {
|
|
|
2227
2273
|
finishPan();
|
|
2228
2274
|
cancelMovement();
|
|
2229
2275
|
const before = state.displayGraph;
|
|
2230
|
-
|
|
2231
|
-
renderGraph({ forceFit: true });
|
|
2276
|
+
renderGraph({ forceFit: true, arrange: true });
|
|
2232
2277
|
moveLayout(before, state.displayGraph);
|
|
2233
2278
|
announce(`Arranged using ${LAYOUT_NAMES[state.view.algorithm]}. Evidence and selection are unchanged.`);
|
|
2234
2279
|
}
|
|
@@ -2371,6 +2416,7 @@ export function startViewer() {
|
|
|
2371
2416
|
button.addEventListener('click', () => {
|
|
2372
2417
|
const row = state.activityElements.get(event.id);
|
|
2373
2418
|
if (row) {
|
|
2419
|
+
setWorkspacePanel('activity', true);
|
|
2374
2420
|
row.querySelector('button').focus();
|
|
2375
2421
|
row.scrollIntoView({ block: 'nearest' });
|
|
2376
2422
|
}
|
|
@@ -2433,6 +2479,7 @@ export function startViewer() {
|
|
|
2433
2479
|
updateSelection();
|
|
2434
2480
|
renderInspector();
|
|
2435
2481
|
renderActivity();
|
|
2482
|
+
revealInspector();
|
|
2436
2483
|
announce(`Related evidence for ${(node || edge).label} is shown in the inspector.`);
|
|
2437
2484
|
} else toast(state.replayFrame ? 'No evidence link for this event in the displayed revision. Return to Live to inspect current links.' : 'This captured event has no architecture evidence link. Tool activity alone does not establish an architectural claim.');
|
|
2438
2485
|
});
|
|
@@ -2463,6 +2510,7 @@ export function startViewer() {
|
|
|
2463
2510
|
function replayAt(index) {
|
|
2464
2511
|
const frame = state.frames[index];
|
|
2465
2512
|
if (!frame) return;
|
|
2513
|
+
setWorkspacePanel('history', true);
|
|
2466
2514
|
resetMotionBaseline();
|
|
2467
2515
|
state.replayFrame = frame;
|
|
2468
2516
|
render();
|
|
@@ -2534,6 +2582,7 @@ export function startViewer() {
|
|
|
2534
2582
|
if (!state.closed && attempt === state.connectEpoch) {
|
|
2535
2583
|
state.projectName = friendlyProjectName(info.projectRoot);
|
|
2536
2584
|
$('project-path').textContent = info.projectRoot;
|
|
2585
|
+
$('project-path').title = info.projectRoot;
|
|
2537
2586
|
renderStatus();
|
|
2538
2587
|
}
|
|
2539
2588
|
} catch { /* The project ID remains a usable fallback. */ }
|
|
@@ -2588,24 +2637,85 @@ export function startViewer() {
|
|
|
2588
2637
|
$('orientation-prompt').focus();
|
|
2589
2638
|
}
|
|
2590
2639
|
};
|
|
2591
|
-
const
|
|
2592
|
-
|
|
2593
|
-
$('
|
|
2594
|
-
$('activity-
|
|
2595
|
-
|
|
2640
|
+
const workspacePanels = {
|
|
2641
|
+
details: { panel: $('live-sidebar'), toggle: $('details-toggle') },
|
|
2642
|
+
history: { panel: $('history-panel'), toggle: $('history-toggle') },
|
|
2643
|
+
activity: { panel: $('activity-panel'), toggle: $('activity-toggle') },
|
|
2644
|
+
};
|
|
2645
|
+
function setWorkspacePanel(name, open) {
|
|
2646
|
+
const { panel, toggle } = workspacePanels[name];
|
|
2647
|
+
if (!open && panel.contains(document.activeElement)) toggle.focus();
|
|
2648
|
+
panel.hidden = !open;
|
|
2649
|
+
toggle.setAttribute('aria-expanded', String(open));
|
|
2650
|
+
if (name === 'details') $('workspace-body').dataset.detailsOpen = String(open);
|
|
2651
|
+
if (name === 'activity') $('activity-content').hidden = !open;
|
|
2652
|
+
}
|
|
2653
|
+
const onToggleDetails = () => setWorkspacePanel('details', $('live-sidebar').hidden);
|
|
2654
|
+
const onToggleHistory = () => setWorkspacePanel('history', $('history-panel').hidden);
|
|
2655
|
+
const onToggleActivity = () => setWorkspacePanel('activity', $('activity-panel').hidden);
|
|
2656
|
+
const onCloseDetails = () => {
|
|
2657
|
+
setWorkspacePanel('details', false);
|
|
2658
|
+
$('details-toggle').focus();
|
|
2659
|
+
};
|
|
2660
|
+
const onViewUpdate = () => {
|
|
2661
|
+
$('version-update-guide').open = !$('version-update-guide').hidden;
|
|
2662
|
+
revealDetailsSection($('version-update-status'));
|
|
2596
2663
|
};
|
|
2664
|
+
const panelKeyHandlers = new Map();
|
|
2665
|
+
for (const [name, { panel, toggle }] of Object.entries(workspacePanels)) {
|
|
2666
|
+
setWorkspacePanel(name, false);
|
|
2667
|
+
const onKeyDown = event => {
|
|
2668
|
+
if (event.key !== 'Escape' || event.defaultPrevented || event.target?.tagName?.toLowerCase() === 'select') return;
|
|
2669
|
+
event.preventDefault();
|
|
2670
|
+
setWorkspacePanel(name, false);
|
|
2671
|
+
toggle.focus();
|
|
2672
|
+
};
|
|
2673
|
+
panel.addEventListener('keydown', onKeyDown);
|
|
2674
|
+
panelKeyHandlers.set(panel, onKeyDown);
|
|
2675
|
+
}
|
|
2597
2676
|
$('onboarding-action').addEventListener('click', onOnboardingAction);
|
|
2598
2677
|
$('orientation-copy').addEventListener('click', onCopyOrientation);
|
|
2678
|
+
$('details-toggle').addEventListener('click', onToggleDetails);
|
|
2679
|
+
$('details-close').addEventListener('click', onCloseDetails);
|
|
2680
|
+
$('version-update-indicator').addEventListener('click', onViewUpdate);
|
|
2681
|
+
$('history-toggle').addEventListener('click', onToggleHistory);
|
|
2599
2682
|
$('activity-toggle').addEventListener('click', onToggleActivity);
|
|
2600
|
-
const
|
|
2601
|
-
if (state.closed || state.searchQuery === $('diagram-search').value) return;
|
|
2602
|
-
state.searchQuery = $('diagram-search').value;
|
|
2683
|
+
const applyFilters = () => {
|
|
2603
2684
|
// Filtering is not a source change: cancel existing decoration/movement
|
|
2604
2685
|
// and render directly, without changing the snapshot arrival baseline.
|
|
2605
2686
|
finishPan();
|
|
2606
2687
|
clearMotion();
|
|
2607
2688
|
renderOnboarding();
|
|
2608
|
-
renderGraph({ forceFit: true });
|
|
2689
|
+
renderGraph({ forceFit: true, arrange: true });
|
|
2690
|
+
};
|
|
2691
|
+
const onSearchInput = () => {
|
|
2692
|
+
if (state.closed || state.searchQuery === $('diagram-search').value) return;
|
|
2693
|
+
state.searchQuery = $('diagram-search').value;
|
|
2694
|
+
applyFilters();
|
|
2695
|
+
};
|
|
2696
|
+
const onNodeTypeClick = event => {
|
|
2697
|
+
if (state.closed || !currentGraph()) return;
|
|
2698
|
+
const container = $('node-type-filters');
|
|
2699
|
+
let button = event.target;
|
|
2700
|
+
while (button && button.parentElement !== container) button = button.parentElement;
|
|
2701
|
+
const kind = button?.dataset.kind;
|
|
2702
|
+
if (!kind || !state.nodeTypeButtons.has(kind)) return;
|
|
2703
|
+
// null means all, including future kinds. Explicit selections retain their
|
|
2704
|
+
// choices when a kind disappears and returns; new kinds stay unselected.
|
|
2705
|
+
if (state.nodeTypes === null) state.nodeTypes = new Set(currentGraph().nodes.map(node => node.kind));
|
|
2706
|
+
if (state.nodeTypes.has(kind)) state.nodeTypes.delete(kind);
|
|
2707
|
+
else state.nodeTypes.add(kind);
|
|
2708
|
+
applyFilters();
|
|
2709
|
+
};
|
|
2710
|
+
const onAllNodeTypes = () => {
|
|
2711
|
+
if (state.closed) return;
|
|
2712
|
+
state.nodeTypes = null;
|
|
2713
|
+
applyFilters();
|
|
2714
|
+
};
|
|
2715
|
+
const onNoNodeTypes = () => {
|
|
2716
|
+
if (state.closed) return;
|
|
2717
|
+
state.nodeTypes = new Set();
|
|
2718
|
+
applyFilters();
|
|
2609
2719
|
};
|
|
2610
2720
|
const clearSearch = () => {
|
|
2611
2721
|
$('diagram-search').value = '';
|
|
@@ -2627,6 +2737,9 @@ export function startViewer() {
|
|
|
2627
2737
|
};
|
|
2628
2738
|
$('diagram-search').addEventListener('input', onSearchInput);
|
|
2629
2739
|
$('diagram-search-clear').addEventListener('click', clearSearch);
|
|
2740
|
+
$('node-type-filters')?.addEventListener('click', onNodeTypeClick);
|
|
2741
|
+
$('node-types-all')?.addEventListener('click', onAllNodeTypes);
|
|
2742
|
+
$('node-types-none')?.addEventListener('click', onNoNodeTypes);
|
|
2630
2743
|
window.addEventListener('keydown', onSearchKeyDown);
|
|
2631
2744
|
$('pause').addEventListener('click', () => control(state.snapshot?.paused ? 'resume' : 'pause'));
|
|
2632
2745
|
$('session').addEventListener('change', () => control('session', $('session').value));
|
|
@@ -2771,9 +2884,17 @@ export function startViewer() {
|
|
|
2771
2884
|
dashboardInfo.close();
|
|
2772
2885
|
$('onboarding-action').removeEventListener('click', onOnboardingAction);
|
|
2773
2886
|
$('orientation-copy').removeEventListener('click', onCopyOrientation);
|
|
2887
|
+
$('details-toggle').removeEventListener('click', onToggleDetails);
|
|
2888
|
+
$('details-close').removeEventListener('click', onCloseDetails);
|
|
2889
|
+
$('version-update-indicator').removeEventListener('click', onViewUpdate);
|
|
2890
|
+
$('history-toggle').removeEventListener('click', onToggleHistory);
|
|
2774
2891
|
$('activity-toggle').removeEventListener('click', onToggleActivity);
|
|
2892
|
+
for (const [panel, handler] of panelKeyHandlers) panel.removeEventListener('keydown', handler);
|
|
2775
2893
|
$('diagram-search').removeEventListener('input', onSearchInput);
|
|
2776
2894
|
$('diagram-search-clear').removeEventListener('click', clearSearch);
|
|
2895
|
+
$('node-type-filters')?.removeEventListener('click', onNodeTypeClick);
|
|
2896
|
+
$('node-types-all')?.removeEventListener('click', onAllNodeTypes);
|
|
2897
|
+
$('node-types-none')?.removeEventListener('click', onNoNodeTypes);
|
|
2777
2898
|
window.removeEventListener?.('keydown', onSearchKeyDown);
|
|
2778
2899
|
$('architecture').removeEventListener('wheel', onDiagramWheel);
|
|
2779
2900
|
connectionDialog.dispose();
|
package/runtime/web/index.html
CHANGED
|
@@ -16,6 +16,15 @@
|
|
|
16
16
|
<div class="page">
|
|
17
17
|
<header class="masthead">
|
|
18
18
|
<a class="brand" href="/" aria-label="Graphlin live viewer"><span class="brand-mark" aria-hidden="true"></span>graphlin</a>
|
|
19
|
+
<section class="project-details" aria-label="Project and Graphlin version">
|
|
20
|
+
<p id="project-label" class="project-label">Local project</p>
|
|
21
|
+
<dl>
|
|
22
|
+
<div><dt>Branch</dt><dd id="project-branch">Checking…</dd></div>
|
|
23
|
+
<div class="project-path"><dt>Path</dt><dd id="project-path">Checking…</dd></div>
|
|
24
|
+
<div><dt>Graphlin</dt><dd id="graphlin-version">Checking…</dd></div>
|
|
25
|
+
</dl>
|
|
26
|
+
</section>
|
|
27
|
+
<button class="update-indicator" id="version-update-indicator" type="button" aria-controls="version-update-guide" hidden>Update available</button>
|
|
19
28
|
<div class="connection" id="connection" data-state="connecting">
|
|
20
29
|
<span class="connection-dot" aria-hidden="true"></span>
|
|
21
30
|
<span id="connection-label" role="status">Connecting to local service</span>
|
|
@@ -24,29 +33,7 @@
|
|
|
24
33
|
</header>
|
|
25
34
|
|
|
26
35
|
<main id="main">
|
|
27
|
-
<
|
|
28
|
-
<div>
|
|
29
|
-
<h1>Architecture, as it happens.</h1>
|
|
30
|
-
<p>Follow the work. Inspect the evidence behind each connection.</p>
|
|
31
|
-
</div>
|
|
32
|
-
<p id="project-label" class="project-label">Local project</p>
|
|
33
|
-
</div>
|
|
34
|
-
|
|
35
|
-
<section class="project-details" aria-label="Project and Graphlin version">
|
|
36
|
-
<dl>
|
|
37
|
-
<div><dt>Branch</dt><dd id="project-branch">Checking…</dd></div>
|
|
38
|
-
<div class="project-path"><dt>Path</dt><dd id="project-path">Checking…</dd></div>
|
|
39
|
-
<div><dt>Graphlin</dt><dd id="graphlin-version">Checking…</dd></div>
|
|
40
|
-
</dl>
|
|
41
|
-
<p id="version-update-status" role="status">Checking for updates…</p>
|
|
42
|
-
<details id="version-update-guide" hidden>
|
|
43
|
-
<summary>How to update</summary>
|
|
44
|
-
<p id="version-update-steps">Press Ctrl+C in the viewer terminal, then run this command. Start a new Claude Code or Codex session after setup finishes.</p>
|
|
45
|
-
<pre id="version-update-command" tabindex="0"></pre>
|
|
46
|
-
<button id="version-update-copy" type="button">Copy update command</button>
|
|
47
|
-
<span id="version-update-copy-status" role="status"></span>
|
|
48
|
-
</details>
|
|
49
|
-
</section>
|
|
36
|
+
<h1 class="screen-reader">Live architecture workspace</h1>
|
|
50
37
|
|
|
51
38
|
<div id="demo-banner" class="notice demo-notice" role="status" hidden>
|
|
52
39
|
<strong>Demo session</strong>
|
|
@@ -61,30 +48,57 @@
|
|
|
61
48
|
<span>Session</span>
|
|
62
49
|
<select id="session" disabled><option value="">Waiting for a session</option></select>
|
|
63
50
|
</label>
|
|
51
|
+
<div class="panel-controls" role="group" aria-label="Workspace panels">
|
|
52
|
+
<button type="button" id="details-toggle" aria-expanded="false" aria-controls="live-sidebar">Details</button>
|
|
53
|
+
<button type="button" id="history-toggle" aria-expanded="false" aria-controls="history-panel">History</button>
|
|
54
|
+
<button type="button" id="activity-toggle" aria-expanded="false" aria-controls="activity-panel">Activity</button>
|
|
55
|
+
</div>
|
|
64
56
|
<div class="session-actions">
|
|
65
57
|
<button type="button" id="classification-log" aria-haspopup="dialog" aria-controls="diagnostics-dialog">Classification log</button>
|
|
66
|
-
<button type="button" id="how-to-connect" aria-haspopup="dialog" aria-controls="connection-dialog">
|
|
58
|
+
<button type="button" id="how-to-connect" aria-haspopup="dialog" aria-controls="connection-dialog">Connect</button>
|
|
67
59
|
<button type="button" id="pause" aria-describedby="capture-note" disabled>Pause classification</button>
|
|
68
60
|
<button type="button" id="export" disabled>Export JSON</button>
|
|
69
61
|
</div>
|
|
70
62
|
</div>
|
|
71
63
|
|
|
72
|
-
<div class="
|
|
73
|
-
<div class="classification-status">
|
|
74
|
-
<span class="status-dot" id="classifier-dot" data-state="waiting" aria-hidden="true"></span>
|
|
75
|
-
<strong id="classifier-label">Waiting for local service</strong>
|
|
76
|
-
<span id="capture-note">Capture status will appear with the first snapshot.</span>
|
|
77
|
-
</div>
|
|
78
|
-
<div class="coverage-status"><span id="coverage-label">Coverage not reported</span><span id="queue-label">No snapshot yet</span></div>
|
|
79
|
-
</div>
|
|
80
|
-
|
|
81
|
-
<div class="workspace-body">
|
|
64
|
+
<div class="workspace-body" id="workspace-body" data-details-open="false">
|
|
82
65
|
<section class="drawing" id="drawing" data-theme="sketchbook" aria-labelledby="canvas-title">
|
|
83
66
|
<div class="drawing-toolbar">
|
|
84
67
|
<div class="drawing-heading">
|
|
85
68
|
<h2 id="canvas-title">Live architecture</h2>
|
|
86
69
|
<span id="revision">Revision —</span>
|
|
87
70
|
</div>
|
|
71
|
+
<div class="layout-toolbar" role="group" aria-label="Diagram arrangement">
|
|
72
|
+
<label for="layout">Layout
|
|
73
|
+
<select id="layout" aria-describedby="layout-note" disabled>
|
|
74
|
+
<option value="hierarchy">Hierarchy top-down</option>
|
|
75
|
+
<option value="dependency">Dependency left-right</option>
|
|
76
|
+
<option value="grouped">Group by type</option>
|
|
77
|
+
<option value="circular">Circular</option>
|
|
78
|
+
<option value="grid">Grid</option>
|
|
79
|
+
<option value="original">Original</option>
|
|
80
|
+
</select>
|
|
81
|
+
</label>
|
|
82
|
+
<button type="button" id="arrange" disabled>Arrange</button>
|
|
83
|
+
<label class="auto-arrange" for="auto-arrange"><input id="auto-arrange" type="checkbox" checked disabled> Auto-arrange</label>
|
|
84
|
+
<div class="theme-picker">
|
|
85
|
+
<label for="theme">Theme
|
|
86
|
+
<select id="theme" aria-describedby="theme-note" disabled>
|
|
87
|
+
<option value="sketchbook">Sketchbook</option>
|
|
88
|
+
<option value="ocean">Ocean</option>
|
|
89
|
+
<option value="forest">Forest</option>
|
|
90
|
+
<option value="sunset">Sunset</option>
|
|
91
|
+
<option value="berry">Berry</option>
|
|
92
|
+
<option value="sepia">Sepia</option>
|
|
93
|
+
<option value="blueprint">Blueprint dark</option>
|
|
94
|
+
<option value="midnight">Midnight dark</option>
|
|
95
|
+
</select>
|
|
96
|
+
</label>
|
|
97
|
+
<span class="theme-preview" aria-hidden="true"><i></i><i></i><i></i><i></i></span>
|
|
98
|
+
</div>
|
|
99
|
+
<span class="screen-reader" id="theme-note">Colors follow component types. Themes change appearance only.</span>
|
|
100
|
+
<span class="screen-reader" id="layout-note">Arranges when components or connections change.</span>
|
|
101
|
+
</div>
|
|
88
102
|
<div class="zoom-controls" role="group" aria-label="Diagram zoom">
|
|
89
103
|
<button type="button" id="zoom-out" aria-label="Zoom out" disabled>−</button>
|
|
90
104
|
<output id="zoom-level" aria-label="Zoom level">100%</output>
|
|
@@ -92,43 +106,19 @@
|
|
|
92
106
|
<button type="button" id="fit" disabled>Fit</button>
|
|
93
107
|
</div>
|
|
94
108
|
</div>
|
|
95
|
-
<div class="
|
|
96
|
-
<
|
|
97
|
-
<
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
</
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
<label class="auto-arrange" for="auto-arrange"><input id="auto-arrange" type="checkbox" checked disabled> Auto-arrange</label>
|
|
108
|
-
<div class="theme-picker">
|
|
109
|
-
<label for="theme">Theme
|
|
110
|
-
<select id="theme" aria-describedby="theme-note" disabled>
|
|
111
|
-
<option value="sketchbook">Sketchbook</option>
|
|
112
|
-
<option value="ocean">Ocean</option>
|
|
113
|
-
<option value="forest">Forest</option>
|
|
114
|
-
<option value="sunset">Sunset</option>
|
|
115
|
-
<option value="berry">Berry</option>
|
|
116
|
-
<option value="sepia">Sepia</option>
|
|
117
|
-
<option value="blueprint">Blueprint dark</option>
|
|
118
|
-
<option value="midnight">Midnight dark</option>
|
|
119
|
-
</select>
|
|
120
|
-
</label>
|
|
121
|
-
<span class="theme-preview" aria-hidden="true"><i></i><i></i><i></i><i></i></span>
|
|
109
|
+
<div class="filter-toolbar">
|
|
110
|
+
<div class="diagram-search-bar" role="search" aria-label="Diagram search">
|
|
111
|
+
<label class="screen-reader" for="diagram-search">Find components</label>
|
|
112
|
+
<input id="diagram-search" type="search" maxlength="200" autocomplete="off" spellcheck="false" placeholder="Find components…" aria-controls="architecture" aria-describedby="diagram-search-hint" aria-keyshortcuts="/">
|
|
113
|
+
<button id="diagram-search-clear" type="button" hidden>Clear search</button>
|
|
114
|
+
<span class="screen-reader" id="diagram-search-hint">/ to search · Esc to clear</span>
|
|
115
|
+
<span id="diagram-search-status" role="status" aria-live="polite" aria-atomic="true"></span>
|
|
116
|
+
</div>
|
|
117
|
+
<div class="node-type-toolbar" role="group" aria-label="Component type filters">
|
|
118
|
+
<button type="button" id="node-types-all">All types</button>
|
|
119
|
+
<button type="button" id="node-types-none">Clear all</button>
|
|
120
|
+
<div id="node-type-filters" role="group" aria-label="Visible component types"></div>
|
|
122
121
|
</div>
|
|
123
|
-
<span class="screen-reader" id="theme-note">Colors follow component types. Themes change appearance only.</span>
|
|
124
|
-
<span id="layout-note">Arranges when components or connections change.</span>
|
|
125
|
-
</div>
|
|
126
|
-
<div class="diagram-search-bar" role="search" aria-label="Diagram search">
|
|
127
|
-
<label for="diagram-search">Find components</label>
|
|
128
|
-
<input id="diagram-search" type="search" maxlength="200" autocomplete="off" spellcheck="false" placeholder="Search labels…" aria-controls="architecture" aria-describedby="diagram-search-hint" aria-keyshortcuts="/">
|
|
129
|
-
<button id="diagram-search-clear" type="button" hidden>Clear search</button>
|
|
130
|
-
<span id="diagram-search-hint">/ to search · Esc to clear</span>
|
|
131
|
-
<span id="diagram-search-status" role="status" aria-live="polite" aria-atomic="true"></span>
|
|
132
122
|
</div>
|
|
133
123
|
<div class="diagram-stage" id="diagram-stage">
|
|
134
124
|
<svg id="architecture" viewBox="0 0 920 510" role="group" tabindex="0" aria-labelledby="diagram-title diagram-desc">
|
|
@@ -164,15 +154,29 @@
|
|
|
164
154
|
</div>
|
|
165
155
|
<span id="graph-count">No components yet</span>
|
|
166
156
|
</div>
|
|
167
|
-
<details class="shape-key">
|
|
168
|
-
<summary>Shape key</summary>
|
|
169
|
-
<div id="shape-key-items"></div>
|
|
170
|
-
<p>Shapes describe component types. Display overrides change appearance only.</p>
|
|
171
|
-
</details>
|
|
172
157
|
<p class="diagram-note" id="diagram-note">Code classification does not establish runtime connectivity.</p>
|
|
173
158
|
</section>
|
|
174
159
|
|
|
175
|
-
<aside class="live-sidebar" id="live-sidebar" aria-label="Live activity and evidence">
|
|
160
|
+
<aside class="live-sidebar" id="live-sidebar" aria-label="Live activity and evidence" hidden>
|
|
161
|
+
<div class="details-heading" id="details-heading"><h2>Details</h2><button type="button" id="details-close" aria-label="Close details">Close</button></div>
|
|
162
|
+
<div class="status-strip" aria-label="Capture and classification status">
|
|
163
|
+
<div class="classification-status">
|
|
164
|
+
<span class="status-dot" id="classifier-dot" data-state="waiting" aria-hidden="true"></span>
|
|
165
|
+
<strong id="classifier-label">Waiting for local service</strong>
|
|
166
|
+
<span id="capture-note">Capture status will appear with the first snapshot.</span>
|
|
167
|
+
</div>
|
|
168
|
+
<div class="coverage-status"><span id="coverage-label">Coverage not reported</span><span id="queue-label">No snapshot yet</span></div>
|
|
169
|
+
</div>
|
|
170
|
+
<section class="version-details" aria-label="Graphlin updates">
|
|
171
|
+
<p id="version-update-status" role="status">Checking for updates…</p>
|
|
172
|
+
<details id="version-update-guide" hidden>
|
|
173
|
+
<summary>How to update</summary>
|
|
174
|
+
<p id="version-update-steps">Press Ctrl+C in the viewer terminal, then run this command. Start a new Claude Code or Codex session after setup finishes.</p>
|
|
175
|
+
<pre id="version-update-command" tabindex="0"></pre>
|
|
176
|
+
<button id="version-update-copy" type="button">Copy update command</button>
|
|
177
|
+
<span id="version-update-copy-status" role="status"></span>
|
|
178
|
+
</details>
|
|
179
|
+
</section>
|
|
176
180
|
<section class="onboarding" aria-labelledby="onboarding-title">
|
|
177
181
|
<h2 id="onboarding-title">Getting your first shape</h2>
|
|
178
182
|
<ol class="onboarding-progress" aria-label="Observed setup progress">
|
|
@@ -193,6 +197,11 @@
|
|
|
193
197
|
<ol id="sidebar-hook-list" class="hook-receipts" aria-label="Received hooks across this project, newest first"></ol>
|
|
194
198
|
</details>
|
|
195
199
|
<section class="sidebar-history sidebar-theme" id="sidebar-history" data-theme="sketchbook" aria-labelledby="sidebar-change-heading">
|
|
200
|
+
<details class="shape-key">
|
|
201
|
+
<summary>Shape key</summary>
|
|
202
|
+
<div id="shape-key-items"></div>
|
|
203
|
+
<p>Shapes describe component types. Display overrides change appearance only.</p>
|
|
204
|
+
</details>
|
|
196
205
|
<div class="sidebar-heading"><h2 id="sidebar-change-heading">Diagram changes</h2><span class="sidebar-count" id="sidebar-change-count">0</span></div>
|
|
197
206
|
<p class="sidebar-note" id="sidebar-change-note">Newest changes first. Select a shape to inspect it, or a revision to replay.</p>
|
|
198
207
|
<p class="sidebar-empty" id="sidebar-change-empty">Shape changes will stack here as this session discovers or changes the architecture.</p>
|
|
@@ -212,7 +221,7 @@
|
|
|
212
221
|
</aside>
|
|
213
222
|
</div>
|
|
214
223
|
|
|
215
|
-
<div class="playback">
|
|
224
|
+
<div class="playback" id="history-panel" role="region" aria-label="Architecture history" hidden>
|
|
216
225
|
<div class="mode-buttons" role="group" aria-label="Architecture view">
|
|
217
226
|
<button id="live" type="button" aria-pressed="true">Live</button>
|
|
218
227
|
<button id="replay" type="button" aria-pressed="false" disabled>Replay</button>
|
|
@@ -226,13 +235,12 @@
|
|
|
226
235
|
</div>
|
|
227
236
|
</section>
|
|
228
237
|
|
|
229
|
-
<section class="activity-section" aria-labelledby="activity-heading">
|
|
238
|
+
<section class="activity-section" id="activity-panel" aria-labelledby="activity-heading" hidden>
|
|
230
239
|
<div class="activity-heading">
|
|
231
240
|
<div><h2 id="activity-heading">Activity stream</h2><p id="activity-note">Observable work, independent of classification.</p></div>
|
|
232
241
|
<span id="activity-count">0 events</span>
|
|
233
|
-
<button id="activity-toggle" type="button" aria-expanded="true" aria-controls="activity-content">Hide activity log</button>
|
|
234
242
|
</div>
|
|
235
|
-
<div id="activity-content">
|
|
243
|
+
<div id="activity-content" hidden>
|
|
236
244
|
<p class="activity-empty" id="activity-empty">No activity received. Start work in a connected agent session; its captured events will appear here.</p>
|
|
237
245
|
<ol id="activity-list" class="activity-list" aria-label="Recent activity, newest first"></ol>
|
|
238
246
|
</div>
|
package/runtime/web/style.css
CHANGED
|
@@ -24,39 +24,42 @@ button:disabled { opacity: .5; cursor: default; }
|
|
|
24
24
|
button:focus-visible, a:focus-visible, input:focus-visible, select:focus-visible, summary:focus-visible { outline: 3px solid var(--blue); outline-offset: 3px; }
|
|
25
25
|
a { color: var(--blue); }
|
|
26
26
|
p, h1, h2, h3 { margin: 0; }
|
|
27
|
-
.page {
|
|
27
|
+
.page { width: 100%; height: 100vh; height: 100dvh; min-height: 620px; display: flex; flex-direction: column; }
|
|
28
|
+
main { flex: 1; min-height: 0; display: flex; flex-direction: column; }
|
|
28
29
|
.skip-link { position: fixed; top: -100px; left: 20px; z-index: 20; padding: 12px 18px; background: var(--white); border: 2px solid var(--blue); border-radius: 6px; }
|
|
29
30
|
.skip-link:focus { top: 12px; }
|
|
30
|
-
.masthead { display: flex; align-items: center;
|
|
31
|
-
.brand { display: inline-flex; align-items: center; gap:
|
|
32
|
-
.brand-mark { width:
|
|
31
|
+
.masthead { display: flex; align-items: center; min-height: 54px; padding: 7px 16px; border-bottom: 1px solid var(--line); gap: 22px; background: var(--white); flex-shrink: 0; }
|
|
32
|
+
.brand { display: inline-flex; align-items: center; gap: 9px; color: var(--ink); font-size: 23px; font-weight: 750; letter-spacing: -1.1px; text-decoration: none; flex-shrink: 0; }
|
|
33
|
+
.brand-mark { width: 22px; height: 22px; border: 2px solid var(--blue); border-radius: 6px; transform: rotate(-9deg); position: relative; }
|
|
33
34
|
.brand-mark::after { content: ""; position: absolute; right: -8px; bottom: -6px; width: 12px; height: 12px; border: 3px solid var(--paper); border-radius: 50%; background: var(--teal); }
|
|
34
|
-
.connection { display: flex; align-items: center; gap:
|
|
35
|
+
.connection { display: flex; align-items: center; gap: 7px; color: var(--muted); font-size: 11px; margin-left: auto; max-width: 250px; }
|
|
36
|
+
.update-indicator { flex-shrink: 0; min-height: 28px; padding: 3px 8px; font-size: 10px; color: var(--teal); border-color: var(--teal); }
|
|
35
37
|
.connection-dot, .status-dot { display: inline-block; flex: 0 0 7px; width: 7px; height: 7px; border-radius: 50%; background: var(--muted); }
|
|
36
38
|
.connection[data-state="connected"] .connection-dot, .status-dot[data-state="ready"] { background: var(--teal); }
|
|
37
39
|
.connection[data-state="reconnecting"] .connection-dot, .status-dot[data-state="waiting"] { background: var(--amber); }
|
|
38
40
|
.connection[data-state="error"] .connection-dot, .connection[data-state="auth"] .connection-dot, .status-dot[data-state="error"] { background: var(--red); }
|
|
39
41
|
.text-button { background: transparent; border-color: transparent; padding: 4px 6px; min-height: 32px; color: var(--blue); font-size: 12px; }
|
|
40
|
-
.intro { padding: 25px 0 24px; display: flex; align-items: end; justify-content: space-between; gap: 22px; }
|
|
41
42
|
h1 { font-size: clamp(27px, 2.3vw, 36px); letter-spacing: -1px; font-weight: 650; line-height: 1.2; }
|
|
42
|
-
.
|
|
43
|
-
.intro .project-label { font-size: 12px; max-width: 250px; text-align: right; overflow-wrap: anywhere; }
|
|
44
|
-
.notice { padding: 11px 16px; margin-bottom: 14px; border-radius: 7px; font-size: 13px; }
|
|
43
|
+
.notice { flex-shrink: 0; padding: 6px 16px; font-size: 11px; max-height: 20dvh; overflow: auto; }
|
|
45
44
|
.demo-notice { display: flex; gap: 12px; color: #744807; background: #fff7e6; border: 1px solid #e6cb96; }
|
|
46
45
|
.demo-notice strong { white-space: nowrap; }
|
|
47
46
|
.error-notice { color: #85223a; background: #fff1f4; border: 1px solid #e8b8c3; }
|
|
48
|
-
.workspace {
|
|
49
|
-
.workspace-toolbar {
|
|
50
|
-
.session-picker { display: flex; align-items: center; gap:
|
|
51
|
-
.session-picker select { color: var(--ink); font-size:
|
|
52
|
-
.session-actions { display: flex; flex-wrap: wrap; gap:
|
|
53
|
-
.
|
|
47
|
+
.workspace { flex: 1; min-height: 0; display: flex; flex-direction: column; background: var(--white); }
|
|
48
|
+
.workspace-toolbar { padding: 6px 12px; display: flex; flex-wrap: wrap; gap: 6px 14px; align-items: center; border-bottom: 1px solid var(--line); flex-shrink: 0; }
|
|
49
|
+
.session-picker { display: flex; align-items: center; gap: 7px; font-size: 11px; color: var(--muted); min-width: 0; }
|
|
50
|
+
.session-picker select { color: var(--ink); font-size: 11px; font-weight: 600; border: 1px solid var(--line); border-radius: 5px; padding: 5px 22px 5px 8px; background: var(--paper); max-width: min(240px, 30vw); min-width: 100px; min-height: 32px; }
|
|
51
|
+
.session-actions { display: flex; flex-wrap: wrap; gap: 5px; margin-left: auto; font-size: 11px; }
|
|
52
|
+
.session-actions button, .panel-controls button { min-height: 32px; padding: 5px 9px; font-size: 11px; }
|
|
53
|
+
.panel-controls { display: flex; gap: 4px; }
|
|
54
|
+
.panel-controls button[aria-expanded="true"] { background: var(--soft); border-color: var(--blue); color: var(--blue); }
|
|
55
|
+
.status-strip { display: flex; flex-direction: column; gap: 9px; padding: 14px 17px; font-size: 11px; border-bottom: 1px solid var(--line); }
|
|
54
56
|
.classification-status { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
|
|
55
57
|
.classification-status strong { font-weight: 650; }
|
|
56
58
|
#capture-note { color: var(--muted); }
|
|
57
|
-
.coverage-status { display: flex; gap: 4px
|
|
59
|
+
.coverage-status { display: flex; gap: 4px 12px; color: var(--muted); flex-wrap: wrap; }
|
|
58
60
|
#coverage-label { max-width: 67ch; }
|
|
59
|
-
.workspace-body { display: grid; grid-template-columns: minmax(0, 1fr)
|
|
61
|
+
.workspace-body { position: relative; flex: 1; min-height: 0; display: grid; grid-template-columns: minmax(0, 1fr); align-items: stretch; }
|
|
62
|
+
.workspace-body[data-details-open="true"] { grid-template-columns: minmax(0, 1fr) 340px; }
|
|
60
63
|
.sidebar-theme,
|
|
61
64
|
.drawing {
|
|
62
65
|
--canvas: #fbfaf7; --ink: #293340; --muted: #4b5765; --node-ink: #343d49;
|
|
@@ -67,7 +70,7 @@ h1 { font-size: clamp(27px, 2.3vw, 36px); letter-spacing: -1px; font-weight: 650
|
|
|
67
70
|
--role-queue: #fae8bd; --role-external: #f4dbe3; --role-module: #e3e7ed;
|
|
68
71
|
--role-function: #d6edf2; --role-class: #dedaf3; --role-interface: #dde3fa;
|
|
69
72
|
--role-event: #f8ddc7; --role-configuration: #e6edce; --role-package: #eee0d1;
|
|
70
|
-
color-scheme: light; color: var(--ink); min-width: 0; display: flex; flex-direction: column;
|
|
73
|
+
color-scheme: light; color: var(--ink); min-width: 0; min-height: 0; display: flex; flex-direction: column;
|
|
71
74
|
background-color: var(--canvas); background-image: radial-gradient(var(--diagram-grid) .65px, transparent .65px); background-size: 18px 18px;
|
|
72
75
|
}
|
|
73
76
|
.sidebar-theme[data-theme="ocean"],
|
|
@@ -163,33 +166,43 @@ h1 { font-size: clamp(27px, 2.3vw, 36px); letter-spacing: -1px; font-weight: 650
|
|
|
163
166
|
.drawing [data-kind="package"], .sidebar-theme [data-kind="package"] { --node-fill: var(--role-package); }
|
|
164
167
|
.drawing button:hover:not(:disabled) { background: var(--soft); border-color: var(--diagram-focus); }
|
|
165
168
|
.drawing :is(button, input, select, summary):focus-visible { outline-color: var(--diagram-focus); }
|
|
166
|
-
.drawing-toolbar { display: flex;
|
|
167
|
-
.drawing-heading { display: flex; align-items: baseline; gap:
|
|
169
|
+
.drawing-toolbar { display: flex; flex-wrap: wrap; align-items: center; padding: 6px 12px; gap: 6px 18px; background: var(--canvas); border-bottom: 1px solid var(--line); flex-shrink: 0; }
|
|
170
|
+
.drawing-heading { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
|
|
168
171
|
.drawing-heading h2 { font-size: 13px; font-weight: 600; }
|
|
169
172
|
#revision { font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; }
|
|
170
|
-
.zoom-controls { display: flex; gap: 3px; align-items: center; background: var(--canvas); flex-shrink: 0; }
|
|
173
|
+
.zoom-controls { display: flex; gap: 3px; align-items: center; background: var(--canvas); flex-shrink: 0; margin-left: auto; }
|
|
171
174
|
.zoom-controls button { border: 0; background: transparent; min-width: 31px; min-height: 32px; padding: 2px 8px; font-size: 17px; }
|
|
172
175
|
.zoom-controls #fit { border: 1px solid var(--line); font-size: 11px; margin-left: 5px; min-width: 39px; }
|
|
173
|
-
.layout-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap:
|
|
174
|
-
.project-details {
|
|
175
|
-
.project-details dl { display: flex; flex-wrap: wrap; gap:
|
|
176
|
-
.project-details dl > div { min-width: 0; }
|
|
177
|
-
.project-details dt { color: var(--muted); font-size: 10px;
|
|
176
|
+
.layout-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; font-size: 11px; }
|
|
177
|
+
.project-details { min-width: 0; flex: 1; font-size: 11px; }
|
|
178
|
+
.project-details dl { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 16px; margin: 0; }
|
|
179
|
+
.project-details dl > div { display: flex; align-items: baseline; gap: 5px; min-width: 0; }
|
|
180
|
+
.project-details dt { color: var(--muted); font-size: 10px; }
|
|
178
181
|
.project-details dd { margin: 0; overflow-wrap: anywhere; }
|
|
179
|
-
.project-details .project-path { flex: 1 1
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
.project-details
|
|
183
|
-
.
|
|
184
|
-
.
|
|
185
|
-
.
|
|
186
|
-
.
|
|
182
|
+
.project-details .project-path { flex: 1 1 160px; }
|
|
183
|
+
#project-path { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
184
|
+
#project-branch { max-width: 22ch; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
185
|
+
.project-details .project-label { font-size: 11px; font-weight: 650; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
186
|
+
.version-details { padding: 12px 17px; font-size: 11px; color: var(--muted); border-bottom: 1px solid var(--line); }
|
|
187
|
+
.version-details details { margin-top: 8px; }
|
|
188
|
+
.version-details summary { cursor: pointer; font-weight: 600; }
|
|
189
|
+
.version-details pre { white-space: pre-wrap; overflow-wrap: anywhere; padding: 10px; background: var(--paper); }
|
|
190
|
+
.version-details button { min-height: 32px; font-size: 11px; }
|
|
191
|
+
.diagram-search-bar { display: flex; align-items: center; gap: 6px; flex: 0 1 360px; min-width: 200px; font-size: 11px; color: var(--muted); }
|
|
192
|
+
.diagram-search-bar input { flex: 1 1 130px; width: 100%; min-width: 80px; min-height: 32px; padding: 5px 9px; border: 1px solid var(--line); border-radius: 5px; background: var(--white); color: var(--ink); }
|
|
187
193
|
.diagram-search-bar input::placeholder { color: var(--muted); opacity: 1; }
|
|
188
|
-
.diagram-search-bar button { min-height:
|
|
194
|
+
.diagram-search-bar button { min-height: 32px; font-size: 11px; padding: 5px 7px; white-space: nowrap; }
|
|
195
|
+
#diagram-search-status { font-size: 10px; min-width: 72px; max-width: 140px; line-height: 1.3; }
|
|
196
|
+
.filter-toolbar { display: flex; align-items: center; gap: 12px; padding: 5px 12px; background: var(--canvas); border-bottom: 1px solid var(--line); flex-shrink: 0; }
|
|
197
|
+
.node-type-toolbar { display: flex; align-items: center; gap: 5px; min-width: 0; flex: 1; }
|
|
198
|
+
.node-type-toolbar button { font-size: 10px; min-height: 30px; padding: 4px 8px; white-space: nowrap; background: var(--white); flex-shrink: 0; }
|
|
199
|
+
#node-type-filters { display: flex; align-items: center; gap: 5px; min-width: 0; overflow-x: auto; padding: 4px; scrollbar-width: thin; }
|
|
200
|
+
#node-type-filters button[aria-pressed="true"] { background: var(--node-fill, var(--soft)); border-color: var(--blue); color: var(--ink); }
|
|
201
|
+
#node-type-filters button[aria-pressed="false"] { border-style: dashed; color: var(--muted); }
|
|
189
202
|
#diagram-search-status:empty { display: none; }
|
|
190
203
|
.layout-toolbar label { display: flex; align-items: center; gap: 8px; color: var(--muted); }
|
|
191
|
-
.layout-toolbar select, .shape-picker select { color: var(--ink); background: var(--white); border: 1px solid var(--line); border-radius: 5px; padding:
|
|
192
|
-
.layout-toolbar button { font-size: 11px; min-height:
|
|
204
|
+
.layout-toolbar select, .shape-picker select { color: var(--ink); background: var(--white); border: 1px solid var(--line); border-radius: 5px; padding: 5px 23px 5px 8px; font: inherit; min-height: 32px; }
|
|
205
|
+
.layout-toolbar button { font-size: 11px; min-height: 32px; padding: 5px 9px; }
|
|
193
206
|
.layout-toolbar .auto-arrange { color: var(--ink); white-space: nowrap; }
|
|
194
207
|
.theme-picker { display: flex; align-items: center; gap: 8px; }
|
|
195
208
|
.theme-preview { display: inline-flex; align-items: center; padding: 2px; }
|
|
@@ -198,14 +211,14 @@ h1 { font-size: clamp(27px, 2.3vw, 36px); letter-spacing: -1px; font-weight: 650
|
|
|
198
211
|
.theme-preview i:nth-child(3) { transform: rotate(-3deg); background: var(--role-queue); }
|
|
199
212
|
.theme-preview i:nth-child(4) { transform: rotate(7deg); background: var(--role-datastore); }
|
|
200
213
|
.auto-arrange input { width: 15px; height: 15px; margin: 0; accent-color: var(--blue); }
|
|
201
|
-
|
|
214
|
+
|
|
202
215
|
#zoom-level { font-size: 11px; color: var(--muted); min-width: 39px; text-align: center; font-variant-numeric: tabular-nums; }
|
|
203
|
-
.diagram-stage { position: relative; min-height:
|
|
216
|
+
.diagram-stage { position: relative; min-height: 180px; flex: 1 1 0; overflow: hidden; }
|
|
204
217
|
#architecture { display: block; height: 100%; width: 100%; touch-action: pan-y; }
|
|
205
218
|
#architecture:focus-visible { outline: 2px solid var(--diagram-focus); outline-offset: -5px; }
|
|
206
219
|
#architecture.is-pannable { cursor: grab; touch-action: none; }
|
|
207
220
|
#architecture.is-panning { cursor: grabbing; }
|
|
208
|
-
.empty-canvas { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding:
|
|
221
|
+
.empty-canvas { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: safe center; text-align: center; padding: 24px; overflow: auto; pointer-events: none; }
|
|
209
222
|
.empty-canvas h3 { font-size: 24px; letter-spacing: -.6px; font-weight: 600; margin: 20px 0 9px; }
|
|
210
223
|
.empty-canvas p { color: var(--muted); max-width: 46ch; font-size: 13px; }
|
|
211
224
|
.orientation { pointer-events: auto; max-width: 46ch; margin-top: 18px; }
|
|
@@ -225,14 +238,14 @@ h1 { font-size: clamp(27px, 2.3vw, 36px); letter-spacing: -1px; font-weight: 650
|
|
|
225
238
|
.empty-map i { width: 41px; height: 30px; border: 1.5px dashed #7999c3; border-radius: 6px; background: var(--paper); }
|
|
226
239
|
.empty-map i:last-child { height: 38px; width: 31px; border-radius: 50% / 14%; }
|
|
227
240
|
.empty-map span { width: 40px; border-top: 1.5px dashed #7999c3; }
|
|
228
|
-
.drawing-footer { display: flex; justify-content: space-between; gap: 16px; padding:
|
|
241
|
+
.drawing-footer { display: flex; justify-content: space-between; gap: 5px 16px; padding: 6px 12px 2px; font-size: 10px; color: var(--muted); flex-wrap: wrap; flex-shrink: 0; background: var(--canvas); }
|
|
229
242
|
.legend { display: flex; flex-wrap: wrap; gap: 17px; }
|
|
230
243
|
.legend span { display: flex; align-items: center; gap: 6px; }
|
|
231
244
|
.legend i { width: 15px; height: 10px; display: inline-block; border: 1.5px solid var(--blue); border-radius: 3px; background: var(--white); }
|
|
232
245
|
.legend .proposed { border-color: var(--amber); border-style: dashed; background: var(--paper); }
|
|
233
246
|
.legend .stale { border-color: var(--diagram-stale); border-style: dotted; background: var(--paper); }
|
|
234
|
-
.diagram-note { color: var(--muted); font-size: 10px; padding:
|
|
235
|
-
.shape-key { color: var(--muted); font-size:
|
|
247
|
+
.diagram-note { color: var(--muted); font-size: 10px; padding: 0 12px 5px; background: var(--canvas); flex-shrink: 0; }
|
|
248
|
+
.shape-key { color: var(--muted); font-size: 11px; padding: 10px 17px; border-bottom: 1px solid var(--line); }
|
|
236
249
|
.shape-key summary { cursor: pointer; width: fit-content; padding: 3px 0; }
|
|
237
250
|
#shape-key-items { display: grid; grid-template-columns: repeat(auto-fit, minmax(115px, 1fr)); gap: 7px 12px; margin: 9px 0; }
|
|
238
251
|
.shape-key-item { display: flex; align-items: center; gap: 7px; }
|
|
@@ -307,7 +320,10 @@ h1 { font-size: clamp(27px, 2.3vw, 36px); letter-spacing: -1px; font-weight: 650
|
|
|
307
320
|
.arrow-observed { fill: var(--diagram-edge); }
|
|
308
321
|
.arrow-proposed { fill: var(--amber); }
|
|
309
322
|
.arrow-stale { fill: var(--diagram-stale); }
|
|
310
|
-
.live-sidebar { min-width: 0; max-height:
|
|
323
|
+
.live-sidebar { min-width: 0; min-height: 0; max-height: 100%; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; border-left: 1px solid var(--line); background: var(--white); }
|
|
324
|
+
.details-heading { position: sticky; top: 0; z-index: 1; display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 8px 17px; background: var(--white); border-bottom: 1px solid var(--line); }
|
|
325
|
+
.details-heading h2 { font-size: 13px; }
|
|
326
|
+
.details-heading button { font-size: 11px; min-height: 30px; padding: 4px 8px; }
|
|
311
327
|
.sidebar-hooks { padding: 14px 17px 12px; border-bottom: 1px solid var(--line); }
|
|
312
328
|
.sidebar-hooks > summary { display: flex; align-items: center; gap: 10px; min-height: 30px; cursor: pointer; list-style: none; }
|
|
313
329
|
.sidebar-hooks > summary::-webkit-details-marker { display: none; }
|
|
@@ -393,7 +409,7 @@ h1 { font-size: clamp(27px, 2.3vw, 36px); letter-spacing: -1px; font-weight: 650
|
|
|
393
409
|
.source-reference summary { color: var(--blue); cursor: pointer; font-size: 11px; margin-top: 8px; }
|
|
394
410
|
.source-reference pre { font: 11px/1.6 ui-monospace, "SFMono-Regular", Consolas, monospace; white-space: pre-wrap; overflow-wrap: anywhere; margin: 9px 0 0; border-radius: 4px; padding: 9px; background: var(--paper); max-height: 220px; overflow: auto; tab-size: 2; }
|
|
395
411
|
.related-event { width: 100%; padding: 6px 0; text-align: left; font-size: 11px; color: var(--blue); background: none; border: 0; font-weight: 500; }
|
|
396
|
-
.playback { border-top: 1px solid var(--line); display: grid; grid-template-columns: auto minmax(100px, 1fr) minmax(190px, 290px); align-items: center; gap:
|
|
412
|
+
.playback { border-top: 1px solid var(--line); display: grid; grid-template-columns: auto minmax(100px, 1fr) minmax(190px, 290px); align-items: center; gap: 14px; padding: 8px 12px; flex-shrink: 0; max-height: 25dvh; overflow: auto; }
|
|
397
413
|
.mode-buttons { display: flex; background: var(--paper); border: 1px solid var(--line); border-radius: 7px; padding: 2px; }
|
|
398
414
|
.mode-buttons button { border: 0; background: transparent; color: var(--muted); min-height: 31px; font-size: 12px; border-radius: 4px; }
|
|
399
415
|
.mode-buttons button[aria-pressed="true"] { color: var(--blue); background: var(--white); box-shadow: 0 1px 4px #16324e12; }
|
|
@@ -401,9 +417,10 @@ h1 { font-size: clamp(27px, 2.3vw, 36px); letter-spacing: -1px; font-weight: 650
|
|
|
401
417
|
.scrubber input { accent-color: var(--blue); min-width: 20px; width: 100%; cursor: pointer; }
|
|
402
418
|
.scrubber output { font-size: 11px; white-space: nowrap; min-width: 60px; color: var(--muted); font-variant-numeric: tabular-nums; }
|
|
403
419
|
#replay-note { font-size: 10px; color: var(--muted); text-align: right; }
|
|
404
|
-
.activity-section { padding-top:
|
|
405
|
-
|
|
406
|
-
.activity-heading
|
|
420
|
+
.activity-section { height: min(220px, 28dvh); min-height: 110px; flex-shrink: 0; display: flex; flex-direction: column; padding: 10px 16px 0; border-top: 1px solid var(--line); background: var(--white); }
|
|
421
|
+
#activity-content { min-height: 0; overflow: auto; overscroll-behavior: contain; }
|
|
422
|
+
.activity-heading { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 6px 12px; margin-bottom: 7px; flex-shrink: 0; }
|
|
423
|
+
.activity-heading h2 { font-size: 13px; font-weight: 600; }
|
|
407
424
|
.activity-heading p { font-size: 11px; color: var(--muted); margin-top: 3px; }
|
|
408
425
|
#activity-count { color: var(--muted); font-size: 11px; }
|
|
409
426
|
.activity-list { list-style: none; padding: 0; margin: 0; border-top: 1px solid var(--line); max-height: 330px; overflow-y: auto; scrollbar-width: thin; }
|
|
@@ -419,7 +436,7 @@ h1 { font-size: clamp(27px, 2.3vw, 36px); letter-spacing: -1px; font-weight: 650
|
|
|
419
436
|
.event-state { color: var(--muted); text-align: right; font-size: 11px; }
|
|
420
437
|
.activity-empty { padding: 25px 22px; color: var(--muted); font-size: 12px; border: 1px dashed #bbcee6; border-radius: 8px; }
|
|
421
438
|
.activity-item.is-focused { background: #eaf0fc; }
|
|
422
|
-
footer {
|
|
439
|
+
footer { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 2px 12px; padding: 3px 12px; border-top: 1px solid var(--line); font-size: 9px; color: var(--muted); flex-shrink: 0; }
|
|
423
440
|
footer span { margin: 0 8px; color: #91a7c3; }
|
|
424
441
|
.toast { position: fixed; bottom: 25px; left: 50%; transform: translateX(-50%); background: var(--ink); color: var(--white); padding: 12px 20px; border-radius: 8px; font-size: 12px; z-index: 10; max-width: calc(100vw - 32px); box-shadow: 0 6px 30px #16324e20; }
|
|
425
442
|
.connection-dialog { width: min(760px, calc(100% - 32px)); max-height: calc(100vh - 48px); max-height: calc(100dvh - 48px); margin: auto; padding: 25px 28px; border: 1px solid var(--line); border-radius: 13px; background: var(--white); color: var(--ink); box-shadow: 0 24px 100px #16324e30; overflow-y: auto; }
|
|
@@ -491,54 +508,41 @@ footer span { margin: 0 8px; color: #91a7c3; }
|
|
|
491
508
|
.diagnostics-log-path { color: var(--muted); font-size: 11px; line-height: 1.6; overflow-wrap: anywhere; margin: 8px 0 0; }
|
|
492
509
|
.screen-reader { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
|
|
493
510
|
|
|
494
|
-
@media (min-width: 1450px) {
|
|
495
|
-
.workspace-body { grid-template-columns: minmax(0, 1fr) 345px; }
|
|
496
|
-
.inspector { padding: 19px 25px; }
|
|
497
|
-
}
|
|
498
511
|
@media (max-width: 1100px) {
|
|
499
|
-
.
|
|
500
|
-
.
|
|
501
|
-
.
|
|
502
|
-
.
|
|
503
|
-
.
|
|
504
|
-
.inspector { padding: 17px; }
|
|
505
|
-
.playback { gap: 16px; grid-template-columns: auto 1fr; }
|
|
506
|
-
#replay-note { grid-column: 2; margin-top: -9px; text-align: left; }
|
|
507
|
-
.drawing-toolbar { padding: 15px 15px 0; }
|
|
508
|
-
.layout-toolbar { padding-right: 15px; padding-left: 15px; }
|
|
509
|
-
.diagram-search-bar { padding-right: 15px; padding-left: 15px; }
|
|
510
|
-
.drawing-heading { gap: 3px 10px; }
|
|
511
|
-
.drawing-footer { padding-right: 15px; padding-left: 15px; }
|
|
512
|
-
.diagram-note { padding-left: 15px; }
|
|
513
|
-
}
|
|
514
|
-
@media (max-width: 1000px) {
|
|
515
|
-
.workspace-body { grid-template-columns: 1fr; }
|
|
516
|
-
.live-sidebar { border-left: 0; border-top: 1px solid var(--line); max-height: 640px; }
|
|
517
|
-
.inspector { padding: 17px 21px; }
|
|
518
|
-
.change-tiles { grid-template-columns: repeat(8, minmax(0, 1fr)); }
|
|
519
|
-
.inspector-header { margin-bottom: 8px; }
|
|
520
|
-
.inspector-empty { padding-top: 5px; }
|
|
521
|
-
.inspector-glyph { display: none; }
|
|
522
|
-
.inspector-empty h3 { font-size: 18px; margin-bottom: 6px; }
|
|
523
|
-
.inspector-empty p { max-width: 65ch; margin-bottom: 7px; }
|
|
524
|
-
.inspector-empty .inspector-hint { margin-top: 5px; }
|
|
512
|
+
.drawing-heading { flex: 1; }
|
|
513
|
+
.layout-toolbar { order: 1; width: 100%; }
|
|
514
|
+
.theme-preview { display: none; }
|
|
515
|
+
.session-actions { gap: 4px; }
|
|
516
|
+
.workspace-body[data-details-open="true"] { grid-template-columns: minmax(0, 1fr) 310px; }
|
|
525
517
|
}
|
|
526
518
|
@media (max-width: 800px) {
|
|
527
|
-
.page {
|
|
528
|
-
.masthead {
|
|
529
|
-
.
|
|
530
|
-
.
|
|
531
|
-
.
|
|
532
|
-
.
|
|
533
|
-
.
|
|
534
|
-
.
|
|
535
|
-
.session-
|
|
536
|
-
.
|
|
537
|
-
.
|
|
519
|
+
.page { height: auto; min-height: 100dvh; }
|
|
520
|
+
.masthead { flex-wrap: wrap; gap: 6px 16px; padding: 8px 12px; }
|
|
521
|
+
.project-details { flex-basis: 100%; order: 1; }
|
|
522
|
+
.project-details .project-label { display: inline; margin-right: 12px; }
|
|
523
|
+
.project-details dl { gap: 3px 12px; }
|
|
524
|
+
.connection { font-size: 10px; max-width: 60%; }
|
|
525
|
+
.session-picker { flex: 1; }
|
|
526
|
+
.session-picker select { max-width: 100%; width: 100%; min-width: 0; }
|
|
527
|
+
.session-actions { width: 100%; margin-left: 0; }
|
|
528
|
+
.workspace-body[data-details-open="true"] { grid-template-columns: minmax(0, 1fr); }
|
|
529
|
+
.live-sidebar { position: absolute; top: 0; bottom: 0; right: 0; width: min(360px, 100%); z-index: 4; box-shadow: -6px 0 24px #16324e18; }
|
|
530
|
+
.layout-toolbar { flex-wrap: nowrap; overflow-x: auto; padding: 4px; scrollbar-width: thin; }
|
|
531
|
+
.layout-toolbar > * { flex-shrink: 0; }
|
|
532
|
+
.filter-toolbar { flex-wrap: wrap; gap: 2px; }
|
|
533
|
+
.diagram-search-bar { flex: 1 1 100%; }
|
|
534
|
+
.node-type-toolbar { flex-basis: 100%; }
|
|
535
|
+
.diagram-stage { min-height: max(320px, calc(100dvh - 335px)); }
|
|
536
|
+
.empty-canvas { padding: 20px; }
|
|
537
|
+
.empty-canvas h3 { font-size: 21px; }
|
|
538
|
+
.empty-canvas p { font-size: 12px; }
|
|
539
|
+
.playback { gap: 5px 12px; grid-template-columns: auto minmax(0, 1fr); }
|
|
540
|
+
#replay-note { grid-column: 1 / -1; text-align: left; }
|
|
538
541
|
.event-detail { display: none; }
|
|
542
|
+
.activity-row { grid-template-columns: 57px 5px minmax(60px, 1fr) 68px; gap: 7px; padding: 12px 3px; font-size: 11px; }
|
|
543
|
+
.legend { gap: 5px 12px; }
|
|
539
544
|
}
|
|
540
545
|
@media (max-width: 550px) {
|
|
541
|
-
.change-tiles { grid-template-columns: repeat(4, minmax(0, 1fr)); }
|
|
542
546
|
.diagnostics-dialog { padding: 19px 15px; width: calc(100% - 20px); max-height: calc(100dvh - 24px); }
|
|
543
547
|
.diagnostics-header h2 { font-size: 21px; }
|
|
544
548
|
.diagnostics-filters { grid-template-columns: minmax(0, 1fr) auto; gap: 10px; }
|
|
@@ -552,59 +556,16 @@ footer span { margin: 0 8px; color: #91a7c3; }
|
|
|
552
556
|
.connection-steps { padding-left: 18px; }
|
|
553
557
|
.connection-steps pre { padding: 10px; font-size: 11px; }
|
|
554
558
|
.connection-step-heading button { min-height: 40px; }
|
|
555
|
-
.
|
|
556
|
-
.
|
|
557
|
-
.
|
|
558
|
-
.
|
|
559
|
-
#connection-label { max-width: 145px; text-align: right; }
|
|
560
|
-
.connection .text-button { min-height: 26px; padding: 0 2px; font-size: 10px; }
|
|
561
|
-
.intro { display: block; padding: 22px 2px 18px; }
|
|
562
|
-
h1 { font-size: 27px; letter-spacing: -.8px; }
|
|
563
|
-
.intro p { font-size: 12px; max-width: 38ch; }
|
|
564
|
-
.intro .project-label { max-width: none; text-align: left; font-size: 10px; padding-top: 3px; }
|
|
565
|
-
.demo-notice { flex-direction: column; gap: 3px; font-size: 11px; padding: 11px 13px; }
|
|
566
|
-
.workspace { border-radius: 9px; }
|
|
567
|
-
.workspace-toolbar { flex-wrap: wrap; gap: 10px; padding: 12px; }
|
|
568
|
-
.session-picker { width: 100%; }
|
|
569
|
-
.session-picker select { flex: 1; max-width: none; min-width: 0; min-height: 43px; }
|
|
570
|
-
.session-actions { width: 100%; }
|
|
571
|
-
.session-actions button { flex: 1; min-height: 43px; }
|
|
572
|
-
.status-strip { flex-direction: column; align-items: start; gap: 8px; padding: 11px 13px; }
|
|
573
|
-
.classification-status { max-width: none; font-size: 10px; gap: 6px; }
|
|
574
|
-
#capture-note { flex-basis: 100%; padding-left: 13px; }
|
|
575
|
-
.coverage-status { flex-direction: row; text-align: left; gap: 5px 14px; font-size: 10px; max-width: none; }
|
|
576
|
-
.drawing-toolbar { padding: 10px 10px 0; gap: 7px; align-items: start; }
|
|
577
|
-
.layout-toolbar { padding: 10px 12px 3px; gap: 9px; }
|
|
578
|
-
.diagram-search-bar { padding: 10px 12px 3px; gap: 8px; }
|
|
579
|
-
.layout-toolbar select, .layout-toolbar button { min-height: 40px; }
|
|
580
|
-
.auto-arrange { min-height: 30px; }
|
|
581
|
-
.shape-key { padding-right: 12px; padding-left: 12px; }
|
|
582
|
-
.drawing-heading { display: flex; flex-direction: column; padding-top: 8px; gap: 3px; }
|
|
583
|
-
.drawing-heading h2 { font-size: 11px; }
|
|
559
|
+
.brand { font-size: 22px; }
|
|
560
|
+
.drawing-toolbar { gap: 3px 7px; }
|
|
561
|
+
.drawing-heading { gap: 3px 8px; }
|
|
562
|
+
.drawing-heading h2 { font-size: 12px; }
|
|
584
563
|
#revision { font-size: 10px; }
|
|
585
|
-
.
|
|
586
|
-
.
|
|
587
|
-
.
|
|
588
|
-
|
|
589
|
-
.diagram-stage { height: 350px; min-height: 350px; }
|
|
590
|
-
.empty-canvas { padding: 24px; }
|
|
591
|
-
.empty-canvas h3 { font-size: 21px; }
|
|
592
|
-
.empty-canvas p { font-size: 12px; }
|
|
593
|
-
.legend { gap: 7px 13px; }
|
|
594
|
-
.drawing-footer { padding: 9px 12px 5px; gap: 8px; font-size: 9px; }
|
|
595
|
-
.diagram-note { padding: 0 12px 12px; font-size: 9px; }
|
|
596
|
-
.inspector { padding: 13px 16px; }
|
|
597
|
-
.playback { padding: 12px; gap: 9px; grid-template-columns: 1fr; }
|
|
598
|
-
.mode-buttons { justify-self: start; }
|
|
599
|
-
.mode-buttons button { min-height: 40px; min-width: 63px; }
|
|
600
|
-
.scrubber input { min-height: 35px; }
|
|
601
|
-
#replay-note { grid-column: 1; margin-top: -3px; }
|
|
602
|
-
.activity-heading h2 { font-size: 16px; }
|
|
603
|
-
.activity-heading p { font-size: 10px; max-width: 30ch; }
|
|
604
|
-
.activity-row { grid-template-columns: 57px 5px minmax(60px, 1fr) 68px; gap: 7px; padding: 12px 3px; font-size: 11px; }
|
|
564
|
+
.layout-toolbar label { gap: 5px; }
|
|
565
|
+
.drawing-footer { font-size: 9px; }
|
|
566
|
+
.diagram-note { font-size: 9px; }
|
|
567
|
+
.activity-heading p { font-size: 10px; }
|
|
605
568
|
.activity-row time, .event-state { font-size: 9px; }
|
|
606
|
-
.activity-empty { padding: 18px; font-size: 11px; }
|
|
607
|
-
footer { flex-direction: column; gap: 5px; margin-top: 22px; }
|
|
608
569
|
}
|
|
609
570
|
@media (prefers-reduced-motion: reduce) {
|
|
610
571
|
*, *::before, *::after { scroll-behavior: auto !important; animation: none !important; transition: none !important; }
|
package/scripts/control.mjs
CHANGED
|
@@ -39,7 +39,7 @@ async function handle(line) {
|
|
|
39
39
|
const supported = ['2024-11-05', '2025-03-26', '2025-06-18'];
|
|
40
40
|
return result({ protocolVersion: supported.includes(message.params?.protocolVersion) ? message.params.protocolVersion : '2025-06-18',
|
|
41
41
|
capabilities: { tools: { listChanged: false } },
|
|
42
|
-
serverInfo: { name: 'graphlin', version: '0.1.
|
|
42
|
+
serverInfo: { name: 'graphlin', version: '0.1.3' },
|
|
43
43
|
instructions: 'Controls only. Passive host hooks provide observations when separately activated. No drawing calls after each action.' });
|
|
44
44
|
}
|
|
45
45
|
if (message.method === 'ping') return result({});
|