authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-07-23 13:09:55+02:00
committergravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-07-23 13:09:55+02:00
loge51538b812392d522947494ae32dacf3f3859f5e
treedb8c5fbd1d0df94349c43b98a7016af27e4d29d9
parent1c9a6de26eb41f77dbf2f6039427f50f2e9f8b39

Restore light-mode surfaces and refine dialog styling

- Restore light-mode glass, dialog, dropdown, and composer colors - Refine provider wizard, changed-file cards, sidebar controls, and buttons - Update wizard step tests for the new list structure

18 files changed, 212 insertions(+), 225 deletions(-)

apps/web/src/components/ChatView.tsx+1-1
...@@ -5592,7 +5592,7 @@ function ChatViewContent(props: ChatViewProps) {...@@ -5592,7 +5592,7 @@ function ChatViewContent(props: ChatViewProps) {
5592 : undefined5592 : undefined
5593 }5593 }
5594 >5594 >
5595 <div className="chat-composer-glass-host mx-auto w-full max-w-3xl overflow-hidden rounded-[22px]">5595 <div className="chat-composer-glass-host mx-auto w-full max-w-3xl rounded-[22px]">
5596 <div ref={attachDraftHeroComposerAnchorRef} className="relative z-10 isolate">5596 <div ref={attachDraftHeroComposerAnchorRef} className="relative z-10 isolate">
5597 <ChatComposer5597 <ChatComposer
5598 composerRef={composerRef}5598 composerRef={composerRef}
apps/web/src/components/GitActionsControl.tsx+9-20
...@@ -544,10 +544,7 @@ function PublishRepositoryDialog(props: PublishRepositoryDialogProps) {...@@ -544,10 +544,7 @@ function PublishRepositoryDialog(props: PublishRepositoryDialogProps) {
544544
545 return (545 return (
546 <Dialog open={props.open} onOpenChange={handleOpenChange}>546 <Dialog open={props.open} onOpenChange={handleOpenChange}>
547 <DialogPopup547 <DialogPopup className="max-w-xl overflow-hidden">
548 backdropClassName="bg-black/40 backdrop-blur-none"
549 className="workflow-dialog-surface max-w-xl overflow-hidden before:hidden"
550 >
551 <div className="flex min-h-0 flex-col overflow-hidden border-foreground/10 bg-transparent">548 <div className="flex min-h-0 flex-col overflow-hidden border-foreground/10 bg-transparent">
552 <DialogHeader className="border-b border-border/70 bg-foreground/[0.025] dark:border-transparent dark:bg-transparent">549 <DialogHeader className="border-b border-border/70 bg-foreground/[0.025] dark:border-transparent dark:bg-transparent">
553 <DialogTitle>Publish repository</DialogTitle>550 <DialogTitle>Publish repository</DialogTitle>
...@@ -1828,16 +1825,13 @@ export default function GitActionsControl({...@@ -1828,16 +1825,13 @@ export default function GitActionsControl({
1828 }1825 }
1829 }}1826 }}
1830 >1827 >
1831 <DialogPopup1828 <DialogPopup>
1832 backdropClassName="bg-black/40 backdrop-blur-none"
1833 className="command-palette-dialog-surface workflow-dialog-surface before:hidden"
1834 >
1835 <DialogHeader>1829 <DialogHeader>
1836 <DialogTitle>{COMMIT_DIALOG_TITLE}</DialogTitle>1830 <DialogTitle>{COMMIT_DIALOG_TITLE}</DialogTitle>
1837 <DialogDescription>{COMMIT_DIALOG_DESCRIPTION}</DialogDescription>1831 <DialogDescription>{COMMIT_DIALOG_DESCRIPTION}</DialogDescription>
1838 </DialogHeader>1832 </DialogHeader>
1839 <DialogPanel className="space-y-4">1833 <DialogPanel className="space-y-4">
1840 <div className="space-y-3 rounded-lg border border-input bg-muted/40 p-3 text-xs dark:border-transparent dark:bg-white/[0.035]">1834 <div className="space-y-3 rounded-xl bg-zinc-25 p-3 text-sm ring-1 ring-black/5 dark:bg-white/[0.035] dark:ring-white/5">
1841 <div className="grid grid-cols-[auto_1fr] items-center gap-x-2 gap-y-1">1835 <div className="grid grid-cols-[auto_1fr] items-center gap-x-2 gap-y-1">
1842 <span className="text-muted-foreground">Branch</span>1836 <span className="text-muted-foreground">Branch</span>
1843 <span className="flex items-center justify-between gap-2">1837 <span className="flex items-center justify-between gap-2">
...@@ -1845,9 +1839,7 @@ export default function GitActionsControl({...@@ -1845,9 +1839,7 @@ export default function GitActionsControl({
1845 {gitStatusForActions?.refName ?? "(detached HEAD)"}1839 {gitStatusForActions?.refName ?? "(detached HEAD)"}
1846 </span>1840 </span>
1847 {isDefaultRef && (1841 {isDefaultRef && (
1848 <span className="text-right text-warning text-xs">1842 <span className="text-right text-warning">Warning: default refName</span>
1849 Warning: default refName
1850 </span>
1851 )}1843 )}
1852 </span>1844 </span>
1853 </div>1845 </div>
...@@ -1886,14 +1878,14 @@ export default function GitActionsControl({...@@ -1886,14 +1878,14 @@ export default function GitActionsControl({
1886 <p className="font-medium">none</p>1878 <p className="font-medium">none</p>
1887 ) : (1879 ) : (
1888 <div className="space-y-2">1880 <div className="space-y-2">
1889 <ScrollArea className="h-44 rounded-md border border-input bg-background dark:border-transparent dark:bg-white/[0.025]">1881 <ScrollArea className="h-44 rounded-lg bg-card ring-1 ring-black/5 dark:bg-white/[0.025] dark:ring-white/5">
1890 <div className="space-y-1 p-1">1882 <div className="space-y-1 p-1">
1891 {allFiles.map((file) => {1883 {allFiles.map((file) => {
1892 const isExcluded = excludedFiles.has(file.path);1884 const isExcluded = excludedFiles.has(file.path);
1893 return (1885 return (
1894 <div1886 <div
1895 key={file.path}1887 key={file.path}
1896 className="flex w-full items-center gap-2 rounded-md px-2 py-1 font-mono text-xs transition-colors hover:bg-accent/50"1888 className="flex w-full items-center gap-2 rounded-md px-2 py-1 font-mono hover:bg-accent/50"
1897 >1889 >
1898 {isEditingFiles && (1890 {isEditingFiles && (
1899 <Checkbox1891 <Checkbox
...@@ -1952,7 +1944,7 @@ export default function GitActionsControl({...@@ -1952,7 +1944,7 @@ export default function GitActionsControl({
1952 </div>1944 </div>
1953 </div>1945 </div>
1954 <div className="space-y-1">1946 <div className="space-y-1">
1955 <p className="text-xs font-medium">Commit message (optional)</p>1947 <p className="text-sm font-medium">Commit message (optional)</p>
1956 <Textarea1948 <Textarea
1957 value={dialogCommitMessage}1949 value={dialogCommitMessage}
1958 onChange={(event) => setDialogCommitMessage(event.target.value)}1950 onChange={(event) => setDialogCommitMessage(event.target.value)}
...@@ -1961,7 +1953,7 @@ export default function GitActionsControl({...@@ -1961,7 +1953,7 @@ export default function GitActionsControl({
1961 />1953 />
1962 </div>1954 </div>
1963 </DialogPanel>1955 </DialogPanel>
1964 <DialogFooter className="dark:border-transparent dark:bg-transparent">1956 <DialogFooter variant="bare">
1965 <Button1957 <Button
1966 variant="outline"1958 variant="outline"
1967 size="sm"1959 size="sm"
...@@ -2004,10 +1996,7 @@ export default function GitActionsControl({...@@ -2004,10 +1996,7 @@ export default function GitActionsControl({
2004 }1996 }
2005 }}1997 }}
2006 >1998 >
2007 <DialogPopup1999 <DialogPopup className="max-w-xl">
2008 backdropClassName="bg-black/40 backdrop-blur-none"
2009 className="workflow-dialog-surface max-w-xl before:hidden"
2010 >
2011 <DialogHeader>2000 <DialogHeader>
2012 <DialogTitle>2001 <DialogTitle>
2013 {pendingDefaultBranchActionCopy?.title ?? "Run action on default refName?"}2002 {pendingDefaultBranchActionCopy?.title ?? "Run action on default refName?"}
apps/web/src/components/ProjectScriptsControl.tsx+1-1
...@@ -461,7 +461,7 @@ export default function ProjectScriptsControl({...@@ -461,7 +461,7 @@ export default function ProjectScriptsControl({
461 }}461 }}
462 open={dialogOpen}462 open={dialogOpen}
463 >463 >
464 <DialogPopup className="before:hidden">464 <DialogPopup>
465 <DialogHeader>465 <DialogHeader>
466 <DialogTitle>{isEditing ? "Edit Action" : "Add Action"}</DialogTitle>466 <DialogTitle>{isEditing ? "Edit Action" : "Add Action"}</DialogTitle>
467 <DialogDescription>467 <DialogDescription>
apps/web/src/components/SidebarV2.tsx+7-7
...@@ -677,13 +677,13 @@ const SidebarV2Row = memo(function SidebarV2Row(props: {...@@ -677,13 +677,13 @@ const SidebarV2Row = memo(function SidebarV2Row(props: {
677 <span className="text-red-600 dark:text-red-400">−{diff.deletions}</span>677 <span className="text-red-600 dark:text-red-400">−{diff.deletions}</span>
678 </span>678 </span>
679 ) : null}679 ) : null}
680 <span className="ml-auto inline-flex shrink-0 items-center gap-1">680 <span
681 aria-hidden
682 className="pointer-events-none ml-auto inline-flex shrink-0 items-center gap-1"
683 >
681 {isRemote ? (684 {isRemote ? (
682 <span685 <span className="inline-flex shrink-0 items-center text-sidebar-muted-foreground/70">
683 aria-label={props.environmentLabel ?? "Remote environment"}686 <ServerIcon aria-hidden className="size-3.5" />
684 className="inline-flex shrink-0 items-center text-sidebar-muted-foreground/70"
685 >
686 <ServerIcon aria-hidden className="size-4" />
687 </span>687 </span>
688 ) : null}688 ) : null}
689 {driverKind ? (689 {driverKind ? (
...@@ -691,7 +691,7 @@ const SidebarV2Row = memo(function SidebarV2Row(props: {...@@ -691,7 +691,7 @@ const SidebarV2Row = memo(function SidebarV2Row(props: {
691 <ProviderInstanceIcon691 <ProviderInstanceIcon
692 driverKind={driverKind}692 driverKind={driverKind}
693 displayName={thread.session?.providerName ?? modelInstanceId}693 displayName={thread.session?.providerName ?? modelInstanceId}
694 iconClassName="size-4"694 iconClassName="size-3.5"
695 />695 />
696 </span>696 </span>
697 ) : null}697 ) : null}
apps/web/src/components/chat/ChangedFilesTree.tsx+2-2
...@@ -41,8 +41,8 @@ export const ChangedFilesCard = memo(function ChangedFilesCard(props: {...@@ -41,8 +41,8 @@ export const ChangedFilesCard = memo(function ChangedFilesCard(props: {
41 const summaryStat = useMemo(() => summarizeTurnDiffStats(files), [files]);41 const summaryStat = useMemo(() => summarizeTurnDiffStats(files), [files]);
4242
43 return (43 return (
44 <div className="mt-4 rounded-2xl bg-background p-2 pt-4 shadow-xs/5 not-dark:bg-clip-padding dark:bg-input/32">44 <div className="mt-4 rounded-2xl border border-border/70 bg-secondary p-2 pt-4 dark:border-transparent dark:bg-input/32">
45 <div className="sticky top-0 z-10 mb-3 flex items-center justify-between gap-2 bg-background px-2 before:absolute before:inset-x-0 before:-top-4 before:h-4 before:bg-background before:content-[''] dark:bg-[color-mix(in_srgb,var(--foreground)_2.5%,var(--background))] dark:before:bg-[color-mix(in_srgb,var(--foreground)_2.5%,var(--background))]">45 <div className="sticky top-0 z-10 mb-3 flex items-center justify-between gap-2 bg-secondary px-2 before:absolute before:inset-x-0 before:-top-4 before:h-4 before:bg-secondary before:content-[''] dark:bg-[color-mix(in_srgb,var(--foreground)_2.5%,var(--background))] dark:before:bg-[color-mix(in_srgb,var(--foreground)_2.5%,var(--background))]">
46 <p className="flex items-center gap-1 whitespace-nowrap font-medium text-foreground text-xs leading-4">46 <p className="flex items-center gap-1 whitespace-nowrap font-medium text-foreground text-xs leading-4">
47 <span>47 <span>
48 {files.length} changed file{files.length === 1 ? "" : "s"}48 {files.length} changed file{files.length === 1 ? "" : "s"}
apps/web/src/components/chat/ComposerCommandMenu.tsx+1-4
...@@ -139,10 +139,7 @@ export const ComposerCommandMenu = memo(function ComposerCommandMenu(props: {...@@ -139,10 +139,7 @@ export const ComposerCommandMenu = memo(function ComposerCommandMenu(props: {
139 );139 );
140 }}140 }}
141 >141 >
142 <div142 <div ref={listRef} className="dropdown-glass relative w-full overflow-hidden rounded-[20px]">
143 ref={listRef}
144 className="relative w-full overflow-hidden rounded-[20px] border border-border/80 bg-popover/96 shadow-lg/8 backdrop-blur-xs"
145 >
146 {props.items.length > 0 ? (143 {props.items.length > 0 ? (
147 <CommandList className="max-h-72">144 <CommandList className="max-h-72">
148 {groups.map((group, groupIndex) => (145 {groups.map((group, groupIndex) => (
apps/web/src/components/chat/MessagesTimeline.tsx+2-2
...@@ -1285,8 +1285,8 @@ function AssistantChangedFilesSectionInner({...@@ -1285,8 +1285,8 @@ function AssistantChangedFilesSectionInner({
1285 const summaryStat = summarizeTurnDiffStats(checkpointFiles);1285 const summaryStat = summarizeTurnDiffStats(checkpointFiles);
12861286
1287 return (1287 return (
1288 <div className="mt-4 rounded-2xl bg-background p-2 pt-4 shadow-xs/5 not-dark:bg-clip-padding dark:bg-input/32">1288 <div className="mt-4 rounded-2xl border border-border/70 bg-secondary p-2 pt-4 dark:border-transparent dark:bg-input/32">
1289 <div className="sticky top-2 z-10 mb-3 flex items-center justify-between gap-2 bg-background px-2 before:absolute before:inset-x-0 before:-top-4 before:h-4 before:bg-background before:content-[''] dark:bg-[color-mix(in_srgb,var(--foreground)_2.5%,var(--background))] dark:before:bg-[color-mix(in_srgb,var(--foreground)_2.5%,var(--background))]">1289 <div className="sticky top-2 z-10 mb-3 flex items-center justify-between gap-2 bg-secondary px-2 before:absolute before:inset-x-0 before:-top-4 before:h-4 before:bg-secondary before:content-[''] dark:bg-[color-mix(in_srgb,var(--foreground)_2.5%,var(--background))] dark:before:bg-[color-mix(in_srgb,var(--foreground)_2.5%,var(--background))]">
1290 <p className="flex items-center gap-1 whitespace-nowrap font-medium text-foreground text-xs leading-4">1290 <p className="flex items-center gap-1 whitespace-nowrap font-medium text-foreground text-xs leading-4">
1291 <span>1291 <span>
1292 {checkpointFiles.length} changed file{checkpointFiles.length === 1 ? "" : "s"}1292 {checkpointFiles.length} changed file{checkpointFiles.length === 1 ? "" : "s"}
apps/web/src/components/clerk/T3ConnectSidebarSignIn.tsx+2-2
...@@ -55,10 +55,10 @@ function ConfiguredT3ConnectSidebarSignIn() {...@@ -55,10 +55,10 @@ function ConfiguredT3ConnectSidebarSignIn() {
55 <SidebarMenuItem>55 <SidebarMenuItem>
56 <SidebarMenuButton56 <SidebarMenuButton
57 size="sm"57 size="sm"
58 className="gap-2 px-2 py-2 text-xs text-muted-foreground hover:bg-accent hover:text-foreground"58 className="h-8 items-center gap-2 rounded-md px-2 py-1.5 text-sm font-medium text-sidebar-muted-foreground/80 hover:bg-sidebar-row-hover hover:text-sidebar-foreground"
59 onClick={openAuthPrompt}59 onClick={openAuthPrompt}
60 >60 >
61 <LogInIcon className="size-4" />61 <LogInIcon className="size-4 shrink-0" />
62 <span>Sign in to T3 Connect</span>62 <span>Sign in to T3 Connect</span>
63 </SidebarMenuButton>63 </SidebarMenuButton>
64 </SidebarMenuItem>64 </SidebarMenuItem>
apps/web/src/components/settings/AddProviderInstanceDialog.tsx+17-20
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1"use client";1"use client";
22
3import { Radio as RadioPrimitive } from "@base-ui/react/radio";3import { Radio as RadioPrimitive } from "@base-ui/react/radio";
4import { CheckIcon } from "lucide-react";
4import { useMemo, useState } from "react";5import { useMemo, useState } from "react";
5import {6import {
6 ProviderInstanceId,7 ProviderInstanceId,
...@@ -223,7 +224,7 @@ export function AddProviderInstanceDialog({ open, onOpenChange }: AddProviderIns...@@ -223,7 +224,7 @@ export function AddProviderInstanceDialog({ open, onOpenChange }: AddProviderIns
223 <Dialog open={open} onOpenChange={onOpenChange}>224 <Dialog open={open} onOpenChange={onOpenChange}>
224 <DialogPopup className="max-w-xl overflow-hidden">225 <DialogPopup className="max-w-xl overflow-hidden">
225 <div className="flex min-h-0 flex-col overflow-hidden">226 <div className="flex min-h-0 flex-col overflow-hidden">
226 <DialogHeader className="border-b border-border/70">227 <DialogHeader>
227 <DialogTitle>Add provider instance</DialogTitle>228 <DialogTitle>Add provider instance</DialogTitle>
228 <DialogDescription>229 <DialogDescription>
229 Configure an additional provider instance — for example, a second Codex install230 Configure an additional provider instance — for example, a second Codex install
...@@ -239,41 +240,37 @@ export function AddProviderInstanceDialog({ open, onOpenChange }: AddProviderIns...@@ -239,41 +240,37 @@ export function AddProviderInstanceDialog({ open, onOpenChange }: AddProviderIns
239240
240 <div241 <div
241 data-slot="dialog-panel"242 data-slot="dialog-panel"
242 className="space-y-4 border-b border-border/70 bg-foreground/[0.025] px-6 py-5"243 className="space-y-4 bg-zinc-25/80 px-6 py-5 ring-1 ring-black/5 dark:bg-white/2 dark:ring-white/5"
243 >244 >
244 <AnimatedHeight>245 <AnimatedHeight>
245 <div className={cn("grid gap-2", wizardStep !== 0 && "hidden")}>246 <div className={cn("grid gap-2", wizardStep !== 0 && "hidden")}>
246 <span247 <div id="add-instance-driver-label" className="text-sm font-medium text-foreground">
247 id="add-instance-driver-label"
248 className="text-xs font-medium text-foreground"
249 >
250 Driver248 Driver
251 </span>249 </div>
252 <RadioGroup250 <RadioGroup
253 value={driver}251 value={driver}
254 onValueChange={(value) => setDriver(ProviderDriverKind.make(value))}252 onValueChange={(value) => setDriver(ProviderDriverKind.make(value))}
255 aria-labelledby="add-instance-driver-label"253 aria-labelledby="add-instance-driver-label"
256 className="grid grid-cols-2 gap-2.5"254 className="grid grid-cols-1 gap-2 sm:grid-cols-2"
257 >255 >
258 {DRIVER_OPTIONS.map((option) => {256 {DRIVER_OPTIONS.map((option) => {
259 const IconComponent = option.icon;257 const IconComponent = option.icon;
260 const isSelected = option.value === driver;
261 return (258 return (
262 <RadioPrimitive.Root259 <RadioPrimitive.Root
263 key={option.value}260 key={option.value}
264 value={option.value}261 value={option.value}
265 className={cn(262 className="relative flex cursor-pointer items-center gap-3 rounded-lg bg-card px-3 py-3 text-left text-muted-foreground outline-none ring-1 ring-black/5 hover:bg-zinc-50 focus-visible:ring-2 focus-visible:ring-ring data-checked:bg-primary/8 data-checked:text-foreground data-checked:ring-2 data-checked:ring-primary data-checked:hover:bg-primary/8 dark:bg-white/3 dark:ring-white/5 dark:hover:bg-white/5 dark:data-checked:bg-primary/15 dark:data-checked:ring-primary dark:data-checked:hover:bg-primary/15"
266 "relative flex cursor-pointer items-center gap-3 rounded-lg border px-3 py-3 text-left outline-none transition-[background-color,border-color,box-shadow]",
267 "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background",
268 isSelected
269 ? "border-primary bg-background shadow-sm ring-2 ring-primary/35"
270 : "border-border bg-background hover:border-foreground/20 hover:bg-muted/50",
271 )}
272 >263 >
273 <IconComponent className="size-5 shrink-0" aria-hidden />264 <IconComponent className="size-4 shrink-0" aria-hidden />
274 <span className="min-w-0 flex-1 truncate text-sm font-medium text-foreground">265 <span className="min-w-0 flex-1 truncate text-sm font-medium text-foreground">
275 {option.label}266 {option.label}
276 </span>267 </span>
268 <RadioPrimitive.Indicator
269 className="grid size-5 shrink-0 place-items-center rounded-full bg-primary text-primary-foreground"
270 aria-hidden
271 >
272 <CheckIcon className="size-3.5 shrink-0" />
273 </RadioPrimitive.Indicator>
277 {option.badgeLabel ? (274 {option.badgeLabel ? (
278 <Badge variant="warning" size="sm">275 <Badge variant="warning" size="sm">
279 {option.badgeLabel}276 {option.badgeLabel}
...@@ -290,11 +287,11 @@ export function AddProviderInstanceDialog({ open, onOpenChange }: AddProviderIns...@@ -290,11 +287,11 @@ export function AddProviderInstanceDialog({ open, onOpenChange }: AddProviderIns
290 value={option.value}287 value={option.value}
291 disabled288 disabled
292 className={cn(289 className={cn(
293 "relative flex cursor-not-allowed items-center gap-3 rounded-lg border border-border bg-background px-3 py-3 text-left opacity-55 outline-none",290 "relative flex cursor-not-allowed items-center gap-3 rounded-lg bg-card/60 px-3 py-3 text-left opacity-55 outline-none ring-1 ring-black/5 dark:bg-white/2 dark:ring-white/5",
294 )}291 )}
295 >292 >
296 <IconComponent293 <IconComponent
297 className="size-5 shrink-0 text-muted-foreground"294 className="size-4 shrink-0 text-muted-foreground"
298 aria-hidden295 aria-hidden
299 />296 />
300 <span className="min-w-0 flex-1 truncate text-sm font-medium text-foreground">297 <span className="min-w-0 flex-1 truncate text-sm font-medium text-foreground">
...@@ -409,7 +406,7 @@ export function AddProviderInstanceDialog({ open, onOpenChange }: AddProviderIns...@@ -409,7 +406,7 @@ export function AddProviderInstanceDialog({ open, onOpenChange }: AddProviderIns
409 </AnimatedHeight>406 </AnimatedHeight>
410 </div>407 </div>
411408
412 <DialogFooter className="border-t bg-transparent">409 <DialogFooter variant="bare">
413 <Button410 <Button
414 variant="outline"411 variant="outline"
415 size="sm"412 size="sm"
apps/web/src/components/settings/AddProviderInstanceWizardSteps.test.tsx+7-3
...@@ -9,6 +9,10 @@ interface StepButtonProps {...@@ -9,6 +9,10 @@ interface StepButtonProps {
9 readonly onClick: () => void;9 readonly onClick: () => void;
10}10}
1111
12interface StepListItemProps {
13 readonly children: ReactElement<StepButtonProps>;
14}
15
12function renderStepButtons(16function renderStepButtons(
13 currentStep: number,17 currentStep: number,
14 instanceIdError: string | null,18 instanceIdError: string | null,
...@@ -21,9 +25,9 @@ function renderStepButtons(...@@ -21,9 +25,9 @@ function renderStepButtons(
21 onNavigation,25 onNavigation,
22 });26 });
2327
24 return Children.toArray(header.props.children).filter(28 return Children.toArray(header.props.children)
25 (child): child is ReactElement<StepButtonProps> => isValidElement(child),29 .filter((child): child is ReactElement<StepListItemProps> => isValidElement(child))
26 );30 .map((item) => item.props.children);
27}31}
2832
29describe("AddProviderInstanceWizardSteps", () => {33describe("AddProviderInstanceWizardSteps", () => {
apps/web/src/components/settings/AddProviderInstanceWizardSteps.tsx+43-40
...@@ -21,50 +21,53 @@ export function AddProviderInstanceWizardSteps({...@@ -21,50 +21,53 @@ export function AddProviderInstanceWizardSteps({
21 onNavigation,21 onNavigation,
22}: AddProviderInstanceWizardStepsProps) {22}: AddProviderInstanceWizardStepsProps) {
23 return (23 return (
24 <div className="grid grid-cols-3 gap-2">24 <ol
25 className="grid grid-cols-3 gap-1 rounded-xl bg-zinc-25 p-1 ring-1 ring-black/5 dark:bg-white/4 dark:ring-white/5"
26 role="list"
27 >
25 {ADD_PROVIDER_WIZARD_STEPS.map((step, index) => (28 {ADD_PROVIDER_WIZARD_STEPS.map((step, index) => (
26 <button29 <li key={step} className="min-w-0">
27 key={step}30 <button
28 type="button"31 type="button"
29 className={cn(
30 "grid min-w-0 grid-cols-[1rem_minmax(0,1fr)] gap-x-2 rounded-lg border px-3 py-2 text-left",
31 index === currentStep
32 ? "border-primary bg-primary/10 ring-1 ring-primary/25"
33 : index < currentStep
34 ? "border-border bg-background"
35 : "border-border bg-muted/40",
36 )}
37 aria-current={index === currentStep ? "step" : undefined}
38 onClick={() =>
39 onNavigation(
40 resolveWizardNavigation(currentStep, index, ADD_PROVIDER_WIZARD_STEPS.length, {
41 instanceIdError,
42 }),
43 )
44 }
45 >
46 <span
47 className={cn(32 className={cn(
48 "row-span-2 mt-0.5 grid size-4 place-items-center rounded-full border",33 "flex w-full min-w-0 cursor-pointer items-center gap-2 rounded-lg px-2.5 py-2 text-left outline-none hover:bg-card focus-visible:ring-2 focus-visible:ring-ring max-sm:justify-center max-sm:px-2",
49 index < currentStep34 index === currentStep &&
50 ? "border-primary bg-primary text-primary-foreground"35 "bg-card text-foreground shadow-xs ring-1 ring-black/5 hover:bg-card dark:shadow-none dark:ring-white/5",
51 : index === currentStep
52 ? "border-primary bg-background"
53 : "border-muted-foreground/35 bg-background",
54 )}36 )}
55 aria-hidden37 aria-current={index === currentStep ? "step" : undefined}
38 aria-label={`${step}, step ${index + 1}${index < currentStep && summaries[index] ? `, ${summaries[index]}` : ""}`}
39 onClick={() =>
40 onNavigation(
41 resolveWizardNavigation(currentStep, index, ADD_PROVIDER_WIZARD_STEPS.length, {
42 instanceIdError,
43 }),
44 )
45 }
56 >46 >
57 {index < currentStep ? <CheckIcon className="size-3" /> : null}47 <span
58 </span>48 className={cn(
59 <span className="text-[10px] font-medium uppercase text-muted-foreground">49 "grid size-5 shrink-0 place-items-center rounded-full text-sm font-medium ring-1",
60 Step {index + 1}50 index < currentStep
61 </span>51 ? "bg-primary text-primary-foreground ring-primary"
62 <span className="truncate text-xs font-semibold text-foreground">52 : index === currentStep
63 {step}53 ? "bg-primary/10 text-primary ring-primary/30"
64 {index < currentStep && summaries[index] ? `: ${summaries[index]}` : ""}54 : "bg-card text-muted-foreground ring-black/10 dark:bg-white/5 dark:ring-white/10",
65 </span>55 )}
66 </button>56 aria-hidden
57 >
58 {index < currentStep ? <CheckIcon className="size-4 shrink-0" /> : index + 1}
59 </span>
60 <span
61 className={cn(
62 "min-w-0 truncate text-sm font-medium max-sm:hidden",
63 index === currentStep ? "text-foreground" : "text-muted-foreground",
64 )}
65 >
66 {step}
67 </span>
68 </button>
69 </li>
67 ))}70 ))}
68 </div>71 </ol>
69 );72 );
70}73}
apps/web/src/components/ui/alert-dialog.tsx+3-3
...@@ -19,7 +19,7 @@ function AlertDialogBackdrop({ className, ...props }: AlertDialogPrimitive.Backd...@@ -19,7 +19,7 @@ function AlertDialogBackdrop({ className, ...props }: AlertDialogPrimitive.Backd
19 <AlertDialogPrimitive.Backdrop19 <AlertDialogPrimitive.Backdrop
20 forceRender20 forceRender
21 className={cn(21 className={cn(
22 "fixed inset-0 z-50 bg-black/20 backdrop-blur-[2px] transition-all duration-200 data-ending-style:opacity-0 data-starting-style:opacity-0 dark:bg-black/55",22 "dialog-backdrop fixed inset-0 z-50 transition-all duration-200 data-ending-style:opacity-0 data-starting-style:opacity-0",
23 className,23 className,
24 )}24 )}
25 data-slot="alert-dialog-backdrop"25 data-slot="alert-dialog-backdrop"
...@@ -56,9 +56,9 @@ function AlertDialogPopup({...@@ -56,9 +56,9 @@ function AlertDialogPopup({
56 >56 >
57 <AlertDialogPrimitive.Popup57 <AlertDialogPrimitive.Popup
58 className={cn(58 className={cn(
59 "dialog-glass -translate-y-[calc(1.25rem*var(--nested-dialogs))] relative row-start-2 flex max-h-full min-h-0 w-full min-w-0 max-w-lg scale-[calc(1-0.1*var(--nested-dialogs))] flex-col rounded-2xl border border-border/70 text-popover-foreground opacity-[calc(1-0.1*var(--nested-dialogs))] transition-[scale,opacity,translate] duration-200 ease-in-out will-change-transform before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-2xl)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] data-nested:data-ending-style:translate-y-8 data-nested:data-starting-style:translate-y-8 data-nested-dialog-open:origin-top data-ending-style:scale-98 data-starting-style:scale-98 data-ending-style:opacity-0 data-starting-style:opacity-0 dark:before:shadow-[0_-1px_--theme(--color-white/6%)]",59 "dialog-glass -translate-y-[calc(1.25rem*var(--nested-dialogs))] relative row-start-2 flex max-h-full min-h-0 w-full min-w-0 max-w-lg scale-[calc(1-0.1*var(--nested-dialogs))] flex-col rounded-2xl border text-popover-foreground opacity-[calc(1-0.1*var(--nested-dialogs))] outline-none transition-[scale,opacity,translate] duration-200 ease-in-out will-change-transform data-nested:data-ending-style:translate-y-8 data-nested:data-starting-style:translate-y-8 data-nested-dialog-open:origin-top data-ending-style:scale-98 data-starting-style:scale-98 data-ending-style:opacity-0 data-starting-style:opacity-0",
60 bottomStickOnMobile &&60 bottomStickOnMobile &&
61 "max-sm:max-w-none max-sm:rounded-none max-sm:border-x-0 max-sm:border-t max-sm:border-b-0 max-sm:opacity-[calc(1-min(var(--nested-dialogs),1))] max-sm:data-ending-style:translate-y-4 max-sm:data-starting-style:translate-y-4 max-sm:before:hidden max-sm:before:rounded-none",61 "max-sm:max-w-none max-sm:rounded-none max-sm:border-x-0 max-sm:border-t max-sm:border-b-0 max-sm:opacity-[calc(1-min(var(--nested-dialogs),1))] max-sm:data-ending-style:translate-y-4 max-sm:data-starting-style:translate-y-4",
62 className,62 className,
63 )}63 )}
64 data-slot="alert-dialog-popup"64 data-slot="alert-dialog-popup"
apps/web/src/components/ui/button.tsx+1-1
...@@ -40,7 +40,7 @@ const buttonVariants = cva(...@@ -40,7 +40,7 @@ const buttonVariants = cva(
40 "border-transparent text-foreground data-pressed:bg-accent [:hover,[data-pressed]]:bg-accent [&_svg:not([class*='text-'])]:text-muted-foreground",40 "border-transparent text-foreground data-pressed:bg-accent [:hover,[data-pressed]]:bg-accent [&_svg:not([class*='text-'])]:text-muted-foreground",
41 link: "border-transparent underline-offset-4 [:hover,[data-pressed]]:underline",41 link: "border-transparent underline-offset-4 [:hover,[data-pressed]]:underline",
42 outline:42 outline:
43 "border-transparent bg-popover not-dark:bg-clip-padding text-foreground shadow-none dark:bg-input/32 [:hover,[data-pressed]]:bg-accent/50 dark:[:hover,[data-pressed]]:bg-input/64 [&_svg:not([class*='text-'])]:text-muted-foreground",43 "border-input bg-popover not-dark:bg-clip-padding text-foreground shadow-xs/5 not-disabled:not-active:not-data-pressed:before:shadow-[0_1px_--theme(--color-black/4%)] dark:bg-input/32 dark:not-disabled:before:shadow-[0_-1px_--theme(--color-white/2%)] dark:not-disabled:not-active:not-data-pressed:before:shadow-[0_-1px_--theme(--color-white/6%)] [:disabled,:active,[data-pressed]]:shadow-none [:hover,[data-pressed]]:bg-accent/50 dark:[:hover,[data-pressed]]:bg-input/64 [&_svg:not([class*='text-'])]:text-muted-foreground",
44 secondary:44 secondary:
45 "border-transparent bg-secondary text-secondary-foreground [:active,[data-pressed]]:bg-secondary/80 [:hover,[data-pressed]]:bg-secondary/90",45 "border-transparent bg-secondary text-secondary-foreground [:active,[data-pressed]]:bg-secondary/80 [:hover,[data-pressed]]:bg-secondary/90",
46 },46 },
apps/web/src/components/ui/command.tsx+2-2
...@@ -30,7 +30,7 @@ function CommandDialogBackdrop({ className, ...props }: CommandDialogPrimitive.B...@@ -30,7 +30,7 @@ function CommandDialogBackdrop({ className, ...props }: CommandDialogPrimitive.B
30 return (30 return (
31 <CommandDialogPrimitive.Backdrop31 <CommandDialogPrimitive.Backdrop
32 className={cn(32 className={cn(
33 "fixed inset-0 z-50 bg-black/20 backdrop-blur-[2px] transition-opacity duration-200 data-ending-style:opacity-0 data-starting-style:opacity-0 dark:bg-black/55",33 "dialog-backdrop fixed inset-0 z-50 transition-all duration-200 data-ending-style:opacity-0 data-starting-style:opacity-0",
34 className,34 className,
35 )}35 )}
36 data-slot="command-dialog-backdrop"36 data-slot="command-dialog-backdrop"
...@@ -66,7 +66,7 @@ function CommandDialogPopup({...@@ -66,7 +66,7 @@ function CommandDialogPopup({
66 <CommandDialogViewport>66 <CommandDialogViewport>
67 <CommandDialogPrimitive.Popup67 <CommandDialogPrimitive.Popup
68 className={cn(68 className={cn(
69 "chat-composer-glass command-palette-glass pointer-events-auto -translate-y-[calc(1.25rem*var(--nested-dialogs))] relative row-start-2 flex max-h-105 min-h-0 w-full min-w-0 max-w-xl scale-[calc(1-0.1*var(--nested-dialogs))] flex-col rounded-2xl bg-[color-mix(in_srgb,var(--background)_var(--glass-opacity),transparent)] text-foreground opacity-[calc(1-0.1*var(--nested-dialogs))] outline-none transition-[scale,opacity,translate] duration-200 ease-in-out will-change-transform data-nested:data-ending-style:translate-y-8 data-nested:data-starting-style:translate-y-8 data-nested-dialog-open:origin-top data-ending-style:scale-98 data-starting-style:scale-98 data-ending-style:opacity-0 data-starting-style:opacity-0 **:data-[slot=scroll-area-viewport]:data-has-overflow-y:pe-1",69 "dialog-glass pointer-events-auto -translate-y-[calc(1.25rem*var(--nested-dialogs))] relative row-start-2 flex max-h-105 min-h-0 w-full min-w-0 max-w-xl scale-[calc(1-0.1*var(--nested-dialogs))] flex-col rounded-2xl border text-foreground opacity-[calc(1-0.1*var(--nested-dialogs))] outline-none transition-[scale,opacity,translate] duration-200 ease-in-out will-change-transform data-nested:data-ending-style:translate-y-8 data-nested:data-starting-style:translate-y-8 data-nested-dialog-open:origin-top data-ending-style:scale-98 data-starting-style:scale-98 data-ending-style:opacity-0 data-starting-style:opacity-0 **:data-[slot=scroll-area-viewport]:data-has-overflow-y:pe-1",
70 className,70 className,
71 )}71 )}
72 data-slot="command-dialog-popup"72 data-slot="command-dialog-popup"
apps/web/src/components/ui/dialog.tsx+4-5
...@@ -25,7 +25,7 @@ function DialogBackdrop({ className, ...props }: DialogPrimitive.Backdrop.Props)...@@ -25,7 +25,7 @@ function DialogBackdrop({ className, ...props }: DialogPrimitive.Backdrop.Props)
25 <DialogPrimitive.Backdrop25 <DialogPrimitive.Backdrop
26 forceRender26 forceRender
27 className={cn(27 className={cn(
28 "fixed inset-0 z-50 bg-black/20 backdrop-blur-[2px] transition-all duration-200 data-ending-style:opacity-0 data-starting-style:opacity-0 dark:bg-black/55",28 "dialog-backdrop fixed inset-0 z-50 transition-all duration-200 data-ending-style:opacity-0 data-starting-style:opacity-0",
29 className,29 className,
30 )}30 )}
31 data-slot="dialog-backdrop"31 data-slot="dialog-backdrop"
...@@ -67,9 +67,9 @@ function DialogPopup({...@@ -67,9 +67,9 @@ function DialogPopup({
67 >67 >
68 <DialogPrimitive.Popup68 <DialogPrimitive.Popup
69 className={cn(69 className={cn(
70 "dialog-glass -translate-y-[calc(1.25rem*var(--nested-dialogs))] relative row-start-2 flex max-h-full min-h-0 w-full min-w-0 max-w-lg scale-[calc(1-0.1*var(--nested-dialogs))] flex-col rounded-2xl border border-border/70 text-popover-foreground opacity-[calc(1-0.1*var(--nested-dialogs))] transition-[scale,opacity,translate] duration-200 ease-in-out will-change-transform before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-2xl)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] data-nested:data-ending-style:translate-y-8 data-nested:data-starting-style:translate-y-8 data-nested-dialog-open:origin-top data-ending-style:scale-98 data-starting-style:scale-98 data-ending-style:opacity-0 data-starting-style:opacity-0 dark:before:shadow-[0_-1px_--theme(--color-white/6%)]",70 "dialog-glass -translate-y-[calc(1.25rem*var(--nested-dialogs))] relative row-start-2 flex max-h-full min-h-0 w-full min-w-0 max-w-lg scale-[calc(1-0.1*var(--nested-dialogs))] flex-col rounded-2xl border text-popover-foreground opacity-[calc(1-0.1*var(--nested-dialogs))] outline-none transition-[scale,opacity,translate] duration-200 ease-in-out will-change-transform data-nested:data-ending-style:translate-y-8 data-nested:data-starting-style:translate-y-8 data-nested-dialog-open:origin-top data-ending-style:scale-98 data-starting-style:scale-98 data-ending-style:opacity-0 data-starting-style:opacity-0",
71 bottomStickOnMobile &&71 bottomStickOnMobile &&
72 "max-sm:max-w-none max-sm:rounded-none max-sm:border-x-0 max-sm:border-t max-sm:border-b-0 max-sm:opacity-[calc(1-min(var(--nested-dialogs),1))] max-sm:data-ending-style:translate-y-4 max-sm:data-starting-style:translate-y-4 max-sm:before:hidden max-sm:before:rounded-none",72 "max-sm:max-w-none max-sm:rounded-none max-sm:border-x-0 max-sm:border-t max-sm:border-b-0 max-sm:opacity-[calc(1-min(var(--nested-dialogs),1))] max-sm:data-ending-style:translate-y-4 max-sm:data-starting-style:translate-y-4",
73 className,73 className,
74 )}74 )}
75 data-slot="dialog-popup"75 data-slot="dialog-popup"
...@@ -116,8 +116,7 @@ function DialogFooter({...@@ -116,8 +116,7 @@ function DialogFooter({
116 className={cn(116 className={cn(
117 "flex flex-col-reverse gap-2 px-6 sm:flex-row sm:justify-end sm:rounded-b-[calc(var(--radius-2xl)-1px)]",117 "flex flex-col-reverse gap-2 px-6 sm:flex-row sm:justify-end sm:rounded-b-[calc(var(--radius-2xl)-1px)]",
118 variant === "default" && "border-t bg-muted/72 py-4",118 variant === "default" && "border-t bg-muted/72 py-4",
119 variant === "bare" &&119 variant === "bare" && "py-4",
120 "in-[[data-slot=dialog-popup]:has([data-slot=dialog-panel])]:pt-3 pt-4 pb-6",
121 className,120 className,
122 )}121 )}
123 data-slot="dialog-footer"122 data-slot="dialog-footer"
apps/web/src/index.css+107-109
...@@ -77,7 +77,9 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil...@@ -77,7 +77,9 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil
7777
78:root {78:root {
79 --app-scrollbar-width: 6px;79 --app-scrollbar-width: 6px;
80 --glass-blur: 12px;
80 --glass-opacity: 80%;81 --glass-opacity: 80%;
82 --glass-saturation: 1.14;
81 --desktop-window-right-resize-inset: 0px;83 --desktop-window-right-resize-inset: 0px;
82 --workspace-topbar-height: 52px;84 --workspace-topbar-height: 52px;
83 --workspace-controls-top: 0px;85 --workspace-controls-top: 0px;
...@@ -88,6 +90,11 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil...@@ -88,6 +90,11 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil
88 --workspace-titlebar-control-gap: 0.75rem;90 --workspace-titlebar-control-gap: 0.75rem;
89}91}
9092
93.dark {
94 --glass-blur: 16px;
95 --glass-saturation: 1.08;
96}
97
91[data-slot="sidebar-wrapper"] {98[data-slot="sidebar-wrapper"] {
92 --workspace-titlebar-content-left: calc(99 --workspace-titlebar-content-left: calc(
93 var(--workspace-controls-left) + var(--workspace-titlebar-control-size) +100 var(--workspace-controls-left) + var(--workspace-titlebar-control-size) +
...@@ -108,6 +115,7 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil...@@ -108,6 +115,7 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil
108}115}
109116
110@theme inline {117@theme inline {
118 --color-zinc-25: oklch(99.2% 0 0);
111 --animate-skeleton: skeleton 2s -1s infinite linear;119 --animate-skeleton: skeleton 2s -1s infinite linear;
112 /* Duty-cycled indicator animations: long holds with stepped ramps, so the120 /* Duty-cycled indicator animations: long holds with stepped ramps, so the
113 compositor updates discrete frames instead of every vsync. */121 compositor updates discrete frames instead of every vsync. */
...@@ -365,25 +373,39 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil...@@ -365,25 +373,39 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil
365373
366 .chat-composer-glass {374 .chat-composer-glass {
367 background: color-mix(in srgb, var(--background) var(--glass-opacity), transparent);375 background: color-mix(in srgb, var(--background) var(--glass-opacity), transparent);
368 -webkit-backdrop-filter: blur(16px);376 -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
369 backdrop-filter: blur(16px);377 backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
370 }378 }
371379
372 .chat-composer-glass-host {380 .chat-composer-glass-host {
373 --chat-composer-glass-surface: color-mix(in srgb, var(--background) 96%, var(--color-black));381 --chat-composer-glass-surface: var(--card);
382
383 position: relative;
384 isolation: isolate;
385 box-shadow:
386 0 0 0 1px rgb(0 0 0 / 8%),
387 0 12px 28px -18px rgb(0 0 0 / 40%);
388 }
374389
390 .chat-composer-glass-host::before {
391 position: absolute;
392 z-index: -1;
393 inset: 0;
394 border-radius: inherit;
375 background: color-mix(395 background: color-mix(
376 in srgb,396 in srgb,
377 var(--chat-composer-glass-surface) var(--glass-opacity),397 var(--chat-composer-glass-surface) var(--glass-opacity),
378 transparent398 transparent
379 );399 );
380 box-shadow: 0 12px 28px -18px rgb(0 0 0 / 40%);400 -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
381 -webkit-backdrop-filter: blur(16px);401 backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
382 backdrop-filter: blur(16px);402 content: "";
383 }403 }
384404
385 .dark .chat-composer-glass-host {405 .dark .chat-composer-glass-host {
386 --chat-composer-glass-surface: color-mix(in srgb, var(--background) 96%, var(--color-white));406 --chat-composer-glass-surface: color-mix(in srgb, var(--background) 84%, var(--color-white));
407
408 box-shadow: 0 0 0 1px rgb(255 255 255 / 10%);
387 }409 }
388410
389 .alert-glass {411 .alert-glass {
...@@ -395,8 +417,8 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil...@@ -395,8 +417,8 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil
395 color-mix(in srgb, var(--alert-glass-tint) 4%, transparent)417 color-mix(in srgb, var(--alert-glass-tint) 4%, transparent)
396 ),418 ),
397 color-mix(in srgb, var(--background) var(--glass-opacity), transparent) !important;419 color-mix(in srgb, var(--background) var(--glass-opacity), transparent) !important;
398 -webkit-backdrop-filter: blur(16px) saturate(1.1);420 -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
399 backdrop-filter: blur(16px) saturate(1.1);421 backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
400 }422 }
401423
402 .alert-glass[data-variant="error"] {424 .alert-glass[data-variant="error"] {
...@@ -416,12 +438,16 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil...@@ -416,12 +438,16 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil
416 }438 }
417439
418 .dropdown-glass,440 .dropdown-glass,
419 .dialog-glass,441 .dialog-glass {
420 .workflow-dialog-surface,
421 .command-palette-dialog-surface {
422 background: color-mix(in srgb, var(--background) var(--glass-opacity), transparent);442 background: color-mix(in srgb, var(--background) var(--glass-opacity), transparent);
423 -webkit-backdrop-filter: blur(16px);443 -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
424 backdrop-filter: blur(16px);444 backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
445 }
446
447 .dialog-backdrop {
448 background: color-mix(in srgb, var(--background) 60%, transparent);
449 -webkit-backdrop-filter: blur(4px);
450 backdrop-filter: blur(4px);
425 }451 }
426452
427 .dropdown-glass {453 .dropdown-glass {
...@@ -429,30 +455,12 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil...@@ -429,30 +455,12 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil
429 box-shadow: 0 16px 40px -18px rgb(0 0 0 / 55%);455 box-shadow: 0 16px 40px -18px rgb(0 0 0 / 55%);
430 }456 }
431457
432 .command-palette-glass {458 .dialog-glass {
433 box-shadow: 0 24px 64px -24px rgb(0 0 0 / 65%);
434 }
435
436 .command-menu-surface {
437 background: color-mix(in srgb, var(--background) var(--glass-opacity), transparent);
438 -webkit-backdrop-filter: blur(16px);
439 backdrop-filter: blur(16px);
440 box-shadow: 0 24px 64px -24px rgb(0 0 0 / 65%);
441 }
442
443 .workflow-dialog-surface {
444 border-color: color-mix(in srgb, var(--foreground) 10%, transparent);459 border-color: color-mix(in srgb, var(--foreground) 10%, transparent);
445 box-shadow: 0 24px 64px -24px rgb(0 0 0 / 65%);460 box-shadow: 0 24px 64px -24px rgb(0 0 0 / 65%);
446 }461 }
447462
448 .command-palette-dialog-surface {463 .dark .dropdown-glass {
449 border-color: color-mix(in srgb, var(--foreground) 10%, transparent) !important;
450 background: color-mix(in srgb, var(--background) var(--glass-opacity), transparent) !important;
451 box-shadow: 0 24px 64px -24px rgb(0 0 0 / 65%) !important;
452 }
453
454 .dark .dropdown-glass,
455 .dark .dialog-glass {
456 box-shadow: 0 18px 44px -18px rgb(0 0 0 / 80%);464 box-shadow: 0 18px 44px -18px rgb(0 0 0 / 80%);
457 }465 }
458466
...@@ -460,33 +468,15 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil...@@ -460,33 +468,15 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil
460 background: color-mix(in srgb, var(--background) var(--glass-opacity), transparent);468 background: color-mix(in srgb, var(--background) var(--glass-opacity), transparent);
461 }469 }
462470
463 .dark .command-menu-surface,471 .dark .dialog-glass {
464 .dark .command-palette-glass,472 border-color: color-mix(in srgb, var(--color-white) 14%, transparent);
465 .dark .workflow-dialog-surface {473 box-shadow:
466 box-shadow: none;474 inset 0 1px rgb(255 255 255 / 4%),
467 }475 0 24px 72px -20px rgb(0 0 0 / 90%);
468
469 .dark .command-palette-dialog-surface {
470 box-shadow: none !important;
471 }
472
473 .dark
474 .command-palette-dialog-surface
475 :is([data-slot="input-control"], [data-slot="textarea-control"]) {
476 background: var(--secondary) !important;
477 }
478
479 .dark
480 .command-palette-dialog-surface
481 :is([data-slot="input-control"], [data-slot="textarea-control"]):not(:has(:focus-visible)) {
482 border-color: transparent !important;
483 box-shadow: none !important;
484 }476 }
485477
486 .dark478 .dark .dialog-backdrop {
487 .command-palette-dialog-surface479 background: color-mix(in srgb, var(--background) 64%, transparent);
488 :is([data-slot="input-control"], [data-slot="textarea-control"])::before {
489 box-shadow: none !important;
490 }480 }
491481
492 .glass-opacity-slider {482 .glass-opacity-slider {
...@@ -622,15 +612,12 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil...@@ -622,15 +612,12 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil
622 background: var(--background) !important;612 background: var(--background) !important;
623 }613 }
624614
625 .chat-composer-glass-host {615 .chat-composer-glass-host::before {
626 background: var(--chat-composer-glass-surface);616 background: var(--chat-composer-glass-surface);
627 }617 }
628618
629 .dialog-glass,619 .dialog-glass,
630 .dropdown-glass,620 .dropdown-glass {
631 .command-menu-surface,
632 .command-palette-dialog-surface,
633 .workflow-dialog-surface {
634 background: var(--popover) !important;621 background: var(--popover) !important;
635 }622 }
636 }623 }
...@@ -673,25 +660,25 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil...@@ -673,25 +660,25 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil
673:root {660:root {
674 color-scheme: light;661 color-scheme: light;
675 --radius: 0.625rem;662 --radius: 0.625rem;
676 --background: var(--color-white);663 --background: var(--color-zinc-25);
677 --app-chrome-background: var(--background);664 --app-chrome-background: var(--background);
678 --surface-raised: var(--secondary);665 --surface-raised: color-mix(in srgb, var(--card) 20%, transparent);
679 --foreground: var(--color-neutral-800);666 --foreground: var(--color-zinc-800);
680 --card: var(--color-white);667 --card: var(--color-white);
681 --card-foreground: var(--color-neutral-800);668 --card-foreground: var(--color-zinc-800);
682 --popover: var(--color-white);669 --popover: var(--color-white);
683 --popover-foreground: var(--color-neutral-800);670 --popover-foreground: var(--color-zinc-800);
684 --primary: oklch(0.488 0.217 264);671 --primary: oklch(0.488 0.217 264);
685 --primary-foreground: var(--color-white);672 --primary-foreground: var(--color-white);
686 --secondary: --alpha(var(--color-black) / 4%);673 --secondary: var(--color-zinc-50);
687 --secondary-foreground: var(--color-neutral-800);674 --secondary-foreground: var(--color-zinc-800);
688 --muted: --alpha(var(--color-black) / 4%);675 --muted: var(--color-zinc-50);
689 --muted-foreground: color-mix(in srgb, var(--color-neutral-500) 90%, var(--color-black));676 --muted-foreground: var(--color-zinc-500);
690 --accent: --alpha(var(--color-black) / 4%);677 --accent: var(--color-zinc-100);
691 --accent-foreground: var(--color-neutral-800);678 --accent-foreground: var(--color-zinc-900);
692 --destructive: var(--color-red-500);679 --destructive: var(--color-red-500);
693 --border: --alpha(var(--color-black) / 8%);680 --border: var(--color-zinc-200);
694 --input: --alpha(var(--color-black) / 10%);681 --input: var(--color-zinc-300);
695 --ring: oklch(0.488 0.217 264);682 --ring: oklch(0.488 0.217 264);
696 --destructive-foreground: var(--color-red-700);683 --destructive-foreground: var(--color-red-700);
697 --info: var(--color-blue-500);684 --info: var(--color-blue-500);
...@@ -700,16 +687,17 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil...@@ -700,16 +687,17 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil
700 --success-foreground: var(--color-emerald-700);687 --success-foreground: var(--color-emerald-700);
701 --warning: var(--color-amber-500);688 --warning: var(--color-amber-500);
702 --warning-foreground: var(--color-amber-700);689 --warning-foreground: var(--color-amber-700);
703 /* Fallbacks for standalone sidebar primitives. App sidebars override these690 /* Keep every sidebar primitive on the same light surface hierarchy, including
704 roles on their own root so the surface hierarchy stays out of page content. */691 portaled mobile sheets and settings navigation outside the app sidebar. */
705 --sidebar: var(--card);692 --sidebar: var(--color-zinc-50);
706 --sidebar-foreground: var(--foreground);693 --sidebar-foreground: var(--foreground);
707 --sidebar-muted-foreground: var(--muted-foreground);694 --sidebar-muted-foreground: var(--muted-foreground);
708 --sidebar-control-surface: var(--accent);695 --sidebar-control-surface: var(--color-zinc-100);
709 --sidebar-row-hover: var(--accent);696 --sidebar-row-hover: var(--color-zinc-25);
710 --sidebar-row-active: var(--accent);697 --sidebar-row-active: var(--color-white);
711 --sidebar-row-selected: var(--muted);698 --sidebar-row-selected: var(--color-white);
712 --sidebar-border: var(--border);699 --sidebar-border: var(--border);
700 --sidebar-stage-fade: var(--sidebar);
713701
714 @variant dark {702 @variant dark {
715 color-scheme: dark;703 color-scheme: dark;
...@@ -742,34 +730,44 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil...@@ -742,34 +730,44 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil
742 --success-foreground: var(--color-emerald-400);730 --success-foreground: var(--color-emerald-400);
743 --warning: var(--color-amber-500);731 --warning: var(--color-amber-500);
744 --warning-foreground: var(--color-amber-400);732 --warning-foreground: var(--color-amber-400);
745 }733 --sidebar: var(--card);
746}734 --sidebar-foreground: var(--foreground);
747735 --sidebar-muted-foreground: var(--muted-foreground);
748/* Give both navigation implementations the same cool graphite surface. The736 --sidebar-control-surface: var(--muted);
749 version attribute remains useful for layout-specific styling, but color is737 --sidebar-row-hover: var(--accent);
750 shared so switching the beta does not switch visual systems. */738 --sidebar-row-active: var(--accent);
739 --sidebar-row-selected: var(--muted);
740 --sidebar-border: var(--border);
741 --sidebar-stage-fade: var(--card);
742 }
743}
744
745/* Keep both navigation implementations on the same quiet zinc hierarchy:
746 zinc-50 navigation, zinc-25 hover, and white selected/raised surfaces.
747 The version attribute remains useful for layout-specific styling without
748 changing the color system when the beta is toggled. */
751[data-sidebar-version="v1"],749[data-sidebar-version="v1"],
752[data-sidebar-version="v2"] {750[data-sidebar-version="v2"] {
753 --background: oklch(0.965 0.009 265);751 --background: var(--color-zinc-25);
754 --foreground: oklch(0.27 0.025 265);752 --foreground: var(--color-zinc-800);
755 --card: oklch(0.945 0.014 265);753 --card: var(--color-white);
756 --card-foreground: var(--foreground);754 --card-foreground: var(--color-zinc-800);
757 --accent: oklch(0.875 0.008 265);755 --accent: var(--color-zinc-100);
758 --accent-foreground: oklch(0.24 0.012 265);756 --accent-foreground: var(--color-zinc-900);
759 --muted: oklch(0.9 0.018 265);757 --muted: var(--color-zinc-50);
760 --muted-foreground: oklch(0.49 0.03 265);758 --muted-foreground: var(--color-zinc-500);
761 --border: oklch(0.72 0.025 265 / 28%);759 --border: var(--color-zinc-200);
762 --input: oklch(0.62 0.03 265 / 28%);760 --input: var(--color-zinc-300);
763 --sidebar: var(--card);761 --sidebar: var(--color-zinc-50);
764 --sidebar-foreground: var(--foreground);762 --sidebar-foreground: var(--color-zinc-800);
765 --sidebar-muted-foreground: var(--muted-foreground);763 --sidebar-muted-foreground: var(--color-zinc-500);
766 --sidebar-control-surface: var(--muted);764 --sidebar-control-surface: var(--color-zinc-100);
767 --sidebar-row-hover: color-mix(in srgb, var(--foreground) 6%, transparent);765 --sidebar-row-hover: var(--color-zinc-25);
768 --sidebar-row-active: color-mix(in srgb, var(--foreground) 11%, transparent);766 --sidebar-row-active: var(--color-white);
769 --sidebar-row-selected: color-mix(in srgb, var(--foreground) 7%, transparent);767 --sidebar-row-selected: var(--color-white);
770 --sidebar-border: var(--border);768 --sidebar-border: var(--color-zinc-200);
771 --sidebar-stage-fade: var(--sidebar);769 --sidebar-stage-fade: var(--sidebar);
772 background-color: var(--card);770 background-color: var(--sidebar);
773}771}
774772
775.dark [data-sidebar-version="v1"],773.dark [data-sidebar-version="v1"],
packages/contracts/src/settings.test.ts+2-2
...@@ -38,12 +38,12 @@ describe("ClientSettings glass opacity", () => {...@@ -38,12 +38,12 @@ describe("ClientSettings glass opacity", () => {
38 expect(decodeClientSettings({}).glassOpacity).toBe(80);38 expect(decodeClientSettings({}).glassOpacity).toBe(80);
39 });39 });
4040
41 it.each([49, 101, 72.5])("rejects an invalid glass opacity: %s", (value) => {41 it.each([39, 101, 72.5])("rejects an invalid glass opacity: %s", (value) => {
42 expect(() => decodeClientSettings({ glassOpacity: value })).toThrow();42 expect(() => decodeClientSettings({ glassOpacity: value })).toThrow();
43 expect(() => decodeClientSettingsPatch({ glassOpacity: value })).toThrow();43 expect(() => decodeClientSettingsPatch({ glassOpacity: value })).toThrow();
44 });44 });
4545
46 it.each([50, 75, 100])("accepts a glass opacity within the supported range: %s", (value) => {46 it.each([40, 75, 100])("accepts a glass opacity within the supported range: %s", (value) => {
47 expect(decodeClientSettings({ glassOpacity: value }).glassOpacity).toBe(value);47 expect(decodeClientSettings({ glassOpacity: value }).glassOpacity).toBe(value);
48 expect(decodeClientSettingsPatch({ glassOpacity: value }).glassOpacity).toBe(value);48 expect(decodeClientSettingsPatch({ glassOpacity: value }).glassOpacity).toBe(value);
49 });49 });
packages/contracts/src/settings.ts+1-1
...@@ -48,7 +48,7 @@ export const SidebarAutoSettleAfterDays = Schema.Number.check(...@@ -48,7 +48,7 @@ export const SidebarAutoSettleAfterDays = Schema.Number.check(
48);48);
49export type SidebarAutoSettleAfterDays = typeof SidebarAutoSettleAfterDays.Type;49export type SidebarAutoSettleAfterDays = typeof SidebarAutoSettleAfterDays.Type;
50export const DEFAULT_SIDEBAR_AUTO_SETTLE_AFTER_DAYS: SidebarAutoSettleAfterDays = 3;50export const DEFAULT_SIDEBAR_AUTO_SETTLE_AFTER_DAYS: SidebarAutoSettleAfterDays = 3;
51export const MIN_GLASS_OPACITY = 50;51export const MIN_GLASS_OPACITY = 40;
52export const MAX_GLASS_OPACITY = 100;52export const MAX_GLASS_OPACITY = 100;
53export const GlassOpacity = Schema.Int.check(53export const GlassOpacity = Schema.Int.check(
54 Schema.isBetween({54 Schema.isBetween({