graphlin 0.1.1 → 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.
@@ -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,13 +33,7 @@
24
33
  </header>
25
34
 
26
35
  <main id="main">
27
- <div class="intro">
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>
36
+ <h1 class="screen-reader">Live architecture workspace</h1>
34
37
 
35
38
  <div id="demo-banner" class="notice demo-notice" role="status" hidden>
36
39
  <strong>Demo session</strong>
@@ -45,30 +48,57 @@
45
48
  <span>Session</span>
46
49
  <select id="session" disabled><option value="">Waiting for a session</option></select>
47
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>
48
56
  <div class="session-actions">
49
57
  <button type="button" id="classification-log" aria-haspopup="dialog" aria-controls="diagnostics-dialog">Classification log</button>
50
- <button type="button" id="how-to-connect" aria-haspopup="dialog" aria-controls="connection-dialog">How to connect</button>
58
+ <button type="button" id="how-to-connect" aria-haspopup="dialog" aria-controls="connection-dialog">Connect</button>
51
59
  <button type="button" id="pause" aria-describedby="capture-note" disabled>Pause classification</button>
52
60
  <button type="button" id="export" disabled>Export JSON</button>
53
61
  </div>
54
62
  </div>
55
63
 
56
- <div class="status-strip" aria-label="Capture and classification status">
57
- <div class="classification-status">
58
- <span class="status-dot" id="classifier-dot" data-state="waiting" aria-hidden="true"></span>
59
- <strong id="classifier-label">Waiting for local service</strong>
60
- <span id="capture-note">Capture status will appear with the first snapshot.</span>
61
- </div>
62
- <div class="coverage-status"><span id="coverage-label">Coverage not reported</span><span id="queue-label">No snapshot yet</span></div>
63
- </div>
64
-
65
- <div class="workspace-body">
64
+ <div class="workspace-body" id="workspace-body" data-details-open="false">
66
65
  <section class="drawing" id="drawing" data-theme="sketchbook" aria-labelledby="canvas-title">
67
66
  <div class="drawing-toolbar">
68
67
  <div class="drawing-heading">
69
68
  <h2 id="canvas-title">Live architecture</h2>
70
69
  <span id="revision">Revision —</span>
71
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>
72
102
  <div class="zoom-controls" role="group" aria-label="Diagram zoom">
73
103
  <button type="button" id="zoom-out" aria-label="Zoom out" disabled>−</button>
74
104
  <output id="zoom-level" aria-label="Zoom level">100%</output>
@@ -76,36 +106,19 @@
76
106
  <button type="button" id="fit" disabled>Fit</button>
77
107
  </div>
78
108
  </div>
79
- <div class="layout-toolbar" role="group" aria-label="Diagram arrangement">
80
- <label for="layout">Layout
81
- <select id="layout" disabled>
82
- <option value="hierarchy">Hierarchy top-down</option>
83
- <option value="dependency">Dependency left-right</option>
84
- <option value="grouped">Group by type</option>
85
- <option value="circular">Circular</option>
86
- <option value="grid">Grid</option>
87
- <option value="original">Original</option>
88
- </select>
89
- </label>
90
- <button type="button" id="arrange" disabled>Arrange</button>
91
- <label class="auto-arrange" for="auto-arrange"><input id="auto-arrange" type="checkbox" checked disabled> Auto-arrange</label>
92
- <div class="theme-picker">
93
- <label for="theme">Theme
94
- <select id="theme" aria-describedby="theme-note" disabled>
95
- <option value="sketchbook">Sketchbook</option>
96
- <option value="ocean">Ocean</option>
97
- <option value="forest">Forest</option>
98
- <option value="sunset">Sunset</option>
99
- <option value="berry">Berry</option>
100
- <option value="sepia">Sepia</option>
101
- <option value="blueprint">Blueprint dark</option>
102
- <option value="midnight">Midnight dark</option>
103
- </select>
104
- </label>
105
- <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>
106
121
  </div>
107
- <span class="screen-reader" id="theme-note">Colors follow component types. Themes change appearance only.</span>
108
- <span id="layout-note">Arranges when components or connections change.</span>
109
122
  </div>
110
123
  <div class="diagram-stage" id="diagram-stage">
