feat(web): add font size setting

- Add Settings > Display > Font Size selector (80/90/100/110/120%)
- Persist preference (hapi-font-scale) and apply globally via --app-font-scale
- Normalize main UI typography to match Settings
This commit is contained in:
therainisme
2026-01-29 16:25:47 +08:00
committed by weishu
parent 0ceda16160
commit efbe4f8d90
15 changed files with 232 additions and 22 deletions
@@ -36,7 +36,7 @@ export function AttachmentItem() {
const isError = status.type === 'incomplete'
return (
<AttachmentPrimitive.Root className="flex items-center gap-2 rounded-lg bg-[var(--app-subtle-bg)] px-3 py-2 text-sm text-[var(--app-fg)]">
<AttachmentPrimitive.Root className="flex items-center gap-2 rounded-lg bg-[var(--app-subtle-bg)] px-3 py-2 text-base text-[var(--app-fg)]">
{isUploading ? <Spinner size="sm" label={null} className="text-[var(--app-hint)]" /> : null}
{isError ? (
<span className="text-red-500">
@@ -550,7 +550,7 @@ export function HappyComposer(props: {
onSelect={handleSelect}
onKeyDown={handleKeyDown}
onPaste={handlePaste}
className="flex-1 resize-none bg-transparent text-sm leading-snug text-[var(--app-fg)] placeholder-[var(--app-hint)] focus:outline-none disabled:cursor-not-allowed disabled:opacity-50"
className="flex-1 resize-none bg-transparent text-base leading-snug text-[var(--app-fg)] placeholder-[var(--app-hint)] focus:outline-none disabled:cursor-not-allowed disabled:opacity-50"
/>
</div>
@@ -32,7 +32,7 @@ function FileAttachment(props: { attachment: AttachmentMetadata }) {
<div className="flex items-center gap-2 rounded-lg bg-[var(--app-bg)] px-3 py-2">
<FileIcon fileName={attachment.filename} size={24} />
<div className="min-w-0 flex-1">
<div className="truncate text-sm font-medium text-[var(--app-fg)]">
<div className="truncate text-base font-medium text-[var(--app-fg)]">
{attachment.filename}
</div>
<div className="text-xs text-[var(--app-hint)]">
+1 -1
View File
@@ -19,7 +19,7 @@ function MarkdownContent(props: MarkdownRendererProps) {
<MarkdownTextPrimitive
remarkPlugins={MARKDOWN_PLUGINS}
components={mergedComponents}
className={cn('aui-md min-w-0 max-w-full break-words text-sm')}
className={cn('aui-md min-w-0 max-w-full break-words text-base')}
/>
</TextMessagePartProvider>
)
+1 -1
View File
@@ -209,7 +209,7 @@ export function SessionActionMenu(props: SessionActionMenuProps) {
: undefined
const baseItemClassName =
'flex w-full items-center gap-3 rounded-md px-3 py-2 text-left text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--app-link)]'
'flex w-full items-center gap-3 rounded-md px-3 py-2 text-left text-base transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--app-link)]'
return (
<div
+2 -2
View File
@@ -215,7 +215,7 @@ function SessionItem(props: {
className={`h-2 w-2 rounded-full ${statusDotClass}`}
/>
</span>
<div className="truncate text-sm font-medium">
<div className="truncate text-base font-medium">
{sessionName}
</div>
</div>
@@ -390,7 +390,7 @@ export function SessionList(props: {
collapsed={isCollapsed}
/>
<div className="flex items-center gap-2 min-w-0 flex-1">
<span className="font-medium text-sm break-words" title={group.directory}>
<span className="font-medium text-base break-words" title={group.directory}>
{group.displayName}
</span>
<span className="shrink-0 text-xs text-[var(--app-hint)]">
@@ -42,7 +42,7 @@ function Pre(props: ComponentPropsWithoutRef<'pre'>) {
<pre
{...rest}
className={cn(
'aui-md-pre m-0 w-max min-w-full rounded-b-md rounded-t-none bg-[var(--app-code-bg)] p-2 text-xs',
'aui-md-pre m-0 w-max min-w-full rounded-b-md rounded-t-none bg-[var(--app-code-bg)] p-2 text-sm',
className
)}
/>
@@ -160,23 +160,23 @@ function H1(props: ComponentPropsWithoutRef<'h1'>) {
}
function H2(props: ComponentPropsWithoutRef<'h2'>) {
return <h2 {...props} className={cn('aui-md-h2 mt-3 text-sm font-semibold', props.className)} />
return <h2 {...props} className={cn('aui-md-h2 mt-3 text-base font-semibold', props.className)} />
}
function H3(props: ComponentPropsWithoutRef<'h3'>) {
return <h3 {...props} className={cn('aui-md-h3 mt-2 text-sm font-semibold', props.className)} />
return <h3 {...props} className={cn('aui-md-h3 mt-2 text-base font-semibold', props.className)} />
}
function H4(props: ComponentPropsWithoutRef<'h4'>) {
return <h4 {...props} className={cn('aui-md-h4 mt-2 text-sm font-semibold', props.className)} />
return <h4 {...props} className={cn('aui-md-h4 mt-2 text-base font-semibold', props.className)} />
}
function H5(props: ComponentPropsWithoutRef<'h5'>) {
return <h5 {...props} className={cn('aui-md-h5 mt-2 text-sm font-semibold', props.className)} />
return <h5 {...props} className={cn('aui-md-h5 mt-2 text-base font-semibold', props.className)} />
}
function H6(props: ComponentPropsWithoutRef<'h6'>) {
return <h6 {...props} className={cn('aui-md-h6 mt-2 text-sm font-semibold', props.className)} />
return <h6 {...props} className={cn('aui-md-h6 mt-2 text-base font-semibold', props.className)} />
}
function Strong(props: ComponentPropsWithoutRef<'strong'>) {
@@ -225,7 +225,7 @@ export function MarkdownText() {
<MarkdownTextPrimitive
remarkPlugins={MARKDOWN_PLUGINS}
components={defaultComponents}
className={cn('aui-md min-w-0 max-w-full break-words text-sm')}
className={cn('aui-md min-w-0 max-w-full break-words text-base')}
/>
)
}
@@ -6,7 +6,7 @@ export function SyntaxHighlighter(props: SyntaxHighlighterProps) {
return (
<div className="aui-md-codeblock min-w-0 w-full max-w-full overflow-x-auto overflow-y-hidden rounded-b-md bg-[var(--app-code-bg)]">
<pre className="shiki m-0 w-max min-w-full p-2 text-xs font-mono">
<pre className="shiki m-0 w-max min-w-full p-2 text-sm font-mono">
<code className="block">{highlighted ?? props.code}</code>
</pre>
</div>