Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/web/src/components/ComposerPromptEditorTiptap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1101,7 +1101,7 @@ function ComposerPromptEditorTiptapInner(

const editorAttributes = {
class: cn(
"composer-tiptap -m-1 block max-h-52 min-h-12 overflow-y-auto p-1 whitespace-pre-wrap wrap-break-word bg-transparent leading-relaxed text-foreground focus:outline-none",
"composer-tiptap -m-1 block max-h-52 min-h-19.5 redesign:min-h-12 overflow-y-auto p-1 whitespace-pre-wrap wrap-break-word bg-transparent leading-relaxed text-foreground focus:outline-none",
className,
),
"data-testid": "composer-editor",
Expand Down
3 changes: 3 additions & 0 deletions apps/web/src/components/GitActionsControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ import { getSourceControlPresentation, sourceControlIcon } from "~/sourceControl
import { sourceControlClients } from "@t3tools/client-runtime/source-control-clients";
import { formatDiffCount } from "@t3tools/client-runtime/diff-count";
import { useOpenLink } from "~/browser/useOpenLink";
import { useClientSettings } from "~/hooks/useSettings";

interface GitActionsControlProps {
presentation?: "toolbar" | "menu";
Expand Down Expand Up @@ -1151,8 +1152,10 @@ export default function GitActionsControl({
? (quickAction.hint ?? "This action is currently unavailable.")
: null;
const gitActionProgress = resolveGitActionProgressPresentation(vcsActionState);
const redesign = useClientSettings((s) => s.redesignEnabled);
// A fresh draft with nothing to commit, push, or diff hides those rows until there is work.
const quietEmptyDraft =
redesign &&
isPanel &&
draftId !== undefined &&
gitActionProgress === null &&
Expand Down
4 changes: 3 additions & 1 deletion apps/web/src/components/ThreadStatusIndicators.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,9 @@ function PullRequestBadge({
aria-hidden
className={cn("size-3 shrink-0", presentation.toneClassName)}
/>
<span className="text-secondary-label">{presentation.text}</span>
<span className={cn(presentation.toneClassName, "redesign:text-secondary-label")}>
{presentation.text}
</span>
</span>
</TooltipTrigger>
<TooltipPopup
Expand Down
29 changes: 25 additions & 4 deletions apps/web/src/components/chat/ComposerPrimaryActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { memo, type MouseEventHandler, type PointerEventHandler } from "react";
import { CheckIcon, ChevronDownIcon, ChevronLeftIcon, Minimize2Icon, PlayIcon } from "lucide-react";
import { CornerUpRight, ListPlus } from "lucide";
import { MorphIcon } from "~/components/MorphIcon";
import { useEnvironmentIdentificationMode } from "~/hooks/useSettings";
import { useClientSettings, useEnvironmentIdentificationMode } from "~/hooks/useSettings";
import { formatContextWindowTokens } from "~/lib/contextWindow";
import { cn } from "~/lib/utils";
import { useShortcutModifierState } from "../../shortcutModifierState";
Expand Down Expand Up @@ -113,6 +113,7 @@ export const ComposerPrimaryActions = memo(function ComposerPrimaryActions({
? { onPointerDown: preventPointerFocus }
: undefined;
const environmentIdentificationMode = useEnvironmentIdentificationMode();
const redesign = useClientSettings((s) => s.redesignEnabled);
const shortcutModifiers = useShortcutModifierState();
const isQueuing =
!isEditingQueuedMessage &&
Expand Down Expand Up @@ -368,7 +369,16 @@ export const ComposerPrimaryActions = memo(function ComposerPrimaryActions({
render={
<button
type="button"
className="flex h-7 shrink-0 cursor-pointer items-center gap-1.5 rounded-(--control-radius) px-2 font-medium text-base text-foreground/80 tabular-nums outline-none hover:bg-accent hover:text-foreground focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-64 sm:text-sm aria-pressed:bg-accent/60 aria-pressed:text-foreground [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:text-muted-foreground"
className={
redesign
? "flex h-7 shrink-0 cursor-pointer items-center gap-1.5 rounded-(--control-radius) px-2 font-medium text-base text-foreground/80 tabular-nums outline-none hover:bg-accent hover:text-foreground focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-64 sm:text-sm aria-pressed:bg-accent/60 aria-pressed:text-foreground [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:text-muted-foreground"
: cn(
"flex h-9 shrink-0 cursor-pointer items-center gap-1.5 rounded-full border px-2.5 text-xs tabular-nums outline-none transition-colors focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-64 sm:h-8 [&_svg]:pointer-events-none [&_svg]:size-3.5",
keepFullHistory
? "border-border text-muted-foreground hover:text-foreground"
: "border-warning/40 text-warning hover:bg-warning/8",
)
}
{...pointerFocusProps}
aria-pressed={!keepFullHistory}
aria-label={`Compact ${compactTokens} tokens of history before sending`}
Expand All @@ -378,8 +388,19 @@ export const ComposerPrimaryActions = memo(function ComposerPrimaryActions({
}
>
<Minimize2Icon aria-hidden="true" />
{/* The token count lives in the tooltip so the permission label keeps its room. */}
{keepFullHistory ? "Keep full" : "Compact next"}
{/* The new design keeps the token count in the tooltip so the permission label keeps its room. */}
{redesign ? (
keepFullHistory ? (
"Keep full"
) : (
"Compact next"
)
) : (
<>
{keepFullHistory ? "Full" : "Compact"}
<span>{compactTokens}</span>
</>
)}
</TooltipTrigger>
<TooltipPopup>
<div>
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/components/chat/OpenInPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -412,11 +412,11 @@ export const OpenInPicker = memo(function OpenInPicker({
getOpenInIconClass(primaryOption.kind),
// Full-color app icons outshout the panel's line icons until the row is in use.
isPanel &&
"grayscale dark:opacity-70 group-hover/details-split:opacity-100 group-hover/details-split:grayscale-0 group-has-[[data-popup-open]]/details-split:opacity-100 group-has-[[data-popup-open]]/details-split:grayscale-0",
"redesign:grayscale redesign:dark:opacity-70 redesign:group-hover/details-split:opacity-100 redesign:group-hover/details-split:grayscale-0 redesign:group-has-[[data-popup-open]]/details-split:opacity-100 redesign:group-has-[[data-popup-open]]/details-split:grayscale-0",
// A hairline keeps pale app icons from dissolving into the card.
isPanel &&
primaryOption.kind === "brand" &&
"rounded-sm ring-1 ring-black/10 dark:ring-white/15",
"redesign:rounded-sm redesign:ring-1 redesign:ring-black/10 redesign:dark:ring-white/15",
)}
/>
) : isPanel ? (
Expand Down
8 changes: 5 additions & 3 deletions apps/web/src/components/chat/TraitsPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
normalizeModelSlug,
} from "@t3tools/shared/model";
import { memo } from "react";
import { GaugeIcon } from "lucide-react";
import { BrainIcon, GaugeIcon } from "lucide-react";
import {
Menu,
MenuGroup,
Expand Down Expand Up @@ -600,15 +600,17 @@ export const TraitsPicker = memo(function TraitsPicker({
size === "xs" ? "gap-1" : "gap-1.5",
)}
>
<ComposerControlIcon icon={GaugeIcon} size={size} />
<ComposerControlIcon icon={BrainIcon} size={size} className="redesign:hidden" />
<ComposerControlIcon icon={GaugeIcon} size={size} className="hidden redesign:block" />
<span data-composer-control-label className="min-w-0 truncate">
{triggerLabel}
</span>
<ComposerControlChevron size={size} />
</span>
) : (
<>
<ComposerControlIcon icon={GaugeIcon} size={size} />
<ComposerControlIcon icon={BrainIcon} size={size} className="redesign:hidden" />
<ComposerControlIcon icon={GaugeIcon} size={size} className="hidden redesign:block" />
<span data-composer-control-label>{triggerLabel}</span>
<ComposerControlChevron size={size} />
</>
Expand Down
51 changes: 27 additions & 24 deletions apps/web/src/components/settings/AppearanceSettingsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -351,32 +351,35 @@ export function AppearanceSettingsPanel() {
</div>
}
/>
<SettingsRow
{...searchableSetting("sidebar-project-icon-colors")}
description="Show project icons in the sidebar in full color instead of gray."
resetAction={
settings.sidebarProjectIconColors !==
DEFAULT_UNIFIED_SETTINGS.sidebarProjectIconColors ? (
<SettingResetButton
label="colored project icons"
onClick={() =>
updateSettings({
sidebarProjectIconColors: DEFAULT_UNIFIED_SETTINGS.sidebarProjectIconColors,
})
{/* Only the new design grays project icons, so the option only exists there. */}
{settings.redesignEnabled ? (
<SettingsRow
{...searchableSetting("sidebar-project-icon-colors")}
description="Show project icons in the sidebar in full color instead of gray."
resetAction={
settings.sidebarProjectIconColors !==
DEFAULT_UNIFIED_SETTINGS.sidebarProjectIconColors ? (
<SettingResetButton
label="colored project icons"
onClick={() =>
updateSettings({
sidebarProjectIconColors: DEFAULT_UNIFIED_SETTINGS.sidebarProjectIconColors,
})
}
/>
) : null
}
control={
<Switch
checked={settings.sidebarProjectIconColors}
onCheckedChange={(checked) =>
updateSettings({ sidebarProjectIconColors: Boolean(checked) })
}
aria-label="Colored project icons"
/>
) : null
}
control={
<Switch
checked={settings.sidebarProjectIconColors}
onCheckedChange={(checked) =>
updateSettings({ sidebarProjectIconColors: Boolean(checked) })
}
aria-label="Colored project icons"
/>
}
/>
}
/>
) : null}
<SettingsRow
{...searchableSetting("math-rendering")}
description="Typeset LaTeX math, such as $x^2$ or \[x^2\], in messages and Markdown previews. The composer keeps TeX source editable."
Expand Down
21 changes: 21 additions & 0 deletions apps/web/src/components/settings/BetaSettingsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,27 @@ export function BetaSettingsPanel() {
return (
<SettingsPageContainer>
<SettingsSection id="beta" title="Beta">
<SettingsRow
{...searchableSetting("new-design")}
description="Try the quieter design: gray sidebar icons, dimmer read threads, and simpler composer controls."
resetAction={
settings.redesignEnabled !== DEFAULT_UNIFIED_SETTINGS.redesignEnabled ? (
<SettingResetButton
label="new design"
onClick={() =>
updateSettings({ redesignEnabled: DEFAULT_UNIFIED_SETTINGS.redesignEnabled })
}
/>
) : null
}
control={
<Switch
checked={settings.redesignEnabled}
onCheckedChange={(checked) => updateSettings({ redesignEnabled: Boolean(checked) })}
aria-label="New design"
/>
}
/>
<SettingsRow
{...searchableSetting("working-shelf")}
description="Fold working and monitoring threads into a Working section. They return to the top of the inbox when they need you."
Expand Down
6 changes: 6 additions & 0 deletions apps/web/src/components/settings/settingsSearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,12 @@ export const SETTINGS_SEARCH_ITEMS = [
to: "/settings/general",
searchTerms: ["usage quota rate limit reset recover continue"],
},
{
id: "new-design",
title: "New design",
to: "/settings/beta",
searchTerms: ["beta redesign look style sidebar composer gray quieter classic"],
},
{
id: "working-shelf",
title: "Working section",
Expand Down
10 changes: 7 additions & 3 deletions apps/web/src/components/sidebar/SidebarListMarkers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { type ReactNode } from "react";
import { cn } from "~/lib/utils";
import { useClientSettings } from "../../hooks/useSettings";
import {
animateSidebarLayoutChanges,
sidebarMarkerId,
Expand Down Expand Up @@ -115,6 +116,8 @@ export function SidebarSectionHeader(props: {
: props.marker === "snoozed-header"
? "snoozed"
: "settled";
const redesign = useClientSettings((s) => s.redesignEnabled);
const collapsedCount = props.toggle.expanded ? null : props.count;
return (
<SortableSidebarMarker
marker={props.marker}
Expand All @@ -127,12 +130,13 @@ export function SidebarSectionHeader(props: {
tone={props.isDropTarget ? "accent" : props.dragging ? "emphasized" : "muted"}
data-testid={`sidebar-${shelf}-shelf-toggle`}
accessory={
props.toggle.expanded ? null : (
<span className="font-normal tabular-nums">{props.count}</span>
)
redesign && collapsedCount !== null ? (
<span className="font-normal tabular-nums">{collapsedCount}</span>
) : null
}
>
{props.label}
{!redesign && collapsedCount !== null ? ` (${collapsedCount})` : null}
</CollapsibleSectionHeader>
</SortableSidebarMarker>
);
Expand Down
23 changes: 15 additions & 8 deletions apps/web/src/components/sidebar/SidebarThreadRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ function SidebarProviderStack(props: {
acpRegistryIconUrl={currentEntry.acpRegistryIconUrl}
showBadge={showInstanceBadge}
size="compact"
iconClassName="opacity-50 grayscale"
iconClassName="opacity-60 redesign:opacity-50 redesign:grayscale"
/>
);
if (stack.length === 1) {
Expand Down Expand Up @@ -978,12 +978,12 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: SidebarThreadRowP
? cn(
"truncate",
shouldRecede
? "text-foreground/75 group-hover/sidebar-row:text-foreground group-focus-visible/sidebar-row:text-foreground"
? "text-secondary-label redesign:text-foreground/75 redesign:group-hover/sidebar-row:text-foreground redesign:group-focus-visible/sidebar-row:text-foreground"
: isUnread || isWoke || status === "input" || status === "request"
? "text-foreground"
: status === "failed"
? "text-foreground/95"
: "text-foreground",
: "text-foreground/90 redesign:text-foreground",
)
: cn(
"truncate group-focus-within/sidebar-row:text-foreground group-hover/sidebar-row:text-foreground",
Expand Down Expand Up @@ -1284,15 +1284,20 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: SidebarThreadRowP
// Repeated brand colors outshout titles; color returns on the row in focus.
!props.isActive &&
!projectIconColors &&
"opacity-60 grayscale group-focus-visible/sidebar-row:opacity-100 group-focus-visible/sidebar-row:grayscale-0 group-any-hover/sidebar-row:opacity-100 group-any-hover/sidebar-row:grayscale-0",
"redesign:opacity-60 redesign:grayscale redesign:group-focus-visible/sidebar-row:opacity-100 redesign:group-focus-visible/sidebar-row:grayscale-0 redesign:group-any-hover/sidebar-row:opacity-100 redesign:group-any-hover/sidebar-row:grayscale-0",
)}
/>
) : null}
{/* Left of the status slot, the pin would slide when hover swaps the
label for the wider row controls. */}
{pinIndicator}
{props.projectDisplayName ? (
<span className="min-w-0 flex-1 truncate text-secondary-label text-xs">
<span
className={cn(
"min-w-0 flex-1 truncate text-secondary-label text-xs",
shouldRecede ? "font-normal" : "font-medium redesign:font-normal",
)}
>
{props.projectDisplayName}
</span>
) : (
Expand Down Expand Up @@ -1449,7 +1454,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: SidebarThreadRowP
{thread.branch ? (
<>
<ThreadWorktreeIndicator thread={thread} />
<span className="flex min-w-0 flex-1">
<span className="flex min-w-0 flex-1 text-muted-foreground/40 redesign:text-inherit">
<MiddleTruncate value={thread.branch} showTitle={false} />
</span>
</>
Expand All @@ -1458,9 +1463,11 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: SidebarThreadRowP
<EnvironmentMachineIcon
aria-hidden
kind={props.environmentMachine}
className="size-3 shrink-0"
className="size-3 shrink-0 text-muted-foreground/40 redesign:text-inherit"
/>
<span className="min-w-0 flex-1 truncate">{props.scratchMachineLabel}</span>
<span className="min-w-0 flex-1 truncate text-muted-foreground/40 redesign:text-inherit">
{props.scratchMachineLabel}
</span>
</>
) : (
<span className="flex-1" />
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ const inlineButtonVariants = cva(
muted: "text-muted-foreground hover:text-foreground",
destructive: "text-destructive/80 hover:text-destructive",
picker:
"gap-1 text-foreground decoration-from-font underline-offset-4 hover:underline data-popup-open:underline",
"gap-1 text-foreground underline decoration-foreground/30 decoration-dotted decoration-from-font underline-offset-4 hover:decoration-foreground hover:decoration-solid data-popup-open:decoration-foreground data-popup-open:decoration-solid redesign:no-underline redesign:hover:underline redesign:data-popup-open:underline",
},
},
},
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/ui/collapsible-section-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export function CollapsibleSectionHeader({
className={cn(
"shrink-0 transition-transform",
variant === "row" ? "size-4 text-muted-foreground" : "size-3",
!expanded && "-rotate-90",
!expanded && "rotate-180 redesign:-rotate-90",
)}
/>
</button>
Expand Down
9 changes: 7 additions & 2 deletions apps/web/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
@custom-variant light (&:not(.dark, .dark *));
/* Window Controls Overlay: active when Electron exposes native titlebar control geometry. */
@custom-variant wco (&:is(.wco, .wco *));
/* Beta "New design" (Settings -> Beta) sets data-redesign on <html>. */
@custom-variant redesign (&:where([data-redesign] *));

/* Secondary hovering pointers reveal sidebar actions on touch-primary devices. */
@custom-variant any-hover {
Expand Down Expand Up @@ -1243,8 +1245,6 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil
--accent-foreground: #f7f9ff;
--muted: #0a0a0a;
--muted-foreground: #a3a3a3;
/* Row metadata sits a step below read titles (75% white). */
--secondary-label: #8f8f8f;
--border: rgb(255 255 255 / 8%);
--input: rgb(255 255 255 / 18%);
--sidebar: var(--card);
Expand Down Expand Up @@ -1663,6 +1663,11 @@ body {
padding: 0;
}

/* New design: sidebar row metadata sits a step below read titles (75% white). */
.dark[data-redesign] [data-app-sidebar] {
--secondary-label: #8f8f8f;
}

/* Lighter default icon stroke. Zero specificity, so stroke-* classes still win
over it while it still beats lucide's stroke-width="2" attribute. */
:where(svg.lucide[stroke-width="2"]) {
Expand Down
Loading
Loading