111
124
  <svg id="architecture" viewBox="0 0 920 510" role="group" tabindex="0" aria-labelledby="diagram-title diagram-desc">
@@ -141,15 +154,29 @@
141
154
  </div>
142
155
  <span id="graph-count">No components yet</span>
143
156
  </div>
144
- <details class="shape-key">
145
- <summary>Shape key</summary>
146
- <div id="shape-key-items"></div>
147
- <p>Shapes describe component types. Display overrides change appearance only.</p>
148
- </details>
149
157
  <p class="diagram-note" id="diagram-note">Code classification does not establish runtime connectivity.</p>
150
158
  </section>
151
159
 
152
- <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>
153
180
  <section class="onboarding" aria-labelledby="onboarding-title">
154
181
  <h2 id="onboarding-title">Getting your first shape</h2>
155
182
  <ol class="onboarding-progress" aria-label="Observed setup progress">
@@ -170,6 +197,11 @@
170
197
  <ol id="sidebar-hook-list" class="hook-receipts" aria-label="Received hooks across this project, newest first"></ol>
171
198
  </details>
172
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>
173
205
  <div class="sidebar-heading"><h2 id="sidebar-change-heading">Diagram changes</h2><span class="sidebar-count" id="sidebar-change-count">0</span></div>
174
206
  <p class="sidebar-note" id="sidebar-change-note">Newest changes first. Select a shape to inspect it, or a revision to replay.</p>
175
207
  <p class="sidebar-empty" id="sidebar-change-empty">Shape changes will stack here as this session discovers or changes the architecture.</p>
@@ -189,7 +221,7 @@
189
221
  </aside>
190
222
  </div>
191
223
 
192
- <div class="playback">
224
+ <div class="playback" id="history-panel" role="region" aria-label="Architecture history" hidden>
193
225
  <div class="mode-buttons" role="group" aria-label="Architecture view">
194
226
  <button id="live" type="button" aria-pressed="true">Live</button>
195
227
  <button id="replay" type="button" aria-pressed="false" disabled>Replay</button>
@@ -203,13 +235,12 @@
203
235
  </div>
204
236
  </section>
205
237
 
206
- <section class="activity-section" aria-labelledby="activity-heading">
238
+ <section class="activity-section" id="activity-panel" aria-labelledby="activity-heading" hidden>
207
239
  <div class="activity-heading">
208
240
  <div><h2 id="activity-heading">Activity stream</h2><p id="activity-note">Observable work, independent of classification.</p></div>
209
241
  <span id="activity-count">0 events</span>
210
- <button id="activity-toggle" type="button" aria-expanded="true" aria-controls="activity-content">Hide activity log</button>
211
242
  </div>
212
- <div id="activity-content">
243
+ <div id="activity-content" hidden>
213
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>
214
245
  <ol id="activity-list" class="activity-list" aria-label="Recent activity, newest first"></ol>
215
246
  </div>
@@ -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 { max-width: 1720px; margin: auto; padding: 0 38px; }
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; justify-content: space-between; min-height: 85px; border-bottom: 1px solid var(--line); gap: 20px; }
31
- .brand { display: inline-flex; align-items: center; gap: 12px; color: var(--ink); font-size: 29px; font-weight: 750; letter-spacing: -1.4px; text-decoration: none; }
32
- .brand-mark { width: 28px; height: 28px; border: 3px solid var(--blue); border-radius: 7px; transform: rotate(-9deg); position: relative; }
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: 8px; color: var(--muted); font-size: 12px; }
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
- .intro p { color: var(--muted); margin-top: 7px; }
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 { border: 1px solid var(--line); border-radius: 13px; background: var(--white); overflow: hidden; box-shadow: 0 8px 30px #16324e05; }
49
- .workspace-toolbar { min-height: 71px; padding: 14px 21px; display: flex; flex-wrap: wrap; gap: 20px; align-items: center; justify-content: space-between; border-bottom: 1px solid var(--line); }
50
- .session-picker { display: flex; align-items: center; gap: 11px; font-size: 12px; color: var(--muted); min-width: 0; }
51
- .session-picker select { color: var(--ink); font-size: 14px; font-weight: 600; border: 1px solid var(--line); border-radius: 6px; padding: 8px 30px 8px 11px; background: var(--paper); max-width: min(320px, 35vw); min-width: 150px; }
52
- .session-actions { display: flex; flex-wrap: wrap; gap: 8px; flex-shrink: 0; max-width: 100%; font-size: 12px; }
53
- .status-strip { display: flex; justify-content: space-between; gap: 15px; align-items: center; padding: 11px 21px; font-size: 11px; border-bottom: 1px solid var(--line); }
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 16px; justify-content: flex-end; color: var(--muted); text-align: right; flex-wrap: wrap; max-width: 52%; }
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) 315px; align-items: start; }
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,17 +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; justify-content: space-between; align-items: center; padding: 17px 21px 0; gap: 15px; z-index: 1; }
167
- .drawing-heading { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
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: 8px 12px; padding: 12px 21px 4px; font-size: 11px; }
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; }
181
+ .project-details dd { margin: 0; overflow-wrap: anywhere; }
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); }
193
+ .diagram-search-bar input::placeholder { color: var(--muted); opacity: 1; }
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); }
202
+ #diagram-search-status:empty { display: none; }
174
203
  .layout-toolbar label { display: flex; align-items: center; gap: 8px; color: var(--muted); }
