Skip to content

Commit 241be48

Browse files
committed
Platform font and emcn chrome for pages
Pages live inside the app, and the platform — emcn, every workspace surface — renders the system stack, not the docs' Inter webfont; Inter made pages read as foreign next to the app around them. The face is now the platform stack with weights on the platform scale (400/500/600), the Inter delivery machinery (page-font.ts, the preview data-URI fetch, the public woff2) is gone, and the section filter wears emcn ChipInput's exact chrome (30px rounded-lg, --surface-5 fill flipping to --surface-4 in dark, --border, 14px, no focus ring). The docs' geometry — layout, spacing, rails, tables, code frames — is unchanged.
1 parent d71370b commit 241be48

6 files changed

Lines changed: 37 additions & 92 deletions

File tree

‎apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/preview-panel.tsx‎

Lines changed: 3 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import {
1313
usesSimArtifactStyles,
1414
} from '@/lib/workspace-files/artifact-stylesheet'
1515
import { compileSimPage, isSimPageSource } from '@/lib/workspace-files/page-compile'
16-
import { SIM_PAGE_FONT_URL, simPageFontFace } from '@/lib/workspace-files/page-font'
1716
import { useHorizontalWheelScroll } from '@/app/workspace/[workspaceId]/files/components/file-viewer/use-horizontal-wheel-scroll'
1817
import { type CsvImportFileDescriptor, useCsvTruncationImport } from './csv-import'
1918
import { DataTable } from './data-table'
@@ -145,55 +144,6 @@ const HTML_PREVIEW_BOOTSTRAP = `<script>
145144
})()
146145
</script>`
147146

