@prestack/thread-ui 0.0.1 → 0.0.2
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/dist/ui/animations/anm_appear.ts +13 -0
- package/dist/ui/animations/anm_blur_in.ts +19 -0
- package/dist/ui/animations/anm_blur_out.ts +18 -0
- package/dist/ui/animations/anm_dismiss.ts +18 -0
- package/dist/ui/animations/anm_drop.ts +19 -0
- package/dist/ui/animations/anm_fall.ts +18 -0
- package/dist/ui/animations/anm_flash.ts +18 -0
- package/dist/ui/animations/anm_glow.ts +16 -0
- package/dist/ui/animations/anm_lift.ts +16 -0
- package/dist/ui/animations/anm_nudge.ts +16 -0
- package/dist/ui/animations/anm_pop_in.ts +23 -0
- package/dist/ui/animations/anm_pop_out.ts +22 -0
- package/dist/ui/animations/anm_press.ts +12 -0
- package/dist/ui/animations/anm_pulse.ts +13 -0
- package/dist/ui/animations/anm_rise.ts +19 -0
- package/dist/ui/animations/anm_shake.ts +13 -0
- package/dist/ui/animations/anm_slide_in_left.ts +19 -0
- package/dist/ui/animations/anm_slide_in_right.ts +19 -0
- package/dist/ui/animations/anm_slide_out_left.ts +18 -0
- package/dist/ui/animations/anm_slide_out_right.ts +18 -0
- package/dist/ui/animations/anm_spin.ts +12 -0
- package/dist/ui/animations/anm_swell.ts +16 -0
- package/dist/ui/animations/anm_wobble.ts +24 -0
- package/dist/ui/animations/anm_zoom_in.ts +19 -0
- package/dist/ui/animations/anm_zoom_out.ts +18 -0
- package/dist/ui/animations.ts +317 -0
- package/dist/ui/attach.js +13 -4
- package/dist/ui/attach.ts +613 -0
- package/dist/ui/charts.tsx +74 -0
- package/dist/ui/checked.d.ts +2 -2
- package/dist/ui/checked.ts +32 -0
- package/dist/ui/collector.ts +140 -0
- package/dist/ui/compositions.d.ts +10 -1
- package/dist/ui/compositions.js +262 -23
- package/dist/ui/compositions.ts +640 -0
- package/dist/ui/consent.ts +55 -0
- package/dist/ui/content.tsx +39 -0
- package/dist/ui/contrast.ts +27 -0
- package/dist/ui/controls.tsx +30 -0
- package/dist/ui/disclosure.tsx +22 -0
- package/dist/ui/dismiss-layer.ts +87 -0
- package/dist/ui/door.ts +140 -0
- package/dist/ui/drag.ts +316 -0
- package/dist/ui/elements/accordion.js +2 -2
- package/dist/ui/elements/accordion.tsx +47 -0
- package/dist/ui/elements/accordion.works.ts +66 -0
- package/dist/ui/elements/aspect-ratio.js +1 -1
- package/dist/ui/elements/aspect-ratio.tsx +30 -0
- package/dist/ui/elements/aspect-ratio.works.ts +5 -0
- package/dist/ui/elements/avatar.js +1 -1
- package/dist/ui/elements/avatar.tsx +33 -0
- package/dist/ui/elements/avatar.works.ts +16 -0
- package/dist/ui/elements/badge.js +1 -1
- package/dist/ui/elements/badge.tsx +32 -0
- package/dist/ui/elements/badge.works.ts +9 -0
- package/dist/ui/elements/bars.js +2 -2
- package/dist/ui/elements/bars.tsx +49 -0
- package/dist/ui/elements/bars.works.ts +29 -0
- package/dist/ui/elements/box.js +3 -2
- package/dist/ui/elements/box.tsx +35 -0
- package/dist/ui/elements/breadcrumb.js +2 -2
- package/dist/ui/elements/breadcrumb.tsx +37 -0
- package/dist/ui/elements/breadcrumb.works.ts +24 -0
- package/dist/ui/elements/button.js +1 -1
- package/dist/ui/elements/button.tsx +45 -0
- package/dist/ui/elements/button.works.tsx +52 -0
- package/dist/ui/elements/column.js +1 -1
- package/dist/ui/elements/column.tsx +28 -0
- package/dist/ui/elements/consent.js +2 -2
- package/dist/ui/elements/consent.tsx +70 -0
- package/dist/ui/elements/consent.works.tsx +132 -0
- package/dist/ui/elements/donut.js +2 -2
- package/dist/ui/elements/donut.tsx +44 -0
- package/dist/ui/elements/donut.works.ts +42 -0
- package/dist/ui/elements/each.js +1 -1
- package/dist/ui/elements/each.tsx +33 -0
- package/dist/ui/elements/each.works.tsx +82 -0
- package/dist/ui/elements/empty.js +1 -1
- package/dist/ui/elements/empty.tsx +32 -0
- package/dist/ui/elements/field.js +1 -1
- package/dist/ui/elements/field.tsx +44 -0
- package/dist/ui/elements/field.works.ts +65 -0
- package/dist/ui/elements/funnel.js +2 -2
- package/dist/ui/elements/funnel.tsx +49 -0
- package/dist/ui/elements/funnel.works.ts +37 -0
- package/dist/ui/elements/gauge.js +1 -1
- package/dist/ui/elements/gauge.tsx +39 -0
- package/dist/ui/elements/gauge.works.ts +42 -0
- package/dist/ui/elements/glyph.js +1 -1
- package/dist/ui/elements/glyph.tsx +29 -0
- package/dist/ui/elements/glyph.works.tsx +42 -0
- package/dist/ui/elements/grid.js +1 -1
- package/dist/ui/elements/grid.tsx +28 -0
- package/dist/ui/elements/heatmap.js +2 -2
- package/dist/ui/elements/heatmap.tsx +47 -0
- package/dist/ui/elements/heatmap.works.ts +24 -0
- package/dist/ui/elements/hit-box.js +1 -1
- package/dist/ui/elements/hit-box.tsx +46 -0
- package/dist/ui/elements/hit-box.works.tsx +576 -0
- package/dist/ui/elements/image.js +1 -1
- package/dist/ui/elements/image.tsx +28 -0
- package/dist/ui/elements/line-chart.js +2 -2
- package/dist/ui/elements/line-chart.tsx +44 -0
- package/dist/ui/elements/line-chart.works.ts +41 -0
- package/dist/ui/elements/link.js +1 -1
- package/dist/ui/elements/link.tsx +37 -0
- package/dist/ui/elements/link.works.ts +25 -0
- package/dist/ui/elements/media.js +1 -1
- package/dist/ui/elements/media.tsx +56 -0
- package/dist/ui/elements/menu.js +2 -2
- package/dist/ui/elements/menu.tsx +50 -0
- package/dist/ui/elements/menu.works.tsx +153 -0
- package/dist/ui/elements/money.js +1 -1
- package/dist/ui/elements/money.tsx +28 -0
- package/dist/ui/elements/money.works.ts +9 -0
- package/dist/ui/elements/pagination.js +2 -2
- package/dist/ui/elements/pagination.tsx +38 -0
- package/dist/ui/elements/pagination.works.ts +20 -0
- package/dist/ui/elements/progress.js +1 -1
- package/dist/ui/elements/progress.tsx +33 -0
- package/dist/ui/elements/progress.works.ts +8 -0
- package/dist/ui/elements/radio-group.js +2 -2
- package/dist/ui/elements/radio-group.tsx +49 -0
- package/dist/ui/elements/radio-group.works.ts +33 -0
- package/dist/ui/elements/row.js +1 -1
- package/dist/ui/elements/row.tsx +28 -0
- package/dist/ui/elements/scatter.js +1 -1
- package/dist/ui/elements/scatter.tsx +41 -0
- package/dist/ui/elements/scatter.works.ts +38 -0
- package/dist/ui/elements/select.js +2 -2
- package/dist/ui/elements/select.tsx +42 -0
- package/dist/ui/elements/select.works.ts +5 -0
- package/dist/ui/elements/separator.js +1 -1
- package/dist/ui/elements/separator.tsx +30 -0
- package/dist/ui/elements/skeleton.js +1 -1
- package/dist/ui/elements/skeleton.tsx +30 -0
- package/dist/ui/elements/slider.js +1 -1
- package/dist/ui/elements/slider.tsx +33 -0
- package/dist/ui/elements/slider.works.ts +5 -0
- package/dist/ui/elements/sparkline.js +1 -1
- package/dist/ui/elements/sparkline.tsx +36 -0
- package/dist/ui/elements/sparkline.works.ts +32 -0
- package/dist/ui/elements/spinner.js +1 -1
- package/dist/ui/elements/spinner.tsx +32 -0
- package/dist/ui/elements/stack.js +1 -1
- package/dist/ui/elements/stack.tsx +28 -0
- package/dist/ui/elements/stat.js +1 -1
- package/dist/ui/elements/stat.tsx +35 -0
- package/dist/ui/elements/stat.works.ts +14 -0
- package/dist/ui/elements/surface.js +1 -1
- package/dist/ui/elements/surface.tsx +35 -0
- package/dist/ui/elements/surface.works.ts +9 -0
- package/dist/ui/elements/table.js +2 -2
- package/dist/ui/elements/table.tsx +61 -0
- package/dist/ui/elements/table.works.ts +30 -0
- package/dist/ui/elements/tabs.js +2 -2
- package/dist/ui/elements/tabs.tsx +47 -0
- package/dist/ui/elements/tabs.works.ts +84 -0
- package/dist/ui/elements/text.js +1 -1
- package/dist/ui/elements/text.tsx +32 -0
- package/dist/ui/elements/textarea.js +1 -1
- package/dist/ui/elements/textarea.tsx +37 -0
- package/dist/ui/elements/textarea.works.ts +5 -0
- package/dist/ui/elements/time.js +1 -1
- package/dist/ui/elements/time.tsx +34 -0
- package/dist/ui/elements/time.works.ts +25 -0
- package/dist/ui/elements/toggle.js +1 -1
- package/dist/ui/elements/toggle.tsx +33 -0
- package/dist/ui/elements/toggle.works.ts +5 -0
- package/dist/ui/elements/tooltip.js +1 -1
- package/dist/ui/elements/tooltip.tsx +39 -0
- package/dist/ui/elements/tooltip.works.ts +77 -0
- package/dist/ui/elements/value.js +1 -1
- package/dist/ui/elements/value.tsx +30 -0
- package/dist/ui/elements/value.works.ts +9 -0
- package/dist/ui/elements.ts +127 -0
- package/dist/ui/focus-scope.ts +121 -0
- package/dist/ui/fonts.ts +355 -0
- package/dist/ui/generators/dev.ts +79 -0
- package/dist/ui/generators/elements.d.ts +1 -11
- package/dist/ui/generators/elements.js +43 -13
- package/dist/ui/generators/elements.ts +794 -0
- package/dist/ui/generators/i18n.ts +301 -0
- package/dist/ui/generators/icons.ts +214 -0
- package/dist/ui/generators/react.js +10 -3
- package/dist/ui/generators/react.ts +268 -0
- package/dist/ui/generators/routes.d.ts +1 -0
- package/dist/ui/generators/routes.js +89 -69
- package/dist/ui/generators/routes.ts +739 -0
- package/dist/ui/generators/show.ts +259 -0
- package/dist/ui/generators/styles.js +1 -1
- package/dist/ui/generators/styles.ts +670 -0
- package/dist/ui/generators/variants.ts +205 -0
- package/dist/ui/i18n/catalogs/en.ts +191 -0
- package/dist/ui/i18n/format.ts +221 -0
- package/dist/ui/i18n/locales.ts +33 -0
- package/dist/ui/i18n/parse.ts +315 -0
- package/dist/ui/i18n/translator.ts +31 -0
- package/dist/ui/i18n/types.ts +96 -0
- package/dist/ui/icons.ts +331 -0
- package/dist/ui/islands/map.ts +58 -0
- package/dist/ui/localizations.ts +75 -0
- package/dist/ui/makers/message.ts +292 -0
- package/dist/ui/material.ts +236 -0
- package/dist/ui/materials/mat_bordered.ts +13 -0
- package/dist/ui/materials/mat_ceramic.ts +13 -0
- package/dist/ui/materials/mat_clay.ts +13 -0
- package/dist/ui/materials/mat_elevated.ts +24 -0
- package/dist/ui/materials/mat_flat.ts +34 -0
- package/dist/ui/materials/mat_glass.ts +20 -0
- package/dist/ui/materials/mat_lit.ts +13 -0
- package/dist/ui/materials/mat_moulded.ts +11 -0
- package/dist/ui/materials.ts +206 -0
- package/dist/ui/modes.ts +44 -0
- package/dist/ui/navigation.tsx +32 -0
- package/dist/ui/positioning.ts +90 -0
- package/dist/ui/presented.tsx +119 -0
- package/dist/ui/provider.d.ts +2 -0
- package/dist/ui/provider.js +7 -1
- package/dist/ui/provider.tsx +182 -0
- package/dist/ui/render-loop.ts +133 -0
- package/dist/ui/roving-focus.ts +161 -0
- package/dist/ui/screens.js +17 -8
- package/dist/ui/screens.ts +142 -0
- package/dist/ui/scroll-lock.ts +28 -0
- package/dist/ui/series.ts +97 -0
- package/dist/ui/signals.ts +386 -0
- package/dist/ui/styleguide.ts +231 -0
- package/dist/ui/styleguides.ts +374 -0
- package/dist/ui/themes.ts +22 -0
- package/dist/ui/token.js +1 -1
- package/dist/ui/token.ts +78 -0
- package/dist/ui/tokens/shadow.ts +25 -0
- package/dist/ui/tokens.ts +235 -0
- package/dist/ui/validators.ts +232 -0
- package/dist/ui/variant.d.ts +1 -0
- package/dist/ui/variant.js +3 -0
- package/dist/ui/variant.ts +352 -0
- package/dist/ui/variants/var_avatar-round.ts +14 -0
- package/dist/ui/variants/var_bubble-me.ts +14 -0
- package/dist/ui/variants/var_bubble-them.ts +13 -0
- package/dist/ui/variants/var_button-border.ts +76 -0
- package/dist/ui/variants/var_button-cta.ts +76 -0
- package/dist/ui/variants/var_button-destructive-ghost.ts +72 -0
- package/dist/ui/variants/var_button-destructive.ts +76 -0
- package/dist/ui/variants/var_button-fill.ts +76 -0
- package/dist/ui/variants/var_button-ghost.ts +72 -0
- package/dist/ui/variants/var_button-glass.ts +55 -0
- package/dist/ui/variants/var_button-normal.ts +76 -0
- package/dist/ui/variants/var_button-quiet.ts +74 -0
- package/dist/ui/variants/var_card.ts +17 -0
- package/dist/ui/variants/var_chip.ts +13 -0
- package/dist/ui/variants/var_choice.ts +15 -0
- package/dist/ui/variants/var_framed.ts +10 -0
- package/dist/ui/variants/var_glyph-plain.ts +11 -0
- package/dist/ui/variants/var_input.ts +43 -0
- package/dist/ui/variants/var_link-inline.ts +29 -0
- package/dist/ui/variants/var_list-row.ts +34 -0
- package/dist/ui/variants/var_rule.ts +10 -0
- package/dist/ui/variants/var_skeleton-block.ts +12 -0
- package/dist/ui/variants/var_spinner-turning.ts +12 -0
- package/dist/ui/variants/var_stat-tile.ts +13 -0
- package/dist/ui/variants/var_tab-bar.ts +15 -0
- package/dist/ui/variants/var_tap-target.ts +25 -0
- package/dist/ui/variants.ts +64 -0
- package/dist/ui/wrappers.d.ts +4 -1
- package/dist/ui/wrappers.js +60 -20
- package/dist/ui/wrappers.tsx +426 -0
- package/package.json +8 -2
package/dist/ui/fonts.ts
ADDED
|
@@ -0,0 +1,355 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process'
|
|
2
|
+
import { existsSync, readFileSync, writeSync } from 'node:fs'
|
|
3
|
+
import { basename, join } from 'node:path'
|
|
4
|
+
import { argv } from 'node:process'
|
|
5
|
+
|
|
6
|
+
import { generators, settling } from '@prestack/thread/core/chain'
|
|
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
|
+
|
|
11
|
+
const MANIFEST = 'package.json'
|
|
12
|
+
|
|
13
|
+
const SHIP = '--ship'
|
|
14
|
+
|
|
15
|
+
const ENTRY = 'dev'
|
|
16
|
+
|
|
17
|
+
const SHIPPED = 'build'
|
|
18
|
+
|
|
19
|
+
const say = (line: string): void => {
|
|
20
|
+
writeSync(1, line)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface Manifest {
|
|
24
|
+
readonly scripts?: Record<string, string>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function declared(at: string, entry: string, read: (where: string) => string | undefined): string | undefined {
|
|
28
|
+
const source = read(join(at, MANIFEST))
|
|
29
|
+
if (source === undefined) return undefined
|
|
30
|
+
return (JSON.parse(source) as Manifest).scripts?.[entry]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function unrunnable(entry: string, said: string | undefined): string | undefined {
|
|
34
|
+
if (said !== undefined) return undefined
|
|
35
|
+
return (
|
|
36
|
+
`this app's ${MANIFEST} declares no \`${entry}\` script, so there is nothing here to run. ` +
|
|
37
|
+
`\`thread attach ${named('integrity')}\` is what writes it, along with the \`thread gate\` hook that ` +
|
|
38
|
+
'stands in front of it — run the attach and try again.'
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function run(): Promise<number> {
|
|
43
|
+
const tree = findTree()
|
|
44
|
+
const shipping = argv.includes(SHIP)
|
|
45
|
+
const entry = shipping ? SHIPPED : ENTRY
|
|
46
|
+
const read = (where: string): string | undefined => (existsSync(where) ? readFileSync(where, 'utf8') : undefined)
|
|
47
|
+
|
|
48
|
+
const refused = unrunnable(entry, declared(tree.at, entry, read))
|
|
49
|
+
if (refused !== undefined) {
|
|
50
|
+
say(`thread: ${refused}\n`)
|
|
51
|
+
return 1
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const settled = settling(tree.at, await generators(tree))
|
|
55
|
+
for (const said of settled.ran) if (said.trim() !== '') say(`${said}\n`)
|
|
56
|
+
|
|
57
|
+
if (settled.unsettled.length > 0) {
|
|
58
|
+
say(
|
|
59
|
+
`thread: ${settled.unsettled.join(', ')} would not settle after ${String(settled.rounds)} rounds, ` +
|
|
60
|
+
'so what this tree declares and what is written from it still disagree. Nothing is served over that.\n',
|
|
61
|
+
)
|
|
62
|
+
return 1
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
say(`thread: ${basename(tree.at)} settled in ${String(settled.rounds)} round(s); running \`${entry}\`\n`)
|
|
66
|
+
|
|
67
|
+
const extra = argv.slice(2).filter((one) => one !== SHIP)
|
|
68
|
+
const held = spawnSync('npm', ['run', entry, ...(extra.length === 0 ? [] : ['--', ...extra])], {
|
|
69
|
+
cwd: tree.at,
|
|
70
|
+
stdio: 'inherit',
|
|
71
|
+
})
|
|
72
|
+
if (held.error !== undefined) {
|
|
73
|
+
say(`thread: could not start \`npm run ${entry}\` — ${held.error.message}\n`)
|
|
74
|
+
return 1
|
|
75
|
+
}
|
|
76
|
+
return held.status ?? 1
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (invoked(import.meta.url)) exit(await run())
|
|
@@ -17,15 +17,5 @@ export declare function translated(name: string, tag: string | Readonly<Record<s
|
|
|
17
17
|
}, carries?: Readonly<Record<string, string>>, always?: readonly string[], places?: Readonly<Record<string, Readonly<Record<string, string>>>>, works?: string, measures?: readonly string[], reads?: string, gives?: readonly string[], keeps?: boolean, animates?: boolean, says?: string, called?: string, wires?: string, dresses?: string): string;
|
|
18
18
|
export declare const CARRIES: readonly string[];
|
|
19
19
|
export declare const READINGS: readonly string[];
|
|
20
|
-
export declare const APP: {
|
|
21
|
-
readonly wrappers: `${string}/wrappers`;
|
|
22
|
-
readonly content: `${string}/content`;
|
|
23
|
-
readonly rows: `${string}/charts`;
|
|
24
|
-
readonly validators: `${string}/validators`;
|
|
25
|
-
readonly fonts: `${string}/fonts`;
|
|
26
|
-
readonly map: `${string}/islands/map`;
|
|
27
|
-
readonly parts: `${string}/disclosure`;
|
|
28
|
-
readonly consent: `${string}/consent`;
|
|
29
|
-
readonly drag: `${string}/drag`;
|
|
30
|
-
};
|
|
31
20
|
export declare const declared: Set<string>;
|
|
21
|
+
export declare const wears: Map<string, Readonly<Record<string, string>>>;
|
|
@@ -11,9 +11,8 @@ import { join, resolve } from 'node:path';
|
|
|
11
11
|
import { argv, stdout } from 'node:process';
|
|
12
12
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
13
13
|
import { exit, invoked } from "@prestack/thread/core/invoked";
|
|
14
|
-
import { named as packageOf } from "@prestack/thread/core/scope";
|
|
15
14
|
import { findTree, inTree } from "@prestack/thread/core/tree";
|
|
16
|
-
import { classOfElement, looking } from "../variant.js";
|
|
15
|
+
import { camel, classOf, classOfElement, looking } from "../variant.js";
|
|
17
16
|
const idOf = (name) => name.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
|
|
18
17
|
export function pascal(id) {
|
|
19
18
|
return id
|
|
@@ -39,6 +38,10 @@ export function owned(body, rows) {
|
|
|
39
38
|
}
|
|
40
39
|
export function translated(name, tag, arranges, raises, from, carries = {}, always = [], places = {}, works, measures = [], reads, gives = [], keeps = false, animates = false, says, called, wires, dresses) {
|
|
41
40
|
const made = pascal(name);
|
|
41
|
+
const dressing = wears.get(name);
|
|
42
|
+
if (dressing === undefined) {
|
|
43
|
+
throw new Error(`\`${name}\` is written before the variant rows that wear it were read, so it would ship wearing none of them`);
|
|
44
|
+
}
|
|
42
45
|
const invoking = [
|
|
43
46
|
...(carries['dispatch'] === undefined ? [' readonly dispatch?: string'] : []),
|
|
44
47
|
...(carries['input'] === undefined ? [' readonly input?: Readonly<Record<string, unknown>>'] : []),
|
|
@@ -184,7 +187,7 @@ export function translated(name, tag, arranges, raises, from, carries = {}, alwa
|
|
|
184
187
|
'reorders. Name the field with `by`, or state `keyed: \'position\'` where position IS identity.');
|
|
185
188
|
}
|
|
186
189
|
return (`${pad}{props.${over}.map((${`each${String(rows.length)}`}, at) => (\n` +
|
|
187
|
-
`${region(place, depth + 2, [...rows, `each${String(rows.length)}`], true).replace(`<${
|
|
190
|
+
`${region(place, depth + 2, [...rows, `each${String(rows.length)}`], true).replace(`<${as}`, `<${as} key={at}`)}\n` +
|
|
188
191
|
`${pad}))}`);
|
|
189
192
|
}
|
|
190
193
|
return (`${pad}<Each rows={${carries[over] === undefined ? `${over}(props${keeps ? ', kept' : ''})` : `props.${over}`}} by={${carries[by] === undefined ? `'${by}'` : `props.${by}`}}>\n` +
|
|
@@ -290,9 +293,8 @@ export function translated(name, tag, arranges, raises, from, carries = {}, alwa
|
|
|
290
293
|
...always.map((one) => ` '${one}' as const,`),
|
|
291
294
|
...raises.map((one) => ` ...(props.${one} === true ? ['${one}' as const] : []),`),
|
|
292
295
|
...(keeps ? [' ...(kept.holds ?? []),'] : []),
|
|
293
|
-
].join('\n')}\n ]
|
|
294
|
-
return
|
|
295
|
-
'use client'
|
|
296
|
+
].join('\n')}\n ],${raises.length === 0 ? '' : `\n raises: [${raises.map((one) => `'${one}' as const`).join(', ')}],`}`;
|
|
297
|
+
return `'use client'
|
|
296
298
|
|
|
297
299
|
${[
|
|
298
300
|
...(arranges || stated.some(([, kind]) => KINDS[kind]?.from === 'react' || KINDS[kind]?.from === 'both')
|
|
@@ -334,6 +336,7 @@ ${keeps ? ' const kept = uses(props)\n' : ''} const surface = useSurface({
|
|
|
334
336
|
)
|
|
335
337
|
},
|
|
336
338
|
'${classOfElement(name)}',
|
|
339
|
+
${JSON.stringify(dressing)},
|
|
337
340
|
)
|
|
338
341
|
`;
|
|
339
342
|
}
|
|
@@ -424,17 +427,30 @@ const READS = {
|
|
|
424
427
|
};
|
|
425
428
|
export const CARRIES = Object.keys(KINDS);
|
|
426
429
|
export const READINGS = Object.keys(READS);
|
|
427
|
-
const
|
|
428
|
-
export const APP = { wrappers: `${UI}/wrappers`, content: `${UI}/content`, rows: `${UI}/charts`, validators: `${UI}/validators`, fonts: `${UI}/fonts`, map: `${UI}/islands/map`, parts: `${UI}/disclosure`, consent: `${UI}/consent`, drag: `${UI}/drag` };
|
|
429
|
-
const OWN = { wrappers: '../wrappers.tsx', content: '../content.tsx', rows: '../charts.tsx', validators: '../validators.ts', fonts: '../fonts.ts', map: '../islands/map.ts', parts: '../disclosure.tsx', consent: '../consent.ts', drag: '../drag.ts' };
|
|
430
|
+
const BESIDE = { wrappers: '../wrappers.tsx', content: '../content.tsx', rows: '../charts.tsx', validators: '../validators.ts', fonts: '../fonts.ts', map: '../islands/map.ts', parts: '../disclosure.tsx', consent: '../consent.ts', drag: '../drag.ts' };
|
|
430
431
|
export const declared = new Set();
|
|
432
|
+
export const wears = new Map();
|
|
431
433
|
const SHEET = `elements.${'ts'}`;
|
|
432
434
|
const BY = 'thread elements';
|
|
433
435
|
const INSTALLED = 'node_' + 'modules/';
|
|
434
436
|
function derived(row, own) {
|
|
437
|
+
if (typeof row.at !== 'string' || row.at.startsWith(INSTALLED))
|
|
438
|
+
return false;
|
|
435
439
|
if ((row.derives ?? []).some((one) => one.by === BY && one.at === row.at))
|
|
436
440
|
return true;
|
|
437
|
-
return
|
|
441
|
+
return own ? row.by === BY : true;
|
|
442
|
+
}
|
|
443
|
+
const VARIANTS_SHEET = `variants.${'ts'}`;
|
|
444
|
+
async function variants(own) {
|
|
445
|
+
if (own) {
|
|
446
|
+
const held = (await import(__rewriteRelativeImportExtension(pathToFileURL(resolve(fileURLToPath(import.meta.url), '../..', VARIANTS_SHEET)).href)));
|
|
447
|
+
return held.VARIANTS?.declares ?? [];
|
|
448
|
+
}
|
|
449
|
+
const at = inTree(findTree(), VARIANTS_SHEET);
|
|
450
|
+
if (!existsSync(at))
|
|
451
|
+
return [];
|
|
452
|
+
const held = (await import(__rewriteRelativeImportExtension(pathToFileURL(at).href)));
|
|
453
|
+
return held.VARIANT?.declares ?? [];
|
|
438
454
|
}
|
|
439
455
|
async function run() {
|
|
440
456
|
const own = argv.includes('--own');
|
|
@@ -442,19 +458,33 @@ async function run() {
|
|
|
442
458
|
? (await import(__rewriteRelativeImportExtension(pathToFileURL(resolve(fileURLToPath(import.meta.url), '../../elements.ts')).href)))
|
|
443
459
|
: undefined;
|
|
444
460
|
const tree = own ? undefined : findTree();
|
|
461
|
+
if (tree !== undefined && !existsSync(inTree(tree, SHEET))) {
|
|
462
|
+
stdout.write(`thread: this tree has no ${tree.name}/${SHEET}, so it declares no element and there is nothing to write or check.\n`);
|
|
463
|
+
return 0;
|
|
464
|
+
}
|
|
445
465
|
const sheet = tree === undefined
|
|
446
466
|
? { ELEMENT: held?.ELEMENTS }
|
|
447
467
|
: (await import(__rewriteRelativeImportExtension(pathToFileURL(inTree(tree, SHEET)).href)));
|
|
448
468
|
if (sheet.ELEMENT === undefined) {
|
|
449
469
|
stdout.write(own
|
|
450
|
-
? 'elements:
|
|
470
|
+
? 'elements: thread/ui/elements.ts exports no `ELEMENTS`.\n'
|
|
451
471
|
: `thread: ${String(tree?.name)}/${SHEET} exports no \`ELEMENT\`, so nothing declares an element here.\n`);
|
|
452
472
|
return 1;
|
|
453
473
|
}
|
|
454
|
-
const root =
|
|
455
|
-
const from =
|
|
474
|
+
const root = findTree().at;
|
|
475
|
+
const from = BESIDE;
|
|
456
476
|
for (const one of sheet.ELEMENT.declares ?? [])
|
|
457
477
|
declared.add(one.id);
|
|
478
|
+
const dressed = await variants(own);
|
|
479
|
+
for (const one of sheet.ELEMENT.declares ?? []) {
|
|
480
|
+
const held = {};
|
|
481
|
+
for (const row of dressed) {
|
|
482
|
+
if (!row.wears.includes(pascal(one.id)))
|
|
483
|
+
continue;
|
|
484
|
+
held[camel(row.id)] = classOf(row.id);
|
|
485
|
+
}
|
|
486
|
+
wears.set(one.id, held);
|
|
487
|
+
}
|
|
458
488
|
const rows = (sheet.ELEMENT.declares ?? []).filter((row) => derived(row, own));
|
|
459
489
|
const checking = argv.includes('--check');
|
|
460
490
|
for (const row of rows) {
|