Spaces:
Running
Running
AM ligature mark; grid overview icon; sidebar polish (frame gap, play, mono panels, collapse count); settings de-box
Browse files- web/src/components/Locked.tsx +2 -2
- web/src/components/Sidebar.tsx +6 -4
- web/src/components/icons.tsx +20 -5
- web/src/styles.css +16 -9
web/src/components/Locked.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
import { useState } from 'react';
|
| 2 |
-
import { SlidersGlyph, SunGlyph,
|
| 3 |
import Logo from './Logo';
|
| 4 |
|
| 5 |
// A frozen, slightly dimmed replica of the real sidebar so the install page
|
|
@@ -40,7 +40,7 @@ function MockSidebar() {
|
|
| 40 |
<div className="ov-fixed">
|
| 41 |
<div className="row ov-row">
|
| 42 |
<span className="status ov-spacer" />
|
| 43 |
-
<span className="ov-tile"><
|
| 44 |
<span className="name">overview</span>
|
| 45 |
</div>
|
| 46 |
</div>
|
|
|
|
| 1 |
import { useState } from 'react';
|
| 2 |
+
import { SlidersGlyph, SunGlyph, GridGlyph, PlusGlyph, AmMark } from './icons';
|
| 3 |
import Logo from './Logo';
|
| 4 |
|
| 5 |
// A frozen, slightly dimmed replica of the real sidebar so the install page
|
|
|
|
| 40 |
<div className="ov-fixed">
|
| 41 |
<div className="row ov-row">
|
| 42 |
<span className="status ov-spacer" />
|
| 43 |
+
<span className="ov-tile"><GridGlyph /></span>
|
| 44 |
<span className="name">overview</span>
|
| 45 |
</div>
|
| 46 |
</div>
|
web/src/components/Sidebar.tsx
CHANGED
|
@@ -4,7 +4,7 @@ import { STATE_LABEL } from '../types';
|
|
| 4 |
import Logo from './Logo';
|
| 5 |
import NewSession from './NewSession';
|
| 6 |
import FolderPicker from './FolderPicker';
|
| 7 |
-
import { SlidersGlyph, SunGlyph, MoonGlyph, CloseGlyph, PencilGlyph, StopGlyph,
|
| 8 |
|
| 9 |
type Zone = 'before' | 'after' | 'on';
|
| 10 |
|
|
@@ -149,7 +149,9 @@ export default function Sidebar({
|
|
| 149 |
)}
|
| 150 |
<span className="age">{fmtAgo(ages?.[s.id])}</span>
|
| 151 |
<span className="row-actions">
|
| 152 |
-
{s.running
|
|
|
|
|
|
|
| 153 |
<button className="mini-btn" title="Delete" onClick={(e) => { e.stopPropagation(); onDeleteSession(s.id); }}><CloseGlyph /></button>
|
| 154 |
</span>
|
| 155 |
</div>
|
|
@@ -183,7 +185,7 @@ export default function Sidebar({
|
|
| 183 |
) : (
|
| 184 |
<>
|
| 185 |
<span className="name">{g.name}</span>
|
| 186 |
-
<span className="count">{g.sessionIds.length}</span>
|
| 187 |
<span className="row-actions">
|
| 188 |
<button className="mini-btn" title="Rename" onClick={(e) => { e.stopPropagation(); startEdit(ref, g.name); }}><PencilGlyph /></button>
|
| 189 |
<button className="mini-btn" title="Delete group" onClick={(e) => { e.stopPropagation(); onDeleteGroup(g.id); }}><CloseGlyph /></button>
|
|
@@ -274,7 +276,7 @@ export default function Sidebar({
|
|
| 274 |
title="All agents: digests, states, replies"
|
| 275 |
>
|
| 276 |
<span className="status ov-spacer" />
|
| 277 |
-
<span className="ov-tile"><
|
| 278 |
<span className="name">overview</span>
|
| 279 |
{waiting > 0 && <span className="ov-wait">{waiting} waiting</span>}
|
| 280 |
</div>
|
|
|
|
| 4 |
import Logo from './Logo';
|
| 5 |
import NewSession from './NewSession';
|
| 6 |
import FolderPicker from './FolderPicker';
|
| 7 |
+
import { SlidersGlyph, SunGlyph, MoonGlyph, CloseGlyph, PencilGlyph, StopGlyph, PlayGlyph, GridGlyph, PlusGlyph, AmMark } from './icons';
|
| 8 |
|
| 9 |
type Zone = 'before' | 'after' | 'on';
|
| 10 |
|
|
|
|
| 149 |
)}
|
| 150 |
<span className="age">{fmtAgo(ages?.[s.id])}</span>
|
| 151 |
<span className="row-actions">
|
| 152 |
+
{s.running
|
| 153 |
+
? <button className="mini-btn" title="Stop" onClick={(e) => { e.stopPropagation(); onStopSession(s.id); }}><StopGlyph /></button>
|
| 154 |
+
: <button className="mini-btn" title="Start" onClick={(e) => { e.stopPropagation(); onOpenSession(s.id, groupId); }}><PlayGlyph /></button>}
|
| 155 |
<button className="mini-btn" title="Delete" onClick={(e) => { e.stopPropagation(); onDeleteSession(s.id); }}><CloseGlyph /></button>
|
| 156 |
</span>
|
| 157 |
</div>
|
|
|
|
| 185 |
) : (
|
| 186 |
<>
|
| 187 |
<span className="name">{g.name}</span>
|
| 188 |
+
{!open && <span className="count">{g.sessionIds.length}</span>}
|
| 189 |
<span className="row-actions">
|
| 190 |
<button className="mini-btn" title="Rename" onClick={(e) => { e.stopPropagation(); startEdit(ref, g.name); }}><PencilGlyph /></button>
|
| 191 |
<button className="mini-btn" title="Delete group" onClick={(e) => { e.stopPropagation(); onDeleteGroup(g.id); }}><CloseGlyph /></button>
|
|
|
|
| 276 |
title="All agents: digests, states, replies"
|
| 277 |
>
|
| 278 |
<span className="status ov-spacer" />
|
| 279 |
+
<span className="ov-tile"><GridGlyph /></span>
|
| 280 |
<span className="name">overview</span>
|
| 281 |
{waiting > 0 && <span className="ov-wait">{waiting} waiting</span>}
|
| 282 |
</div>
|
web/src/components/icons.tsx
CHANGED
|
@@ -67,6 +67,12 @@ export const CloseGlyph = ({ className }: { className?: string }) => (
|
|
| 67 |
</G>
|
| 68 |
);
|
| 69 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
export const StopGlyph = ({ className }: { className?: string }) => (
|
| 71 |
<svg className={className} viewBox="0 0 16 16" fill="currentColor">
|
| 72 |
<rect x="4.4" y="4.4" width="7.2" height="7.2" rx="1.4" />
|
|
@@ -98,18 +104,27 @@ export const PulseGlyph = ({ className }: { className?: string }) => (
|
|
| 98 |
</G>
|
| 99 |
);
|
| 100 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
export const PlusGlyph = ({ className }: { className?: string }) => (
|
| 102 |
<G className={className}>
|
| 103 |
<path d="M8 3.2v9.6M3.2 8h9.6" />
|
| 104 |
</G>
|
| 105 |
);
|
| 106 |
|
| 107 |
-
// The AM
|
| 108 |
-
//
|
| 109 |
export const AmMark = ({ className }: { className?: string }) => (
|
| 110 |
-
<svg className={className} viewBox="0 0
|
| 111 |
-
<path fillRule="evenodd" d="M0
|
| 112 |
-
<path d="M18 0H38V24H34V4H30V24H26V4H22V24H18Z" />
|
| 113 |
</svg>
|
| 114 |
);
|
| 115 |
|
|
|
|
| 67 |
</G>
|
| 68 |
);
|
| 69 |
|
| 70 |
+
export const PlayGlyph = ({ className }: { className?: string }) => (
|
| 71 |
+
<G className={className}>
|
| 72 |
+
<path d="M5.6 3.9v8.2L12.4 8z" />
|
| 73 |
+
</G>
|
| 74 |
+
);
|
| 75 |
+
|
| 76 |
export const StopGlyph = ({ className }: { className?: string }) => (
|
| 77 |
<svg className={className} viewBox="0 0 16 16" fill="currentColor">
|
| 78 |
<rect x="4.4" y="4.4" width="7.2" height="7.2" rx="1.4" />
|
|
|
|
| 104 |
</G>
|
| 105 |
);
|
| 106 |
|
| 107 |
+
// Overview: four panes at a glance — what the page literally is.
|
| 108 |
+
export const GridGlyph = ({ className }: { className?: string }) => (
|
| 109 |
+
<G className={className}>
|
| 110 |
+
<rect x="2.2" y="2.2" width="4.8" height="4.8" rx="0.8" />
|
| 111 |
+
<rect x="9" y="2.2" width="4.8" height="4.8" rx="0.8" />
|
| 112 |
+
<rect x="2.2" y="9" width="4.8" height="4.8" rx="0.8" />
|
| 113 |
+
<rect x="9" y="9" width="4.8" height="4.8" rx="0.8" />
|
| 114 |
+
</G>
|
| 115 |
+
);
|
| 116 |
+
|
| 117 |
export const PlusGlyph = ({ className }: { className?: string }) => (
|
| 118 |
<G className={className}>
|
| 119 |
<path d="M8 3.2v9.6M3.2 8h9.6" />
|
| 120 |
</G>
|
| 121 |
);
|
| 122 |
|
| 123 |
+
// The AM ligature: rectangular A and M sharing their middle stroke under one
|
| 124 |
+
// continuous roof. One 4-unit stroke, 4-unit counters.
|
| 125 |
export const AmMark = ({ className }: { className?: string }) => (
|
| 126 |
+
<svg className={className} viewBox="0 0 28 24" fill="currentColor" aria-label="Agent Manager">
|
| 127 |
+
<path fillRule="evenodd" d="M0 0H28V24H24V4H20V24H16V4H12V24H8V14H4V24H0ZM4 4H8V10H4Z" />
|
|
|
|
| 128 |
</svg>
|
| 129 |
);
|
| 130 |
|
web/src/styles.css
CHANGED
|
@@ -87,7 +87,7 @@ body {
|
|
| 87 |
.brand h1 { font-size: 14px; margin: 0; letter-spacing: -0.01em; white-space: nowrap; }
|
| 88 |
.brand .brand-actions { display: flex; gap: 4px; flex: none; }
|
| 89 |
/* AM monogram — the brand mark */
|
| 90 |
-
.am-mark { width:
|
| 91 |
.icon-btn.add-btn { color: var(--accent); }
|
| 92 |
.icon-btn.add-btn:hover, .icon-btn.add-btn.on { border-color: color-mix(in srgb, var(--accent) 55%, var(--border)); color: var(--accent); }
|
| 93 |
.icon-btn { background: transparent; border: 1px solid var(--border); color: var(--muted); border-radius: var(--r-md); width: 32px; height: 30px; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; }
|
|
@@ -308,7 +308,10 @@ body {
|
|
| 308 |
.row.group-head .name { flex: initial; font-size: 11.5px; font-weight: 400; color: var(--text); }
|
| 309 |
.row.group-head:hover .name { color: var(--accent); }
|
| 310 |
.row.group-head .rename { width: 130px; flex: none; }
|
| 311 |
-
|
|
|
|
|
|
|
|
|
|
| 312 |
.row.group-head:hover .count { display: none; }
|
| 313 |
/* per-group + on the frame line, revealed on hover */
|
| 314 |
.g-add { position: absolute; top: -9px; right: 8px; background: var(--panel); border: none; color: var(--muted); padding: 0 4px; cursor: pointer; opacity: 0; transition: opacity 0.12s; z-index: 1; }
|
|
@@ -326,8 +329,12 @@ body {
|
|
| 326 |
.status.stopped { background: transparent; border: 1.5px solid var(--muted); opacity: 0.5; }
|
| 327 |
|
| 328 |
/* quick-add utilities (shell / files) pinned above the legend */
|
| 329 |
-
.quick-add { display: flex; gap:
|
| 330 |
-
.quick-add .btn-ghost { flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: 7px; padding:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 331 |
|
| 332 |
.legend { display: flex; flex-wrap: wrap; gap: 6px 12px; padding: 10px 16px; border-top: 1px solid var(--border); font-size: 10.5px; color: var(--muted); }
|
| 333 |
.legend span { display: inline-flex; align-items: center; gap: 5px; }
|
|
@@ -486,9 +493,9 @@ a.btn-ghost { text-decoration: none; }
|
|
| 486 |
.s-help { color: var(--muted); font-size: 12.5px; line-height: 1.5; margin: 2px 0 0; }
|
| 487 |
.s-muted { color: var(--muted); }
|
| 488 |
/* agents: one hairline row per CLI — dot · logo · name · version · state */
|
| 489 |
-
/*
|
| 490 |
-
.agent-rows { display: flex; flex-direction: column;
|
| 491 |
-
.agent-row { display: flex; align-items: center; gap: 10px; padding: 9px
|
| 492 |
.agent-row + .agent-row { border-top: 1px solid var(--border); }
|
| 493 |
.agent-row .spacer { flex: 1; }
|
| 494 |
.ar-name { white-space: nowrap; font-weight: 500; }
|
|
@@ -513,8 +520,8 @@ a.btn-ghost { text-decoration: none; }
|
|
| 513 |
transition: opacity 0.12s ease, transform 0.12s ease;
|
| 514 |
}
|
| 515 |
.tip:hover::after, .tip:focus-visible::after { opacity: 1; transform: none; }
|
| 516 |
-
.kv { display: flex; flex-direction: column;
|
| 517 |
-
.kv > div { display: flex; justify-content: space-between; gap: 16px; padding:
|
| 518 |
.kv > div:last-child { border-bottom: none; }
|
| 519 |
.kv span { color: var(--muted); }
|
| 520 |
.kv b { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 65%; }
|
|
|
|
| 87 |
.brand h1 { font-size: 14px; margin: 0; letter-spacing: -0.01em; white-space: nowrap; }
|
| 88 |
.brand .brand-actions { display: flex; gap: 4px; flex: none; }
|
| 89 |
/* AM monogram — the brand mark */
|
| 90 |
+
.am-mark { width: 16px; height: 13.7px; color: var(--accent); flex: none; display: block; }
|
| 91 |
.icon-btn.add-btn { color: var(--accent); }
|
| 92 |
.icon-btn.add-btn:hover, .icon-btn.add-btn.on { border-color: color-mix(in srgb, var(--accent) 55%, var(--border)); color: var(--accent); }
|
| 93 |
.icon-btn { background: transparent; border: 1px solid var(--border); color: var(--muted); border-radius: var(--r-md); width: 32px; height: 30px; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; }
|
|
|
|
| 308 |
.row.group-head .name { flex: initial; font-size: 11.5px; font-weight: 400; color: var(--text); }
|
| 309 |
.row.group-head:hover .name { color: var(--accent); }
|
| 310 |
.row.group-head .rename { width: 130px; flex: none; }
|
| 311 |
+
/* actions must not reserve space when hidden — an invisible-but-present span
|
| 312 |
+
would stretch the label's frame punch-out and leave a gap in the border */
|
| 313 |
+
.row.group-head .row-actions { position: static; transform: none; background: var(--panel); display: none; opacity: 1; pointer-events: auto; }
|
| 314 |
+
.row.group-head:hover .row-actions, .row.group-head:focus-within .row-actions { display: flex; }
|
| 315 |
.row.group-head:hover .count { display: none; }
|
| 316 |
/* per-group + on the frame line, revealed on hover */
|
| 317 |
.g-add { position: absolute; top: -9px; right: 8px; background: var(--panel); border: none; color: var(--muted); padding: 0 4px; cursor: pointer; opacity: 0; transition: opacity 0.12s; z-index: 1; }
|
|
|
|
| 329 |
.status.stopped { background: transparent; border: 1.5px solid var(--muted); opacity: 0.5; }
|
| 330 |
|
| 331 |
/* quick-add utilities (shell / files) pinned above the legend */
|
| 332 |
+
.quick-add { display: flex; gap: 6px; padding: 10px 10px; border-top: 1px solid var(--border); }
|
| 333 |
+
.quick-add .btn-ghost { flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: 7px; padding: 5.5px 8px; font-family: var(--font-mono); font-size: 12px; font-weight: 500; border-radius: var(--r-sm); }
|
| 334 |
+
/* the create panel speaks the same dense mono voice as the rows */
|
| 335 |
+
.controls .btn-primary, .controls .btn-ghost { font-family: var(--font-mono); font-size: 12px; font-weight: 500; padding: 6px 10px; border-radius: var(--r-sm); }
|
| 336 |
+
.controls .widget input, .controls .widget select, .controls .agent-pick, .controls .fp-current { font-size: 12.5px; }
|
| 337 |
+
.controls .seg button { font-family: var(--font-mono); }
|
| 338 |
|
| 339 |
.legend { display: flex; flex-wrap: wrap; gap: 6px 12px; padding: 10px 16px; border-top: 1px solid var(--border); font-size: 10.5px; color: var(--muted); }
|
| 340 |
.legend span { display: inline-flex; align-items: center; gap: 5px; }
|
|
|
|
| 493 |
.s-help { color: var(--muted); font-size: 12.5px; line-height: 1.5; margin: 2px 0 0; }
|
| 494 |
.s-muted { color: var(--muted); }
|
| 495 |
/* agents: one hairline row per CLI — dot · logo · name · version · state */
|
| 496 |
+
/* plain hairline rows — no box (sidebar voice: boundaries only where they mean something) */
|
| 497 |
+
.agent-rows { display: flex; flex-direction: column; margin-top: 4px; }
|
| 498 |
+
.agent-row { display: flex; align-items: center; gap: 10px; padding: 9px 2px; font-size: 13px; }
|
| 499 |
.agent-row + .agent-row { border-top: 1px solid var(--border); }
|
| 500 |
.agent-row .spacer { flex: 1; }
|
| 501 |
.ar-name { white-space: nowrap; font-weight: 500; }
|
|
|
|
| 520 |
transition: opacity 0.12s ease, transform 0.12s ease;
|
| 521 |
}
|
| 522 |
.tip:hover::after, .tip:focus-visible::after { opacity: 1; transform: none; }
|
| 523 |
+
.kv { display: flex; flex-direction: column; margin-top: 4px; }
|
| 524 |
+
.kv > div { display: flex; justify-content: space-between; gap: 16px; padding: 10px 2px; border-bottom: 1px solid var(--border); font-size: 13px; }
|
| 525 |
.kv > div:last-child { border-bottom: none; }
|
| 526 |
.kv span { color: var(--muted); }
|
| 527 |
.kv b { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 65%; }
|