148-
/**
149-
* Inter for the sandboxed preview: `font-src` allows only `data:`, so the
150-
* host fetches the app-served woff2 once per session and inlines it. Until
151-
* the fetch lands (or off-browser) the page renders on the system stack and
152-
* upgrades on the next build — a font swap, not a layout break.
153-
*/
154-
let interFontFaceCss = ''
155-
let interFontFacePromise: Promise<string> | null = null
156-
function loadInterFontFace(): Promise<string> {
157-
if (!interFontFacePromise) {
158-
interFontFacePromise = fetch(SIM_PAGE_FONT_URL)
159-
.then((response) => (response.ok ? response.arrayBuffer() : null))
160-
.then((buffer) => {
161-
if (!buffer) return ''
162-
const bytes = new Uint8Array(buffer)
163-
let binary = ''
164-
for (let i = 0; i < bytes.length; i += 0x8000) {
165-
binary += String.fromCharCode(...bytes.subarray(i, i + 0x8000))
166-
}
167-
interFontFaceCss = simPageFontFace(`data:font/woff2;base64,${btoa(binary)}`)
168-
return interFontFaceCss
169-
})
170-
.catch(() => '')
171-
}
172-
return interFontFacePromise
173-
}
174-
175-
function useSimPageFontFace(): string {
176-
const [fontFace, setFontFace] = useState(interFontFaceCss)
177-
useEffect(() => {
178-
if (interFontFaceCss) return
179-
let cancelled = false
180-
loadInterFontFace().then((value) => {
181-
if (!cancelled && value) setFontFace(value)
182-
})
183-
return () => {
184-
cancelled = true
185-
}
186-
}, [])
187-
return fontFace
188-
}
189-
190-
/**
191-
* Stamps the app's resolved theme onto the document root. The frame has an
192-
* opaque origin and inherits nothing, so `prefers-color-scheme` inside it
193-
* follows the OS rather than Sim's own toggle — a page would render light
194-
* against a dark app. Every theme-aware page keys off `[data-theme]`, so
195-
* carrying it across is what makes the two agree.
196-
*/
197147
function stampTheme(html: string, theme: 'dark' | 'light'): string {
198148
if (/<html[\s>]/i.test(html)) {
199149
return html.replace(/<html(\s[^>]*)?>/i, (match, attrs: string | undefined) =>
@@ -207,8 +157,7 @@ export function buildHtmlPreviewDocument(
207157
content: string,
208158
theme: 'dark' | 'light' = 'light',
209159
workspaceId?: string,
210-
lenient?: boolean,
211-
fontFaceCss: string = interFontFaceCss
160+
lenient?: boolean
212161
): string {
213162
// The pdf model: a page file STORES its source (frontmatter + markdown +
214163
// sim: fences) and every rendering surface compiles on demand. Partial
@@ -225,7 +174,7 @@ export function buildHtmlPreviewDocument(
225174
// token overrides follow the sheet so the app's live values beat its
226175
// fallbacks, and the shell follows both.
227176
usesSimArtifactStyles(content)
228-
? `<style>${fontFaceCss}${SIM_ARTIFACT_STYLESHEET}</style><style>${simTokenOverrides()}</style>${SIM_ARTIFACT_SHELL}`
177+
? `<style>${SIM_ARTIFACT_STYLESHEET}</style><style>${simTokenOverrides()}</style>${SIM_ARTIFACT_SHELL}`
229178
: '',
230179
HTML_PREVIEW_BOOTSTRAP,
231180
].join('')
@@ -331,16 +280,14 @@ const HtmlPreview = memo(function HtmlPreview({
331280
}) {
332281
const { resolvedTheme } = useTheme()
333282
const router = useRouter()
334-
const fontFaceCss = useSimPageFontFace()
335283
const batchedContent = useStreamBatchedValue(content, isStreaming === true, 2000)
336284
const builtContent = buildHtmlPreviewDocument(
337285
batchedContent,
338286
resolvedTheme === 'dark' ? 'dark' : 'light',
339287
workspaceId,
340288
// Mid-stream, a fence still being written is malformed by definition —
341289
// suppress the skip notices until the stream settles.
342-
isStreaming === true,
343-
fontFaceCss
290+
isStreaming === true
344291
)
345292
// AFTER the build: workspace image srcs (/api/files/view/…) only exist in
346293
// the COMPILED document — the raw source says sim:file/… — so substituting

‎apps/sim/lib/workspace-files/artifact-stylesheet.test.ts‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,10 @@ describe('docs fidelity', () => {
143143
})
144144

145145
// Inter leads the stack; each surface injects the matching @font-face.
146-
it('leads the sans stack with the docs font', () => {
147-
expect(SIM_ARTIFACT_STYLESHEET).toContain('--font-sans: "Inter",')
146+
// Pages live inside the app: the PLATFORM stack, not the docs' webfont.
147+
it('uses the platform font stack', () => {
148+
expect(SIM_ARTIFACT_STYLESHEET).toContain('--font-sans: ui-sans-serif, -apple-system')
149+
expect(SIM_ARTIFACT_STYLESHEET).not.toContain('"Inter"')
148150
})
149151

150152
// The docs' prose anchor: 500 weight, 1.5px underline offset 3.5px, hover

‎apps/sim/lib/workspace-files/artifact-stylesheet.ts‎

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ const ARTIFACT_TOKENS = [
3535
'--surface-1',
3636
'--surface-2',
3737
'--surface-3',
38+
'--surface-4',
3839
'--surface-5',
3940
'--surface-hover',
4041
'--surface-active',
@@ -111,6 +112,7 @@ export const SIM_ARTIFACT_STYLESHEET = `
111112
--surface-1: #fbfbfb;
112113
--surface-2: #ffffff;
113114
--surface-3: #f7f7f7;
115+
--surface-4: #f5f5f5;
114116
--surface-5: #f3f3f3;
115117
--surface-hover: #f2f2f2;
116118
--surface-active: #ececec;
@@ -136,7 +138,10 @@ export const SIM_ARTIFACT_STYLESHEET = `
136138
--code-bg: #f5f5f5;
137139
--code-surface: var(--surface-5);
138140
--selection-bg: #add6ff;
139-
--font-sans: "Inter", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
141+
/* The PLATFORM stack, not the docs' webfont: pages live inside the app,
142+
and emcn/sim render the system face — Inter here read as foreign. The
143+
docs' geometry survives; the face is the platform's. */
144+
--font-sans: ui-sans-serif, -apple-system, system-ui, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
140145
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
141146
--text-caption: 12px;
142147
--text-small: 13px;
@@ -149,6 +154,7 @@ export const SIM_ARTIFACT_STYLESHEET = `
149154
--surface-1: #1e1e1e;
150155
--surface-2: #232323;
151156
--surface-3: #242424;
157+
--surface-4: #2c2c2c;
152158
--surface-5: #363636;
153159
--surface-hover: #262626;
154160
--surface-active: #2c2c2c;
@@ -186,6 +192,7 @@ export const SIM_ARTIFACT_STYLESHEET = `
186192
--surface-1: #1e1e1e;
187193
--surface-2: #232323;
188194
--surface-3: #242424;
195+
--surface-4: #2c2c2c;
189196
--surface-5: #363636;
190197
--surface-hover: #262626;
191198
--surface-active: #2c2c2c;
@@ -218,9 +225,9 @@ body {
218225
-webkit-font-smoothing: antialiased;
219226
}
220227
h1, h2, h3, h4 { text-wrap: balance; }
221-
h1 { font-size: 1.5rem; font-weight: 550; letter-spacing: -0.02em; color: var(--text-primary); line-height: 1.25; margin: 0 0 0.75rem; }
228+
h1 { font-size: 1.5rem; font-weight: 600; letter-spacing: -0.02em; color: var(--text-primary); line-height: 1.25; margin: 0 0 0.75rem; }
222229
h2 { font-size: 1.25rem; font-weight: 500; letter-spacing: -0.015em; color: var(--text-primary); margin: 2.5rem 0 0.9rem; }
223-
h3, h4 { font-size: var(--text-md); font-weight: 470; letter-spacing: -0.01em; color: var(--text-body); margin: 1.75rem 0 0.5rem; }
230+
h3, h4 { font-size: var(--text-md); font-weight: 500; letter-spacing: -0.01em; color: var(--text-body); margin: 1.75rem 0 0.5rem; }
224231
p, li { max-width: 70ch; }
225232
p { margin: 0 0 1rem; }
226233
/* Content links — the docs' prose anchor (fumadocs prose, compiled): 500
@@ -236,7 +243,7 @@ a {
236243
transition: opacity 0.2s;
237244
}
238245
a:hover { opacity: 0.8; }
239-
strong { color: var(--text-primary); font-weight: 550; }
246+
strong { color: var(--text-primary); font-weight: 600; }
240247
hr { border: 0; border-top: 1px solid var(--border); margin: 3em 0; }
241248
hr + * { margin-top: 0; }
242249
/* Lists — the docs' prose metrics: 1rem/1.625em indents, muted markers. */
@@ -427,16 +434,24 @@ figcaption { font-size: 0.875em; line-height: 1.4285714; color: var(--text-prima
427434
}
428435
.art-bar-title { font-size: var(--text-sm); font-weight: 500; color: var(--text-primary); margin-right: auto; }
429436
.art-search {
430-
width: min(260px, 45vw);
437+
/* emcn ChipInput chrome verbatim (chip-chrome.ts): h-30px rounded-lg,
438+
--surface-5 fill (--surface-4 dark via token), --border-1 border, 14px
439+
text, px-2, no focus ring. */
431440
height: 30px;
432-
padding: 0 0.6rem;
433-
font-family: var(--font-sans);
434-
font-size: var(--text-small);
435-
color: var(--text-primary);
436-
background: var(--surface-5);
441+
padding: 0 0.5rem;
437442
border: 1px solid var(--border);
438443
border-radius: 0.5rem;
444+
background: var(--chip-fill, var(--surface-5));
445+
font: inherit;
446+
font-size: var(--text-sm);
447+
color: var(--text-body);
448+
outline: none;
449+
transition: background-color 0.15s, border-color 0.15s;
450+
width: 200px;
439451
}
452+
/* emcn's chip fill flips to --surface-4 in dark. */
453+
:root[data-theme="dark"] { --chip-fill: var(--surface-4); }
454+
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { --chip-fill: var(--surface-4); } }
440455
.art-search::placeholder { color: var(--text-muted); }
441456
/* Top-of-page controls: Copy page + prev/next chevrons on the title row. */
442457
.page-actions {
@@ -525,7 +540,7 @@ figcaption { font-size: 0.875em; line-height: 1.4285714; color: var(--text-prima
525540
align-items: center;
526541
gap: 6px;
527542
font-size: var(--text-small);
528-
font-weight: 480;
543+
font-weight: 500;
529544
color: var(--text-muted);
530545
margin: 0 0 0.5rem;
531546
}
@@ -537,7 +552,7 @@ figcaption { font-size: 0.875em; line-height: 1.4285714; color: var(--text-prima
537552
padding-top: 6px;
538553
padding-bottom: 6px;
539554
font-size: var(--text-small);
540-
font-weight: 430;
555+
font-weight: 400;
541556
line-height: 1.4;
542557
color: var(--text-muted);
543558
text-decoration: none;
@@ -552,12 +567,12 @@ figcaption { font-size: 0.875em; line-height: 1.4285714; color: var(--text-prima
552567
.toc-items a::after {
553568
content: attr(data-text);
554569
display: block; height: 0; visibility: hidden; overflow: hidden;
555-
font-weight: 470; pointer-events: none;
570+
font-weight: 500; pointer-events: none;
556571
}
557572
.toc-items a[data-depth="2"] { padding-left: 20px; }
558573
.toc-items a[data-depth="3"] { padding-left: 32px; }
559574
.toc-items a:hover { color: var(--text-body); }
560-
.toc-items a.is-active { color: var(--text-primary); font-weight: 470; }
575+
.toc-items a.is-active { color: var(--text-primary); font-weight: 500; }
561576
/* The track: the full outline path in foreground at 10%, and the same path in
562577
full foreground clipped to the active range — fumadocs animates the clip
563578
window, which is the "black segment" that slides as you scroll. */

‎apps/sim/lib/workspace-files/page-document.ts‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import {
44
SIM_ARTIFACT_STYLESHEET,
55
} from '@/lib/workspace-files/artifact-stylesheet'
66
import { compileSimPage } from '@/lib/workspace-files/page-compile'
7-
import { SIM_PAGE_FONT_URL, simPageFontFace } from '@/lib/workspace-files/page-font'
87

98
/**
109
* Renders page source as a fully self-contained styled document for the
@@ -21,6 +20,6 @@ export function renderSimPageDocument(source: string, options?: { workspaceId?:
2120
const compiled = compileSimPage(source, { ...options, baseUrl: getBaseUrl() })
2221
return compiled.replace(
2322
'</head>',
24-
`<style>${simPageFontFace(SIM_PAGE_FONT_URL)}${SIM_ARTIFACT_STYLESHEET}</style>${SIM_ARTIFACT_SHELL}</head>`
23+
`<style>${SIM_ARTIFACT_STYLESHEET}</style>${SIM_ARTIFACT_SHELL}</head>`
2524
)
2625
}

‎apps/sim/lib/workspace-files/page-font.ts‎

Lines changed: 0 additions & 18 deletions
This file was deleted.
-47.3 KB
Binary file not shown.

0 commit comments

Comments
 (0)