175
- .layout-toolbar select, .shape-picker select { color: var(--ink); background: var(--white); border: 1px solid var(--line); border-radius: 5px; padding: 7px 25px 7px 9px; font: inherit; min-height: 34px; }
176
- .layout-toolbar button { font-size: 11px; min-height: 34px; padding: 5px 12px; }
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; }
177
206
  .layout-toolbar .auto-arrange { color: var(--ink); white-space: nowrap; }
178
207
  .theme-picker { display: flex; align-items: center; gap: 8px; }
179
208
  .theme-preview { display: inline-flex; align-items: center; padding: 2px; }
@@ -182,14 +211,14 @@ h1 { font-size: clamp(27px, 2.3vw, 36px); letter-spacing: -1px; font-weight: 650
182
211
  .theme-preview i:nth-child(3) { transform: rotate(-3deg); background: var(--role-queue); }
183
212
  .theme-preview i:nth-child(4) { transform: rotate(7deg); background: var(--role-datastore); }
184
213
  .auto-arrange input { width: 15px; height: 15px; margin: 0; accent-color: var(--blue); }
185
- #layout-note { flex-basis: 100%; color: var(--muted); font-size: 10px; }
214
+
186
215
  #zoom-level { font-size: 11px; color: var(--muted); min-width: 39px; text-align: center; font-variant-numeric: tabular-nums; }
187
- .diagram-stage { position: relative; min-height: 410px; height: clamp(420px, 49vh, 650px); flex: 0 0 auto; overflow: hidden; }
216
+ .diagram-stage { position: relative; min-height: 180px; flex: 1 1 0; overflow: hidden; }
188
217
  #architecture { display: block; height: 100%; width: 100%; touch-action: pan-y; }
189
218
  #architecture:focus-visible { outline: 2px solid var(--diagram-focus); outline-offset: -5px; }
190
219
  #architecture.is-pannable { cursor: grab; touch-action: none; }
191
220
  #architecture.is-panning { cursor: grabbing; }
192
- .empty-canvas { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: 40px; pointer-events: none; }
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; }
193
222
  .empty-canvas h3 { font-size: 24px; letter-spacing: -.6px; font-weight: 600; margin: 20px 0 9px; }
194
223
  .empty-canvas p { color: var(--muted); max-width: 46ch; font-size: 13px; }
195
224
  .orientation { pointer-events: auto; max-width: 46ch; margin-top: 18px; }
