authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-08-05 18:03:10+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-08-05 16:03:10+00:00
logde592a00e89776d2e0614f3be6e666012d90cd51
treedacbe55ace1bf68532b584f843ef736ce3a8ce34
parent2a04db134c2d88f06e5b8d61a8410cb51ea07430
signature Signed by PGP key B5690EEEBB952194

Enrich terminal font previews (#5428)


3 files changed, 84 insertions(+), 18 deletions(-)

apps/mobile/src/features/settings/appearance/components/AppearancePreviews.tsx+47-6
...@@ -1,4 +1,11 @@...@@ -1,4 +1,11 @@
1import { Platform, ScrollView, View, useColorScheme } from "react-native";1import {
2 Platform,
3 ScrollView,
4 type StyleProp,
5 type TextStyle,
6 View,
7 useColorScheme,
8} from "react-native";
29
3import { AppText as Text } from "../../../../components/AppText";10import { AppText as Text } from "../../../../components/AppText";
4import {11import {
...@@ -54,14 +61,48 @@ export function TerminalAppearancePreview(props: { readonly fontSize: number })...@@ -54,14 +61,48 @@ export function TerminalAppearancePreview(props: { readonly fontSize: number })
54 fontSize: props.fontSize,61 fontSize: props.fontSize,
55 lineHeight,62 lineHeight,
56 } as const;63 } as const;
64 // AppText stamps the sans font on every node, so nested spans must
65 // re-apply the terminal font instead of relying on inheritance, exactly
66 // like the code preview's tokens below.
67 const span = (color: string, extra?: TextStyle): StyleProp<TextStyle> => [
68 lineStyle,
69 { color, ...extra },
70 ];
5771
58 return (72 return (
59 <View className="p-4">73 <View className="p-4">
60 <Text style={[lineStyle, { color: theme.foreground }]}>$ npm run dev</Text>74 <Text style={span(theme.foreground)}>
61 <Text style={[lineStyle, { color: theme.palette[2] }]}>✓ Ready in 430ms</Text>75 <Text style={span(theme.palette[2])}>→ </Text>
62 <Text style={[lineStyle, { color: theme.foreground }]}>76 <Text style={span(theme.palette[6])}>t3code </Text>
63 Local: http://localhost:3000{" "}77 <Text style={span(theme.palette[4])}>git:(</Text>
64 <Text style={[lineStyle, { color: theme.cursorForeground }]}>▏</Text>78 <Text style={span(theme.palette[1])}>main</Text>
79 <Text style={span(theme.palette[4])}>)</Text>
80 <Text style={span(theme.palette[3])}> ✗</Text>
81 <Text style={span(theme.foreground)}> vpr dev</Text>
82 </Text>
83 <Text style={span(theme.foreground)}>
84 <Text style={span(theme.palette[2])}>VITE v7.1.1</Text>
85 <Text style={span(theme.mutedForeground)}> ready in</Text>
86 <Text style={span(theme.foreground)}> 1.24s</Text>
87 </Text>
88 <Text style={span(theme.foreground)}>
89 <Text style={span(theme.palette[2])}>→ </Text>
90 <Text style={span(theme.mutedForeground)}>Local: </Text>
91 <Text style={span(theme.palette[6], { textDecorationLine: "underline" })}>
92 http://127.0.0.1:5173/
93 </Text>
94 </Text>
95 <Text style={span(theme.foreground)}>
96 <Text style={span(theme.palette[2])}>✓ 85 passed</Text>
97 <Text style={span(theme.palette[3])}> △ 2 warnings</Text>
98 <Text style={span(theme.palette[1])}> ✗ 0 failed</Text>
99 </Text>
100 <Text style={span(theme.foreground)}>
101 <Text style={span(theme.background, { backgroundColor: theme.palette[2] })}>
102 {" READY "}
103 </Text>
104 <Text style={span(theme.mutedForeground)}> watching for changes</Text>{" "}
105 <Text style={span(theme.cursorForeground)}>▏</Text>
65 </Text>106 </Text>
66 </View>107 </View>
67 );108 );
apps/web/src/components/settings/SettingsFontPreviews.tsx+20-5
...@@ -120,11 +120,26 @@ export function CodeFontPreview() {...@@ -120,11 +120,26 @@ export function CodeFontPreview() {
120 );120 );
121}121}
122122
123const TERMINAL_PROMPT = "\x1b[2m$\x1b[0m ";123// A zsh-style prompt: green arrow, cyan project, blue/red git segment,
124// yellow dirty marker. It doubles as the echo loop's fresh-line prompt.
125const TERMINAL_PROMPT =
126 "\x1b[1;32m→\x1b[0m \x1b[1;36mt3code\x1b[0m \x1b[1;34mgit:(\x1b[1;31mmain\x1b[1;34m)\x1b[0m \x1b[1;33m✗\x1b[0m ";
127// A dev-server startup: brand line, addresses, a test summary, and a READY
128// badge. Together the lines cover bold, dim, underline, the six accent
129// colors, and a background cell, so a font choice shows every SGR the
130// terminal actually renders.
124const TERMINAL_PREVIEW_TRANSCRIPT =131const TERMINAL_PREVIEW_TRANSCRIPT =
125 `${TERMINAL_PROMPT}npm run dev\r\n` +132 `${TERMINAL_PROMPT}vpr dev\r\n` +
126 "\x1b[32m✓\x1b[0m Ready in 430ms\r\n" +133 "\r\n" +
127 "\x1b[2mLocal:\x1b[0m \x1b[36mhttp://localhost:3000\x1b[0m\r\n" +134 " \x1b[1;32mVITE\x1b[0m \x1b[32mv7.1.1\x1b[0m \x1b[2mready in\x1b[0m \x1b[1m1.24s\x1b[0m\r\n" +
135 "\r\n" +
136 " \x1b[32m→\x1b[0m \x1b[2mLocal:\x1b[0m \x1b[4;36mhttp://127.0.0.1:5173/\x1b[0m\r\n" +
137 " \x1b[32m→\x1b[0m \x1b[2mNetwork:\x1b[0m \x1b[4;36mhttp://192.168.1.24:5173/\x1b[0m\r\n" +
138 "\r\n" +
139 " \x1b[32m✓ 85 passed\x1b[0m \x1b[33m△ 2 warnings\x1b[0m \x1b[31m✗ 0 failed\x1b[0m\r\n" +
140 "\r\n" +
141 " \x1b[42;30m READY \x1b[0m \x1b[2mwatching for changes — press\x1b[0m \x1b[1mq\x1b[0m \x1b[2mto quit\x1b[0m\r\n" +
142 "\r\n" +
128 TERMINAL_PROMPT;143 TERMINAL_PROMPT;
129144
130/** The surface treats an omitted family or size as "use the built-in default". */145/** The surface treats an omitted family or size as "use the built-in default". */
...@@ -225,7 +240,7 @@ export function TerminalFontPreview({ family, size }: { family: string; size: nu...@@ -225,7 +240,7 @@ export function TerminalFontPreview({ family, size }: { family: string; size: nu
225 return (240 return (
226 <div241 <div
227 ref={mountRef}242 ref={mountRef}
228 className="relative mt-1 mb-2 h-36 overflow-hidden rounded-lg border border-border"243 className="relative mt-1 mb-2 h-52 overflow-hidden rounded-lg border border-border"
229 aria-label="Terminal font preview"244 aria-label="Terminal font preview"
230 />245 />
231 );246 );
scripts/mobile-showcase-environment.ts+17-7
...@@ -44,17 +44,27 @@ const PROJECT_SCRIPTS = JSON.stringify([...@@ -44,17 +44,27 @@ const PROJECT_SCRIPTS = JSON.stringify([
44 },44 },
45]);45]);
4646
47const SHOWCASE_TERMINAL_PROMPT =
48 "\u001b[1;32m→\u001b[0m \u001b[1;36mt3code\u001b[0m \u001b[1;34mgit:(\u001b[1;31mfeat/remote-command-center\u001b[1;34m)\u001b[0m \u001b[1;33m✗\u001b[0m ";
49
50// A dev-server startup mirroring the web settings' terminal font preview:
51// zsh-style prompt, brand line, addresses, the thread's 612-test summary,
52// and a READY badge, so the scene exercises bold, dim, underline, the six
53// accent colors, and a background cell.
47export const SHOWCASE_TERMINAL_BUFFER = [54export const SHOWCASE_TERMINAL_BUFFER = [
48 "\u001b[38;5;75m~/Code/t3code\u001b[0m \u001b[38;5;212mfeat/remote-command-center\u001b[0m",55 `${SHOWCASE_TERMINAL_PROMPT}vpr dev`,
49 "$ vp test run --changed",56 "",
57 " \u001b[1;32mVITE\u001b[0m \u001b[32mv7.1.1\u001b[0m \u001b[2mready in\u001b[0m \u001b[1m1.24s\u001b[0m",
58 "",
59 " \u001b[32m→\u001b[0m \u001b[2mLocal:\u001b[0m \u001b[4;36mhttp://127.0.0.1:5173/\u001b[0m",
60 " \u001b[32m→\u001b[0m \u001b[2mNetwork:\u001b[0m \u001b[4;36mhttp://192.168.1.24:5173/\u001b[0m",
61 " \u001b[32m→\u001b[0m \u001b[2mProject:\u001b[0m \u001b[1mt3code\u001b[0m \u001b[2m— ~/Code/t3code\u001b[0m",
50 "",62 "",
51 " \u001b[38;5;117mt3code-mobile\u001b[0m 184 passed",63 " \u001b[32m✓ 612 passed\u001b[0m \u001b[33m△ 2 warnings\u001b[0m \u001b[31m✗ 0 failed\u001b[0m",
52 " \u001b[38;5;213mclient-runtime\u001b[0m 263 passed",
53 " \u001b[38;5;221mserver\u001b[0m 165 passed",
54 "",64 "",
55 "\u001b[32m✨ 612 tests passed\u001b[0m · 3 environments online",65 " \u001b[42;30m READY \u001b[0m \u001b[2mwatching for changes — press\u001b[0m \u001b[1mq\u001b[0m \u001b[2mto quit\u001b[0m",
56 "",66 "",
57 "\u001b[38;5;75m~/Code/t3code\u001b[0m \u001b[38;5;212mfeat/remote-command-center\u001b[0m $ ",67 SHOWCASE_TERMINAL_PROMPT,
58].join("\r\n");68].join("\r\n");
5969
60const BASE_ENVIRONMENT_PRESENCE = `export function environmentLabel(count: number): string {70const BASE_ENVIRONMENT_PRESENCE = `export function environmentLabel(count: number): string {