loki-mode 7.91.1 → 7.93.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -134,10 +134,17 @@
134
134
  font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11';
135
135
  }
136
136
 
137
- /* Dashboard Layout */
137
+ /* Dashboard Layout. v7.92 sidebar rebalance: a THREE-column grid -- a clean
138
+ left navigation rail, the main content, and a collapsible right status
139
+ sidebar. The left column is navigation only; the system status + session
140
+ controls moved out of the cramped left footer into the right column so
141
+ both surfaces can breathe. The third track is sized by a CSS var so the
142
+ same rule drives expanded (288px) and collapsed (a 48px rail). */
138
143
  .dashboard-layout {
144
+ --status-rail-width: 48px;
145
+ --status-panel-width: 288px;
139
146
  display: grid;
140
- grid-template-columns: 240px 1fr;
147
+ grid-template-columns: 240px 1fr var(--status-panel-width);
141
148
  grid-template-rows: 1fr;
142
149
  /* Pin to the viewport (not min-height) so the grid row can never grow
143
150
  taller than the screen. With min-height the 1fr row stretched to the
@@ -150,8 +157,16 @@
150
157
  overflow: hidden;
151
158
  }
152
159
 
160
+ /* Collapsed: the status sidebar shrinks to a thin rail and the center
161
+ content reclaims the freed width. Driven by a class on the GRID (not just
162
+ the aside) so the track resizes in lockstep with the panel. */
163
+ .dashboard-layout.status-collapsed {
164
+ grid-template-columns: 240px 1fr var(--status-rail-width);
165
+ }
166
+
153
167
  @media (max-width: 768px) {
154
- .dashboard-layout {
168
+ .dashboard-layout,
169
+ .dashboard-layout.status-collapsed {
155
170
  grid-template-columns: 1fr;
156
171
  }
157
172
  .sidebar { display: none; }
@@ -162,6 +177,18 @@
162
177
  width: 240px;
163
178
  z-index: 100;
164
179
  }
180
+ /* On mobile the third column would steal the whole row; collapse it to a
181
+ pinned rail on the right edge instead so status stays one tap away
182
+ without crowding the content. */
183
+ .status-sidebar {
184
+ position: fixed;
185
+ right: 0; top: 0; bottom: 0;
186
+ width: var(--status-rail-width);
187
+ z-index: 90;
188
+ }
189
+ .dashboard-layout:not(.status-collapsed) .status-sidebar {
190
+ width: min(320px, 88vw);
191
+ }
165
192
  }
166
193
 
167
194
  /* Sidebar - glass effect. v7.84 enterprise IA: a three-region flex column
@@ -352,51 +379,56 @@
352
379
  flex-shrink: 0;
353
380
  }
354
381
 
355
- /* Sidebar footer: anchored bottom region. v7.84 enterprise pass -- the dev
356
- "API URL + Go" control is no longer always-visible clutter; it now lives
357
- behind a small Settings (gear) popover. The footer shows only the
358
- intentional controls: session control, Settings, and the theme toggle. */
359
- .sidebar-footer {
360
- padding: 10px 12px 12px;
361
- border-top: 1px solid var(--loki-border);
382
+ /* (v7.92) The left sidebar footer was removed: the session control + the
383
+ Settings gear it used to hold moved into the right status sidebar, so the
384
+ left column is navigation only.
385
+
386
+ (v7.93) The Settings gear + upward-opening popover were replaced by an
387
+ inline collapsible disclosure that lives in the right sidebar's scrolling
388
+ body (.status-settings). Inline-in-flow has no anchoring/clipping/z-index
389
+ failure modes, is keyboard-accessible, and matches the collapsible-sidebar
390
+ design language. The disclosure header is a real button (aria-expanded +
391
+ aria-controls); the region is hidden from the tab order while collapsed. */
392
+
393
+ /* Inline Settings disclosure (right-sidebar, in-flow). */
394
+ .status-settings {
362
395
  flex: 0 0 auto;
396
+ border: 1px solid var(--loki-border);
397
+ border-radius: 8px;
398
+ background: var(--loki-bg-tertiary);
399
+ overflow: hidden;
363
400
  }
364
401
 
365
- .sidebar-controls {
402
+ /* Disclosure header: a full-width real button. The chevron rotates to point
403
+ down when the section is open. */
404
+ .settings-disclosure {
366
405
  display: flex;
367
- gap: 6px;
368
406
  align-items: center;
369
- padding: 8px 0 0;
370
- }
371
-
372
- /* Icon-style footer buttons (settings gear + theme). The theme toggle keeps
373
- its text label; both share the muted-chip resting style. */
374
- .footer-btn {
375
- display: inline-flex;
376
- align-items: center;
377
- gap: 6px;
378
- padding: 6px 10px;
379
- background: var(--loki-bg-tertiary);
380
- border: 1px solid var(--loki-border);
381
- border-radius: 7px;
407
+ gap: 8px;
408
+ width: 100%;
409
+ box-sizing: border-box;
410
+ padding: 9px 11px;
411
+ background: transparent;
412
+ border: none;
413
+ border-radius: 8px;
414
+ font-family: 'Inter', system-ui, sans-serif;
382
415
  font-size: 11px;
416
+ font-weight: 600;
417
+ text-transform: uppercase;
418
+ letter-spacing: 0.06em;
383
419
  color: var(--loki-text-secondary);
384
420
  cursor: pointer;
385
- transition: all var(--loki-transition);
386
- font-family: inherit;
421
+ transition: background var(--loki-transition), color var(--loki-transition);
387
422
  }
388
-
389
- .footer-btn:hover {
423
+ .settings-disclosure:hover {
390
424
  background: var(--loki-bg-hover);
391
425
  color: var(--loki-text-primary);
392
426
  }
393
-
394
- .footer-btn:focus-visible {
427
+ .settings-disclosure:focus-visible {
395
428
  outline: 2px solid var(--loki-accent);
396
- outline-offset: 1px;
429
+ outline-offset: -2px;
397
430
  }
398
-
399
- .footer-btn svg {
431
+ .settings-disclosure svg {
400
432
  width: 14px;
401
433
  height: 14px;
402
434
  stroke: currentColor;
@@ -404,31 +436,56 @@
404
436
  fill: none;
405
437
  flex-shrink: 0;
406
438
  }
407
-
408
-
409
- .footer-settings {
410
- position: relative;
439
+ .settings-disclosure .settings-gear {
440
+ margin-right: 2px;
441
+ }
442
+ .settings-disclosure .settings-chevron {
443
+ margin-left: auto;
444
+ transition: transform var(--loki-transition);
445
+ }
446
+ .settings-disclosure[aria-expanded="true"] .settings-chevron {
447
+ transform: rotate(90deg);
411
448
  }
412
449
 
413
- /* Settings popover: opens above the gear; holds the (rarely used) API URL
414
- override so it is reachable but never crowds the product chrome. */
415
- .settings-popover {
416
- display: none;
417
- position: absolute;
418
- bottom: calc(100% + 8px);
419
- left: 0;
420
- width: 232px;
421
- padding: 12px;
422
- background: var(--loki-bg-card);
423
- border: 1px solid var(--loki-border);
424
- border-radius: 10px;
425
- box-shadow: var(--loki-glass-shadow);
426
- z-index: 50;
450
+ /* Collapsible region. Uses the grid 0fr/1fr trick for a smooth height
451
+ transition; the inner wrapper is the actual clipping box. While collapsed
452
+ the region carries the [hidden] attribute (set by JS). We override the
453
+ default display:none [hidden] gives so the height can animate, but pair it
454
+ with visibility:hidden on the inner wrapper, which removes the input + Go
455
+ button from the tab order while collapsed (a plain 0fr collapse would
456
+ leave them tab-focusable). visibility is delayed on collapse and reset on
457
+ expand so the controls are reachable exactly when the section is open. */
458
+ .settings-region {
459
+ display: grid;
460
+ grid-template-rows: 1fr;
461
+ transition: grid-template-rows var(--loki-transition);
427
462
  }
428
- .settings-popover.open {
429
- display: block;
463
+ .settings-region[hidden] {
464
+ display: grid;
465
+ grid-template-rows: 0fr;
466
+ }
467
+ .settings-region-inner {
468
+ overflow: hidden;
469
+ min-height: 0;
470
+ visibility: visible;
471
+ transition: visibility 0s linear 0s;
472
+ }
473
+ .settings-region[hidden] .settings-region-inner {
474
+ visibility: hidden;
475
+ transition: visibility 0s linear 0.2s;
476
+ }
477
+ @media (prefers-reduced-motion: reduce) {
478
+ .settings-region[hidden] .settings-region-inner { transition: none; }
430
479
  }
431
- .settings-popover-label {
480
+ .settings-region-body {
481
+ padding: 4px 11px 11px;
482
+ }
483
+ @media (prefers-reduced-motion: reduce) {
484
+ .settings-region { transition: none; }
485
+ .settings-disclosure .settings-chevron { transition: none; }
486
+ }
487
+ .settings-field-label {
488
+ display: block;
432
489
  font-size: 9.5px;
433
490
  font-weight: 600;
434
491
  text-transform: uppercase;
@@ -436,7 +493,7 @@
436
493
  color: var(--loki-text-muted);
437
494
  margin-bottom: 6px;
438
495
  }
439
- .settings-popover-row {
496
+ .settings-field-row {
440
497
  display: flex;
441
498
  gap: 6px;
442
499
  align-items: center;
@@ -610,6 +667,147 @@
610
667
  cursor: default;
611
668
  }
612
669
 
670
+ /* Right status sidebar (v7.92). Mirrors the left sidebar's glass treatment
671
+ and three-region flex column (anchored header + scrolling body), but on
672
+ the right edge and collapsible. It owns its own scroll (min-height:0 +
673
+ overflow-y:auto on the body) so the tall status panel can never grow the
674
+ grid row and reintroduce the window-scroll bug documented above. */
675
+ .status-sidebar {
676
+ display: flex;
677
+ flex-direction: column;
678
+ background: var(--loki-glass-bg);
679
+ backdrop-filter: blur(16px) saturate(1.4);
680
+ -webkit-backdrop-filter: blur(16px) saturate(1.4);
681
+ border-left: 1px solid var(--loki-glass-border);
682
+ overflow: hidden;
683
+ min-height: 0;
684
+ height: 100vh;
685
+ }
686
+
687
+ /* Anchored header: a small label + the collapse toggle. Does not scroll. */
688
+ .status-header {
689
+ display: flex;
690
+ align-items: center;
691
+ justify-content: space-between;
692
+ gap: 8px;
693
+ padding: 16px 14px 12px;
694
+ flex: 0 0 auto;
695
+ border-bottom: 1px solid var(--loki-border-light);
696
+ }
697
+ .status-header-title {
698
+ font-family: 'Inter', system-ui, sans-serif;
699
+ font-size: 10px;
700
+ font-weight: 600;
701
+ text-transform: uppercase;
702
+ letter-spacing: 0.09em;
703
+ color: var(--loki-text-muted);
704
+ white-space: nowrap;
705
+ overflow: hidden;
706
+ text-overflow: ellipsis;
707
+ }
708
+
709
+ /* Collapse/expand toggle: a small square icon button. The chevron points
710
+ right when expanded (click to collapse) and is swapped in the rail. */
711
+ .status-toggle {
712
+ display: inline-flex;
713
+ align-items: center;
714
+ justify-content: center;
715
+ width: 28px;
716
+ height: 28px;
717
+ flex: 0 0 auto;
718
+ padding: 0;
719
+ background: var(--loki-bg-tertiary);
720
+ border: 1px solid var(--loki-border);
721
+ border-radius: 7px;
722
+ color: var(--loki-text-secondary);
723
+ cursor: pointer;
724
+ transition: background var(--loki-transition), color var(--loki-transition);
725
+ }
726
+ .status-toggle:hover {
727
+ background: var(--loki-bg-hover);
728
+ color: var(--loki-text-primary);
729
+ }
730
+ .status-toggle:focus-visible {
731
+ outline: 2px solid var(--loki-accent);
732
+ outline-offset: 1px;
733
+ }
734
+ .status-toggle svg {
735
+ width: 15px;
736
+ height: 15px;
737
+ stroke: currentColor;
738
+ stroke-width: 2;
739
+ fill: none;
740
+ }
741
+
742
+ /* Scrolling body: holds the session-control component + the settings gear.
743
+ The only scroller in this column. */
744
+ .status-body {
745
+ flex: 1 1 auto;
746
+ min-height: 0;
747
+ overflow-y: auto;
748
+ overflow-x: hidden;
749
+ padding: 12px;
750
+ display: flex;
751
+ flex-direction: column;
752
+ gap: 10px;
753
+ }
754
+
755
+ /* Collapsed state: hide the expanded body AND the whole header (its collapse
756
+ chevron is meaningless once collapsed); the rail becomes the sole
757
+ affordance, so there is never a redundant pair of chevrons. */
758
+ .dashboard-layout.status-collapsed .status-body,
759
+ .dashboard-layout.status-collapsed .status-header {
760
+ display: none;
761
+ }
762
+
763
+ /* Collapsed rail: a thin vertical strip with the expand affordance and a
764
+ single glanceable live/connected dot. Hidden while expanded. */
765
+ .status-rail {
766
+ display: none;
767
+ flex-direction: column;
768
+ align-items: center;
769
+ gap: 14px;
770
+ padding: 14px 0;
771
+ flex: 1 1 auto;
772
+ min-height: 0;
773
+ }
774
+ .dashboard-layout.status-collapsed .status-rail {
775
+ display: flex;
776
+ }
777
+ .status-rail-label {
778
+ writing-mode: vertical-rl;
779
+ text-orientation: mixed;
780
+ font-family: 'Inter', system-ui, sans-serif;
781
+ font-size: 9.5px;
782
+ font-weight: 600;
783
+ text-transform: uppercase;
784
+ letter-spacing: 0.12em;
785
+ color: var(--loki-text-muted);
786
+ user-select: none;
787
+ }
788
+ /* Live dot on the rail. Reflects connection state via a class set by JS that
789
+ reads the shared API client (see initStatusSidebar). Never a static dot
790
+ implying liveness it does not have. */
791
+ .status-rail-dot {
792
+ width: 8px;
793
+ height: 8px;
794
+ border-radius: 50%;
795
+ background: var(--loki-text-muted);
796
+ flex: 0 0 auto;
797
+ }
798
+ .status-rail-dot.connected {
799
+ background: var(--loki-success);
800
+ animation: pulse 2s infinite;
801
+ }
802
+ @media (prefers-reduced-motion: reduce) {
803
+ .status-rail-dot.connected { animation: none; }
804
+ }
805
+
806
+ /* (v7.93) The anchored .status-footer that held the settings gear + its
807
+ upward-opening popover was removed: Settings is now an inline disclosure
808
+ inside the scrolling .status-body, so there is no separate footer to
809
+ anchor or hide on collapse. */
810
+
613
811
  /* Main Content */
614
812
  .main-content {
615
813
  padding: 28px 32px;
@@ -927,28 +1125,10 @@
927
1125
  </div>
928
1126
  </nav>
929
1127
 
930
- <div class="sidebar-footer">
931
- <loki-session-control id="session-control"></loki-session-control>
932
- <div class="sidebar-controls">
933
- <!-- Settings (gear) holds the rarely-used API URL override behind a
934
- popover so it no longer clutters the always-visible footer. -->
935
- <div class="footer-settings">
936
- <button class="footer-btn" id="settings-btn" type="button" title="Settings" aria-label="Settings" aria-haspopup="true" aria-expanded="false">
937
- <svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 11-2.83 2.83l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 11-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 11-2.83-2.83l.06-.06a1.65 1.65 0 00.33-1.82 1.65 1.65 0 00-1.51-1H3a2 2 0 110-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 112.83-2.83l.06.06a1.65 1.65 0 001.82.33H9a1.65 1.65 0 001-1.51V3a2 2 0 114 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 112.83 2.83l-.06.06a1.65 1.65 0 00-.33 1.82V9a1.65 1.65 0 001.51 1H21a2 2 0 110 4h-.09a1.65 1.65 0 00-1.51 1z"/></svg>
938
- <span>Settings</span>
939
- </button>
940
- <div class="settings-popover" id="settings-popover" role="dialog" aria-label="Settings">
941
- <div class="settings-popover-label">API URL</div>
942
- <div class="settings-popover-row">
943
- <input type="text" class="api-url-input" id="api-url" placeholder="API URL">
944
- <button class="api-btn" id="connect-btn" type="button">Go</button>
945
- </div>
946
- </div>
947
- </div>
948
- <!-- v7.90.1: dark mode removed (light-only). The theme toggle and its
949
- handler are gone; the dashboard ships a single light theme. -->
950
- </div>
951
- </div>
1128
+ <!-- v7.92 sidebar rebalance: the system status + session controls moved
1129
+ out of this left footer into a dedicated, collapsible RIGHT sidebar
1130
+ (#status-sidebar) so the left column is clean navigation that can
1131
+ breathe. The settings gear moved with them. -->
952
1132
  </aside>
953
1133
 
954
1134
  <!-- Main Content -->
@@ -1414,6 +1594,67 @@
1414
1594
  <loki-wiki-browser id="wiki-browser"></loki-wiki-browser>
1415
1595
  </div>
1416
1596
  </main>
1597
+
1598
+ <!-- v7.92 Right status sidebar: system status + session controls, moved out
1599
+ of the cramped left footer. Collapsible (state remembered per machine;
1600
+ default expanded). Collapsed -> a thin rail with an expand affordance
1601
+ and a single live/connected dot, so the info is never lost. -->
1602
+ <aside class="status-sidebar" id="status-sidebar" aria-label="System status and session controls">
1603
+ <div class="status-header">
1604
+ <span class="status-header-title">Session</span>
1605
+ <button class="status-toggle" id="status-toggle" type="button"
1606
+ aria-controls="status-sidebar" aria-expanded="true"
1607
+ title="Collapse status panel" aria-label="Collapse status panel">
1608
+ <svg viewBox="0 0 24 24" aria-hidden="true"><polyline points="9 18 15 12 9 6"/></svg>
1609
+ </button>
1610
+ </div>
1611
+
1612
+ <!-- Expanded body: the intact session-control component (status rows,
1613
+ Pause/Stop, model selector, Connected/version, agent/task counts) plus
1614
+ the inline Settings disclosure. Its API/polling wiring is preserved by
1615
+ moving the element whole. -->
1616
+ <div class="status-body">
1617
+ <loki-session-control id="session-control"></loki-session-control>
1618
+
1619
+ <!-- Settings: an inline collapsible disclosure (replaces the former
1620
+ floating gear popover, which mis-anchored in this column). The
1621
+ header is a real button driving aria-expanded + aria-controls; the
1622
+ region is [hidden] while collapsed so the API controls stay out of
1623
+ the tab order. Default collapsed - the API override is rarely used. -->
1624
+ <div class="status-settings">
1625
+ <button class="settings-disclosure" id="settings-disclosure" type="button"
1626
+ aria-expanded="false" aria-controls="settings-region">
1627
+ <svg class="settings-gear" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 11-2.83 2.83l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 11-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 11-2.83-2.83l.06-.06a1.65 1.65 0 00.33-1.82 1.65 1.65 0 00-1.51-1H3a2 2 0 110-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 112.83-2.83l.06.06a1.65 1.65 0 001.82.33H9a1.65 1.65 0 001-1.51V3a2 2 0 114 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 112.83 2.83l-.06.06a1.65 1.65 0 00-.33 1.82V9a1.65 1.65 0 001.51 1H21a2 2 0 110 4h-.09a1.65 1.65 0 00-1.51 1z"/></svg>
1628
+ <span>Settings</span>
1629
+ <svg class="settings-chevron" viewBox="0 0 24 24" aria-hidden="true"><polyline points="9 6 15 12 9 18"/></svg>
1630
+ </button>
1631
+ <div class="settings-region" id="settings-region" hidden>
1632
+ <div class="settings-region-inner">
1633
+ <div class="settings-region-body">
1634
+ <label class="settings-field-label" for="api-url">API URL</label>
1635
+ <div class="settings-field-row">
1636
+ <input type="text" class="api-url-input" id="api-url" placeholder="API URL">
1637
+ <button class="api-btn" id="connect-btn" type="button">Go</button>
1638
+ </div>
1639
+ </div>
1640
+ </div>
1641
+ </div>
1642
+ </div>
1643
+ </div>
1644
+
1645
+ <!-- Collapsed rail: shown only when the column is collapsed. The expand
1646
+ button + a vertical label + a single live/connected dot wired to the
1647
+ shared API client (set in initStatusSidebar). -->
1648
+ <div class="status-rail" aria-hidden="false">
1649
+ <button class="status-toggle" id="status-expand" type="button"
1650
+ aria-controls="status-sidebar" aria-expanded="false"
1651
+ title="Expand status panel" aria-label="Expand status panel">
1652
+ <svg viewBox="0 0 24 24" aria-hidden="true"><polyline points="15 18 9 12 15 6"/></svg>
1653
+ </button>
1654
+ <span class="status-rail-dot" id="status-rail-dot" title="Connection status" role="status" aria-label="Disconnected"></span>
1655
+ <span class="status-rail-label" aria-hidden="true">Status</span>
1656
+ </div>
1657
+ </aside>
1417
1658
  </div>
1418
1659
 
1419
1660
  <!-- Keyboard Shortcuts Help Overlay -->
@@ -3609,6 +3850,10 @@ var LokiDashboard=(()=>{var He=Object.defineProperty;var _t=Object.getOwnPropert
3609
3850
 
3610
3851
  .model-select {
3611
3852
  flex: 1;
3853
+ /* Allow the select to shrink below its longest option's intrinsic
3854
+ width (the flex min-content floor) so it never overflows a narrow
3855
+ sidebar column; the native control ellipsizes the label. */
3856
+ min-width: 0;
3612
3857
  padding: 5px 8px;
3613
3858
  border-radius: 4px;
3614
3859
  border: 1px solid var(--loki-border);
@@ -15211,36 +15456,110 @@ document.addEventListener('DOMContentLoaded', function() {
15211
15456
  updateComponentsApiUrl(apiUrlInput.value);
15212
15457
  });
15213
15458
 
15214
- // v7.84 Settings popover: holds the API URL override. Toggled by the gear,
15215
- // closed on outside click or Escape. Best-effort -- if the elements are not
15216
- // present (older build) this block simply no-ops.
15217
- (function initSettingsPopover() {
15218
- var settingsBtn = document.getElementById('settings-btn');
15219
- var popover = document.getElementById('settings-popover');
15220
- if (!settingsBtn || !popover) return;
15221
- function open() {
15222
- popover.classList.add('open');
15223
- settingsBtn.setAttribute('aria-expanded', 'true');
15224
- }
15225
- function close() {
15226
- popover.classList.remove('open');
15227
- settingsBtn.setAttribute('aria-expanded', 'false');
15459
+ // v7.93 Settings disclosure: an inline collapsible section in the right
15460
+ // sidebar holding the API URL override (replaces the former floating popover).
15461
+ // The header button drives aria-expanded; the region toggles the [hidden]
15462
+ // attribute (which both animates the height via CSS and removes the API
15463
+ // controls from the tab order while collapsed). State is remembered per
15464
+ // machine. Best-effort -- no-ops on an older build without the elements.
15465
+ (function initSettingsDisclosure() {
15466
+ var btn = document.getElementById('settings-disclosure');
15467
+ var region = document.getElementById('settings-region');
15468
+ if (!btn || !region) return;
15469
+ var STORE_KEY = 'loki-settings-expanded';
15470
+
15471
+ function apply(expanded, persist) {
15472
+ btn.setAttribute('aria-expanded', String(expanded));
15473
+ region.hidden = !expanded;
15474
+ if (persist) {
15475
+ try { localStorage.setItem(STORE_KEY, expanded ? '1' : '0'); } catch (e) { /* ignore */ }
15476
+ }
15228
15477
  }
15229
- settingsBtn.addEventListener('click', function(e) {
15230
- e.stopPropagation();
15231
- if (popover.classList.contains('open')) { close(); }
15232
- else { open(); }
15233
- });
15234
- // Clicks inside the popover should not close it.
15235
- popover.addEventListener('click', function(e) { e.stopPropagation(); });
15236
- document.addEventListener('click', function() {
15237
- if (popover.classList.contains('open')) close();
15238
- });
15239
- document.addEventListener('keydown', function(e) {
15240
- if (e.key === 'Escape' && popover.classList.contains('open')) close();
15478
+
15479
+ var stored = null;
15480
+ try { stored = localStorage.getItem(STORE_KEY); } catch (e) { /* ignore */ }
15481
+ // Default collapsed: the API override is rarely used.
15482
+ apply(stored === '1', false);
15483
+
15484
+ btn.addEventListener('click', function() {
15485
+ apply(btn.getAttribute('aria-expanded') !== 'true', true);
15241
15486
  });
15242
15487
  })();
15243
15488
 
15489
+ // v7.92 Right status sidebar collapse. Toggled by the header chevron (when
15490
+ // expanded) and the rail chevron (when collapsed). State is remembered per
15491
+ // machine in localStorage; default is EXPANDED so nothing is hidden on first
15492
+ // load. Best-effort -- no-ops on an older build without the elements.
15493
+ (function initStatusSidebar() {
15494
+ var layout = document.querySelector('.dashboard-layout');
15495
+ var aside = document.getElementById('status-sidebar');
15496
+ var collapseBtn = document.getElementById('status-toggle');
15497
+ var expandBtn = document.getElementById('status-expand');
15498
+ if (!layout || !aside || !collapseBtn || !expandBtn) return;
15499
+ var STORE_KEY = 'loki-status-collapsed';
15500
+
15501
+ function syncAria(collapsed) {
15502
+ // aria-expanded describes the panel's state on whichever control is the
15503
+ // active affordance. Both controls point at the same panel.
15504
+ collapseBtn.setAttribute('aria-expanded', String(!collapsed));
15505
+ expandBtn.setAttribute('aria-expanded', String(!collapsed));
15506
+ }
15507
+
15508
+ function apply(collapsed, persist) {
15509
+ layout.classList.toggle('status-collapsed', collapsed);
15510
+ syncAria(collapsed);
15511
+ if (persist) {
15512
+ try { localStorage.setItem(STORE_KEY, collapsed ? '1' : '0'); } catch (e) { /* ignore */ }
15513
+ }
15514
+ }
15515
+
15516
+ function collapse() {
15517
+ apply(true, true);
15518
+ // Move focus to the rail's expand control so keyboard focus is never
15519
+ // orphaned on a now-hidden button.
15520
+ try { expandBtn.focus(); } catch (e) { /* ignore */ }
15521
+ }
15522
+ function expand() {
15523
+ apply(false, true);
15524
+ try { collapseBtn.focus(); } catch (e) { /* ignore */ }
15525
+ }
15526
+
15527
+ collapseBtn.addEventListener('click', collapse);
15528
+ expandBtn.addEventListener('click', expand);
15529
+
15530
+ // Restore remembered state (default expanded). Applied without persisting
15531
+ // and without stealing focus on load. On a narrow viewport the expanded
15532
+ // panel is a full overlay, so force the rail on load there (without
15533
+ // persisting -- the desktop preference is untouched); the user can still tap
15534
+ // expand to open the overlay and the header chevron to close it.
15535
+ var stored = null;
15536
+ try { stored = localStorage.getItem(STORE_KEY); } catch (e) { /* ignore */ }
15537
+ var startCollapsed = (window.innerWidth <= 768) ? true : (stored === '1');
15538
+ apply(startCollapsed, false);
15539
+
15540
+ // Glanceable live dot on the collapsed rail. Wired to the SAME shared API
15541
+ // client the session-control uses, so it reflects real connection state
15542
+ // rather than a static dot implying liveness it does not have.
15543
+ var dot = document.getElementById('status-rail-dot');
15544
+ if (dot && typeof LokiDashboard !== 'undefined' && LokiDashboard.getApiClient) {
15545
+ try {
15546
+ var api = LokiDashboard.getApiClient({ baseUrl: window.location.origin });
15547
+ function setConnected(connected) {
15548
+ dot.classList.toggle('connected', !!connected);
15549
+ dot.setAttribute('aria-label', connected ? 'Connected' : 'Disconnected');
15550
+ }
15551
+ api.addEventListener('api:connected', function () { setConnected(true); });
15552
+ api.addEventListener('api:disconnected', function () { setConnected(false); });
15553
+ api.addEventListener('api:status-update', function () { setConnected(true); });
15554
+ // Initial probe so the dot is honest before the first event arrives.
15555
+ if (typeof api.getStatus === 'function') {
15556
+ api.getStatus().then(function () { setConnected(true); })
15557
+ .catch(function () { setConnected(false); });
15558
+ }
15559
+ } catch (e) { /* leave the dot in its muted default */ }
15560
+ }
15561
+ })();
15562
+
15244
15563
  // Offline detection
15245
15564
  window.addEventListener('online', function() {
15246
15565
  document.getElementById('offline-banner').classList.remove('show');
@@ -2,7 +2,7 @@
2
2
 
3
3
  The flagship product of [Autonomi](https://www.autonomi.dev/). Loki Mode is a spec-driven autonomous builder with a built-in trust layer that takes any spec to a deployed product and verifies completion with evidence (quality gates plus a completion council), not just a "done" claim. Complete installation instructions for all platforms and use cases.
4
4
 
5
- **Version:** v7.91.1
5
+ **Version:** v7.93.0
6
6
 
7
7
  ---
8
8
 
@@ -395,7 +395,7 @@ provider works inside the container. Provide auth with your Anthropic API key:
395
395
  # Run Loki Mode in Docker (Claude provider, API-key auth)
396
396
  docker run --rm -e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
397
397
  -v $(pwd):/workspace -w /workspace \
398
- asklokesh/loki-mode:7.91.1 start ./my-spec.md
398
+ asklokesh/loki-mode:7.93.0 start ./my-spec.md
399
399
  ```
400
400
 
401
401
  ##### docker compose + .env (no host install)