@mlx-bun/module-chat 0.0.0-stage → 1.0.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/LICENSE +21 -0
- package/README.md +85 -2
- package/package.json +39 -4
- package/src/app-tools.ts +101 -0
- package/src/backend.ts +108 -0
- package/src/describe.ts +47 -0
- package/src/events.ts +75 -0
- package/src/extensions.ts +44 -0
- package/src/history.ts +188 -0
- package/src/index.ts +74 -0
- package/src/manifest.ts +26 -0
- package/src/model-bridge.ts +75 -0
- package/src/panel/adapters-panel.ts +287 -0
- package/src/panel/assistant.ts +469 -0
- package/src/panel/chat.ts +1076 -0
- package/src/panel/composer.ts +947 -0
- package/src/panel/dom.ts +94 -0
- package/src/panel/host.ts +28 -0
- package/src/panel/index.ts +100 -0
- package/src/panel/markdown.ts +547 -0
- package/src/panel/popovers.ts +17 -0
- package/src/panel/rag.ts +274 -0
- package/src/panel/sessions.ts +470 -0
- package/src/panel/style.ts +532 -0
- package/src/panel/template.ts +189 -0
- package/src/panel/ui-catalog.ts +77 -0
- package/src/panel/voice.ts +141 -0
- package/src/pi-backend.ts +989 -0
- package/src/policy.ts +417 -0
- package/src/protocol.ts +265 -0
- package/src/provider.ts +72 -0
- package/src/routes.ts +49 -0
- package/src/session-files.ts +36 -0
- package/src/session-search.ts +175 -0
- package/src/surface.ts +26 -0
- package/src/tool-approvals.ts +108 -0
- package/src/web-tools.ts +478 -0
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
// The panel's stylesheet, injected once into the page's head. It uses the host page's design tokens (`--ink`, `--dim`,
|
|
2
|
+
// `--hairline`, `--card`, `--blue`, ...) so it follows the theme; the host supplies those and the base element styles.
|
|
3
|
+
export const STYLE = String.raw`
|
|
4
|
+
/* ── the panel element is the page: sidebar and conversation side by side ── */
|
|
5
|
+
mlx-chat-panel{display:flex;flex-direction:row;height:100%}
|
|
6
|
+
/* ── base rules the panel shares with its host page (identical there; a standalone host has only these) ── */
|
|
7
|
+
.grad{background-clip:text;-webkit-background-clip:text;color:transparent}
|
|
8
|
+
.g-hero{background-image:linear-gradient(96deg,var(--orange) 0%,var(--pink) 30%,var(--purple) 62%,var(--blue) 100%)}
|
|
9
|
+
.pill{display:inline-flex;align-items:center;gap:8px;
|
|
10
|
+
border:1px solid var(--hairline);border-radius:999px;padding:6px 13px;
|
|
11
|
+
font-size:12.5px;font-weight:600;color:var(--dim);letter-spacing:.01em;white-space:nowrap}
|
|
12
|
+
.pill .dot{width:8px;height:8px;border-radius:50%;background:var(--dimmer);
|
|
13
|
+
box-shadow:0 0 0 transparent;transition:all .4s}
|
|
14
|
+
.pill.ok .dot{background:var(--green);box-shadow:0 0 11px var(--green)}
|
|
15
|
+
.pill.ok{color:var(--ink)}
|
|
16
|
+
.pill.bad .dot{background:var(--red);box-shadow:0 0 11px var(--red)}
|
|
17
|
+
.pill.warn .dot{background:var(--orange);box-shadow:0 0 11px var(--orange)}
|
|
18
|
+
.samplepop{position:absolute;bottom:calc(100% + 9px);right:0;z-index:40;width:300px;
|
|
19
|
+
max-height:min(70vh,560px);overflow-y:auto;
|
|
20
|
+
background:var(--popover-panel);border:1px solid var(--hairline);border-radius:13px;padding:13px 14px;
|
|
21
|
+
backdrop-filter:blur(22px) saturate(1.3);-webkit-backdrop-filter:blur(22px) saturate(1.3);
|
|
22
|
+
box-shadow:0 18px 50px rgba(0,0,0,.35);display:none;
|
|
23
|
+
transform:translateY(4px);opacity:0;transition:opacity .16s var(--ease),transform .16s var(--ease)}
|
|
24
|
+
.samplepop.open{display:block;transform:translateY(0);opacity:1}
|
|
25
|
+
.samplepop h4{font-size:12px;font-weight:600;color:var(--dim);letter-spacing:.02em;
|
|
26
|
+
text-transform:uppercase;margin:0}
|
|
27
|
+
.mem-head{display:flex;align-items:center;gap:10px;padding:16px 18px;border-bottom:1px solid var(--hairline-soft);flex:0 0 auto}
|
|
28
|
+
.mem-head h2{font-size:16px;font-weight:700;letter-spacing:-.01em;flex:1 1 auto}
|
|
29
|
+
.mem-close{appearance:none;background:none;border:none;cursor:pointer;color:var(--dim);font-size:15px;
|
|
30
|
+
width:30px;height:30px;border-radius:8px;transition:color .2s,background .2s;flex:0 0 auto}
|
|
31
|
+
.mem-close:hover{color:var(--ink);background:var(--card-hover)}
|
|
32
|
+
@keyframes fade{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
|
|
33
|
+
/* ── chat ── */
|
|
34
|
+
#chat-think{display:none;cursor:pointer;gap:7px;padding:5px 11px;font-size:12px;user-select:none;-webkit-user-select:none;
|
|
35
|
+
transition:color .22s var(--ease),border-color .22s var(--ease)}
|
|
36
|
+
#chat-think .dot{width:7px;height:7px}
|
|
37
|
+
#chat-think:hover{border-color:var(--hairline-strong)}
|
|
38
|
+
#chat-think.think-on{color:var(--ink)}
|
|
39
|
+
#chat-think.think-on .dot{background:var(--blue);box-shadow:0 0 11px var(--blue)}
|
|
40
|
+
/* sampling disclosure: a small pill that opens a popover above the composer */
|
|
41
|
+
#chat-sampling-wrap{position:relative;display:inline-flex}
|
|
42
|
+
#chat-sampling{cursor:pointer;gap:6px;padding:5px 11px;font-size:12px;user-select:none;-webkit-user-select:none;
|
|
43
|
+
transition:color .22s var(--ease),border-color .22s var(--ease)}
|
|
44
|
+
#chat-sampling:hover{border-color:var(--hairline-strong);color:var(--ink)}
|
|
45
|
+
#chat-sampling.on{color:var(--ink);border-color:var(--hairline-strong)}
|
|
46
|
+
#chat-sampling.dirty .dot{background:var(--blue);box-shadow:0 0 11px var(--blue)}
|
|
47
|
+
/* Distinct one-shot-armed dot state (task brief item 2) — orange/amber
|
|
48
|
+
reads as "temporary/pending" against the plain blue "overridden"
|
|
49
|
+
state, matching the same semantic the orange dot already carries
|
|
50
|
+
elsewhere (.pill.warn) rather than inventing a new color meaning. */
|
|
51
|
+
#chat-sampling.armed .dot{background:var(--orange);box-shadow:0 0 11px var(--orange);animation:pulse-armed 1.6s var(--ease) infinite}
|
|
52
|
+
@media (prefers-reduced-motion:reduce){#chat-sampling.armed .dot{animation:none}}
|
|
53
|
+
@keyframes pulse-armed{0%,100%{opacity:1}50%{opacity:.45}}
|
|
54
|
+
#chat-sampling .dot{width:7px;height:7px}
|
|
55
|
+
.samp-head-row{display:flex;align-items:center;justify-content:space-between;gap:8px;margin:0 0 10px}
|
|
56
|
+
.samp-oneshot-chip{font:10.5px/1 var(--mono);color:var(--orange);background:rgba(255,159,10,.12);
|
|
57
|
+
border:1px solid rgba(255,159,10,.3);border-radius:999px;padding:3px 8px;letter-spacing:.01em;white-space:nowrap}
|
|
58
|
+
.samp-scope-row{display:flex;align-items:center;gap:9px;margin:0 0 12px;padding-bottom:11px;border-bottom:1px solid var(--hairline-soft)}
|
|
59
|
+
.samp-scope-row label{font-size:12.5px;color:var(--ink);width:74px;flex:0 0 auto}
|
|
60
|
+
.samp-scope-row select{flex:1 1 auto;background:var(--card);border:1px solid var(--hairline);border-radius:7px;
|
|
61
|
+
color:var(--ink);font-size:12px;padding:5px 8px;cursor:pointer;appearance:none;-webkit-appearance:none;
|
|
62
|
+
transition:border-color .18s var(--ease)}
|
|
63
|
+
.samp-scope-row select:hover,.samp-scope-row select:focus{border-color:var(--hairline-strong)}
|
|
64
|
+
.samprow{display:flex;align-items:center;gap:9px;margin:0 0 11px}
|
|
65
|
+
.samprow label{font-size:12.5px;color:var(--ink);width:74px;flex:0 0 auto}
|
|
66
|
+
.samprow input[type=range]{flex:1 1 auto;-webkit-appearance:none;appearance:none;height:4px;margin:0;
|
|
67
|
+
border-radius:3px;background:rgba(255,255,255,.15);outline:none;cursor:pointer}
|
|
68
|
+
.samprow input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:15px;height:15px;
|
|
69
|
+
border-radius:50%;background:var(--ink);border:0;box-shadow:0 1px 4px rgba(0,0,0,.5);cursor:pointer;
|
|
70
|
+
transition:background .15s var(--ease)}
|
|
71
|
+
.samprow input[type=range]:hover::-webkit-slider-thumb,
|
|
72
|
+
.samprow input[type=range]:focus::-webkit-slider-thumb{background:var(--blue)}
|
|
73
|
+
.samp-val{width:40px;flex:0 0 auto;text-align:right;font-family:var(--mono);font-size:12px;color:var(--ink)}
|
|
74
|
+
.samp-val.auto{color:var(--dimmer)}
|
|
75
|
+
.samp-num{width:74px;flex:0 0 auto;text-align:right;background:var(--card);border:1px solid var(--hairline);
|
|
76
|
+
border-radius:6px;color:var(--ink);font-family:var(--mono);font-size:11.5px;padding:3px 6px}
|
|
77
|
+
.samp-num.auto{color:var(--dimmer)}
|
|
78
|
+
.samppop-foot{display:flex;justify-content:space-between;align-items:center;margin-top:3px}
|
|
79
|
+
.samppop-foot .note{font-size:10.5px;color:var(--dimmer);line-height:1.3;flex:1 1 auto;padding-right:8px}
|
|
80
|
+
.samppop-foot button{appearance:none;cursor:pointer;background:var(--card);border:1px solid var(--hairline);
|
|
81
|
+
color:var(--dim);border-radius:8px;padding:5px 11px;font-size:11.5px;font-weight:600;flex:0 0 auto;
|
|
82
|
+
transition:color .18s var(--ease),border-color .18s var(--ease)}
|
|
83
|
+
.samppop-foot button:hover{color:var(--ink);border-color:var(--hairline-strong)}
|
|
84
|
+
/* Advanced sampling disclosure — collapsed by default, same visual language
|
|
85
|
+
as the primary rows so it doesn't read as a separate control system. */
|
|
86
|
+
.samp-adv{margin:2px 0 12px}
|
|
87
|
+
.samp-adv > summary{cursor:pointer;font-size:11.5px;font-weight:600;color:var(--dim);letter-spacing:.02em;
|
|
88
|
+
text-transform:uppercase;list-style:none;padding:2px 0 8px;user-select:none;-webkit-user-select:none}
|
|
89
|
+
.samp-adv > summary::-webkit-details-marker{display:none}
|
|
90
|
+
.samp-adv > summary::before{content:"▸ ";display:inline-block;transition:transform .15s var(--ease)}
|
|
91
|
+
.samp-adv[open] > summary::before{transform:rotate(90deg)}
|
|
92
|
+
.samp-adv > summary:hover{color:var(--ink)}
|
|
93
|
+
.samp-adv .seed-row{display:flex;align-items:center;gap:9px;margin:0 0 4px}
|
|
94
|
+
.samp-adv .seed-row label{font-size:12.5px;color:var(--ink);width:74px;flex:0 0 auto}
|
|
95
|
+
.samp-adv .seed-row input[type=text]{flex:1 1 auto;background:var(--card);border:1px solid var(--hairline);
|
|
96
|
+
border-radius:6px;color:var(--ink);font-family:var(--mono);font-size:12px;padding:5px 8px}
|
|
97
|
+
.samp-adv .seed-row input[type=text]::placeholder{color:var(--dimmer)}
|
|
98
|
+
/* System prompt pill + popover — same chrome as
|
|
99
|
+
#chat-sampling-wrap/.samplepop, just to its left in the hint row. */
|
|
100
|
+
#chat-sysprompt-wrap{position:relative;display:inline-flex}
|
|
101
|
+
#chat-sysprompt{cursor:pointer;gap:6px;padding:5px 11px;font-size:12px;user-select:none;-webkit-user-select:none;
|
|
102
|
+
transition:color .22s var(--ease),border-color .22s var(--ease)}
|
|
103
|
+
#chat-sysprompt:hover{border-color:var(--hairline-strong);color:var(--ink)}
|
|
104
|
+
#chat-sysprompt.on{color:var(--ink);border-color:var(--hairline-strong)}
|
|
105
|
+
#chat-sysprompt.dirty .dot{background:var(--purple);box-shadow:0 0 11px var(--purple)}
|
|
106
|
+
#chat-sysprompt .dot{width:7px;height:7px}
|
|
107
|
+
.sysprompt-pop{width:320px}
|
|
108
|
+
#sysprompt-text{width:100%;resize:vertical;min-height:80px;background:var(--card);
|
|
109
|
+
border:1px solid var(--hairline);border-radius:9px;color:var(--ink);
|
|
110
|
+
font:12.5px/1.5 inherit;padding:9px 10px;margin:0 0 6px}
|
|
111
|
+
#sysprompt-text::placeholder{color:var(--dimmer)}
|
|
112
|
+
#sysprompt-text:focus{outline:none;border-color:var(--hairline-strong)}
|
|
113
|
+
.sysprompt-foot-row{display:flex;justify-content:space-between;align-items:center;margin:0 0 10px}
|
|
114
|
+
.sysprompt-foot-row .note{font-size:10.5px;color:var(--dimmer)}
|
|
115
|
+
.sysprompt-foot-row button,.sysprompt-preset-row button{appearance:none;cursor:pointer;background:var(--card);
|
|
116
|
+
border:1px solid var(--hairline);color:var(--dim);border-radius:8px;padding:5px 11px;font-size:11.5px;
|
|
117
|
+
font-weight:600;flex:0 0 auto;transition:color .18s var(--ease),border-color .18s var(--ease)}
|
|
118
|
+
.sysprompt-foot-row button:hover,.sysprompt-preset-row button:hover{color:var(--ink);border-color:var(--hairline-strong)}
|
|
119
|
+
.sysprompt-presets{margin-top:12px;padding-top:12px;border-top:1px solid var(--hairline-soft)}
|
|
120
|
+
.sysprompt-preset-row{display:flex;align-items:center;gap:6px;margin:0 0 6px}
|
|
121
|
+
#sysprompt-preset-select{flex:1 1 auto;min-width:0;max-width:none;appearance:none;-webkit-appearance:none;
|
|
122
|
+
cursor:pointer;background:var(--card);text-overflow:ellipsis}
|
|
123
|
+
/* ─── Adapter routing table — same right-side
|
|
124
|
+
overlay/panel/head/body chrome as the Memory panel (mem-overlay/
|
|
125
|
+
mem-panel/mem-head/mem-body), new top-level ids since it's a distinct
|
|
126
|
+
panel opened from the composer instead of the sidebar. ─── */
|
|
127
|
+
#adapters-overlay{position:fixed;inset:var(--nav-h) 0 0 0;z-index:150;display:none;justify-content:flex-end;
|
|
128
|
+
background:var(--overlay-scrim);backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px)}
|
|
129
|
+
#adapters-overlay.open{display:flex;animation:fade .22s var(--ease)}
|
|
130
|
+
#adapters-panel{width:min(520px,100%);height:100%;background:var(--overlay-panel);border-left:1px solid var(--hairline);
|
|
131
|
+
display:flex;flex-direction:column;box-shadow:-24px 0 60px rgba(0,0,0,.4)}
|
|
132
|
+
#adapters-body{flex:1 1 auto;overflow-y:auto;padding:14px 18px 24px}
|
|
133
|
+
.ad-empty{text-align:center;color:var(--dim);padding:50px 18px;font-size:13.5px;line-height:1.6}
|
|
134
|
+
.ad-sec-title{font-size:10.5px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--dimmer);margin:0 0 9px}
|
|
135
|
+
.ad-note{font-size:11.5px;color:var(--dim);line-height:1.5;margin:0 0 16px}
|
|
136
|
+
.ad-row{display:flex;align-items:flex-start;gap:11px;padding:11px 12px;border-radius:12px;
|
|
137
|
+
border:1px solid var(--hairline-soft);margin-bottom:9px;transition:border-color .15s var(--ease)}
|
|
138
|
+
.ad-row.incompatible{opacity:.55}
|
|
139
|
+
.ad-row.selected{border-color:rgba(10,132,255,.5);background:rgba(10,132,255,.06)}
|
|
140
|
+
.ad-row-main{flex:1 1 auto;min-width:0}
|
|
141
|
+
.ad-row-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
|
|
142
|
+
.ad-row-id{font-size:13.5px;font-weight:600;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
143
|
+
.ad-badge{font:10.5px var(--mono);letter-spacing:.02em;padding:2px 8px;border-radius:999px;border:1px solid var(--hairline);color:var(--dim);white-space:nowrap}
|
|
144
|
+
.ad-badge.mounted{color:var(--green);border-color:rgba(48,209,88,.4)}
|
|
145
|
+
.ad-badge.selected{color:var(--blue);border-color:rgba(10,132,255,.4)}
|
|
146
|
+
.ad-meta{font-size:11.5px;color:var(--dimmer);margin-top:4px;display:flex;gap:10px;flex-wrap:wrap}
|
|
147
|
+
.ad-meta b{color:var(--dim);font-weight:600;font-variant-numeric:tabular-nums}
|
|
148
|
+
.ad-why{font-size:11px;color:var(--dimmer);margin-top:4px;font-style:italic}
|
|
149
|
+
.ad-actions{display:flex;gap:6px;flex:0 0 auto;flex-wrap:wrap;align-items:center}
|
|
150
|
+
.ad-actions button{appearance:none;cursor:pointer;background:var(--card);border:1px solid var(--hairline);
|
|
151
|
+
color:var(--dim);border-radius:8px;padding:5px 11px;font-size:11.5px;font-weight:600;white-space:nowrap;
|
|
152
|
+
transition:color .18s var(--ease),border-color .18s var(--ease),background .18s var(--ease)}
|
|
153
|
+
.ad-actions button:hover:not(:disabled){color:var(--ink);border-color:var(--hairline-strong)}
|
|
154
|
+
.ad-actions button:disabled{opacity:.4;cursor:default}
|
|
155
|
+
.ad-actions button.primary{color:var(--ink);border-color:rgba(10,132,255,.5);background:rgba(10,132,255,.1)}
|
|
156
|
+
.ad-actions label.ad-stack-chk{display:flex;align-items:center;gap:5px;font-size:11px;color:var(--dimmer);cursor:pointer;white-space:nowrap}
|
|
157
|
+
.ad-actions label.ad-stack-chk input{width:auto;margin:0;accent-color:var(--blue)}
|
|
158
|
+
.ad-stack-bar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:10px 12px;border-radius:12px;
|
|
159
|
+
background:var(--card);border:1px solid var(--hairline);margin-bottom:14px}
|
|
160
|
+
.ad-stack-bar .lbl{font-size:11.5px;color:var(--dim);flex:0 0 auto}
|
|
161
|
+
.ad-stack-bar .expr{font:12px var(--mono);color:var(--ink);flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
162
|
+
/* App-aware assistant spotlight ('02d723a:docs/design/web-chat-redesign.md' Appendix A,
|
|
163
|
+
beat-matrix Axis 12) — NEVER-HIJACK overlay: pointer-events:none on the whole thing (the
|
|
164
|
+
highlighted control stays clickable straight through the ring/popover),
|
|
165
|
+
no focus trap, transform/opacity-only animation, auto-dismiss handled
|
|
166
|
+
in assistant.ts. Positioned in fixed viewport coordinates by JS
|
|
167
|
+
(getBoundingClientRect of the target), not CSS layout. */
|
|
168
|
+
#assistant-spotlight{position:fixed;inset:0;z-index:95;pointer-events:none;
|
|
169
|
+
opacity:0;transform:scale(.98);transition:opacity .22s var(--ease),transform .22s var(--ease)}
|
|
170
|
+
#assistant-spotlight.show{opacity:1;transform:none}
|
|
171
|
+
#assistant-spotlight .asr-ring{position:fixed;border-radius:12px;
|
|
172
|
+
box-shadow:0 0 0 3px var(--blue),0 0 0 9999px rgba(0,0,0,.32),0 0 24px rgba(10,132,255,.5);
|
|
173
|
+
transition:left .22s var(--ease),top .22s var(--ease),width .22s var(--ease),height .22s var(--ease)}
|
|
174
|
+
#assistant-spotlight .asr-pop{position:fixed;max-width:min(320px,80vw);background:var(--overlay-panel);
|
|
175
|
+
backdrop-filter:blur(20px);border:1px solid var(--hairline);border-radius:12px;padding:10px 14px;
|
|
176
|
+
font-size:13px;line-height:1.4;box-shadow:0 16px 50px rgba(0,0,0,.35)}
|
|
177
|
+
@media (prefers-reduced-motion:reduce){
|
|
178
|
+
#assistant-spotlight{transition:opacity .01ms;transform:none}
|
|
179
|
+
#assistant-spotlight .asr-ring{transition:none}
|
|
180
|
+
}
|
|
181
|
+
/* ═══════════════════ CHAT ═══════════════════ */
|
|
182
|
+
#chat-main{flex:1;min-width:0;display:flex;flex-direction:column}
|
|
183
|
+
/* recent-chats sidebar */
|
|
184
|
+
#chat-sidebar{flex:0 0 252px;display:flex;flex-direction:column;gap:8px;padding:14px 12px;
|
|
185
|
+
border-right:1px solid var(--hairline);overflow-y:auto;background:var(--card-2)}
|
|
186
|
+
#chat-new{width:100%;text-align:left;padding:10px 13px;border-radius:11px;cursor:pointer;
|
|
187
|
+
background:var(--card);border:1px solid var(--hairline);color:var(--ink);font-size:13.5px;font-weight:600;
|
|
188
|
+
transition:background .15s,border-color .15s}
|
|
189
|
+
#chat-new:hover{background:var(--card-hover);border-color:rgba(10,132,255,.5)}
|
|
190
|
+
/* Session search — trivial client-side substring filter over titles.
|
|
191
|
+
No server change: SessionListItem
|
|
192
|
+
carries no preview text today, so title-only is the honest scope. */
|
|
193
|
+
#chat-sess-search{width:100%;background:var(--card);border:1px solid var(--hairline);border-radius:10px;
|
|
194
|
+
color:var(--ink);font-size:12.5px;padding:8px 11px;outline:none;transition:border-color .2s}
|
|
195
|
+
#chat-sess-search:focus{border-color:rgba(10,132,255,.5)}
|
|
196
|
+
#chat-sess-search::placeholder{color:var(--dimmer)}
|
|
197
|
+
.sesslist{display:flex;flex-direction:column;gap:2px;margin-top:4px}
|
|
198
|
+
.sess{position:relative;padding:9px 50px 9px 11px;border-radius:10px;cursor:pointer;border:1px solid transparent}
|
|
199
|
+
.sess:hover{background:var(--card)}
|
|
200
|
+
.sess.active{background:var(--card);border-color:var(--hairline)}
|
|
201
|
+
.sess.sess-hidden{display:none}
|
|
202
|
+
.sess .stitle{font-size:13px;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
203
|
+
.sess .smeta{font-size:11px;color:var(--dim);margin-top:2px;display:flex;gap:5px;align-items:center;white-space:nowrap}
|
|
204
|
+
.sess .sbtn{position:absolute;top:7px;width:20px;height:20px;border:none;background:transparent;color:var(--dim);
|
|
205
|
+
cursor:pointer;border-radius:6px;opacity:0;font-size:12px;line-height:1;transition:opacity .12s,background .12s,color .12s}
|
|
206
|
+
.sess:hover .sbtn{opacity:.85}
|
|
207
|
+
.sess .sfork{right:28px}
|
|
208
|
+
.sess .sdel{right:6px}
|
|
209
|
+
.sess .sfork:hover{background:var(--card-hover);color:var(--ink);opacity:1}
|
|
210
|
+
.sess .sdel:hover{background:rgba(255,69,58,.18);color:var(--red);opacity:1}
|
|
211
|
+
.sessempty{color:var(--dim);font-size:12px;padding:8px 11px;line-height:1.5}
|
|
212
|
+
/* ─── mobile drawer: the sidebar becomes a
|
|
213
|
+
slide-over panel on narrow viewports instead of vanishing outright. ─── */
|
|
214
|
+
@media (max-width:760px){
|
|
215
|
+
#chat-sidebar{
|
|
216
|
+
position:fixed;top:var(--nav-h);bottom:0;left:0;width:min(84vw,320px);z-index:120;
|
|
217
|
+
border-right:1px solid var(--hairline);
|
|
218
|
+
transform:translateX(-100%);transition:transform .28s var(--ease);
|
|
219
|
+
box-shadow:0 0 0 rgba(0,0,0,0);
|
|
220
|
+
}
|
|
221
|
+
#chat-sidebar.drawer-open{transform:translateX(0);box-shadow:24px 0 60px rgba(0,0,0,.45)}
|
|
222
|
+
#chat-drawer-backdrop{display:none;position:fixed;inset:var(--nav-h) 0 0 0;z-index:110;
|
|
223
|
+
background:var(--overlay-scrim);backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px)}
|
|
224
|
+
#chat-drawer-backdrop.open{display:block;animation:fade .2s var(--ease)}
|
|
225
|
+
}
|
|
226
|
+
@media (max-width:760px) and (prefers-reduced-motion:reduce){
|
|
227
|
+
#chat-sidebar{transition:none}
|
|
228
|
+
}
|
|
229
|
+
#chat-scroll{flex:1;overflow-y:auto}
|
|
230
|
+
#chat-thread{max-width:840px;margin:0 auto;padding:30px 22px 16px;display:flex;flex-direction:column;gap:18px}
|
|
231
|
+
.msg{display:flex;flex-direction:column;gap:6px;max-width:90%}
|
|
232
|
+
.msg .who{font-size:11px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:var(--dimmer)}
|
|
233
|
+
.msg .bubble{border-radius:16px;padding:13px 17px;line-height:1.6;word-break:break-word;overflow-wrap:anywhere}
|
|
234
|
+
.msg.user{align-self:flex-end;align-items:flex-end}
|
|
235
|
+
.msg.user .bubble{background:linear-gradient(120deg,rgba(10,132,255,.26),rgba(94,92,230,.26));border:1px solid rgba(10,132,255,.35)}
|
|
236
|
+
.msg.assistant{align-self:flex-start;width:100%;max-width:840px}
|
|
237
|
+
.msg.assistant .bubble{background:var(--card);border:1px solid var(--hairline)}
|
|
238
|
+
.msg .meta{font-size:11.5px;color:var(--dimmer);font-variant-numeric:tabular-nums}
|
|
239
|
+
.msg .meta b{color:var(--green);font-weight:600}
|
|
240
|
+
/* Message actions: a quiet hover/focus row under each message,
|
|
241
|
+
same visual language as the code-block copy button (.cbcopy) — dim by
|
|
242
|
+
default, brightens on hover, never shouts. Kept visible via :focus-within
|
|
243
|
+
too so keyboard users can tab to the buttons without hovering. */
|
|
244
|
+
.msg-actions{display:flex;gap:6px;opacity:0;transition:opacity .15s;margin-top:2px}
|
|
245
|
+
.msg:hover .msg-actions,.msg:focus-within .msg-actions,.msg-actions.pinned{opacity:1}
|
|
246
|
+
.msg.user .msg-actions{align-self:flex-end}
|
|
247
|
+
.maction{font:11px/1 var(--mono);color:var(--dim);background:transparent;border:1px solid var(--hairline);
|
|
248
|
+
border-radius:6px;padding:4px 9px;cursor:pointer;transition:color .15s,border-color .15s}
|
|
249
|
+
.maction:hover{color:var(--ink);border-color:var(--dim)}
|
|
250
|
+
.maction:disabled{opacity:.4;cursor:default}
|
|
251
|
+
.maction:disabled:hover{color:var(--dim);border-color:var(--hairline)}
|
|
252
|
+
/* Edit-and-resend as sibling branch: ChatGPT/Claude's '< i/n >' linear
|
|
253
|
+
toggle (a tree view is explicitly out of scope). Only
|
|
254
|
+
rendered on the LAST user message, and only when count > 1. */
|
|
255
|
+
.sib-toggle{display:flex;align-items:center;gap:5px;font:11px/1 var(--mono);color:var(--dimmer)}
|
|
256
|
+
.sib-toggle button{background:transparent;border:1px solid var(--hairline);border-radius:6px;color:var(--dim);
|
|
257
|
+
cursor:pointer;padding:2px 7px;line-height:1.4}
|
|
258
|
+
.sib-toggle button:hover:not(:disabled){color:var(--ink);border-color:var(--dim)}
|
|
259
|
+
.sib-toggle button:disabled{opacity:.35;cursor:default}
|
|
260
|
+
.msg-edit-box{width:100%;box-sizing:border-box;background:var(--card-2);border:1px solid var(--hairline);
|
|
261
|
+
border-radius:10px;padding:9px 11px;color:var(--ink);font:inherit;line-height:1.5;resize:vertical;min-height:44px}
|
|
262
|
+
.msg-edit-actions{display:flex;gap:8px;margin-top:6px;justify-content:flex-end}
|
|
263
|
+
.thinkbox{margin:0 0 12px;border:1px solid rgba(10,132,255,.18);border-radius:12px;background:rgba(10,132,255,.06);overflow:hidden}
|
|
264
|
+
.thinkbox summary{cursor:pointer;padding:7px 10px;font-size:12px;font-weight:600;color:var(--dim);user-select:none}
|
|
265
|
+
.thinkbody{padding:0 12px 10px;color:var(--dim);font-size:.92em;white-space:pre-wrap}
|
|
266
|
+
.cursor{display:inline-block;width:8px;height:17px;vertical-align:-3px;border-radius:2px;
|
|
267
|
+
background:linear-gradient(180deg,var(--pink),var(--purple));animation:blink 1s steps(2) infinite}
|
|
268
|
+
@keyframes blink{50%{opacity:0}}
|
|
269
|
+
/* tool cards */
|
|
270
|
+
.tool{margin:10px 0;border:1px solid var(--hairline);border-radius:14px;overflow:hidden;background:var(--card-2)}
|
|
271
|
+
.tool .thead{display:flex;align-items:center;gap:10px;padding:11px 15px;cursor:pointer;user-select:none;transition:background .2s}
|
|
272
|
+
.tool .thead:hover{background:rgba(255,255,255,.04)}
|
|
273
|
+
.tool .ticon{width:26px;height:26px;border-radius:8px;display:flex;align-items:center;justify-content:center;
|
|
274
|
+
font-size:13px;background:rgba(94,92,230,.18);color:var(--cyan);flex:0 0 auto}
|
|
275
|
+
.tool .tname{font:600 13.5px var(--mono);color:var(--ink)}
|
|
276
|
+
.tool .targs{font:12px var(--mono);color:var(--dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1 1 auto}
|
|
277
|
+
.tool .tstat{font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;flex:0 0 auto;display:flex;align-items:center;gap:6px}
|
|
278
|
+
.tool .tstat .sdot{width:7px;height:7px;border-radius:50%}
|
|
279
|
+
.tool.running .tstat{color:var(--cyan)}
|
|
280
|
+
.tool.running .sdot{background:var(--cyan);box-shadow:0 0 8px var(--cyan);animation:blink 1.1s infinite}
|
|
281
|
+
.tool.ok .tstat{color:var(--green)}
|
|
282
|
+
.tool.ok .sdot{background:var(--green)}
|
|
283
|
+
.tool.fail .tstat{color:var(--red)}
|
|
284
|
+
.tool.fail .sdot{background:var(--red)}
|
|
285
|
+
.tool .caret{color:var(--dimmer);transition:transform .25s var(--ease);flex:0 0 auto}
|
|
286
|
+
.tool.open .caret{transform:rotate(90deg)}
|
|
287
|
+
.tool .tbody{display:none;border-top:1px solid var(--hairline-soft);padding:12px 15px}
|
|
288
|
+
.tool.open .tbody{display:block}
|
|
289
|
+
.tool .blk{margin-bottom:10px}
|
|
290
|
+
.tool .blk:last-child{margin-bottom:0}
|
|
291
|
+
.tool .blbl{font-size:10.5px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--dimmer);margin-bottom:5px}
|
|
292
|
+
.tool pre{background:var(--code-bg-soft);border-radius:9px;padding:10px 12px;overflow-x:auto;
|
|
293
|
+
font:12px/1.55 var(--mono);color:rgba(245,245,247,.7);white-space:pre-wrap;word-break:break-word;max-height:260px;overflow-y:auto}
|
|
294
|
+
/* approval dialog */
|
|
295
|
+
.approval{margin:12px 0;border:1px solid rgba(255,159,10,.45);border-radius:16px;overflow:hidden;
|
|
296
|
+
background:rgba(255,159,10,.06);animation:fade .4s var(--ease)}
|
|
297
|
+
.approval .ahead{display:flex;align-items:center;gap:10px;padding:13px 16px;border-bottom:1px solid rgba(255,159,10,.25)}
|
|
298
|
+
.approval .ahead .ai{width:28px;height:28px;border-radius:8px;display:flex;align-items:center;justify-content:center;
|
|
299
|
+
background:rgba(255,159,10,.2);color:var(--orange);font-size:15px}
|
|
300
|
+
.approval .ahead .at{font-weight:700;font-size:14px}
|
|
301
|
+
.approval .ahead .as{font:12px var(--mono);color:var(--orange);margin-left:auto}
|
|
302
|
+
.approval .abody{padding:13px 16px}
|
|
303
|
+
.approval pre{background:var(--code-bg);color:var(--code-ink);border-radius:10px;padding:12px 14px;overflow-x:auto;
|
|
304
|
+
font:12.5px/1.6 var(--mono);white-space:pre-wrap;word-break:break-word;max-height:280px;overflow-y:auto}
|
|
305
|
+
.approval .diffadd{color:var(--green)}
|
|
306
|
+
.approval .diffdel{color:var(--red)}
|
|
307
|
+
.approval .actions{display:flex;gap:10px;padding:0 16px 15px}
|
|
308
|
+
.approval .resolved{padding:11px 16px;font-size:13px;font-weight:600;display:flex;align-items:center;gap:8px}
|
|
309
|
+
.approval .resolved.allow{color:var(--green)}
|
|
310
|
+
.approval .resolved.deny{color:var(--red)}
|
|
311
|
+
/* Editable arguments (LM Studio's pattern): pre-filled
|
|
312
|
+
with the proposed args JSON; the diff (edit/write only) stays above as
|
|
313
|
+
a read-only visual aid so large content edits don't only exist as raw
|
|
314
|
+
JSON. Approve re-parses the textarea and ships the edited object. */
|
|
315
|
+
.approval .a-diff{margin-bottom:10px}
|
|
316
|
+
.approval textarea.a-args{width:100%;background:var(--code-bg);color:var(--code-ink);border:1px solid var(--hairline);
|
|
317
|
+
border-radius:10px;padding:12px 14px;font:12.5px/1.6 var(--mono);resize:vertical;min-height:64px;max-height:280px}
|
|
318
|
+
.approval textarea.a-args:focus{outline:none;border-color:var(--orange)}
|
|
319
|
+
.approval .a-argerr{margin-top:8px;font-size:12px;color:var(--red)}
|
|
320
|
+
.approval .a-always{display:flex;align-items:center;gap:9px;padding:0 16px 13px;font-size:12.5px;
|
|
321
|
+
color:var(--dim);cursor:pointer;font-weight:500}
|
|
322
|
+
.approval .a-always input{width:auto;margin:0;accent-color:var(--orange)}
|
|
323
|
+
/* ─── Chat-with-files RAG v1 Sources panel ('02d723a:docs/design/web-chat-redesign.md' Appendix A,
|
|
324
|
+
beat-matrix Axis 5) — deliberately close to the memchip's citation language above:
|
|
325
|
+
a collapsed "Sources · K" line under the reply that expands to
|
|
326
|
+
filename+snippet per retrieved chunk, plus [n] markers in the reply
|
|
327
|
+
text rendered as small round citation buttons that jump to their row. */
|
|
328
|
+
.sources{margin-top:11px;border:1px solid var(--hairline-soft);border-radius:12px;overflow:hidden;
|
|
329
|
+
background:rgba(255,255,255,.03)}
|
|
330
|
+
.sources summary{list-style:none;display:flex;align-items:center;gap:7px;padding:8px 13px;
|
|
331
|
+
cursor:pointer;user-select:none;font-size:12.5px;font-weight:600;color:var(--dim)}
|
|
332
|
+
.sources summary::-webkit-details-marker{display:none}
|
|
333
|
+
.sources summary::before{content:"›";display:inline-block;color:var(--dimmer);
|
|
334
|
+
transition:transform .2s var(--ease)}
|
|
335
|
+
.sources[open] summary::before{transform:rotate(90deg)}
|
|
336
|
+
.src-list{border-top:1px solid var(--hairline-soft);padding:9px 13px 11px;display:flex;flex-direction:column;gap:10px}
|
|
337
|
+
.src-row{border-radius:9px;padding:7px 9px;transition:background .5s var(--ease)}
|
|
338
|
+
.src-row.pulse{background:rgba(10,132,255,.16)}
|
|
339
|
+
.src-n{font:700 11px var(--mono);color:var(--blue);margin-right:7px}
|
|
340
|
+
.src-meta{display:inline-flex;align-items:baseline;gap:8px;flex-wrap:wrap}
|
|
341
|
+
.src-file{font-size:12.5px;font-weight:600;color:var(--ink)}
|
|
342
|
+
.src-range{font:11px var(--mono);color:var(--dimmer)}
|
|
343
|
+
.src-snippet{margin-top:4px;font-size:12px;line-height:1.5;color:var(--dim);white-space:pre-wrap;word-break:break-word}
|
|
344
|
+
/* [n] citation markers rendered inline in the assistant's reply text
|
|
345
|
+
(markdown.ts's linkifyCitations) — small round buttons, never full-size
|
|
346
|
+
links, so they read as footnote markers rather than ordinary hyperlinks. */
|
|
347
|
+
:is(.bubble) .cite-mark{display:inline-flex;align-items:center;justify-content:center;
|
|
348
|
+
min-width:16px;height:16px;padding:0 4px;margin:0 1px;border-radius:999px;border:none;
|
|
349
|
+
background:rgba(10,132,255,.18);color:var(--blue);font:700 10px var(--mono);
|
|
350
|
+
cursor:pointer;vertical-align:2px;line-height:1;transition:background .15s}
|
|
351
|
+
:is(.bubble) .cite-mark:hover{background:rgba(10,132,255,.3)}
|
|
352
|
+
@media (prefers-reduced-motion:reduce){.src-row{transition:none}.sources summary::before{transition:none}}
|
|
353
|
+
/* chat hello / starters */
|
|
354
|
+
#chat-hello{max-width:840px;margin:6vh auto 0;padding:0 22px;text-align:center}
|
|
355
|
+
#chat-hello h1{font-size:clamp(34px,5vw,54px);font-weight:700;letter-spacing:-.03em;line-height:1.08}
|
|
356
|
+
#chat-hello p{margin-top:13px;color:var(--dim);font-size:15.5px}
|
|
357
|
+
#chat-hello .chips{display:flex;flex-wrap:wrap;gap:10px;justify-content:center;margin-top:28px}
|
|
358
|
+
.chip{border:1px solid var(--hairline);background:var(--card);color:var(--dim);border-radius:999px;
|
|
359
|
+
padding:9px 16px;font-size:13.5px;cursor:pointer;transition:all .25s var(--ease)}
|
|
360
|
+
.chip:hover{color:var(--ink);border-color:var(--hairline-strong);background:var(--card-hover)}
|
|
361
|
+
/* chat composer */
|
|
362
|
+
#chat-foot{flex:0 0 auto;border-top:1px solid var(--hairline);padding:14px 22px 16px;
|
|
363
|
+
background:var(--card-2);backdrop-filter:blur(16px)}
|
|
364
|
+
.composer{max-width:840px;margin:0 auto}
|
|
365
|
+
.queuebar{display:none;max-width:840px;margin:0 auto 10px;gap:8px;flex-wrap:wrap;font-size:12px;color:var(--dim)}
|
|
366
|
+
.queuebar .qtag{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--hairline);
|
|
367
|
+
border-radius:999px;padding:4px 11px;background:var(--card)}
|
|
368
|
+
.queuebar .qtag b{color:var(--ink);font-weight:600}
|
|
369
|
+
.inputrow{display:flex;gap:10px;align-items:flex-end}
|
|
370
|
+
#chat-box-wrap{position:relative;flex:1;min-width:0}
|
|
371
|
+
#chat-box{width:100%;background:var(--card);border:1px solid var(--hairline);border-radius:16px;
|
|
372
|
+
color:var(--ink);font:15px/1.5 inherit;padding:13px 16px;resize:none;outline:none;
|
|
373
|
+
max-height:200px;min-height:50px;transition:border-color .25s;box-sizing:border-box}
|
|
374
|
+
#chat-box:focus{border-color:rgba(10,132,255,.5)}
|
|
375
|
+
/* Unified "#" retrieval mention picker — same
|
|
376
|
+
floating-panel chrome as .samplepop, anchored above the textarea it
|
|
377
|
+
types into instead of a pill. Zero layout shift: absolutely positioned,
|
|
378
|
+
hidden by default, never reflows the composer underneath it. */
|
|
379
|
+
.mentionpop{position:absolute;bottom:calc(100% + 8px);left:0;right:0;z-index:40;
|
|
380
|
+
max-height:min(46vh,320px);overflow-y:auto;
|
|
381
|
+
background:var(--popover-panel);border:1px solid var(--hairline);border-radius:13px;padding:6px;
|
|
382
|
+
backdrop-filter:blur(22px) saturate(1.3);-webkit-backdrop-filter:blur(22px) saturate(1.3);
|
|
383
|
+
box-shadow:0 18px 50px rgba(0,0,0,.35);display:none;
|
|
384
|
+
transform:translateY(4px);opacity:0;transition:opacity .14s var(--ease),transform .14s var(--ease)}
|
|
385
|
+
.mentionpop.open{display:block;transform:translateY(0);opacity:1}
|
|
386
|
+
.mention-sec-title{font-size:10.5px;font-weight:600;color:var(--dimmer);letter-spacing:.04em;
|
|
387
|
+
text-transform:uppercase;padding:7px 9px 4px}
|
|
388
|
+
.mention-row{display:flex;align-items:center;gap:8px;padding:7px 9px;border-radius:9px;cursor:pointer}
|
|
389
|
+
.mention-row:hover,.mention-row.active{background:var(--card-hover)}
|
|
390
|
+
.mention-ico{font-size:12px;color:var(--dimmer);flex:0 0 auto}
|
|
391
|
+
.mention-label{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
|
|
392
|
+
font-size:13px;color:var(--ink)}
|
|
393
|
+
.mention-tag{flex:0 0 auto;font-size:10px;color:var(--dimmer);text-transform:uppercase;letter-spacing:.03em}
|
|
394
|
+
.mention-empty{padding:10px 9px;font-size:12px;color:var(--dim)}
|
|
395
|
+
/* Pulse a file's attach-chip when it's selected from the mention picker
|
|
396
|
+
(the file is already attached — this is the only feedback needed). */
|
|
397
|
+
.attach-chip.pulse{animation:chippulse .5s var(--ease) 2}
|
|
398
|
+
@keyframes chippulse{0%,100%{box-shadow:0 0 0 rgba(10,132,255,0)}50%{box-shadow:0 0 0 3px rgba(10,132,255,.45)}}
|
|
399
|
+
@media (prefers-reduced-motion:reduce){.attach-chip.pulse{animation:none}}
|
|
400
|
+
#chat-send{background:linear-gradient(120deg,var(--blue),var(--purple));color:#fff;
|
|
401
|
+
border:none;border-radius:14px;height:50px;padding:0 22px;font:600 14px inherit;cursor:pointer;transition:filter .2s}
|
|
402
|
+
#chat-send:hover{filter:brightness(1.14)}
|
|
403
|
+
#chat-send:disabled{filter:grayscale(.8) brightness(.6);cursor:default}
|
|
404
|
+
#chat-stop{display:none;background:var(--card);border:1px solid var(--hairline);color:var(--ink);
|
|
405
|
+
border-radius:14px;height:50px;padding:0 20px;font:600 14px inherit;cursor:pointer}
|
|
406
|
+
#chat-stop:hover{background:var(--card-hover)}
|
|
407
|
+
/* Wrap + shrink rules (2026-07-06 jank fix): without flex-wrap/min-width:0
|
|
408
|
+
the right pill cluster overflowed past the viewport edge at tablet
|
|
409
|
+
widths ("no adapter" clipped). The status line ellipsizes first; the
|
|
410
|
+
pills wrap to a second row before anything leaves the screen. */
|
|
411
|
+
.chat-hint{max-width:840px;margin:8px auto 0;font-size:11.5px;color:var(--dimmer);display:flex;justify-content:space-between;gap:10px;flex-wrap:wrap}
|
|
412
|
+
.chat-hint #chat-status-line{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
413
|
+
.chat-hint .hint-right{display:flex;gap:12px;align-items:center;flex-wrap:wrap;justify-content:flex-end;min-width:0}
|
|
414
|
+
/* Native <select> in .pill clothes: reset the UA appearance and draw our
|
|
415
|
+
own chevron — the stock arrow collided with the label text ("no adaptɘr"
|
|
416
|
+
glyph pile-up), and long adapter ids now ellipsize instead of overflow. */
|
|
417
|
+
#chat-adapter{appearance:none;-webkit-appearance:none;max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
|
|
418
|
+
padding-right:27px;cursor:pointer;
|
|
419
|
+
background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='5' viewBox='0 0 8 5'%3E%3Cpath d='M1 1l3 3 3-3' fill='none' stroke='%23909098' stroke-width='1.4' stroke-linecap='round'/%3E%3C/svg%3E");
|
|
420
|
+
background-repeat:no-repeat;background-position:right 11px center}
|
|
421
|
+
/* perf strip — always-visible, quiet: replaces the old bare #chat-tps text
|
|
422
|
+
with tok/s + TTFT + context-fill + (server-driven only) a lane badge. */
|
|
423
|
+
#chat-perf{max-width:840px;margin:8px auto 0;display:flex;align-items:center;gap:14px;
|
|
424
|
+
font:11px/1 var(--mono);letter-spacing:.01em;color:var(--dimmer);flex-wrap:wrap}
|
|
425
|
+
#chat-perf .pf-item{white-space:nowrap}
|
|
426
|
+
#chat-perf .pf-item b{color:var(--ink);font-weight:600}
|
|
427
|
+
/* adapter chip — upgraded presentation over the bare <select>: current
|
|
428
|
+
selection + specialty line, options grayed by /v1/adapters/available's
|
|
429
|
+
compatible flag rather than hidden outright. */
|
|
430
|
+
#chat-adapter option:disabled{color:var(--dimmer);font-style:italic}
|
|
431
|
+
/* "manage adapters" gear beside the quick-switch <select> — opens the
|
|
432
|
+
routing table (#adapters-overlay), same pill sizing as #chat-think. */
|
|
433
|
+
#adapters-manage{appearance:none;-webkit-appearance:none;cursor:pointer;display:inline-flex;align-items:center;
|
|
434
|
+
justify-content:center;width:27px;height:27px;border-radius:999px;background:var(--card);
|
|
435
|
+
border:1px solid var(--hairline);color:var(--dim);font-size:12.5px;line-height:1;flex:0 0 auto;
|
|
436
|
+
transition:color .18s var(--ease),border-color .18s var(--ease),background .18s var(--ease)}
|
|
437
|
+
#adapters-manage:hover{color:var(--ink);background:var(--card-hover);border-color:var(--hairline-strong)}
|
|
438
|
+
/* attach (+) button + attachment chips */
|
|
439
|
+
#chat-attach-btn{flex:0 0 auto;width:50px;height:50px;border-radius:14px;background:var(--card);
|
|
440
|
+
border:1px solid var(--hairline);color:var(--ink);font-size:23px;line-height:1;cursor:pointer;transition:background .15s,border-color .15s}
|
|
441
|
+
#chat-attach-btn:hover{background:var(--card-hover);border-color:rgba(10,132,255,.5)}
|
|
442
|
+
/* hold-to-talk mic (voice.ts) */
|
|
443
|
+
#chat-mic-btn{flex:0 0 auto;width:50px;height:50px;border-radius:14px;background:var(--card);
|
|
444
|
+
border:1px solid var(--hairline);color:var(--ink);font-size:20px;line-height:1;cursor:pointer;user-select:none;-webkit-user-select:none;transition:background .15s,border-color .15s}
|
|
445
|
+
#chat-mic-btn:hover{background:var(--card-hover);border-color:rgba(10,132,255,.5)}
|
|
446
|
+
#chat-mic-btn.recording{background:rgba(255,69,58,.18);border-color:rgba(255,69,58,.7);animation:micpulse 1s ease-in-out infinite}
|
|
447
|
+
#chat-mic-btn.busy{opacity:.6;cursor:progress}
|
|
448
|
+
@keyframes micpulse{0%,100%{box-shadow:0 0 0 0 rgba(255,69,58,.35)}50%{box-shadow:0 0 0 8px rgba(255,69,58,0)}}
|
|
449
|
+
.attach-row{display:none;flex-wrap:wrap;gap:8px;max-width:840px;width:100%;margin:0 auto 8px}
|
|
450
|
+
.attach-chip{display:flex;align-items:center;gap:7px;background:var(--card);border:1px solid var(--hairline);
|
|
451
|
+
border-radius:10px;padding:5px 9px;font-size:12px;max-width:240px}
|
|
452
|
+
.attach-chip img{width:34px;height:34px;object-fit:cover;border-radius:6px;display:block}
|
|
453
|
+
.attach-chip .att-ico{font-size:16px}
|
|
454
|
+
.attach-chip .att-name{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--ink)}
|
|
455
|
+
.attach-chip .att-x{border:none;background:transparent;color:var(--dim);cursor:pointer;font-size:12px;line-height:1;padding:0 2px}
|
|
456
|
+
.attach-chip .att-x:hover{color:var(--red)}
|
|
457
|
+
/* Chat-with-files RAG v1 ('02d723a:docs/design/web-chat-redesign.md' Appendix A,
|
|
458
|
+
beat-matrix Axis 5): shown on
|
|
459
|
+
text-file chips once the attached set is large enough that only the
|
|
460
|
+
most relevant chunks get sent per turn instead of the whole file
|
|
461
|
+
(composer.ts's shouldRetrieve threshold) — transparency principle, the
|
|
462
|
+
mode switch must be visible on the chip, not just in prompt content
|
|
463
|
+
the user never sees. */
|
|
464
|
+
.attach-chip .att-tag{flex:0 0 auto;font-size:9.5px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;
|
|
465
|
+
color:var(--purple);background:rgba(191,90,242,.14);border-radius:999px;padding:2px 7px}
|
|
466
|
+
.composer.dragover{outline:2px dashed var(--blue);outline-offset:6px;border-radius:14px}
|
|
467
|
+
.msg-atts{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:8px}
|
|
468
|
+
.msg-att-img{max-width:220px;max-height:220px;border-radius:10px;display:block}
|
|
469
|
+
.msg-att-file{display:inline-flex;align-items:center;gap:5px;font-size:12px;background:rgba(255,255,255,.08);border-radius:8px;padding:5px 10px}
|
|
470
|
+
.msg.user .msg-text{white-space:pre-wrap;word-break:break-word}
|
|
471
|
+
/* ── rendered markdown (the same rules the host page applies to the memory panel articles) ── */
|
|
472
|
+
/* markdown-ish rendering — shared by chat bubbles AND the memory panel's
|
|
473
|
+
article view (.mem-article-render renders vault/Reference docs through
|
|
474
|
+
the same mdToHtml; without this scope the panel's code fences lost
|
|
475
|
+
their header chrome — "shCopy" mash, 2026-07-07 visual QA). */
|
|
476
|
+
.bubble p{margin:0 0 10px}
|
|
477
|
+
.bubble p:last-child{margin-bottom:0}
|
|
478
|
+
.bubble ul,.bubble ol{margin:0 0 10px;padding-left:22px}
|
|
479
|
+
.bubble li{margin:3px 0}
|
|
480
|
+
.bubble strong{font-weight:700;color:var(--ink)}
|
|
481
|
+
.bubble em{font-style:italic}
|
|
482
|
+
.bubble code{font-family:var(--mono);font-size:.86em;background:rgba(127,127,127,.18);padding:2px 7px;border-radius:6px}
|
|
483
|
+
.bubble pre{background:var(--code-bg);color:var(--code-ink);border:1px solid var(--hairline-soft);border-radius:12px;
|
|
484
|
+
padding:13px 15px;overflow-x:auto;margin:10px 0;font:12.5px/1.6 var(--mono)}
|
|
485
|
+
.bubble pre code{background:none;padding:0;font-size:inherit;color:inherit}
|
|
486
|
+
.bubble h1,.bubble h2,.bubble h3,.bubble h4,.bubble h5,.bubble h6{font-weight:700;margin:14px 0 7px;letter-spacing:-.01em;line-height:1.3}
|
|
487
|
+
.bubble h1{font-size:1.28em}
|
|
488
|
+
.bubble h2{font-size:1.16em}
|
|
489
|
+
.bubble h3{font-size:1.06em}
|
|
490
|
+
.bubble h4,.bubble h5,.bubble h6{font-size:1em;color:var(--dim)}
|
|
491
|
+
.bubble h1:first-child,.bubble h2:first-child,.bubble h3:first-child{margin-top:0}
|
|
492
|
+
.bubble a{color:var(--cyan);text-decoration:none;border-bottom:1px solid rgba(100,210,255,.34)}
|
|
493
|
+
.bubble a:hover{border-bottom-color:var(--cyan)}
|
|
494
|
+
.bubble del{opacity:.55}
|
|
495
|
+
.bubble blockquote{margin:9px 0;padding:3px 14px;border-left:3px solid var(--hairline);color:var(--dim)}
|
|
496
|
+
.bubble blockquote p:last-child{margin-bottom:0}
|
|
497
|
+
.bubble hr{border:none;border-top:1px solid var(--hairline);margin:15px 0}
|
|
498
|
+
.bubble li.task{list-style:none;margin-left:-18px}
|
|
499
|
+
.bubble li.task input{margin-right:7px;accent-color:var(--blue);vertical-align:-1px}
|
|
500
|
+
.bubble .md-tablewrap{overflow-x:auto;margin:11px 0}
|
|
501
|
+
.bubble table.md-table{width:100%;border-collapse:collapse;font-size:.92em}
|
|
502
|
+
.bubble table.md-table th,.bubble table.md-table td{border:1px solid var(--hairline-soft);padding:6px 11px;text-align:left}
|
|
503
|
+
.bubble table.md-table th{background:rgba(255,255,255,.05);font-weight:600;color:var(--ink)}
|
|
504
|
+
.bubble .codeblock{margin:11px 0;border:1px solid var(--hairline-soft);border-radius:12px;overflow:hidden;background:var(--code-bg);color:var(--code-ink)}
|
|
505
|
+
.bubble .cbhead{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:5px 9px 5px 13px;background:rgba(255,255,255,.04);border-bottom:1px solid var(--hairline-soft)}
|
|
506
|
+
.bubble .cblang{font:11px/1 var(--mono);color:rgba(245,245,247,.65);letter-spacing:.02em}
|
|
507
|
+
.bubble .cbcopy{font:11px/1 var(--mono);color:rgba(245,245,247,.65);background:transparent;border:1px solid rgba(255,255,255,.2);border-radius:6px;padding:4px 10px;cursor:pointer;transition:color .15s,border-color .15s}
|
|
508
|
+
.bubble .cbcopy:hover{color:var(--code-ink);border-color:rgba(255,255,255,.4)}
|
|
509
|
+
.bubble .codeblock pre{margin:0;border:none;border-radius:0;background:transparent;color:inherit}
|
|
510
|
+
/* Canvas v1 ('02d723a:docs/design/web-chat-redesign.md' Appendix A,
|
|
511
|
+
beat-matrix Axis 2): Preview|Source toggle
|
|
512
|
+
for html/svg fences, sharing .cbhead with the existing lang tag + Copy
|
|
513
|
+
button. .cbhead keeps lang pinned left; the toggle+copy group sits
|
|
514
|
+
right via margin-left:auto on the first right-side element instead of
|
|
515
|
+
space-between, since space-between with 3 children would push the
|
|
516
|
+
toggle to the dead center of the bar. */
|
|
517
|
+
.bubble .cbhead{justify-content:flex-start}
|
|
518
|
+
.bubble .cbtoggle{display:inline-flex;gap:2px;margin-left:auto;padding:2px;border-radius:7px;background:rgba(255,255,255,.05)}
|
|
519
|
+
.bubble .cbview{font:11px/1 var(--mono);color:rgba(245,245,247,.55);background:transparent;border:none;
|
|
520
|
+
border-radius:5px;padding:4px 9px;cursor:pointer;transition:color .15s,background .15s}
|
|
521
|
+
.bubble .cbview:hover{color:var(--code-ink)}
|
|
522
|
+
.bubble .cbview.active{color:var(--code-ink);background:rgba(255,255,255,.14)}
|
|
523
|
+
/* has-canvas keeps Copy visually last, right after the toggle group. */
|
|
524
|
+
.bubble .codeblock.has-canvas .cbcopy{margin-left:2px}
|
|
525
|
+
.bubble .cbcanvas{position:relative;background:#fff}
|
|
526
|
+
.bubble .cbframe{display:block;width:100%;height:420px;min-height:160px;max-height:80vh;
|
|
527
|
+
border:none;resize:vertical;overflow:auto;background:#fff}
|
|
528
|
+
/* No layout shift on toggle: pre and cbcanvas are simply swapped via
|
|
529
|
+
[hidden] — the codeblock's own border/rounding stays put, and the
|
|
530
|
+
iframe's own default height (420px) plus resize:vertical means the
|
|
531
|
+
block doesn't jump when Preview first opens vs. later re-opens. */
|
|
532
|
+
`;
|