@@ -209,14 +238,14 @@ h1 { font-size: clamp(27px, 2.3vw, 36px); letter-spacing: -1px; font-weight: 650
209
238
  .empty-map i { width: 41px; height: 30px; border: 1.5px dashed #7999c3; border-radius: 6px; background: var(--paper); }
210
239
  .empty-map i:last-child { height: 38px; width: 31px; border-radius: 50% / 14%; }
211
240
  .empty-map span { width: 40px; border-top: 1.5px dashed #7999c3; }
212
- .drawing-footer { display: flex; justify-content: space-between; gap: 16px; padding: 10px 21px 4px; font-size: 10px; color: var(--muted); flex-wrap: wrap; }
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); }
213
242
  .legend { display: flex; flex-wrap: wrap; gap: 17px; }
214
243
  .legend span { display: flex; align-items: center; gap: 6px; }
215
244
  .legend i { width: 15px; height: 10px; display: inline-block; border: 1.5px solid var(--blue); border-radius: 3px; background: var(--white); }
216
245
  .legend .proposed { border-color: var(--amber); border-style: dashed; background: var(--paper); }
217
246
  .legend .stale { border-color: var(--diagram-stale); border-style: dotted; background: var(--paper); }
218
- .diagram-note { color: var(--muted); font-size: 10px; padding: 3px 21px 14px; }
219
- .shape-key { color: var(--muted); font-size: 10px; padding: 6px 21px; }
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); }
220
249
  .shape-key summary { cursor: pointer; width: fit-content; padding: 3px 0; }
221
250
  #shape-key-items { display: grid; grid-template-columns: repeat(auto-fit, minmax(115px, 1fr)); gap: 7px 12px; margin: 9px 0; }
222
251
  .shape-key-item { display: flex; align-items: center; gap: 7px; }
@@ -291,7 +320,10 @@ h1 { font-size: clamp(27px, 2.3vw, 36px); letter-spacing: -1px; font-weight: 650
291
320
  .arrow-observed { fill: var(--diagram-edge); }
292
321
  .arrow-proposed { fill: var(--amber); }
293
322
  .arrow-stale { fill: var(--diagram-stale); }
294
- .live-sidebar { min-width: 0; max-height: min(920px, 90vh); overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; border-left: 1px solid var(--line); }
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; }
295
327
  .sidebar-hooks { padding: 14px 17px 12px; border-bottom: 1px solid var(--line); }
296
328
  .sidebar-hooks > summary { display: flex; align-items: center; gap: 10px; min-height: 30px; cursor: pointer; list-style: none; }
297
329
  .sidebar-hooks > summary::-webkit-details-marker { display: none; }
@@ -377,7 +409,7 @@ h1 { font-size: clamp(27px, 2.3vw, 36px); letter-spacing: -1px; font-weight: 650
377
409
  .source-reference summary { color: var(--blue); cursor: pointer; font-size: 11px; margin-top: 8px; }
378
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; }
379
411
  .related-event { width: 100%; padding: 6px 0; text-align: left; font-size: 11px; color: var(--blue); background: none; border: 0; font-weight: 500; }
380
- .playback { border-top: 1px solid var(--line); display: grid; grid-template-columns: auto minmax(100px, 1fr) minmax(190px, 290px); align-items: center; gap: 22px; padding: 14px 21px; }
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; }
381
413
  .mode-buttons { display: flex; background: var(--paper); border: 1px solid var(--line); border-radius: 7px; padding: 2px; }
382
414
  .mode-buttons button { border: 0; background: transparent; color: var(--muted); min-height: 31px; font-size: 12px; border-radius: 4px; }
