@prestack/thread-ui 0.1.9 → 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.
Files changed (210) hide show
  1. package/CHANGELOG.md +366 -0
  2. package/dist/ui/attach.js +6 -7
  3. package/dist/ui/compositions.d.ts +0 -2
  4. package/dist/ui/compositions.js +2 -112
  5. package/dist/ui/generators/routes.js +1 -0
  6. package/dist/ui/variant.d.ts +1 -3
  7. package/package.json +2 -2
  8. package/dist/ui/animations/anm_appear.ts +0 -13
  9. package/dist/ui/animations/anm_blur_in.ts +0 -19
  10. package/dist/ui/animations/anm_blur_out.ts +0 -18
  11. package/dist/ui/animations/anm_dismiss.ts +0 -18
  12. package/dist/ui/animations/anm_drop.ts +0 -19
  13. package/dist/ui/animations/anm_fall.ts +0 -18
  14. package/dist/ui/animations/anm_flash.ts +0 -18
  15. package/dist/ui/animations/anm_glow.ts +0 -16
  16. package/dist/ui/animations/anm_lift.ts +0 -16
  17. package/dist/ui/animations/anm_nudge.ts +0 -16
  18. package/dist/ui/animations/anm_pop_in.ts +0 -23
  19. package/dist/ui/animations/anm_pop_out.ts +0 -22
  20. package/dist/ui/animations/anm_press.ts +0 -12
  21. package/dist/ui/animations/anm_pulse.ts +0 -13
  22. package/dist/ui/animations/anm_rise.ts +0 -19
  23. package/dist/ui/animations/anm_shake.ts +0 -13
  24. package/dist/ui/animations/anm_slide_in_left.ts +0 -19
  25. package/dist/ui/animations/anm_slide_in_right.ts +0 -19
  26. package/dist/ui/animations/anm_slide_out_left.ts +0 -18
  27. package/dist/ui/animations/anm_slide_out_right.ts +0 -18
  28. package/dist/ui/animations/anm_spin.ts +0 -12
  29. package/dist/ui/animations/anm_swell.ts +0 -16
  30. package/dist/ui/animations/anm_wobble.ts +0 -24
  31. package/dist/ui/animations/anm_zoom_in.ts +0 -19
  32. package/dist/ui/animations/anm_zoom_out.ts +0 -18
  33. package/dist/ui/animations.ts +0 -308
  34. package/dist/ui/attach.ts +0 -553
  35. package/dist/ui/charts.tsx +0 -69
  36. package/dist/ui/checked.ts +0 -32
  37. package/dist/ui/collector.ts +0 -140
  38. package/dist/ui/compositions.ts +0 -555
  39. package/dist/ui/consent.ts +0 -55
  40. package/dist/ui/content.tsx +0 -27
  41. package/dist/ui/contrast.ts +0 -27
  42. package/dist/ui/controls.tsx +0 -25
  43. package/dist/ui/disclosure.tsx +0 -20
  44. package/dist/ui/dismiss-layer.ts +0 -87
  45. package/dist/ui/door.ts +0 -140
  46. package/dist/ui/drag.ts +0 -316
  47. package/dist/ui/elements/accordion.tsx +0 -47
  48. package/dist/ui/elements/accordion.works.ts +0 -66
  49. package/dist/ui/elements/aspect-ratio.tsx +0 -31
  50. package/dist/ui/elements/aspect-ratio.works.ts +0 -5
  51. package/dist/ui/elements/avatar.tsx +0 -34
  52. package/dist/ui/elements/avatar.works.ts +0 -16
  53. package/dist/ui/elements/badge.tsx +0 -33
  54. package/dist/ui/elements/badge.works.ts +0 -9
  55. package/dist/ui/elements/bars.tsx +0 -50
  56. package/dist/ui/elements/bars.works.ts +0 -29
  57. package/dist/ui/elements/box.tsx +0 -36
  58. package/dist/ui/elements/breadcrumb.tsx +0 -38
  59. package/dist/ui/elements/breadcrumb.works.ts +0 -24
  60. package/dist/ui/elements/button.tsx +0 -46
  61. package/dist/ui/elements/button.works.tsx +0 -52
  62. package/dist/ui/elements/column.tsx +0 -29
  63. package/dist/ui/elements/consent.tsx +0 -71
  64. package/dist/ui/elements/consent.works.tsx +0 -132
  65. package/dist/ui/elements/donut.tsx +0 -45
  66. package/dist/ui/elements/donut.works.ts +0 -42
  67. package/dist/ui/elements/each.tsx +0 -34
  68. package/dist/ui/elements/each.works.tsx +0 -82
  69. package/dist/ui/elements/empty.tsx +0 -33
  70. package/dist/ui/elements/field.tsx +0 -45
  71. package/dist/ui/elements/field.works.ts +0 -65
  72. package/dist/ui/elements/funnel.tsx +0 -50
  73. package/dist/ui/elements/funnel.works.ts +0 -37
  74. package/dist/ui/elements/gauge.tsx +0 -40
  75. package/dist/ui/elements/gauge.works.ts +0 -42
  76. package/dist/ui/elements/glyph.tsx +0 -30
  77. package/dist/ui/elements/glyph.works.tsx +0 -42
  78. package/dist/ui/elements/grid.tsx +0 -29
  79. package/dist/ui/elements/heatmap.tsx +0 -48
  80. package/dist/ui/elements/heatmap.works.ts +0 -24
  81. package/dist/ui/elements/hit-box.tsx +0 -47
  82. package/dist/ui/elements/hit-box.works.tsx +0 -576
  83. package/dist/ui/elements/image.tsx +0 -29
  84. package/dist/ui/elements/line-chart.tsx +0 -45
  85. package/dist/ui/elements/line-chart.works.ts +0 -41
  86. package/dist/ui/elements/link.tsx +0 -38
  87. package/dist/ui/elements/link.works.ts +0 -25
  88. package/dist/ui/elements/media.tsx +0 -57
  89. package/dist/ui/elements/menu.tsx +0 -51
  90. package/dist/ui/elements/menu.works.tsx +0 -154
  91. package/dist/ui/elements/money.tsx +0 -36
  92. package/dist/ui/elements/money.works.ts +0 -9
  93. package/dist/ui/elements/pagination.tsx +0 -39
  94. package/dist/ui/elements/pagination.works.ts +0 -20
  95. package/dist/ui/elements/progress.tsx +0 -34
  96. package/dist/ui/elements/progress.works.ts +0 -8
  97. package/dist/ui/elements/radio-group.tsx +0 -50
  98. package/dist/ui/elements/radio-group.works.ts +0 -33
  99. package/dist/ui/elements/row.tsx +0 -29
  100. package/dist/ui/elements/scatter.tsx +0 -42
  101. package/dist/ui/elements/scatter.works.ts +0 -38
  102. package/dist/ui/elements/select.tsx +0 -43
  103. package/dist/ui/elements/select.works.ts +0 -5
  104. package/dist/ui/elements/separator.tsx +0 -31
  105. package/dist/ui/elements/skeleton.tsx +0 -31
  106. package/dist/ui/elements/slider.tsx +0 -34
  107. package/dist/ui/elements/slider.works.ts +0 -5
  108. package/dist/ui/elements/sparkline.tsx +0 -37
  109. package/dist/ui/elements/sparkline.works.ts +0 -32
  110. package/dist/ui/elements/spinner.tsx +0 -33
  111. package/dist/ui/elements/stack.tsx +0 -29
  112. package/dist/ui/elements/stat.tsx +0 -36
  113. package/dist/ui/elements/stat.works.ts +0 -14
  114. package/dist/ui/elements/surface.tsx +0 -36
  115. package/dist/ui/elements/surface.works.ts +0 -9
  116. package/dist/ui/elements/table.tsx +0 -62
  117. package/dist/ui/elements/table.works.ts +0 -30
  118. package/dist/ui/elements/tabs.tsx +0 -47
  119. package/dist/ui/elements/tabs.works.ts +0 -84
  120. package/dist/ui/elements/text.tsx +0 -33
  121. package/dist/ui/elements/textarea.tsx +0 -38
  122. package/dist/ui/elements/textarea.works.ts +0 -5
  123. package/dist/ui/elements/time.tsx +0 -40
  124. package/dist/ui/elements/time.works.ts +0 -29
  125. package/dist/ui/elements/toggle.tsx +0 -34
  126. package/dist/ui/elements/toggle.works.ts +0 -5
  127. package/dist/ui/elements/tooltip.tsx +0 -40
  128. package/dist/ui/elements/tooltip.works.ts +0 -77
  129. package/dist/ui/elements/value.tsx +0 -38
  130. package/dist/ui/elements/value.works.ts +0 -9
  131. package/dist/ui/elements.ts +0 -127
  132. package/dist/ui/focus-scope.ts +0 -121
  133. package/dist/ui/fonts.ts +0 -355
  134. package/dist/ui/generators/dev.d.ts +0 -1
  135. package/dist/ui/generators/dev.js +0 -61
  136. package/dist/ui/generators/dev.ts +0 -74
  137. package/dist/ui/generators/elements.ts +0 -796
  138. package/dist/ui/generators/i18n.ts +0 -283
  139. package/dist/ui/generators/icons.ts +0 -207
  140. package/dist/ui/generators/react.ts +0 -267
  141. package/dist/ui/generators/routes.ts +0 -847
  142. package/dist/ui/generators/show.ts +0 -261
  143. package/dist/ui/generators/styles.ts +0 -677
  144. package/dist/ui/generators/variants.ts +0 -196
  145. package/dist/ui/i18n/catalogs/en.ts +0 -191
  146. package/dist/ui/i18n/format.ts +0 -221
  147. package/dist/ui/i18n/locales.ts +0 -33
  148. package/dist/ui/i18n/parse.ts +0 -315
  149. package/dist/ui/i18n/translator.ts +0 -31
  150. package/dist/ui/i18n/types.ts +0 -96
  151. package/dist/ui/icons.ts +0 -331
  152. package/dist/ui/islands/map.ts +0 -58
  153. package/dist/ui/localizations.ts +0 -75
  154. package/dist/ui/material.ts +0 -236
  155. package/dist/ui/materials/mat_bordered.ts +0 -13
  156. package/dist/ui/materials/mat_ceramic.ts +0 -13
  157. package/dist/ui/materials/mat_clay.ts +0 -13
  158. package/dist/ui/materials/mat_elevated.ts +0 -24
  159. package/dist/ui/materials/mat_flat.ts +0 -34
  160. package/dist/ui/materials/mat_glass.ts +0 -20
  161. package/dist/ui/materials/mat_lit.ts +0 -13
  162. package/dist/ui/materials/mat_moulded.ts +0 -11
  163. package/dist/ui/materials.ts +0 -197
  164. package/dist/ui/modes.ts +0 -44
  165. package/dist/ui/navigation.tsx +0 -29
  166. package/dist/ui/positioning.ts +0 -90
  167. package/dist/ui/presented.tsx +0 -119
  168. package/dist/ui/provider.tsx +0 -184
  169. package/dist/ui/render-loop.ts +0 -133
  170. package/dist/ui/roving-focus.ts +0 -161
  171. package/dist/ui/screens.ts +0 -141
  172. package/dist/ui/scroll-lock.ts +0 -28
  173. package/dist/ui/series.ts +0 -97
  174. package/dist/ui/signals.ts +0 -386
  175. package/dist/ui/styleguide.ts +0 -231
  176. package/dist/ui/styleguides.ts +0 -374
  177. package/dist/ui/themes.ts +0 -22
  178. package/dist/ui/token.ts +0 -78
  179. package/dist/ui/tokens/shadow.ts +0 -25
  180. package/dist/ui/tokens.ts +0 -234
  181. package/dist/ui/validators.ts +0 -232
  182. package/dist/ui/variant.ts +0 -352
  183. package/dist/ui/variants/var_avatar-round.ts +0 -14
  184. package/dist/ui/variants/var_bubble-me.ts +0 -14
  185. package/dist/ui/variants/var_bubble-them.ts +0 -13
  186. package/dist/ui/variants/var_button-border.ts +0 -76
  187. package/dist/ui/variants/var_button-cta.ts +0 -76
  188. package/dist/ui/variants/var_button-destructive-ghost.ts +0 -72
  189. package/dist/ui/variants/var_button-destructive.ts +0 -76
  190. package/dist/ui/variants/var_button-fill.ts +0 -76
  191. package/dist/ui/variants/var_button-ghost.ts +0 -72
  192. package/dist/ui/variants/var_button-glass.ts +0 -55
  193. package/dist/ui/variants/var_button-normal.ts +0 -76
  194. package/dist/ui/variants/var_button-quiet.ts +0 -74
  195. package/dist/ui/variants/var_card.ts +0 -17
  196. package/dist/ui/variants/var_chip.ts +0 -13
  197. package/dist/ui/variants/var_choice.ts +0 -15
  198. package/dist/ui/variants/var_framed.ts +0 -10
  199. package/dist/ui/variants/var_glyph-plain.ts +0 -11
  200. package/dist/ui/variants/var_input.ts +0 -43
  201. package/dist/ui/variants/var_link-inline.ts +0 -29
  202. package/dist/ui/variants/var_list-row.ts +0 -34
  203. package/dist/ui/variants/var_rule.ts +0 -10
  204. package/dist/ui/variants/var_skeleton-block.ts +0 -12
  205. package/dist/ui/variants/var_spinner-turning.ts +0 -12
  206. package/dist/ui/variants/var_stat-tile.ts +0 -13
  207. package/dist/ui/variants/var_tab-bar.ts +0 -15
  208. package/dist/ui/variants/var_tap-target.ts +0 -25
  209. package/dist/ui/variants.ts +0 -64
  210. package/dist/ui/wrappers.tsx +0 -429
