Skip to content
Open
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
10 changes: 9 additions & 1 deletion apps/web/src/appInit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,16 @@ function startClientAppearance(): void {
if (previous?.diffColorScheme !== settings.diffColorScheme) {
root.dataset.diffColorScheme = settings.diffColorScheme;
}
if (previous?.chatWidth !== settings.chatWidth) {
if (
previous?.chatWidth !== settings.chatWidth ||
previous.chatCustomWidthRem !== settings.chatCustomWidthRem
) {
root.dataset.chatWidth = settings.chatWidth;
if (settings.chatWidth === "custom") {
root.style.setProperty("--chat-content-max-width", `${settings.chatCustomWidthRem}rem`);
} else {
root.style.removeProperty("--chat-content-max-width");
}
}
if (previous?.appearanceContrast !== settings.appearanceContrast) {
applyAppearanceContrast(root, settings.appearanceContrast);
Expand Down
66 changes: 62 additions & 4 deletions apps/web/src/components/settings/AppearanceSettingsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import {
type EffortDisplay,
type EnvironmentIdentificationMode,
MAX_APPEARANCE_CONTRAST,
MAX_CHAT_CUSTOM_WIDTH_REM,
MAX_GLASS_OPACITY,
MAX_PANEL_ANIMATION_DURATION_MS,
MIN_APPEARANCE_CONTRAST,
MIN_CHAT_CUSTOM_WIDTH_REM,
MIN_GLASS_OPACITY,
MIN_PANEL_ANIMATION_DURATION_MS,
} from "@t3tools/contracts/settings";
Expand Down Expand Up @@ -76,6 +78,7 @@ const CHAT_WIDTH_LABELS: Record<ChatWidth, string> = {
comfortable: "Comfortable",
wide: "Wide",
full: "Full",
custom: "Custom",
};

const EFFORT_DISPLAY_LABELS: Record<EffortDisplay, string> = {
Expand Down Expand Up @@ -119,6 +122,14 @@ export function AppearanceSettingsPanel() {
"--settings-slider-progress": `${appearanceContrastRatio * 100}%`,
"--settings-slider-fill-offset": `${0.5 - appearanceContrastRatio}rem`,
} as CSSProperties;
const chatCustomWidthRatio =
(settings.chatCustomWidthRem - MIN_CHAT_CUSTOM_WIDTH_REM) /
(MAX_CHAT_CUSTOM_WIDTH_REM - MIN_CHAT_CUSTOM_WIDTH_REM);
const chatCustomWidthLabel = `${settings.chatCustomWidthRem * settings.fontSizeInterface} px`;
const chatCustomWidthSliderStyle = {
"--settings-slider-progress": `${chatCustomWidthRatio * 100}%`,
"--settings-slider-fill-offset": `${0.5 - chatCustomWidthRatio}rem`,
} as CSSProperties;
const panelAnimationDurationRatio =
(settings.panelAnimationDurationMs - MIN_PANEL_ANIMATION_DURATION_MS) /
(MAX_PANEL_ANIMATION_DURATION_MS - MIN_PANEL_ANIMATION_DURATION_MS);
Expand Down Expand Up @@ -246,10 +257,16 @@ export function AppearanceSettingsPanel() {
{...searchableSetting("chat-width")}
description="Set how wide messages and the composer can grow on large screens."
resetAction={
settings.chatWidth !== DEFAULT_UNIFIED_SETTINGS.chatWidth ? (
settings.chatWidth !== DEFAULT_UNIFIED_SETTINGS.chatWidth ||
settings.chatCustomWidthRem !== DEFAULT_UNIFIED_SETTINGS.chatCustomWidthRem ? (
<SettingResetButton
label="chat width"
onClick={() => updateSettings({ chatWidth: DEFAULT_UNIFIED_SETTINGS.chatWidth })}
onClick={() =>
updateSettings({
chatWidth: DEFAULT_UNIFIED_SETTINGS.chatWidth,
chatCustomWidthRem: DEFAULT_UNIFIED_SETTINGS.chatCustomWidthRem,
})
}
/>
) : null
}
Expand All @@ -258,7 +275,12 @@ export function AppearanceSettingsPanel() {
<Select
value={settings.chatWidth}
onValueChange={(value) => {
if (value === "comfortable" || value === "wide" || value === "full")
if (
value === "comfortable" ||
value === "wide" ||
value === "full" ||
value === "custom"
)
updateSettings({ chatWidth: value });
}}
>
Expand All @@ -269,11 +291,47 @@ export function AppearanceSettingsPanel() {
<SelectItem value="comfortable">Comfortable (default)</SelectItem>
<SelectItem value="wide">Wide</SelectItem>
<SelectItem value="full">Full</SelectItem>
<SelectItem value="custom">Custom</SelectItem>
</SelectPopup>
</Select>
</div>
}
/>
>
{settings.chatWidth === "custom" ? (
<div className="flex justify-end pb-2">
<div className="flex w-full items-center gap-3 sm:w-52">
<output
className="min-w-18 rounded-md bg-muted px-2 py-1 text-center font-mono text-xs font-medium tabular-nums text-foreground"
htmlFor="chat-custom-width"
>
{chatCustomWidthLabel}
</output>
<input
aria-label="Custom chat width"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
aria-valuetext={`${chatCustomWidthLabel}`}
className="settings-slider min-w-0 flex-1"
id="chat-custom-width"
max={MAX_CHAT_CUSTOM_WIDTH_REM}
min={MIN_CHAT_CUSTOM_WIDTH_REM}
onChange={(event) => {
const chatCustomWidthRem = Number(event.currentTarget.value);
if (
Number.isInteger(chatCustomWidthRem) &&
chatCustomWidthRem >= MIN_CHAT_CUSTOM_WIDTH_REM &&
chatCustomWidthRem <= MAX_CHAT_CUSTOM_WIDTH_REM
) {
updateSettings({ chatCustomWidthRem });
}
}}
step={2}
style={chatCustomWidthSliderStyle}
type="range"
value={settings.chatCustomWidthRem}
/>
</div>
</div>
) : null}
</SettingsRow>
<ThreadDetailsItemsRow />
<SettingsRow
{...searchableSetting("effort-display")}
Expand Down
6 changes: 5 additions & 1 deletion apps/web/src/components/settings/SettingsPanels.tsx

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Low

...(settings.glassOpacity !== DEFAULT_UNIFIED_SETTINGS.glassOpacity ? ["Glass opacity"] : []),

A nondefault chatCustomWidthRem is not detected as a dirty setting when chatWidth is at its default, so RestoreDeviceDefaultsButton stays disabled and restoreDefaults returns without resetting the custom width. Include chatCustomWidthRem in the Chat width dirty check.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/components/settings/SettingsPanels.tsx around line 432:

A nondefault `chatCustomWidthRem` is not detected as a dirty setting when `chatWidth` is at its default, so `RestoreDeviceDefaultsButton` stays disabled and `restoreDefaults` returns without resetting the custom width. Include `chatCustomWidthRem` in the Chat width dirty check.

Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,10 @@ export function useSettingsRestore(onRestored?: () => void) {
...(settings.diffColorScheme !== DEFAULT_UNIFIED_SETTINGS.diffColorScheme
? ["Diff colors"]
: []),
...(settings.chatWidth !== DEFAULT_UNIFIED_SETTINGS.chatWidth ? ["Chat width"] : []),
...(settings.chatWidth !== DEFAULT_UNIFIED_SETTINGS.chatWidth ||
settings.chatCustomWidthRem !== DEFAULT_UNIFIED_SETTINGS.chatCustomWidthRem
? ["Chat width"]
: []),
...(settings.effortDisplay !== DEFAULT_UNIFIED_SETTINGS.effortDisplay
? ["Effort display"]
: []),
Expand Down Expand Up @@ -609,6 +612,7 @@ export function useSettingsRestore(onRestored?: () => void) {
appearanceContrast: DEFAULT_UNIFIED_SETTINGS.appearanceContrast,
diffColorScheme: DEFAULT_UNIFIED_SETTINGS.diffColorScheme,
chatWidth: DEFAULT_UNIFIED_SETTINGS.chatWidth,
chatCustomWidthRem: DEFAULT_UNIFIED_SETTINGS.chatCustomWidthRem,
effortDisplay: DEFAULT_UNIFIED_SETTINGS.effortDisplay,
mathRenderingEnabled: DEFAULT_UNIFIED_SETTINGS.mathRenderingEnabled,
timestampFormat: DEFAULT_UNIFIED_SETTINGS.timestampFormat,
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/settings/settingsSearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ export const SETTINGS_SEARCH_ITEMS = [
id: "chat-width",
title: "Chat width",
to: "/settings/appearance",
searchTerms: ["wide full width column layout messages composer monitor"],
searchTerms: ["wide full custom width column layout messages composer monitor"],
},
{
id: "thread-details-items",
Expand Down
3 changes: 2 additions & 1 deletion apps/web/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -2254,7 +2254,8 @@ code {
}

/* Chat width setting. Comfortable keeps the default --chat-content-max-width,
which sizes the timeline lane, the composer, and ChatCanvas's width probe. */
which sizes the timeline lane, the composer, and ChatCanvas's width probe.
Custom sets it inline from appInit.tsx. */
:root[data-chat-width="wide"] {
--chat-content-max-width: 72rem;
}
Expand Down
9 changes: 9 additions & 0 deletions packages/contracts/src/settings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,15 @@ describe("ClientSettings notifications", () => {
});
});

describe("ClientSettings chat width", () => {
it("keeps a saved preset when existing settings omit the custom width", () => {
expect(decodeClientSettings({ chatWidth: "wide" })).toMatchObject({
chatWidth: "wide",
chatCustomWidthRem: 60,
});
});
});

describe("ClientSettings quit confirmation", () => {
it.each([
[true, "hold"],
Expand Down
14 changes: 13 additions & 1 deletion packages/contracts/src/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,9 +324,17 @@ export const LoadBalancingWeights = Schema.Record(

export const DiffColorScheme = Schema.Literals(["red-green", "blue-orange"]);

export const ChatWidth = Schema.Literals(["comfortable", "wide", "full"]);
export const ChatWidth = Schema.Literals(["comfortable", "wide", "full", "custom"]);
export type ChatWidth = typeof ChatWidth.Type;

/** In rem so the cap scales with the interface font size. */
export const MIN_CHAT_CUSTOM_WIDTH_REM = 46;
export const MAX_CHAT_CUSTOM_WIDTH_REM = 96;
export const ChatCustomWidthRem = Schema.Int.check(
Schema.isBetween({ minimum: MIN_CHAT_CUSTOM_WIDTH_REM, maximum: MAX_CHAT_CUSTOM_WIDTH_REM }),
);
const DEFAULT_CHAT_CUSTOM_WIDTH_REM: typeof ChatCustomWidthRem.Type = 60;

/** How the composer shows reasoning effort: the level's name, or rising bars. */
export const EffortDisplay = Schema.Literals(["label", "bars"]);
export type EffortDisplay = typeof EffortDisplay.Type;
Expand Down Expand Up @@ -357,6 +365,9 @@ export const ClientSettingsSchema = Schema.Struct({
Schema.withDecodingDefault(Effect.succeed("red-green" as const)),
),
chatWidth: ChatWidth.pipe(Schema.withDecodingDefault(Effect.succeed("comfortable" as const))),
chatCustomWidthRem: ChatCustomWidthRem.pipe(
Schema.withDecodingDefault(Effect.succeed(DEFAULT_CHAT_CUSTOM_WIDTH_REM)),
),
effortDisplay: EffortDisplay.pipe(Schema.withDecodingDefault(Effect.succeed("label" as const))),
loadBalancingEnabled: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(false))),
loadBalancingWeights: LoadBalancingWeights.pipe(Schema.withDecodingDefault(Effect.succeed({}))),
Expand Down Expand Up @@ -1721,6 +1732,7 @@ export const ClientSettingsPatch = Schema.Struct({
inAppNotificationsEnabled: Schema.optionalKey(Schema.Boolean),
diffColorScheme: Schema.optionalKey(DiffColorScheme),
chatWidth: Schema.optionalKey(ChatWidth),
chatCustomWidthRem: Schema.optionalKey(ChatCustomWidthRem),
effortDisplay: Schema.optionalKey(EffortDisplay),
loadBalancingEnabled: Schema.optionalKey(Schema.Boolean),
loadBalancingWeights: Schema.optionalKey(LoadBalancingWeights),
Expand Down
Loading