383
415
  .mode-buttons button[aria-pressed="true"] { color: var(--blue); background: var(--white); box-shadow: 0 1px 4px #16324e12; }
@@ -385,9 +417,10 @@ h1 { font-size: clamp(27px, 2.3vw, 36px); letter-spacing: -1px; font-weight: 650
385
417
  .scrubber input { accent-color: var(--blue); min-width: 20px; width: 100%; cursor: pointer; }
386
418
  .scrubber output { font-size: 11px; white-space: nowrap; min-width: 60px; color: var(--muted); font-variant-numeric: tabular-nums; }
387
419
  #replay-note { font-size: 10px; color: var(--muted); text-align: right; }
388
- .activity-section { padding-top: 24px; }
389
- .activity-heading { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 12px; margin-bottom: 13px; }
390
- .activity-heading h2 { font-size: 17px; letter-spacing: -.3px; font-weight: 600; }
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; }
391
424
  .activity-heading p { font-size: 11px; color: var(--muted); margin-top: 3px; }
392
425
  #activity-count { color: var(--muted); font-size: 11px; }
393
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; }
@@ -403,7 +436,7 @@ h1 { font-size: clamp(27px, 2.3vw, 36px); letter-spacing: -1px; font-weight: 650
403
436
  .event-state { color: var(--muted); text-align: right; font-size: 11px; }
404
437
  .activity-empty { padding: 25px 22px; color: var(--muted); font-size: 12px; border: 1px dashed #bbcee6; border-radius: 8px; }
405
438
  .activity-item.is-focused { background: #eaf0fc; }
406
- footer { border-top: 1px solid var(--line); margin-top: 28px; padding: 18px 0 24px; display: flex; justify-content: space-between; gap: 18px; color: var(--muted); font-size: 10px; }
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; }
407
440
  footer span { margin: 0 8px; color: #91a7c3; }
408
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; }
409
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; }
@@ -475,53 +508,41 @@ footer span { margin: 0 8px; color: #91a7c3; }
475
508
  .diagnostics-log-path { color: var(--muted); font-size: 11px; line-height: 1.6; overflow-wrap: anywhere; margin: 8px 0 0; }
476
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; }
477
510
 
478
- @media (min-width: 1450px) {
479
- .workspace-body { grid-template-columns: minmax(0, 1fr) 345px; }
480
- .inspector { padding: 19px 25px; }
481
- }
482
511
  @media (max-width: 1100px) {
483
- .page { padding: 0 24px; }
484
- .workspace-body { grid-template-columns: minmax(0, 1fr) 280px; }
485
- .status-strip { align-items: start; }
486
- .classification-status { max-width: 62%; }
487
- .coverage-status { gap: 4px; flex-direction: column; }
488
- .inspector { padding: 17px; }
489
- .playback { gap: 16px; grid-template-columns: auto 1fr; }
490
- #replay-note { grid-column: 2; margin-top: -9px; text-align: left; }
491
- .drawing-toolbar { padding: 15px 15px 0; }
492
- .layout-toolbar { padding-right: 15px; padding-left: 15px; }
493
- .drawing-heading { gap: 3px 10px; }
494
- .drawing-footer { padding-right: 15px; padding-left: 15px; }
495
- .diagram-note { padding-left: 15px; }
496
- }
497
- @media (max-width: 1000px) {
498
- .workspace-body { grid-template-columns: 1fr; }
499
- .live-sidebar { border-left: 0; border-top: 1px solid var(--line); max-height: 640px; }
500
- .inspector { padding: 17px 21px; }
501
- .change-tiles { grid-template-columns: repeat(8, minmax(0, 1fr)); }
502
- .inspector-header { margin-bottom: 8px; }
503
- .inspector-empty { padding-top: 5px; }
504
- .inspector-glyph { display: none; }
505
- .inspector-empty h3 { font-size: 18px; margin-bottom: 6px; }
506
- .inspector-empty p { max-width: 65ch; margin-bottom: 7px; }
507
- .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; }
508
517
  }
509
518
  @media (max-width: 800px) {
510
- .page { padding: 0 18px; }
511
- .masthead { min-height: 74px; }
512
- .intro { padding: 22px 0; align-items: start; }
513
- .intro .project-label { max-width: 130px; padding-top: 4px; }
514
- .diagram-stage { min-height: 360px; height: 46vh; }
515
- .drawing-footer { padding-top: 7px; }
516
- .status-strip { padding: 11px 16px; }
517
- .workspace-toolbar { padding: 13px 16px; }
518
- .session-picker select { max-width: 32vw; min-width: 130px; }
519
- .session-actions button { padding: 8px 10px; }
520
- .activity-row { grid-template-columns: 64px 6px minmax(100px, 1fr) 80px; gap: 10px; }
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; }
521
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; }
522
544
  }
