feat(quick-pane): promote chats into full sessions

This commit is contained in:
2026-08-28 13:43:49 +02:00
parent b44b1be4b7
commit 0eb163b6c4
11 changed files with 149 additions and 95 deletions
+1 -1
View File
@@ -40,7 +40,7 @@ The app creates or focuses a dedicated Quick Pane window. `Esc` and **Close** hi
`Ctrl+N` starts a new chat: it aborts active work when needed, closes the old runtime with retryable cleanup, clears draft/transcript and submitted-prompt mappings, then prepares a fresh runtime and model catalog. Automatic stale renewal performs the same idle-chat replacement while preserving the current draft. `Ctrl+N` starts a new chat: it aborts active work when needed, closes the old runtime with retryable cleanup, clears draft/transcript and submitted-prompt mappings, then prepares a fresh runtime and model catalog. Automatic stale renewal performs the same idle-chat replacement while preserving the current draft.
Quick Pane opens at 640×240 with a 520×240 minimum. Frontend content measurement requests native growth up to 520px tall after transcript updates; beyond that cap, transcript and model results scroll inside pane while outer document and pane remain clipped. Quick Pane opens at 640×360 with a 520×360 minimum. Frontend content measurement requests native growth up to 520px tall after transcript updates; beyond that cap, transcript and model results scroll inside pane while outer document and pane remain clipped.
## Escalation ## Escalation
+2 -2
View File
@@ -26,7 +26,7 @@
- `Esc` and **Close** hide without cleanup. Reopening an idle chat at or under three minutes preserves runtime, draft/catalog, and transcript; reopening after more than three minutes renews it. - `Esc` and **Close** hide without cleanup. Reopening an idle chat at or under three minutes preserves runtime, draft/catalog, and transcript; reopening after more than three minutes renews it.
- `Ctrl+N` prevents browser default, aborts active work when needed, retries queued cleanup, clears draft/transcript/raw-prompt mappings, and prepares a fresh runtime/catalog. Idle reset does not send an abort. - `Ctrl+N` prevents browser default, aborts active work when needed, retries queued cleanup, clears draft/transcript/raw-prompt mappings, and prepares a fresh runtime/catalog. Idle reset does not send an abort.
- Stale runtimes created by superseded preparation are queued after cleanup failure and retried before later stale-chat renewal. - Stale runtimes created by superseded preparation are queued after cleanup failure and retried before later stale-chat renewal.
- Quick Pane opens at 640×240 with a 520×240 native minimum. Content-driven native growth stops at 520px tall; transcript and model results scroll internally after that. - Quick Pane opens at 640×360 with a 520×360 native minimum. Content-driven native growth stops at 520px tall; transcript and model results scroll internally after that.
- Settings-startup and model-catalog errors show **Retry**. Catalog retry cleans up failed runtime before replacement preparation; model selection and Send stay disabled until ready. - Settings-startup and model-catalog errors show **Retry**. Catalog retry cleans up failed runtime before replacement preparation; model selection and Send stay disabled until ready.
- **Continue in full session** appears after first completed assistant answer with valid handoff settings, plus preserved request/runtime escalation notices. It never starts automatically. Handoff creates normal session with transcript-derived current-chat context, workspace, and follow-up model. Current answer extraction starts only after exact mapped submitted prompt in snapshot transcript; failed follow-ups with no current assistant message never reuse prior answer as partial findings. - **Continue in full session** appears after first completed assistant answer with valid handoff settings, plus preserved request/runtime escalation notices. It never starts automatically. Handoff creates normal session with transcript-derived current-chat context, workspace, and follow-up model. Current answer extraction starts only after exact mapped submitted prompt in snapshot transcript; failed follow-ups with no current assistant message never reuse prior answer as partial findings.
@@ -41,7 +41,7 @@
7. Continue; verify normal session receives handoff and follow-up model. 7. Continue; verify normal session receives handoff and follow-up model.
8. Close Quick Pane and reopen within three minutes; verify idle runtime, draft/catalog, and full transcript remain. Repeat after more than three idle minutes; verify fresh chat/runtime appears. 8. Close Quick Pane and reopen within three minutes; verify idle runtime, draft/catalog, and full transcript remain. Repeat after more than three idle minutes; verify fresh chat/runtime appears.
9. Press `Ctrl+N` while idle and active; verify fresh empty chat in both cases and active work aborts before replacement. 9. Press `Ctrl+N` while idle and active; verify fresh empty chat in both cases and active work aborts before replacement.
10. Verify pane opens at 640×240, grows only to 520px tall as transcript grows, then keeps transcript/model results scrolling internally. 10. Verify pane opens at 640×360, grows only to 520px tall as transcript grows, then keeps transcript/model results scrolling internally.
11. Induce settings-startup or model-catalog failure; verify visible **Retry**, disabled model/Send controls until recovery, and successful retry. Verify empty catalog stays blocked without unsupported actions. 11. Induce settings-startup or model-catalog failure; verify visible **Retry**, disabled model/Send controls until recovery, and successful retry. Verify empty catalog stays blocked without unsupported actions.
## Exit criteria ## Exit criteria
+18 -4
View File
@@ -242,9 +242,9 @@ test("Quick Pane uses native dimensions and request-first compact layout", async
}, },
{ {
width: 640, width: 640,
height: 240, height: 360,
minWidth: 520, minWidth: 520,
minHeight: 240, minHeight: 360,
transparent: true, transparent: true,
}, },
); );
@@ -257,7 +257,7 @@ test("Quick Pane uses native dimensions and request-first compact layout", async
); );
assert.match(nativeSource, /get_or_create_quick_pane/); assert.match(nativeSource, /get_or_create_quick_pane/);
assert.match(nativeSource, /fn resize_quick_pane/); assert.match(nativeSource, /fn resize_quick_pane/);
assert.match(nativeSource, /height\.clamp\(240\.0, 520\.0\)/); assert.match(nativeSource, /height\.clamp\(360\.0, 520\.0\)/);
assert.match(source, /new ResizeObserver\(resizeToContent\)/); assert.match(source, /new ResizeObserver\(resizeToContent\)/);
assert.match(source, /invoke\("resize_quick_pane", \{ height \}\)/); assert.match(source, /invoke\("resize_quick_pane", \{ height \}\)/);
assert.doesNotMatch(source, /onCloseRequested/); assert.doesNotMatch(source, /onCloseRequested/);
@@ -267,7 +267,10 @@ test("Quick Pane uses native dimensions and request-first compact layout", async
assert.match(source, /className=\{`quick-pane-exchange/); assert.match(source, /className=\{`quick-pane-exchange/);
assert.match(source, /onOpen=\{handleModelMenuOpen\}/); assert.match(source, /onOpen=\{handleModelMenuOpen\}/);
assert.match(source, /onClose=\{handleModelMenuClose\}/); assert.match(source, /onClose=\{handleModelMenuClose\}/);
assert.match(source, /requestPaneResize\(520\)/); assert.match(source, /requestPaneResize\(QUICK_PANE_MAX_HEIGHT\)/);
assert.match(source, /const QUICK_PANE_MIN_HEIGHT = 360/);
assert.match(source, /const QUICK_PANE_MAX_HEIGHT = 520/);
assert.match(source, /height = clampQuickPaneHeight\(height\)/);
assert.match(source, /if \(modelMenuOpenRef\.current\) return;/); assert.match(source, /if \(modelMenuOpenRef\.current\) return;/);
assert.match(source, /submittedPromptTurnsRef/); assert.match(source, /submittedPromptTurnsRef/);
assert.match(source, /function elapsedLabel/); assert.match(source, /function elapsedLabel/);
@@ -324,6 +327,17 @@ test("Quick Pane uses native dimensions and request-first compact layout", async
stylesheet, stylesheet,
/\.quick-pane-header-actions\s*\{[^}]*overflow:\s*(?:hidden|clip)/, /\.quick-pane-header-actions\s*\{[^}]*overflow:\s*(?:hidden|clip)/,
); );
assert.match(
stylesheet,
/\.quick-pane-toolbar-control\s*\{[\s\S]*?height:\s*27px[\s\S]*?padding:\s*0 8px[\s\S]*?border:[\s\S]*?border-radius:[\s\S]*?background:[\s\S]*?font:/,
);
assert.match(stylesheet, /\.quick-pane-toolbar-control:hover:not\(:disabled\)/);
assert.match(stylesheet, /\.quick-pane-toolbar-control:focus-visible/);
assert.match(stylesheet, /\.quick-pane-toolbar-control:disabled/);
assert.match(source, /triggerClassName="quick-pane-toolbar-control"/);
assert.match(source, /controlClassName="quick-pane-toolbar-control"/);
assert.match(source, /className="quick-pane-toolbar-control quick-pane-action"/);
assert.match(source, /className="quick-pane-toolbar-control quick-pane-close"/);
assert.match(stylesheet, /#f0a347/); assert.match(stylesheet, /#f0a347/);
assert.doesNotMatch(stylesheet, /#8aa9d6|#46494f|#23252a/); assert.doesNotMatch(stylesheet, /#8aa9d6|#46494f|#23252a/);
+4 -4
View File
@@ -334,7 +334,7 @@ fn quick_pane_height(height: f64) -> Result<f64, String> {
if !height.is_finite() { if !height.is_finite() {
return Err("Quick Pane height must be finite".to_owned()); return Err("Quick Pane height must be finite".to_owned());
} }
Ok(height.clamp(240.0, 520.0)) Ok(height.clamp(360.0, 520.0))
} }
#[tauri::command] #[tauri::command]
@@ -891,9 +891,9 @@ mod tests {
.unwrap(); .unwrap();
assert_eq!(quick_pane.width, 640.0); assert_eq!(quick_pane.width, 640.0);
assert_eq!(quick_pane.height, 240.0); assert_eq!(quick_pane.height, 360.0);
assert_eq!(quick_pane.min_width, Some(520.0)); assert_eq!(quick_pane.min_width, Some(520.0));
assert_eq!(quick_pane.min_height, Some(240.0)); assert_eq!(quick_pane.min_height, Some(360.0));
assert_eq!(quick_pane.url.to_string(), "index.html"); assert_eq!(quick_pane.url.to_string(), "index.html");
assert!(!quick_pane.visible); assert!(!quick_pane.visible);
assert!(quick_pane.transparent); assert!(quick_pane.transparent);
@@ -903,7 +903,7 @@ mod tests {
#[test] #[test]
fn quick_pane_height_stays_within_content_limit() { fn quick_pane_height_stays_within_content_limit() {
assert_eq!(quick_pane_height(200.0), Ok(240.0)); assert_eq!(quick_pane_height(200.0), Ok(360.0));
assert_eq!(quick_pane_height(420.0), Ok(420.0)); assert_eq!(quick_pane_height(420.0), Ok(420.0));
assert_eq!(quick_pane_height(900.0), Ok(520.0)); assert_eq!(quick_pane_height(900.0), Ok(520.0));
} }
+2 -2
View File
@@ -29,9 +29,9 @@
"label": "quick-pane", "label": "quick-pane",
"title": "Pi Quick Pane", "title": "Pi Quick Pane",
"width": 640, "width": 640,
"height": 240, "height": 360,
"minWidth": 520, "minWidth": 520,
"minHeight": 240, "minHeight": 360,
"center": true, "center": true,
"visible": false, "visible": false,
"decorations": false, "decorations": false,
+3 -1
View File
@@ -92,6 +92,7 @@ export function ModelSelector({
label = "Model", label = "Model",
placement = "up", placement = "up",
className = "", className = "",
triggerClassName = "",
onOpen, onOpen,
onClose, onClose,
}: { }: {
@@ -103,6 +104,7 @@ export function ModelSelector({
label?: string; label?: string;
placement?: "up" | "down"; placement?: "up" | "down";
className?: string; className?: string;
triggerClassName?: string;
onOpen?: () => void; onOpen?: () => void;
onClose?: () => void; onClose?: () => void;
}) { }) {
@@ -209,7 +211,7 @@ export function ModelSelector({
<button <button
ref={triggerRef} ref={triggerRef}
type="button" type="button"
className="model-trigger" className={["model-trigger", triggerClassName].filter(Boolean).join(" ")}
aria-expanded={open} aria-expanded={open}
aria-haspopup="dialog" aria-haspopup="dialog"
aria-controls={open ? menuId : undefined} aria-controls={open ? menuId : undefined}
+3
View File
@@ -7,6 +7,7 @@ type ThinkingSelectorProps = {
value: string; value: string;
disabled?: boolean; disabled?: boolean;
className?: string; className?: string;
controlClassName?: string;
onChange: (level: string) => void; onChange: (level: string) => void;
}; };
@@ -15,6 +16,7 @@ export function ThinkingSelector({
value, value,
disabled = false, disabled = false,
className, className,
controlClassName,
onChange, onChange,
}: ThinkingSelectorProps) { }: ThinkingSelectorProps) {
const selected = reconcileThinkingLevel(model, value); const selected = reconcileThinkingLevel(model, value);
@@ -24,6 +26,7 @@ export function ThinkingSelector({
<label className={classes}> <label className={classes}>
<span>Thinking</span> <span>Thinking</span>
<select <select
className={controlClassName}
aria-label="Thinking" aria-label="Thinking"
value={selected} value={selected}
disabled={disabled} disabled={disabled}
+30 -4
View File
@@ -239,6 +239,21 @@ describe("QuickPane", () => {
]); ]);
}); });
it("applies shared toolbar-control classes to compact controls", async () => {
bridgeMock([defaultModels]);
render(<QuickPane />);
expect(await preparedTrigger()).toHaveClass("quick-pane-toolbar-control");
expect(screen.getByRole("combobox", { name: "Thinking" })).toHaveClass(
"quick-pane-toolbar-control",
);
expect(screen.getByRole("button", { name: "Continue" })).toHaveClass(
"quick-pane-toolbar-control",
);
expect(
screen.getByRole("button", { name: "Close Quick Pane" }),
).toHaveClass("quick-pane-toolbar-control");
});
it("starts native dragging only from the strip above toolbar controls", async () => { it("starts native dragging only from the strip above toolbar controls", async () => {
bridgeMock([defaultModels]); bridgeMock([defaultModels]);
const { container } = render(<QuickPane />); const { container } = render(<QuickPane />);
@@ -390,7 +405,12 @@ describe("QuickPane", () => {
id: "quick/model", id: "quick/model",
name: "Quick Model", name: "Quick Model",
reasoning: true, reasoning: true,
thinkingLevelMap: { minimal: null, low: null, medium: null, high: "high" }, thinkingLevelMap: {
minimal: null,
low: null,
medium: null,
high: "high",
},
}, },
]; ];
bridgeMock([thinkingModels], () => bridgeMock([thinkingModels], () =>
@@ -424,7 +444,10 @@ describe("QuickPane", () => {
expect(screen.queryByRole("button", { name: "Abort" })).toBeNull(); expect(screen.queryByRole("button", { name: "Abort" })).toBeNull();
expect(screen.queryByRole("button", { name: "Renew" })).toBeNull(); expect(screen.queryByRole("button", { name: "Renew" })).toBeNull();
await ask("Inspect"); await ask("Inspect");
expect(await screen.findByRole("button", { name: "Abort" })).toBeEnabled(); expect(await screen.findByRole("button", { name: "Abort" })).toHaveClass(
"quick-pane-toolbar-control",
);
expect(screen.getByRole("button", { name: "Abort" })).toBeEnabled();
await userEvent.click(screen.getByRole("button", { name: "Abort" })); await userEvent.click(screen.getByRole("button", { name: "Abort" }));
await waitFor(() => expect(tracker.runtimeCount()).toBe(2)); await waitFor(() => expect(tracker.runtimeCount()).toBe(2));
expect(mocks.invoke).toHaveBeenCalledWith("abort", { agentId: "agent-1" }); expect(mocks.invoke).toHaveBeenCalledWith("abort", { agentId: "agent-1" });
@@ -436,7 +459,10 @@ describe("QuickPane", () => {
streaming = false; streaming = false;
await ask("Again"); await ask("Again");
expect(await screen.findByRole("button", { name: "Renew" })).toBeEnabled(); expect(await screen.findByRole("button", { name: "Renew" })).toHaveClass(
"quick-pane-toolbar-control",
);
expect(screen.getByRole("button", { name: "Renew" })).toBeEnabled();
}); });
it("reports Abort failures without replacing active chat", async () => { it("reports Abort failures without replacing active chat", async () => {
@@ -510,7 +536,7 @@ describe("QuickPane", () => {
expect(resizeCalls.length).toBeGreaterThan(resizeCallCountBeforeClose); expect(resizeCalls.length).toBeGreaterThan(resizeCallCountBeforeClose);
expect(resizeCalls[resizeCalls.length - 1]).toEqual([ expect(resizeCalls[resizeCalls.length - 1]).toEqual([
"resize_quick_pane", "resize_quick_pane",
{ height: 240 }, { height: 360 },
]); ]);
}); });
+17 -15
View File
@@ -71,6 +71,12 @@ type TextSelection = {
}; };
const CHAT_RENEWAL_MS = 180_000; const CHAT_RENEWAL_MS = 180_000;
const QUICK_PANE_MIN_HEIGHT = 360;
const QUICK_PANE_MAX_HEIGHT = 520;
function clampQuickPaneHeight(height: number) {
return Math.min(QUICK_PANE_MAX_HEIGHT, Math.max(QUICK_PANE_MIN_HEIGHT, height));
}
function messageText(message: Message) { function messageText(message: Message) {
if (typeof message.content === "string") return message.content; if (typeof message.content === "string") return message.content;
@@ -391,6 +397,7 @@ export default function QuickPane() {
if (intent !== "pointer") scheduleInputSelectionRestore(); if (intent !== "pointer") scheduleInputSelectionRestore();
}; };
const requestPaneResize = useCallback((height: number) => { const requestPaneResize = useCallback((height: number) => {
height = clampQuickPaneHeight(height);
if (requestedPaneHeightRef.current !== height) if (requestedPaneHeightRef.current !== height)
resizeRetryCountRef.current = 0; resizeRetryCountRef.current = 0;
requestedPaneHeightRef.current = height; requestedPaneHeightRef.current = height;
@@ -420,7 +427,7 @@ export default function QuickPane() {
} }
if ( if (
!mountedRef.current || !mountedRef.current ||
(modelMenuOpenRef.current && height !== 520) || (modelMenuOpenRef.current && height !== QUICK_PANE_MAX_HEIGHT) ||
resizeRetryCountRef.current >= 1 resizeRetryCountRef.current >= 1
) )
return; return;
@@ -430,7 +437,7 @@ export default function QuickPane() {
if ( if (
mountedRef.current && mountedRef.current &&
requestedPaneHeightRef.current === height && requestedPaneHeightRef.current === height &&
(!modelMenuOpenRef.current || height === 520) (!modelMenuOpenRef.current || height === QUICK_PANE_MAX_HEIGHT)
) )
requestPaneResizeRef.current?.(height); requestPaneResizeRef.current?.(height);
}, 250); }, 250);
@@ -441,7 +448,7 @@ export default function QuickPane() {
const handleModelMenuOpen = useCallback(() => { const handleModelMenuOpen = useCallback(() => {
modelMenuOpenRef.current = true; modelMenuOpenRef.current = true;
setModelMenuOpen(true); setModelMenuOpen(true);
requestPaneResize(520); requestPaneResize(QUICK_PANE_MAX_HEIGHT);
}, [requestPaneResize]); }, [requestPaneResize]);
const handleModelMenuClose = useCallback(() => { const handleModelMenuClose = useCallback(() => {
modelMenuOpenRef.current = false; modelMenuOpenRef.current = false;
@@ -750,16 +757,9 @@ export default function QuickPane() {
(Number.parseFloat(styles.rowGap || styles.gap || "0") || 0) + (Number.parseFloat(styles.rowGap || styles.gap || "0") || 0) +
(Number.parseFloat(styles.paddingTop) || 0) + (Number.parseFloat(styles.paddingTop) || 0) +
(Number.parseFloat(styles.paddingBottom) || 0); (Number.parseFloat(styles.paddingBottom) || 0);
const height = Math.min( requestPaneResize(
520,
Math.max(
240,
Math.ceil(
pane.clientHeight - exchange.clientHeight + contentHeight, pane.clientHeight - exchange.clientHeight + contentHeight,
),
),
); );
requestPaneResize(height);
}); });
}; };
if (typeof ResizeObserver === "undefined") { if (typeof ResizeObserver === "undefined") {
@@ -1137,6 +1137,7 @@ export default function QuickPane() {
<div className="quick-pane-primary-controls"> <div className="quick-pane-primary-controls">
<ModelSelector <ModelSelector
className="quick-pane-model-selector" className="quick-pane-model-selector"
triggerClassName="quick-pane-toolbar-control"
placement="down" placement="down"
models={models} models={models}
current={{ current={{
@@ -1161,6 +1162,7 @@ export default function QuickPane() {
/> />
<ThinkingSelector <ThinkingSelector
className="quick-pane-thinking" className="quick-pane-thinking"
controlClassName="quick-pane-toolbar-control"
model={selectedCatalogModel} model={selectedCatalogModel}
value={thinkingLevel} value={thinkingLevel}
disabled={catalogOverlayActive || runActive || chatReplacing} disabled={catalogOverlayActive || runActive || chatReplacing}
@@ -1170,7 +1172,7 @@ export default function QuickPane() {
<div className="quick-pane-header-actions"> <div className="quick-pane-header-actions">
{runtime && ( {runtime && (
<button <button
className="quick-pane-action" className="quick-pane-toolbar-control quick-pane-action"
type="button" type="button"
disabled={catalogOverlayActive || promoting} disabled={catalogOverlayActive || promoting}
onClick={continueFull} onClick={continueFull}
@@ -1179,7 +1181,7 @@ export default function QuickPane() {
</button> </button>
)} )}
<button <button
className="quick-pane-close" className="quick-pane-toolbar-control quick-pane-close"
type="button" type="button"
aria-label="Close Quick Pane" aria-label="Close Quick Pane"
title="Close Quick Pane (Esc)" title="Close Quick Pane (Esc)"
@@ -1236,7 +1238,7 @@ export default function QuickPane() {
> >
{requestActive && ( {requestActive && (
<button <button
className="quick-pane-action" className="quick-pane-toolbar-control quick-pane-action"
type="button" type="button"
disabled={submissionStarting || chatReplacing} disabled={submissionStarting || chatReplacing}
onClick={() => void replaceChat(false, true)} onClick={() => void replaceChat(false, true)}
@@ -1246,7 +1248,7 @@ export default function QuickPane() {
)} )}
{!runActive && hasExchange && ( {!runActive && hasExchange && (
<button <button
className="quick-pane-action" className="quick-pane-toolbar-control quick-pane-action"
type="button" type="button"
disabled={submissionStarting || chatReplacing} disabled={submissionStarting || chatReplacing}
onClick={() => void replaceChat(false, false)} onClick={() => void replaceChat(false, false)}
+41 -34
View File
@@ -87,11 +87,11 @@ body {
} }
.quick-pane-primary-controls { .quick-pane-primary-controls {
flex: 1 1 auto; flex: 1 1 0;
} }
.quick-pane-header-actions { .quick-pane-header-actions {
flex: 0 1 auto; flex: 0 0 auto;
justify-content: end; justify-content: end;
} }
@@ -105,8 +105,7 @@ body {
} }
.quick-pane-model-selector { .quick-pane-model-selector {
flex: 1 1 230px; flex: 1 1 0;
max-width: 310px;
color: #bbb7b0; color: #bbb7b0;
font-weight: 750; font-weight: 750;
letter-spacing: 0.04em; letter-spacing: 0.04em;
@@ -150,16 +149,6 @@ body {
} }
} }
.quick-pane .model-trigger {
height: 27px;
border: 0;
padding: 5px 25px 5px 8px;
color: #e6e3df;
background-color: #353535;
font-size: 11px;
font-weight: 500;
}
.quick-pane .model-menu { .quick-pane .model-menu {
width: min(400px, calc(100vw - 24px)); width: min(400px, calc(100vw - 24px));
max-height: min(350px, calc(100vh - 56px)); max-height: min(350px, calc(100vh - 56px));
@@ -236,16 +225,48 @@ body {
} }
.quick-pane button:focus-visible, .quick-pane button:focus-visible,
.quick-pane select:focus-visible, .quick-pane select:focus-visible {
.quick-pane-request:focus {
outline: 3px solid #f0a347; outline: 3px solid #f0a347;
outline-offset: 2px; outline-offset: 2px;
} }
.quick-pane-close { .quick-pane-toolbar-control {
color: #e6e3df !important; box-sizing: border-box;
background: #45413c !important; height: 27px;
font-size: 11px !important; min-width: 0;
padding: 0 8px;
border: 1px solid #70522f;
border-radius: 5px;
color: #e6e3df;
background: #353535;
font: 500 11px / 1 Inter, ui-sans-serif, system-ui, sans-serif;
cursor: pointer;
}
.quick-pane .model-trigger.quick-pane-toolbar-control,
.quick-pane .thinking-selector.thinking-selector select.quick-pane-toolbar-control {
appearance: auto;
background: #353535;
}
.quick-pane-toolbar-control:hover:not(:disabled) {
border-color: #d88735;
background: #44382d;
}
.quick-pane-toolbar-control:focus-visible {
outline: 3px solid #f0a347;
outline-offset: 2px;
}
.quick-pane-toolbar-control:disabled {
opacity: 0.58;
cursor: not-allowed;
}
.quick-pane-request:focus {
outline: 3px solid #f0a347;
outline-offset: 2px;
} }
.quick-pane-exchange { .quick-pane-exchange {
@@ -577,13 +598,6 @@ body {
gap: 4px; gap: 4px;
} }
.quick-pane-action {
min-height: 30px;
padding: 4px 7px !important;
color: #e6e3df !important;
background: #45413c !important;
font-size: 10px !important;
}
.quick-pane-send { .quick-pane-send {
position: absolute; position: absolute;
@@ -643,13 +657,6 @@ body {
font-size: 10px; font-size: 10px;
} }
.quick-pane-close {
width: 27px;
height: 27px;
padding: 0 !important;
font-size: 18px !important;
}
.quick-pane-user-message { .quick-pane-user-message {
max-width: 88%; max-width: 88%;
} }