Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
233 changes: 188 additions & 45 deletions desktop/renderer/css/popovers.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,63 +3,206 @@
Loaded after styles.css (tokens + kit). Colours come from tokens only.
============================================================ */

.popover{position:absolute;background:var(--bg-overlay);border-radius:0;box-shadow:var(--shadow-overlay);width:340px;z-index:25;overflow:hidden}
.poprow{display:flex;align-items:flex-start;gap:var(--s3);padding:8px var(--s4);width:100%}
.poprow:hover{background:var(--bg-hover)}
.poprow.on{background:var(--accent-wash)}
/* ---- The popover shell ----
Every composer popover is a floating --lift card: radius 18, the pop shadow,
6px inside (the kit's .tk-pop rides on the same element). anchorStyle() pins
it above the composer card of the chip that opened it, gives it that chip's
width and caps its height to what the content column leaves; the numbers
here are only the fallback for a popover with no chip on screen. */
.popover{position:absolute;z-index:25;width:340px;max-width:calc(100vw - 32px);overflow:hidden;
background:var(--lift);border-radius:var(--r-pop);box-shadow:var(--sh-pop);padding:6px;
display:flex;flex-direction:column;gap:1px;color:var(--ink)}
.popfoot{flex:none;display:flex;align-items:center;justify-content:flex-end;gap:6px;
margin-top:4px;padding:6px 0 0 2px;box-shadow:inset 0 1px 0 var(--divider)}
.popfoot .grow{flex:1;min-width:0}
/* Footer verbs are the small pill size everywhere, including the ones another
function hands the shell (the model step's Back · Use default · Use this model). */
.popover .popfoot .btn:not(.icon){height:26px;padding:0 10px;font-size:12px}
.popover .btn svg{width:14px;height:14px}