@@ -1,236 +0,0 @@
1
- import { held, naming, sheeted, type Holds, type Token, type Wearing } from './token.ts'
2
-
3
- export interface Setting {
4
- readonly property: string
5
-
6
- readonly is?: string
7
-
8
- readonly token?: string
9
-
10
- readonly asks?: Readonly<Record<string, string | number>>
11
- }
12
-
13
- export const BOUND = {
14
- background: ['colour', 'gradient', 'texture', 'noise', 'img'],
15
- backgroundImage: ['gradient', 'texture', 'noise', 'img'],
16
- backdropFilter: ['blur'],
17
- borderWidth: ['edge'],
18
- borderStyle: ['line'],
19
- borderColor: ['colour'],
20
- boxShadow: ['shadow'],
21
- color: ['colour'],
22
- opacity: ['opacity'],
23
- } as const
24
-
25
- type Property = keyof typeof BOUND
26
-
27
- type MaterialSettingFor<P extends Property> = {
28
- readonly property: P
29
-
30
- readonly is?: string
31
-
32
- readonly token?: (typeof BOUND)[P][number]
33
-
34
- readonly asks?: Readonly<Record<string, string | number>>
35
- }
36
-
37
- type MaterialSetting = { [P in Property]: MaterialSettingFor<P> }[Property]
38
-
39
- export interface Material {
40
- readonly id: string
41
-
42
- readonly sets: readonly MaterialSetting[]
43
-
44
- readonly states?: readonly {
45
- readonly when: readonly string[]
46
- readonly sets: readonly MaterialSetting[]
47
- }[]
48
-
49
- readonly plays?: string
50
-
51
- readonly keyframes?: readonly {
52
- readonly id: string
53
- readonly sets: Readonly<Record<string, readonly MaterialSetting[]>>
54
- }[]
55
- }
56
-
57
- export const FREE = ['transition', 'outline'] as const
58
-
59
- export const material = <const T extends Material>(
60
- one: T & {
61
- readonly sets: {
62
- readonly [I in keyof T['sets']]: T['sets'][I] extends { readonly is: infer Said }
63
- ? { readonly is: Wearing<Said & string> }
64
- : unknown
65
- }
66
- } & (T extends { readonly states: infer States }
67
- ? {
68
- readonly states: {
69
- readonly [S in keyof States]: States[S] extends { readonly sets: infer Sets }
70
- ? {
71
- readonly sets: {
72
- readonly [I in keyof Sets]: Sets[I] extends { readonly is: infer Said }
73
- ? { readonly is: Wearing<Said & string> }
74
- : unknown
75
- }
76
- }
77
- : unknown
78
- }
79
- }
80
- : unknown) &
81
- (T extends { readonly keyframes: infer Frames }
82
- ? {
83
- readonly keyframes: {
84
- readonly [F in keyof Frames]: Frames[F] extends { readonly sets: infer Stops }
85
- ? {
86
- readonly sets: {
87
- readonly [Stop in keyof Stops]: {
88
- readonly [I in keyof Stops[Stop]]: Stops[Stop][I] extends { readonly is: infer Said }
89
- ? { readonly is: Wearing<Said & string> }
90
- : unknown
91
- }
92
- }
93
- }
94
- : unknown
95
- }
96
- }
97
- : unknown),
98
- ): T => one
99
-
100
- function rowsIn(value: string): readonly string[] {
101
- return value.split(/\s+/).filter((one) => one !== '')
102
- }
103
-
104
- function bounded(
105
- set: Setting,
106
- by: string,
107
- kinds: Readonly<Record<string, string>>,
108
- bound: Readonly<Record<string, readonly string[]>>,
109
- ): void {
110
- const value = set.is ?? ''
111
-
112
- const rows = rowsIn(value)
113
-
114
- const allowed = bound[set.property]
115
- if (allowed === undefined) return
116
-
117
- for (const row of rows) {
118
- const kind = kinds[row]
119
- if (kind === undefined || allowed.includes(kind)) continue
120
- const list = allowed.map((one) => `\`${one}\``).join(' · ')
121
- throw new Error(
122
- `\`${by}\` sets \`${set.property}\` to \`${row}\`, and \`${row}\` is a \`${kind}\` row. ` +
123
- `\`${set.property}\` takes: ${list}.`,
124
- )
125
- }
126
- }
127
-
128
- function valued(
129
- set: Setting,
130
- named: ReadonlySet<string>,
131
- tokens: Readonly<Record<string, Token>>,
132
- by: string,
133
- kinds: Readonly<Record<string, string>> | undefined,
134
- bound: Readonly<Record<string, readonly string[]>>,
135
- ): Holds {
136
- if (set.is !== undefined && set.token !== undefined) {
137
- throw new Error(
138
- `\`${by}\` states both a value and a token for \`${set.property}\`. A property is either a ` +
139
- 'value the style guide holds or a token asked for one, never both.',
140
- )
141
- }
142
-
143
- if (set.is !== undefined) {
144
- if (kinds !== undefined) bounded(set, by, kinds, bound)
145
- return naming(named, set.is, by, set.property)
146
- }
147
-
148
- if (set.token === undefined) {
149
- throw new Error(
150
- `\`${by}\` sets \`${set.property}\` to nothing. State a value with \`is\`, or name a token ` +
151
- 'and what to ask it for.',
152
- )
153
- }
154
-
155
- const token = tokens[set.token]
156
- if (token === undefined) {
157
- throw new Error(
158
- `\`${by}\` asks \`${set.token}\` for \`${set.property}\`, and no token by that name is ` +
159
- 'declared. A token has to exist before anything can ask it for a value.',
160
- )
161
- }
162
-
163
- if (token.makes === undefined) {
164
- throw new Error(
165
- `\`${by}\` asks \`${set.token}\` for \`${set.property}\`, and \`${set.token}\` carries no ` +
166
- 'rule, so there is nothing to ask. Name a style guide value instead.',
167
- )
168
- }
169
-
170
- return held(named, token.makes(set.asks ?? {}), by)
171
- }
172
-
173
- export function composed(
174
- sets: readonly Setting[],
175
- named: ReadonlySet<string>,
176
- tokens: Readonly<Record<string, Token>>,
177
- by: string,
178
- kinds: Readonly<Record<string, string>>,
179
- bound: Readonly<Record<string, readonly string[]>>,
180
- ): Readonly<Record<string, Holds>> {
181
- const made: Record<string, Holds> = {}
182
- for (const set of sets) {
183
-
184
- if (bound[set.property] === undefined && (set.property === 'animation' || set.property === 'animationName')) {
185
- throw new Error(
186
- `\`${by}\` sets \`${set.property}\` itself, which buries the keyframes name inside a ` +
187
- 'string where nothing can check it. Name a style guide row of the `animation` kind, or ' +
188
- 'declare keyframes and name them with `plays`.',
189
- )
190
- }
191
- made[set.property] = valued(set, named, tokens, by, kinds, bound)
192
- }
193
- return made
194
- }
195
-
196
- function playing(material: Material, plays: string | undefined): Readonly<Record<string, Holds>> {
197
- if (plays === undefined) return {}
198
- if ((material.keyframes ?? []).some((one) => one.id === plays)) return { animationName: sheeted(material.id, plays) }
199
- const declared = (material.keyframes ?? []).map((one) => one.id)
200
- throw new Error(
201
- `\`${material.id}\` plays \`${plays}\`, and declares no keyframes by that name. ` +
202
- (declared.length === 0
203
- ? 'It declares none at all, so there is nothing for it to play.'
204
- : `It declares: ${declared.join(' · ')}.`),
205
- )
206
- }
207
-
208
- export function compose(
209
- material: Material,
210
- named: ReadonlySet<string>,
211
- tokens: Readonly<Record<string, Token>>,
212
- kinds: Readonly<Record<string, string>>,
213
- ): Readonly<Record<string, Holds>> {
214
- return {
215
- ...composed(material.sets, named, tokens, material.id, kinds, BOUND),
216
- ...playing(material, material.plays),
217
- }
218
- }
219
-
220
- export function keyframesOf(
221
- material: Material,
222
- named: ReadonlySet<string>,
223
- tokens: Readonly<Record<string, Token>>,
224
- kinds: Readonly<Record<string, string>>,
225
- ): readonly {
226
- readonly id: string
227
- readonly stops: readonly { readonly stop: string; readonly made: Readonly<Record<string, Holds>> }[]
228
- }[] {
229
- return (material.keyframes ?? []).map((one) => ({
230
- id: one.id,
231
- stops: Object.keys(one.sets).map((stop) => ({
232
- stop,
233
- made: composed(one.sets[stop] ?? [], named, tokens, material.id, kinds, BOUND),
234
- })),
235
- }))
236
- }
@@ -1,13 +0,0 @@
1
- import { material } from '../material.ts'
2
-
3
- export const BORDERED = material({
4
- id: 'bordered',
5
- sets: [
6
- { property: 'background', is: 'colour-surface' },
7
- { property: 'borderWidth', is: 'edge-thick' },
8
- { property: 'borderStyle', is: 'line-solid' },
9
- { property: 'borderColor', is: 'colour-ink' },
10
- { property: 'boxShadow', is: 'shadow-hard' },
11
- { property: 'color', is: 'colour-ink' },
12
- ],
13
- } as const)
@@ -1,13 +0,0 @@
1
- import { material } from '../material.ts'
2
-
3
- export const CERAMIC = material({
4
- id: 'ceramic',
5
- sets: [
6
- { property: 'backgroundImage', is: 'gradient-sheen' },
7
- { property: 'borderWidth', is: 'edge-hair' },
8
- { property: 'borderStyle', is: 'line-solid' },
9
- { property: 'borderColor', is: 'colour-edge-ceramic' },
10
- { property: 'boxShadow', is: 'shadow-2' },
11
- { property: 'color', is: 'colour-ink' },
12
- ],
13
- } as const)
@@ -1,13 +0,0 @@
1
- import { material } from '../material.ts'
2
-
3
- export const CLAY = material({
4
- id: 'clay',
5
- sets: [
6
- { property: 'background', is: 'colour-surface' },
7
- { property: 'borderWidth', is: 'edge-thin' },
8
- { property: 'borderStyle', is: 'line-solid' },
9
- { property: 'borderColor', is: 'colour-edge-clay' },
10
- { property: 'boxShadow', is: 'shadow-2' },
11
- { property: 'color', is: 'colour-ink' },
12
- ],
13
- } as const)
@@ -1,24 +0,0 @@
1
- import { material } from '../material.ts'
2
-
3
- export const ELEVATED = material({
4
- id: 'elevated',
5
- sets: [
6
- { property: 'background', is: 'colour-surface' },
7
- { property: 'boxShadow', is: 'shadow-2' },
8
- { property: 'color', is: 'colour-ink' },
9
- ],
10
- states: [
11
- {
12
- when: ['hover'],
13
- sets: [{ property: 'boxShadow', is: 'shadow-3' }],
14
- },
15
- {
16
- when: ['active'],
17
- sets: [{ property: 'boxShadow', is: 'shadow-1' }],
18
- },
19
- {
20
- when: ['active', 'hover'],
21
- sets: [{ property: 'boxShadow', is: 'shadow-1' }],
22
- },
23
- ],
24
- } as const)
@@ -1,34 +0,0 @@
1
- import { material } from '../material.ts'
2
-
3
- export const FLAT = material({
4
- id: 'flat',
5
- sets: [
6
- { property: 'background', is: 'colour-surface' },
7
- { property: 'borderWidth', is: 'edge-hair' },
8
- { property: 'borderStyle', is: 'line-solid' },
9
- { property: 'borderColor', is: 'colour-edge-faint' },
10
- { property: 'boxShadow', is: 'shadow-1' },
11
- { property: 'color', is: 'colour-ink' },
12
- ],
13
- states: [
14
- {
15
- when: ['hover'],
16
- sets: [{ property: 'boxShadow', is: 'shadow-2' }],
17
- },
18
- {
19
- when: ['focus'],
20
- sets: [
21
- { property: 'borderColor', is: 'colour-accent' },
22
- { property: 'borderWidth', is: 'edge-thick' },
23
- ],
24
- },
25
- {
26
- when: ['focus', 'hover'],
27
- sets: [
28
- { property: 'borderColor', is: 'colour-accent' },
29
- { property: 'borderWidth', is: 'edge-thick' },
30
- { property: 'boxShadow', is: 'shadow-2' },
31
- ],
32
- },
33
- ],
34
- } as const)
@@ -1,20 +0,0 @@
1
- import { material } from '../material.ts'
2
-
3
- export const GLASS = material({
4
- id: 'glass',
5
- sets: [
6
- { property: 'background', is: 'colour-glass' },
7
- { property: 'backdropFilter', is: 'blur-md' },
8
- { property: 'borderWidth', is: 'edge-hair' },
9
- { property: 'borderStyle', is: 'line-solid' },
10
- { property: 'borderColor', is: 'colour-edge-glass' },
11
- { property: 'boxShadow', is: 'shadow-3' },
12
- { property: 'color', is: 'colour-ink' },
13
- ],
14
- states: [
15
- {
16
- when: ['hover'],
17
- sets: [{ property: 'backdropFilter', is: 'blur-lg' }],
18
- },
19
- ],
20
- } as const)
@@ -1,13 +0,0 @@
1
- import { material } from '../material.ts'
2
-
3
- export const LIT = material({
4
- id: 'lit',
5
- sets: [
6
- { property: 'background', is: 'colour-surface-lit' },
7
- { property: 'borderWidth', is: 'edge-thin' },
8
- { property: 'borderStyle', is: 'line-solid' },
9
- { property: 'borderColor', is: 'colour-edge-lit' },
10
- { property: 'boxShadow', is: 'shadow-glow' },
11
- { property: 'color', is: 'colour-ink' },
12
- ],
13
- } as const)
@@ -1,11 +0,0 @@
1
- import { material } from '../material.ts'
2
-
3
- export const MOULDED = material({
4
- id: 'moulded',
5
- sets: [
6
- { property: 'background', is: 'colour-page' },
7
- { property: 'borderStyle', is: 'line-off' },
8
- { property: 'boxShadow', is: 'shadow-moulded' },
9
- { property: 'color', is: 'colour-ink' },
10
- ],
11
- } as const)
@@ -1,197 +0,0 @@
1
- import { existsSync, readFileSync, writeFileSync } from '@prestack/thread/integrations/disk'
2
- import { basename, join } from 'node:path'
3
- import { argv, stdout } from 'node:process'
4
- import { pathToFileURL } from 'node:url'
5
-
6
- import { exit, invoked } from '@prestack/thread/core/invoked'
7
- import type { Threaded } from '@prestack/thread/core/thread'
8
- import { findTree, inTree, strayFiles } from '@prestack/thread/core/tree'
9
-
10
- export const MATERIALS = {
11
- id: 'material',
12
- at: 'thread/ui/materials.ts',
13
- files: 'ui/mat_*.ts',
14
- describes:
15
- 'A combination of style guide values that gives one specific appearance, worn by any variant that names it: glass, clay, a glaze. It composes and never computes, because every value it sets is a name the style guide holds and never a raw one, which is what lets one material be worn by many variants. Each row names a file prefixed `mat_`, and that file is the material; a file under it that no row names is not a material but a file somebody wrote. An app adds its own to this same list, and a row that goes missing breaks the build, because what the framework offers is not an app’s to withdraw. These are materials and not aesthetics: Neumorphism, Claymorphism and Cyberpunk are templates, and a template is what picks a material.',
16
- version: 1,
17
- cites: [
18
- {
19
- on: '2026-08-25',
20
- said: 'Style guides they can be used for simple pickings that need no thinking. We can pick colours and basic effects for using the app. Materials however are for more complex and detailed work. Materials.ts is a declaration sheet, SSOT, and each row is a file, mat_',
21
- },
22
- ],
23
- declares: [
24
- { id: 'glass', at: 'thread/ui/materials/mat_glass.ts', describes: 'A pane you can see through, with what is behind it softened and desaturation corrected for.' },
25
- { id: 'flat', at: 'thread/ui/materials/mat_flat.ts', describes: 'Paper. A plain filled panel with a hairline and almost no lift — the same tokens as glass, asked more quietly.' },
26
- { id: 'elevated', at: 'thread/ui/materials/mat_elevated.ts', describes: 'Something lifted off the page, reading by its shadow rather than by any outline.' },
27
- { id: 'bordered', at: 'thread/ui/materials/mat_bordered.ts', describes: 'Ink on paper — a hard outline and a shadow with no blur, offset like a stamp.' },
28
- { id: 'moulded', at: 'thread/ui/materials/mat_moulded.ts', describes: 'Pressed out of the page rather than laid on it. Its fill is the page itself, and its relief is one shadow cast twice in opposite directions.' },
29
- { id: 'clay', at: 'thread/ui/materials/mat_clay.ts', describes: 'Something soft and inflated, lit from above — an outer shadow it sits in and an inset highlight along its top edge.' },
30
- { id: 'lit', at: 'thread/ui/materials/mat_lit.ts', describes: 'Something that emits rather than reflects. Its glow is a shadow cast from nowhere in particular, and it names no colour of its own.' },
31
- { id: 'ceramic', at: 'thread/ui/materials/mat_ceramic.ts', describes: 'A glazed surface with the light falling down its face. The one material that asks for a gradient rather than a fill.' },
32
- ],
33
- } as const satisfies Threaded & { readonly files: string }
34
-
35
- export function nameOf(id: string): string {
36
- return id.replace(/-/g, '_').toUpperCase()
37
- }
38
-
39
- function starter(row: Threaded): string {
40
- const name = nameOf(row.id)
41
- return `import { material } from './material.ts'
42
-
43
- export const ${name} = material({
44
- id: '${row.id}',
45
- sets: [],
46
- } as const)
47
- `
48
- }
49
-
50
- export function seeds(rows: readonly Threaded[], held: (at: string) => boolean): readonly { readonly path: string; readonly content: string }[] {
51
- return rows.filter((row) => !held(row.at)).map((row) => ({ path: row.at, content: starter(row) }))
52
- }
53
-
54
- export async function mismatched(
55
- rows: readonly Threaded[],
56
- loader: (row: Threaded) => Promise<{ readonly id?: string } | undefined>,
57
- ): Promise<readonly string[]> {
58
- const found: string[] = []
59
- for (const row of rows) {
60
- const material = await loader(row)
61
- if (material === undefined) {
62
- found.push(
63
- `\`${row.id}\` is declared at \`${row.at}\`, and that file exports no \`${nameOf(row.id)}\`. ` +
64
- 'A row names a file, and the file is the thing.',
65
- )
66
- continue
67
- }
68
- if (material.id !== row.id) {
69
- found.push(
70
- `\`${row.at}\` declares itself \`${String(material.id)}\`, and the row that names it calls it \`${row.id}\`. ` +
71
- "A material file and the row that names it have to be the same material — make the file's `id` match the row, or fix the row.",
72
- )
73
- }
74
- }
75
- return found
76
- }
77
-
78
- export function orphaned(rows: readonly Threaded[], strays: readonly string[]): readonly string[] {
79
- const named = new Set(rows.map((row) => row.at))
80
- return strays
81
- .filter((one) => !named.has(one))
82
- .map(
83
- (one) =>
84
- `\`${one}\` is under materials and no row names it — not a material, a file somebody wrote. ` +
85
- 'Add a row for it, or delete the file.',
86
- )
87
- }
88
-
89
- const GUARDED = /=\s*material\s*\(/
90
-
91
- const TYPED = /import type \{ Material \} from '([^']*)'/
92
-
93
- const OPENS = /export const ([A-Z0-9_]+) = \{/
94
-
95
- const CLOSES = /\} as const satisfies Material/
96
-
97
- export function unguarded(rows: readonly Threaded[], read: (at: string) => string): readonly string[] {
98
- return rows
99
- .filter((row) => !GUARDED.test(read(row.at)))
100
- .map(
101
- (row) =>
102
- `\`${row.at}\` declares \`${row.id}\` with \`satisfies Material\`, which accepts a value no ` +
103
- 'row holds — `satisfies` checks against `string`, so `8px` and `var(--x)` compile. Run ' +
104
- '`thread materials` and it rewrites the two lines that wrap the declaration, leaving what is ' +
105
- 'between them alone.',
106
- )
107
- }
108
-
109
- export function guarding(source: string): string | undefined {
110
- if (GUARDED.test(source)) return undefined
111
- const typed = TYPED.exec(source)
112
- const opens = OPENS.exec(source)
113
- if (typed === null || opens === null || !CLOSES.test(source)) return undefined
114
- return source
115
- .replace(typed[0], `import { material } from '${typed[1] ?? ''}'`)
116
- .replace(opens[0], `export const ${opens[1] ?? ''} = material({`)
117
- .replace(CLOSES, '} as const)')
118
- }
119
-
120
- async function run(): Promise<number> {
121
- const tree = findTree()
122
- const at = inTree(tree, 'materials.ts')
123
- if (!existsSync(at)) {
124
- stdout.write(`thread: this tree has no ${tree.name}/${basename(at)}, so it declares no material and there is nothing to seed or check.\n`)
125
- return 0
126
- }
127
- const held = (await import(pathToFileURL(at).href)) as { MATERIAL?: { readonly declares?: readonly Threaded[] } }
128
-
129
- if (held.MATERIAL === undefined) {
130
- stdout.write(`thread: ${tree.name}/materials.ts exports no \`MATERIAL\`, so nothing declares a material here.\n`)
131
- return 1
132
- }
133
-
134
- const rows = held.MATERIAL.declares ?? []
135
- const checking = argv.includes('--check')
136
- const exists = (where: string): boolean => existsSync(join(tree.at, where))
137
- const made = seeds(rows, exists)
138
-
139
- if (checking) {
140
- const unseeded = rows.find((row) => !exists(row.at))
141
- if (unseeded !== undefined) {
142
- stdout.write(
143
- `thread: \`${unseeded.id}\` is declared at \`${unseeded.at}\`, and the file is not there yet. ` +
144
- 'A row names a file, and `--check` never writes one — run `thread materials` and commit what it seeds.\n',
145
- )
146
- return 1
147
- }
148
- } else {
149
- for (const one of made) {
150
- writeFileSync(join(tree.at, one.path), one.content, 'utf8')
151
- stdout.write(`thread: seeded ${one.path}\n`)
152
- }
153
- }
154
-
155
- const wrong = (
156
- await mismatched(rows, async (row) => {
157
- const module = (await import(pathToFileURL(join(tree.at, row.at)).href)) as Record<string, unknown>
158
- return module[nameOf(row.id)] as { readonly id?: string } | undefined
159
- })
160
- )[0]
161
- if (wrong !== undefined) {
162
- stdout.write(`thread: ${wrong}\n`)
163
- return 1
164
- }
165
-
166
- const read = (at: string): string => readFileSync(join(tree.at, at), 'utf8')
167
- if (checking) {
168
- const loose = unguarded(rows, read)[0]
169
- if (loose !== undefined) {
170
- stdout.write(`thread: ${loose}\n`)
171
- return 1
172
- }
173
- } else {
174
- for (const row of rows) {
175
- const written = guarding(read(row.at))
176
- if (written === undefined) continue
177
- writeFileSync(join(tree.at, row.at), written, 'utf8')
178
- stdout.write(`thread: ${row.at} now declares through \`material(...)\`\n`)
179
- }
180
- }
181
-
182
- const stray = orphaned(rows, strayFiles(tree.at, 'mat_', '.' + 'ts'))[0]
183
- if (stray !== undefined) {
184
- stdout.write(`thread: ${stray}\n`)
185
- return 1
186
- }
187
-
188
- stdout.write(
189
- checking
190
- ? `thread: ${String(rows.length)} material(s) declared, all seeded\n`
191
- : `thread: ${String(rows.length)} material(s) declared, ${String(made.length)} seeded, ` +
192
- `${String(rows.length - made.length)} already written\n`,
193
- )
194
- return 0
195
- }
196
-
197
- if (invoked(import.meta.url)) exit(await run())
package/dist/ui/modes.ts DELETED
@@ -1,44 +0,0 @@
1
- import type { Threaded } from '@prestack/thread/core/thread'
2
-
3
- const here = 'thread/ui/modes.ts'
4
-
5
- const said = [
6
- {
7
- on: '2026-08-25',
8
- said: 'themes can have dark and light modes too. So they are not at all the same.',
9
- },
10
- ]
11
-
12
- const one = <Id extends string>(
13
- id: Id,
14
- is: string,
15
- describes: string,
16
- ): Omit<Threaded, 'at'> & { readonly id: Id; readonly is: string } => ({
17
- id,
18
- is,
19
- describes,
20
- version: 1,
21
- })
22
-
23
- export const holds = [
24
- one(
25
- 'dark',
26
- '(prefers-color-scheme: dark)',
27
- 'The reader has asked their device for a dark interface. Nothing about the app chooses this and nothing about the app can override it.',
28
- ),
29
- one(
30
- 'light',
31
- '(prefers-color-scheme: light)',
32
- 'The reader has asked their device for a light interface. Stated rather than assumed, because the resting values are not the same thing as an answer to the question.',
33
- ),
34
- ] as const satisfies readonly (Omit<Threaded, 'at'> & { readonly is: string })[]
35
-
36
- export const MODES = {
37
- id: 'mode',
38
- at: here,
39
- describes:
40
- 'Every way a reader’s own device can ask for a different look, and the query that answers it. A mode is chosen for the app rather than by it, which is what tells it from a theme — and a theme has its own modes, so the two are dimensions rather than one list. A mode changes what the variables hold, never what a material or a variant says.',
41
- version: 1,
42
- cites: said,
43
- holds,
44
- } as const satisfies Threaded & { readonly holds: readonly (Omit<Threaded, 'at'> & { readonly is: string })[] }
@@ -1,29 +0,0 @@
1
- 'use client'
2
-
3
- import type { MapIsland, Screen } from './islands/map.ts'
4
-
5
- export type ScreenName = Screen['id']
6
-
7
- export function trailTo(map: MapIsland, to: ScreenName): readonly ScreenName[] {
8
- const entries = map.screens.filter((s) => s.entry).map((s) => s.id)
9
- if (entries.includes(to)) return [to]
10
-
11
- const seen = new Set<ScreenName>(entries)
12
- let frontier: readonly (readonly ScreenName[])[] = entries.map((id) => [id])
13
-
14
- while (frontier.length > 0) {
15
- const next: (readonly ScreenName[])[] = []
16
- for (const path of frontier) {
17
- const here = path[path.length - 1] as ScreenName
18
- for (const link of map.links.filter((l) => l.from === here)) {
19
- if (link.to === to) return [...path, link.to]
20
- if (seen.has(link.to)) continue
21
- seen.add(link.to)
22
- next.push([...path, link.to])
23
- }
24
- }
25
- frontier = next
26
- }
27
- return []
28
- }
29
-