字号调整

This commit is contained in:
fengxiangdebaozi committed 2026-06-01 22:17:39 +08:00
1 parent 0c5938a5a3
commit fa41d620fd
6 files changed
+68 -28

No files matched your search

+10 -4
View File
@@ -6,8 +6,10 @@ import { useLocalSettings } from "@/core/settings";
/** /**
* 基准字号 = 16px,对应《深蓝色 UI 规范》§2.1.2 后台系统正文字号(16px Regular)。 * 基准字号 = 16px,对应《深蓝色 UI 规范》§2.1.2 后台系统正文字号(16px Regular)。
* 用户在「外观 → 字号」里选择的倍数作用于 <html> 根元素的 font-size;由于 Tailwind * 用户在「外观 → 字号」里选择的倍数通过 CSS 变量 `--ui-font-scale` 生效:globals.css
* 的字号 / 间距基于 rem,整套界面会随之按比例放大 / 缩小。`fontScale === 1` 即默认 16px。 * 只把 Tailwind 的 `--text-*` 字号 token 乘以该倍数,**不动间距 token**,所以放大 / 缩小
* 的只有文字本身,padding / margin / 宽高等布局尺寸保持不变,不会把页面挤错位。
* `fontScale === 1` 即默认 16px。
*/ */
export const BASE_FONT_PX = 16; export const BASE_FONT_PX = 16;
@@ -20,7 +22,8 @@ export const FONT_SCALE_OPTIONS: { value: number; label: string; px: number }[]
]; ];
/** /**
* 将用户选择的字号倍数应用到 <html> 根元素。挂在应用根部,保证刷新后即时生效、全局统一。 * 把用户选择的字号倍数写到 <html> 的 `--ui-font-scale` CSS 变量。挂在应用根部,
* 保证刷新后即时生效、全局统一。只设变量、不直接改 font-size,因此布局不受影响。
* 不渲染任何 DOM。 * 不渲染任何 DOM。
*/ */
export function FontScaleApplier() { export function FontScaleApplier() {
@@ -32,7 +35,10 @@ export function FontScaleApplier() {
typeof fontScale === "number" && Number.isFinite(fontScale) && fontScale > 0 typeof fontScale === "number" && Number.isFinite(fontScale) && fontScale > 0
? fontScale ? fontScale
: 1; : 1;
document.documentElement.style.fontSize = `${BASE_FONT_PX * scale}px`; const root = document.documentElement;
root.style.setProperty("--ui-font-scale", String(scale));
// 清掉旧实现可能残留的根级 font-size 内联样式(旧版用它做整页缩放)。
root.style.removeProperty("font-size");
}, [fontScale]); }, [fontScale]);
return null; return null;
@@ -56,12 +56,12 @@ export function MessageBubble({
{senderName} {senderName}
</span> </span>
{badge && ( {badge && (
<span className="text-[9px] font-mono font-black uppercase text-[var(--brand-900)] bg-[var(--brand-200)] border border-red-200 px-1.5 py-0.2 rounded"> <span className="text-[calc(9px*var(--ui-font-scale,1))] font-mono font-black uppercase text-[var(--brand-900)] bg-[var(--brand-200)] border border-red-200 px-1.5 py-0.2 rounded">
{badge} {badge}
</span> </span>
)} )}
</div> </div>
<span className="text-[10px] text-slate-400 font-mono font-medium shrink-0">{time}</span> <span className="text-[calc(10px*var(--ui-font-scale,1))] text-slate-400 font-mono font-medium shrink-0">{time}</span>
</div> </div>
{children} {children}
</div> </div>
@@ -139,13 +139,13 @@ export const sharedMarkdownComponents = {
const isBlock = /language-/.test(className ?? ""); const isBlock = /language-/.test(className ?? "");
if (isBlock) { if (isBlock) {
return ( return (
<pre className="bg-slate-100 text-slate-800 rounded-md p-2 my-1 overflow-x-auto text-[11px] font-mono"> <pre className="bg-slate-100 text-slate-800 rounded-md p-2 my-1 overflow-x-auto text-[calc(11px*var(--ui-font-scale,1))] font-mono">
<code>{children}</code> <code>{children}</code>
</pre> </pre>
); );
} }
return ( return (
<code className="bg-slate-100 text-slate-800 rounded px-1 py-0.5 text-[11px] font-mono"> <code className="bg-slate-100 text-slate-800 rounded px-1 py-0.5 text-[calc(11px*var(--ui-font-scale,1))] font-mono">
{children} {children}
</code> </code>
); );
@@ -161,7 +161,7 @@ export const sharedMarkdownComponents = {
</a> </a>
), ),
table: ({ children }: { children?: React.ReactNode }) => ( table: ({ children }: { children?: React.ReactNode }) => (
<table className="my-1 border-collapse text-[11px]">{children}</table> <table className="my-1 border-collapse text-[calc(11px*var(--ui-font-scale,1))]">{children}</table>
), ),
th: ({ children }: { children?: React.ReactNode }) => ( th: ({ children }: { children?: React.ReactNode }) => (
<th className="border border-slate-300 px-1.5 py-0.5 bg-slate-50 font-bold text-left"> <th className="border border-slate-300 px-1.5 py-0.5 bg-slate-50 font-bold text-left">
@@ -254,7 +254,7 @@ export function ReasoningBlock({
<button <button
type="button" type="button"
onClick={handleToggle} onClick={handleToggle}
className="inline-flex items-center gap-1.5 text-[11.5px] text-slate-500 hover:text-slate-700 transition-colors cursor-pointer" className="inline-flex items-center gap-1.5 text-[calc(11.5px*var(--ui-font-scale,1))] text-slate-500 hover:text-slate-700 transition-colors cursor-pointer"
> >
<BrainIcon className={cn("size-3.5", streaming && "text-primary animate-pulse")} /> <BrainIcon className={cn("size-3.5", streaming && "text-primary animate-pulse")} />
<span className={cn(streaming && "animate-pulse")}>{headerLabel}</span> <span className={cn(streaming && "animate-pulse")}>{headerLabel}</span>
@@ -265,7 +265,7 @@ export function ReasoningBlock({
{open && ( {open && (
<div <div
className={cn( className={cn(
"rounded-md border border-slate-200 bg-slate-50/60 px-3 py-2 text-[11.5px] text-slate-600 leading-relaxed", "rounded-md border border-slate-200 bg-slate-50/60 px-3 py-2 text-[calc(11.5px*var(--ui-font-scale,1))] text-slate-600 leading-relaxed",
contentClassName, contentClassName,
)} )}
> >
@@ -86,10 +86,10 @@ export function MessageStepsCard({
const display = getStepDisplay(lastReasoning.key, lastReasoning.label, lastReasoning.args); const display = getStepDisplay(lastReasoning.key, lastReasoning.label, lastReasoning.args);
const Icon = display.icon; const Icon = display.icon;
return ( return (
<div className={cn("flex items-center gap-2 text-[11.5px] text-slate-500 py-0.5", className)}> <div className={cn("flex items-center gap-2 text-[calc(11.5px*var(--ui-font-scale,1))] text-slate-500 py-0.5", className)}>
<Icon className="size-3.5 text-slate-400" /> <Icon className="size-3.5 text-slate-400" />
<span>{display.label}</span> <span>{display.label}</span>
<span className="text-[10px] text-slate-400 font-mono">{lastReasoning.ts}</span> <span className="text-[calc(10px*var(--ui-font-scale,1))] text-slate-400 font-mono">{lastReasoning.ts}</span>
</div> </div>
); );
} }
@@ -108,7 +108,7 @@ export function MessageStepsCard({
> >
<ChainOfThoughtStep <ChainOfThoughtStep
label={ label={
<span className="text-[11.5px] opacity-60"> <span className="text-[calc(11.5px*var(--ui-font-scale,1))] opacity-60">
{showAbove ? "隐藏步骤" : `查看其他 ${priorToolSteps.length} 个步骤`} {showAbove ? "隐藏步骤" : `查看其他 ${priorToolSteps.length} 个步骤`}
</span> </span>
} }
@@ -189,9 +189,9 @@ function ToolStep({
: undefined : undefined
} }
label={ label={
<span className="text-[12px] text-slate-700"> <span className="text-[calc(12px*var(--ui-font-scale,1))] text-slate-700">
<span className="font-medium">{display.label}</span> <span className="font-medium">{display.label}</span>
<span className="ml-2 text-[10px] text-slate-400 font-mono">{step.ts}</span> <span className="ml-2 text-[calc(10px*var(--ui-font-scale,1))] text-slate-400 font-mono">{step.ts}</span>
</span> </span>
} }
> >
@@ -204,10 +204,10 @@ function ReasoningStep({ step, streaming }: { step: BubbleStep; streaming: boole
const display = getStepDisplay(step.key, step.label, step.args); const display = getStepDisplay(step.key, step.label, step.args);
const Icon = display.icon; const Icon = display.icon;
return ( return (
<div className="flex items-center gap-2 text-[11.5px] text-slate-500"> <div className="flex items-center gap-2 text-[calc(11.5px*var(--ui-font-scale,1))] text-slate-500">
<Icon className={cn("size-3.5", streaming ? "text-primary animate-pulse" : "text-slate-400")} /> <Icon className={cn("size-3.5", streaming ? "text-primary animate-pulse" : "text-slate-400")} />
<span>{display.label}</span> <span>{display.label}</span>
<span className="text-[10px] text-slate-400 font-mono">{step.ts}</span> <span className="text-[calc(10px*var(--ui-font-scale,1))] text-slate-400 font-mono">{step.ts}</span>
</div> </div>
); );
} }
@@ -167,7 +167,7 @@ export function Step2Panel(props: Step2PanelProps) {
<Cpu className="w-5 h-5 text-primary" strokeWidth={2.5} /> <Cpu className="w-5 h-5 text-primary" strokeWidth={2.5} />
<div> <div>
<h3 className="text-sm font-extrabold text-slate-900">多智能体圆桌会商中心</h3> <h3 className="text-sm font-extrabold text-slate-900">多智能体圆桌会商中心</h3>
<p className="text-[10px] text-slate-400 font-medium"> <p className="text-[calc(10px*var(--ui-font-scale,1))] text-slate-400 font-medium">
分布式领域专家实时动态博弈与纳什协商收敛中 分布式领域专家实时动态博弈与纳什协商收敛中
</p> </p>
</div> </div>
@@ -202,7 +202,7 @@ export function Step2Panel(props: Step2PanelProps) {
<button <button
type="button" type="button"
onClick={onResumeRoundtable} onClick={onResumeRoundtable}
className={`bg-emerald-100 hover:bg-emerald-200 text-emerald-800 text-[10px] font-bold px-2.5 py-1 rounded-xl flex items-center gap-1 transition border border-emerald-200/40 shadow-sm ${ className={`bg-emerald-100 hover:bg-emerald-200 text-emerald-800 text-[calc(10px*var(--ui-font-scale,1))] font-bold px-2.5 py-1 rounded-xl flex items-center gap-1 transition border border-emerald-200/40 shadow-sm ${
pauseFlash pauseFlash
? "ring-2 ring-emerald-400 ring-offset-1 animate-pulse" ? "ring-2 ring-emerald-400 ring-offset-1 animate-pulse"
: "" : ""
@@ -215,7 +215,7 @@ export function Step2Panel(props: Step2PanelProps) {
<button <button
type="button" type="button"
onClick={onPauseRoundtable} onClick={onPauseRoundtable}
className="bg-amber-100 hover:bg-amber-200 text-amber-800 text-[10px] font-bold px-2.5 py-1 rounded-xl flex items-center gap-1 transition border border-amber-200/40 shadow-sm" className="bg-amber-100 hover:bg-amber-200 text-amber-800 text-[calc(10px*var(--ui-font-scale,1))] font-bold px-2.5 py-1 rounded-xl flex items-center gap-1 transition border border-amber-200/40 shadow-sm"
> >
<Pause className="w-3.5 h-3.5" /> <Pause className="w-3.5 h-3.5" />
暂停会商 暂停会商
@@ -279,13 +279,13 @@ export function Step2Panel(props: Step2PanelProps) {
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className="text-xs font-extrabold text-amber-900">总控协调需要澄清</span> <span className="text-xs font-extrabold text-amber-900">总控协调需要澄清</span>
{pendingClarification.clarificationType && ( {pendingClarification.clarificationType && (
<span className="text-[9px] font-mono font-black uppercase text-amber-900 bg-amber-200 border border-amber-300 px-1.5 py-0.2 rounded"> <span className="text-[calc(9px*var(--ui-font-scale,1))] font-mono font-black uppercase text-amber-900 bg-amber-200 border border-amber-300 px-1.5 py-0.2 rounded">
{pendingClarification.clarificationType} {pendingClarification.clarificationType}
</span> </span>
)} )}
</div> </div>
{pendingClarification.context && ( {pendingClarification.context && (
<p className="text-[11px] text-amber-800 italic">{pendingClarification.context}</p> <p className="text-[calc(11px*var(--ui-font-scale,1))] text-amber-800 italic">{pendingClarification.context}</p>
)} )}
<p className="text-xs text-slate-800 leading-relaxed font-medium whitespace-pre-wrap break-words"> <p className="text-xs text-slate-800 leading-relaxed font-medium whitespace-pre-wrap break-words">
{pendingClarification.question} {pendingClarification.question}
@@ -306,7 +306,7 @@ export function Step2Panel(props: Step2PanelProps) {
key={`${idx}-${label}`} key={`${idx}-${label}`}
type="button" type="button"
onClick={() => onSubmitClarification(label)} onClick={() => onSubmitClarification(label)}
className="text-[11px] font-bold px-3 py-1.5 rounded-full bg-white border border-amber-300 text-amber-900 hover:bg-amber-100 transition active:scale-95 cursor-pointer" className="text-[calc(11px*var(--ui-font-scale,1))] font-bold px-3 py-1.5 rounded-full bg-white border border-amber-300 text-amber-900 hover:bg-amber-100 transition active:scale-95 cursor-pointer"
> >
{label} {label}
</button> </button>
@@ -580,17 +580,17 @@ function PresentFileCard({
<div className="flex items-start gap-2"> <div className="flex items-start gap-2">
<FileIcon className="mt-0.5 size-4 shrink-0 text-slate-400" /> <FileIcon className="mt-0.5 size-4 shrink-0 text-slate-400" />
<span <span
className="truncate text-[12px] font-medium text-slate-700" className="truncate text-[calc(12px*var(--ui-font-scale,1))] font-medium text-slate-700"
title={filename} title={filename}
> >
{filename} {filename}
</span> </span>
</div> </div>
<div className="flex items-center justify-between gap-2"> <div className="flex items-center justify-between gap-2">
<span className="rounded bg-slate-100 px-1.5 py-0.5 text-[10px] font-mono font-semibold text-slate-500"> <span className="rounded bg-slate-100 px-1.5 py-0.5 text-[calc(10px*var(--ui-font-scale,1))] font-mono font-semibold text-slate-500">
{typeLabel} {typeLabel}
</span> </span>
<span className="text-[10px] text-slate-400">点击预览</span> <span className="text-[calc(10px*var(--ui-font-scale,1))] text-slate-400">点击预览</span>
</div> </div>
</button> </button>
); );
@@ -1,4 +1,7 @@
@import "tailwindcss"; /* 注意:不要在这里 `@import "tailwindcss"`。Tailwind 已由全局 globals.css 引入,
* 本文件被打进同一份样式表,再次 import 会把整套 theme/utilities 重复发一遍
* (CSS 体积翻倍,且重复发出的 --text-* 等主题变量会和全局覆盖打架)。
* 本文件只写圆桌页自己的作用域规则即可。 */
/* 圆桌规划 — 浅色红色主题(对齐 docs/浅红色ui修改.md) */ /* 圆桌规划 — 浅色红色主题(对齐 docs/浅红色ui修改.md) */
.roundtable-planning { .roundtable-planning {
+31
View File
@@ -820,3 +820,34 @@
border-color: var(--border); border-color: var(--border);
outline-color: var(--ring); outline-color: var(--ring);
} }
/* ─── 全局字号缩放(只改字号,不动布局)──────────────────────────────────
* 用户在「设置 → 外观 → 字号」里选的倍数由 FontScaleApplier 写到
* <html> 的 `--ui-font-scale`(默认 1)。这里只把 Tailwind 的 `--text-*`
* 字号 token 乘以该倍数 —— 间距 token(--spacing)原样不动,所以 padding /
* margin / gap / 宽高这些布局尺寸不随字号变化,避免放大字体把页面挤错位。
* 行高用 Tailwind 默认的无单位比值,会跟随字号等比变化(文字不会重叠)。
* 不在 @layer 内,优先级高于 Tailwind 的主题层变量,确保覆盖生效。 */
:root {
--ui-font-scale: 1;
--text-xs: calc(0.75rem * var(--ui-font-scale, 1));
--text-sm: calc(0.875rem * var(--ui-font-scale, 1));
--text-base: calc(1rem * var(--ui-font-scale, 1));
--text-lg: calc(1.125rem * var(--ui-font-scale, 1));
--text-xl: calc(1.25rem * var(--ui-font-scale, 1));
--text-2xl: calc(1.5rem * var(--ui-font-scale, 1));
--text-3xl: calc(1.875rem * var(--ui-font-scale, 1));
--text-4xl: calc(2.25rem * var(--ui-font-scale, 1));
--text-5xl: calc(3rem * var(--ui-font-scale, 1));
--text-6xl: calc(3.75rem * var(--ui-font-scale, 1));
--text-7xl: calc(4.5rem * var(--ui-font-scale, 1));
--text-8xl: calc(6rem * var(--ui-font-scale, 1));
--text-9xl: calc(8rem * var(--ui-font-scale, 1));
}
/* 没有显式 text-* 类的元素(继承自 body 的默认正文)也跟随缩放。
* 只设 font-size,不影响任何盒模型尺寸。 */
body {
font-size: calc(1rem * var(--ui-font-scale, 1));
}