.ctxbar{display:flex;height:6px;border-radius:0;overflow:hidden;margin-top:10px;gap:1px}
.ctxbar i{display:block}
.swatch{width:8px;height:8px;border-radius:0;flex:none}
.popfoot{display:flex;align-items:center;justify-content:flex-end;gap:var(--s3);padding:var(--s4);box-shadow:inset 0 1px 0 var(--line-soft)}
/* ---------------- model selector popup ---------------- */
.selpop{width:460px;max-height:60vh}
.selhead{flex:none;display:flex;align-items:center;gap:8px;min-height:32px;padding:4px 8px;
font:600 13px/1.3 var(--font-ui);letter-spacing:-.003em;color:var(--ink)}
.selhead .selttl{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.selhead .logo,.selhead .tk-ico{flex:none}
.selhead .selbusy{margin-left:auto;flex:none;display:inline-flex;align-items:center;gap:6px;font-weight:500;color:var(--ink-2)}
.selhead .selbusy .tk-spin{width:11px;height:11px}

.modellist{display:flex;flex-direction:column}
.modelrow{display:flex;align-items:center;gap:var(--s4);padding:8px var(--s4);width:100%;border-left:2px solid transparent;cursor:pointer}
.modelrow + .modelrow{box-shadow:inset 0 1px 0 var(--line-soft)}
.modelrow:hover{background:var(--bg-hover)}
.modelrow.on{background:var(--accent-wash);border-left-color:var(--accent)}
.modelrow .col{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
.modelrow .nm{color:var(--text-primary);font-size:13px}
.selsearch{flex:none;padding:0 0 6px}
.selpop .tk-inpwrap{height:36px;padding:0 12px}
.selpop .tk-inpwrap > svg{width:15px;height:15px;flex:none}
.selpop .tk-inpwrap input{font-size:13.5px}
.selpop .tk-inpwrap.is-error{background:var(--lift);border-color:var(--critical);box-shadow:0 0 0 4px var(--critical-wash)}
.selpop .tk-inpwrap.is-warn{border-color:var(--amber)}
.selpop .tk-inpwrap .tk-spin{flex:none;margin-right:4px}

/* ---------------- model selector popup ---------------- */
.selpop { width: 460px; max-width: calc(100vw - 32px); display: flex; flex-direction: column; max-height: 60vh; }
.selhead {
flex: none; height: 36px; display: flex; align-items: center; gap: var(--s3);
padding: 0 var(--s5); font-size: 13px; font-weight: 500;
box-shadow: inset 0 -1px 0 var(--line-soft);
}
.selsearch { flex: none; padding: var(--s3) var(--s5); }
.selbody { flex: 1; min-height: 0; overflow-y: auto; }
.selbody{flex:1 1 auto;min-height:0;overflow-y:auto;display:flex;flex-direction:column}
/* The list scrolls inside the popup — it must never grow past the window. */
.sellist { flex: 1; min-height: 0; max-height: 320px; overflow-y: auto; display: flex; flex-direction: column; }
.selpop .popfoot { flex: none; }
.sellist{flex:1 1 auto;min-height:0;max-height:340px;overflow-y:auto;display:flex;flex-direction:column;gap:1px}
.selbody > .sellist{flex:none}
.selpop .popfoot{flex:none}

/* One row: badge · id over its one-line facts · right slot (radio, Unverified, download). */
.selpop .modelrow{display:flex;align-items:center;gap:10px;width:100%;min-height:42px;padding:6px 10px;flex:none;
border:0;border-radius:var(--r-well);background:transparent;color:var(--ink);text-align:left;cursor:pointer;
transition:background var(--t-state) var(--ease)}
.selpop .modelrow:hover{background:var(--hover)}
.selpop .modelrow.on{background:var(--brand-wash)}
.selpop .modelrow:focus-visible{outline:2px solid var(--brand);outline-offset:-2px}
.selpop .modelrow .col{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
.selpop .modelrow .nm{font:600 13.5px/1.3 var(--font-ui);letter-spacing:-.003em;color:var(--ink);
overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.selpop .modelrow .nm.mono{font:500 13px/18px var(--font-mono);letter-spacing:0}
.selpop .modelrow .col > .cap{font-size:12px;line-height:1.4;color:var(--ink-2);overflow:hidden;
display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}
.selpop .modelrow .cap.mono,.selpop .modelrow .cap .mono{font-family:var(--font-mono);font-size:11.5px;letter-spacing:0}
.selpop .modelrow.dim .nm{color:var(--ink-3)}
.selpop .modelrow.dim .logo,.selpop .modelrow.dim .tk-ico{opacity:.55}
.selpop .selr{margin-left:auto;flex:none;display:flex;align-items:center;gap:6px;padding-left:4px}
.selpop .seldl svg{width:13px;height:13px}
.selpop .selsep{margin:4px 8px}
.selspin{width:22px;height:22px;flex:none;display:grid;place-items:center}
/* MP-11: the typed query inside a result. A wrapper around the matched letters
only — the row's .nm keeps exactly the text it had. */
.selhit{background:var(--brand-wash);color:var(--blue);border-radius:3px}

.selnote{flex:none;display:flex;align-items:center;gap:8px;margin:0;padding:12px 10px;font-size:12.5px;line-height:1.45;color:var(--ink-2)}
/* A switch error. The inline colour on it is what drive-selector reads — keep it. */
.selerr{flex:none;display:flex;align-items:flex-start;gap:8px;margin:6px 0 0;padding:8px 10px;border-radius:var(--r-well);
background:var(--critical-wash);font-size:12.5px;line-height:1.45;overflow-wrap:anywhere;max-height:140px;overflow-y:auto}
.selerr > svg{flex:none;width:14px;height:14px;margin-top:2px}

/* MP-16: the popover's own download — the raw CLI line in a mono well. */
.selpull{gap:8px;padding:2px 2px 6px}
.selpull .selpullline{margin:0;padding:10px 12px;border-radius:var(--r-well);background:var(--well);color:var(--ink);
font:400 12px/1.5 var(--font-mono);white-space:pre-wrap;overflow-wrap:anywhere}
.selpull > .cap:not(.selpullline){margin:0 4px}

/* MP-04: the preset list's key field. */
.selkey{flex:none;padding:8px 0 2px}

/* ---- The add-provider wizard inside the popover (MP-06…10) ---- */
.selwiz{gap:6px;padding:0 2px 6px}
.selwiz .tk-lbl{padding:4px 4px 0}
.selwiz #wiz-key,.selwiz #wiz-url{font-family:var(--font-mono);font-size:12.5px}
.selpop .ob-help{margin:0;padding:0 4px;max-width:none;font-size:12.5px;line-height:1.45;color:var(--ink-2)}
.selpop .ob-err{margin:0;padding:0 4px;max-width:none;font-size:12.5px;line-height:1.45;white-space:pre-wrap;color:var(--critical)}
.selpop .ob-err.tk-help--warn{color:var(--amber-text)}

/* Inside the popover the shell's own body is the scroller, so the list must
not add a second one — two nested scrollers is how the footer ends up
somewhere nobody can reach. */
.selpop .ob-wizlist{max-height:none;overflow:visible}
.selpop .ob-wiz{display:flex;flex-direction:column;gap:6px;width:100%;padding:0 0 4px}
/* The popover's header already names the provider; the step's own kicker and heading would say it twice. */
.selpop .ob-wiz > .ob-kicker,.selpop .ob-wiz > .ob-h{display:none}
.selpop #wiz-model-q{width:100%;height:34px;flex:none;padding:0 14px;border:1px solid transparent;border-radius:var(--r-pill);
background:var(--well);color:var(--ink);font:400 13.5px/1 var(--font-ui);outline:none}
.selpop #wiz-model-q::placeholder{color:var(--ink-3)}
.selpop #wiz-model-q:focus{background:var(--lift);border-color:var(--brand);box-shadow:var(--focus-ring)}
.selpop .prows{display:flex;flex-direction:column;gap:1px;border:0;box-shadow:none;background:transparent}
.selpop .prows [data-wizmodel]{display:flex;align-items:center;gap:10px;width:100%;min-height:42px;padding:6px 10px;
border:0;border-radius:var(--r-well);background:transparent;box-shadow:none;color:var(--ink);text-align:left;cursor:pointer}
.selpop .prows [data-wizmodel]:hover{background:var(--hover)}
.selpop .prows [data-wizmodel].on{background:var(--brand-wash)}
.selpop .prows [data-wizmodel] .col{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
.selpop .prows [data-wizmodel] .nm{font:500 13.5px/1.3 var(--font-ui);color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.selpop .prows [data-wizmodel] .ep{font:400 11.5px/1.4 var(--font-mono);color:var(--ink-2)}
.selpop .prows [data-wizmodel] .ann{margin-left:auto;flex:none;background:var(--brand-wash);color:var(--blue)}

/* ---- Context breakdown (CM-15…17) ---- */
.ctxpop{width:380px}
.ctxin{flex:none;display:flex;flex-direction:column;gap:10px;padding:8px 8px 10px}
.ctxpop .ctxttl{margin:0;font:500 12.5px/1.4 var(--font-mono);letter-spacing:0;color:var(--ink)}
.ctxmeter{display:flex;flex:none;height:8px}
.ctxmeter > i,.ctxmeter > b{display:block;height:100%;flex:none;border-radius:0}
.ctxmeter > b{background:var(--bd-2)}
.ctxmeter.proj > i{background:repeating-linear-gradient(90deg,var(--brand) 0 3px,transparent 3px 5px)}
.ctxpop .kvgrid{grid-template-columns:minmax(0,1fr) max-content;gap:5px 12px;font-size:13px}
.ctxpop .kvgrid dt{color:var(--ink)}
.ctxpop .kvgrid dd{text-align:right;font-size:12px;line-height:18px;color:var(--ink)}
.ctxpop .kvgrid .ter{color:var(--ink-2)}
.ctxpop .ctxin > .cap{margin:0;line-height:1.5}
/* ---- Item 7C: the context panel's binding line ---- */
.ctxbound{display:flex;align-items:flex-start;gap:6px;color:var(--amber-text)}
.ctxbound > svg{flex:none;width:13px;height:13px;margin-top:2px}
/* context panel dials */
.ctxdials { box-shadow: inset 0 1px 0 var(--line-soft); padding: var(--s3) 0; }
.ctxdial { display: flex; align-items: center; gap: var(--s4); padding: var(--s2) var(--s5); }
.ctxdial .col { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; font-size: 13px; }
.ctxdial .btn { min-width: 28px; justify-content: center; }
.ctxdials{flex:none;padding:0 8px 2px}
.ctxdial{display:flex;align-items:center;gap:6px;padding:10px 0 4px;box-shadow:inset 0 1px 0 var(--divider)}
.ctxdial .col{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
.ctxdial .ctxdt{font:600 13px/1.3 var(--font-ui);color:var(--ink)}
.ctxdial .ctxstep{display:flex;align-items:center;gap:4px}
.ctxdial .ctxval{min-width:32px;text-align:center;font-size:13px}

.modelrow.dim { opacity: .5; }
/* ---- Modes (CM-12…14) ---- */
.modepop{width:360px}
.modehead{flex:none;display:flex;align-items:baseline;gap:8px;min-height:30px;padding:6px 8px 4px;
font:600 13px/1.3 var(--font-ui);color:var(--ink)}
.modehead .cap{margin-left:auto;min-width:0;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.poprow{display:flex;align-items:center;gap:10px;width:100%;min-height:44px;padding:6px 10px;flex:none;
border:0;border-radius:var(--r-well);background:transparent;color:var(--ink);text-align:left;cursor:pointer;
transition:background var(--t-state) var(--ease)}
.poprow:hover{background:var(--hover)}
.poprow:focus-visible{outline:2px solid var(--brand);outline-offset:-2px}
.poprow .col{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
.poprow .ml{font:600 13.5px/1.3 var(--font-ui);color:var(--ink)}
.poprow .col > .cap{font-size:12px;line-height:1.4}
.poprow .modecur{margin-left:auto;flex:none}
/* The row's word takes the chip's colour: default ink, plan blue, auto amber, bypass red. */
.poprow.modetone-plan .ml{color:var(--blue)}
.poprow.modetone-auto .ml{color:var(--amber-text)}
.poprow.modetone-bypass .ml{color:var(--critical)}
.modenote{flex:none;display:flex;flex-direction:column;gap:4px;padding:6px 10px 2px}
.modenote p{margin:0;line-height:1.5}
.modenote .modewarn{color:var(--amber-text)}
.modenote .ob-help{max-width:none;font-size:12px;color:var(--ink-2)}

/* ============================================================
Coding-mode popover — the unavailable state (item 6)
============================================================
`.dim` is applied to the mode rows when the agent has no
/api/coding-mode route, but the only `.dim` rules that existed were
`.palrow`, `.modelrow` and `.tuirow`, so the unavailable state was
invisible. A disabled row also has to stop looking clickable. */
.poprow.dim{opacity:.5}
/api/coding-mode route. A disabled row has to look unavailable
and stop looking clickable. (The smoke looks for this rule.) */
.poprow.dim{cursor:default}
.poprow.dim .ml,.poprow.dim .col > .cap{color:var(--ink-3)}
.poprow[disabled]{cursor:default}
.poprow[disabled]:hover{background:transparent}

/* ---- Item 7C: the context panel's binding line ---- */
.ctxbound{color:var(--warn)}

.modelrow .ann{margin-left:auto}

/* Inside the popover the shell's own body is the scroller, so the list must
not add a second one — two nested scrollers is how the footer ends up
somewhere nobody can reach. */
.selpop .ob-wizlist{max-height:none;overflow:visible}
.selpop .ob-wiz{padding:var(--s4) var(--s5)}
/* ---- Voice language menu (CM-08) ----
voiceMenuHTML draws it inside the voice strip; it floats above the strip as a
popover, off .composerwrap (the strip's positioned ancestor). The .tk-vmenu
compound keeps these declarations ahead of the strip's own .vsm* rules. */
.voicestrip .vsmenu.tk-vmenu{position:absolute;z-index:16;right:var(--s6);bottom:calc(100% + 6px);
width:420px;max-width:calc(100% - 16px);max-height:none;margin:0;padding:6px;border:0;border-radius:var(--r-pop);
background:var(--lift);box-shadow:var(--sh-pop);display:flex;flex-direction:column;gap:1px;overflow:hidden}
.voicestrip .tk-vmenu .vsmtitle{flex:none;padding:6px 8px 2px;font:600 13px/1.3 var(--font-ui);color:var(--ink)}
/* The list gives up height first on a short window, so the menu never climbs under the toolbar (940×620). */
.voicestrip .tk-vmenu .vsmscroll{min-height:0;max-height:min(300px,max(160px,calc(100vh - 460px)));overflow-y:auto;display:flex;flex-direction:column;gap:1px}
.voicestrip .tk-vmenu .vsmhead{padding:8px 8px 3px;font:600 12px/1.3 var(--font-ui);letter-spacing:0;text-transform:none;color:var(--ink-2)}
.voicestrip .tk-vmenu .vsmrow{display:flex;align-items:center;gap:6px;padding:0 8px 0 0;border-radius:var(--r-well);background:transparent}
.voicestrip .tk-vmenu .vsmrow:hover{background:var(--hover)}
.voicestrip .tk-vmenu .vsmpick{flex:1;min-width:0;display:flex;align-items:center;gap:10px;height:auto;min-height:42px;padding:5px 10px;
border:0;border-radius:var(--r-well);background:transparent;color:var(--ink);text-align:left}
.voicestrip .tk-vmenu .vsmbody{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
.voicestrip .tk-vmenu .vsmline{display:flex;align-items:center;gap:6px;min-width:0}
.voicestrip .tk-vmenu .vsmname{flex:none;font:500 13.5px/1.3 var(--font-ui);color:var(--ink)}
.voicestrip .tk-vmenu .vsmtag{flex:none;font:400 11px/1 var(--font-mono);color:var(--ink-2)}
.voicestrip .tk-vmenu .vsmnote{flex:none;text-align:left;font-size:12px;line-height:1.35;color:var(--ink-2);
white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.voicestrip .tk-vmenu .vsmbadge{flex:none;height:18px;display:inline-flex;align-items:center;padding:0 7px;border-radius:var(--r-pill);
background:var(--brand-wash);color:var(--blue);font:600 11px/1 var(--font-ui)}
.voicestrip .tk-vmenu .vsmadd{width:26px;height:26px;flex:none;display:grid;place-items:center;border:0;border-radius:50%;
background:transparent;color:var(--ink-2)}
.voicestrip .tk-vmenu .vsmadd:hover{background:var(--press);color:var(--ink)}
.voicestrip .tk-vmenu .vsmadd.on{background:var(--brand-wash);color:var(--blue)}
.voicestrip .tk-vmenu .vsmadd svg{width:14px;height:14px}
.voicestrip .tk-vmenu .vsmprog{flex:none;width:60px;min-width:0}
.voicestrip .tk-vmenu .vsmerr{padding:6px 10px;font-size:12.5px;line-height:1.45;color:var(--critical)}
.voicestrip .tk-vmenu .vsmfoot{flex:none;margin-top:4px;padding:8px 10px 4px;box-shadow:inset 0 1px 0 var(--divider);
font-size:12px;line-height:1.5;color:var(--ink-2)}
.voicestrip .tk-vmenu .vsmempty{padding:10px;font-size:12.5px;color:var(--ink-2)}
/* On a short window the long note scrolls in its own few lines instead of pushing the languages out. */
@media (max-height:700px){
.voicestrip .tk-vmenu .vsmfoot{max-height:78px;overflow-y:auto}
}
Loading