@prestack/thread-ui 0.1.10 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +187 -0
- package/dist/ui/attach.js +6 -7
- package/dist/ui/compositions.d.ts +0 -8
- package/dist/ui/compositions.js +3 -166
- package/dist/ui/generators/routes.js +1 -0
- package/package.json +2 -2
- package/dist/ui/animations/anm_appear.ts +0 -13
- package/dist/ui/animations/anm_blur_in.ts +0 -19
- package/dist/ui/animations/anm_blur_out.ts +0 -18
- package/dist/ui/animations/anm_dismiss.ts +0 -18
- package/dist/ui/animations/anm_drop.ts +0 -19
- package/dist/ui/animations/anm_fall.ts +0 -18
- package/dist/ui/animations/anm_flash.ts +0 -18
- package/dist/ui/animations/anm_glow.ts +0 -16
- package/dist/ui/animations/anm_lift.ts +0 -16
- package/dist/ui/animations/anm_nudge.ts +0 -16
- package/dist/ui/animations/anm_pop_in.ts +0 -23
- package/dist/ui/animations/anm_pop_out.ts +0 -22
- package/dist/ui/animations/anm_press.ts +0 -12
- package/dist/ui/animations/anm_pulse.ts +0 -13
- package/dist/ui/animations/anm_rise.ts +0 -19
- package/dist/ui/animations/anm_shake.ts +0 -13
- package/dist/ui/animations/anm_slide_in_left.ts +0 -19
- package/dist/ui/animations/anm_slide_in_right.ts +0 -19
- package/dist/ui/animations/anm_slide_out_left.ts +0 -18
- package/dist/ui/animations/anm_slide_out_right.ts +0 -18
- package/dist/ui/animations/anm_spin.ts +0 -12
- package/dist/ui/animations/anm_swell.ts +0 -16
- package/dist/ui/animations/anm_wobble.ts +0 -24
- package/dist/ui/animations/anm_zoom_in.ts +0 -19
- package/dist/ui/animations/anm_zoom_out.ts +0 -18
- package/dist/ui/animations.ts +0 -308
- package/dist/ui/attach.ts +0 -553
- package/dist/ui/charts.tsx +0 -69
- package/dist/ui/checked.ts +0 -32
- package/dist/ui/collector.ts +0 -140
- package/dist/ui/compositions.ts +0 -613
- package/dist/ui/consent.ts +0 -55
- package/dist/ui/content.tsx +0 -27
- package/dist/ui/contrast.ts +0 -27
- package/dist/ui/controls.tsx +0 -25
- package/dist/ui/disclosure.tsx +0 -20
- package/dist/ui/dismiss-layer.ts +0 -87
- package/dist/ui/door.ts +0 -140
- package/dist/ui/drag.ts +0 -316
- package/dist/ui/elements/accordion.tsx +0 -47
- package/dist/ui/elements/accordion.works.ts +0 -66
- package/dist/ui/elements/aspect-ratio.tsx +0 -31
- package/dist/ui/elements/aspect-ratio.works.ts +0 -5
- package/dist/ui/elements/avatar.tsx +0 -34
- package/dist/ui/elements/avatar.works.ts +0 -16
- package/dist/ui/elements/badge.tsx +0 -33
- package/dist/ui/elements/badge.works.ts +0 -9
- package/dist/ui/elements/bars.tsx +0 -50
- package/dist/ui/elements/bars.works.ts +0 -29
- package/dist/ui/elements/box.tsx +0 -36
- package/dist/ui/elements/breadcrumb.tsx +0 -38
- package/dist/ui/elements/breadcrumb.works.ts +0 -24
- package/dist/ui/elements/button.tsx +0 -46
- package/dist/ui/elements/button.works.tsx +0 -52
- package/dist/ui/elements/column.tsx +0 -29
- package/dist/ui/elements/consent.tsx +0 -71
- package/dist/ui/elements/consent.works.tsx +0 -132
- package/dist/ui/elements/donut.tsx +0 -45
- package/dist/ui/elements/donut.works.ts +0 -42
- package/dist/ui/elements/each.tsx +0 -34
- package/dist/ui/elements/each.works.tsx +0 -82
- package/dist/ui/elements/empty.tsx +0 -33
- package/dist/ui/elements/field.tsx +0 -45
- package/dist/ui/elements/field.works.ts +0 -65
- package/dist/ui/elements/funnel.tsx +0 -50
- package/dist/ui/elements/funnel.works.ts +0 -37
- package/dist/ui/elements/gauge.tsx +0 -40
- package/dist/ui/elements/gauge.works.ts +0 -42
- package/dist/ui/elements/glyph.tsx +0 -30
- package/dist/ui/elements/glyph.works.tsx +0 -42
- package/dist/ui/elements/grid.tsx +0 -29
- package/dist/ui/elements/heatmap.tsx +0 -48
- package/dist/ui/elements/heatmap.works.ts +0 -24
- package/dist/ui/elements/hit-box.tsx +0 -47
- package/dist/ui/elements/hit-box.works.tsx +0 -576
- package/dist/ui/elements/image.tsx +0 -29
- package/dist/ui/elements/line-chart.tsx +0 -45
- package/dist/ui/elements/line-chart.works.ts +0 -41
- package/dist/ui/elements/link.tsx +0 -38
- package/dist/ui/elements/link.works.ts +0 -25
- package/dist/ui/elements/media.tsx +0 -57
- package/dist/ui/elements/menu.tsx +0 -51
- package/dist/ui/elements/menu.works.tsx +0 -154
- package/dist/ui/elements/money.tsx +0 -36
- package/dist/ui/elements/money.works.ts +0 -9
- package/dist/ui/elements/pagination.tsx +0 -39
- package/dist/ui/elements/pagination.works.ts +0 -20
- package/dist/ui/elements/progress.tsx +0 -34
- package/dist/ui/elements/progress.works.ts +0 -8
- package/dist/ui/elements/radio-group.tsx +0 -50
- package/dist/ui/elements/radio-group.works.ts +0 -33
- package/dist/ui/elements/row.tsx +0 -29
- package/dist/ui/elements/scatter.tsx +0 -42
- package/dist/ui/elements/scatter.works.ts +0 -38
- package/dist/ui/elements/select.tsx +0 -43
- package/dist/ui/elements/select.works.ts +0 -5
- package/dist/ui/elements/separator.tsx +0 -31
- package/dist/ui/elements/skeleton.tsx +0 -31
- package/dist/ui/elements/slider.tsx +0 -34
- package/dist/ui/elements/slider.works.ts +0 -5
- package/dist/ui/elements/sparkline.tsx +0 -37
- package/dist/ui/elements/sparkline.works.ts +0 -32
- package/dist/ui/elements/spinner.tsx +0 -33
- package/dist/ui/elements/stack.tsx +0 -29
- package/dist/ui/elements/stat.tsx +0 -36
- package/dist/ui/elements/stat.works.ts +0 -14
- package/dist/ui/elements/surface.tsx +0 -36
- package/dist/ui/elements/surface.works.ts +0 -9
- package/dist/ui/elements/table.tsx +0 -62
- package/dist/ui/elements/table.works.ts +0 -30
- package/dist/ui/elements/tabs.tsx +0 -47
- package/dist/ui/elements/tabs.works.ts +0 -84
- package/dist/ui/elements/text.tsx +0 -33
- package/dist/ui/elements/textarea.tsx +0 -38
- package/dist/ui/elements/textarea.works.ts +0 -5
- package/dist/ui/elements/time.tsx +0 -40
- package/dist/ui/elements/time.works.ts +0 -29
- package/dist/ui/elements/toggle.tsx +0 -34
- package/dist/ui/elements/toggle.works.ts +0 -5
- package/dist/ui/elements/tooltip.tsx +0 -40
- package/dist/ui/elements/tooltip.works.ts +0 -77
- package/dist/ui/elements/value.tsx +0 -38
- package/dist/ui/elements/value.works.ts +0 -9
- package/dist/ui/elements.ts +0 -127
- package/dist/ui/focus-scope.ts +0 -121
- package/dist/ui/fonts.ts +0 -355
- package/dist/ui/generators/dev.d.ts +0 -1
- package/dist/ui/generators/dev.js +0 -62
- package/dist/ui/generators/dev.ts +0 -75
- package/dist/ui/generators/elements.ts +0 -796
- package/dist/ui/generators/i18n.ts +0 -283
- package/dist/ui/generators/icons.ts +0 -207
- package/dist/ui/generators/react.ts +0 -267
- package/dist/ui/generators/routes.ts +0 -847
- package/dist/ui/generators/show.ts +0 -261
- package/dist/ui/generators/styles.ts +0 -677
- package/dist/ui/generators/variants.ts +0 -196
- package/dist/ui/i18n/catalogs/en.ts +0 -191
- package/dist/ui/i18n/format.ts +0 -221
- package/dist/ui/i18n/locales.ts +0 -33
- package/dist/ui/i18n/parse.ts +0 -315
- package/dist/ui/i18n/translator.ts +0 -31
- package/dist/ui/i18n/types.ts +0 -96
- package/dist/ui/icons.ts +0 -331
- package/dist/ui/islands/map.ts +0 -58
- package/dist/ui/localizations.ts +0 -75
- package/dist/ui/material.ts +0 -236
- package/dist/ui/materials/mat_bordered.ts +0 -13
- package/dist/ui/materials/mat_ceramic.ts +0 -13
- package/dist/ui/materials/mat_clay.ts +0 -13
- package/dist/ui/materials/mat_elevated.ts +0 -24
- package/dist/ui/materials/mat_flat.ts +0 -34
- package/dist/ui/materials/mat_glass.ts +0 -20
- package/dist/ui/materials/mat_lit.ts +0 -13
- package/dist/ui/materials/mat_moulded.ts +0 -11
- package/dist/ui/materials.ts +0 -197
- package/dist/ui/modes.ts +0 -44
- package/dist/ui/navigation.tsx +0 -29
- package/dist/ui/positioning.ts +0 -90
- package/dist/ui/presented.tsx +0 -119
- package/dist/ui/provider.tsx +0 -184
- package/dist/ui/render-loop.ts +0 -133
- package/dist/ui/roving-focus.ts +0 -161
- package/dist/ui/screens.ts +0 -141
- package/dist/ui/scroll-lock.ts +0 -28
- package/dist/ui/series.ts +0 -97
- package/dist/ui/signals.ts +0 -386
- package/dist/ui/styleguide.ts +0 -231
- package/dist/ui/styleguides.ts +0 -374
- package/dist/ui/themes.ts +0 -22
- package/dist/ui/token.ts +0 -78
- package/dist/ui/tokens/shadow.ts +0 -25
- package/dist/ui/tokens.ts +0 -234
- package/dist/ui/validators.ts +0 -232
- package/dist/ui/variant.ts +0 -349
- package/dist/ui/variants/var_avatar-round.ts +0 -14
- package/dist/ui/variants/var_bubble-me.ts +0 -14
- package/dist/ui/variants/var_bubble-them.ts +0 -13
- package/dist/ui/variants/var_button-border.ts +0 -76
- package/dist/ui/variants/var_button-cta.ts +0 -76
- package/dist/ui/variants/var_button-destructive-ghost.ts +0 -72
- package/dist/ui/variants/var_button-destructive.ts +0 -76
- package/dist/ui/variants/var_button-fill.ts +0 -76
- package/dist/ui/variants/var_button-ghost.ts +0 -72
- package/dist/ui/variants/var_button-glass.ts +0 -55
- package/dist/ui/variants/var_button-normal.ts +0 -76
- package/dist/ui/variants/var_button-quiet.ts +0 -74
- package/dist/ui/variants/var_card.ts +0 -17
- package/dist/ui/variants/var_chip.ts +0 -13
- package/dist/ui/variants/var_choice.ts +0 -15
- package/dist/ui/variants/var_framed.ts +0 -10
- package/dist/ui/variants/var_glyph-plain.ts +0 -11
- package/dist/ui/variants/var_input.ts +0 -43
- package/dist/ui/variants/var_link-inline.ts +0 -29
- package/dist/ui/variants/var_list-row.ts +0 -34
- package/dist/ui/variants/var_rule.ts +0 -10
- package/dist/ui/variants/var_skeleton-block.ts +0 -12
- package/dist/ui/variants/var_spinner-turning.ts +0 -12
- package/dist/ui/variants/var_stat-tile.ts +0 -13
- package/dist/ui/variants/var_tab-bar.ts +0 -15
- package/dist/ui/variants/var_tap-target.ts +0 -25
- package/dist/ui/variants.ts +0 -64
- package/dist/ui/wrappers.tsx +0 -429
package/dist/ui/fonts.ts
DELETED
|
@@ -1,355 +0,0 @@
|
|
|
1
|
-
import type { Threaded } from '@prestack/thread/core/thread'
|
|
2
|
-
|
|
3
|
-
import { sheeted, type Holds } from './token.ts'
|
|
4
|
-
|
|
5
|
-
export const FACES = ['body', 'heading', 'display', 'mono'] as const
|
|
6
|
-
export type Face = (typeof FACES)[number]
|
|
7
|
-
|
|
8
|
-
export interface FontFamily {
|
|
9
|
-
|
|
10
|
-
readonly name: string
|
|
11
|
-
|
|
12
|
-
readonly module: string
|
|
13
|
-
|
|
14
|
-
readonly file: string
|
|
15
|
-
|
|
16
|
-
readonly weight: string
|
|
17
|
-
|
|
18
|
-
readonly range: string
|
|
19
|
-
|
|
20
|
-
readonly licence: string
|
|
21
|
-
|
|
22
|
-
readonly fallback: string
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface FontSet {
|
|
26
|
-
readonly name: string
|
|
27
|
-
|
|
28
|
-
readonly families: Readonly<Partial<Record<Face, FontFamily>>>
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const SANS_FALLBACK = 'ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif'
|
|
32
|
-
const SERIF_FALLBACK = 'ui-serif, Georgia, Cambria, Times New Roman, serif'
|
|
33
|
-
const MONO_FALLBACK = 'ui-monospace, SFMono-Regular, Menlo, Consolas, monospace'
|
|
34
|
-
|
|
35
|
-
const LATIN =
|
|
36
|
-
'U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD'
|
|
37
|
-
|
|
38
|
-
const OFL = 'SIL Open Font Licence 1.1'
|
|
39
|
-
|
|
40
|
-
const INTER: FontFamily = {
|
|
41
|
-
name: 'Inter Variable',
|
|
42
|
-
module: '@fontsource-variable/inter',
|
|
43
|
-
file: 'files/inter-latin-wght-normal.woff2',
|
|
44
|
-
weight: '100 900',
|
|
45
|
-
range: LATIN,
|
|
46
|
-
licence: OFL,
|
|
47
|
-
fallback: SANS_FALLBACK,
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const JETBRAINS_MONO: FontFamily = {
|
|
51
|
-
name: 'JetBrains Mono Variable',
|
|
52
|
-
module: '@fontsource-variable/jetbrains-mono',
|
|
53
|
-
file: 'files/jetbrains-mono-latin-wght-normal.woff2',
|
|
54
|
-
weight: '100 800',
|
|
55
|
-
range: LATIN,
|
|
56
|
-
licence: OFL,
|
|
57
|
-
fallback: MONO_FALLBACK,
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const WORK_SANS: FontFamily = {
|
|
61
|
-
name: 'Work Sans Variable',
|
|
62
|
-
module: '@fontsource-variable/work-sans',
|
|
63
|
-
file: 'files/work-sans-latin-wght-normal.woff2',
|
|
64
|
-
weight: '100 900',
|
|
65
|
-
range: LATIN,
|
|
66
|
-
licence: OFL,
|
|
67
|
-
fallback: SANS_FALLBACK,
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const SPACE_GROTESK: FontFamily = {
|
|
71
|
-
name: 'Space Grotesk Variable',
|
|
72
|
-
module: '@fontsource-variable/space-grotesk',
|
|
73
|
-
file: 'files/space-grotesk-latin-wght-normal.woff2',
|
|
74
|
-
weight: '300 700',
|
|
75
|
-
range: LATIN,
|
|
76
|
-
licence: OFL,
|
|
77
|
-
fallback: SANS_FALLBACK,
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const ROBOTO_MONO: FontFamily = {
|
|
81
|
-
name: 'Roboto Mono Variable',
|
|
82
|
-
module: '@fontsource-variable/roboto-mono',
|
|
83
|
-
file: 'files/roboto-mono-latin-wght-normal.woff2',
|
|
84
|
-
weight: '100 700',
|
|
85
|
-
range: LATIN,
|
|
86
|
-
licence: OFL,
|
|
87
|
-
fallback: MONO_FALLBACK,
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const SOURCE_SERIF: FontFamily = {
|
|
91
|
-
name: 'Source Serif 4 Variable',
|
|
92
|
-
module: '@fontsource-variable/source-serif-4',
|
|
93
|
-
file: 'files/source-serif-4-latin-wght-normal.woff2',
|
|
94
|
-
weight: '200 900',
|
|
95
|
-
range: LATIN,
|
|
96
|
-
licence: OFL,
|
|
97
|
-
fallback: SERIF_FALLBACK,
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const PLAYFAIR_DISPLAY: FontFamily = {
|
|
101
|
-
name: 'Playfair Display Variable',
|
|
102
|
-
module: '@fontsource-variable/playfair-display',
|
|
103
|
-
file: 'files/playfair-display-latin-wght-normal.woff2',
|
|
104
|
-
weight: '400 900',
|
|
105
|
-
range: LATIN,
|
|
106
|
-
licence: OFL,
|
|
107
|
-
fallback: SERIF_FALLBACK,
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const SOURCE_CODE_PRO: FontFamily = {
|
|
111
|
-
name: 'Source Code Pro Variable',
|
|
112
|
-
module: '@fontsource-variable/source-code-pro',
|
|
113
|
-
file: 'files/source-code-pro-latin-wght-normal.woff2',
|
|
114
|
-
weight: '200 900',
|
|
115
|
-
range: LATIN,
|
|
116
|
-
licence: OFL,
|
|
117
|
-
fallback: MONO_FALLBACK,
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const LIBRE_BASKERVILLE: FontFamily = {
|
|
121
|
-
name: 'Libre Baskerville Variable',
|
|
122
|
-
module: '@fontsource-variable/libre-baskerville',
|
|
123
|
-
file: 'files/libre-baskerville-latin-wght-normal.woff2',
|
|
124
|
-
weight: '400 700',
|
|
125
|
-
range: LATIN,
|
|
126
|
-
licence: OFL,
|
|
127
|
-
fallback: SERIF_FALLBACK,
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const SETS: Readonly<Record<string, FontSet>> = {
|
|
131
|
-
|
|
132
|
-
inter: {
|
|
133
|
-
name: 'inter',
|
|
134
|
-
families: { body: INTER, heading: INTER, display: INTER, mono: JETBRAINS_MONO },
|
|
135
|
-
},
|
|
136
|
-
|
|
137
|
-
grotesk: {
|
|
138
|
-
name: 'grotesk',
|
|
139
|
-
families: { body: WORK_SANS, heading: SPACE_GROTESK, display: SPACE_GROTESK, mono: ROBOTO_MONO },
|
|
140
|
-
},
|
|
141
|
-
|
|
142
|
-
editorial: {
|
|
143
|
-
name: 'editorial',
|
|
144
|
-
families: { body: SOURCE_SERIF, heading: SOURCE_SERIF, display: PLAYFAIR_DISPLAY, mono: SOURCE_CODE_PRO },
|
|
145
|
-
},
|
|
146
|
-
|
|
147
|
-
libre: {
|
|
148
|
-
name: 'libre',
|
|
149
|
-
families: { body: LIBRE_BASKERVILLE, heading: LIBRE_BASKERVILLE, display: LIBRE_BASKERVILLE, mono: SOURCE_CODE_PRO },
|
|
150
|
-
},
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const SET_REGISTRY = new Map<string, FontSet>(Object.entries(SETS))
|
|
154
|
-
|
|
155
|
-
export function defineFontSet(set: FontSet): void {
|
|
156
|
-
if (SET_REGISTRY.has(set.name)) throw new Error(`font set "${set.name}" is already registered`)
|
|
157
|
-
|
|
158
|
-
const faces = new Set<string>(FACES)
|
|
159
|
-
for (const face of Object.keys(set.families).sort()) {
|
|
160
|
-
if (!faces.has(face)) {
|
|
161
|
-
throw new Error(
|
|
162
|
-
`font set "${set.name}" serves "${face}", which is not a face. Faces: ${[...FACES].sort().join(', ')}`,
|
|
163
|
-
)
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
SET_REGISTRY.set(set.name, set)
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
export function fontSets(): readonly FontSet[] {
|
|
171
|
-
return [...SET_REGISTRY.values()].sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0))
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
export function fontSet(name: string): FontSet | undefined {
|
|
175
|
-
return SET_REGISTRY.get(name)
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
export function fontFaces(): readonly string[] {
|
|
179
|
-
return [...FACES].sort()
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
export function fontModules(): readonly string[] {
|
|
183
|
-
const modules = new Set<string>()
|
|
184
|
-
for (const set of fontSets()) {
|
|
185
|
-
for (const family of Object.values(set.families)) modules.add(family.module)
|
|
186
|
-
}
|
|
187
|
-
return [...modules].sort()
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
export function fontIn(set: string, face: string): FontFamily | undefined {
|
|
191
|
-
return SET_REGISTRY.get(set)?.families[face as Face]
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
export function fontVariable(face: string): string {
|
|
195
|
-
return `--thread-font-${face}`
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
export function fontStack(family: FontFamily): string {
|
|
199
|
-
return `'${family.name}', ${family.fallback}`
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const GENERIC: Readonly<Record<Face, string>> = {
|
|
203
|
-
body: 'sans-serif',
|
|
204
|
-
heading: 'sans-serif',
|
|
205
|
-
display: 'sans-serif',
|
|
206
|
-
mono: 'monospace',
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
export function fontFor(face: Face): string {
|
|
210
|
-
return `var(${fontVariable(face)}, ${GENERIC[face]})`
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
export interface FontRole {
|
|
214
|
-
readonly face: Face
|
|
215
|
-
|
|
216
|
-
readonly size: string
|
|
217
|
-
|
|
218
|
-
readonly weight: string
|
|
219
|
-
|
|
220
|
-
readonly leading: string
|
|
221
|
-
|
|
222
|
-
readonly tracking: string
|
|
223
|
-
|
|
224
|
-
readonly case?: 'uppercase' | 'none'
|
|
225
|
-
|
|
226
|
-
readonly style?: 'italic' | 'normal'
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
type Row = Omit<Threaded, 'at'> & {
|
|
230
|
-
readonly section?: string
|
|
231
|
-
readonly is?: string
|
|
232
|
-
readonly sets?: Readonly<Record<string, string>>
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
const sectionOf = (face: string, id: string): string => {
|
|
236
|
-
if (id === 'mono') return 'fixed-width'
|
|
237
|
-
if (face === 'body') return 'reading'
|
|
238
|
-
return 'headline'
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
const role = <Id extends string>(
|
|
242
|
-
id: Id,
|
|
243
|
-
sets: FontRole,
|
|
244
|
-
describes: string,
|
|
245
|
-
): { readonly id: Id; readonly sets: FontRole; readonly describes: string } => ({ id, sets, describes })
|
|
246
|
-
|
|
247
|
-
const ROLES = [
|
|
248
|
-
role('display', { face: 'display', size: '40px', weight: '700', leading: '1.15', tracking: '-0.02em' },
|
|
249
|
-
'The one line a screen is built around, used once. Large enough that its leading has to be pulled in and its letters set tight, which is why it is its own role rather than a bigger heading.'),
|
|
250
|
-
role('title', { face: 'heading', size: '32px', weight: '700', leading: '1.15', tracking: '-0.02em' },
|
|
251
|
-
'What the page is called. The first wording a reader resolves after the display line, and the last one allowed to be this heavy.'),
|
|
252
|
-
role('heading', { face: 'heading', size: '24px', weight: '600', leading: '1.15', tracking: '-0.02em' },
|
|
253
|
-
'What a section is called — the size that gives a screen its structure, and the one a reader scans for.'),
|
|
254
|
-
role('subheading', { face: 'heading', size: '20px', weight: '600', leading: '1.5', tracking: '0' },
|
|
255
|
-
'What a part of a section is called. Set at running leading rather than tight, because at this size the lines are read rather than scanned.'),
|
|
256
|
-
role('lead', { face: 'body', size: '20px', weight: '400', leading: '1.5', tracking: '0' },
|
|
257
|
-
'The paragraph that opens a page — body wording at a heading\'s size, so it carries weight without being a heading.'),
|
|
258
|
-
role('body', { face: 'body', size: '16px', weight: '400', leading: '1.5', tracking: '0' },
|
|
259
|
-
'Running text, and the size every other role is judged against. What wording is set in unless something says otherwise.'),
|
|
260
|
-
role('label', { face: 'body', size: '13px', weight: '500', leading: '1.5', tracking: '0' },
|
|
261
|
-
'Wording attached to a control — the name above a field, the words on a button. Slightly forward of body, because a label names a thing rather than describing it.'),
|
|
262
|
-
role('caption', { face: 'body', size: '12px', weight: '400', leading: '1.5', tracking: '0.02em' },
|
|
263
|
-
'The small print — a hint under a field, a timestamp, a legal line. Its letters are opened up, because small type set tight closes into a block.'),
|
|
264
|
-
role('overline', { face: 'body', size: '12px', weight: '600', leading: '1.5', tracking: '0.02em', case: 'uppercase' },
|
|
265
|
-
'A micro label above the thing it names — a category, a section marker. The one role forced to capitals, which is what makes it read as a marker rather than a sentence.'),
|
|
266
|
-
role('mono', { face: 'mono', size: '13px', weight: '400', leading: '1.5', tracking: '0' },
|
|
267
|
-
'Wording where every character asks the same width — code, keys, anything meant to be compared column by column.'),
|
|
268
|
-
] as const
|
|
269
|
-
|
|
270
|
-
export type FontRoleId = (typeof ROLES)[number]['id']
|
|
271
|
-
|
|
272
|
-
export const ROLE_IDS: readonly FontRoleId[] = ROLES.map((one) => one.id)
|
|
273
|
-
|
|
274
|
-
export const DEFAULT_ROLE: FontRoleId = 'body'
|
|
275
|
-
|
|
276
|
-
export const SET_ROW = 'family-set'
|
|
277
|
-
|
|
278
|
-
export const holds: readonly Row[] = [
|
|
279
|
-
|
|
280
|
-
{
|
|
281
|
-
id: SET_ROW,
|
|
282
|
-
section: 'the set',
|
|
283
|
-
is: 'inter',
|
|
284
|
-
describes:
|
|
285
|
-
'Which set of families every face is drawn from. One name, because a set provides all four faces at once — changing it changes the whole app rather than one heading.',
|
|
286
|
-
version: 1,
|
|
287
|
-
},
|
|
288
|
-
...ROLES.map((one): Row => ({
|
|
289
|
-
id: one.id,
|
|
290
|
-
section: sectionOf(one.sets.face, one.id),
|
|
291
|
-
sets: one.sets as unknown as Readonly<Record<string, string>>,
|
|
292
|
-
describes: one.describes,
|
|
293
|
-
version: 1,
|
|
294
|
-
})),
|
|
295
|
-
]
|
|
296
|
-
|
|
297
|
-
export const FONTS = {
|
|
298
|
-
id: 'font',
|
|
299
|
-
at: 'thread/ui/fonts.ts',
|
|
300
|
-
describes:
|
|
301
|
-
'Every way wording is set, and the only place that is decided. A role names one of the four faces a set provides and states the size, weight, leading, tracking and case that job is set at — so `Text.body` needs to be told nothing beyond which job it is doing. It holds no colour and shifts under no mode or theme: colour belongs to the material or variant the wording is worn by, where a coloured control can overrule the theme it sits in. The style guide holds no font, because a font is not a token — this file is the source, and an app changes its typography by changing a row here or by naming a different set.',
|
|
302
|
-
version: 1,
|
|
303
|
-
cites: [
|
|
304
|
-
{
|
|
305
|
-
on: '2026-08-28',
|
|
306
|
-
said: 'Styleguide DOES NOT DEFINE FONTS. It takes care of token values. Let\'s leave fonts.ts as the SSOT for font family per type + size and appearence per type for the entire app. So UI puts Text.body("this is the label") and it reads from fonts to see what is the body, the font, the size, everything.',
|
|
307
|
-
},
|
|
308
|
-
{
|
|
309
|
-
on: '2026-08-28',
|
|
310
|
-
said: 'Fonts now are SSOT for fonts. So font is not a token anymore.',
|
|
311
|
-
},
|
|
312
|
-
],
|
|
313
|
-
holds,
|
|
314
|
-
} satisfies Threaded & { readonly holds: readonly Row[] }
|
|
315
|
-
|
|
316
|
-
export function classOfRole(id: string): string {
|
|
317
|
-
return `f-${id}`
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
export function settingsOf(id: string, role: FontRole): Readonly<Record<string, Holds>> {
|
|
321
|
-
return {
|
|
322
|
-
fontFamily: sheeted(id, `var(${fontVariable(role.face)})`),
|
|
323
|
-
fontSize: sheeted(id, role.size),
|
|
324
|
-
fontWeight: sheeted(id, role.weight),
|
|
325
|
-
lineHeight: sheeted(id, role.leading),
|
|
326
|
-
letterSpacing: sheeted(id, role.tracking),
|
|
327
|
-
...(role.case === undefined ? {} : { textTransform: sheeted(id, role.case) }),
|
|
328
|
-
...(role.style === undefined ? {} : { fontStyle: sheeted(id, role.style) }),
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
export function roleClass(is: FontRoleId | undefined, className: string | undefined): string {
|
|
333
|
-
const own = classOfRole(is ?? DEFAULT_ROLE)
|
|
334
|
-
return className === undefined || className === '' ? own : `${own} ${className}`
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
export function familyFor(set: FontSet, face: string, by: string): FontFamily {
|
|
338
|
-
const found = set.families[face as Face]
|
|
339
|
-
if (found !== undefined) return found
|
|
340
|
-
const has = Object.keys(set.families).sort().map((one) => `\`${one}\``)
|
|
341
|
-
throw new Error(
|
|
342
|
-
`\`${by}\` is set in the \`${face}\` face, and the \`${set.name}\` set does not provide one. ` +
|
|
343
|
-
`It provides ${has.join(', ')}. Name a face the set has, or give the set that one.`,
|
|
344
|
-
)
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
export function setNamed(name: string): FontSet {
|
|
348
|
-
const found = SET_REGISTRY.get(name)
|
|
349
|
-
if (found !== undefined) return found
|
|
350
|
-
const has = [...SET_REGISTRY.keys()].sort().map((one) => `\`${one}\``)
|
|
351
|
-
throw new Error(
|
|
352
|
-
`\`${name}\` is named as the font set, and no set by that name is registered. ` +
|
|
353
|
-
`The registered sets are ${has.join(', ')}.`,
|
|
354
|
-
)
|
|
355
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function unrunnable(program: string): string;
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { releases, starts } from '@prestack/thread/integrations/cli';
|
|
2
|
-
import { basename } from 'node:path';
|
|
3
|
-
import { argv, stdout } from 'node:process';
|
|
4
|
-
import { batched, generators, settling } from '@prestack/thread/core/chain';
|
|
5
|
-
import { existsSync, readFileSync } from '@prestack/thread/integrations/disk';
|
|
6
|
-
import { exit, invoked } from '@prestack/thread/core/invoked';
|
|
7
|
-
import { named } from '@prestack/thread/core/scope';
|
|
8
|
-
import { findTree } from '@prestack/thread/core/tree';
|
|
9
|
-
import { programOf } from '@prestack/thread/integrations/target';
|
|
10
|
-
const SERVER = 'next';
|
|
11
|
-
const SHIP = '--ship';
|
|
12
|
-
const ENTRY = 'dev';
|
|
13
|
-
const SHIPPED = 'build';
|
|
14
|
-
const GATE = 'thread gate';
|
|
15
|
-
const say = (line) => {
|
|
16
|
-
stdout.write(line);
|
|
17
|
-
};
|
|
18
|
-
export function unrunnable(program) {
|
|
19
|
-
return (program + ' is not installed here, so there is nothing to serve this app with. ' +
|
|
20
|
-
named('ui') + ' brings it: run thread attach ' + named('ui') + ' and try again.');
|
|
21
|
-
}
|
|
22
|
-
async function run() {
|
|
23
|
-
const tree = findTree();
|
|
24
|
-
const shipping = argv.includes(SHIP);
|
|
25
|
-
const entry = shipping ? SHIPPED : ENTRY;
|
|
26
|
-
const found = programOf(SERVER, tree.at, existsSync, (path) => readFileSync(path, 'utf8'));
|
|
27
|
-
if (found === undefined) {
|
|
28
|
-
say('thread: ' + unrunnable(SERVER) + '\n');
|
|
29
|
-
return 1;
|
|
30
|
-
}
|
|
31
|
-
const settled = settling(tree.at, await generators(tree));
|
|
32
|
-
for (const said of settled.ran)
|
|
33
|
-
if (said.trim() !== '')
|
|
34
|
-
say(said + '\n');
|
|
35
|
-
if (settled.unsettled.length > 0) {
|
|
36
|
-
say('thread: ' + settled.unsettled.join(', ') + ' would not settle after ' + String(settled.rounds) + ' rounds, ' +
|
|
37
|
-
'so what this tree declares and what is written from it still disagree. Nothing is served over that.\n');
|
|
38
|
-
return 1;
|
|
39
|
-
}
|
|
40
|
-
say('thread: ' + basename(tree.at) + ' settled in ' + String(settled.rounds) + ' round(s); running the gate\n');
|
|
41
|
-
const gated = batched(tree.at, [GATE]);
|
|
42
|
-
for (const one of gated)
|
|
43
|
-
if (one.said !== '')
|
|
44
|
-
say(one.said + '\n');
|
|
45
|
-
if (!gated.every((one) => one.ok)) {
|
|
46
|
-
say('thread: the gate refused this tree, and nothing is served over a red gate. What it refused is printed above.\n');
|
|
47
|
-
return 1;
|
|
48
|
-
}
|
|
49
|
-
say('thread: the gate passed; running ' + SERVER + ' ' + entry + '\n');
|
|
50
|
-
const extra = argv.slice(2).filter((one) => one !== SHIP);
|
|
51
|
-
if (!shipping)
|
|
52
|
-
releases();
|
|
53
|
-
const [program = '', ...lead] = found;
|
|
54
|
-
const held = starts(program, [...lead, entry, ...extra], { at: tree.at, shows: true });
|
|
55
|
-
if (!held.there) {
|
|
56
|
-
say('thread: could not start ' + SERVER + ' ' + entry + ': ' + held.why + '\n');
|
|
57
|
-
return 1;
|
|
58
|
-
}
|
|
59
|
-
return held.code;
|
|
60
|
-
}
|
|
61
|
-
if (invoked(import.meta.url))
|
|
62
|
-
exit(await run());
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { releases, starts } from '@prestack/thread/integrations/cli'
|
|
2
|
-
import { basename } from 'node:path'
|
|
3
|
-
import { argv, stdout } from 'node:process'
|
|
4
|
-
|
|
5
|
-
import { batched, generators, settling } from '@prestack/thread/core/chain'
|
|
6
|
-
import { existsSync, readFileSync } from '@prestack/thread/integrations/disk'
|
|
7
|
-
import { exit, invoked } from '@prestack/thread/core/invoked'
|
|
8
|
-
import { named } from '@prestack/thread/core/scope'
|
|
9
|
-
import { findTree } from '@prestack/thread/core/tree'
|
|
10
|
-
import { programOf } from '@prestack/thread/integrations/target'
|
|
11
|
-
|
|
12
|
-
const SERVER = 'next'
|
|
13
|
-
|
|
14
|
-
const SHIP = '--ship'
|
|
15
|
-
|
|
16
|
-
const ENTRY = 'dev'
|
|
17
|
-
|
|
18
|
-
const SHIPPED = 'build'
|
|
19
|
-
|
|
20
|
-
const GATE = 'thread gate'
|
|
21
|
-
|
|
22
|
-
const say = (line: string): void => {
|
|
23
|
-
stdout.write(line)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function unrunnable(program: string): string {
|
|
27
|
-
return (
|
|
28
|
-
program + ' is not installed here, so there is nothing to serve this app with. ' +
|
|
29
|
-
named('ui') + ' brings it: run thread attach ' + named('ui') + ' and try again.'
|
|
30
|
-
)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async function run(): Promise<number> {
|
|
34
|
-
const tree = findTree()
|
|
35
|
-
const shipping = argv.includes(SHIP)
|
|
36
|
-
const entry = shipping ? SHIPPED : ENTRY
|
|
37
|
-
|
|
38
|
-
const found = programOf(SERVER, tree.at, existsSync, (path) => readFileSync(path, 'utf8'))
|
|
39
|
-
if (found === undefined) {
|
|
40
|
-
say('thread: ' + unrunnable(SERVER) + '\n')
|
|
41
|
-
return 1
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const settled = settling(tree.at, await generators(tree))
|
|
45
|
-
for (const said of settled.ran) if (said.trim() !== '') say(said + '\n')
|
|
46
|
-
|
|
47
|
-
if (settled.unsettled.length > 0) {
|
|
48
|
-
say(
|
|
49
|
-
'thread: ' + settled.unsettled.join(', ') + ' would not settle after ' + String(settled.rounds) + ' rounds, ' +
|
|
50
|
-
'so what this tree declares and what is written from it still disagree. Nothing is served over that.\n',
|
|
51
|
-
)
|
|
52
|
-
return 1
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
say('thread: ' + basename(tree.at) + ' settled in ' + String(settled.rounds) + ' round(s); running the gate\n')
|
|
56
|
-
const gated = batched(tree.at, [GATE])
|
|
57
|
-
for (const one of gated) if (one.said !== '') say(one.said + '\n')
|
|
58
|
-
if (!gated.every((one) => one.ok)) {
|
|
59
|
-
say('thread: the gate refused this tree, and nothing is served over a red gate. What it refused is printed above.\n')
|
|
60
|
-
return 1
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
say('thread: the gate passed; running ' + SERVER + ' ' + entry + '\n')
|
|
64
|
-
const extra = argv.slice(2).filter((one) => one !== SHIP)
|
|
65
|
-
if (!shipping) releases()
|
|
66
|
-
const [program = '', ...lead] = found
|
|
67
|
-
const held = starts(program, [...lead, entry, ...extra], { at: tree.at, shows: true })
|
|
68
|
-
if (!held.there) {
|
|
69
|
-
say('thread: could not start ' + SERVER + ' ' + entry + ': ' + held.why + '\n')
|
|
70
|
-
return 1
|
|
71
|
-
}
|
|
72
|
-
return held.code
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (invoked(import.meta.url)) exit(await run())
|