harlay 0.1.2 → 0.2.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.
@@ -4,9 +4,9 @@
4
4
  var import_electron = require("electron");
5
5
  import_electron.contextBridge.exposeInMainWorld("harlay", {
6
6
  onModel: (cb) => void import_electron.ipcRenderer.on("harlay:model", (_e, model) => cb(model)),
7
- onShown: (cb) => void import_electron.ipcRenderer.on("harlay:shown", () => cb()),
8
- onBrief: (cb) => void import_electron.ipcRenderer.on("harlay:brief", (_e, brief) => cb(brief)),
7
+ onShown: (cb) => void import_electron.ipcRenderer.on("harlay:shown", (_e, opts) => cb(opts)),
9
8
  act: (action) => import_electron.ipcRenderer.send("harlay:action", action),
9
+ addChat: (tool, id) => import_electron.ipcRenderer.invoke("harlay:addChat", { tool, id }),
10
10
  resize: (height) => import_electron.ipcRenderer.send("harlay:resize", height),
11
11
  hide: () => import_electron.ipcRenderer.send("harlay:hide"),
12
12
  ready: () => import_electron.ipcRenderer.send("harlay:ready")
@@ -1,205 +1,274 @@
1
- /* Palette and type follow the landing page (apps/web); system fonts keep the app free of network calls. */
1
+ /* Type: the system's own fonts (SF Pro, SF Mono), so the panel reads like a native Mac menu. */
2
2
  :root {
3
- --bg: #f6f5f1;
3
+ --bg: #faf9f6;
4
+ --glow: radial-gradient(120% 60% at 0% 0%, rgba(var(--accent-rgb), .07), transparent 60%), radial-gradient(80% 50% at 100% 0%, rgba(255, 150, 90, .05), transparent 60%);
4
5
  --card: #fff;
5
- --sunk: #efeee9;
6
- --ink-rgb: 22, 22, 26;
7
- --accent-rgb: 47, 108, 246;
8
- --line: rgba(var(--ink-rgb), .08);
9
- --line2: rgba(var(--ink-rgb), .13);
10
- --line3: rgba(var(--ink-rgb), .22);
11
- --fg: #16161a;
12
- --fg2: #55555e;
13
- --fg3: #7c7c84;
14
- --accent: #2f6cf6;
15
- --accent-hi: #4a80ff;
6
+ --sunk: #f1efea;
7
+ --ink-rgb: 27, 26, 23;
8
+ --accent-rgb: 59, 91, 246;
9
+ --line: rgba(var(--ink-rgb), .07);
10
+ --line2: rgba(var(--ink-rgb), .11);
11
+ --line3: rgba(var(--ink-rgb), .2);
12
+ --fg: #1b1a17;
13
+ --fg2: #5c5953;
14
+ --fg3: #8f8b84;
15
+ --accent: #3b5bf6;
16
+ --accent-hi: #5574ff;
16
17
  --accent-soft: rgba(var(--accent-rgb), .09);
17
- --ok: #1e9e5a;
18
- --err: #e0453a;
19
- --warn: #b98516;
20
- --serif: ui-serif, "New York", Georgia, serif;
18
+ --fresh: #17a06c;
19
+ --long: #c98a12;
20
+ --bad: #e5483b;
21
+ --bad-rgb: 229, 72, 59;
22
+ --health-fresh: #2fb35a;
23
+ --health-long: #e8a40c;
24
+ --health-bad: #e5483b;
21
25
  --sans: system-ui, -apple-system, "Helvetica Neue", sans-serif;
22
26
  --mono: ui-monospace, "SF Mono", Menlo, monospace;
23
27
  --ease: cubic-bezier(.2, .7, .1, 1);
24
- --hover: rgba(255, 255, 255, .65);
25
- --foot: rgba(255, 255, 255, .5);
26
- --shadow-rgb: 20, 25, 40;
28
+ --hover: rgba(var(--ink-rgb), .045);
29
+ --foot: rgba(var(--ink-rgb), .025);
30
+ --shadow-rgb: 30, 28, 24;
31
+ --rim: inset 0 1px 0 rgba(255, 255, 255, .9);
27
32
  }
28
- /* Follows the macOS appearance setting. */
33
+ /* Follows the macOS appearance setting (or Harlay's, which sets it). */
29
34
  @media (prefers-color-scheme: dark) {
30
35
  :root {
31
- --bg: #1b1b1f;
32
- --card: #26262c;
33
- --sunk: #141417;
34
- --ink-rgb: 236, 236, 241;
35
- --accent-rgb: 92, 141, 255;
36
- --fg: #ececf1;
37
- --fg2: #b4b4bd;
38
- --fg3: #8b8b95;
39
- --accent: #5c8dff;
40
- --accent-hi: #7aa2ff;
41
- --ok: #3ccf7f;
42
- --err: #ff6b5f;
43
- --warn: #e2ad3b;
36
+ --bg: #111113;
37
+ --glow: radial-gradient(120% 60% at 0% 0%, rgba(var(--accent-rgb), .13), transparent 58%), radial-gradient(80% 50% at 100% 0%, rgba(255, 130, 90, .07), transparent 60%);
38
+ --card: #1b1b1f;
39
+ --sunk: #0b0b0d;
40
+ --ink-rgb: 243, 241, 236;
41
+ --accent-rgb: 138, 162, 255;
42
+ --fg: #f3f1ec;
43
+ --fg2: #aeaaa3;
44
+ --fg3: #77746e;
45
+ --accent: #8aa2ff;
46
+ --accent-hi: #a6b7ff;
47
+ --fresh: #4cd4a0;
48
+ --long: #f0b54a;
49
+ --bad: #ff6b5e;
50
+ --bad-rgb: 255, 107, 94;
51
+ --health-fresh: #4cd27a;
52
+ --health-long: #f5b84a;
53
+ --health-bad: #ff6b5e;
44
54
  --hover: rgba(255, 255, 255, .05);
45
- --foot: rgba(0, 0, 0, .18);
55
+ --foot: rgba(0, 0, 0, .2);
46
56
  --shadow-rgb: 0, 0, 0;
57
+ --rim: inset 0 1px 0 rgba(255, 255, 255, .06);
47
58
  }
48
59
  }
49
60
  * { box-sizing: border-box; margin: 0; padding: 0; }
50
61
  html, body { background: transparent; color-scheme: light dark; }
51
- body { font: 13px/1.4 var(--sans); color: var(--fg); -webkit-font-smoothing: antialiased; user-select: none; cursor: default; overflow: hidden; }
52
- button { font: inherit; color: inherit; background: none; border: 0; text-align: left; cursor: default; }
62
+ body { font: 13px/1.45 var(--sans); color: var(--fg); -webkit-font-smoothing: antialiased; user-select: none; cursor: default; overflow: hidden; }
63
+ button, input { font: inherit; color: inherit; background: none; border: 0; text-align: left; cursor: default; }
53
64
  :focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
65
+ /* Opening the popover focuses the panel itself (see show), which shows no ring. */
66
+ #panel:focus { outline: none; }
54
67
  [hidden] { display: none !important; }
68
+ ::selection { background: rgba(var(--accent-rgb), .25); }
55
69
 
56
- .wrap { width: 392px; padding: 6px 16px 20px; }
70
+ .wrap { width: 392px; padding: 6px 16px 22px; }
57
71
  .panel {
58
- background: var(--bg);
72
+ background: var(--glow), var(--bg);
59
73
  border: 1px solid var(--line2);
60
- border-radius: 14px;
61
- box-shadow: 0 22px 48px -18px rgba(var(--shadow-rgb), .35), 0 2px 8px rgba(var(--shadow-rgb), .08);
74
+ border-radius: 16px;
75
+ box-shadow: var(--rim), 0 28px 60px -22px rgba(var(--shadow-rgb), .4), 0 2px 10px rgba(var(--shadow-rgb), .08);
62
76
  overflow: hidden;
63
77
  opacity: 0;
64
78
  transform: translateY(-6px) scale(.985);
65
79
  transform-origin: top center;
66
80
  }
67
- .panel.in { opacity: 1; transform: none; transition: opacity .16s ease-out, transform .22s var(--ease); }
81
+ .panel.in { opacity: 1; transform: none; transition: opacity .18s ease-out, transform .26s var(--ease); }
68
82
 
69
83
  /* header */
70
- .top { display: flex; align-items: center; gap: 8px; padding: 12px 10px 8px 16px; }
71
- .brand { display: flex; align-items: center; gap: 7px; font-weight: 600; font-size: 14.5px; letter-spacing: -.02em; }
72
- .brand svg { width: 17px; height: 17px; }
84
+ .top { display: flex; align-items: center; gap: 6px; height: 52px; padding: 0 10px 0 18px; }
85
+ .top:has(#back:not([hidden])) { padding-left: 10px; }
86
+ .brand { display: flex; align-items: center; gap: 7px; font-size: 14px; font-weight: 600; letter-spacing: -.01em; }
87
+ .brand svg { width: 17px; height: 17px; margin-top: 1px; }
73
88
  .mk-b { fill: var(--accent); }
74
- .pill { margin-left: auto; display: inline-flex; align-items: center; gap: 6px; padding: 3px 9px; border-radius: 999px; background: var(--card); border: 1px solid var(--line2); font: 500 10.5px var(--mono); color: var(--fg2); }
75
- .pill i { width: 6px; height: 6px; border-radius: 50%; background: var(--ok); box-shadow: 0 0 0 3px rgba(30, 158, 90, .14); }
76
- .pill.auto i { background: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
77
- .icon-btn { width: 28px; height: 28px; display: grid; place-items: center; border-radius: 8px; color: var(--fg2); }
78
- .icon-btn:hover { background: rgba(var(--ink-rgb), .06); color: var(--fg); }
89
+ .view-title { font-size: 14px; font-weight: 600; letter-spacing: -.01em; }
90
+ #open-settings { margin-left: auto; }
91
+ .icon-btn { width: 30px; height: 30px; display: grid; place-items: center; border-radius: 9px; color: var(--fg3); transition: background-color .15s, color .15s; }
92
+ .icon-btn:hover { background: var(--hover); color: var(--fg); }
79
93
  .icon-btn svg { width: 16px; height: 16px; }
80
94
 
81
- /* headline */
82
- .hero { padding: 2px 16px 12px; }
83
- .hero h1 { font: 500 21px/1.15 var(--serif); letter-spacing: -.01em; }
84
- .hero h1 em { font-style: normal; color: var(--accent); }
85
- .hero p { margin-top: 4px; font-size: 12px; color: var(--fg3); }
95
+ /* agent groups */
96
+ .scroll { max-height: 440px; overflow-y: auto; padding: 0 8px 4px; }
97
+ .scroll::-webkit-scrollbar { width: 8px; }
98
+ .scroll::-webkit-scrollbar-thumb { background: rgba(var(--ink-rgb), .14); border-radius: 8px; border: 2px solid transparent; background-clip: padding-box; }
99
+ .grp + .grp { margin-top: 6px; padding-top: 6px; border-top: 1px solid var(--line); }
100
+ .grp-h { display: flex; align-items: center; gap: 10px; padding: 9px 8px 6px; }
101
+ .agent { flex: none; width: 20px; height: 20px; border-radius: 6px; box-shadow: 0 1px 2px rgba(var(--shadow-rgb), .18); }
102
+ .agent.badge { display: grid; place-items: center; background: var(--sunk); box-shadow: inset 0 0 0 1px var(--line2); font: 600 8.5px var(--mono); color: var(--fg2); }
103
+ .grp-name { font-size: 12.5px; font-weight: 600; letter-spacing: -.005em; }
104
+ .add { margin-left: auto; width: 26px; height: 26px; display: grid; place-items: center; border-radius: 8px; color: var(--fg3); transition: background-color .15s, color .15s; }
105
+ .add:hover, .add[aria-expanded="true"] { background: var(--hover); color: var(--fg); }
106
+ .add svg { width: 14px; height: 14px; }
107
+ .none { padding: 2px 8px 10px 38px; font-size: 12px; color: var(--fg3); }
86
108
 
87
- .label { display: flex; align-items: center; gap: 7px; padding: 8px 16px 6px; font: 500 10.5px var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--fg3); }
88
- .label::before { content: ""; width: 5px; height: 5px; background: var(--accent); }
109
+ .item { border-radius: 10px; transition: background-color .15s, box-shadow .15s; }
110
+ .item:hover { background: var(--hover); }
111
+ .item.open { background: var(--card); box-shadow: var(--rim), 0 0 0 1px var(--line2), 0 6px 18px -10px rgba(var(--shadow-rgb), .25); }
112
+ .row { display: flex; align-items: center; gap: 10px; width: 100%; height: 36px; padding: 0 10px 0 8px; border-radius: 10px; }
113
+ .st { flex: none; width: 20px; display: grid; place-items: center; }
114
+ .st i { width: 6px; height: 6px; border-radius: 50%; }
115
+ .st.limit i { background: var(--bad); box-shadow: 0 0 0 3px rgba(var(--bad-rgb), .16); }
116
+ .st.warning i { background: var(--long); box-shadow: 0 0 0 3px rgba(201, 138, 18, .16); }
117
+ .st.handed_off i { background: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
118
+ .title { flex: 1; min-width: 0; font-size: 13.5px; font-weight: 500; letter-spacing: -.01em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
119
+ .time { flex: none; width: 26px; text-align: right; font: 400 10.5px var(--mono); color: var(--fg3); }
89
120
 
90
- /* usage */
91
- .usage { padding: 0 8px 4px; }
92
- .usage .label { padding: 2px 8px 6px; }
93
- .u-row { display: flex; gap: 10px; padding: 6px 8px; }
94
- .u-name { flex: none; width: 86px; font-size: 12px; font-weight: 600; padding-top: 1px; }
95
- .u-body { flex: 1; min-width: 0; }
96
- .u-win { display: flex; align-items: center; gap: 8px; height: 18px; }
97
- .u-lbl { flex: none; width: 34px; font: 10.5px var(--mono); color: var(--fg3); }
98
- .u-bar { flex: 1; height: 5px; border-radius: 999px; background: rgba(var(--ink-rgb), .08); overflow: hidden; }
99
- .u-bar i { display: block; height: 100%; border-radius: inherit; background: var(--accent); }
100
- .u-bar i.warm { background: var(--warn); }
101
- .u-bar i.hot { background: var(--err); }
102
- .u-pct { flex: none; width: 38px; text-align: right; font: 500 11px var(--mono); color: var(--fg); }
103
- .u-reset { flex: none; width: 92px; font: 10.5px var(--mono); color: var(--fg3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
104
- .u-meta { margin-top: 2px; font-size: 11px; color: var(--fg3); }
105
- .link.inline { margin: 0 0 0 6px; font-size: 11px; }
106
- .switch:disabled { opacity: .4; }
121
+ .hw { flex: none; display: inline-grid; place-items: center; width: 22px; height: 16px; }
122
+ .hw.lg { width: 25px; height: 18px; }
107
123
 
108
- .ask { margin: 0 8px 8px; padding: 11px 12px; border-radius: 10px; background: var(--card); border: 1px solid rgba(var(--accent-rgb), .35); }
109
- .ask-title { font-size: 12.5px; font-weight: 600; }
110
- .ask-body { margin-top: 3px; font-size: 11.5px; color: var(--fg2); }
111
- .ask-btns { display: flex; gap: 6px; margin-top: 9px; }
112
-
113
- /* sessions */
114
- .label.group { padding: 10px 8px 4px; }
115
- .label.group:first-child { padding-top: 2px; }
116
- .label.attn::before { background: var(--err); }
117
- .label .count { margin-left: auto; letter-spacing: 0; color: var(--fg3); opacity: .8; }
118
- .scroll { max-height: 372px; overflow-y: auto; padding: 0 8px 8px; }
119
- .scroll::-webkit-scrollbar { width: 8px; }
120
- .scroll::-webkit-scrollbar-thumb { background: rgba(var(--ink-rgb), .15); border-radius: 8px; border: 2px solid var(--bg); }
121
- .item { border-radius: 10px; border: 1px solid transparent; transition: background-color .15s, border-color .15s; }
122
- .item:hover { background: var(--hover); }
123
- .item.open { background: var(--card); border-color: var(--line2); box-shadow: 0 1px 2px rgba(var(--ink-rgb), .04); }
124
- .row { display: flex; align-items: center; gap: 10px; width: 100%; padding: 8px; border-radius: 10px; }
125
- .st { flex: none; width: 8px; height: 8px; border-radius: 50%; box-shadow: inset 0 0 0 1.5px var(--line3); }
126
- .st.limit { background: var(--err); box-shadow: 0 0 0 3px rgba(224, 69, 58, .14); }
127
- .st.warning { background: var(--warn); box-shadow: 0 0 0 3px rgba(185, 133, 22, .14); }
128
- .st.handed_off { background: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
129
- .main { flex: 1; min-width: 0; }
130
- .title { display: block; font-weight: 600; font-size: 13px; letter-spacing: -.005em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
131
- .meta { display: block; margin-top: 1px; font: 10.5px var(--mono); color: var(--fg3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
132
- .tag { flex: none; font: 500 10px var(--mono); padding: 2px 7px; border-radius: 999px; border: 1px solid; }
133
- .tag.limit { color: var(--err); border-color: rgba(224, 69, 58, .3); background: rgba(224, 69, 58, .07); }
134
- .tag.warning { color: var(--warn); border-color: rgba(185, 133, 22, .3); background: rgba(185, 133, 22, .08); }
135
- .tag.handed_off { color: var(--accent); border-color: rgba(var(--accent-rgb), .3); background: var(--accent-soft); }
136
- .chev { flex: none; width: 14px; height: 14px; color: var(--fg3); transition: transform .2s var(--ease); }
137
- .item.open .chev { transform: rotate(90deg); }
138
-
139
- .drawer { display: none; padding: 0 10px 11px 26px; }
124
+ .drawer { display: none; padding: 2px 12px 13px 38px; }
140
125
  .item.open .drawer { display: block; }
141
126
  .detail { font-size: 12px; color: var(--fg2); }
142
- .path { margin-top: 2px; font: 10.5px var(--mono); color: var(--fg3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
143
- .sub { margin: 10px 0 6px; font: 500 10px var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--fg3); }
127
+ .path { margin-top: 10px; font: 10.5px var(--mono); color: var(--fg3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
128
+ .sub { margin: 12px 0 7px; font: 500 9.5px var(--mono); letter-spacing: .1em; text-transform: uppercase; color: var(--fg3); }
144
129
  .targets { display: flex; gap: 6px; flex-wrap: wrap; }
145
- .btn { display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 11px; border-radius: 7px; border: 1px solid var(--line2); background: var(--card); font-size: 12px; font-weight: 500; box-shadow: 0 1px 1px rgba(var(--ink-rgb), .04); }
130
+
131
+ /* health card in the drawer */
132
+ .h-size { margin-top: 2px; font-variant-numeric: tabular-nums; }
133
+ .health { margin-top: 2px; padding: 10px 11px; border-radius: 10px; background: var(--sunk); box-shadow: inset 0 0 0 1px var(--line); }
134
+ .health.struggling { background: rgba(var(--bad-rgb), .06); box-shadow: inset 0 0 0 1px rgba(var(--bad-rgb), .2); }
135
+ .h-why { margin-top: 8px; list-style: none; display: grid; gap: 5px; line-height: 1.4; }
136
+ .h-why li { font-size: 12px; color: var(--fg2); font-variant-numeric: tabular-nums; }
137
+ .h-why code { font: 11px var(--mono); color: var(--fg); }
138
+ .health .btn { margin-top: 9px; }
139
+
140
+ .btn { display: inline-flex; align-items: center; gap: 7px; height: 29px; padding: 0 12px 0 9px; border-radius: 9px; border: 1px solid var(--line2); background: var(--card); font-size: 12.5px; font-weight: 500; letter-spacing: -.005em; box-shadow: var(--rim), 0 1px 2px rgba(var(--shadow-rgb), .06); transition: border-color .15s, background-color .15s, transform .1s; }
146
141
  .btn:hover { border-color: var(--line3); }
147
- .btn.primary { background: var(--accent); border-color: var(--accent); color: #fff; box-shadow: 0 6px 14px -6px rgba(var(--accent-rgb), .7); }
148
- .btn.primary:hover { background: var(--accent-hi); }
142
+ .btn:active { transform: translateY(1px); }
143
+ .btn .agent { width: 15px; height: 15px; border-radius: 4px; box-shadow: none; }
144
+ .btn.primary { padding: 0 13px 0 10px; background: linear-gradient(180deg, var(--accent-hi), var(--accent)); border-color: transparent; color: #fff; box-shadow: inset 0 1px 0 rgba(255, 255, 255, .22), 0 6px 16px -6px rgba(var(--accent-rgb), .7); }
145
+ .btn.primary:hover { filter: brightness(1.06); }
146
+ .btn.danger { background: linear-gradient(180deg, #ff7a6e, var(--bad)); border-color: transparent; color: #fff; box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 6px 16px -6px rgba(var(--bad-rgb), .7); }
147
+ .btn.danger:hover { filter: brightness(1.05); }
149
148
  .btn:disabled { opacity: .45; box-shadow: none; }
150
- .link { margin-top: 10px; display: inline-flex; align-items: center; gap: 5px; font-size: 12px; font-weight: 500; color: var(--accent); }
149
+ .btn svg { width: 13px; height: 13px; }
150
+ .links { display: flex; gap: 14px; margin-top: 11px; }
151
+ .link { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; font-weight: 500; color: var(--accent); }
151
152
  .link:hover { color: var(--accent-hi); }
153
+ .link.quiet { color: var(--fg3); }
154
+ .link.quiet:hover { color: var(--bad); }
152
155
  .link svg { width: 13px; height: 13px; }
153
- .busy { display: flex; align-items: center; gap: 8px; height: 28px; font-size: 12px; color: var(--fg2); }
156
+ .muted-note { font-size: 12px; color: var(--fg3); }
157
+ .busy { display: flex; align-items: center; gap: 8px; height: 29px; font-size: 12px; color: var(--fg2); }
154
158
  .spin { width: 12px; height: 12px; border-radius: 50%; border: 1.5px solid var(--accent); border-right-color: transparent; animation: spin .7s linear infinite; }
155
- .empty { margin: 0 8px 10px; padding: 14px; border-radius: 10px; border: 1px dashed var(--line3); font-size: 12px; color: var(--fg3); }
159
+
160
+ /* add a chat by ID */
161
+ .add-form { display: flex; gap: 6px; padding: 2px 8px 6px 38px; }
162
+ .add-form input { flex: 1; min-width: 0; height: 29px; padding: 0 10px; border-radius: 9px; border: 1px solid var(--line2); background: var(--card); font: 11.5px var(--mono); color: var(--fg); cursor: text; user-select: text; }
163
+ .add-form input::placeholder { font-family: var(--sans); font-size: 12px; color: var(--fg3); }
164
+ .add-form input:focus { outline: none; border-color: rgba(var(--accent-rgb), .55); box-shadow: 0 0 0 3px var(--accent-soft); }
165
+ .add-error { padding: 0 8px 8px 38px; font-size: 11.5px; color: var(--bad); }
166
+
167
+ .more { display: flex; align-items: center; gap: 9px; width: calc(100% - 24px); margin: 6px 12px 12px; padding: 10px 12px; border-radius: 11px; background: var(--card); box-shadow: var(--rim), 0 0 0 1px var(--line); font-size: 13px; font-weight: 500; color: var(--fg2); transition: color .15s, box-shadow .15s; }
168
+ .more:hover { color: var(--fg); box-shadow: var(--rim), 0 0 0 1px var(--line3); }
169
+ .more .gauge { width: 15px; height: 15px; color: var(--accent); }
170
+ .more .arrow { width: 14px; height: 14px; margin-left: auto; color: var(--fg3); transition: transform .2s var(--ease); }
171
+ .more:hover .arrow { transform: translateX(2px); color: var(--fg); }
172
+
173
+ /* usage */
174
+ .u { padding: 0 18px 12px; }
175
+ .u-agent + .u-agent { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--line); }
176
+ .u-head { display: flex; align-items: center; gap: 10px; margin-bottom: 9px; }
177
+ .u-win { display: flex; align-items: center; gap: 10px; height: 21px; }
178
+ .u-lbl { flex: none; width: 36px; font: 10.5px var(--mono); color: var(--fg3); }
179
+ .u-bar { flex: 1; height: 6px; border-radius: 999px; background: rgba(var(--ink-rgb), .08); overflow: hidden; }
180
+ .u-bar i { display: block; height: 100%; border-radius: inherit; background: linear-gradient(90deg, rgba(var(--accent-rgb), .55), var(--accent)); }
181
+ .u-bar i.warm { background: linear-gradient(90deg, rgba(240, 181, 74, .55), var(--long)); }
182
+ .u-bar i.hot { background: linear-gradient(90deg, rgba(var(--bad-rgb), .55), var(--bad)); }
183
+ .u-pct { flex: none; width: 40px; text-align: right; font: 500 11px var(--mono); }
184
+ .u-reset { flex: none; width: 88px; font: 10.5px var(--mono); color: var(--fg3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
185
+ .u-meta { margin-top: 5px; font-size: 11.5px; color: var(--fg3); }
186
+ .u-meta .link { margin-left: 6px; font-size: 11.5px; }
187
+ .ask { margin: 0 14px 12px; padding: 12px 13px; border-radius: 12px; background: var(--card); box-shadow: var(--rim), inset 0 0 0 1px rgba(var(--accent-rgb), .35); }
188
+ .ask-title { font-size: 13px; font-weight: 600; }
189
+ .ask-body { margin-top: 3px; font-size: 12px; color: var(--fg2); }
190
+ .ask-btns { display: flex; gap: 6px; margin-top: 10px; }
156
191
 
157
192
  /* settings */
158
- .set { padding: 0 16px 12px; }
159
- .set .label { padding: 10px 0 7px; }
160
- .seg { display: flex; padding: 2px; border-radius: 8px; background: var(--sunk); border: 1px solid var(--line); }
161
- .seg button { flex: 1; height: 24px; padding: 0 9px; border-radius: 6px; font-size: 11.5px; font-weight: 500; color: var(--fg2); text-align: center; }
162
- .seg button[aria-pressed="true"] { background: var(--card); color: var(--fg); box-shadow: 0 1px 2px rgba(var(--ink-rgb), .12); }
163
- .seg button:disabled { opacity: .4; }
164
- .set-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 7px 0; }
193
+ .set { padding: 0 18px 10px; }
194
+ .set-row { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 12px 0; }
165
195
  .set-row + .set-row { border-top: 1px solid var(--line); }
166
- .set-row .k { font-size: 12.5px; font-weight: 500; }
167
- .set-row .hint { display: block; margin-top: 1px; font-size: 11px; font-weight: 400; color: var(--fg3); }
168
- .set-row .seg { flex: none; width: 150px; }
169
- .switch { flex: none; position: relative; width: 32px; height: 19px; border-radius: 999px; background: rgba(var(--ink-rgb), .16); transition: background-color .2s; }
170
- .switch::after { content: ""; position: absolute; top: 2px; left: 2px; width: 15px; height: 15px; border-radius: 50%; background: #fff; box-shadow: 0 1px 2px rgba(0, 0, 0, .2); transition: transform .2s var(--ease); }
196
+ .set-row .k { font-size: 13px; font-weight: 500; }
197
+ .set-row .hint { display: block; margin-top: 2px; font-size: 11.5px; font-weight: 400; color: var(--fg3); }
198
+ .seg { flex: none; display: flex; padding: 2px; border-radius: 9px; background: var(--sunk); box-shadow: inset 0 0 0 1px var(--line); }
199
+ .seg button { height: 25px; padding: 0 10px; border-radius: 7px; font-size: 12px; font-weight: 500; color: var(--fg2); text-align: center; }
200
+ .seg button[aria-pressed="true"] { background: var(--card); color: var(--fg); box-shadow: var(--rim), 0 1px 3px rgba(var(--shadow-rgb), .15); }
201
+ .switch { flex: none; position: relative; width: 34px; height: 20px; border-radius: 999px; background: rgba(var(--ink-rgb), .16); transition: background-color .2s; }
202
+ .switch::after { content: ""; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%; background: #fff; box-shadow: 0 1px 3px rgba(0, 0, 0, .25); transition: transform .2s var(--ease); }
171
203
  .switch[aria-checked="true"] { background: var(--accent); }
172
- .switch[aria-checked="true"]::after { transform: translateX(13px); }
204
+ .switch[aria-checked="true"]::after { transform: translateX(14px); }
205
+ .switch:disabled { opacity: .4; }
173
206
 
174
207
  /* footer */
175
- .foot { display: flex; align-items: center; justify-content: space-between; padding: 9px 12px 9px 16px; border-top: 1px solid var(--line); background: var(--foot); font-size: 11.5px; color: var(--fg3); }
176
- kbd { margin-right: 6px; padding: 1px 5px; border-radius: 4px; border: 1px solid var(--line3); background: var(--card); font: 500 10.5px var(--mono); color: var(--fg); }
177
- .quit { padding: 3px 7px; border-radius: 6px; font-size: 11.5px; color: var(--fg2); }
178
- .quit:hover { background: rgba(var(--ink-rgb), .06); color: var(--fg); }
179
-
180
- /* brief preview */
181
- .one-line { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
182
- .pad { padding: 4px 8px 12px; }
183
- .btn svg { width: 13px; height: 13px; }
184
- .brief-scroll { max-height: 400px; padding: 0 16px 12px; }
185
- .md { font-size: 12.5px; line-height: 1.5; color: var(--fg2); user-select: text; cursor: auto; }
186
- .md h3 { margin: 2px 0 8px; font: 500 16px/1.25 var(--serif); color: var(--fg); }
187
- .md h4 { margin: 14px 0 5px; font-size: 12.5px; font-weight: 600; color: var(--fg); }
188
- .md p { margin: 0 0 7px; }
189
- .md strong { color: var(--fg); font-weight: 600; }
190
- .md code { padding: 0 4px; border-radius: 4px; background: var(--sunk); border: 1px solid var(--line); font: 11px var(--mono); color: var(--fg); word-break: break-word; }
191
- .md pre { margin: 0 0 8px; padding: 8px 10px; border-radius: 8px; background: var(--sunk); border: 1px solid var(--line); font: 11px/1.45 var(--mono); color: var(--fg); white-space: pre-wrap; word-break: break-word; }
192
- .md blockquote { margin: 0 0 8px; padding-left: 10px; border-left: 2px solid rgba(var(--accent-rgb), .45); }
193
- .md blockquote p { margin: 0 0 3px; }
194
- .md ul { margin: 0 0 8px; padding-left: 16px; }
195
- .md li { margin: 0 0 3px; }
196
- .md li:has(.box) { list-style: none; margin-left: -16px; display: flex; gap: 7px; align-items: baseline; }
197
- .box { flex: none; width: 10px; height: 10px; border-radius: 3px; border: 1.5px solid var(--line3); transform: translateY(1px); }
198
- .box.done { background: var(--ok); border-color: var(--ok); }
199
- .brief-bar { display: flex; gap: 6px; align-items: center; padding: 10px 16px 12px; border-top: 1px solid var(--line); }
200
- .brief-bar .push { margin-left: auto; }
208
+ .foot { display: flex; align-items: center; justify-content: space-between; padding: 10px 12px 10px 18px; border-top: 1px solid var(--line); background: var(--foot); font-size: 11.5px; color: var(--fg3); }
209
+ kbd { margin-right: 6px; padding: 1px 5px; border-radius: 5px; border: 1px solid var(--line3); background: var(--card); font: 500 10.5px var(--mono); color: var(--fg); box-shadow: 0 1px 0 var(--line2); }
210
+ .quit { padding: 3px 8px; border-radius: 7px; font-size: 11.5px; color: var(--fg2); }
211
+ .quit:hover { background: var(--hover); color: var(--fg); }
212
+
213
+ .empty { margin: 0 18px 12px; padding: 14px; border-radius: 10px; border: 1px dashed var(--line3); font-size: 12px; color: var(--fg3); }
201
214
 
202
215
  @keyframes spin { to { transform: rotate(360deg); } }
203
216
  @media (prefers-reduced-motion: reduce) {
204
217
  *, *::before, *::after { transition: none !important; animation: none !important; }
205
218
  }
219
+
220
+ /* router (⌥H) */
221
+ .r-src { display: flex; align-items: center; gap: 10px; margin: 4px 16px 0; padding: 10px 12px; border-radius: 12px; background: var(--card); border: 1px solid var(--line2); }
222
+ .r-src-ic { position: relative; flex: none; display: grid; }
223
+ .r-src-ic .agent { width: 26px; height: 26px; border-radius: 7px; }
224
+ .r-src-ic .st { position: absolute; right: -7px; bottom: -6px; width: auto; }
225
+ .r-src-title { flex: 1; min-width: 0; font-weight: 600; font-size: 13.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
226
+ .r-src-time { flex: none; font: 11px var(--mono); color: var(--fg3); }
227
+ .r-lines { display: block; width: calc(100% - 40px); height: 46px; margin: 0 20px; overflow: visible; }
228
+ .r-lines path { fill: none; stroke: var(--line3); stroke-width: 1.5; transition: stroke .2s; }
229
+ .r-lines path.on { stroke: var(--accent); stroke-width: 2; stroke-dasharray: 5 5; animation: flow .7s linear infinite; }
230
+ .r-lines circle { fill: var(--accent); }
231
+ @keyframes flow { to { stroke-dashoffset: -10; } }
232
+ .r-tiles { display: grid; grid-template-columns: repeat(var(--n, 3), minmax(0, 1fr)); gap: 0; margin: 0 20px; }
233
+ .r-tiles.n1 { --n: 1; } .r-tiles.n2 { --n: 2; } .r-tiles.n3 { --n: 3; } .r-tiles.n4 { --n: 4; }
234
+ .tile { position: relative; display: flex; flex-direction: column; align-items: center; gap: 5px; padding: 10px 4px 8px; border-radius: 12px; border: 1px solid transparent; text-align: center; transition: background-color .15s, border-color .15s, transform .15s var(--ease); }
235
+ .tile.sel { background: var(--card); border-color: rgba(var(--accent-rgb), .45); box-shadow: 0 6px 18px -10px rgba(var(--accent-rgb), .6); transform: translateY(-1px); }
236
+ .tile-ic { position: relative; display: grid; }
237
+ .tile-ic .agent { width: 40px; height: 40px; border-radius: 10px; }
238
+ .tile-ic .agent.badge { font-size: 12px; }
239
+ .corner { position: absolute; right: -6px; bottom: -5px; min-width: 17px; height: 17px; padding: 0 3px; display: grid; place-items: center; border-radius: 6px; background: var(--fg); color: var(--bg); box-shadow: 0 0 0 2px var(--bg); }
240
+ .corner svg { width: 11px; height: 11px; }
241
+ .corner.mono { font: 600 8.5px var(--mono); }
242
+ .tile-name { font-size: 11.5px; font-weight: 500; color: var(--fg2); }
243
+ .tile.sel .tile-name { color: var(--fg); }
244
+ .tile-meter { width: 34px; height: 3px; border-radius: 999px; overflow: hidden; }
245
+ .tile-meter.on { background: rgba(var(--ink-rgb), .1); }
246
+ .tile-meter i { display: block; height: 100%; background: var(--accent); }
247
+ .tile-meter i.warm { background: var(--long); }
248
+ .tile-meter i.hot { background: var(--bad); }
249
+ .tile-key { position: absolute; top: 5px; right: 6px; font: 500 9px var(--mono); color: var(--fg3); opacity: 0; transition: opacity .15s; }
250
+ .tile.sel .tile-key, .tile:hover .tile-key { opacity: 1; }
251
+ .r-busy { justify-content: center; height: 110px; }
252
+ .r-foot { display: flex; align-items: center; justify-content: space-between; margin: 6px 12px 10px; }
253
+ .r-hint { display: flex; gap: 4px; }
254
+ .r-acts { display: flex; align-items: center; gap: 14px; }
255
+ .link.r-dismiss { color: var(--fg3); }
256
+ .link.r-dismiss:hover { color: var(--fg); }
257
+ .r-alert { margin: 4px 16px 8px; font-size: 12px; font-weight: 500; color: var(--bad); }
258
+ .r-hint kbd { padding: 1px 5px; border-radius: 4px; border: 1px solid var(--line3); font: 500 10px var(--mono); color: var(--fg3); }
259
+
260
+ /* chat health: a battery with one look per state (fresh, long, struggling), and the state in words in the card */
261
+ .battery { width: 22px; height: 11px; overflow: visible; }
262
+ .hw.lg .battery { width: 25px; height: 12.5px; }
263
+ .battery .b-case { fill: none; stroke: var(--fg3); stroke-width: 1.3; }
264
+ .battery .b-nub { fill: var(--fg3); }
265
+ .battery .b-fill { transition: width .3s, fill .3s; }
266
+ .battery.fresh .b-fill { fill: var(--health-fresh); }
267
+ .battery.long .b-fill { fill: var(--health-long); }
268
+ .battery.struggling .b-fill { fill: var(--health-bad); }
269
+ .battery.struggling { animation: lowbatt 2.2s ease-in-out infinite; }
270
+ @keyframes lowbatt { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }
271
+ @media (prefers-reduced-motion: reduce) { .battery.struggling { animation: none; } }
272
+ .h-state { font-size: 12px; font-weight: 600; }
273
+ .health.long .h-state { color: var(--health-long); }
274
+ .health.struggling .h-state { color: var(--health-bad); }
@@ -2,31 +2,33 @@
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="utf-8">
5
- <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:">
5
+ <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self'; style-src 'self'; font-src 'self'; img-src 'self' data:">
6
6
  <title>Harlay</title>
7
7
  <link rel="stylesheet" href="popover.css">
8
8
  </head>
9
9
  <body>
10
10
  <div class="wrap">
11
- <main class="panel" id="panel">
11
+ <main class="panel" id="panel" tabindex="-1">
12
12
  <header class="top">
13
- <div class="brand">
13
+ <button class="icon-btn" id="back" aria-label="Back" title="Back" hidden></button>
14
+ <div class="brand" id="brand">
14
15
  <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M2.5 20 10 4h4.3L6.8 20z" fill="currentColor" opacity=".45"/><path class="mk-b" d="M10.2 20 17.7 4H22l-7.5 16z"/></svg>
15
16
  harlay
16
17
  </div>
17
- <span class="pill" id="pill"><i></i><span id="pill-text">Standing by</span></span>
18
- <button class="icon-btn" id="toggle-theme" aria-label="Dark mode" title="Dark mode"></button>
19
- <button class="icon-btn" id="toggle-settings" aria-label="Settings" title="Settings"></button>
18
+ <h1 class="view-title" id="view-title" hidden></h1>
19
+ <button class="icon-btn" id="open-settings" aria-label="Settings" title="Settings"></button>
20
20
  </header>
21
21
  <section id="home"></section>
22
+ <section id="route" hidden></section>
23
+ <section id="usage" hidden></section>
22
24
  <section id="settings" hidden></section>
23
- <section id="brief" hidden></section>
24
25
  <footer class="foot">
25
- <span><kbd id="hotkey">⌥H</kbd>hands off the latest session</span>
26
+ <span><kbd id="hotkey">⌥H</kbd>routes your latest chat</span>
26
27
  <button class="quit" id="quit">Quit</button>
27
28
  </footer>
28
29
  </main>
29
30
  </div>
31
+ <script src="vendor/motion.js"></script>
30
32
  <script src="popover.js"></script>
31
33
  </body>
32
34
  </html>