opensteer 0.9.1 → 0.9.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.
Files changed (34) hide show
  1. package/README.md +0 -3
  2. package/dist/{chunk-4LP7QP2O.js → chunk-2TIVULZY.js} +3 -236
  3. package/dist/chunk-2TIVULZY.js.map +1 -0
  4. package/dist/{chunk-Z53HNZ7Z.js → chunk-FIMNKEG5.js} +3 -3
  5. package/dist/{chunk-Z53HNZ7Z.js.map → chunk-FIMNKEG5.js.map} +1 -1
  6. package/dist/{chunk-6PGXWW3X.js → chunk-GREXSYNC.js} +120 -2047
  7. package/dist/chunk-GREXSYNC.js.map +1 -0
  8. package/dist/{chunk-L4FWHBQJ.js → chunk-UM2Q4JD2.js} +5 -36
  9. package/dist/chunk-UM2Q4JD2.js.map +1 -0
  10. package/dist/cli/bin.cjs +422 -2616
  11. package/dist/cli/bin.cjs.map +1 -1
  12. package/dist/cli/bin.js +5 -5
  13. package/dist/cli/bin.js.map +1 -1
  14. package/dist/index.cjs +170 -2361
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +59 -757
  17. package/dist/index.d.ts +59 -757
  18. package/dist/index.js +3 -3
  19. package/dist/local-view/public/assets/app.css +219 -55
  20. package/dist/local-view/public/assets/app.js +58 -2
  21. package/dist/local-view/public/index.html +101 -26
  22. package/dist/local-view/serve-entry.cjs +2 -235
  23. package/dist/local-view/serve-entry.cjs.map +1 -1
  24. package/dist/local-view/serve-entry.js +1 -1
  25. package/dist/opensteer-IBDPRIEX.js +6 -0
  26. package/dist/{opensteer-KZCRP425.js.map → opensteer-IBDPRIEX.js.map} +1 -1
  27. package/dist/{session-control-VGBFOH3Y.js → session-control-IFE3IPS3.js} +3 -3
  28. package/dist/{session-control-VGBFOH3Y.js.map → session-control-IFE3IPS3.js.map} +1 -1
  29. package/package.json +6 -6
  30. package/skills/opensteer/SKILL.md +1 -1
  31. package/dist/chunk-4LP7QP2O.js.map +0 -1
  32. package/dist/chunk-6PGXWW3X.js.map +0 -1
  33. package/dist/chunk-L4FWHBQJ.js.map +0 -1
  34. package/dist/opensteer-KZCRP425.js +0 -6
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import './chunk-KCINASQC.js';
2
- export { Opensteer } from './chunk-L4FWHBQJ.js';
3
- export { CloudSessionProxy, DEFERRED_MATCH_ATTR_KEYS, ElementPathError, MATCH_ATTRIBUTE_PRIORITY, OPENSTEER_DOM_ACTION_BRIDGE_SYMBOL, OpensteerCloudClient, OpensteerRuntime, OpensteerSessionRuntime, STABLE_PRIMARY_ATTR_KEYS, assertProviderSupportsEngine, buildArrayFieldPathCandidates, buildDomDescriptorKey, buildDomDescriptorPayload, buildDomDescriptorVersion, buildPathCandidates, buildPathSelectorHint, buildSegmentSelector, cloneElementPath, cloneReplayElementPath, cloneStructuralElementAnchor, createDomDescriptorStore, createDomRuntime, createOpensteerExtractionDescriptorStore, createOpensteerSemanticRuntime, defaultFallbackPolicy, defaultPolicy, defaultRetryPolicy, defaultSettlePolicy, defaultTimeoutPolicy, delayWithSignal, dispatchSemanticOperation, hashDomDescriptorPersist, isCurrentUrlField, isValidCssAttributeKey, normalizeExtractedValue, normalizeOpensteerProviderMode, parseDomDescriptorRecord, parseExtractionDescriptorRecord, resolveCloudConfig, resolveDomActionBridge, resolveExtractedValueInContext, resolveOpensteerProvider, resolveOpensteerRuntimeConfig, runWithPolicyTimeout, sanitizeElementPath, sanitizeReplayElementPath, sanitizeStructuralElementAnchor, settleWithPolicy, shouldKeepAttributeForPath } from './chunk-6PGXWW3X.js';
4
- export { DEFAULT_OPENSTEER_ENGINE, OPENSTEER_ENGINE_NAMES, OPENSTEER_FILESYSTEM_WORKSPACE_LAYOUT, OPENSTEER_FILESYSTEM_WORKSPACE_VERSION, OpensteerBrowserManager, createArtifactStore, createFilesystemOpensteerWorkspace, createObservationStore, manifestToExternalBinaryLocation, normalizeObservabilityConfig, normalizeOpensteerEngineName, normalizeWorkspaceId, resolveFilesystemWorkspacePath, resolveOpensteerEngineName } from './chunk-4LP7QP2O.js';
2
+ export { Opensteer } from './chunk-UM2Q4JD2.js';
3
+ export { CloudSessionProxy, DEFERRED_MATCH_ATTR_KEYS, ElementPathError, MATCH_ATTRIBUTE_PRIORITY, OPENSTEER_DOM_ACTION_BRIDGE_SYMBOL, OpensteerCloudClient, OpensteerRuntime, OpensteerSessionRuntime, STABLE_PRIMARY_ATTR_KEYS, assertProviderSupportsEngine, buildArrayFieldPathCandidates, buildDomDescriptorKey, buildDomDescriptorPayload, buildDomDescriptorVersion, buildPathCandidates, buildPathSelectorHint, buildSegmentSelector, cloneElementPath, cloneReplayElementPath, cloneStructuralElementAnchor, createDomDescriptorStore, createDomRuntime, createOpensteerExtractionDescriptorStore, createOpensteerSemanticRuntime, defaultFallbackPolicy, defaultPolicy, defaultRetryPolicy, defaultSettlePolicy, defaultTimeoutPolicy, delayWithSignal, dispatchSemanticOperation, hashDomDescriptorPersist, isCurrentUrlField, isValidCssAttributeKey, normalizeExtractedValue, normalizeOpensteerProviderMode, parseDomDescriptorRecord, parseExtractionDescriptorRecord, resolveCloudConfig, resolveDomActionBridge, resolveExtractedValueInContext, resolveOpensteerProvider, resolveOpensteerRuntimeConfig, runWithPolicyTimeout, sanitizeElementPath, sanitizeReplayElementPath, sanitizeStructuralElementAnchor, settleWithPolicy, shouldKeepAttributeForPath } from './chunk-GREXSYNC.js';
4
+ export { DEFAULT_OPENSTEER_ENGINE, OPENSTEER_ENGINE_NAMES, OPENSTEER_FILESYSTEM_WORKSPACE_LAYOUT, OPENSTEER_FILESYSTEM_WORKSPACE_VERSION, OpensteerBrowserManager, createArtifactStore, createFilesystemOpensteerWorkspace, createObservationStore, manifestToExternalBinaryLocation, normalizeObservabilityConfig, normalizeOpensteerEngineName, normalizeWorkspaceId, resolveFilesystemWorkspacePath, resolveOpensteerEngineName } from './chunk-2TIVULZY.js';
5
5
  export { OpensteerAttachAmbiguousError, clearPersistedSessionRecord, discoverLocalCdpBrowsers, inspectCdpEndpoint, listLocalChromeProfiles, readPersistedCloudSessionRecord, readPersistedLocalBrowserSessionRecord, readPersistedSessionRecord, resolveCloudSessionRecordPath, resolveLiveSessionRecordPath, resolveLocalSessionRecordPath, writePersistedSessionRecord } from './chunk-BMPUL66S.js';
