Fix UI font size scaling
This commit is contained in:
parent
fdf2df4edf
commit
1e6b9aaca5
12 changed files with 104 additions and 21 deletions
1
studio/frontend/package-lock.json
generated
1
studio/frontend/package-lock.json
generated
|
|
@ -85,6 +85,7 @@
|
|||
"eslint-plugin-react-hooks": "^7.0.1",
|
||||
"eslint-plugin-react-refresh": "^0.5.2",
|
||||
"globals": "^17.4.0",
|
||||
"postcss": "^8.5.15",
|
||||
"typescript": "~5.9.3",
|
||||
"typescript-eslint": "^8.55.0",
|
||||
"vite": "^8.0.16"
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@
|
|||
"eslint-plugin-react-hooks": "^7.0.1",
|
||||
"eslint-plugin-react-refresh": "^0.5.2",
|
||||
"globals": "^17.4.0",
|
||||
"postcss": "^8.5.15",
|
||||
"typescript": "~5.9.3",
|
||||
"typescript-eslint": "^8.55.0",
|
||||
"vite": "^8.0.16"
|
||||
|
|
|
|||
|
|
@ -52,8 +52,8 @@ export function ShutdownDialog({
|
|||
onAfterShutdown?.();
|
||||
document.body.innerHTML = `
|
||||
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100vh;font-family:sans-serif;gap:12px">
|
||||
<p style="font-size:1.1rem;font-weight:600;margin:0">Unsloth Studio has stopped.</p>
|
||||
<p style="font-size:0.9rem;color:#888;margin:0">You can now close this tab.</p>
|
||||
<p style="font-size:calc(1.1rem * var(--ui-font-scale, 1));font-weight:600;margin:0">Unsloth Studio has stopped.</p>
|
||||
<p style="font-size:calc(0.9rem * var(--ui-font-scale, 1));color:#888;margin:0">You can now close this tab.</p>
|
||||
</div>`;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -510,11 +510,14 @@ export function applyCustomizationToDocument(
|
|||
setVar("--custom-chat-font", null);
|
||||
}
|
||||
|
||||
// Scale typography without changing the root rem or layout dimensions.
|
||||
if (c.uiFontSize !== null && c.uiFontSize !== UI_FONT_SIZE_RANGE.default) {
|
||||
style.fontSize = `${c.uiFontSize}px`;
|
||||
setVar("--ui-font-scale", String(c.uiFontSize / UI_FONT_SIZE_RANGE.default));
|
||||
} else {
|
||||
style.removeProperty("font-size");
|
||||
setVar("--ui-font-scale", null);
|
||||
}
|
||||
// Clear the root size used by older builds.
|
||||
style.removeProperty("font-size");
|
||||
|
||||
if (c.codeFontSize !== null) {
|
||||
el.setAttribute("data-code-font-size", "");
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import type { ReactElement } from "react";
|
|||
import { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts";
|
||||
import {
|
||||
CHART_CONTAINER_CLASS,
|
||||
CHART_FONT_SIZE,
|
||||
DEFAULT_CHART_MARGIN,
|
||||
DEFAULT_Y_AXIS_WIDTH,
|
||||
formatAxisMetric,
|
||||
|
|
@ -70,7 +71,7 @@ export function EvalLossChartCard({
|
|||
tickLine={false}
|
||||
axisLine={false}
|
||||
tickMargin={8}
|
||||
fontSize={10}
|
||||
fontSize={CHART_FONT_SIZE}
|
||||
tickFormatter={(value) => formatStepTick(Number(value))}
|
||||
interval="preserveStartEnd"
|
||||
/>
|
||||
|
|
@ -81,7 +82,7 @@ export function EvalLossChartCard({
|
|||
axisLine={false}
|
||||
tickMargin={8}
|
||||
tickCount={5}
|
||||
fontSize={10}
|
||||
fontSize={CHART_FONT_SIZE}
|
||||
width={DEFAULT_Y_AXIS_WIDTH}
|
||||
tickFormatter={(value) => formatAxisMetric(Number(value))}
|
||||
/>
|
||||
|
|
@ -132,7 +133,7 @@ export function EvalLossChartCard({
|
|||
tickLine={false}
|
||||
axisLine={false}
|
||||
tickMargin={8}
|
||||
fontSize={10}
|
||||
fontSize={CHART_FONT_SIZE}
|
||||
interval="preserveStartEnd"
|
||||
/>
|
||||
<YAxis
|
||||
|
|
@ -140,7 +141,7 @@ export function EvalLossChartCard({
|
|||
axisLine={false}
|
||||
tickMargin={8}
|
||||
tickCount={5}
|
||||
fontSize={10}
|
||||
fontSize={CHART_FONT_SIZE}
|
||||
width={DEFAULT_Y_AXIS_WIDTH}
|
||||
/>
|
||||
<Line
|
||||
|
|
|
|||
|
|
@ -15,8 +15,9 @@ import type { ReactElement } from "react";
|
|||
import { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts";
|
||||
import type { ScaleMode } from "./types";
|
||||
import {
|
||||
CHART_SYNC_ID,
|
||||
CHART_CONTAINER_CLASS,
|
||||
CHART_FONT_SIZE,
|
||||
CHART_SYNC_ID,
|
||||
DEFAULT_CHART_MARGIN,
|
||||
DEFAULT_Y_AXIS_WIDTH,
|
||||
formatAxisMetric,
|
||||
|
|
@ -78,7 +79,7 @@ export function GradNormChartCard({
|
|||
tickLine={false}
|
||||
axisLine={false}
|
||||
tickMargin={8}
|
||||
fontSize={10}
|
||||
fontSize={CHART_FONT_SIZE}
|
||||
tickFormatter={(value) => formatStepTick(Number(value))}
|
||||
interval="preserveStartEnd"
|
||||
/>
|
||||
|
|
@ -89,7 +90,7 @@ export function GradNormChartCard({
|
|||
axisLine={false}
|
||||
tickMargin={8}
|
||||
tickCount={5}
|
||||
fontSize={10}
|
||||
fontSize={CHART_FONT_SIZE}
|
||||
width={DEFAULT_Y_AXIS_WIDTH}
|
||||
tickFormatter={(value) => {
|
||||
const num = Number(value);
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts";
|
|||
import type { ScaleMode } from "./types";
|
||||
import {
|
||||
CHART_CONTAINER_CLASS,
|
||||
CHART_FONT_SIZE,
|
||||
CHART_SYNC_ID,
|
||||
DEFAULT_CHART_MARGIN,
|
||||
DEFAULT_Y_AXIS_WIDTH,
|
||||
|
|
@ -76,7 +77,7 @@ export function LearningRateChartCard({
|
|||
tickLine={false}
|
||||
axisLine={false}
|
||||
tickMargin={8}
|
||||
fontSize={10}
|
||||
fontSize={CHART_FONT_SIZE}
|
||||
tickFormatter={(value) => formatStepTick(Number(value))}
|
||||
interval="preserveStartEnd"
|
||||
/>
|
||||
|
|
@ -87,7 +88,7 @@ export function LearningRateChartCard({
|
|||
axisLine={false}
|
||||
tickMargin={8}
|
||||
tickCount={5}
|
||||
fontSize={10}
|
||||
fontSize={CHART_FONT_SIZE}
|
||||
width={DEFAULT_Y_AXIS_WIDTH}
|
||||
tickFormatter={(value) => {
|
||||
const num = Number(value);
|
||||
|
|
|
|||
|
|
@ -22,8 +22,9 @@ import {
|
|||
} from "recharts";
|
||||
import type { ScaleMode } from "./types";
|
||||
import {
|
||||
CHART_SYNC_ID,
|
||||
CHART_CONTAINER_CLASS,
|
||||
CHART_FONT_SIZE,
|
||||
CHART_SYNC_ID,
|
||||
DEFAULT_CHART_MARGIN,
|
||||
DEFAULT_Y_AXIS_WIDTH,
|
||||
formatAxisMetric,
|
||||
|
|
@ -96,7 +97,7 @@ export function TrainingLossChartCard({
|
|||
tickLine={false}
|
||||
axisLine={false}
|
||||
tickMargin={8}
|
||||
fontSize={10}
|
||||
fontSize={CHART_FONT_SIZE}
|
||||
tickFormatter={(value) => formatStepTick(Number(value))}
|
||||
interval="preserveStartEnd"
|
||||
/>
|
||||
|
|
@ -107,7 +108,7 @@ export function TrainingLossChartCard({
|
|||
axisLine={false}
|
||||
tickMargin={8}
|
||||
tickCount={5}
|
||||
fontSize={10}
|
||||
fontSize={CHART_FONT_SIZE}
|
||||
width={DEFAULT_Y_AXIS_WIDTH}
|
||||
tickFormatter={(value) => {
|
||||
const num = Number(value);
|
||||
|
|
@ -152,7 +153,7 @@ export function TrainingLossChartCard({
|
|||
value: formatMetric(avgRaw),
|
||||
}),
|
||||
position: "insideTopRight",
|
||||
fontSize: 10,
|
||||
fontSize: CHART_FONT_SIZE,
|
||||
fill: "#3b82f6",
|
||||
}}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ export const DEFAULT_VISIBLE_POINTS = 160;
|
|||
export const CHART_CONTAINER_CLASS = "h-[220px] w-full";
|
||||
export const DEFAULT_CHART_MARGIN = { top: 4, right: 8, bottom: 0, left: 4 };
|
||||
export const DEFAULT_Y_AXIS_WIDTH = 45;
|
||||
export const CHART_FONT_SIZE = "calc(10px * var(--ui-font-scale, 1))";
|
||||
const TRAILING_ZEROES_RE = /\.?0+$/;
|
||||
const NEGATIVE_ZERO_RE = /^-0$/;
|
||||
|
||||
|
|
|
|||
|
|
@ -140,13 +140,13 @@ export function TrainingSection() {
|
|||
tickLine={false}
|
||||
axisLine={false}
|
||||
tickMargin={8}
|
||||
fontSize={10}
|
||||
fontSize="calc(10px * var(--ui-font-scale, 1))"
|
||||
/>
|
||||
<YAxis
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
tickMargin={8}
|
||||
fontSize={10}
|
||||
fontSize="calc(10px * var(--ui-font-scale, 1))"
|
||||
/>
|
||||
<Line
|
||||
type="monotone"
|
||||
|
|
|
|||
|
|
@ -577,6 +577,11 @@ html[data-code-font-size] :is(pre, code, kbd, samp) {
|
|||
font-size: var(--custom-code-font-size) !important;
|
||||
}
|
||||
|
||||
/* Scale inherited text without changing the root rem used by layout tokens. */
|
||||
body {
|
||||
font-size: calc(1rem * var(--ui-font-scale, 1));
|
||||
}
|
||||
|
||||
/* Pointer cursors: hand cursor over interactive elements. */
|
||||
html.pointer-cursors
|
||||
:is(
|
||||
|
|
|
|||
|
|
@ -4,11 +4,79 @@
|
|||
import path from "node:path";
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import { defineConfig } from "vite";
|
||||
import postcss, { type Plugin as PostcssPlugin } from "postcss";
|
||||
import { type Plugin as VitePlugin, defineConfig } from "vite";
|
||||
|
||||
const UI_FONT_SCALE_VAR = "--ui-font-scale";
|
||||
const CSS_WIDE_FONT_SIZE_PATTERN =
|
||||
/^(?:inherit|initial|revert(?:-layer)?|unset|smaller|larger)$/;
|
||||
const RELATIVE_FONT_SIZE_PATTERN =
|
||||
/(?:^|[^\w.-])-?(?:\d*\.)?\d+(?:em|ex|cap|ch|ic|lh|rlh|%)\b/;
|
||||
const ABSOLUTE_FONT_SIZE_PATTERN =
|
||||
/-?(?:\d*\.)?\d+(?:px|pt|pc|rem|cm|mm|q|in)\b/;
|
||||
const FONT_SIZE_FUNCTION_PATTERN = /\b(?:calc|clamp|max|min|var)\(/;
|
||||
const CSS_MODULE_ID_PATTERN = /\.css(?:$|\?)/;
|
||||
|
||||
// Relative sizes inherit the scale and must not compound through nested text.
|
||||
function shouldScaleFontSize(value: string): boolean {
|
||||
const normalized = value.trim().toLowerCase();
|
||||
if (
|
||||
normalized.includes(UI_FONT_SCALE_VAR) ||
|
||||
normalized.includes("--custom-code-font-size") ||
|
||||
CSS_WIDE_FONT_SIZE_PATTERN.test(normalized)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Root rem stays fixed, so rem typography still needs the multiplier.
|
||||
if (RELATIVE_FONT_SIZE_PATTERN.test(normalized)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
ABSOLUTE_FONT_SIZE_PATTERN.test(normalized) ||
|
||||
FONT_SIZE_FUNCTION_PATTERN.test(normalized)
|
||||
);
|
||||
}
|
||||
|
||||
const scaleAbsoluteFontSizes: PostcssPlugin = {
|
||||
postcssPlugin: "unsloth-ui-font-scaling",
|
||||
// biome-ignore lint/style/useNamingConvention: PostCSS visitor API key.
|
||||
Declaration: {
|
||||
"font-size": (declaration) => {
|
||||
if (!shouldScaleFontSize(declaration.value)) {
|
||||
return;
|
||||
}
|
||||
declaration.value = `calc(${declaration.value} * var(${UI_FONT_SCALE_VAR}, 1))`;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Scale named, arbitrary, and vendor font sizes in Tailwind's generated CSS.
|
||||
function uiFontScalingPlugin(): VitePlugin {
|
||||
return {
|
||||
name: "unsloth-ui-font-scaling",
|
||||
// Run after Tailwind and before Vite converts CSS into a module.
|
||||
enforce: "pre",
|
||||
async transform(code, id) {
|
||||
if (!CSS_MODULE_ID_PATTERN.test(id)) {
|
||||
return null;
|
||||
}
|
||||
const result = await postcss([scaleAbsoluteFontSizes]).process(code, {
|
||||
from: id,
|
||||
map: false,
|
||||
});
|
||||
if (result.css === code) {
|
||||
return null;
|
||||
}
|
||||
return { code: result.css, map: null };
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react(), tailwindcss()],
|
||||
plugins: [react(), tailwindcss(), uiFontScalingPlugin()],
|
||||
optimizeDeps: {
|
||||
include: ["@dagrejs/dagre", "@dagrejs/graphlib"],
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue