@oharness/server 0.9.2 → 0.10.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.
- package/dist/i18n.js +356 -62
- package/dist/i18n.js.map +1 -1
- package/dist/main.js +4 -0
- package/dist/main.js.map +1 -1
- package/dist/page/client/account.js +430 -23
- package/dist/page/client/account.js.map +1 -1
- package/dist/page/client/base.js +20 -1
- package/dist/page/client/base.js.map +1 -1
- package/dist/page/client/composer.js +709 -58
- package/dist/page/client/composer.js.map +1 -1
- package/dist/page/client/computer.d.ts +27 -0
- package/dist/page/client/computer.js +358 -0
- package/dist/page/client/computer.js.map +1 -0
- package/dist/page/client/dialogs.js +93 -6
- package/dist/page/client/dialogs.js.map +1 -1
- package/dist/page/client/events.js +94 -7
- package/dist/page/client/events.js.map +1 -1
- package/dist/page/client/files.js +86 -1
- package/dist/page/client/files.js.map +1 -1
- package/dist/page/client/index.js +4 -0
- package/dist/page/client/index.js.map +1 -1
- package/dist/page/client/panels.js +12 -6
- package/dist/page/client/panels.js.map +1 -1
- package/dist/page/client/routines.js +296 -41
- package/dist/page/client/routines.js.map +1 -1
- package/dist/page/client/shell.js +229 -51
- package/dist/page/client/shell.js.map +1 -1
- package/dist/page/client/state.js +273 -19
- package/dist/page/client/state.js.map +1 -1
- package/dist/page/client/tasks.d.ts +8 -0
- package/dist/page/client/tasks.js +293 -0
- package/dist/page/client/tasks.js.map +1 -0
- package/dist/page/client/transcript.js +174 -6
- package/dist/page/client/transcript.js.map +1 -1
- package/dist/page/client/usage.js +1 -11
- package/dist/page/client/usage.js.map +1 -1
- package/dist/page/style.js +706 -104
- package/dist/page/style.js.map +1 -1
- package/dist/page.js +161 -54
- package/dist/page.js.map +1 -1
- package/dist/prefs.d.ts +14 -0
- package/dist/prefs.js +8 -1
- package/dist/prefs.js.map +1 -1
- package/dist/remote-crypto.d.ts +78 -0
- package/dist/remote-crypto.js +216 -0
- package/dist/remote-crypto.js.map +1 -0
- package/dist/remote.d.ts +103 -0
- package/dist/remote.js +498 -0
- package/dist/remote.js.map +1 -0
- package/dist/server.d.ts +70 -0
- package/dist/server.js +1291 -49
- package/dist/server.js.map +1 -1
- package/dist/starters.d.ts +92 -0
- package/dist/starters.js +213 -0
- package/dist/starters.js.map +1 -0
- package/dist/theme.js +5 -0
- package/dist/theme.js.map +1 -1
- package/dist/turn-shots.d.ts +27 -0
- package/dist/turn-shots.js +71 -0
- package/dist/turn-shots.js.map +1 -0
- package/package.json +2 -2
package/dist/page/style.js
CHANGED
|
@@ -32,32 +32,105 @@ code,pre,.mono{font-family:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,
|
|
|
32
32
|
|
|
33
33
|
/* sidebar */
|
|
34
34
|
aside{
|
|
35
|
-
width:
|
|
36
|
-
display:flex;flex-direction:column;overflow:hidden;
|
|
35
|
+
--side-w:296px;width:var(--side-w);flex:0 0 var(--side-w);
|
|
36
|
+
display:flex;flex-direction:column;align-items:flex-end;overflow:hidden;
|
|
37
|
+
}
|
|
38
|
+
/* Folding, the sidebar narrows while its contents keep their width, pinned
|
|
39
|
+
to its right edge, so they slide out to the left behind the frame rather
|
|
40
|
+
than squeezing; the tab strip's inset grows by as much as the sidebar
|
|
41
|
+
loses, so the first tab glides to its place beside the toggle. Only once a
|
|
42
|
+
hand has touched the toggle: a page opened folded starts folded. */
|
|
43
|
+
#side-top,#side-body{width:var(--side-w);flex-shrink:0}
|
|
44
|
+
body.nav-anim>aside{transition:width .26s cubic-bezier(.2,0,0,1),flex-basis .26s cubic-bezier(.2,0,0,1)}
|
|
45
|
+
body.nav-anim #tabs{transition:padding-left .26s cubic-bezier(.2,0,0,1)}
|
|
46
|
+
body.nav-anim .wordmark{transition:opacity .14s}
|
|
47
|
+
body.nav-collapsed .wordmark{opacity:0}
|
|
48
|
+
@media (prefers-reduced-motion:reduce){body.nav-anim>aside,body.nav-anim #tabs,body.nav-anim .wordmark{transition:none}}
|
|
49
|
+
/*
|
|
50
|
+
* A frame with the panel as a card inside it.
|
|
51
|
+
*
|
|
52
|
+
* The top bar and the rail are one L in the window's own colour, --frame, and
|
|
53
|
+
* the panel and the page sit inside it. It takes a third colour: a top bar
|
|
54
|
+
* over the sidebar in either of the other two runs into the surface below it
|
|
55
|
+
* in that colour — a grey one boxed the white rail in, a white one ran on
|
|
56
|
+
* into the page. The panel is a card set into the frame, rounded all round
|
|
57
|
+
* and clear of the page by a margin. The traffic lights sit on the frame, one
|
|
58
|
+
* colour, where the rail alone is too narrow to hold them.
|
|
59
|
+
*/
|
|
60
|
+
#side-top{height:36px;flex:0 0 36px;background:var(--frame);display:flex;align-items:center;overflow:hidden}
|
|
61
|
+
/* Just past the toggle, which ends at 40px on the web and at 108px beside
|
|
62
|
+
the traffic lights, and fixed there, so the mark stays in the corner when
|
|
63
|
+
the sidebar folds and only the name goes with it. */
|
|
64
|
+
.brand{position:fixed;z-index:30;top:0;left:48px;height:36px;display:flex;align-items:center;gap:8px;white-space:nowrap;pointer-events:none}
|
|
65
|
+
body.inset-titlebar .brand{left:116px}
|
|
66
|
+
body.inset-titlebar #side-top{-webkit-app-region:drag}
|
|
67
|
+
#side-body{flex:1 1 auto;display:flex;min-height:0;background:var(--frame)}
|
|
68
|
+
/*
|
|
69
|
+
* Two columns: a rail of the app's places, and the panel of the work.
|
|
70
|
+
*
|
|
71
|
+
* The rail is the frame and the panel a card in it, so the two read as
|
|
72
|
+
* different surfaces without a line between them.
|
|
73
|
+
*/
|
|
74
|
+
#rail{
|
|
75
|
+
width:52px;flex:0 0 52px;background:var(--frame);display:flex;flex-direction:column;
|
|
76
|
+
align-items:center;padding:8px 0 10px;gap:6px;overflow:hidden;
|
|
77
|
+
}
|
|
78
|
+
#rail #nav{padding:0;gap:4px;align-items:center}
|
|
79
|
+
#rail .nav-item{width:36px;height:36px;padding:0;justify-content:center;border-radius:9px;color:var(--dim)}
|
|
80
|
+
#rail .nav-item:hover{color:var(--fg)}
|
|
81
|
+
#rail .nav-item.active{color:var(--fg)}
|
|
82
|
+
/* Drawn at 18px on the 16 grid, the shared 1.5 stroke would render at 1.7px
|
|
83
|
+
and read heavier than every other icon on the page; 1.2 lands near 1.35. */
|
|
84
|
+
#rail .nav-item .ico{width:18px;height:18px;stroke-width:1.2}
|
|
85
|
+
/* The panel's icons follow at their own 14-16px, so the rail and the panel
|
|
86
|
+
beside it draw at one weight. Matched on the attribute, so the mark keeps its 2.5. */
|
|
87
|
+
#side-panel svg[stroke-width="1.5"]{stroke-width:1.25}
|
|
88
|
+
#rail .nav-item>span{display:none}
|
|
89
|
+
.rail-tip{
|
|
90
|
+
position:fixed;z-index:80;transform:translateY(-50%);padding:4px 9px;border-radius:7px;
|
|
91
|
+
background:var(--accent);color:var(--bg);font-size:12px;line-height:1.4;white-space:nowrap;
|
|
92
|
+
pointer-events:none;opacity:0;transition:opacity .08s;
|
|
93
|
+
}
|
|
94
|
+
.rail-tip.on{opacity:1}
|
|
95
|
+
#side-panel{
|
|
96
|
+
width:236px;flex:0 0 236px;background:var(--panel);border-radius:12px;margin:0 8px 8px 0;
|
|
97
|
+
display:flex;flex-direction:column;overflow:hidden;
|
|
98
|
+
}
|
|
99
|
+
#side-new{padding:0 8px 2px;display:flex;align-items:center;gap:2px}
|
|
100
|
+
#side-new #new{flex:1;min-width:0}
|
|
101
|
+
#side-search{
|
|
102
|
+
display:grid;place-items:center;flex:0 0 auto;width:28px;height:28px;border:0;border-radius:6px;
|
|
103
|
+
background:transparent;color:var(--dim);cursor:pointer;padding:0;
|
|
37
104
|
}
|
|
38
|
-
|
|
39
|
-
|
|
105
|
+
#side-search:hover{background:var(--hover);color:var(--fg)}
|
|
106
|
+
#side-search .ico-sm{width:16px;height:16px}
|
|
107
|
+
/* Folded away on a desktop, the whole sidebar goes, rail and all, and the
|
|
108
|
+
page has the window: keeping the rail left a strip of chrome and pushed the
|
|
109
|
+
tabs a rail's width plus the traffic lights in from the edge. */
|
|
40
110
|
body.nav-collapsed>aside{width:0;flex-basis:0}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
body.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
111
|
+
/* In the top bar, at the same spot folded or not, so the click that folded the
|
|
112
|
+
sidebar can bring it straight back. On a desktop it steps past the traffic
|
|
113
|
+
lights (14-74px, set in desktop/main.js). */
|
|
114
|
+
#nav-toggle{
|
|
115
|
+
position:fixed;z-index:30;top:4px;left:12px;width:28px;height:28px;border:0;border-radius:7px;
|
|
116
|
+
background:transparent;color:var(--dim);cursor:pointer;padding:0;
|
|
117
|
+
display:grid;place-items:center;-webkit-app-region:no-drag;
|
|
118
|
+
}
|
|
119
|
+
#nav-toggle:hover{background:var(--hover);color:var(--fg)}
|
|
120
|
+
#nav-toggle svg{width:16px;height:16px;display:block;shape-rendering:geometricPrecision}
|
|
121
|
+
body.inset-titlebar #nav-toggle{left:80px}
|
|
122
|
+
body.shelf-full #nav-toggle{display:none}
|
|
123
|
+
/* The window has no title bar, so the strip's empty stretch is its handle.
|
|
124
|
+
Folded, the strip is the whole top bar and starts past the mark beside the
|
|
125
|
+
toggle: 66px on the web, 134px beside the traffic lights. */
|
|
126
|
+
body.inset-titlebar #tabs{-webkit-app-region:drag}
|
|
127
|
+
body.inset-titlebar #tabs>*{-webkit-app-region:no-drag}
|
|
128
|
+
body.nav-collapsed #tabs{padding-left:74px}
|
|
129
|
+
body.inset-titlebar.nav-collapsed #tabs{padding-left:142px}
|
|
130
|
+
/* The name is in the top bar on a wide screen, so the panel's header is only
|
|
131
|
+
the phone drawer's, where it carries the name and the close button. */
|
|
132
|
+
aside header{display:none;padding:8px 14px;min-height:44px;gap:8px;align-items:center}
|
|
133
|
+
#side-panel{padding-top:8px}
|
|
61
134
|
.mark{width:18px;height:18px;color:var(--fg);opacity:.85;flex:0 0 auto}
|
|
62
135
|
.mark svg{width:18px;height:18px;display:block}
|
|
63
136
|
/* Slightly tightened and a touch heavier than body text, which is what makes
|
|
@@ -95,6 +168,10 @@ body.inset-titlebar aside header{padding-top:38px;-webkit-app-region:drag}
|
|
|
95
168
|
.fold .ico-sm{width:16px;height:16px;flex:0 0 auto;opacity:.5}
|
|
96
169
|
.fold.open .ico-sm,.fold:hover .ico-sm{opacity:.9}
|
|
97
170
|
.fold-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
171
|
+
/* A worktree hangs under the project it came from: indented by its icon's
|
|
172
|
+
* width, no rule or outline to say so. */
|
|
173
|
+
.fold.wt{padding-left:24px;color:var(--dim)}
|
|
174
|
+
.fold.wt.open,.fold.wt:hover{color:var(--fg)}
|
|
98
175
|
/* The routine group's run count, where a project header keeps its actions. */
|
|
99
176
|
.fold .count{flex:0 0 auto;font-size:11px;color:var(--dim);padding-right:4px}
|
|
100
177
|
/* A gap between projects, but not above the first one. The selector names the
|
|
@@ -106,9 +183,13 @@ body.inset-titlebar aside header{padding-top:38px;-webkit-app-region:drag}
|
|
|
106
183
|
edge, where the count and the ⋯ already live and a third thing would crowd
|
|
107
184
|
them; dim, because it marks a row rather than asking for attention. */
|
|
108
185
|
.pin-mark{
|
|
109
|
-
|
|
110
|
-
|
|
186
|
+
flex:0 0 auto;margin-left:3px;padding:2px;border-radius:4px;opacity:.5;
|
|
187
|
+
display:inline-grid;place-items:center;cursor:pointer;
|
|
111
188
|
}
|
|
189
|
+
.pin-mark svg{width:11px;height:11px;display:block}
|
|
190
|
+
/* It is the unpin control, so it answers the pointer like one: full ink and a
|
|
191
|
+
fill on hover, the same as the ⋯ beside it. */
|
|
192
|
+
.pin-mark:hover,.pin-mark:focus-visible{opacity:1;background:var(--sel);outline:none}
|
|
112
193
|
/* Dragging: the row being carried fades, and the one under the pointer grows
|
|
113
194
|
the edge it would be dropped against. A line on the row rather than a
|
|
114
195
|
floating element, because this list is rebuilt on every repaint and a
|
|
@@ -191,7 +272,7 @@ body.inset-titlebar aside header{padding-top:38px;-webkit-app-region:drag}
|
|
|
191
272
|
already had two rules in it and a third would have made a list of five
|
|
192
273
|
entries look like three lists. */
|
|
193
274
|
.row-menu .menu-head{
|
|
194
|
-
padding:
|
|
275
|
+
padding:12px 12px 11px;margin-bottom:6px;border-radius:12px;background:var(--field);
|
|
195
276
|
}
|
|
196
277
|
/* A section label, not a surface: the fill belongs to the account's facts
|
|
197
278
|
above, and on a one-word caption it read as a second card pressed against
|
|
@@ -199,6 +280,25 @@ body.inset-titlebar aside header{padding-top:38px;-webkit-app-region:drag}
|
|
|
199
280
|
.row-menu .menu-head.menu-section{
|
|
200
281
|
margin:10px 0 2px;padding:4px 10px;background:transparent;
|
|
201
282
|
}
|
|
283
|
+
/* The workspaces: a heading and its choices on one faint surface — lighter
|
|
284
|
+
than the account card above it, so the card stays the menu's one solid
|
|
285
|
+
thing and this reads as a quiet group rather than a second card. Inside,
|
|
286
|
+
the workspace in use carries a stronger fill, and hover sits between. */
|
|
287
|
+
.row-menu .menu-choices{margin:8px 0 4px;padding:2px 4px 4px;border-radius:12px;background:color-mix(in srgb,var(--fg) 2%,transparent)}
|
|
288
|
+
.row-menu .menu-choices .menu-head.menu-section{margin:4px 0 0;padding-left:6px}
|
|
289
|
+
.row-menu .menu-choices .menu-item{border-radius:9px}
|
|
290
|
+
.row-menu .menu-choices .menu-item:hover{background:color-mix(in srgb,var(--fg) 4%,transparent)}
|
|
291
|
+
.row-menu .menu-choices .menu-item.on{background:color-mix(in srgb,var(--fg) 6%,transparent)}
|
|
292
|
+
.row-menu .menu-choices .menu-item.on .menu-label{font-weight:500}
|
|
293
|
+
.row-menu .menu-choices .menu-section-action:hover,.row-menu .menu-choices .menu-section-action:focus-visible{background:color-mix(in srgb,var(--fg) 5%,transparent)}
|
|
294
|
+
/* A section heading with its one action at the right: the label keeps the
|
|
295
|
+
caption type, the action does not — it is a button, not part of the name. */
|
|
296
|
+
.row-menu .menu-section-row{display:flex;align-items:center;justify-content:space-between;gap:8px;padding-right:4px}
|
|
297
|
+
.menu-section-action{
|
|
298
|
+
height:22px;padding:0 8px;border:0;border-radius:6px;background:transparent;color:var(--dim);
|
|
299
|
+
font:inherit;font-size:12px;font-weight:500;letter-spacing:0;text-transform:none;cursor:pointer;
|
|
300
|
+
}
|
|
301
|
+
.menu-section-action:hover,.menu-section-action:focus-visible{background:var(--hover);color:var(--fg);outline:none}
|
|
202
302
|
.menu-head-main{font-size:14px;font-weight:500;color:var(--fg);font-variant-numeric:tabular-nums}
|
|
203
303
|
.menu-head-sub{margin-top:2px;font-size:12px;color:var(--dim)}
|
|
204
304
|
/* The billing card borrows .menu-head's surface but not its caption type: the
|
|
@@ -263,6 +363,15 @@ body.inset-titlebar aside header{padding-top:38px;-webkit-app-region:drag}
|
|
|
263
363
|
.row-menu .danger .menu-key{color:var(--dim)}
|
|
264
364
|
/* A rule, not a gap: the group below it is the one that cannot be undone. */
|
|
265
365
|
.row-menu .menu-sep{height:1px;margin:5px 8px;background:var(--line)}
|
|
366
|
+
/* The + menu's groups are told apart by space alone: no rule between them,
|
|
367
|
+
the same way nothing in it is outlined. */
|
|
368
|
+
#attach-picker .menu-sep{height:10px;margin:0;background:none}
|
|
369
|
+
/* Each group of the + menu opens with its own heading; the ones after the
|
|
370
|
+
first get room above them, which is what sets the groups apart. */
|
|
371
|
+
#attach-picker .menu-head:not(:first-child){padding-top:12px}
|
|
372
|
+
/* Tall enough for all three groups at once; the list scrolls only on a short
|
|
373
|
+
window, not on every one. */
|
|
374
|
+
#attach-picker .menu-body{max-height:min(72vh,560px)}
|
|
266
375
|
/* Sized from the title it stands in for, so the row does not jump when a
|
|
267
376
|
rename starts. */
|
|
268
377
|
.session .title-edit{
|
|
@@ -379,27 +488,19 @@ main{flex:1;display:flex;flex-direction:column;min-width:0}
|
|
|
379
488
|
* Same two shapes as the connector list: the vendor's own icon when the
|
|
380
489
|
* server has one, and a lettered tile when it does not. Sized to the row —
|
|
381
490
|
* the type is 13px, so the mark is 15px and not the 28px tile a card gets. */
|
|
382
|
-
/*
|
|
491
|
+
/* The mark as the vendor drew it, on nothing.
|
|
383
492
|
*
|
|
384
|
-
* A
|
|
385
|
-
*
|
|
386
|
-
*
|
|
387
|
-
*
|
|
388
|
-
*
|
|
389
|
-
*
|
|
390
|
-
|
|
391
|
-
* So a mark that did not bring a background is given one: the same faint
|
|
392
|
-
* fill, the same radius, the same inset. A mark that did bring one keeps it
|
|
393
|
-
* and fills the box, because drawing a second shape around a shape is the
|
|
394
|
-
* thing this is trying to avoid. Either way the block is identical and the
|
|
395
|
-
* ink inside it is the vendor's business. */
|
|
396
|
-
.vmark{width:15px;height:15px;flex:0 0 auto;border-radius:4px;object-fit:contain;
|
|
397
|
-
padding:1.5px;box-sizing:border-box;background:var(--sel)}
|
|
398
|
-
.vmark.filled{padding:0;background:none}
|
|
493
|
+
* A faint tile under every bare mark once gave the column a common edge, but
|
|
494
|
+
* it also shrank the ink to two thirds of the box and put a grey square
|
|
495
|
+
* beside every name — in a list that is all one vendor, a column of the same
|
|
496
|
+
* grey squares. The box is still the same size on every row, so the marks
|
|
497
|
+
* still line up; the ink now fills it. A vendor that ships its own tile keeps
|
|
498
|
+
* it (.filled), since that tile is its mark. */
|
|
499
|
+
.vmark{width:16px;height:16px;flex:0 0 auto;border-radius:3px;object-fit:contain;box-sizing:border-box}
|
|
399
500
|
/* The fallback, and the reason a missing icon is not a broken row: a lettered
|
|
400
501
|
tile in a colour of its own still tells two vendors apart at a glance. */
|
|
401
502
|
.vmono{
|
|
402
|
-
width:
|
|
503
|
+
width:16px;height:16px;flex:0 0 auto;border-radius:4px;box-sizing:border-box;
|
|
403
504
|
display:grid;place-items:center;color:#fff;
|
|
404
505
|
font-size:9px;font-weight:600;letter-spacing:0;text-transform:uppercase;
|
|
405
506
|
}
|
|
@@ -408,8 +509,7 @@ main{flex:1;display:flex;flex-direction:column;min-width:0}
|
|
|
408
509
|
/* Typed as svg.vglyph so it outranks the .ico-sm every drawn icon carries:
|
|
409
510
|
.6 is right for a chrome glyph and too faint for a vendor's mark. Ours is
|
|
410
511
|
bare strokes, so it takes the same block as any other bare mark. */
|
|
411
|
-
svg.vglyph{width:
|
|
412
|
-
padding:1.5px;box-sizing:border-box;border-radius:4px;background:var(--sel)}
|
|
512
|
+
svg.vglyph{width:16px;height:16px;opacity:1;box-sizing:border-box}
|
|
413
513
|
/* Black-on-transparent is what a good half of these vendors ship, and in the
|
|
414
514
|
dark theme that is a black square on a black row. Inverted only where the
|
|
415
515
|
bytes were measured to be exactly that. */
|
|
@@ -435,9 +535,6 @@ details.provider>summary .vmark,details.provider>summary .vmono,details.provider
|
|
|
435
535
|
.pp-item .vmark,.pp-item .vmono,.pp-item .vglyph{
|
|
436
536
|
width:20px;height:20px;border-radius:5px;font-size:11px;
|
|
437
537
|
}
|
|
438
|
-
details.provider>summary .vmark,.pp-item .vmark,
|
|
439
|
-
details.provider>summary .vglyph,.pp-item .vglyph{padding:2.5px}
|
|
440
|
-
details.provider>summary .vmark.filled,.pp-item .vmark.filled{padding:0}
|
|
441
538
|
.menu-head{
|
|
442
539
|
padding:8px 10px 4px;font-size:11px;font-weight:600;letter-spacing:.04em;
|
|
443
540
|
text-transform:uppercase;color:var(--dim);
|
|
@@ -498,18 +595,45 @@ details.provider>summary .vmark.filled,.pp-item .vmark.filled{padding:0}
|
|
|
498
595
|
justify-content:flex-end;flex-wrap:wrap;
|
|
499
596
|
}
|
|
500
597
|
/*
|
|
501
|
-
* The context meter: how much of the model's window this session is using
|
|
502
|
-
*
|
|
503
|
-
*
|
|
504
|
-
*
|
|
505
|
-
*
|
|
506
|
-
*
|
|
507
|
-
*
|
|
598
|
+
* The context meter: how much of the model's window this session is using,
|
|
599
|
+
* as a ring that fills clockwise. A ring rather than a bar because it sits in
|
|
600
|
+
* a row of words and has to read at a glance at the size of one; the numbers
|
|
601
|
+
* are in its tooltip. The sweep is a conic fill and the hole a mask, so there
|
|
602
|
+
* is no stroke anywhere. Its own track colour rather than --line, which at
|
|
603
|
+
* this size disappears into the background. --ctx-pct and --ctx-fill are set
|
|
604
|
+
* from the value in applyState.
|
|
508
605
|
*/
|
|
509
|
-
#ctx{
|
|
510
|
-
|
|
606
|
+
#ctx{
|
|
607
|
+
width:14px;height:14px;border-radius:50%;flex:0 0 auto;cursor:default;
|
|
608
|
+
background:conic-gradient(var(--ctx-fill,var(--brand)) calc(var(--ctx-pct,0) * 1%),var(--track) 0);
|
|
609
|
+
-webkit-mask:radial-gradient(circle,transparent 4.2px,#000 4.6px);
|
|
610
|
+
mask:radial-gradient(circle,transparent 4.2px,#000 4.6px);
|
|
611
|
+
}
|
|
612
|
+
#ctx{border:0;padding:0}
|
|
613
|
+
/* Focus as a change of size, not a ring: this design draws no outlines. */
|
|
614
|
+
#ctx{transition:transform .12s}
|
|
615
|
+
#ctx:focus-visible,#ctx[aria-expanded="true"]{outline:none;transform:scale(1.2)}
|
|
616
|
+
#ctx:hover{transform:scale(1.2)}
|
|
617
|
+
/* The context panel. Every part is a strength of the brand colour: the two
|
|
618
|
+
things the model is given every turn lighter, the conversation full, as it
|
|
619
|
+
is the part a person can do something about. */
|
|
620
|
+
:root{--ctx-system:color-mix(in srgb,var(--brand) 35%,transparent);--ctx-tools:color-mix(in srgb,var(--brand) 65%,transparent);--ctx-messages:var(--brand)}
|
|
621
|
+
.ctx-panel{width:300px;min-width:0;padding:14px 14px 8px;gap:10px;font-size:12.5px}
|
|
622
|
+
.ctx-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px}
|
|
623
|
+
.ctx-title{font-weight:600;color:var(--fg)}
|
|
624
|
+
.ctx-total{color:var(--dim);font-variant-numeric:tabular-nums}
|
|
625
|
+
.ctx-bar{position:relative;display:flex;height:8px;border-radius:4px;background:var(--track);overflow:hidden}
|
|
626
|
+
.ctx-bar i{display:block;height:100%;flex:0 0 auto}
|
|
627
|
+
.ctx-tick{position:absolute;top:0;bottom:0;width:2px;margin-left:-1px;background:var(--warn)}
|
|
628
|
+
.ctx-rows{display:flex;flex-direction:column;gap:5px}
|
|
629
|
+
.ctx-row{display:flex;align-items:center;gap:8px}
|
|
630
|
+
.ctx-dot{width:8px;height:8px;border-radius:50%;flex:0 0 auto}
|
|
631
|
+
.ctx-label{flex:1;color:var(--fg)}
|
|
632
|
+
.ctx-value{color:var(--dim);font-variant-numeric:tabular-nums}
|
|
633
|
+
.ctx-notes{display:flex;flex-direction:column;gap:2px;color:var(--dim);font-size:11.5px;line-height:1.5}
|
|
634
|
+
.ctx-compact{margin:0 -8px}
|
|
511
635
|
#ctx,#ctxtext{display:none}
|
|
512
|
-
#meta.has-ctx #ctx
|
|
636
|
+
#meta.has-ctx #ctx{display:block}
|
|
513
637
|
|
|
514
638
|
/*
|
|
515
639
|
* Over the transcript, not over the whole window.
|
|
@@ -518,31 +642,43 @@ details.provider>summary .vmark.filled,.pp-item .vmark.filled{padding:0}
|
|
|
518
642
|
* is in front. Spanning both left a band of empty chrome above the drawer that
|
|
519
643
|
* belonged to neither.
|
|
520
644
|
*
|
|
521
|
-
* The strip is the
|
|
522
|
-
* front tab is cut from the page under it — the
|
|
523
|
-
*
|
|
524
|
-
*
|
|
525
|
-
*
|
|
526
|
-
* or a grey line would sit between the front tab and the page it is part of.
|
|
645
|
+
* The strip is the top of the frame, at #side-top's height, so one bar runs
|
|
646
|
+
* across the window, and the front tab is cut from the page under it — the
|
|
647
|
+
* shape every browser uses. No bottom padding, or a line of frame would sit
|
|
648
|
+
* between the front tab and the page it is part of; no left padding, so the
|
|
649
|
+
* first tab's edge is the page's.
|
|
527
650
|
*/
|
|
528
651
|
#tabs{
|
|
529
|
-
display:flex;align-items:
|
|
530
|
-
background:var(--
|
|
652
|
+
display:flex;align-items:flex-end;gap:2px;height:36px;padding:0 12px 0 0;overflow-x:auto;flex:0 0 auto;
|
|
653
|
+
background:var(--frame);
|
|
531
654
|
}
|
|
532
655
|
#tabs:empty{display:none}
|
|
656
|
+
/* A scrollbar here takes its height out of the strip and lifts every tab off
|
|
657
|
+
the page, and a hovered tab's buttons were enough to bring one on. Tabs
|
|
658
|
+
shrink first, as a browser's do, and past that the strip still scrolls by
|
|
659
|
+
wheel or trackpad, only without the bar. */
|
|
660
|
+
#tabs{scrollbar-width:none}
|
|
661
|
+
#tabs::-webkit-scrollbar{display:none}
|
|
533
662
|
.tab{
|
|
534
|
-
display:flex;align-items:center;gap:7px;max-width:230px;height:30px;padding:0
|
|
663
|
+
display:flex;align-items:center;gap:7px;max-width:230px;min-width:72px;height:30px;padding:0 14px;
|
|
535
664
|
border:0;border-radius:8px 8px 0 0;background:transparent;color:var(--dim);
|
|
536
|
-
font:inherit;font-size:12.5px;cursor:pointer;flex:0
|
|
537
|
-
}
|
|
538
|
-
|
|
665
|
+
font:inherit;font-size:12.5px;cursor:pointer;flex:0 1 auto;
|
|
666
|
+
}
|
|
667
|
+
/* The tab under the pointer or in front keeps its width, so its buttons fit
|
|
668
|
+
and it is the others that give way. */
|
|
669
|
+
.tab:hover,.tab:focus-within,.tab.on{flex-shrink:0}
|
|
670
|
+
/* Hovered, a tab lifts halfway toward the page it would become, the way a
|
|
671
|
+
browser's do, rather than sinking into a darker grey the front tab never
|
|
672
|
+
is. Mixed from --bg so the dark theme lifts the same way. */
|
|
673
|
+
.tab:hover{background:color-mix(in srgb,var(--bg) 60%,transparent);color:var(--fg)}
|
|
539
674
|
.tab.on{background:var(--bg);color:var(--fg)}
|
|
540
|
-
.tab .
|
|
675
|
+
.tab-page .ico-sm{width:14px;height:14px;opacity:.75}
|
|
676
|
+
.tab .label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
|
|
541
677
|
/* Which project a tab is in, ahead of its title. Set quieter than the title,
|
|
542
678
|
which is still what identifies it. */
|
|
543
679
|
.tab .where{
|
|
544
680
|
max-width:80px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
|
|
545
|
-
font-size:11px;opacity:.6;flex:0
|
|
681
|
+
font-size:11px;opacity:.6;flex:0 1 auto;min-width:0;
|
|
546
682
|
}
|
|
547
683
|
.tab .where::after{content:"/";padding-left:5px;opacity:.5}
|
|
548
684
|
.tab .dot{width:6px;height:6px;border-radius:50%;background:var(--brand);flex:0 0 auto}
|
|
@@ -556,6 +692,10 @@ details.provider>summary .vmark.filled,.pp-item .vmark.filled{padding:0}
|
|
|
556
692
|
transparent — a transparent button still holds its space. A running
|
|
557
693
|
browser keeps its button, because its dot is what says so. */
|
|
558
694
|
.tab:hover .x,.tab:focus-within .x{display:grid}
|
|
695
|
+
/* Even padding while the title is alone, as a label is set; the right side
|
|
696
|
+
gives some back when a button comes in, so the button sits near the edge
|
|
697
|
+
and the tab grows by less. */
|
|
698
|
+
.tab:hover,.tab:focus-within,.tab:has(.tab-browser.running){padding-right:6px}
|
|
559
699
|
/* The tab's browser button's size. Much smaller and the 1.5px strokes thin
|
|
560
700
|
below a pixel and blur; the full 16px outweighs the tab's own text. */
|
|
561
701
|
.tab .x svg{width:14px;height:14px}
|
|
@@ -571,7 +711,7 @@ details.provider>summary .vmark.filled,.pp-item .vmark.filled{padding:0}
|
|
|
571
711
|
.tab:hover .tab-browser,.tab.on .tab-browser,.tab-browser.running{opacity:.62}
|
|
572
712
|
.tab-browser:hover,.tab-browser.on{opacity:1;background:var(--sel)}
|
|
573
713
|
.tab-browser svg{width:14px;height:14px}
|
|
574
|
-
.tab-browser i{display:none;position:absolute;right:2px;top:2px;width:5px;height:5px;border-radius:50%;background:var(--ok);box-shadow:0 0 0 1.5px var(--
|
|
714
|
+
.tab-browser i{display:none;position:absolute;right:2px;top:2px;width:5px;height:5px;border-radius:50%;background:var(--ok);box-shadow:0 0 0 1.5px var(--frame)}
|
|
575
715
|
.tab.on .tab-browser i{box-shadow:0 0 0 1.5px var(--bg)}
|
|
576
716
|
.tab-browser.running i{display:block}
|
|
577
717
|
#tab-add{
|
|
@@ -774,6 +914,83 @@ body.browser-resizing #browser-pane{transition:none}
|
|
|
774
914
|
}
|
|
775
915
|
#browser-pane.live #browser-frame{cursor:crosshair}
|
|
776
916
|
.browser-idle{margin:auto;text-align:center;max-width:32ch}
|
|
917
|
+
/* The Computer card: the window the agent is working in, floating over the
|
|
918
|
+
conversation's top-right corner while a turn works in it. Filled surfaces
|
|
919
|
+
and a shadow, no outline — the menus' elevation, since it sits above the
|
|
920
|
+
page the same way. The picture is letterboxed; the agent's cursor and its
|
|
921
|
+
click ripples sit over it in the conversation's accent. */
|
|
922
|
+
#computer-float{
|
|
923
|
+
position:absolute;z-index:30;top:52px;right:16px;width:340px;
|
|
924
|
+
transition:width .18s ease;cursor:grab;touch-action:none;
|
|
925
|
+
}
|
|
926
|
+
#computer-float[hidden]{display:none}
|
|
927
|
+
/* Larger, to read the window rather than glance at it. */
|
|
928
|
+
#computer-float.big{width:min(62%,880px)}
|
|
929
|
+
#computer-float.dragging{transition:none;user-select:none;cursor:grabbing}
|
|
930
|
+
/* The picture is the card: its corners, its shadow, and the window's own
|
|
931
|
+
proportions (set from the stream in computer.ts), so nothing frames it —
|
|
932
|
+
no bar above it, no fill around it, no bands at its sides. */
|
|
933
|
+
#computer-body{
|
|
934
|
+
aspect-ratio:16/10;max-height:70vh;position:relative;overflow:hidden;border-radius:12px;
|
|
935
|
+
background:var(--field);box-shadow:0 1px 2px #0000001a,0 10px 30px #00000033;
|
|
936
|
+
}
|
|
937
|
+
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]) #computer-body{box-shadow:0 1px 2px #0000004d,0 10px 30px #00000080}}
|
|
938
|
+
:root[data-theme="dark"] #computer-body{box-shadow:0 1px 2px #0000004d,0 10px 30px #00000080}
|
|
939
|
+
/* Over the picture: a pill naming the app on the left, round buttons on the
|
|
940
|
+
right. Translucent dark in both themes, since what is under them is
|
|
941
|
+
somebody else's window, light or dark. The buttons come up with the
|
|
942
|
+
pointer, so a card nobody is reaching for is all picture. */
|
|
943
|
+
#computer-head{
|
|
944
|
+
position:absolute;z-index:2;top:8px;left:8px;right:8px;
|
|
945
|
+
display:flex;align-items:center;gap:6px;pointer-events:none;
|
|
946
|
+
}
|
|
947
|
+
#computer-label{
|
|
948
|
+
display:inline-flex;align-items:center;gap:6px;min-width:0;max-width:100%;margin-right:auto;
|
|
949
|
+
height:24px;padding:0 9px;border-radius:999px;font-size:11.5px;color:#fff;
|
|
950
|
+
background:#000000a6;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
|
|
951
|
+
}
|
|
952
|
+
#computer-live{width:6px;height:6px;flex:0 0 auto;display:grid;place-items:center}
|
|
953
|
+
#computer-live[hidden]{display:none}
|
|
954
|
+
#computer-live i,#computer-pill i{width:6px;height:6px;border-radius:50%;background:var(--ok);animation:computer-pulse 1.6s ease-in-out infinite}
|
|
955
|
+
#computer-pill i{width:7px;height:7px}
|
|
956
|
+
@keyframes computer-pulse{50%{opacity:.35}}
|
|
957
|
+
#computer-where{min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
958
|
+
#computer-head button{
|
|
959
|
+
pointer-events:auto;width:28px;height:28px;padding:0;border:0;border-radius:50%;flex:0 0 auto;
|
|
960
|
+
display:grid;place-items:center;cursor:pointer;color:#fff;
|
|
961
|
+
background:#000000a6;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
|
|
962
|
+
opacity:0;transition:opacity .15s,background .15s;
|
|
963
|
+
}
|
|
964
|
+
#computer-float:hover #computer-head button,#computer-head button:focus-visible{opacity:1}
|
|
965
|
+
@media (hover:none){#computer-head button{opacity:1}}
|
|
966
|
+
#computer-head button:hover{background:#000000d9}
|
|
967
|
+
#computer-head button svg{width:13px;height:13px}
|
|
968
|
+
#computer-stop:hover{background:#c62828e6!important}
|
|
969
|
+
#computer-stop svg{width:11px!important;height:11px!important}
|
|
970
|
+
.computer-video{position:absolute;inset:0;width:100%;height:100%;object-fit:contain}
|
|
971
|
+
.computer-cursor{position:absolute;left:0;top:0;width:22px;height:22px;opacity:0;pointer-events:none;transition:transform .26s cubic-bezier(.3,.7,.2,1),opacity .3s}
|
|
972
|
+
.computer-cursor.on{opacity:1}
|
|
973
|
+
.computer-cursor svg{width:22px;height:22px;display:block;filter:drop-shadow(0 1px 2px #00000059)}
|
|
974
|
+
.computer-cursor path{fill:var(--brand);stroke:#fff;stroke-width:1.6;stroke-linejoin:round}
|
|
975
|
+
.computer-ring{position:absolute;width:30px;height:30px;margin:-15px 0 0 -15px;border-radius:50%;background:color-mix(in srgb,var(--brand) 35%,transparent);pointer-events:none;animation:computer-ring .5s ease-out forwards}
|
|
976
|
+
@keyframes computer-ring{from{transform:scale(.3);opacity:.9}to{transform:scale(1.5);opacity:0}}
|
|
977
|
+
.computer-empty{position:absolute;inset:0;display:grid;place-items:center;padding:24px;text-align:center;color:var(--dim);font-size:12.5px}
|
|
978
|
+
.computer-empty[hidden],.computer-empty:empty{display:none}
|
|
979
|
+
/* Folded: where it is working, one click from the card again. */
|
|
980
|
+
#computer-pill{
|
|
981
|
+
position:absolute;z-index:30;top:52px;right:16px;max-width:280px;
|
|
982
|
+
display:inline-flex;align-items:center;gap:7px;height:30px;padding:0 12px;
|
|
983
|
+
border:0;border-radius:999px;background:var(--panel);color:var(--fg);font:inherit;font-size:12px;cursor:pointer;
|
|
984
|
+
box-shadow:0 1px 2px #0000000f,0 4px 14px #0000001f;
|
|
985
|
+
}
|
|
986
|
+
#computer-pill[hidden]{display:none}
|
|
987
|
+
#computer-pill span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
988
|
+
#computer-pill:hover{background:var(--hover)}
|
|
989
|
+
.att.computer .n.clickable{cursor:pointer}
|
|
990
|
+
@media (max-width:900px){
|
|
991
|
+
#computer-float,#computer-float.big{width:calc(100% - 24px);right:12px}
|
|
992
|
+
}
|
|
993
|
+
.embed #computer-float,.embed #computer-pill{display:none!important}
|
|
777
994
|
/* One column at a time on a narrow window: two would leave neither readable. */
|
|
778
995
|
#browser-hide .ico-x{display:none}
|
|
779
996
|
#browser-scrim{display:none}
|
|
@@ -846,7 +1063,16 @@ body.panelled #composer,body.panelled #jump,body.panelled #changes-btn{display:n
|
|
|
846
1063
|
#explorer-actions button:hover{background:var(--hover);color:var(--fg)}
|
|
847
1064
|
#explorer-actions svg{width:15px;height:15px}
|
|
848
1065
|
#explorer-body{flex:1;display:flex;min-height:0}
|
|
849
|
-
#explorer-
|
|
1066
|
+
#explorer-side{width:var(--explorer-tree-width,min(40%,260px));flex:0 0 auto;display:flex;flex-direction:column;min-height:0}
|
|
1067
|
+
#explorer-find{
|
|
1068
|
+
display:flex;align-items:center;gap:6px;margin:2px 8px 6px;padding:0 8px;height:30px;border-radius:8px;
|
|
1069
|
+
background:var(--hover);color:var(--dim);cursor:text;
|
|
1070
|
+
}
|
|
1071
|
+
#explorer-find svg{width:13px;height:13px;flex:0 0 auto}
|
|
1072
|
+
#explorer-q{flex:1;min-width:0;border:0;outline:0;background:transparent;color:var(--fg);font:inherit;font-size:12.5px;padding:0}
|
|
1073
|
+
#explorer-q::-webkit-search-cancel-button{cursor:pointer}
|
|
1074
|
+
#explorer-tree{flex:1;min-height:0;overflow:auto;padding:2px 6px 12px}
|
|
1075
|
+
.explorer-dir{color:var(--dim);font-size:11.5px;margin-left:6px;overflow:hidden;text-overflow:ellipsis;min-width:0;direction:rtl;text-align:left}
|
|
850
1076
|
/* The line between the tree and the file, and the handle that moves it: a
|
|
851
1077
|
gap wide enough to grab, drawn only while it is being pointed at. */
|
|
852
1078
|
#explorer-split{flex:0 0 7px;margin:0 -3px;position:relative;z-index:1;cursor:col-resize;touch-action:none;outline:0}
|
|
@@ -1122,7 +1348,11 @@ body.shelf-full #main,body.shelf-full aside,body.shelf-full #top{display:none}
|
|
|
1122
1348
|
* positioned from the viewport instead and reuses only the surface styling.
|
|
1123
1349
|
*/
|
|
1124
1350
|
.menu.pop{position:fixed;bottom:auto;left:auto;min-width:220px}
|
|
1125
|
-
|
|
1351
|
+
/* The scrollbar's gutter is kept on both sides, and the composer below gives
|
|
1352
|
+
up the same width on each side (see #composer), so a message and the box it
|
|
1353
|
+
was typed in share both edges at every window width. Taken from one side
|
|
1354
|
+
only, it pushed every message half a scrollbar left of the box. */
|
|
1355
|
+
#log{padding:28px 24px 8px;scrollbar-gutter:stable both-edges}
|
|
1126
1356
|
/*
|
|
1127
1357
|
* One column width, named once.
|
|
1128
1358
|
*
|
|
@@ -1230,6 +1460,8 @@ body.shelf-full #main,body.shelf-full aside,body.shelf-full #top{display:none}
|
|
|
1230
1460
|
max-width:220px;max-height:160px;border-radius:8px;cursor:zoom-in;
|
|
1231
1461
|
background:var(--bg);object-fit:cover;
|
|
1232
1462
|
}
|
|
1463
|
+
/* A clip a little larger than a picture, since it is watched, not glanced at. */
|
|
1464
|
+
.user .shots video{max-width:min(320px,100%);max-height:200px;border-radius:8px;background:#000;display:block}
|
|
1233
1465
|
.user{margin-bottom:18px}
|
|
1234
1466
|
.panel{max-width:720px;margin:8px auto 0}
|
|
1235
1467
|
.panel-loading{display:flex;align-items:center;justify-content:center;gap:8px;padding-block:48px;font-size:13px}
|
|
@@ -1451,6 +1683,9 @@ details.setting>summary:focus-visible{outline:none;background:var(--hover)}
|
|
|
1451
1683
|
.setting-status{display:flex;align-items:center;gap:7px;margin-top:8px;color:var(--dim)}
|
|
1452
1684
|
.status-dot{flex:none;width:7px;height:7px;border-radius:50%;background:var(--dim);opacity:.6}
|
|
1453
1685
|
.status-dot.ok{background:var(--ok);opacity:1}
|
|
1686
|
+
/* A paired phone: its name, when, and the way to forget it, on one line. */
|
|
1687
|
+
.remote-phone{display:flex;align-items:center;gap:10px;min-height:28px}
|
|
1688
|
+
.remote-phone .remote-phone-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
1454
1689
|
/* The presets and the calendar on one line, on the same baseline. */
|
|
1455
1690
|
.usage-window{display:flex;flex-wrap:wrap;align-items:center;gap:8px}
|
|
1456
1691
|
.usage-kind-title{display:flex;align-items:baseline;justify-content:space-between;gap:12px}
|
|
@@ -1709,13 +1944,41 @@ details.provider>summary:hover{background:var(--hover)}
|
|
|
1709
1944
|
* with it. */
|
|
1710
1945
|
.row-head .ghost+.ghost,.row-head .ghost+.switch,.row-head .switch+.ghost{margin-left:0}
|
|
1711
1946
|
.routine-where{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
|
|
1712
|
-
.routine-history{margin-top:
|
|
1713
|
-
.routine-history
|
|
1714
|
-
|
|
1715
|
-
.routine-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
.routine-
|
|
1947
|
+
.routine-history{margin-top:8px}
|
|
1948
|
+
.routine-history-head{display:inline-flex;align-items:center;gap:6px;border:0;background:transparent;
|
|
1949
|
+
font:inherit;cursor:pointer;padding:3px 6px;margin:0 -6px 4px;border-radius:6px}
|
|
1950
|
+
.routine-history-head:hover{background:var(--hover);color:var(--fg)}
|
|
1951
|
+
.routine-history-head .chev{display:inline-block;transition:transform .15s;font-size:10px}
|
|
1952
|
+
.routine-history.folded .chev{transform:rotate(-90deg)}
|
|
1953
|
+
.routine-history.folded .routine-runs{display:none}
|
|
1954
|
+
.routine-runs{display:flex;flex-direction:column;gap:2px}
|
|
1955
|
+
/* Each run a quiet row that lights on hover: status, time, facts on one line,
|
|
1956
|
+
and the first line of what it said beneath. No rules between them. */
|
|
1957
|
+
.routine-run{display:flex;flex-direction:column;gap:3px;text-align:left;border:0;background:transparent;
|
|
1958
|
+
color:var(--fg);font:inherit;font-size:12.5px;padding:7px 8px;margin:0 -8px;border-radius:8px;min-width:0}
|
|
1959
|
+
.routine-run[role=button]{cursor:pointer}
|
|
1960
|
+
.routine-run[role=button]:hover,.routine-run.open{background:var(--hover)}
|
|
1961
|
+
.routine-run:focus-visible{outline:none;background:var(--hover)}
|
|
1962
|
+
.routine-run-detail{display:none;flex-direction:column;align-items:flex-start;gap:8px;margin-top:4px;cursor:default}
|
|
1963
|
+
.routine-run.open .routine-run-detail{display:flex}
|
|
1964
|
+
.routine-run.open:not(.running) .routine-run-text{display:none}
|
|
1965
|
+
.routine-run-full{word-break:break-word;max-height:360px;overflow:auto;width:100%;
|
|
1966
|
+
line-height:1.55;user-select:text;color:var(--fg);font-size:13px}
|
|
1967
|
+
.routine-run-full.err{color:var(--err);white-space:pre-wrap}
|
|
1968
|
+
.routine-run-full.md>:first-child{margin-top:0}
|
|
1969
|
+
.routine-run.running .routine-run-text{color:var(--fg)}
|
|
1970
|
+
.routine-run-top{display:flex;gap:10px;align-items:baseline;flex-wrap:wrap}
|
|
1971
|
+
.routine-run-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}
|
|
1972
|
+
.routine-run-text.err{color:var(--err)}
|
|
1973
|
+
.run-status{font-size:11px;font-weight:550;padding:1px 8px;border-radius:999px;flex:0 0 auto}
|
|
1974
|
+
.run-status.ok{color:var(--ok);background:color-mix(in srgb,var(--ok) 14%,transparent)}
|
|
1975
|
+
.run-status.failed{color:var(--err);background:color-mix(in srgb,var(--err) 14%,transparent)}
|
|
1976
|
+
.run-status.skipped{color:var(--dim);background:var(--hover)}
|
|
1977
|
+
.run-status.running{color:var(--warn);background:color-mix(in srgb,var(--warn) 16%,transparent)}
|
|
1978
|
+
.run-status.running::before{content:"";display:inline-block;width:6px;height:6px;border-radius:50%;
|
|
1979
|
+
background:currentColor;margin-right:5px;vertical-align:1px;animation:run-pulse 1.2s ease-in-out infinite}
|
|
1980
|
+
@keyframes run-pulse{50%{opacity:.25}}
|
|
1981
|
+
.routine-more{align-self:flex-start;margin-top:4px}
|
|
1719
1982
|
.small{font-size:12px;white-space:pre-wrap;word-break:break-all}
|
|
1720
1983
|
.keyrow{display:flex;gap:6px;margin:8px 0 6px}
|
|
1721
1984
|
.keyrow input{
|
|
@@ -1824,6 +2087,54 @@ button.signin-site:disabled{opacity:.4;cursor:default}
|
|
|
1824
2087
|
#browser-import-dialog .dlg-actions{padding:16px 22px 20px}
|
|
1825
2088
|
#browser-import-dialog .dlg-actions button:disabled{opacity:.4;cursor:default}
|
|
1826
2089
|
#browser-import-dialog input[type=text]{height:38px;padding:0 13px;font-size:13px}
|
|
2090
|
+
/* Feedback. Same frame as the import dialog; the kind is a row of fills, the
|
|
2091
|
+
chosen one taking the selection fill rather than a ring. */
|
|
2092
|
+
#feedback-dialog{width:min(520px,94vw);max-width:min(520px,94vw)}
|
|
2093
|
+
#feedback-dialog .cn-head{padding:20px 22px 2px}
|
|
2094
|
+
#feedback-dialog .cn-body{padding:14px 22px 4px;gap:10px}
|
|
2095
|
+
#feedback-dialog .dlg-actions{padding:16px 22px 20px}
|
|
2096
|
+
#feedback-dialog .dlg-actions button:disabled{opacity:.4;cursor:default}
|
|
2097
|
+
#feedback-dialog textarea{width:100%;box-sizing:border-box;min-height:140px;padding:11px 13px;border:0;border-radius:10px;outline:none;background:var(--field);color:var(--fg);font:inherit;font-size:13px;line-height:1.55;resize:vertical}
|
|
2098
|
+
#feedback-dialog textarea:focus-visible{background:var(--hover)}
|
|
2099
|
+
.fb-kinds{display:flex;gap:4px}
|
|
2100
|
+
.fb-kind{height:30px;padding:0 13px;border:0;border-radius:999px;background:var(--field);color:var(--dim);font:inherit;font-size:12.5px;cursor:pointer}
|
|
2101
|
+
.fb-kind:hover{background:var(--hover);color:var(--fg)}
|
|
2102
|
+
.fb-kind.on{background:var(--sel);color:var(--fg);font-weight:550}
|
|
2103
|
+
.fb-note{margin:0;font-size:11.5px;color:var(--dim);line-height:1.5}
|
|
2104
|
+
.fb-note:empty{display:none}
|
|
2105
|
+
/* Attachments as square tiles in a row, the last one adding another. Fills
|
|
2106
|
+
only: the add tile is a softer fill, not a dashed outline. */
|
|
2107
|
+
.fb-media{display:flex;flex-wrap:wrap;gap:8px}
|
|
2108
|
+
.fb-tile{position:relative;width:76px;height:76px;flex:0 0 auto;border-radius:14px;overflow:hidden;background:var(--field)}
|
|
2109
|
+
.fb-thumb{display:block;width:100%;height:100%;object-fit:cover}
|
|
2110
|
+
.fb-tile.is-video .fb-thumb{background:#000}
|
|
2111
|
+
/* A play mark over a recording's first frame, so it reads as video at a glance. */
|
|
2112
|
+
.fb-play{position:absolute;left:50%;top:50%;width:26px;height:26px;margin:-13px 0 0 -13px;border-radius:50%;background:rgba(0,0,0,.5);backdrop-filter:blur(6px)}
|
|
2113
|
+
.fb-play::after{content:"";position:absolute;left:10px;top:8px;border-style:solid;border-width:5px 0 5px 8px;border-color:transparent transparent transparent #fff}
|
|
2114
|
+
.fb-remove{
|
|
2115
|
+
position:absolute;top:5px;right:5px;width:20px;height:20px;padding:0;border:0;border-radius:50%;
|
|
2116
|
+
background:rgba(0,0,0,.55);backdrop-filter:blur(6px);color:#fff;font-size:14px;line-height:20px;text-align:center;cursor:pointer;
|
|
2117
|
+
opacity:.9;transition:opacity .15s,background .15s;
|
|
2118
|
+
}
|
|
2119
|
+
.fb-remove:hover{opacity:1;background:rgba(0,0,0,.75)}
|
|
2120
|
+
.fb-remove:disabled{display:none}
|
|
2121
|
+
.fb-tile.is-uploading::before,.fb-tile.is-done::before{content:"";position:absolute;inset:0;background:rgba(0,0,0,.35)}
|
|
2122
|
+
.fb-spinner{position:absolute;left:50%;top:50%;width:20px;height:20px;margin:-10px 0 0 -10px;border-radius:50%;
|
|
2123
|
+
border:2px solid rgba(255,255,255,.35);border-top-color:#fff;animation:fb-spin .8s linear infinite}
|
|
2124
|
+
@keyframes fb-spin{to{transform:rotate(360deg)}}
|
|
2125
|
+
.fb-done{position:absolute;left:50%;top:50%;width:22px;height:22px;margin:-11px 0 0 -11px;border-radius:50%;background:#fff;color:#111;font-size:12px;font-weight:700;line-height:22px;text-align:center}
|
|
2126
|
+
.fb-add{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:5px;padding:0;border:0;color:var(--dim);font:inherit;cursor:pointer;transition:background .15s,color .15s}
|
|
2127
|
+
.fb-add:hover,.fb-add:focus-visible{background:var(--hover);color:var(--fg);outline:none}
|
|
2128
|
+
.fb-add .fb-add-icon{width:20px;height:20px}
|
|
2129
|
+
.fb-add-label{font-size:11.5px;font-weight:500}
|
|
2130
|
+
/* Beside the tiles, centred on them: what goes here and the limits. It takes
|
|
2131
|
+
the rest of the row and drops below the tiles when the row is full. */
|
|
2132
|
+
.fb-media{align-items:center}
|
|
2133
|
+
.fb-caption{display:flex;flex-direction:column;gap:3px;flex:1;min-width:170px;padding-left:4px}
|
|
2134
|
+
.fb-caption-title{font-size:12.5px;font-weight:500;color:var(--fg)}
|
|
2135
|
+
.fb-caption-sub{font-size:11.5px;color:var(--dim);line-height:1.45}
|
|
2136
|
+
/* Something is being dragged over: the whole dialog is the target, said with a fill. */
|
|
2137
|
+
#feedback-dialog.fb-dropping{background:var(--sel)}
|
|
1827
2138
|
.bi-label{font-size:12px;color:var(--dim)}
|
|
1828
2139
|
.bi-sites{display:flex;flex-direction:column;gap:4px;max-height:240px;overflow:auto}
|
|
1829
2140
|
.bi-site{display:flex;align-items:center;gap:10px;padding:8px 11px;border-radius:9px;background:var(--field);cursor:pointer}
|
|
@@ -1908,6 +2219,23 @@ button.pw-plan:disabled{opacity:.4;cursor:default}
|
|
|
1908
2219
|
}
|
|
1909
2220
|
.pw-tab:hover{color:var(--fg)}
|
|
1910
2221
|
.pw-tab.on{background:var(--bg);color:var(--fg);font-weight:500}
|
|
2222
|
+
.pw-tab{display:inline-flex;align-items:center;justify-content:center;gap:6px}
|
|
2223
|
+
/* The site's badge on the same tab, at this dialog's size. */
|
|
2224
|
+
.pw-tab-save{
|
|
2225
|
+
font-size:10.5px;font-weight:600;padding:1px 6px;border-radius:999px;
|
|
2226
|
+
background:var(--fg);color:var(--bg);font-variant-numeric:tabular-nums;
|
|
2227
|
+
}
|
|
2228
|
+
/* Twelve months at the monthly price, struck, just left of what the year costs. */
|
|
2229
|
+
.pw-was{color:var(--dim);font-weight:400;margin-right:6px;text-decoration-thickness:1px}
|
|
2230
|
+
/* What the credits reach: a quiet heading, then one row per kind of model —
|
|
2231
|
+
the kind in a narrow column, its marks and names beside it. */
|
|
2232
|
+
.pw-media{display:grid;grid-template-columns:auto 1fr;align-items:center;gap:7px 14px;font-size:12.5px}
|
|
2233
|
+
.pw-media-head{grid-column:1/-1;color:var(--dim);font-size:11.5px}
|
|
2234
|
+
.pw-media-kind{color:var(--dim);white-space:nowrap}
|
|
2235
|
+
.pw-media-models{display:flex;flex-wrap:wrap;align-items:center;gap:6px 12px}
|
|
2236
|
+
.pw-media-model{display:inline-flex;align-items:center;gap:5px;color:var(--fg);font-weight:500;white-space:nowrap}
|
|
2237
|
+
.pw-media-mark{display:inline-flex;width:14px;height:14px}
|
|
2238
|
+
.pw-media-mark svg{width:100%;height:100%}
|
|
1911
2239
|
/* A labelled break, not a rule: what follows is a different kind of purchase,
|
|
1912
2240
|
and a bare line would read as the list simply continuing. */
|
|
1913
2241
|
.pw-sep{
|
|
@@ -2056,26 +2384,27 @@ button.pw-plan:disabled{opacity:.4;cursor:default}
|
|
|
2056
2384
|
* is right for browsing and wrong for "the one about the parser, two days
|
|
2057
2385
|
* ago". So this is a search field over every session in every tree opened
|
|
2058
2386
|
* here, the projects themselves, and the few actions worth a shortcut. */
|
|
2059
|
-
#palette{width:min(
|
|
2387
|
+
#palette{width:min(600px,92vw);max-height:min(64vh,560px)}
|
|
2060
2388
|
#palette[open]{display:flex;flex-direction:column}
|
|
2061
2389
|
/* One left edge for everything: the field's magnifier, each row's icon, the
|
|
2062
|
-
group labels and the key hints all start
|
|
2063
|
-
starts where the row labels do. Rows sit
|
|
2390
|
+
group labels and the key hints all start 26px in, and the field's text
|
|
2391
|
+
starts where the row labels do. Rows sit 12px in so their highlight has room;
|
|
2064
2392
|
their own 14px padding brings the content back to the shared edge. */
|
|
2065
|
-
.cmdk-head{position:relative;padding:
|
|
2066
|
-
.cmdk-head .ico-sm{position:absolute;left:
|
|
2067
|
-
#palette .cmdk-head input{height:
|
|
2068
|
-
#cmdk-list{overflow-y:auto;padding:0
|
|
2393
|
+
.cmdk-head{position:relative;padding:16px 16px 10px}
|
|
2394
|
+
.cmdk-head .ico-sm{position:absolute;left:26px;top:calc(50% - 4.5px);pointer-events:none}
|
|
2395
|
+
#palette .cmdk-head input{height:42px;padding:0 12px 0 35px;font-size:14px}
|
|
2396
|
+
#cmdk-list{overflow-y:auto;padding:0 12px 12px;flex:1;scroll-padding-block:6px}
|
|
2069
2397
|
.cmdk-group{
|
|
2070
|
-
padding:
|
|
2398
|
+
padding:16px 14px 6px;font-size:10.5px;letter-spacing:.04em;text-transform:uppercase;
|
|
2071
2399
|
color:var(--dim);
|
|
2072
2400
|
}
|
|
2073
|
-
.cmdk-group:first-child{padding-top:
|
|
2401
|
+
.cmdk-group:first-child{padding-top:8px}
|
|
2074
2402
|
.cmdk-item{
|
|
2075
2403
|
width:100%;display:flex;align-items:center;gap:10px;border:0;border-radius:8px;
|
|
2076
2404
|
background:transparent;color:var(--fg);font:inherit;font-size:13px;cursor:pointer;
|
|
2077
|
-
padding:
|
|
2405
|
+
padding:10px 14px;text-align:left;
|
|
2078
2406
|
}
|
|
2407
|
+
.cmdk-item + .cmdk-item{margin-top:2px}
|
|
2079
2408
|
/* Hover and the keyboard cursor are the same highlight: a list driven by both
|
|
2080
2409
|
at once needs one answer to "which row does Enter take". */
|
|
2081
2410
|
.cmdk-item.cursor{background:var(--sel)}
|
|
@@ -2087,8 +2416,7 @@ button.pw-plan:disabled{opacity:.4;cursor:default}
|
|
|
2087
2416
|
}
|
|
2088
2417
|
.cmdk-empty{padding:12px 14px;font-size:12.5px;color:var(--dim)}
|
|
2089
2418
|
.cmdk-foot{
|
|
2090
|
-
display:flex;gap:
|
|
2091
|
-
box-shadow:0 -1px 0 var(--line);
|
|
2419
|
+
display:flex;gap:18px;padding:10px 26px 16px;font-size:11px;color:var(--dim);
|
|
2092
2420
|
}
|
|
2093
2421
|
.cmdk-foot kbd{
|
|
2094
2422
|
font:inherit;font-size:10.5px;border:1px solid currentColor;border-radius:4px;
|
|
@@ -2108,7 +2436,10 @@ button.pw-plan:disabled{opacity:.4;cursor:default}
|
|
|
2108
2436
|
#connect:has(#cn-aside:not([hidden])){width:min(760px,94vw)}
|
|
2109
2437
|
.cn-grid{display:grid;grid-template-columns:1fr}
|
|
2110
2438
|
#connect:has(#cn-aside:not([hidden])) .cn-grid{grid-template-columns:minmax(0,.85fr) minmax(0,1fr)}
|
|
2439
|
+
/* The sidebar's bare aside rule (a fixed width, contents pinned right) would
|
|
2440
|
+
* otherwise reach this one too, and set the wordmark against the divider. */
|
|
2111
2441
|
.cn-aside{
|
|
2442
|
+
width:auto;flex:initial;align-items:stretch;
|
|
2112
2443
|
display:flex;flex-direction:column;gap:20px;padding:30px 28px;
|
|
2113
2444
|
background:var(--hover);border-right:1px solid var(--line);
|
|
2114
2445
|
border-radius:16px 0 0 16px;
|
|
@@ -2265,6 +2596,10 @@ button.pw-plan:disabled{opacity:.4;cursor:default}
|
|
|
2265
2596
|
three sentences side by side either wrap mid-phrase or shrink the type. Down
|
|
2266
2597
|
the page they read as a short list of offers, which is what they are. */
|
|
2267
2598
|
#starters{display:flex;flex-direction:column;align-items:flex-start;gap:8px}
|
|
2599
|
+
/* A rotated or newly written set gets a short fade, so the change is seen
|
|
2600
|
+
rather than noticed as a flicker. */
|
|
2601
|
+
#starters.fresh button{animation:starters-in .35s ease-out both}
|
|
2602
|
+
@keyframes starters-in{from{opacity:0;transform:translateY(3px)}to{opacity:1;transform:none}}
|
|
2268
2603
|
#starters button{
|
|
2269
2604
|
border:0;background:var(--panel);color:var(--fg);border-radius:999px;
|
|
2270
2605
|
padding:8px 15px;font:inherit;font-size:13px;cursor:pointer;text-align:left;
|
|
@@ -2342,6 +2677,34 @@ details.tool>summary:hover::after{opacity:1}
|
|
|
2342
2677
|
.tool-body{padding:2px 12px 10px}
|
|
2343
2678
|
.tool-body pre{margin:0;overflow-x:auto;font-size:12px;max-height:340px}
|
|
2344
2679
|
.tool-body .label{font-size:11px;color:var(--dim);margin:0 0 4px}
|
|
2680
|
+
/* The screenshots a tool took, under its result: the same thumbnail as a
|
|
2681
|
+
pasted image, a click away from full size. */
|
|
2682
|
+
.tool-shots{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px}
|
|
2683
|
+
.tool-shots img{
|
|
2684
|
+
max-width:220px;max-height:160px;border-radius:8px;cursor:zoom-in;
|
|
2685
|
+
background:var(--bg);object-fit:cover;
|
|
2686
|
+
}
|
|
2687
|
+
/* The windows a Computer use turn ended on: the result, so out in the turn
|
|
2688
|
+
rather than folded into a step, and a little larger than a step's shots. */
|
|
2689
|
+
.turn-shots{display:flex;flex-wrap:wrap;gap:10px;margin:10px 0 4px}
|
|
2690
|
+
.turn-shot{margin:0;display:flex;flex-direction:column;gap:5px;max-width:280px}
|
|
2691
|
+
.turn-shot img{
|
|
2692
|
+
display:block;max-width:280px;max-height:200px;border-radius:10px;cursor:zoom-in;
|
|
2693
|
+
background:var(--bg);object-fit:cover;
|
|
2694
|
+
}
|
|
2695
|
+
.turn-shot figcaption{font-size:11.5px;color:var(--dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
2696
|
+
/* The preview: the image as large as the window allows, over a dark scrim.
|
|
2697
|
+
Dark in both themes — a screenshot reads against black, not the page. */
|
|
2698
|
+
.img-preview{
|
|
2699
|
+
position:fixed;inset:0;z-index:90;display:grid;place-items:center;padding:32px;
|
|
2700
|
+
background:rgba(0,0,0,.82);cursor:zoom-out;
|
|
2701
|
+
}
|
|
2702
|
+
.img-preview img{max-width:100%;max-height:100%;border-radius:8px;object-fit:contain}
|
|
2703
|
+
.img-preview-count{
|
|
2704
|
+
position:absolute;bottom:14px;left:50%;transform:translateX(-50%);
|
|
2705
|
+
color:#fff;font-size:12px;opacity:.7;
|
|
2706
|
+
}
|
|
2707
|
+
.img-preview-count:empty{display:none}
|
|
2345
2708
|
.tool-body .kv{margin:0 0 8px}
|
|
2346
2709
|
.tool-summary{font-size:12.5px;line-height:1.5;margin:0 0 6px}
|
|
2347
2710
|
details.tool-raw>summary{font-size:11px;color:var(--dim);cursor:pointer;margin:0 0 4px}
|
|
@@ -2465,6 +2828,8 @@ details.thinking[open]>summary::after{transform:translateY(-2px) rotate(45deg)}
|
|
|
2465
2828
|
|
|
2466
2829
|
/* composer */
|
|
2467
2830
|
form{padding:6px 24px 18px;background:var(--bg)}
|
|
2831
|
+
/* 24px plus the 11px the transcript's scrollbar gutter takes on each side. */
|
|
2832
|
+
@media (min-width:761px){#composer{padding-left:35px;padding-right:35px}}
|
|
2468
2833
|
.composer{
|
|
2469
2834
|
position:relative;
|
|
2470
2835
|
max-width:720px;margin:0 auto;border-radius:16px;
|
|
@@ -2585,6 +2950,57 @@ textarea.boxed:focus{outline:none;background:var(--field-focus)}
|
|
|
2585
2950
|
}
|
|
2586
2951
|
.icon-btn svg{width:14px;height:14px}
|
|
2587
2952
|
.icon-btn:hover{background:var(--hover)}
|
|
2953
|
+
/* Dictation. Listening is the harness alive, so it takes the brand: the mic
|
|
2954
|
+
becomes a soft lime well holding a stop square, breathing, and the panel
|
|
2955
|
+
above the box shows the voice in lime. Actions (done) keep --accent, as
|
|
2956
|
+
every action does. Fills and shadows only, never a ring. */
|
|
2957
|
+
.mic-btn{width:30px;height:30px;border-radius:999px;color:var(--dim);transition:background .2s,color .2s}
|
|
2958
|
+
.mic-btn svg{width:16px;height:16px}
|
|
2959
|
+
.mic-btn .mic-stop{display:none;width:14px;height:14px}
|
|
2960
|
+
.mic-btn:hover{color:var(--fg)}
|
|
2961
|
+
.mic-btn.is-recording{background:var(--brand-tint);color:var(--brand-ink);animation:mic-breathe 1.8s ease-in-out infinite}
|
|
2962
|
+
.mic-btn.is-recording .mic-idle{display:none}
|
|
2963
|
+
.mic-btn.is-recording .mic-stop{display:block}
|
|
2964
|
+
.mic-btn:disabled{opacity:.45;cursor:default}
|
|
2965
|
+
@keyframes mic-breathe{50%{background:color-mix(in srgb,var(--brand) 34%,transparent)}}
|
|
2966
|
+
.composer-bar .mic-btn,.composer-bar #send,.composer-bar #stop{flex-shrink:0}
|
|
2967
|
+
/* The panel: centred over the box's top edge, a pill of its own. One row:
|
|
2968
|
+
what is going on, then the voice and the two ways out while listening. */
|
|
2969
|
+
.dictate-pop{
|
|
2970
|
+
position:absolute;left:50%;bottom:calc(100% + 12px);z-index:30;transform:translateX(-50%);
|
|
2971
|
+
display:flex;align-items:center;gap:12px;min-height:52px;width:max-content;max-width:min(480px,calc(100% - 24px));
|
|
2972
|
+
padding:8px 8px 8px 20px;border-radius:999px;background:var(--panel);color:var(--fg);
|
|
2973
|
+
box-shadow:0 18px 44px rgba(0,0,0,.26),0 3px 10px rgba(0,0,0,.12);
|
|
2974
|
+
font-size:13.5px;font-weight:500;letter-spacing:.01em;animation:dp-in .18s cubic-bezier(.2,.8,.2,1);
|
|
2975
|
+
}
|
|
2976
|
+
.dictate-pop[hidden]{display:none}
|
|
2977
|
+
@keyframes dp-in{from{opacity:0;transform:translate(-50%,8px) scale(.97)}to{opacity:1;transform:translateX(-50%)}}
|
|
2978
|
+
.dp-dot{width:8px;height:8px;border-radius:50%;background:var(--brand);flex:0 0 auto;animation:dp-dot 1.4s ease-in-out infinite}
|
|
2979
|
+
@keyframes dp-dot{50%{opacity:.35}}
|
|
2980
|
+
.dp-spin{width:14px;height:14px;border-radius:50%;border:2px solid var(--brand-tint);border-top-color:var(--brand);flex:0 0 auto;animation:dp-spin .8s linear infinite}
|
|
2981
|
+
@keyframes dp-spin{to{transform:rotate(360deg)}}
|
|
2982
|
+
.dp-text{white-space:nowrap}
|
|
2983
|
+
.dp-time{color:var(--dim);font-weight:400;font-variant-numeric:tabular-nums;min-width:2.4em}
|
|
2984
|
+
.dp-wave{width:168px;height:32px;flex:0 1 auto;min-width:64px;color:var(--brand)}
|
|
2985
|
+
.dp-btn{width:36px;height:36px;border:0;border-radius:50%;display:grid;place-items:center;cursor:pointer;flex:0 0 auto;padding:0;font:inherit;transition:background .15s,transform .15s}
|
|
2986
|
+
.dp-btn:active{transform:scale(.94)}
|
|
2987
|
+
.dp-btn svg{width:15px;height:15px}
|
|
2988
|
+
.dp-cancel,.dp-close{background:var(--hover);color:var(--fg)}
|
|
2989
|
+
.dp-done{background:var(--accent);color:var(--bg)}
|
|
2990
|
+
.dp-btn:focus-visible{outline:2px solid var(--fg);outline-offset:2px}
|
|
2991
|
+
/* Which parts show is the state's: listening, working, or saying what went wrong. */
|
|
2992
|
+
.dictate-pop:not([data-state="listening"]) .dp-dot,
|
|
2993
|
+
.dictate-pop:not([data-state="listening"]) .dp-time,
|
|
2994
|
+
.dictate-pop:not([data-state="listening"]) .dp-wave,
|
|
2995
|
+
.dictate-pop:not([data-state="listening"]) .dp-cancel,
|
|
2996
|
+
.dictate-pop:not([data-state="listening"]) .dp-done,
|
|
2997
|
+
.dictate-pop:not([data-state="working"]) .dp-spin,
|
|
2998
|
+
.dictate-pop:not([data-state="error"]) .dp-close{display:none}
|
|
2999
|
+
.dictate-pop[data-state="working"]{padding-right:20px}
|
|
3000
|
+
/* The error red mixed toward the text colour: bright enough on the dark
|
|
3001
|
+
theme, still deep on the light one. */
|
|
3002
|
+
.dictate-pop[data-state="error"] .dp-text{color:color-mix(in srgb,var(--err) 62%,var(--fg));white-space:normal;line-height:1.45}
|
|
3003
|
+
@media (prefers-reduced-motion:reduce){.dictate-pop,.dp-dot,.dp-spin,.mic-btn.is-recording{animation:none}}
|
|
2588
3004
|
/* The folder it runs in, set at the same weight as the model beside it: three
|
|
2589
3005
|
settings on one bar, none of them shouting over the others. */
|
|
2590
3006
|
#workspace-picker .picker-btn{gap:6px}
|
|
@@ -2626,6 +3042,54 @@ textarea.boxed:focus{outline:none;background:var(--field-focus)}
|
|
|
2626
3042
|
.effort-name.on{color:var(--fg);font-weight:600}
|
|
2627
3043
|
#attachments{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:8px}
|
|
2628
3044
|
#attachments:empty{display:none}
|
|
3045
|
+
/* The tray: what the message carries, as cards sitting just above the box.
|
|
3046
|
+
Pictures at a size that can be recognised, files as a name and a line;
|
|
3047
|
+
the same fill as the box below and no outline, with the × on the card's
|
|
3048
|
+
corner where a thumbnail leaves no room beside it. */
|
|
3049
|
+
/* The box's own width and centring, so the cards start where the box does
|
|
3050
|
+
rather than at the column's edge. */
|
|
3051
|
+
#tray{display:flex;flex-wrap:wrap;gap:8px;max-width:720px;margin:0 auto 8px}
|
|
3052
|
+
#tray:empty{display:none}
|
|
3053
|
+
.tray-card{
|
|
3054
|
+
position:relative;display:flex;align-items:center;gap:10px;height:64px;
|
|
3055
|
+
max-width:240px;padding:0 30px 0 10px;border-radius:12px;background:var(--panel);
|
|
3056
|
+
}
|
|
3057
|
+
.tray-card.tray-media{width:64px;padding:0;overflow:hidden;background:var(--field)}
|
|
3058
|
+
.tray-card img,.tray-card video{width:100%;height:100%;object-fit:cover;display:block}
|
|
3059
|
+
.tray-card img{cursor:zoom-in}
|
|
3060
|
+
/* A clip: its first frame where a file's icon goes, with its length and size
|
|
3061
|
+
beside the name. */
|
|
3062
|
+
.tray-thumb{position:relative;width:44px;height:44px;flex:0 0 auto;border-radius:9px;overflow:hidden;background:var(--field)}
|
|
3063
|
+
.tray-thumb video{width:100%;height:100%;object-fit:cover;display:block}
|
|
3064
|
+
.tray-duration:not(:empty)::after{content:" · "}
|
|
3065
|
+
.tray-play{position:absolute;left:50%;top:50%;margin:-9px 0 0 -9px;width:18px;height:18px;border-radius:50%;display:grid;place-items:center;background:#0000008c;color:#fff;pointer-events:none;transition:opacity .12s}
|
|
3066
|
+
.tray-card.video:hover .tray-play{opacity:0}
|
|
3067
|
+
.tray-play svg{width:10px;height:10px}
|
|
3068
|
+
.tray-icon{width:36px;height:36px;flex:0 0 auto;border-radius:9px;display:grid;place-items:center;background:var(--field);color:var(--dim)}
|
|
3069
|
+
.tray-icon svg{width:16px;height:16px}
|
|
3070
|
+
.tray-text{display:flex;flex-direction:column;gap:2px;min-width:0}
|
|
3071
|
+
.tray-name{font-size:12.5px;color:var(--fg);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
3072
|
+
.tray-sub{font-size:11px;color:var(--dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
3073
|
+
.tray-card .x{
|
|
3074
|
+
position:absolute;top:4px;right:4px;width:20px;height:20px;padding:0;border:0;border-radius:50%;
|
|
3075
|
+
display:grid;place-items:center;font-size:13px;line-height:1;cursor:pointer;
|
|
3076
|
+
background:var(--field);color:var(--dim);
|
|
3077
|
+
}
|
|
3078
|
+
.tray-card.tray-media .x{background:#0000008c;color:#fff;opacity:0;transition:opacity .12s}
|
|
3079
|
+
.tray-card.tray-media:hover .x,.tray-card .x:focus-visible{opacity:1}
|
|
3080
|
+
/* No hover on a touch screen: the × is always there. */
|
|
3081
|
+
@media (hover:none){.tray-card.tray-media .x{opacity:1}}
|
|
3082
|
+
.tray-card .x:hover{color:var(--fg)}
|
|
3083
|
+
/* A phone: one row that scrolls sideways rather than rows that push the box
|
|
3084
|
+
up the screen, and cards a little smaller so two or three still show. */
|
|
3085
|
+
@media (max-width:760px){
|
|
3086
|
+
#tray{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch;margin-bottom:6px}
|
|
3087
|
+
#tray::-webkit-scrollbar{display:none}
|
|
3088
|
+
.tray-card{flex:0 0 auto;height:56px;max-width:200px}
|
|
3089
|
+
.tray-card.tray-media{width:56px}
|
|
3090
|
+
.tray-icon,.tray-thumb{width:36px;height:36px}
|
|
3091
|
+
}
|
|
3092
|
+
.tray-card.tray-media .x:hover{color:#fff;background:#000000b3}
|
|
2629
3093
|
/* Outside the composer, tracking its width so the rows line up with the box
|
|
2630
3094
|
they are waiting on. min-width:0 twice over — here and on the line inside
|
|
2631
3095
|
each row — because a flex item defaults to refusing to shrink below its
|
|
@@ -2661,6 +3125,91 @@ textarea.boxed:focus{outline:none;background:var(--field-focus)}
|
|
|
2661
3125
|
:root[data-theme="dark"] .q .go:hover{color:var(--brand)}
|
|
2662
3126
|
.q .x{border:0;background:transparent;color:var(--dim);cursor:pointer;font:inherit;padding:0 2px}
|
|
2663
3127
|
.q .x:hover{color:var(--err)}
|
|
3128
|
+
/* The goal being worked toward: the reason the turn keeps going, in the same
|
|
3129
|
+
brand ink that marks the harness at work. Its words in full colour, since
|
|
3130
|
+
unlike a queued line it is what everything below is for. */
|
|
3131
|
+
.q.goal .tag{color:var(--brand-ink)}
|
|
3132
|
+
:root[data-theme="dark"] .q.goal .tag{color:var(--brand)}
|
|
3133
|
+
.q.goal .n{color:var(--fg)}
|
|
3134
|
+
/* A background task: work this conversation set going. Running ones say so
|
|
3135
|
+
in the brand ink that marks the harness at work; ended ones step back, but
|
|
3136
|
+
stay a click from what they said. */
|
|
3137
|
+
.q.task .n{cursor:pointer}
|
|
3138
|
+
.q.task-running .tag,.q.task-queued .tag{color:var(--brand-ink)}
|
|
3139
|
+
:root[data-theme="dark"] .q.task-running .tag,:root[data-theme="dark"] .q.task-queued .tag{color:var(--brand)}
|
|
3140
|
+
.q.task-failed .tag{color:var(--err)}
|
|
3141
|
+
.q.task-completed .n,.q.task-cancelled .n,.q.task-failed .n{color:var(--dim)}
|
|
3142
|
+
.q.task-more{border:0;color:var(--dim);cursor:pointer;font:inherit;font-size:12px;text-align:left}
|
|
3143
|
+
.q.task-more:hover{color:var(--fg)}
|
|
3144
|
+
/* Background results read into the conversation: a card, set apart from
|
|
3145
|
+
what the person typed by its fill and its place on the left. */
|
|
3146
|
+
.task-card-row-wrap{margin-bottom:26px}
|
|
3147
|
+
.task-card{display:flex;flex-direction:column;gap:4px;max-width:520px;background:var(--panel);border-radius:12px;padding:10px 12px;font-size:12px}
|
|
3148
|
+
.task-card-head{color:var(--dim);font-size:11px;margin-bottom:2px}
|
|
3149
|
+
.task-card-row{display:flex;align-items:center;gap:8px;min-width:0}
|
|
3150
|
+
.task-card-row .tag{flex:0 0 auto;color:var(--dim);font-size:11px}
|
|
3151
|
+
.task-card-row.task-completed .tag{color:var(--ok)}
|
|
3152
|
+
.task-card-row.task-failed .tag{color:var(--err)}
|
|
3153
|
+
.task-card-row .n{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
3154
|
+
.task-card-row .go{flex:0 0 auto;height:22px;padding:0 9px;border:0;border-radius:7px;background:var(--hover);color:var(--fg);cursor:pointer;font:inherit;font-size:11px}
|
|
3155
|
+
.task-card-row .go:hover{background:var(--brand-tint);color:var(--brand-ink)}
|
|
3156
|
+
/* The sheet a task opens into: down the right edge, over the conversation,
|
|
3157
|
+
which stays where it was underneath. Set apart by its fill, never a line. */
|
|
3158
|
+
#task-sheet{
|
|
3159
|
+
position:fixed;top:0;right:0;bottom:0;z-index:30;width:min(560px,100vw);
|
|
3160
|
+
display:flex;flex-direction:column;background:var(--panel);
|
|
3161
|
+
box-shadow:-12px 0 32px #00000014;
|
|
3162
|
+
}
|
|
3163
|
+
#task-sheet[hidden]{display:none}
|
|
3164
|
+
/* In the desktop app the tab strip under the sheet's top edge is the window's
|
|
3165
|
+
drag handle, and a drag region takes clicks whatever is drawn over it: the
|
|
3166
|
+
sheet's close and stop buttons would only ever move the window. */
|
|
3167
|
+
#task-sheet{-webkit-app-region:no-drag}
|
|
3168
|
+
.task-head{display:flex;align-items:flex-start;gap:10px;padding:16px 16px 10px}
|
|
3169
|
+
.task-titles{flex:1;min-width:0}
|
|
3170
|
+
.task-title{font-weight:600;font-size:14px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
3171
|
+
.task-meta{color:var(--dim);font-size:12px;margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
3172
|
+
.task-stop{
|
|
3173
|
+
flex:0 0 auto;height:28px;padding:0 10px;border:0;border-radius:8px;
|
|
3174
|
+
background:var(--hover);color:var(--fg);cursor:pointer;font:inherit;font-size:12px;
|
|
3175
|
+
}
|
|
3176
|
+
.task-stop:hover{color:var(--err)}
|
|
3177
|
+
.task-close{flex:0 0 auto;width:28px;height:28px;border:0;border-radius:8px;background:transparent;color:var(--dim);cursor:pointer;font:inherit;font-size:18px;line-height:1}
|
|
3178
|
+
.task-close:hover{background:var(--hover);color:var(--fg)}
|
|
3179
|
+
.task-chips{position:relative;display:flex;flex-wrap:nowrap;gap:6px;overflow-x:auto;padding:0 16px 10px;scrollbar-width:none;overscroll-behavior-x:contain}
|
|
3180
|
+
.task-chips::-webkit-scrollbar{display:none}
|
|
3181
|
+
.task-chips[hidden]{display:none}
|
|
3182
|
+
.task-chip{
|
|
3183
|
+
flex:0 0 auto;height:26px;padding:0 10px;border:0;border-radius:13px;
|
|
3184
|
+
background:var(--hover);color:var(--dim);cursor:pointer;font:inherit;font-size:12px;
|
|
3185
|
+
white-space:nowrap;
|
|
3186
|
+
}
|
|
3187
|
+
.task-chip.on{background:var(--brand-tint);color:var(--brand-ink)}
|
|
3188
|
+
:root[data-theme="dark"] .task-chip.on{color:var(--brand)}
|
|
3189
|
+
.task-log{flex:1;min-height:0;overflow-y:auto;padding:4px 16px 16px;display:flex;flex-direction:column;gap:10px;font-size:13px;line-height:1.55}
|
|
3190
|
+
.task-msg.user{align-self:flex-end;max-width:85%;background:var(--bg);border-radius:12px;padding:8px 12px;white-space:pre-wrap;overflow-wrap:anywhere}
|
|
3191
|
+
.task-msg.assistant{overflow-wrap:anywhere}
|
|
3192
|
+
.task-tool{background:var(--bg);border-radius:10px;padding:6px 10px;font-size:12px}
|
|
3193
|
+
.task-tool summary{display:flex;gap:8px;cursor:pointer;list-style:none;min-width:0}
|
|
3194
|
+
.task-tool summary::-webkit-details-marker{display:none}
|
|
3195
|
+
.task-tool-name{flex:0 0 auto;font-weight:600}
|
|
3196
|
+
.task-tool-input{flex:1;min-width:0;color:var(--dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:var(--mono,ui-monospace,monospace)}
|
|
3197
|
+
.task-tool.err .task-tool-name{color:var(--err)}
|
|
3198
|
+
.task-tool-result{margin:6px 0 0;max-height:240px;overflow:auto;white-space:pre-wrap;overflow-wrap:anywhere;color:var(--dim);font-size:11.5px}
|
|
3199
|
+
.task-note{color:var(--dim);font-size:12px}
|
|
3200
|
+
.task-error{color:var(--err);font-size:12px}
|
|
3201
|
+
.task-empty{color:var(--dim)}
|
|
3202
|
+
.task-working{display:flex;align-items:center;gap:8px;color:var(--dim)}
|
|
3203
|
+
/* The sheet's own surface runs to its bottom edge: no band behind the box. */
|
|
3204
|
+
.task-form{padding:8px 16px 16px;background:transparent}
|
|
3205
|
+
/* Field and send button as one box, the send arrow inside at its corner. */
|
|
3206
|
+
.task-box{display:flex;align-items:flex-end;gap:8px;background:var(--bg);border-radius:14px;padding:8px 8px 8px 12px}
|
|
3207
|
+
.task-input{
|
|
3208
|
+
flex:1;min-width:0;resize:none;border:0;padding:5px 0;max-height:160px;
|
|
3209
|
+
background:transparent;color:var(--fg);font:inherit;font-size:13px;line-height:1.5;outline:none;
|
|
3210
|
+
}
|
|
3211
|
+
.task-input:disabled{opacity:.6}
|
|
3212
|
+
.task-send{flex:0 0 auto}
|
|
2664
3213
|
.att{
|
|
2665
3214
|
display:flex;align-items:center;gap:7px;background:var(--bg);border-radius:8px;
|
|
2666
3215
|
padding:5px 8px;font-size:12px;max-width:260px;
|
|
@@ -2674,6 +3223,37 @@ textarea.boxed:focus{outline:none;background:var(--field-focus)}
|
|
|
2674
3223
|
the chip carries a mark for what it points at instead of a size. */
|
|
2675
3224
|
.att .ico-sm{width:13px;height:13px;opacity:.65}
|
|
2676
3225
|
.att .where{color:var(--dim);font-size:11px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
3226
|
+
/* Goal mode is how this message is sent, not a thing attached to it: a
|
|
3227
|
+
tinted fill, never an outline, sets it apart from the files. */
|
|
3228
|
+
.att.goal{background:var(--brand-tint);color:var(--brand-ink)}
|
|
3229
|
+
:root[data-theme="dark"] .att.goal{color:var(--brand)}
|
|
3230
|
+
.att.goal .ico-sm{opacity:1}
|
|
3231
|
+
/* Computer use stays on for the whole conversation, so it sits in the chip
|
|
3232
|
+
row on every message: a full brand fill there shouted louder than anything
|
|
3233
|
+
the person typed. The file chips' own quiet fill, with the colour kept to
|
|
3234
|
+
the mark. Turning it off is not destructive, so the × does not go red. */
|
|
3235
|
+
.att.computer{color:var(--fg)}
|
|
3236
|
+
.att.computer .ico-sm{opacity:1;color:var(--brand-ink)}
|
|
3237
|
+
.att.computer .x:hover{color:var(--fg)}
|
|
3238
|
+
.att.computer-perm{flex-basis:100%;display:flex;flex-direction:column;align-items:stretch;gap:6px;padding:10px 12px;background:var(--field);border-radius:10px;max-width:none}
|
|
3239
|
+
.computer-perm .cp-head{display:flex;align-items:center;gap:8px;font-weight:600;color:var(--fg)}
|
|
3240
|
+
.computer-perm .cp-body{color:var(--dim);font-size:12px;white-space:normal}
|
|
3241
|
+
.computer-perm .cp-row{display:flex;align-items:center;justify-content:space-between;gap:12px;font-size:12.5px;color:var(--fg)}
|
|
3242
|
+
.computer-perm .cp-ok{width:26px;text-align:center;color:var(--dim);font-size:13px;line-height:24px}
|
|
3243
|
+
/* The app's primary button, inverted on either theme: white on the brand
|
|
3244
|
+
green was unreadable in dark mode. */
|
|
3245
|
+
.computer-perm .cp-btn{border:0;border-radius:7px;padding:4px 12px;background:var(--accent);color:var(--bg);font:inherit;font-size:12px;font-weight:500;cursor:pointer}
|
|
3246
|
+
.computer-perm .cp-btn:hover{background:color-mix(in srgb,var(--accent) 85%,var(--bg))}
|
|
3247
|
+
/* The card's own actions, after the rows: buttons the size of Allow, but
|
|
3248
|
+
secondary — the page's fill on the card's, no outline — and set right, where
|
|
3249
|
+
Allow already sits. Loose green words read as links to somewhere else, and
|
|
3250
|
+
sat under the rows as if they were a third one. */
|
|
3251
|
+
.computer-perm .cp-foot{display:flex;justify-content:flex-end;gap:8px;margin-top:4px}
|
|
3252
|
+
.computer-perm .cp-link{
|
|
3253
|
+
border:0;border-radius:7px;padding:4px 12px;background:var(--bg);color:var(--fg);
|
|
3254
|
+
font:inherit;font-size:12px;font-weight:500;cursor:pointer;
|
|
3255
|
+
}
|
|
3256
|
+
.computer-perm .cp-link:hover{background:color-mix(in srgb,var(--fg) 8%,var(--bg))}
|
|
2677
3257
|
/* Wider than the model picker's: these rows carry a name and the folder it
|
|
2678
3258
|
sits in, and truncating the folder away leaves three files called index.ts. */
|
|
2679
3259
|
#attach-picker .menu{min-width:min(380px,86vw)}
|
|
@@ -2761,8 +3341,11 @@ button.ghost,a.ghost{
|
|
|
2761
3341
|
background:var(--sel);color:var(--fg);font:inherit;font-size:12.5px;cursor:pointer;
|
|
2762
3342
|
}
|
|
2763
3343
|
button.ghost:hover,a.ghost:hover{background:var(--hover)}
|
|
3344
|
+
/* Just above the composer, however tall it is: a fixed offset was covered as
|
|
3345
|
+
soon as attachment cards or a longer draft grew the form under it.
|
|
3346
|
+
--form-h is measured in transcript.ts. */
|
|
2764
3347
|
#jump{
|
|
2765
|
-
position:absolute;right:24px;bottom:
|
|
3348
|
+
position:absolute;right:24px;bottom:calc(var(--form-h, 108px) + 10px);border-radius:999px;padding:7px 15px;border:0;
|
|
2766
3349
|
background:var(--bg);color:var(--fg);cursor:pointer;font:inherit;font-size:12px;
|
|
2767
3350
|
box-shadow:0 1px 2px #0000000f,0 6px 20px #0000001f;
|
|
2768
3351
|
}
|
|
@@ -2773,6 +3356,11 @@ dialog{
|
|
|
2773
3356
|
box-shadow:0 2px 6px #00000014,0 20px 60px #00000033;
|
|
2774
3357
|
}
|
|
2775
3358
|
dialog::backdrop{background:rgba(0,0,0,.55)}
|
|
3359
|
+
/* A phone asking to pair: the code is the thing to read, so it is the largest. */
|
|
3360
|
+
.pair-dialog{width:min(420px,92vw);padding-bottom:16px}
|
|
3361
|
+
.pair-title{padding:22px 24px 6px;font-size:16px;font-weight:600}
|
|
3362
|
+
.pair-body{padding:0 24px;color:var(--dim);font-size:13.5px;line-height:1.5}
|
|
3363
|
+
.pair-code{margin:18px 24px 6px;padding:14px 0;border-radius:12px;background:var(--sel);text-align:center;font-size:30px;letter-spacing:.12em}
|
|
2776
3364
|
/* The approval dialog.
|
|
2777
3365
|
*
|
|
2778
3366
|
* Everything here is arranged around one question: what exactly is about to
|
|
@@ -2913,19 +3501,31 @@ dialog::backdrop{background:rgba(0,0,0,.55)}
|
|
|
2913
3501
|
*/
|
|
2914
3502
|
@media (max-width:760px){
|
|
2915
3503
|
body>aside{
|
|
2916
|
-
position:fixed;inset:0 auto 0 0;z-index:20;width:min(
|
|
3504
|
+
position:fixed;inset:0 auto 0 0;z-index:20;width:min(332px,90vw);flex-basis:auto;
|
|
2917
3505
|
transform:translateX(-100%);transition:transform .2s;
|
|
2918
3506
|
padding-bottom:env(safe-area-inset-bottom);
|
|
2919
3507
|
}
|
|
2920
3508
|
body>aside.open{transform:none;box-shadow:0 0 0 1px #0000000a,0 12px 40px #00000040}
|
|
2921
3509
|
body.nav-open #scrim{display:block}
|
|
2922
3510
|
#nav-close{display:grid}
|
|
2923
|
-
#nav-
|
|
2924
|
-
|
|
2925
|
-
|
|
3511
|
+
#nav-toggle,#side-top{display:none}
|
|
3512
|
+
aside header{display:flex}
|
|
3513
|
+
#side-panel{padding-top:0}
|
|
3514
|
+
#side-panel{border-radius:0;margin:0}
|
|
3515
|
+
body.nav-collapsed>aside{width:min(332px,90vw);flex-basis:auto}
|
|
3516
|
+
#side-body{width:auto;align-self:stretch}
|
|
3517
|
+
#side-panel,body.nav-collapsed #side-panel{flex:1 1 auto;width:auto;min-width:0;margin:0}
|
|
2926
3518
|
#burger{display:grid}
|
|
2927
|
-
|
|
2928
|
-
|
|
3519
|
+
/* One bar, not two: the burger sits at the head of the tab strip, in the
|
|
3520
|
+
frame's colour, rather than on a white row of its own above it. #top
|
|
3521
|
+
keeps its row only while it has the changes button to show. */
|
|
3522
|
+
#top{padding:0;min-height:0}
|
|
3523
|
+
#top:has(#changes-btn:not([hidden])){padding:6px 10px 6px 52px;background:var(--frame)}
|
|
3524
|
+
#burger{position:fixed;z-index:15;top:0;left:4px;width:40px;height:36px;margin:0;border-radius:8px}
|
|
3525
|
+
#tabs,body.nav-collapsed #tabs{padding-left:44px}
|
|
3526
|
+
/* The front tab has the width it needs: its buttons show on a phone, where
|
|
3527
|
+
there is no hover to wait for, and they left the title as "Unt…". */
|
|
3528
|
+
.tab.on{max-width:none}
|
|
2929
3529
|
|
|
2930
3530
|
/* Both panes are the screen while open, and each gets a way out of its own:
|
|
2931
3531
|
the top bar with the changes button is underneath them. */
|
|
@@ -2938,7 +3538,9 @@ dialog::backdrop{background:rgba(0,0,0,.55)}
|
|
|
2938
3538
|
#explorer-head{padding-top:max(10px,env(safe-area-inset-top))}
|
|
2939
3539
|
#explorer-body{flex-direction:column}
|
|
2940
3540
|
#explorer-resizer{display:none}
|
|
2941
|
-
#explorer-
|
|
3541
|
+
#explorer-side{width:auto;height:var(--explorer-tree-height,38%);max-height:none}
|
|
3542
|
+
#explorer-find{height:36px}
|
|
3543
|
+
#explorer-q{font-size:16px}
|
|
2942
3544
|
/* Stacked, so the handle between them drags up and down — and is a strip
|
|
2943
3545
|
a thumb can find rather than a hairline. */
|
|
2944
3546
|
#explorer-split{flex:0 0 18px;margin:0;cursor:row-resize}
|
|
@@ -3001,7 +3603,7 @@ dialog::backdrop{background:rgba(0,0,0,.55)}
|
|
|
3001
3603
|
.effort-stop,.effort-name{width:40px}
|
|
3002
3604
|
.effort-stop{height:40px}
|
|
3003
3605
|
.effort-knob{width:28px;height:28px}
|
|
3004
|
-
#jump{right:14px
|
|
3606
|
+
#jump{right:14px}
|
|
3005
3607
|
|
|
3006
3608
|
/* Dialogs, with the keyboard up: vh is the height without it. */
|
|
3007
3609
|
#market{max-height:82dvh}
|