| author | |
| committer | |
| log | 14b6bfdf157c0ce020a5a55e1c8d3d68552931ef |
| tree | a37f0cfb3bdb47dda4e317809e62f8cbffd408f5 |
| parent | b6a2563db7c0cd1b2df56540852d48c3c96aeaac |
Co-authored-by: codex <codex@users.noreply.github.com>15 files changed, 263 insertions(+), 189 deletions(-)
apps/web/src/components/BranchToolbar.tsx+1-1| ... | @@ -248,7 +248,7 @@ export const BranchToolbar = memo(function BranchToolbar({ | ... | @@ -248,7 +248,7 @@ export const BranchToolbar = memo(function BranchToolbar({ |
| 248 | if (!hasActiveThread || !activeProject) return null; | 248 | if (!hasActiveThread || !activeProject) return null; |
| 249 | 249 | ||
| 250 | return ( | 250 | return ( |
| 251 | <div className="chat-composer-context-strip -mt-2 mx-auto flex w-[calc(100%-1.5rem)] max-w-[calc(48rem-1.5rem)] items-center gap-2 pt-3 pe-2 pb-1 ps-2"> | 251 | <div className="flex w-full items-center gap-2 border-t border-border/60 px-3 py-2"> |
| 252 | {isMobile ? ( | 252 | {isMobile ? ( |
| 253 | <MobileRunContextSelector | 253 | <MobileRunContextSelector |
| 254 | envLocked={envLocked} | 254 | envLocked={envLocked} |
apps/web/src/components/ChatView.tsx+117-117| ... | @@ -5569,125 +5569,125 @@ function ChatViewContent(props: ChatViewProps) { | ... | @@ -5569,125 +5569,125 @@ function ChatViewContent(props: ChatViewProps) { |
| 5569 | : undefined | 5569 | : undefined |
| 5570 | } | 5570 | } |
| 5571 | > | 5571 | > |
| 5572 | <div | 5572 | <div className="chat-composer-glass-host mx-auto w-full max-w-3xl overflow-hidden rounded-[22px]"> |
| 5573 | ref={attachDraftHeroComposerAnchorRef} | 5573 | <div ref={attachDraftHeroComposerAnchorRef} className="relative z-10 isolate"> |
| 5574 | className="relative z-10 isolate mx-auto w-full max-w-3xl before:pointer-events-none before:absolute before:inset-x-0 before:bottom-0 before:-z-10 before:h-2 before:rounded-b-[22px] before:bg-background" | 5574 | <ChatComposer |
| 5575 | > | 5575 | composerRef={composerRef} |
| 5576 | <ChatComposer | 5576 | composerDraftTarget={composerDraftTarget} |
| 5577 | composerRef={composerRef} | 5577 | environmentId={environmentId} |
| 5578 | composerDraftTarget={composerDraftTarget} | 5578 | routeKind={routeKind} |
| 5579 | environmentId={environmentId} | 5579 | routeThreadRef={routeThreadRef} |
| 5580 | routeKind={routeKind} | 5580 | draftId={draftId} |
| 5581 | routeThreadRef={routeThreadRef} | 5581 | activeThreadId={activeThreadId} |
| 5582 | draftId={draftId} | 5582 | activeThreadEnvironmentId={activeThread?.environmentId} |
| 5583 | activeThreadId={activeThreadId} | 5583 | activeThread={activeThread} |
| 5584 | activeThreadEnvironmentId={activeThread?.environmentId} | 5584 | isServerThread={isServerThread} |
| 5585 | activeThread={activeThread} | 5585 | isLocalDraftThread={isLocalDraftThread} |
| 5586 | isServerThread={isServerThread} | 5586 | forceExpandedOnMobile={forceExpandedMobileComposer && isDraftHeroState} |
| 5587 | isLocalDraftThread={isLocalDraftThread} | 5587 | projectSelectionRequired={isLocalDraftThread && activeProject === null} |
| 5588 | forceExpandedOnMobile={forceExpandedMobileComposer && isDraftHeroState} | 5588 | phase={phase} |
| 5589 | projectSelectionRequired={isLocalDraftThread && activeProject === null} | 5589 | isConnecting={isConnecting} |
| 5590 | phase={phase} | 5590 | isSendBusy={isSendBusy} |
| 5591 | isConnecting={isConnecting} | 5591 | isPreparingWorktree={isPreparingWorktree} |
| 5592 | isSendBusy={isSendBusy} | 5592 | environmentUnavailable={activeEnvironmentUnavailableState} |
| 5593 | isPreparingWorktree={isPreparingWorktree} | 5593 | activePendingApproval={activePendingApproval} |
| 5594 | environmentUnavailable={activeEnvironmentUnavailableState} | 5594 | pendingApprovals={pendingApprovals} |
| 5595 | activePendingApproval={activePendingApproval} | 5595 | pendingUserInputs={pendingUserInputs} |
| 5596 | pendingApprovals={pendingApprovals} | 5596 | activePendingProgress={activePendingProgress} |
| 5597 | pendingUserInputs={pendingUserInputs} | 5597 | activePendingResolvedAnswers={activePendingResolvedAnswers} |
| 5598 | activePendingProgress={activePendingProgress} | 5598 | activePendingIsResponding={activePendingIsResponding} |
| 5599 | activePendingResolvedAnswers={activePendingResolvedAnswers} | 5599 | activePendingDraftAnswers={activePendingDraftAnswers} |
| 5600 | activePendingIsResponding={activePendingIsResponding} | 5600 | activePendingQuestionIndex={activePendingQuestionIndex} |
| 5601 | activePendingDraftAnswers={activePendingDraftAnswers} | 5601 | respondingRequestIds={respondingRequestIds} |
| 5602 | activePendingQuestionIndex={activePendingQuestionIndex} | 5602 | showPlanFollowUpPrompt={showPlanFollowUpPrompt} |
| 5603 | respondingRequestIds={respondingRequestIds} | 5603 | activeProposedPlan={activeProposedPlan} |
| 5604 | showPlanFollowUpPrompt={showPlanFollowUpPrompt} | 5604 | activePlan={activePlan as { turnId?: TurnId } | null} |
| 5605 | activeProposedPlan={activeProposedPlan} | 5605 | sidebarProposedPlan={sidebarProposedPlan as { turnId?: TurnId } | null} |
| 5606 | activePlan={activePlan as { turnId?: TurnId } | null} | 5606 | planSidebarLabel={planSidebarLabel} |
| 5607 | sidebarProposedPlan={sidebarProposedPlan as { turnId?: TurnId } | null} | 5607 | planSidebarOpen={planSidebarOpen} |
| 5608 | planSidebarLabel={planSidebarLabel} | 5608 | runtimeMode={runtimeMode} |
| 5609 | planSidebarOpen={planSidebarOpen} | 5609 | interactionMode={interactionMode} |
| 5610 | runtimeMode={runtimeMode} | 5610 | lockedProvider={lockedProvider} |
| 5611 | interactionMode={interactionMode} | 5611 | providerStatuses={providerStatuses as ServerProvider[]} |
| 5612 | lockedProvider={lockedProvider} | 5612 | activeProjectDefaultModelSelection={activeProject?.defaultModelSelection} |
| 5613 | providerStatuses={providerStatuses as ServerProvider[]} | 5613 | activeThreadModelSelection={activeThread?.modelSelection} |
| 5614 | activeProjectDefaultModelSelection={activeProject?.defaultModelSelection} | 5614 | activeThreadActivities={activeThread?.activities} |
| 5615 | activeThreadModelSelection={activeThread?.modelSelection} | 5615 | resolvedTheme={resolvedTheme} |
| 5616 | activeThreadActivities={activeThread?.activities} | 5616 | settings={settings} |
| 5617 | resolvedTheme={resolvedTheme} | 5617 | keybindings={keybindings} |
| 5618 | settings={settings} | 5618 | terminalOpen={Boolean(terminalUiState.terminalOpen)} |
| 5619 | keybindings={keybindings} | 5619 | gitCwd={gitCwd} |
| 5620 | terminalOpen={Boolean(terminalUiState.terminalOpen)} | 5620 | promptRef={promptRef} |
| 5621 | gitCwd={gitCwd} | 5621 | composerImagesRef={composerImagesRef} |
| 5622 | promptRef={promptRef} | 5622 | composerTerminalContextsRef={composerTerminalContextsRef} |
| 5623 | composerImagesRef={composerImagesRef} | 5623 | composerElementContextsRef={composerElementContextsRef} |
| 5624 | composerTerminalContextsRef={composerTerminalContextsRef} | 5624 | onSend={onSend} |
| 5625 | composerElementContextsRef={composerElementContextsRef} | 5625 | onInterrupt={onInterrupt} |
| 5626 | onSend={onSend} | 5626 | onImplementPlanInNewThread={onImplementPlanInNewThread} |
| 5627 | onInterrupt={onInterrupt} | 5627 | onRespondToApproval={onRespondToApproval} |
| 5628 | onImplementPlanInNewThread={onImplementPlanInNewThread} | 5628 | onSelectActivePendingUserInputOption={ |
| 5629 | onRespondToApproval={onRespondToApproval} | 5629 | onSelectActivePendingUserInputOption |
| 5630 | onSelectActivePendingUserInputOption={onSelectActivePendingUserInputOption} | 5630 | } |
| 5631 | onAdvanceActivePendingUserInput={onAdvanceActivePendingUserInput} | 5631 | onAdvanceActivePendingUserInput={onAdvanceActivePendingUserInput} |
| 5632 | onPreviousActivePendingUserInputQuestion={ | 5632 | onPreviousActivePendingUserInputQuestion={ |
| 5633 | onPreviousActivePendingUserInputQuestion | 5633 | onPreviousActivePendingUserInputQuestion |
| 5634 | } | 5634 | } |
| 5635 | onChangeActivePendingUserInputCustomAnswer={ | 5635 | onChangeActivePendingUserInputCustomAnswer={ |
| 5636 | onChangeActivePendingUserInputCustomAnswer | 5636 | onChangeActivePendingUserInputCustomAnswer |
| 5637 | } | 5637 | } |
| 5638 | onProviderModelSelect={onProviderModelSelect} | 5638 | onProviderModelSelect={onProviderModelSelect} |
| 5639 | getModelDisabledReason={getModelDisabledReason} | 5639 | getModelDisabledReason={getModelDisabledReason} |
| 5640 | toggleInteractionMode={toggleInteractionMode} | 5640 | toggleInteractionMode={toggleInteractionMode} |
| 5641 | handleRuntimeModeChange={handleRuntimeModeChange} | 5641 | handleRuntimeModeChange={handleRuntimeModeChange} |
| 5642 | handleInteractionModeChange={handleInteractionModeChange} | 5642 | handleInteractionModeChange={handleInteractionModeChange} |
| 5643 | togglePlanSidebar={togglePlanSidebar} | 5643 | togglePlanSidebar={togglePlanSidebar} |
| 5644 | focusComposer={focusComposer} | 5644 | focusComposer={focusComposer} |
| 5645 | scheduleComposerFocus={scheduleComposerFocus} | 5645 | scheduleComposerFocus={scheduleComposerFocus} |
| 5646 | setThreadError={setThreadError} | 5646 | setThreadError={setThreadError} |
| 5647 | onExpandImage={onExpandTimelineImage} | 5647 | onExpandImage={onExpandTimelineImage} |
| 5648 | /> | 5648 | /> |
| 5649 | </div> | 5649 | </div> |
| 5650 | <div | 5650 | <div className="min-h-0"> |
| 5651 | className={cn( | 5651 | <div |
| 5652 | "min-h-0", | 5652 | data-terminal-open={terminalUiState.terminalOpen ? "true" : undefined} |
| 5653 | isDraftHeroState ? "absolute inset-x-0 top-full" : null, | 5653 | className="relative z-0" |
| 5654 | )} | 5654 | > |
| 5655 | > | 5655 | {isGitRepo && ( |
| 5656 | <div | 5656 | <div className="pointer-events-auto"> |
| 5657 | data-terminal-open={terminalUiState.terminalOpen ? "true" : undefined} | 5657 | <BranchToolbar |
| 5658 | className="chat-composer-lower-chrome relative z-0 pb-[calc(env(safe-area-inset-bottom)+1rem)] sm:pb-[calc(env(safe-area-inset-bottom)+1.25rem)]" | 5658 | environmentId={activeThread.environmentId} |
| 5659 | > | 5659 | threadId={activeThread.id} |
| 5660 | {isGitRepo && ( | 5660 | {...(routeKind === "draft" && draftId ? { draftId } : {})} |
| 5661 | <div className="pointer-events-auto"> | 5661 | onEnvModeChange={onEnvModeChange} |
| 5662 | <BranchToolbar | 5662 | startFromOrigin={startFromOrigin} |
| 5663 | environmentId={activeThread.environmentId} | 5663 | onStartFromOriginChange={onStartFromOriginChange} |
| 5664 | threadId={activeThread.id} | 5664 | {...(canOverrideServerThreadEnvMode |
| 5665 | {...(routeKind === "draft" && draftId ? { draftId } : {})} | 5665 | ? { effectiveEnvModeOverride: envMode } |
| 5666 | onEnvModeChange={onEnvModeChange} | 5666 | : {})} |
| 5667 | startFromOrigin={startFromOrigin} | 5667 | {...(canOverrideServerThreadEnvMode |
| 5668 | onStartFromOriginChange={onStartFromOriginChange} | 5668 | ? { |
| 5669 | {...(canOverrideServerThreadEnvMode | 5669 | activeThreadBranchOverride: activeThreadBranch, |
| 5670 | ? { effectiveEnvModeOverride: envMode } | 5670 | onActiveThreadBranchOverrideChange: |
| 5671 | : {})} | 5671 | setPendingServerThreadBranch, |
| 5672 | {...(canOverrideServerThreadEnvMode | 5672 | } |
| 5673 | ? { | 5673 | : {})} |
| 5674 | activeThreadBranchOverride: activeThreadBranch, | 5674 | envLocked={envLocked} |
| 5675 | onActiveThreadBranchOverrideChange: | 5675 | onComposerFocusRequest={scheduleComposerFocus} |
| 5676 | setPendingServerThreadBranch, | 5676 | {...(canCheckoutPullRequestIntoThread |
| 5677 | } | 5677 | ? { onCheckoutPullRequestRequest: openPullRequestDialog } |
| 5678 | : {})} | 5678 | : {})} |
| 5679 | envLocked={envLocked} | 5679 | {...(hasMultipleEnvironments ? { onEnvironmentChange } : {})} |
| 5680 | onComposerFocusRequest={scheduleComposerFocus} | 5680 | availableEnvironments={logicalProjectEnvironments} |
| 5681 | {...(canCheckoutPullRequestIntoThread | 5681 | /> |
| 5682 | ? { onCheckoutPullRequestRequest: openPullRequestDialog } | 5682 | </div> |
| 5683 | : {})} | 5683 | )} |
| 5684 | {...(hasMultipleEnvironments ? { onEnvironmentChange } : {})} | 5684 | </div> |
| 5685 | availableEnvironments={logicalProjectEnvironments} | ||
| 5686 | /> | ||
| 5687 | </div> | ||
| 5688 | )} | ||
| 5689 | </div> | 5685 | </div> |
| 5690 | </div> | 5686 | </div> |
| 5687 | <div | ||
| 5688 | aria-hidden | ||
| 5689 | className="h-[calc(env(safe-area-inset-bottom)+1rem)] sm:h-[calc(env(safe-area-inset-bottom)+1.25rem)]" | ||
| 5690 | /> | ||
| 5691 | </div> | 5691 | </div> |
| 5692 | </div> | 5692 | </div> |
| 5693 | </div> | 5693 | </div> |
apps/web/src/components/ProjectScriptsControl.tsx+3-6| ... | @@ -276,7 +276,7 @@ export default function ProjectScriptsControl({ | ... | @@ -276,7 +276,7 @@ export default function ProjectScriptsControl({ |
| 276 | > | 276 | > |
| 277 | <ChevronDownIcon className="size-4" /> | 277 | <ChevronDownIcon className="size-4" /> |
| 278 | </MenuTrigger> | 278 | </MenuTrigger> |
| 279 | <MenuPopup align="end" className="command-menu-surface"> | 279 | <MenuPopup align="end"> |
| 280 | {scripts.map((script) => { | 280 | {scripts.map((script) => { |
| 281 | const shortcutLabel = shortcutLabelForCommand( | 281 | const shortcutLabel = shortcutLabelForCommand( |
| 282 | keybindings, | 282 | keybindings, |
| ... | @@ -364,10 +364,7 @@ export default function ProjectScriptsControl({ | ... | @@ -364,10 +364,7 @@ export default function ProjectScriptsControl({ |
| 364 | }} | 364 | }} |
| 365 | open={dialogOpen} | 365 | open={dialogOpen} |
| 366 | > | 366 | > |
| 367 | <DialogPopup | 367 | <DialogPopup className="before:hidden"> |
| 368 | backdropClassName="bg-black/40 backdrop-blur-none" | ||
| 369 | className="command-palette-dialog-surface workflow-dialog-surface before:hidden" | ||
| 370 | > | ||
| 371 | <DialogHeader> | 368 | <DialogHeader> |
| 372 | <DialogTitle>{isEditing ? "Edit Action" : "Add Action"}</DialogTitle> | 369 | <DialogTitle>{isEditing ? "Edit Action" : "Add Action"}</DialogTitle> |
| 373 | <DialogDescription> | 370 | <DialogDescription> |
| ... | @@ -392,7 +389,7 @@ export default function ProjectScriptsControl({ | ... | @@ -392,7 +389,7 @@ export default function ProjectScriptsControl({ |
| 392 | > | 389 | > |
| 393 | <ScriptIcon icon={icon} className="size-4.5" /> | 390 | <ScriptIcon icon={icon} className="size-4.5" /> |
| 394 | </PopoverTrigger> | 391 | </PopoverTrigger> |
| 395 | <PopoverPopup align="start" className="command-menu-surface"> | 392 | <PopoverPopup align="start"> |
| 396 | <div className="grid grid-cols-3 gap-2"> | 393 | <div className="grid grid-cols-3 gap-2"> |
| 397 | {SCRIPT_ICONS.map((entry) => { | 394 | {SCRIPT_ICONS.map((entry) => { |
| 398 | const isSelected = entry.id === icon; | 395 | const isSelected = entry.id === icon; |
apps/web/src/components/chat/ChatComposer.tsx+1-1| ... | @@ -2154,7 +2154,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) | ... | @@ -2154,7 +2154,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) |
| 2154 | ref={composerSurfaceRef} | 2154 | ref={composerSurfaceRef} |
| 2155 | data-chat-composer-mobile-collapsed={isComposerCollapsedMobile ? "true" : "false"} | 2155 | data-chat-composer-mobile-collapsed={isComposerCollapsedMobile ? "true" : "false"} |
| 2156 | className={cn( | 2156 | className={cn( |
| 2157 | "chat-composer-glass rounded-[20px] transition-[background-color] duration-200", | 2157 | "rounded-[20px] transition-[background-color] duration-200", |
| 2158 | isDragOverComposer ? "bg-accent/45 ring-1 ring-primary/70" : null, | 2158 | isDragOverComposer ? "bg-accent/45 ring-1 ring-primary/70" : null, |
| 2159 | projectSelectionRequired ? "opacity-75" : null, | 2159 | projectSelectionRequired ? "opacity-75" : null, |
| 2160 | composerProviderState.composerSurfaceClassName, | 2160 | composerProviderState.composerSurfaceClassName, |
apps/web/src/components/settings/AddProviderInstanceDialog.tsx+4-4| ... | @@ -222,8 +222,8 @@ export function AddProviderInstanceDialog({ open, onOpenChange }: AddProviderIns | ... | @@ -222,8 +222,8 @@ export function AddProviderInstanceDialog({ open, onOpenChange }: AddProviderIns |
| 222 | return ( | 222 | return ( |
| 223 | <Dialog open={open} onOpenChange={onOpenChange}> | 223 | <Dialog open={open} onOpenChange={onOpenChange}> |
| 224 | <DialogPopup className="max-w-xl overflow-hidden"> | 224 | <DialogPopup className="max-w-xl overflow-hidden"> |
| 225 | <div className="flex min-h-0 flex-col overflow-hidden border-foreground/10 bg-background shadow-2xl"> | 225 | <div className="flex min-h-0 flex-col overflow-hidden"> |
| 226 | <DialogHeader className="border-b border-border/70 bg-background"> | 226 | <DialogHeader className="border-b border-border/70"> |
| 227 | <DialogTitle>Add provider instance</DialogTitle> | 227 | <DialogTitle>Add provider instance</DialogTitle> |
| 228 | <DialogDescription> | 228 | <DialogDescription> |
| 229 | Configure an additional provider instance — for example, a second Codex install | 229 | Configure an additional provider instance — for example, a second Codex install |
| ... | @@ -239,7 +239,7 @@ export function AddProviderInstanceDialog({ open, onOpenChange }: AddProviderIns | ... | @@ -239,7 +239,7 @@ export function AddProviderInstanceDialog({ open, onOpenChange }: AddProviderIns |
| 239 | 239 | ||
| 240 | <div | 240 | <div |
| 241 | data-slot="dialog-panel" | 241 | data-slot="dialog-panel" |
| 242 | className="space-y-4 border-b border-border/70 bg-muted/20 px-6 py-5" | 242 | className="space-y-4 border-b border-border/70 bg-foreground/[0.025] px-6 py-5" |
| 243 | > | 243 | > |
| 244 | <AnimatedHeight> | 244 | <AnimatedHeight> |
| 245 | <div className={cn("grid gap-2", wizardStep !== 0 && "hidden")}> | 245 | <div className={cn("grid gap-2", wizardStep !== 0 && "hidden")}> |
| ... | @@ -409,7 +409,7 @@ export function AddProviderInstanceDialog({ open, onOpenChange }: AddProviderIns | ... | @@ -409,7 +409,7 @@ export function AddProviderInstanceDialog({ open, onOpenChange }: AddProviderIns |
| 409 | </AnimatedHeight> | 409 | </AnimatedHeight> |
| 410 | </div> | 410 | </div> |
| 411 | 411 | ||
| 412 | <DialogFooter className="border-t bg-background"> | 412 | <DialogFooter className="border-t bg-transparent"> |
| 413 | <Button | 413 | <Button |
| 414 | variant="outline" | 414 | variant="outline" |
| 415 | size="sm" | 415 | size="sm" |
apps/web/src/components/settings/SettingsPanels.tsx+53-1| ... | @@ -18,7 +18,11 @@ import { | ... | @@ -18,7 +18,11 @@ import { |
| 18 | settlePromise, | 18 | settlePromise, |
| 19 | squashAtomCommandFailure, | 19 | squashAtomCommandFailure, |
| 20 | } from "@t3tools/client-runtime/state/runtime"; | 20 | } from "@t3tools/client-runtime/state/runtime"; |
| 21 | import { DEFAULT_UNIFIED_SETTINGS } from "@t3tools/contracts/settings"; | 21 | import { |
| 22 | DEFAULT_UNIFIED_SETTINGS, | ||
| 23 | MAX_GLASS_OPACITY, | ||
| 24 | MIN_GLASS_OPACITY, | ||
| 25 | } from "@t3tools/contracts/settings"; | ||
| 22 | import { createModelSelection } from "@t3tools/shared/model"; | 26 | import { createModelSelection } from "@t3tools/shared/model"; |
| 23 | import * as Arr from "effect/Array"; | 27 | import * as Arr from "effect/Array"; |
| 24 | import * as Duration from "effect/Duration"; | 28 | import * as Duration from "effect/Duration"; |
| ... | @@ -389,6 +393,7 @@ export function useSettingsRestore(onRestored?: () => void) { | ... | @@ -389,6 +393,7 @@ export function useSettingsRestore(onRestored?: () => void) { |
| 389 | const changedSettingLabels = useMemo( | 393 | const changedSettingLabels = useMemo( |
| 390 | () => [ | 394 | () => [ |
| 391 | ...(theme !== "system" ? ["Theme"] : []), | 395 | ...(theme !== "system" ? ["Theme"] : []), |
| 396 | ...(settings.glassOpacity !== DEFAULT_UNIFIED_SETTINGS.glassOpacity ? ["Glass opacity"] : []), | ||
| 392 | ...(settings.timestampFormat !== DEFAULT_UNIFIED_SETTINGS.timestampFormat | 397 | ...(settings.timestampFormat !== DEFAULT_UNIFIED_SETTINGS.timestampFormat |
| 393 | ? ["Time format"] | 398 | ? ["Time format"] |
| 394 | : []), | 399 | : []), |
| ... | @@ -440,6 +445,7 @@ export function useSettingsRestore(onRestored?: () => void) { | ... | @@ -440,6 +445,7 @@ export function useSettingsRestore(onRestored?: () => void) { |
| 440 | settings.defaultThreadEnvMode, | 445 | settings.defaultThreadEnvMode, |
| 441 | settings.newWorktreesStartFromOrigin, | 446 | settings.newWorktreesStartFromOrigin, |
| 442 | settings.diffIgnoreWhitespace, | 447 | settings.diffIgnoreWhitespace, |
| 448 | settings.glassOpacity, | ||
| 443 | settings.automaticGitFetchInterval, | 449 | settings.automaticGitFetchInterval, |
| 444 | settings.enableAssistantStreaming, | 450 | settings.enableAssistantStreaming, |
| 445 | settings.enableProviderUpdateChecks, | 451 | settings.enableProviderUpdateChecks, |
| ... | @@ -465,6 +471,7 @@ export function useSettingsRestore(onRestored?: () => void) { | ... | @@ -465,6 +471,7 @@ export function useSettingsRestore(onRestored?: () => void) { |
| 465 | timestampFormat: DEFAULT_UNIFIED_SETTINGS.timestampFormat, | 471 | timestampFormat: DEFAULT_UNIFIED_SETTINGS.timestampFormat, |
| 466 | wordWrap: DEFAULT_UNIFIED_SETTINGS.wordWrap, | 472 | wordWrap: DEFAULT_UNIFIED_SETTINGS.wordWrap, |
| 467 | diffIgnoreWhitespace: DEFAULT_UNIFIED_SETTINGS.diffIgnoreWhitespace, | 473 | diffIgnoreWhitespace: DEFAULT_UNIFIED_SETTINGS.diffIgnoreWhitespace, |
| 474 | glassOpacity: DEFAULT_UNIFIED_SETTINGS.glassOpacity, | ||
| 468 | sidebarThreadPreviewCount: DEFAULT_UNIFIED_SETTINGS.sidebarThreadPreviewCount, | 475 | sidebarThreadPreviewCount: DEFAULT_UNIFIED_SETTINGS.sidebarThreadPreviewCount, |
| 469 | autoOpenPlanSidebar: DEFAULT_UNIFIED_SETTINGS.autoOpenPlanSidebar, | 476 | autoOpenPlanSidebar: DEFAULT_UNIFIED_SETTINGS.autoOpenPlanSidebar, |
| 470 | enableAssistantStreaming: DEFAULT_UNIFIED_SETTINGS.enableAssistantStreaming, | 477 | enableAssistantStreaming: DEFAULT_UNIFIED_SETTINGS.enableAssistantStreaming, |
| ... | @@ -559,6 +566,51 @@ export function GeneralSettingsPanel() { | ... | @@ -559,6 +566,51 @@ export function GeneralSettingsPanel() { |
| 559 | } | 566 | } |
| 560 | /> | 567 | /> |
| 561 | 568 | ||
| 569 | <SettingsRow | ||
| 570 | title="Glass opacity" | ||
| 571 | description="Control how transparent glass surfaces are. Higher values make menus, dialogs, and the composer more solid." | ||
| 572 | resetAction={ | ||
| 573 | settings.glassOpacity !== DEFAULT_UNIFIED_SETTINGS.glassOpacity ? ( | ||
| 574 | <SettingResetButton | ||
| 575 | label="glass opacity" | ||
| 576 | onClick={() => | ||
| 577 | updateSettings({ glassOpacity: DEFAULT_UNIFIED_SETTINGS.glassOpacity }) | ||
| 578 | } | ||
| 579 | /> | ||
| 580 | ) : null | ||
| 581 | } | ||
| 582 | control={ | ||
| 583 | <div className="flex w-full items-center gap-3 sm:w-52"> | ||
| 584 | <input | ||
| 585 | aria-label="Glass opacity" | ||
| 586 | className="glass-opacity-slider min-w-0 flex-1" | ||
| 587 | id="glass-opacity" | ||
| 588 | max={MAX_GLASS_OPACITY} | ||
| 589 | min={MIN_GLASS_OPACITY} | ||
| 590 | onChange={(event) => { | ||
| 591 | const glassOpacity = Number(event.currentTarget.value); | ||
| 592 | if ( | ||
| 593 | Number.isInteger(glassOpacity) && | ||
| 594 | glassOpacity >= MIN_GLASS_OPACITY && | ||
| 595 | glassOpacity <= MAX_GLASS_OPACITY | ||
| 596 | ) { | ||
| 597 | updateSettings({ glassOpacity }); | ||
| 598 | } | ||
| 599 | }} | ||
| 600 | step={5} | ||
| 601 | type="range" | ||
| 602 | value={settings.glassOpacity} | ||
| 603 | /> | ||
| 604 | <output | ||
| 605 | className="w-10 text-right font-mono text-xs tabular-nums text-muted-foreground" | ||
| 606 | htmlFor="glass-opacity" | ||
| 607 | > | ||
| 608 | {settings.glassOpacity}% | ||
| 609 | </output> | ||
| 610 | </div> | ||
| 611 | } | ||
| 612 | /> | ||
| 613 | |||
| 562 | <SettingsRow | 614 | <SettingsRow |
| 563 | title="Time format" | 615 | title="Time format" |
| 564 | description="System default follows your browser or OS clock preference." | 616 | description="System default follows your browser or OS clock preference." |
apps/web/src/components/ui/alert-dialog.tsx+2-2| ... | @@ -19,7 +19,7 @@ function AlertDialogBackdrop({ className, ...props }: AlertDialogPrimitive.Backd | ... | @@ -19,7 +19,7 @@ function AlertDialogBackdrop({ className, ...props }: AlertDialogPrimitive.Backd |
| 19 | <AlertDialogPrimitive.Backdrop | 19 | <AlertDialogPrimitive.Backdrop |
| 20 | forceRender | 20 | forceRender |
| 21 | className={cn( | 21 | className={cn( |
| 22 | "fixed inset-0 z-50 bg-background/60 backdrop-blur-xs transition-all duration-200 data-ending-style:opacity-0 data-starting-style:opacity-0", | 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", |
| 23 | className, | 23 | className, |
| 24 | )} | 24 | )} |
| 25 | data-slot="alert-dialog-backdrop" | 25 | data-slot="alert-dialog-backdrop" |
| ... | @@ -56,7 +56,7 @@ function AlertDialogPopup({ | ... | @@ -56,7 +56,7 @@ function AlertDialogPopup({ |
| 56 | > | 56 | > |
| 57 | <AlertDialogPrimitive.Popup | 57 | <AlertDialogPrimitive.Popup |
| 58 | className={cn( | 58 | className={cn( |
| 59 | "-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 bg-popover not-dark:bg-clip-padding text-popover-foreground opacity-[calc(1-0.1*var(--nested-dialogs))] shadow-lg/5 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 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%)]", |
| 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 max-sm:before:hidden max-sm:before:rounded-none", |
| 62 | className, | 62 | className, |
apps/web/src/components/ui/command.tsx+1-1| ... | @@ -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.Backdrop | 31 | <CommandDialogPrimitive.Backdrop |
| 32 | className={cn( | 32 | className={cn( |
| 33 | "fixed inset-0 z-50 bg-black/40 transition-opacity duration-200 data-ending-style:opacity-0 data-starting-style:opacity-0", | 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", |
| 34 | className, | 34 | className, |
| 35 | )} | 35 | )} |
| 36 | data-slot="command-dialog-backdrop" | 36 | data-slot="command-dialog-backdrop" |
apps/web/src/components/ui/dialog.tsx+2-2| ... | @@ -25,7 +25,7 @@ function DialogBackdrop({ className, ...props }: DialogPrimitive.Backdrop.Props) | ... | @@ -25,7 +25,7 @@ function DialogBackdrop({ className, ...props }: DialogPrimitive.Backdrop.Props) |
| 25 | <DialogPrimitive.Backdrop | 25 | <DialogPrimitive.Backdrop |
| 26 | forceRender | 26 | forceRender |
| 27 | className={cn( | 27 | className={cn( |
| 28 | "fixed inset-0 z-50 bg-background/60 backdrop-blur-xs transition-all duration-200 data-ending-style:opacity-0 data-starting-style:opacity-0", | 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", |
| 29 | className, | 29 | className, |
| 30 | )} | 30 | )} |
| 31 | data-slot="dialog-backdrop" | 31 | data-slot="dialog-backdrop" |
| ... | @@ -67,7 +67,7 @@ function DialogPopup({ | ... | @@ -67,7 +67,7 @@ function DialogPopup({ |
| 67 | > | 67 | > |
| 68 | <DialogPrimitive.Popup | 68 | <DialogPrimitive.Popup |
| 69 | className={cn( | 69 | className={cn( |
| 70 | "-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 bg-popover not-dark:bg-clip-padding text-popover-foreground opacity-[calc(1-0.1*var(--nested-dialogs))] shadow-lg/5 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 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%)]", |
| 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 max-sm:before:hidden max-sm:before:rounded-none", |
| 73 | className, | 73 | className, |
apps/web/src/components/ui/menu.tsx+1-1| ... | @@ -42,7 +42,7 @@ function MenuPopup({ | ... | @@ -42,7 +42,7 @@ function MenuPopup({ |
| 42 | align={align} | 42 | align={align} |
| 43 | alignOffset={alignOffset} | 43 | alignOffset={alignOffset} |
| 44 | anchor={anchor} | 44 | anchor={anchor} |
| 45 | className="z-50" | 45 | className="z-[60]" |
| 46 | data-slot="menu-positioner" | 46 | data-slot="menu-positioner" |
| 47 | side={side} | 47 | side={side} |
| 48 | sideOffset={sideOffset} | 48 | sideOffset={sideOffset} |
apps/web/src/components/ui/popover.tsx+2-2| ... | @@ -42,14 +42,14 @@ function PopoverPopup({ | ... | @@ -42,14 +42,14 @@ function PopoverPopup({ |
| 42 | align={align} | 42 | align={align} |
| 43 | alignOffset={alignOffset} | 43 | alignOffset={alignOffset} |
| 44 | anchor={anchor} | 44 | anchor={anchor} |
| 45 | className="z-50 h-(--positioner-height) w-(--positioner-width) max-w-(--available-width) transition-[top,left,right,bottom,transform] data-instant:transition-none" | 45 | className="z-[60] h-(--positioner-height) w-(--positioner-width) max-w-(--available-width) transition-transform data-instant:transition-none" |
| 46 | data-slot="popover-positioner" | 46 | data-slot="popover-positioner" |
| 47 | side={side} | 47 | side={side} |
| 48 | sideOffset={sideOffset} | 48 | sideOffset={sideOffset} |
| 49 | > | 49 | > |
| 50 | <PopoverPrimitive.Popup | 50 | <PopoverPrimitive.Popup |
| 51 | className={cn( | 51 | className={cn( |
| 52 | "relative flex h-(--popup-height,auto) w-(--popup-width,auto) origin-(--transform-origin) rounded-lg border bg-popover not-dark:bg-clip-padding text-popover-foreground shadow-lg/5 outline-none transition-[width,height,scale,opacity] before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] has-data-[slot=calendar]:rounded-xl has-data-[slot=calendar]:before:rounded-[calc(var(--radius-xl)-1px)] data-starting-style:scale-98 data-starting-style:opacity-0 dark:before:shadow-[0_-1px_--theme(--color-white/6%)]", | 52 | "dropdown-glass relative flex h-(--popup-height,auto) w-(--popup-width,auto) origin-(--transform-origin) rounded-lg text-popover-foreground outline-none transition-[width,height,scale,opacity] before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] has-data-[slot=calendar]:rounded-xl has-data-[slot=calendar]:before:rounded-[calc(var(--radius-xl)-1px)] data-starting-style:scale-98 data-starting-style:opacity-0 dark:before:shadow-[0_-1px_--theme(--color-white/6%)]", |
| 53 | tooltipStyle && | 53 | tooltipStyle && |
| 54 | "w-fit text-balance rounded-md text-xs shadow-md/5 before:rounded-[calc(var(--radius-md)-1px)]", | 54 | "w-fit text-balance rounded-md text-xs shadow-md/5 before:rounded-[calc(var(--radius-md)-1px)]", |
| 55 | className, | 55 | className, |
apps/web/src/index.css+35-51| ... | @@ -77,6 +77,7 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil | ... | @@ -77,6 +77,7 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil |
| 77 | 77 | ||
| 78 | :root { | 78 | :root { |
| 79 | --app-scrollbar-width: 6px; | 79 | --app-scrollbar-width: 6px; |
| 80 | --glass-opacity: 80%; | ||
| 80 | --desktop-window-right-resize-inset: 0px; | 81 | --desktop-window-right-resize-inset: 0px; |
| 81 | --workspace-topbar-height: 52px; | 82 | --workspace-topbar-height: 52px; |
| 82 | --workspace-controls-top: 0px; | 83 | --workspace-controls-top: 0px; |
| ... | @@ -362,32 +363,29 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil | ... | @@ -362,32 +363,29 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil |
| 362 | } | 363 | } |
| 363 | 364 | ||
| 364 | .chat-composer-glass { | 365 | .chat-composer-glass { |
| 365 | background: var(--secondary); | 366 | background: color-mix(in srgb, var(--background) var(--glass-opacity), transparent); |
| 366 | -webkit-backdrop-filter: blur(16px); | 367 | -webkit-backdrop-filter: blur(16px); |
| 367 | backdrop-filter: blur(16px); | 368 | backdrop-filter: blur(16px); |
| 368 | } | 369 | } |
| 369 | 370 | ||
| 370 | .chat-composer-context-strip { | 371 | .chat-composer-glass-host { |
| 371 | position: relative; | 372 | background: color-mix(in srgb, var(--background) var(--glass-opacity), transparent); |
| 372 | isolation: isolate; | ||
| 373 | } | ||
| 374 | |||
| 375 | .chat-composer-context-strip::before { | ||
| 376 | position: absolute; | ||
| 377 | z-index: -1; | ||
| 378 | inset: 0; | ||
| 379 | border-radius: 0 0 16px 16px; | ||
| 380 | background: color-mix(in srgb, var(--card) 20%, transparent); | ||
| 381 | box-shadow: 0 12px 28px -18px rgb(0 0 0 / 40%); | 373 | box-shadow: 0 12px 28px -18px rgb(0 0 0 / 40%); |
| 382 | content: ""; | ||
| 383 | -webkit-backdrop-filter: blur(16px); | 374 | -webkit-backdrop-filter: blur(16px); |
| 384 | backdrop-filter: blur(16px); | 375 | backdrop-filter: blur(16px); |
| 385 | } | 376 | } |
| 386 | 377 | ||
| 387 | .dropdown-glass { | 378 | .dropdown-glass, |
| 388 | background: var(--secondary); | 379 | .dialog-glass, |
| 380 | .workflow-dialog-surface, | ||
| 381 | .command-palette-dialog-surface { | ||
| 382 | background: color-mix(in srgb, var(--background) var(--glass-opacity), transparent); | ||
| 389 | -webkit-backdrop-filter: blur(16px); | 383 | -webkit-backdrop-filter: blur(16px); |
| 390 | backdrop-filter: blur(16px); | 384 | backdrop-filter: blur(16px); |
| 385 | } | ||
| 386 | |||
| 387 | .dropdown-glass { | ||
| 388 | border: 1px solid color-mix(in srgb, var(--foreground) 10%, transparent); | ||
| 391 | box-shadow: 0 16px 40px -18px rgb(0 0 0 / 55%); | 389 | box-shadow: 0 16px 40px -18px rgb(0 0 0 / 55%); |
| 392 | } | 390 | } |
| 393 | 391 | ||
| ... | @@ -396,30 +394,25 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil | ... | @@ -396,30 +394,25 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil |
| 396 | } | 394 | } |
| 397 | 395 | ||
| 398 | .command-menu-surface { | 396 | .command-menu-surface { |
| 399 | background: var(--popover); | 397 | background: color-mix(in srgb, var(--background) var(--glass-opacity), transparent); |
| 400 | -webkit-backdrop-filter: none; | 398 | -webkit-backdrop-filter: blur(16px); |
| 401 | backdrop-filter: none; | 399 | backdrop-filter: blur(16px); |
| 402 | box-shadow: 0 24px 64px -24px rgb(0 0 0 / 65%); | 400 | box-shadow: 0 24px 64px -24px rgb(0 0 0 / 65%); |
| 403 | } | 401 | } |
| 404 | 402 | ||
| 405 | .workflow-dialog-surface { | 403 | .workflow-dialog-surface { |
| 406 | border-color: transparent; | 404 | border-color: color-mix(in srgb, var(--foreground) 10%, transparent); |
| 407 | background: var(--secondary); | ||
| 408 | -webkit-backdrop-filter: blur(16px); | ||
| 409 | backdrop-filter: blur(16px); | ||
| 410 | box-shadow: 0 24px 64px -24px rgb(0 0 0 / 65%); | 405 | box-shadow: 0 24px 64px -24px rgb(0 0 0 / 65%); |
| 411 | } | 406 | } |
| 412 | 407 | ||
| 413 | .command-palette-dialog-surface { | 408 | .command-palette-dialog-surface { |
| 414 | border-color: transparent !important; | 409 | border-color: color-mix(in srgb, var(--foreground) 10%, transparent) !important; |
| 415 | background: var(--secondary) !important; | 410 | background: color-mix(in srgb, var(--background) var(--glass-opacity), transparent) !important; |
| 416 | -webkit-backdrop-filter: blur(16px); | ||
| 417 | backdrop-filter: blur(16px); | ||
| 418 | box-shadow: 0 24px 64px -24px rgb(0 0 0 / 65%) !important; | 411 | box-shadow: 0 24px 64px -24px rgb(0 0 0 / 65%) !important; |
| 419 | } | 412 | } |
| 420 | 413 | ||
| 421 | .dark .dropdown-glass { | 414 | .dark .dropdown-glass, |
| 422 | background: color-mix(in srgb, var(--secondary) 80%, var(--background)); | 415 | .dark .dialog-glass { |
| 423 | box-shadow: 0 18px 44px -18px rgb(0 0 0 / 80%); | 416 | box-shadow: 0 18px 44px -18px rgb(0 0 0 / 80%); |
| 424 | } | 417 | } |
| 425 | 418 | ||
| ... | @@ -433,12 +426,6 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil | ... | @@ -433,12 +426,6 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil |
| 433 | box-shadow: none; | 426 | box-shadow: none; |
| 434 | } | 427 | } |
| 435 | 428 | ||
| 436 | .dark .workflow-dialog-surface { | ||
| 437 | background: var(--secondary); | ||
| 438 | -webkit-backdrop-filter: blur(16px); | ||
| 439 | backdrop-filter: blur(16px); | ||
| 440 | } | ||
| 441 | |||
| 442 | .dark .command-palette-dialog-surface { | 429 | .dark .command-palette-dialog-surface { |
| 443 | box-shadow: none !important; | 430 | box-shadow: none !important; |
| 444 | } | 431 | } |
| ... | @@ -462,15 +449,15 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil | ... | @@ -462,15 +449,15 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil |
| 462 | box-shadow: none !important; | 449 | box-shadow: none !important; |
| 463 | } | 450 | } |
| 464 | 451 | ||
| 465 | .dark .chat-composer-context-strip::before { | 452 | .glass-opacity-slider { |
| 466 | background: color-mix(in srgb, var(--card) 45%, transparent); | 453 | accent-color: var(--primary); |
| 467 | box-shadow: 0 14px 32px -18px rgb(0 0 0 / 75%); | 454 | cursor: pointer; |
| 468 | } | 455 | } |
| 469 | 456 | ||
| 470 | .chat-composer-lower-chrome { | 457 | .glass-opacity-slider:focus-visible { |
| 471 | margin-top: -1px; | 458 | border-radius: 9999px; |
| 472 | margin-inline-end: var(--app-scrollbar-width); | 459 | outline: 2px solid var(--ring); |
| 473 | padding-top: 1px; | 460 | outline-offset: 3px; |
| 474 | } | 461 | } |
| 475 | 462 | ||
| 476 | @media (min-width: 40rem) { | 463 | @media (min-width: 40rem) { |
| ... | @@ -485,18 +472,15 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil | ... | @@ -485,18 +472,15 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil |
| 485 | } | 472 | } |
| 486 | 473 | ||
| 487 | @supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) { | 474 | @supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) { |
| 488 | .chat-composer-glass { | 475 | .chat-composer-glass, |
| 489 | background: var(--secondary); | 476 | .chat-composer-glass-host { |
| 490 | } | 477 | background: var(--background); |
| 491 | |||
| 492 | .chat-composer-context-strip::before { | ||
| 493 | background: var(--card); | ||
| 494 | } | ||
| 495 | |||
| 496 | .dropdown-glass { | ||
| 497 | background: color-mix(in srgb, var(--popover) 88%, black); | ||
| 498 | } | 478 | } |
| 499 | 479 | ||
| 480 | .dialog-glass, | ||
| 481 | .dropdown-glass, | ||
| 482 | .command-menu-surface, | ||
| 483 | .command-palette-dialog-surface, | ||
| 500 | .workflow-dialog-surface { | 484 | .workflow-dialog-surface { |
| 501 | background: var(--popover); | 485 | background: var(--popover); |
| 502 | } | 486 | } |
apps/web/src/routes/__root.tsx+11| ... | @@ -127,6 +127,7 @@ function RootRouteView() { | ... | @@ -127,6 +127,7 @@ function RootRouteView() { |
| 127 | <ToastProvider> | 127 | <ToastProvider> |
| 128 | <AnchoredToastProvider> | 128 | <AnchoredToastProvider> |
| 129 | <DocumentTitleSync /> | 129 | <DocumentTitleSync /> |
| 130 | <GlassAppearanceSync /> | ||
| 130 | {primaryEnvironmentAuthenticated ? <AuthenticatedTracingBootstrap /> : null} | 131 | {primaryEnvironmentAuthenticated ? <AuthenticatedTracingBootstrap /> : null} |
| 131 | <RelayClientInstallDialog /> | 132 | <RelayClientInstallDialog /> |
| 132 | <ConnectOnboardingDialog /> | 133 | <ConnectOnboardingDialog /> |
| ... | @@ -141,6 +142,16 @@ function RootRouteView() { | ... | @@ -141,6 +142,16 @@ function RootRouteView() { |
| 141 | ); | 142 | ); |
| 142 | } | 143 | } |
| 143 | 144 | ||
| 145 | function GlassAppearanceSync() { | ||
| 146 | const glassOpacity = useClientSettings((settings) => settings.glassOpacity); | ||
| 147 | |||
| 148 | useEffect(() => { | ||
| 149 | document.documentElement.style.setProperty("--glass-opacity", `${glassOpacity}%`); | ||
| 150 | }, [glassOpacity]); | ||
| 151 | |||
| 152 | return null; | ||
| 153 | } | ||
| 154 | |||
| 144 | function DocumentTitleSync() { | 155 | function DocumentTitleSync() { |
| 145 | const primaryServerVersion = | 156 | const primaryServerVersion = |
| 146 | useAtomValue(primaryServerConfigAtom)?.environment.serverVersion ?? null; | 157 | useAtomValue(primaryServerConfigAtom)?.environment.serverVersion ?? null; |
packages/contracts/src/settings.test.ts+16| ... | @@ -33,6 +33,22 @@ describe("ClientSettings word wrap", () => { | ... | @@ -33,6 +33,22 @@ describe("ClientSettings word wrap", () => { |
| 33 | }); | 33 | }); |
| 34 | }); | 34 | }); |
| 35 | 35 | ||
| 36 | describe("ClientSettings glass opacity", () => { | ||
| 37 | it("defaults to a readable translucent surface", () => { | ||
| 38 | expect(decodeClientSettings({}).glassOpacity).toBe(80); | ||
| 39 | }); | ||
| 40 | |||
| 41 | it.each([49, 101, 72.5])("rejects an invalid glass opacity: %s", (value) => { | ||
| 42 | expect(() => decodeClientSettings({ glassOpacity: value })).toThrow(); | ||
| 43 | expect(() => decodeClientSettingsPatch({ glassOpacity: value })).toThrow(); | ||
| 44 | }); | ||
| 45 | |||
| 46 | it.each([50, 75, 100])("accepts a glass opacity within the supported range: %s", (value) => { | ||
| 47 | expect(decodeClientSettings({ glassOpacity: value }).glassOpacity).toBe(value); | ||
| 48 | expect(decodeClientSettingsPatch({ glassOpacity: value }).glassOpacity).toBe(value); | ||
| 49 | }); | ||
| 50 | }); | ||
| 51 | |||
| 36 | describe("ClientSettings sidebar v2", () => { | 52 | describe("ClientSettings sidebar v2", () => { |
| 37 | it("defaults the beta off with a three-day auto-settle threshold", () => { | 53 | it("defaults the beta off with a three-day auto-settle threshold", () => { |
| 38 | const settings = decodeClientSettings({}); | 54 | const settings = decodeClientSettings({}); |
packages/contracts/src/settings.ts+14| ... | @@ -48,6 +48,16 @@ export const SidebarAutoSettleAfterDays = Schema.Number.check( | ... | @@ -48,6 +48,16 @@ export const SidebarAutoSettleAfterDays = Schema.Number.check( |
| 48 | ); | 48 | ); |
| 49 | export type SidebarAutoSettleAfterDays = typeof SidebarAutoSettleAfterDays.Type; | 49 | export type SidebarAutoSettleAfterDays = typeof SidebarAutoSettleAfterDays.Type; |
| 50 | export const DEFAULT_SIDEBAR_AUTO_SETTLE_AFTER_DAYS: SidebarAutoSettleAfterDays = 3; | 50 | export const DEFAULT_SIDEBAR_AUTO_SETTLE_AFTER_DAYS: SidebarAutoSettleAfterDays = 3; |
| 51 | export const MIN_GLASS_OPACITY = 50; | ||
| 52 | export const MAX_GLASS_OPACITY = 100; | ||
| 53 | export const GlassOpacity = Schema.Int.check( | ||
| 54 | Schema.isBetween({ | ||
| 55 | minimum: MIN_GLASS_OPACITY, | ||
| 56 | maximum: MAX_GLASS_OPACITY, | ||
| 57 | }), | ||
| 58 | ); | ||
| 59 | export type GlassOpacity = typeof GlassOpacity.Type; | ||
| 60 | export const DEFAULT_GLASS_OPACITY: GlassOpacity = 80; | ||
| 51 | 61 | ||
| 52 | export const ClientSettingsSchema = Schema.Struct({ | 62 | export const ClientSettingsSchema = Schema.Struct({ |
| 53 | autoOpenPlanSidebar: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(false))), | 63 | autoOpenPlanSidebar: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(false))), |
| ... | @@ -57,6 +67,9 @@ export const ClientSettingsSchema = Schema.Struct({ | ... | @@ -57,6 +67,9 @@ export const ClientSettingsSchema = Schema.Struct({ |
| 57 | Schema.withDecodingDefault(Effect.succeed([])), | 67 | Schema.withDecodingDefault(Effect.succeed([])), |
| 58 | ), | 68 | ), |
| 59 | diffIgnoreWhitespace: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))), | 69 | diffIgnoreWhitespace: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))), |
| 70 | glassOpacity: GlassOpacity.pipe( | ||
| 71 | Schema.withDecodingDefault(Effect.succeed(DEFAULT_GLASS_OPACITY)), | ||
| 72 | ), | ||
| 60 | // Model favorites. Historically keyed by provider kind, now | 73 | // Model favorites. Historically keyed by provider kind, now |
| 61 | // widened to `ProviderInstanceId` so users can favorite a specific model | 74 | // widened to `ProviderInstanceId` so users can favorite a specific model |
| 62 | // on a custom provider instance (e.g. "Codex Personal · gpt-5") without | 75 | // on a custom provider instance (e.g. "Codex Personal · gpt-5") without |
| ... | @@ -560,6 +573,7 @@ export const ClientSettingsPatch = Schema.Struct({ | ... | @@ -560,6 +573,7 @@ export const ClientSettingsPatch = Schema.Struct({ |
| 560 | confirmThreadArchive: Schema.optionalKey(Schema.Boolean), | 573 | confirmThreadArchive: Schema.optionalKey(Schema.Boolean), |
| 561 | confirmThreadDelete: Schema.optionalKey(Schema.Boolean), | 574 | confirmThreadDelete: Schema.optionalKey(Schema.Boolean), |
| 562 | diffIgnoreWhitespace: Schema.optionalKey(Schema.Boolean), | 575 | diffIgnoreWhitespace: Schema.optionalKey(Schema.Boolean), |
| 576 | glassOpacity: Schema.optionalKey(GlassOpacity), | ||
| 563 | favorites: Schema.optionalKey( | 577 | favorites: Schema.optionalKey( |
| 564 | Schema.Array( | 578 | Schema.Array( |
| 565 | Schema.Struct({ | 579 | Schema.Struct({ |