523
545
  @media (max-width: 550px) {
524
- .change-tiles { grid-template-columns: repeat(4, minmax(0, 1fr)); }
525
546
  .diagnostics-dialog { padding: 19px 15px; width: calc(100% - 20px); max-height: calc(100dvh - 24px); }
526
547
  .diagnostics-header h2 { font-size: 21px; }
527
548
  .diagnostics-filters { grid-template-columns: minmax(0, 1fr) auto; gap: 10px; }
@@ -535,58 +556,16 @@ footer span { margin: 0 8px; color: #91a7c3; }
535
556
  .connection-steps { padding-left: 18px; }
536
557
  .connection-steps pre { padding: 10px; font-size: 11px; }
537
558
  .connection-step-heading button { min-height: 40px; }
538
- .page { padding: 0 12px; }
539
- .brand { font-size: 25px; gap: 10px; }
540
- .brand-mark { height: 25px; width: 25px; }
541
- .connection { font-size: 10px; gap: 5px; max-width: 54%; flex-wrap: wrap; justify-content: flex-end; }
542
- #connection-label { max-width: 145px; text-align: right; }
543
- .connection .text-button { min-height: 26px; padding: 0 2px; font-size: 10px; }
544
- .intro { display: block; padding: 22px 2px 18px; }
545
- h1 { font-size: 27px; letter-spacing: -.8px; }
546
- .intro p { font-size: 12px; max-width: 38ch; }
547
- .intro .project-label { max-width: none; text-align: left; font-size: 10px; padding-top: 3px; }
548
- .demo-notice { flex-direction: column; gap: 3px; font-size: 11px; padding: 11px 13px; }
549
- .workspace { border-radius: 9px; }
550
- .workspace-toolbar { flex-wrap: wrap; gap: 10px; padding: 12px; }
551
- .session-picker { width: 100%; }
552
- .session-picker select { flex: 1; max-width: none; min-width: 0; min-height: 43px; }
553
- .session-actions { width: 100%; }
554
- .session-actions button { flex: 1; min-height: 43px; }
555
- .status-strip { flex-direction: column; align-items: start; gap: 8px; padding: 11px 13px; }
556
- .classification-status { max-width: none; font-size: 10px; gap: 6px; }
557
- #capture-note { flex-basis: 100%; padding-left: 13px; }
558
- .coverage-status { flex-direction: row; text-align: left; gap: 5px 14px; font-size: 10px; max-width: none; }
559
- .drawing-toolbar { padding: 10px 10px 0; gap: 7px; align-items: start; }
560
- .layout-toolbar { padding: 10px 12px 3px; gap: 9px; }
561
- .layout-toolbar select, .layout-toolbar button { min-height: 40px; }
562
- .auto-arrange { min-height: 30px; }
563
- .shape-key { padding-right: 12px; padding-left: 12px; }
564
- .drawing-heading { display: flex; flex-direction: column; padding-top: 8px; gap: 3px; }
565
- .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; }
566
563
  #revision { font-size: 10px; }
567
- .zoom-controls { gap: 0; }
568
- .zoom-controls button { min-width: 30px; min-height: 44px; }
569
- .zoom-controls #fit { min-width: 36px; min-height: 36px; margin-left: 2px; }
570
- #zoom-level { min-width: 31px; font-size: 9px; }
571
- .diagram-stage { height: 350px; min-height: 350px; }
572
- .empty-canvas { padding: 24px; }
573
- .empty-canvas h3 { font-size: 21px; }
574
- .empty-canvas p { font-size: 12px; }
575
- .legend { gap: 7px 13px; }
576
- .drawing-footer { padding: 9px 12px 5px; gap: 8px; font-size: 9px; }
577
- .diagram-note { padding: 0 12px 12px; font-size: 9px; }
578
- .inspector { padding: 13px 16px; }
579
- .playback { padding: 12px; gap: 9px; grid-template-columns: 1fr; }
580
- .mode-buttons { justify-self: start; }
581
- .mode-buttons button { min-height: 40px; min-width: 63px; }
582
- .scrubber input { min-height: 35px; }
583
- #replay-note { grid-column: 1; margin-top: -3px; }
584
- .activity-heading h2 { font-size: 16px; }
585
- .activity-heading p { font-size: 10px; max-width: 30ch; }
586
- .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; }
587
568
  .activity-row time, .event-state { font-size: 9px; }
588
- .activity-empty { padding: 18px; font-size: 11px; }
589
- footer { flex-direction: column; gap: 5px; margin-top: 22px; }
590
569
  }
591
570
  @media (prefers-reduced-motion: reduce) {
592
571
  *, *::before, *::after { scroll-behavior: auto !important; animation: none !important; transition: none !important; }
@@ -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.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({});