Compare commits

...
5 Commits
Author SHA1 Message Date
buzzert 14191de8f4 No rounded corners 2026-09-30 21:30:54 -07:00
buzzert 4ef48fad82 remove "open mode" extra text 2026-09-27 22:56:07 -07:00
buzzert 21beab26f1 Replace quick question backdrop blur with dimming 2026-09-27 00:51:38 -07:00
buzzert 050a46d7a8 Better mobile-optimized settings UI 2026-09-19 18:55:17 -07:00
buzzert 3217c3f66e ios: add copy session ID to chat menu
TestFlight / Build and upload (push) Successful in 1m45s
2026-09-06 14:17:42 -07:00
5 changed files with 142 additions and 83 deletions
@@ -1,5 +1,6 @@
import Observation import Observation
import SwiftUI import SwiftUI
import UIKit
struct SybilSidebarView: View { struct SybilSidebarView: View {
@Bindable var viewModel: SybilViewModel @Bindable var viewModel: SybilViewModel
@@ -169,7 +170,7 @@ struct SybilSidebarItemList: View {
Label(item.starred ? "Unstar" : "Star", systemImage: item.starred ? "star.slash" : "star") Label(item.starred ? "Unstar" : "Star", systemImage: item.starred ? "star.slash" : "star")
} }
if item.kind == .chat { if case let .chat(chatID) = item.selection {
Button { Button {
forkChat(item) forkChat(item)
} label: { } label: {
@@ -183,6 +184,12 @@ struct SybilSidebarItemList: View {
} label: { } label: {
Label("Rename", systemImage: "pencil") Label("Rename", systemImage: "pencil")
} }
Button {
UIPasteboard.general.string = chatID
} label: {
Label("Copy Session ID", systemImage: "doc.on.doc")
}
} }
Button(role: .destructive) { Button(role: .destructive) {
+76 -51
View File
@@ -1,4 +1,4 @@
import { useEffect, useMemo, useRef, useState } from "preact/hooks"; import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "preact/hooks";
import type { TargetedTouchEvent } from "preact"; import type { TargetedTouchEvent } from "preact";
import { import {
Check, Check,
@@ -654,9 +654,10 @@ type ModelComboboxProps = {
value: string; value: string;
onChange: (value: string) => void; onChange: (value: string) => void;
disabled?: boolean; disabled?: boolean;
className?: string;
}; };
function ModelCombobox({ options, value, onChange, disabled = false }: ModelComboboxProps) { function ModelCombobox({ options, value, onChange, disabled = false, className }: ModelComboboxProps) {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [draftValue, setDraftValue] = useState(value); const [draftValue, setDraftValue] = useState(value);
const rootRef = useRef<HTMLDivElement>(null); const rootRef = useRef<HTMLDivElement>(null);
@@ -710,7 +711,12 @@ function ModelCombobox({ options, value, onChange, disabled = false }: ModelComb
return ( return (
<div className="relative" ref={rootRef}> <div className="relative" ref={rootRef}>
<div className="flex h-10 min-w-56 items-center rounded-lg border border-violet-300/22 bg-background/72 px-3 text-sm shadow-[inset_0_1px_0_hsl(255_100%_92%_/_0.06)]"> <div
className={cn(
"flex h-10 min-w-56 items-center rounded-lg border border-violet-300/22 bg-background/72 px-3 text-sm shadow-[inset_0_1px_0_hsl(255_100%_92%_/_0.06)]",
className
)}
>
<input <input
ref={inputRef} ref={inputRef}
value={draftValue} value={draftValue}
@@ -948,7 +954,6 @@ export default function App() {
isCheckingSession, isCheckingSession,
isSigningIn, isSigningIn,
isAuthenticated, isAuthenticated,
authMode,
authError, authError,
handleAuthFailure: baseHandleAuthFailure, handleAuthFailure: baseHandleAuthFailure,
handleSignIn, handleSignIn,
@@ -1018,6 +1023,7 @@ export default function App() {
const transcriptEndRef = useRef<HTMLDivElement>(null); const transcriptEndRef = useRef<HTMLDivElement>(null);
const contextMenuRef = useRef<HTMLDivElement>(null); const contextMenuRef = useRef<HTMLDivElement>(null);
const renameChatInputRef = useRef<HTMLInputElement>(null); const renameChatInputRef = useRef<HTMLInputElement>(null);
const chatSettingsDialogRef = useRef<HTMLDialogElement>(null);
const fileInputRef = useRef<HTMLInputElement>(null); const fileInputRef = useRef<HTMLInputElement>(null);
const dragDepthRef = useRef(0); const dragDepthRef = useRef(0);
const pendingAttachmentsRef = useRef<ChatAttachment[]>([]); const pendingAttachmentsRef = useRef<ChatAttachment[]>([]);
@@ -1930,6 +1936,7 @@ export default function App() {
if (!isAuthenticated) return; if (!isAuthenticated) return;
const handleKeyDown = (event: KeyboardEvent) => { const handleKeyDown = (event: KeyboardEvent) => {
if (chatSettingsDialogRef.current?.open) return;
const hasPrimaryModifier = event.metaKey || event.ctrlKey; const hasPrimaryModifier = event.metaKey || event.ctrlKey;
if (!hasPrimaryModifier || event.altKey) return; if (!hasPrimaryModifier || event.altKey) return;
@@ -2331,16 +2338,16 @@ export default function App() {
return () => window.clearTimeout(timer); return () => window.clearTimeout(timer);
}, [renameChatDialog]); }, [renameChatDialog]);
useEffect(() => { useLayoutEffect(() => {
if (!isChatSettingsOpen) return; if (!isChatSettingsOpen) return;
const handleKeyDown = (event: KeyboardEvent) => { const dialog = chatSettingsDialogRef.current;
if (event.key !== "Escape" || isSavingChatSettings) return; const previouslyFocused = document.activeElement;
event.preventDefault(); dialog?.showModal();
setIsChatSettingsOpen(false); return () => {
dialog?.close();
if (previouslyFocused instanceof HTMLElement) previouslyFocused.focus({ preventScroll: true });
}; };
window.addEventListener("keydown", handleKeyDown); }, [isChatSettingsOpen]);
return () => window.removeEventListener("keydown", handleKeyDown);
}, [isChatSettingsOpen, isSavingChatSettings]);
useEffect(() => { useEffect(() => {
if (!isQuickQuestionOpen) return; if (!isQuickQuestionOpen) return;
@@ -3433,7 +3440,7 @@ export default function App() {
return ( return (
<div className="app-grid-surface app-safe-frame h-full"> <div className="app-grid-surface app-safe-frame h-full">
<div className="workspace-shell flex h-full w-full overflow-hidden bg-transparent md:gap-2"> <div className="workspace-shell flex h-full w-full overflow-hidden bg-transparent">
{isMobileSidebarOpen ? ( {isMobileSidebarOpen ? (
<button <button
type="button" type="button"
@@ -3445,19 +3452,13 @@ export default function App() {
<aside <aside
className={cn( className={cn(
"workspace-sidebar glass-panel fixed inset-y-0 left-0 z-40 flex w-[86vw] max-w-80 shrink-0 flex-col border-r border-violet-300/18 transition-transform md:static md:z-auto md:w-80 md:max-w-none md:rounded-2xl md:border", "workspace-sidebar glass-panel fixed inset-y-0 left-0 z-40 flex w-[86vw] max-w-80 shrink-0 flex-col border-r border-violet-300/18 transition-transform md:static md:z-auto md:w-80 md:max-w-none",
isMobileSidebarOpen ? "translate-x-0" : "-translate-x-full md:translate-x-0" isMobileSidebarOpen ? "translate-x-0" : "-translate-x-full md:translate-x-0"
)} )}
> >
<div className="relative min-h-24 px-4 pb-4 pt-5"> <div className="relative min-h-24 px-4 pb-4 pt-5 flex items-center">
<div className="pr-24"> <div className="sybil-wordmark bg-[linear-gradient(90deg,#ff8df8,#9a6dff_54%,#67dfff)] bg-clip-text text-3xl text-transparent">
<div className="sybil-wordmark bg-[linear-gradient(90deg,#ff8df8,#9a6dff_54%,#67dfff)] bg-clip-text text-3xl text-transparent"> SYBIL
SYBIL
</div>
<p className="mt-2 flex items-center gap-1.5 text-xs font-medium text-muted-foreground">
<span className="h-1.5 w-1.5 rounded-full bg-emerald-400" />
Sybil Web{authMode ? ` (${authMode === "open" ? "open mode" : "token mode"})` : ""}
</p>
</div> </div>
<SybilCharacter <SybilCharacter
className="absolute right-4 top-3 h-[calc(100%-1.5rem)]" className="absolute right-4 top-3 h-[calc(100%-1.5rem)]"
@@ -3608,7 +3609,7 @@ export default function App() {
</aside> </aside>
<main <main
className="workspace-content glass-panel relative flex min-w-0 flex-1 touch-pan-y flex-col overflow-hidden border-violet-300/18 md:touch-auto md:rounded-2xl md:border" className="workspace-content glass-panel relative flex min-w-0 flex-1 touch-pan-y flex-col overflow-hidden md:touch-auto"
onTouchStart={handleMobileWorkspaceTouchStart} onTouchStart={handleMobileWorkspaceTouchStart}
onTouchMove={handleMobileWorkspaceTouchMove} onTouchMove={handleMobileWorkspaceTouchMove}
onTouchEnd={handleMobileWorkspaceTouchEnd} onTouchEnd={handleMobileWorkspaceTouchEnd}
@@ -3822,44 +3823,66 @@ export default function App() {
</div> </div>
) : null} ) : null}
{isChatSettingsOpen ? ( {isChatSettingsOpen ? (
<div <dialog
className="fixed inset-0 z-[60] flex items-center justify-center bg-black/72 p-3 backdrop-blur-md md:p-6" ref={chatSettingsDialogRef}
aria-labelledby="chat-settings-title"
className="fixed inset-0 m-0 flex h-dvh max-h-none w-full max-w-none items-center justify-center overflow-hidden bg-transparent p-0 text-foreground backdrop:bg-black/70 backdrop:backdrop-blur-md md:p-6"
onCancel={(event) => {
event.preventDefault();
if (!isSavingChatSettings) setIsChatSettingsOpen(false);
}}
onMouseDown={(event) => { onMouseDown={(event) => {
if (event.target === event.currentTarget && !isSavingChatSettings) setIsChatSettingsOpen(false); if (event.target === event.currentTarget && !isSavingChatSettings) setIsChatSettingsOpen(false);
}} }}
> >
<form <form
role="dialog" className="glass-panel flex h-full min-h-0 w-full flex-col overflow-hidden md:h-auto md:max-h-full md:max-w-2xl md:rounded-2xl md:border md:border-violet-300/24"
aria-modal="true"
aria-labelledby="chat-settings-title"
className="glass-panel flex max-h-[88vh] w-full max-w-2xl flex-col rounded-2xl border border-violet-300/24 p-4 shadow-2xl shadow-black/45 md:p-5"
onSubmit={(event) => void handleChatSettingsSubmit(event)} onSubmit={(event) => void handleChatSettingsSubmit(event)}
> >
<div className="mb-4 flex items-center justify-between gap-3"> <div className="chat-settings-header grid shrink-0 grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] items-center gap-2 border-b border-violet-300/16 md:flex md:justify-between md:gap-3">
<Button
type="button"
variant="ghost"
className="h-11 justify-self-start px-2 text-violet-200 hover:bg-violet-400/10 hover:text-violet-100 md:hidden"
onClick={() => setIsChatSettingsOpen(false)}
disabled={isSavingChatSettings}
>
Cancel
</Button>
<div className="min-w-0"> <div className="min-w-0">
<h2 id="chat-settings-title" className="text-sm font-semibold text-violet-50"> <h2 id="chat-settings-title" className="text-base font-semibold text-violet-50 md:text-sm">
Chat settings Chat settings
</h2> </h2>
<p className="mt-1 truncate text-xs text-muted-foreground">{chatSettingsTitleDraft.trim() || "New chat"}</p> <p className="mt-1 hidden truncate text-xs text-muted-foreground md:block">{chatSettingsTitleDraft.trim() || "New chat"}</p>
</div> </div>
<Button <Button
type="button" type="button"
size="icon" size="icon"
variant="ghost" variant="ghost"
className="h-8 w-8" className="hidden h-8 w-8 shrink-0 md:inline-flex"
onClick={() => setIsChatSettingsOpen(false)} onClick={() => setIsChatSettingsOpen(false)}
disabled={isSavingChatSettings} disabled={isSavingChatSettings}
aria-label="Close chat settings" aria-label="Close chat settings"
> >
<X className="h-4 w-4" /> <X className="h-4 w-4" />
</Button> </Button>
<Button
type="submit"
variant="ghost"
className="h-11 justify-self-end gap-1.5 px-2 font-semibold text-violet-200 hover:bg-violet-400/10 hover:text-violet-100 md:hidden"
disabled={isSavingChatSettings}
>
{isSavingChatSettings ? <LoaderCircle className="h-4 w-4 animate-spin" /> : null}
Save
</Button>
</div> </div>
<div className="min-h-0 flex-1 space-y-4 overflow-y-auto pr-1"> <div className="chat-settings-content min-h-0 flex-1 space-y-5 overflow-y-auto overscroll-contain md:space-y-4">
<div> <div>
<span className="mb-1.5 block text-xs font-semibold text-violet-100/72">Chat title</span> <label for="chat-settings-chat-title" className="mb-1.5 block text-xs font-semibold text-violet-100/72">Chat title</label>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<input <input
id="chat-settings-chat-title"
value={chatSettingsTitleDraft} value={chatSettingsTitleDraft}
onInput={(event) => { onInput={(event) => {
setChatSettingsTitleDraft(event.currentTarget.value); setChatSettingsTitleDraft(event.currentTarget.value);
@@ -3867,7 +3890,7 @@ export default function App() {
}} }}
maxLength={120} maxLength={120}
placeholder={draftKind === null && selectedItem?.kind === "chat" ? "Chat title" : "Optional title"} placeholder={draftKind === null && selectedItem?.kind === "chat" ? "Chat title" : "Optional title"}
className="h-11 min-w-0 flex-1 rounded-lg border border-violet-300/22 bg-background/72 px-3 text-sm text-violet-50 outline-none shadow-[inset_0_1px_0_hsl(255_100%_92%_/_0.06)] placeholder:text-muted-foreground focus:border-violet-300/45 focus:ring-1 focus:ring-ring/70" className="h-11 min-w-0 flex-1 rounded-lg border border-violet-300/22 bg-background/70 px-3 text-base text-violet-50 outline-none shadow-[inset_0_1px_0_hsl(255_100%_92%_/_0.06)] placeholder:text-muted-foreground focus:border-violet-300/45 focus:ring-1 focus:ring-ring/70 md:text-sm"
disabled={isSavingChatSettings} disabled={isSavingChatSettings}
/> />
{chatSettingsChatId ? ( {chatSettingsChatId ? (
@@ -3895,7 +3918,7 @@ export default function App() {
<label className="block"> <label className="block">
<span className="mb-1.5 block text-xs font-semibold text-violet-100/72">Provider</span> <span className="mb-1.5 block text-xs font-semibold text-violet-100/72">Provider</span>
<select <select
className="h-10 w-full rounded-lg border border-violet-300/22 bg-background/72 px-3 text-sm text-violet-50 outline-none shadow-[inset_0_1px_0_hsl(255_100%_92%_/_0.06)] focus:border-violet-300/45 focus:ring-1 focus:ring-ring/70" className="h-11 w-full rounded-lg border border-violet-300/22 bg-background/70 px-3 text-base text-violet-50 outline-none shadow-[inset_0_1px_0_hsl(255_100%_92%_/_0.06)] focus:border-violet-300/45 focus:ring-1 focus:ring-ring/70 md:h-10 md:text-sm"
value={chatSettingsProviderDraft} value={chatSettingsProviderDraft}
onChange={(event) => { onChange={(event) => {
const nextProvider = event.currentTarget.value as Provider; const nextProvider = event.currentTarget.value as Provider;
@@ -3917,6 +3940,7 @@ export default function App() {
<label className="block min-w-0"> <label className="block min-w-0">
<span className="mb-1.5 block text-xs font-semibold text-violet-100/72">Model</span> <span className="mb-1.5 block text-xs font-semibold text-violet-100/72">Model</span>
<ModelCombobox <ModelCombobox
className="h-11 min-w-0 bg-background/70 text-base md:h-10 md:text-sm"
options={chatSettingsProviderModelOptions} options={chatSettingsProviderModelOptions}
value={chatSettingsModelDraft} value={chatSettingsModelDraft}
disabled={isSavingChatSettings} disabled={isSavingChatSettings}
@@ -3938,7 +3962,7 @@ export default function App() {
if (chatSettingsError) setChatSettingsError(null); if (chatSettingsError) setChatSettingsError(null);
}} }}
placeholder="Add per-chat instructions" placeholder="Add per-chat instructions"
className="min-h-32 resize-y border-violet-300/24 bg-background/72 text-sm text-violet-50 placeholder:text-violet-200/45" className="min-h-32 resize-y border-violet-300/24 bg-background/70 text-base text-violet-50 placeholder:text-violet-200/45 md:text-sm"
disabled={isSavingChatSettings} disabled={isSavingChatSettings}
/> />
</label> </label>
@@ -3952,6 +3976,7 @@ export default function App() {
type="button" type="button"
size="sm" size="sm"
variant="secondary" variant="secondary"
className="h-11 gap-1.5 md:h-8"
onClick={() => setChatSettingsEnabledToolsDraft(getDefaultEnabledTools(availableChatTools))} onClick={() => setChatSettingsEnabledToolsDraft(getDefaultEnabledTools(availableChatTools))}
disabled={isSavingChatSettings} disabled={isSavingChatSettings}
> >
@@ -3962,6 +3987,7 @@ export default function App() {
type="button" type="button"
size="sm" size="sm"
variant="secondary" variant="secondary"
className="h-11 gap-1.5 md:h-8"
onClick={() => setChatSettingsEnabledToolsDraft([])} onClick={() => setChatSettingsEnabledToolsDraft([])}
disabled={isSavingChatSettings} disabled={isSavingChatSettings}
> >
@@ -3971,31 +3997,30 @@ export default function App() {
</div> </div>
) : null} ) : null}
</div> </div>
<div className="space-y-2"> <div className="grid gap-2 md:grid-cols-2">
{availableChatTools.length ? ( {availableChatTools.length ? (
availableChatTools.map((tool) => { availableChatTools.map((tool) => {
const checked = chatSettingsEnabledToolsDraft.includes(tool.name); const checked = chatSettingsEnabledToolsDraft.includes(tool.name);
return ( return (
<label <label
key={tool.name} key={tool.name}
className="flex cursor-pointer items-start gap-3 rounded-lg border border-violet-300/18 bg-background/44 px-3 py-2.5 transition hover:border-violet-300/34 hover:bg-violet-400/8" title={tool.description}
className="flex min-h-11 min-w-0 cursor-pointer items-center gap-3 rounded-lg border border-violet-300/18 bg-background/44 px-3 py-2.5 transition hover:border-violet-300/34 hover:bg-violet-400/8 focus-within:border-violet-300/45 focus-within:ring-1 focus-within:ring-ring/70"
> >
<input <input
type="checkbox" type="checkbox"
checked={checked} checked={checked}
onChange={() => toggleChatSettingsTool(tool.name)} onChange={() => toggleChatSettingsTool(tool.name)}
className="mt-1 h-4 w-4 rounded border-violet-300/35 bg-background/80 accent-violet-400" className="h-4 w-4 shrink-0 rounded border-violet-300/35 bg-background/80 accent-violet-400"
aria-description={tool.description}
disabled={isSavingChatSettings} disabled={isSavingChatSettings}
/> />
<span className="min-w-0"> <span className="min-w-0 break-words text-sm font-medium text-violet-50">{getToolLabel(tool.name)}</span>
<span className="block text-sm font-medium text-violet-50">{getToolLabel(tool.name)}</span>
<span className="mt-0.5 block text-xs leading-5 text-muted-foreground">{tool.description}</span>
</span>
</label> </label>
); );
}) })
) : ( ) : (
<p className="rounded-lg border border-violet-300/18 bg-background/44 px-3 py-2.5 text-sm text-muted-foreground"> <p className="rounded-lg border border-violet-300/18 bg-background/44 px-3 py-2.5 text-sm text-muted-foreground md:col-span-2">
No chat tools are available. No chat tools are available.
</p> </p>
)} )}
@@ -4003,8 +4028,8 @@ export default function App() {
</section> </section>
</div> </div>
{chatSettingsError ? <p className="mt-3 text-sm text-rose-300">{chatSettingsError}</p> : null} {chatSettingsError ? <p role="alert" className="chat-settings-error shrink-0 break-words border-t border-violet-300/16 text-sm text-rose-300">{chatSettingsError}</p> : null}
<div className="mt-4 flex justify-end gap-2"> <div className="hidden shrink-0 justify-end gap-2 border-t border-violet-300/16 p-5 md:flex">
<Button type="button" variant="secondary" onClick={() => setIsChatSettingsOpen(false)} disabled={isSavingChatSettings}> <Button type="button" variant="secondary" onClick={() => setIsChatSettingsOpen(false)} disabled={isSavingChatSettings}>
Cancel Cancel
</Button> </Button>
@@ -4014,7 +4039,7 @@ export default function App() {
</Button> </Button>
</div> </div>
</form> </form>
</div> </dialog>
) : null} ) : null}
{renameChatDialog ? ( {renameChatDialog ? (
<div <div
@@ -4073,7 +4098,7 @@ export default function App() {
) : null} ) : null}
{isQuickQuestionOpen ? ( {isQuickQuestionOpen ? (
<div <div
className="fixed inset-0 z-[60] flex items-center justify-center bg-black/72 p-3 backdrop-blur-md md:p-6" className="fixed inset-0 z-[60] flex items-center justify-center bg-black/40 p-3 md:p-6"
onMouseDown={(event) => { onMouseDown={(event) => {
if (event.target === event.currentTarget) setIsQuickQuestionOpen(false); if (event.target === event.currentTarget) setIsQuickQuestionOpen(false);
}} }}
@@ -378,13 +378,13 @@ function ToolCallStack({
); );
})} })}
{!expanded && hiddenCount ? ( {!expanded && hiddenCount ? (
<span className="absolute bottom-1 right-10 z-20 rounded-full border border-cyan-300/30 bg-slate-950/86 px-2 py-0.5 text-[10px] font-semibold leading-none text-cyan-100 shadow-sm"> <span className="absolute bottom-1 right-10 z-20 rounded-none border border-cyan-300/30 bg-slate-950/86 px-2 py-0.5 text-[10px] font-semibold leading-none text-cyan-100 shadow-sm">
+{hiddenCount} +{hiddenCount}
</span> </span>
) : null} ) : null}
<button <button
type="button" type="button"
className="tool-call-stack-toggle absolute right-0 top-2 z-20 flex h-8 w-8 items-center justify-center rounded-full" className="tool-call-stack-toggle absolute right-0 top-2 z-20 flex h-8 w-8 items-center justify-center rounded-none"
aria-expanded={expanded ? "true" : "false"} aria-expanded={expanded ? "true" : "false"}
aria-label={`${expanded ? "Collapse" : "Expand"} ${countLabel}`} aria-label={`${expanded ? "Collapse" : "Expand"} ${countLabel}`}
title={`${expanded ? "Collapse" : "Expand"} ${countLabel}`} title={`${expanded ? "Collapse" : "Expand"} ${countLabel}`}
@@ -603,7 +603,7 @@ export function ChatMessagesPanel({ messages, isLoading, isSending, onMessageCon
className={cn( className={cn(
"max-w-[85%] space-y-3", "max-w-[85%] space-y-3",
isUser isUser
? "rounded-xl border border-violet-300/24 bg-[linear-gradient(135deg,hsl(258_86%_48%_/_0.86),hsl(278_72%_29%_/_0.86))] px-4 py-3 text-sm leading-6 text-fuchsia-50 shadow-sm" ? "chat-user-message border border-violet-300/24 bg-[linear-gradient(135deg,hsl(258_86%_48%_/_0.86),hsl(278_72%_29%_/_0.86))] px-4 py-3 text-sm leading-6 text-fuchsia-50 shadow-sm"
: "text-base leading-7 text-violet-50" : "text-base leading-7 text-violet-50"
)} )}
onContextMenu={ onContextMenu={
+48 -26
View File
@@ -31,7 +31,7 @@
--secondary-foreground: 254 38% 94%; --secondary-foreground: 254 38% 94%;
--accent: 188 86% 50%; --accent: 188 86% 50%;
--accent-foreground: 230 45% 8%; --accent-foreground: 230 45% 8%;
--radius: 0.5rem; --radius: 0px;
} }
* { * {
@@ -72,6 +72,7 @@ select,
textarea { textarea {
font: inherit; font: inherit;
color-scheme: dark; color-scheme: dark;
border-radius: var(--radius);
} }
::selection { ::selection {
@@ -113,13 +114,39 @@ textarea {
max(0.75rem, var(--safe-area-left)); max(0.75rem, var(--safe-area-left));
} }
.chat-settings-header {
padding:
calc(0.5rem + var(--safe-area-top))
max(0.75rem, var(--safe-area-right))
0.5rem
max(0.75rem, var(--safe-area-left));
}
.chat-settings-content {
padding:
1rem
max(1rem, var(--safe-area-right))
max(1.5rem, var(--safe-area-bottom))
max(1rem, var(--safe-area-left));
scroll-padding-block: 1rem;
}
.chat-settings-error {
padding:
0.75rem
max(1rem, var(--safe-area-right))
max(0.75rem, var(--safe-area-bottom))
max(1rem, var(--safe-area-left));
}
@media (min-width: 768px) { @media (min-width: 768px) {
.app-safe-frame { .chat-settings-header,
padding: .chat-settings-content {
max(0.5rem, var(--safe-area-top)) padding: 1.25rem;
max(0.5rem, var(--safe-area-right)) }
max(0.5rem, var(--safe-area-bottom))
max(0.5rem, var(--safe-area-left)); .chat-settings-error {
padding: 0.75rem 1.25rem;
} }
.app-search-safe-pad { .app-search-safe-pad {
@@ -132,19 +159,11 @@ textarea {
} }
@media (horizontal-viewport-segments: 2) { @media (horizontal-viewport-segments: 2) {
.app-safe-frame {
padding:
max(0.5rem, var(--safe-area-top))
max(0.5rem, var(--safe-area-right))
max(0.5rem, var(--safe-area-bottom))
max(0.5rem, var(--safe-area-left));
}
.workspace-shell { .workspace-shell {
display: grid; display: grid;
grid-template-columns: grid-template-columns:
calc(env(viewport-segment-width 0 0) - max(0.5rem, var(--safe-area-left))) calc(env(viewport-segment-width 0 0) - var(--safe-area-left))
calc(env(viewport-segment-width 1 0) - max(0.5rem, var(--safe-area-right))); calc(env(viewport-segment-width 1 0) - var(--safe-area-right));
column-gap: calc(env(viewport-segment-left 1 0) - env(viewport-segment-right 0 0)); column-gap: calc(env(viewport-segment-left 1 0) - env(viewport-segment-right 0 0));
} }
@@ -153,14 +172,10 @@ textarea {
width: 100%; width: 100%;
max-width: none; max-width: none;
transform: none; transform: none;
border-width: 1px;
border-radius: 1rem;
} }
.workspace-content { .workspace-content {
min-width: 0; min-width: 0;
border-width: 1px;
border-radius: 1rem;
touch-action: auto; touch-action: auto;
} }
@@ -179,6 +194,13 @@ textarea {
0 14px 36px hsl(240 80% 2% / 0.28); 0 14px 36px hsl(240 80% 2% / 0.28);
} }
.chat-user-message {
/* Preserve the original bubble radius independently of the square UI theme.
Browsers without corner-shape support fall back to circular corners. */
border-radius: 0.75rem;
corner-shape: squircle;
}
.tool-call-stack-shell { .tool-call-stack-shell {
perspective: 900px; perspective: 900px;
transform-style: preserve-3d; transform-style: preserve-3d;
@@ -430,7 +452,7 @@ textarea {
overflow-x: auto; overflow-x: auto;
overflow-y: hidden; overflow-y: hidden;
border: 1px solid hsl(var(--border) / 0.86); border: 1px solid hsl(var(--border) / 0.86);
border-radius: 0.625rem; border-radius: var(--radius);
background: hsl(246 34% 10% / 0.76); background: hsl(246 34% 10% / 0.76);
box-shadow: inset 0 1px 0 hsl(258 80% 88% / 0.06); box-shadow: inset 0 1px 0 hsl(258 80% 88% / 0.06);
} }
@@ -449,7 +471,7 @@ textarea {
} }
.md-table-scroll::-webkit-scrollbar-thumb { .md-table-scroll::-webkit-scrollbar-thumb {
border-radius: 9999px; border-radius: var(--radius);
background: hsl(263 78% 72% / 0.34); background: hsl(263 78% 72% / 0.34);
} }
@@ -561,7 +583,7 @@ textarea {
.md-content code { .md-content code {
background: hsl(249 40% 10% / 0.78); background: hsl(249 40% 10% / 0.78);
border-radius: 0.3rem; border-radius: var(--radius);
padding: 0.05rem 0.3rem; padding: 0.05rem 0.3rem;
font-size: 0.86em; font-size: 0.86em;
box-decoration-break: clone; box-decoration-break: clone;
@@ -571,7 +593,7 @@ textarea {
.md-content pre { .md-content pre {
overflow-x: auto; overflow-x: auto;
border: 1px solid hsl(253 31% 29% / 0.72); border: 1px solid hsl(253 31% 29% / 0.72);
border-radius: 0.625rem; border-radius: var(--radius);
background: hsl(249 40% 10% / 0.82); background: hsl(249 40% 10% / 0.82);
padding: 0.75rem; padding: 0.75rem;
box-shadow: inset 0 1px 0 hsl(258 80% 88% / 0.05); box-shadow: inset 0 1px 0 hsl(258 80% 88% / 0.05);
@@ -595,7 +617,7 @@ textarea {
.md-cite-token { .md-cite-token {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
border-radius: 9999px; border-radius: var(--radius);
border: 1px solid hsl(274 40% 55%); border: 1px solid hsl(274 40% 55%);
background: hsl(277 33% 21%); background: hsl(277 33% 21%);
color: hsl(304 42% 96%); color: hsl(304 42% 96%);
+7 -2
View File
@@ -29,9 +29,14 @@ const config: Config = {
}, },
}, },
borderRadius: { borderRadius: {
// Keep surfaces square at every size; rounded-full is reserved for dots.
DEFAULT: "var(--radius)",
sm: "var(--radius)",
md: "var(--radius)",
lg: "var(--radius)", lg: "var(--radius)",
md: "calc(var(--radius) - 2px)", xl: "var(--radius)",
sm: "calc(var(--radius) - 4px)", "2xl": "var(--radius)",
"3xl": "var(--radius)",
}, },
}, },
}, },