6
6
  //# sourceMappingURL=index.js.map
7
7
  //# sourceMappingURL=index.js.map
@@ -16,6 +16,7 @@
16
16
  --accent-foreground: #000000;
17
17
  --destructive: #ef4444;
18
18
  --ring: rgba(255, 255, 255, 0.4);
19
+ --top-bar-height: 44px;
19
20
  }
20
21
 
21
22
  /* ─── Reset ─── */
@@ -34,14 +35,7 @@ body {
34
35
  overflow: hidden;
35
36
  background: var(--background);
36
37
  color: var(--foreground);
37
- font-family:
38
- Inter,
39
- ui-sans-serif,
40
- system-ui,
41
- -apple-system,
42
- BlinkMacSystemFont,
43
- "Segoe UI",
44
- sans-serif;
38
+ font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", system-ui, sans-serif;
45
39
  font-size: 14px;
46
40
  line-height: 1.5;
47
41
  -webkit-font-smoothing: antialiased;
@@ -124,34 +118,78 @@ button {
124
118
  height: 100vh;
125
119
  min-height: 0;
126
120
  display: flex;
121
+ flex-direction: column;
127
122
  overflow: hidden;
128
123
  }
129
124
 
130
- /* ─── Sidebar ─── */
125
+ /* ─── Top Bar ─── */
131
126
 
132
- .sidebar {
133
- width: 280px;
127
+ .top-bar {
128
+ height: var(--top-bar-height);
134
129
  flex-shrink: 0;
135
130
  display: flex;
136
- flex-direction: column;
137
- overflow: hidden;
138
- border-right: 1px solid var(--border);
131
+ align-items: center;
132
+ gap: 12px;
133
+ padding: 0 16px;
139
134
  background: var(--surface);
135
+ border-bottom: 1px solid var(--border);
136
+ z-index: 50;
137
+ position: relative;
138
+ }
139
+
140
+ .drawer-toggle {
141
+ position: relative;
142
+ display: inline-flex;
143
+ align-items: center;
144
+ justify-content: center;
145
+ width: 32px;
146
+ height: 32px;
147
+ border: none;
148
+ border-radius: 6px;
149
+ background: transparent;
150
+ color: var(--muted-foreground);
151
+ padding: 0;
152
+ flex-shrink: 0;
153
+ transition:
154
+ background 150ms ease,
155
+ color 150ms ease;
156
+ }
157
+
158
+ .drawer-toggle:hover {
159
+ background: var(--muted);
160
+ color: var(--foreground);
140
161
  }
141
162
 
142
- .brand-block {
163
+ .drawer-toggle:active {
164
+ background: var(--border-strong);
165
+ }
166
+
167
+ .session-count-badge {
168
+ position: absolute;
169
+ top: 1px;
170
+ right: 1px;
171
+ min-width: 16px;
172
+ height: 16px;
173
+ border-radius: 8px;
174
+ background: var(--accent);
175
+ color: var(--accent-foreground);
176
+ font-size: 9px;
177
+ font-weight: 600;
143
178
  display: flex;
144
179
  align-items: center;
145
- justify-content: space-between;
146
- gap: 12px;
147
- padding: 16px 16px 14px;
148
- border-bottom: 1px solid var(--border);
180
+ justify-content: center;
181
+ padding: 0 4px;
182
+ line-height: 1;
183
+ pointer-events: none;
149
184
  }
150
185
 
186
+ /* ─── Brand ─── */
187
+
151
188
  .brand-row {
152
189
  display: inline-flex;
153
190
  align-items: center;
154
191
  gap: 8px;
192
+ flex-shrink: 0;
155
193
  }
156
194
 
157
195
  .brand-icon {
@@ -182,6 +220,80 @@ button {
182
220
  letter-spacing: 0.04em;
183
221
  }
184
222
 
223
+ .top-bar-divider {
224
+ width: 1px;
225
+ height: 20px;
226
+ background: var(--border-strong);
227
+ flex-shrink: 0;
228
+ }
229
+
230
+ /* ─── Active Session Indicator ─── */
231
+
232
+ .active-session-indicator {
233
+ display: inline-flex;
234
+ align-items: center;
235
+ gap: 8px;
236
+ border: 1px solid var(--border);
237
+ border-radius: 8px;
238
+ background: var(--surface-elevated);
239
+ color: var(--foreground);
240
+ padding: 5px 12px;
241
+ font-size: 12px;
242
+ font-weight: 500;
243
+ white-space: nowrap;
244
+ overflow: hidden;
245
+ max-width: 300px;
246
+ transition:
247
+ background 150ms ease,
248
+ border-color 150ms ease;
249
+ }
250
+
251
+ .active-session-indicator:hover {
252
+ background: var(--surface-raised);
253
+ border-color: var(--border-strong);
254
+ }
255
+
256
+ .active-session-indicator:active {
257
+ background: rgba(255, 255, 255, 0.08);
258
+ }
259
+
260
+ .active-session-dot {
261
+ flex-shrink: 0;
262
+ width: 6px;
263
+ height: 6px;
264
+ border-radius: 50%;
265
+ background: var(--muted-foreground);
266
+ opacity: 0.4;
267
+ transition: all 200ms ease;
268
+ }
269
+
270
+ .active-session-dot.is-active {
271
+ background: #28c840;
272
+ opacity: 1;
273
+ animation: pulse-dot 1.5s ease-in-out infinite;
274
+ }
275
+
276
+ .active-session-label {
277
+ overflow: hidden;
278
+ text-overflow: ellipsis;
279
+ white-space: nowrap;
280
+ min-width: 0;
281
+ }
282
+
283
+ .active-session-chevron {
284
+ flex-shrink: 0;
285
+ color: var(--muted-foreground);
286
+ opacity: 0.5;
287
+ transition: transform 200ms ease;
288
+ }
289
+
290
+ .top-bar-spacer {
291
+ flex: 1;
292
+ min-width: 0;
293
+ }
294
+
295
+ /* ─── View Toggle ─── */
296
+
185
297
  .local-view-toggle {
186
298
  flex-shrink: 0;
187
299
  height: 26px;
@@ -211,52 +323,100 @@ button {
211
323
  opacity: 0.6;
212
324
  }
213
325
 
214
- /* ─── Sidebar Tabs ─── */
326
+ /* ─── Session Drawer ─── */
215
327
 
216
- .sidebar-tab-bar {
217
- padding: 12px 16px;
218
- border-bottom: 1px solid var(--border);
328
+ .drawer-backdrop {
329
+ position: fixed;
330
+ top: var(--top-bar-height);
331
+ left: 0;
332
+ right: 0;
333
+ bottom: 0;
334
+ background: rgba(0, 0, 0, 0.5);
335
+ backdrop-filter: blur(4px);
336
+ -webkit-backdrop-filter: blur(4px);
337
+ opacity: 0;
338
+ pointer-events: none;
339
+ transition: opacity 280ms ease;
340
+ z-index: 90;
341
+ }
342
+
343
+ .drawer-backdrop.is-visible {
344
+ opacity: 1;
345
+ pointer-events: auto;
219
346
  }
220
347
 
221
- .sidebar-tab-bar-inner {
348
+ .session-drawer {
349
+ position: fixed;
350
+ top: var(--top-bar-height);
351
+ left: 0;
352
+ bottom: 0;
353
+ width: 320px;
222
354
  display: flex;
223
- border-radius: 8px;
224
- border: 1px solid rgba(255, 255, 255, 0.06);
225
- background: var(--surface-elevated);
226
- padding: 3px;
355
+ flex-direction: column;
356
+ background: var(--surface);
357
+ border-right: 1px solid var(--border);
358
+ transform: translateX(-100%);
359
+ transition:
360
+ transform 280ms cubic-bezier(0, 0, 0.2, 1),
361
+ box-shadow 280ms ease;
362
+ z-index: 100;
363
+ overflow: hidden;
227
364
  }
228
365
 
229
- .sidebar-tab {
230
- flex: 1;
366
+ .session-drawer.is-open {
367
+ transform: translateX(0);
368
+ box-shadow: 8px 0 32px rgba(0, 0, 0, 0.4);
369
+ }
370
+
371
+ .drawer-header {
372
+ display: flex;
373
+ align-items: center;
374
+ justify-content: space-between;
375
+ padding: 14px 16px;
376
+ border-bottom: 1px solid var(--border);
377
+ flex-shrink: 0;
378
+ }
379
+
380
+ .drawer-title {
381
+ font-size: 13px;
382
+ font-weight: 600;
383
+ color: var(--foreground);
384
+ letter-spacing: 0.02em;
385
+ }
386
+
387
+ .drawer-close {
388
+ display: inline-flex;
389
+ align-items: center;
390
+ justify-content: center;
391
+ width: 28px;
392
+ height: 28px;
231
393
  border: none;
232
394
  border-radius: 6px;
233
- padding: 6px 12px;
234
- font-size: 12px;
235
- font-weight: 500;
236
- color: var(--muted-foreground);
237
395
  background: transparent;
238
- transition: all 200ms;
396
+ color: var(--muted-foreground);
397
+ padding: 0;
398
+ transition:
399
+ background 150ms ease,
400
+ color 150ms ease;
239
401
  }
240
402
 
241
- .sidebar-tab:hover {
242
- background: rgba(255, 255, 255, 0.03);
403
+ .drawer-close:hover {
404
+ background: var(--muted);
243
405
  color: var(--foreground);
244
406
  }
245
407
 
246
- .sidebar-tab.is-active {
247
- background: var(--surface);
248
- color: var(--foreground);
249
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
408
+ .drawer-close:active {
409
+ background: var(--border-strong);
250
410
  }
251
411
 
252
- /* ─── Session List ─── */
253
-
254
- .sidebar-scroll {
412
+ .drawer-scroll {
255
413
  flex: 1;
256
414
  min-height: 0;
257
415
  overflow-y: auto;
258
416
  }
259
417
 
418
+ /* ─── Session List ─── */
419
+
260
420
  .session-list {
261
421
  display: flex;
262
422
  flex-direction: column;
@@ -383,7 +543,7 @@ button {
383
543
  display: flex;
384
544
  align-items: flex-start;
385
545
  justify-content: center;
386
- padding: 20px;
546
+ padding: 16px;
387
547
  min-height: 0;
388
548
  overflow: hidden;
389
549
  }
@@ -750,21 +910,25 @@ button {
750
910
  /* ─── Responsive ─── */
751
911
 
752
912
  @media (max-width: 768px) {
753
- .app-shell {
754
- flex-direction: column;
913
+ .session-drawer {
914
+ width: 100%;
755
915
  }
756
916
 
757
- .sidebar {
758
- width: 100%;
759
- border-right: none;
760
- border-bottom: 1px solid var(--border);
917
+ .viewer-area {
918
+ padding: 8px;
761
919
  }
920
+ }
762
921
 
763
- .sidebar-scroll {
764
- max-height: 200px;
922
+ @media (max-width: 640px) {
923
+ .brand-name {
924
+ display: none;
765
925
  }
766
926
 
767
- .viewer-area {
768
- padding: 12px;
927
+ .top-bar-divider {
928
+ display: none;
929
+ }
930
+
931
+ .active-session-indicator {
932
+ max-width: 180px;
769
933
  }
770
934
  }
@@ -956,6 +956,7 @@ class LocalViewApp {
956
956
  this.layoutFrame = null;
957
957
  this.lastBrowserFrameWidth = null;
958
958
  this.lastStreamAspect = null;
959
+ this.drawerOpen = false;
959
960
 
960
961
  this.viewerAreaEl = document.querySelector(".viewer-area");
961
962
  this.browserFrameEl = document.querySelector(".browser-frame");
@@ -978,6 +979,14 @@ class LocalViewApp {
978
979
  this.newTabButtonEl = document.getElementById("new-tab-button");
979
980
  this.closeBrowserButtonEl = document.getElementById("close-browser-button");
980
981
  this.stopViewButtonEl = document.getElementById("stop-view-button");
982
+ this.drawerToggleEl = document.getElementById("drawer-toggle");
983
+ this.drawerCloseEl = document.getElementById("drawer-close");
984
+ this.drawerBackdropEl = document.getElementById("drawer-backdrop");
985
+ this.sessionDrawerEl = document.getElementById("session-drawer");
986
+ this.activeSessionIndicatorEl = document.getElementById("active-session-indicator");
987
+ this.activeSessionLabelEl = document.getElementById("active-session-label");
988
+ this.activeSessionDotEl = document.getElementById("active-session-dot");
989
+ this.sessionCountBadgeEl = document.getElementById("session-count-badge");
981
990
 
982
991
  this.stream = new LocalBrowserStream(() => this.render());
983
992
  this.cdp = new LocalCdpConnection(() => this.render());
@@ -1007,6 +1016,30 @@ class LocalViewApp {
1007
1016
  }
1008
1017
  }
1009
1018
 
1019
+ openDrawer() {
1020
+ this.drawerOpen = true;
1021
+ this.sessionDrawerEl.classList.add("is-open");
1022
+ this.drawerBackdropEl.classList.add("is-visible");
1023
+ this.drawerToggleEl.setAttribute("aria-expanded", "true");
1024
+ this.activeSessionIndicatorEl.setAttribute("aria-expanded", "true");
1025
+ }
1026
+
1027
+ closeDrawer() {
1028
+ this.drawerOpen = false;
1029
+ this.sessionDrawerEl.classList.remove("is-open");
1030
+ this.drawerBackdropEl.classList.remove("is-visible");
1031
+ this.drawerToggleEl.setAttribute("aria-expanded", "false");
1032
+ this.activeSessionIndicatorEl.setAttribute("aria-expanded", "false");
1033
+ }
1034
+
1035
+ toggleDrawer() {
1036
+ if (this.drawerOpen) {
1037
+ this.closeDrawer();
1038
+ } else {
1039
+ this.openDrawer();
1040
+ }
1041
+ }
1042
+
1010
1043
  bindUi() {
1011
1044
  window.addEventListener("hashchange", () => {
1012
1045
  const hashSessionId = resolveSelectedSessionIdFromHash();
@@ -1015,12 +1048,24 @@ class LocalViewApp {
1015
1048
  }
1016
1049
  });
1017
1050
 
1051
+ this.drawerToggleEl.addEventListener("click", () => this.toggleDrawer());
1052
+ this.drawerCloseEl.addEventListener("click", () => this.closeDrawer());
1053
+ this.drawerBackdropEl.addEventListener("click", () => this.closeDrawer());
1054
+ this.activeSessionIndicatorEl.addEventListener("click", () => this.toggleDrawer());
1055
+
1056
+ document.addEventListener("keydown", (event) => {
1057
+ if (event.key === "Escape" && this.drawerOpen) {
1058
+ this.closeDrawer();
1059
+ }
1060
+ });
1061
+
1018
1062
  this.sessionListEl.addEventListener("click", (event) => {
1019
1063
  const button = event.target.closest("button[data-session-id]");
1020
1064
  if (!button) {
1021
1065
  return;
1022
1066
  }
1023
1067
  this.selectSession(button.dataset.sessionId ?? null);
1068
+ this.closeDrawer();
1024
1069
  });
1025
1070
 
1026
1071
  this.tabStripEl.addEventListener("click", (event) => {
@@ -1182,10 +1227,10 @@ class LocalViewApp {
1182
1227
  );
1183
1228
 
1184
1229
  this.viewerSurfaceEl.addEventListener("keydown", (event) => {
1185
- const payload = createCdpKeyDownPayload(event);
1186
- if (!payload) {
1230
+ if (event.key === "Escape" && this.drawerOpen) {
1187
1231
  return;
1188
1232
  }
1233
+ const payload = createCdpKeyDownPayload(event);
1189
1234
  event.preventDefault();
1190
1235
  void this.dispatchPointerCommand("Input.dispatchKeyEvent", payload);
1191
1236
  });
@@ -1460,6 +1505,17 @@ class LocalViewApp {
1460
1505
  }
1461
1506
 
1462
1507
  renderSessions() {
1508
+ const activeSession = this.sessions.find((s) => s.sessionId === this.selectedSessionId) ?? null;
1509
+ this.activeSessionLabelEl.textContent = activeSession
1510
+ ? activeSession.label
1511
+ : "No session selected";
1512
+ this.activeSessionDotEl.className = activeSession
1513
+ ? "active-session-dot is-active"
1514
+ : "active-session-dot";
1515
+ const sessionCount = this.sessions.length;
1516
+ this.sessionCountBadgeEl.textContent = String(sessionCount);
1517
+ this.sessionCountBadgeEl.hidden = sessionCount === 0;
1518
+
1463
1519
  this.sessionListEl.textContent = "";
1464
1520
  if (this.sessions.length === 0) {
1465
1521
  const empty = document.createElement("div");
@@ -8,37 +8,112 @@
8
8
  </head>
9
9
  <body>
10
10
  <div class="app-shell">
11
- <aside class="sidebar">
12
- <div class="brand-block">
13
- <div class="brand-row">
14
- <svg class="brand-icon" viewBox="0 0 64 64" role="img" aria-label="Opensteer">
15
- <defs>
16
- <radialGradient id="opensteer-brand-fill" cx="34%" cy="22%" r="78%">
17
- <stop offset="0" stop-color="#ffffff" />
18
- <stop offset="0.58" stop-color="#eee9e4" />
19
- <stop offset="1" stop-color="#efc6ba" />
20
- </radialGradient>
21
- </defs>
22
- <circle cx="32" cy="32" r="25" fill="url(#opensteer-brand-fill)" />
23
- </svg>
24
- <span class="brand-name">Opensteer</span>
25
- <span class="brand-badge">Local</span>
26
- </div>
11
+ <header class="top-bar">
12
+ <button
13
+ id="drawer-toggle"
14
+ class="drawer-toggle"
15
+ type="button"
16
+ aria-label="Toggle sessions panel"
17
+ aria-controls="session-drawer"
18
+ aria-expanded="false"
19
+ >
20
+ <svg
21
+ width="18"
22
+ height="18"
23
+ viewBox="0 0 24 24"
24
+ fill="none"
25
+ stroke="currentColor"
26
+ stroke-width="2"
27
+ stroke-linecap="round"
28
+ stroke-linejoin="round"
29
+ >
30
+ <line x1="3" y1="6" x2="21" y2="6" />
31
+ <line x1="3" y1="12" x2="21" y2="12" />
32
+ <line x1="3" y1="18" x2="21" y2="18" />
33
+ </svg>
34
+ <span id="session-count-badge" class="session-count-badge" hidden>0</span>
35
+ </button>
36
+
37
+ <div class="brand-row">
38
+ <svg class="brand-icon" viewBox="0 0 64 64" role="img" aria-label="Opensteer">
39
+ <defs>
40
+ <radialGradient id="opensteer-brand-fill" cx="34%" cy="22%" r="78%">
41
+ <stop offset="0" stop-color="#ffffff" />
42
+ <stop offset="0.58" stop-color="#eee9e4" />
43
+ <stop offset="1" stop-color="#efc6ba" />
44
+ </radialGradient>
45
+ </defs>
46
+ <circle cx="32" cy="32" r="25" fill="url(#opensteer-brand-fill)" />
47
+ </svg>
48
+ <span class="brand-name">Opensteer</span>
49
+ <span class="brand-badge">Local</span>
50
+ </div>
51
+
52
+ <div class="top-bar-divider"></div>
53
+
54
+ <button
55
+ id="active-session-indicator"
56
+ class="active-session-indicator"
57
+ type="button"
58
+ aria-label="Switch session"
59
+ aria-controls="session-drawer"
60
+ aria-expanded="false"
61
+ >
62
+ <span id="active-session-dot" class="active-session-dot"></span>
63
+ <span id="active-session-label" class="active-session-label">No session selected</span>
64
+ <svg
65
+ class="active-session-chevron"
66
+ width="12"
67
+ height="12"
68
+ viewBox="0 0 24 24"
69
+ fill="none"
70
+ stroke="currentColor"
71
+ stroke-width="2"
72
+ stroke-linecap="round"
73
+ stroke-linejoin="round"
74
+ >
75
+ <polyline points="6 9 12 15 18 9" />
76
+ </svg>
77
+ </button>
78
+
79
+ <div class="top-bar-spacer"></div>
80
+
81
+ <button
82
+ id="stop-view-button"
83
+ class="local-view-toggle"
84
+ type="button"
85
+ data-testid="stop-view-button"
86
+ >
87
+ Turn View Off
88
+ </button>
89
+ </header>
90
+
91
+ <div id="drawer-backdrop" class="drawer-backdrop"></div>
92
+ <aside id="session-drawer" class="session-drawer">
93
+ <div class="drawer-header">
94
+ <span class="drawer-title">Sessions</span>
27
95
  <button
28
- id="stop-view-button"
29
- class="local-view-toggle"
96
+ id="drawer-close"
97
+ class="drawer-close"
30
98
  type="button"
31
- data-testid="stop-view-button"
99
+ aria-label="Close sessions panel"
32
100
  >
33
- Turn View Off
101
+ <svg
102
+ width="16"
103
+ height="16"
104
+ viewBox="0 0 24 24"
105
+ fill="none"
106
+ stroke="currentColor"
107
+ stroke-width="2"
108
+ stroke-linecap="round"
109
+ stroke-linejoin="round"
110
+ >
111
+ <line x1="18" y1="6" x2="6" y2="18" />
112
+ <line x1="6" y1="6" x2="18" y2="18" />
113
+ </svg>
34
114
  </button>
35
115
  </div>
36
- <div class="sidebar-tab-bar">
37
- <div class="sidebar-tab-bar-inner">
38
- <button class="sidebar-tab is-active" type="button">Sessions</button>
39
- </div>
40
- </div>
41
- <div class="sidebar-scroll">
116
+ <div class="drawer-scroll">
42
117
  <div id="session-list" class="session-list" data-testid="session-list"></div>
43
118
  </div>
44
119
  </aside>