feat(webchat): Add chat font size setting
Adds a configurable font size option for the webchat interface. Changes: - Add chatFontSize setting to UiSettings (small/medium/large/x-large) - Add --font-size-chat CSS variable in base.css - Update chat text styles to use the CSS variable - Add applyChatFontSize function to apply setting to document - Add font size dropdown control in chat controls UI - Add styles for the font size control Resolves #3027
This commit is contained in:
parent
0b1c8db0ca
commit
169ff8b8d9
@ -84,6 +84,7 @@
|
||||
--mono: "JetBrains Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, monospace;
|
||||
--font-body: "Space Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
--font-display: "Space Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
--font-size-chat: 14px; /* Adjustable via UI settings */
|
||||
|
||||
/* Shadows - Richer with subtle color */
|
||||
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.2);
|
||||
|
||||
@ -325,6 +325,53 @@
|
||||
color: rgba(16, 24, 40, 0.3);
|
||||
}
|
||||
|
||||
/* Font size control */
|
||||
.chat-controls__font-size {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.chat-controls__font-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.chat-controls__font-icon svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
stroke: currentColor;
|
||||
fill: none;
|
||||
stroke-width: 2px;
|
||||
}
|
||||
|
||||
.chat-controls__font-select {
|
||||
padding: 4px 8px;
|
||||
font-size: 12px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--border);
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
color: var(--text);
|
||||
cursor: pointer;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.chat-controls__font-select:hover {
|
||||
border-color: var(--border-strong);
|
||||
}
|
||||
|
||||
.chat-controls__font-select:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
:root[data-theme="light"] .chat-controls__font-select {
|
||||
background: #ffffff;
|
||||
border-color: var(--border);
|
||||
}
|
||||
|
||||
.btn--icon:hover {
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
border-color: rgba(255, 255, 255, 0.2);
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
}
|
||||
|
||||
.chat-text {
|
||||
font-size: 14px;
|
||||
font-size: var(--font-size-chat, 14px);
|
||||
line-height: 1.5;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
|
||||
@ -9,6 +9,7 @@ import { syncUrlWithSessionKey } from "./app-settings";
|
||||
import type { SessionsListResult } from "./types";
|
||||
import type { ThemeMode } from "./theme";
|
||||
import type { ThemeTransitionContext } from "./theme-transition";
|
||||
import type { ChatFontSize } from "./storage";
|
||||
|
||||
export function renderTab(state: AppViewState, tab: Tab) {
|
||||
const href = pathForTab(tab, state.basePath);
|
||||
@ -128,6 +129,44 @@ export function renderChatControls(state: AppViewState) {
|
||||
>
|
||||
${focusIcon}
|
||||
</button>
|
||||
<span class="chat-controls__separator">|</span>
|
||||
${renderFontSizeControl(state)}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
const FONT_SIZE_OPTIONS: Array<{ value: ChatFontSize; label: string }> = [
|
||||
{ value: "small", label: "Small" },
|
||||
{ value: "medium", label: "Medium" },
|
||||
{ value: "large", label: "Large" },
|
||||
{ value: "x-large", label: "X-Large" },
|
||||
];
|
||||
|
||||
function renderFontSizeControl(state: AppViewState) {
|
||||
const currentSize = state.settings.chatFontSize ?? "medium";
|
||||
const textIcon = html`<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7V4h16v3"></path><path d="M9 20h6"></path><path d="M12 4v16"></path></svg>`;
|
||||
return html`
|
||||
<div class="chat-controls__font-size">
|
||||
<span class="chat-controls__font-icon" title="Font size">${textIcon}</span>
|
||||
<select
|
||||
class="chat-controls__font-select"
|
||||
.value=${currentSize}
|
||||
@change=${(e: Event) => {
|
||||
const next = (e.target as HTMLSelectElement).value as ChatFontSize;
|
||||
state.applySettings({
|
||||
...state.settings,
|
||||
chatFontSize: next,
|
||||
});
|
||||
}}
|
||||
title="Chat font size"
|
||||
>
|
||||
${FONT_SIZE_OPTIONS.map(
|
||||
(opt) =>
|
||||
html`<option value=${opt.value} ?selected=${opt.value === currentSize}>
|
||||
${opt.label}
|
||||
</option>`,
|
||||
)}
|
||||
</select>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -17,6 +17,7 @@ const createHost = (tab: Tab): SettingsHost => ({
|
||||
theme: "system",
|
||||
chatFocusMode: false,
|
||||
chatShowThinking: true,
|
||||
chatFontSize: "medium",
|
||||
splitRatio: 0.6,
|
||||
navCollapsed: false,
|
||||
navGroupsCollapsed: {},
|
||||
|
||||
@ -10,7 +10,7 @@ import { loadPresence } from "./controllers/presence";
|
||||
import { loadSessions } from "./controllers/sessions";
|
||||
import { loadSkills } from "./controllers/skills";
|
||||
import { inferBasePathFromPathname, normalizeBasePath, normalizePath, pathForTab, tabFromPath, type Tab } from "./navigation";
|
||||
import { saveSettings, type UiSettings } from "./storage";
|
||||
import { saveSettings, CHAT_FONT_SIZE_PX, type UiSettings, type ChatFontSize } from "./storage";
|
||||
import { resolveTheme, type ResolvedTheme, type ThemeMode } from "./theme";
|
||||
import { startThemeTransition, type ThemeTransitionContext } from "./theme-transition";
|
||||
import { scheduleChatScroll, scheduleLogsScroll } from "./app-scroll";
|
||||
@ -46,6 +46,7 @@ export function applySettings(host: SettingsHost, next: UiSettings) {
|
||||
host.theme = next.theme;
|
||||
applyResolvedTheme(host, resolveTheme(next.theme));
|
||||
}
|
||||
applyChatFontSize(normalized.chatFontSize);
|
||||
host.applySessionKey = host.settings.lastActiveSessionKey;
|
||||
}
|
||||
|
||||
@ -191,6 +192,7 @@ export function inferBasePath() {
|
||||
export function syncThemeWithSettings(host: SettingsHost) {
|
||||
host.theme = host.settings.theme ?? "system";
|
||||
applyResolvedTheme(host, resolveTheme(host.theme));
|
||||
applyChatFontSize(host.settings.chatFontSize ?? "medium");
|
||||
}
|
||||
|
||||
export function applyResolvedTheme(host: SettingsHost, resolved: ResolvedTheme) {
|
||||
@ -201,6 +203,12 @@ export function applyResolvedTheme(host: SettingsHost, resolved: ResolvedTheme)
|
||||
root.style.colorScheme = resolved;
|
||||
}
|
||||
|
||||
export function applyChatFontSize(size: ChatFontSize) {
|
||||
if (typeof document === "undefined") return;
|
||||
const px = CHAT_FONT_SIZE_PX[size] ?? CHAT_FONT_SIZE_PX.medium;
|
||||
document.documentElement.style.setProperty("--font-size-chat", `${px}px`);
|
||||
}
|
||||
|
||||
export function attachThemeListener(host: SettingsHost) {
|
||||
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
||||
host.themeMedia = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
|
||||
@ -2,6 +2,15 @@ const KEY = "moltbot.control.settings.v1";
|
||||
|
||||
import type { ThemeMode } from "./theme";
|
||||
|
||||
export type ChatFontSize = "small" | "medium" | "large" | "x-large";
|
||||
|
||||
export const CHAT_FONT_SIZE_PX: Record<ChatFontSize, number> = {
|
||||
small: 13,
|
||||
medium: 14,
|
||||
large: 16,
|
||||
"x-large": 18,
|
||||
};
|
||||
|
||||
export type UiSettings = {
|
||||
gatewayUrl: string;
|
||||
token: string;
|
||||
@ -10,6 +19,7 @@ export type UiSettings = {
|
||||
theme: ThemeMode;
|
||||
chatFocusMode: boolean;
|
||||
chatShowThinking: boolean;
|
||||
chatFontSize: ChatFontSize; // Chat text font size
|
||||
splitRatio: number; // Sidebar split ratio (0.4 to 0.7, default 0.6)
|
||||
navCollapsed: boolean; // Collapsible sidebar state
|
||||
navGroupsCollapsed: Record<string, boolean>; // Which nav groups are collapsed
|
||||
@ -29,6 +39,7 @@ export function loadSettings(): UiSettings {
|
||||
theme: "system",
|
||||
chatFocusMode: false,
|
||||
chatShowThinking: true,
|
||||
chatFontSize: "medium",
|
||||
splitRatio: 0.6,
|
||||
navCollapsed: false,
|
||||
navGroupsCollapsed: {},
|
||||
@ -69,6 +80,13 @@ export function loadSettings(): UiSettings {
|
||||
typeof parsed.chatShowThinking === "boolean"
|
||||
? parsed.chatShowThinking
|
||||
: defaults.chatShowThinking,
|
||||
chatFontSize:
|
||||
parsed.chatFontSize === "small" ||
|
||||
parsed.chatFontSize === "medium" ||
|
||||
parsed.chatFontSize === "large" ||
|
||||
parsed.chatFontSize === "x-large"
|
||||
? parsed.chatFontSize
|
||||
: defaults.chatFontSize,
|
||||
splitRatio:
|
||||
typeof parsed.splitRatio === "number" &&
|
||||
parsed.splitRatio >= 0.4 &&
|
||||
|
||||
Loading…
Reference in New Issue
Block a user