@prestack/thread-ui 0.1.10 → 0.1.12

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 (209) hide show
  1. package/CHANGELOG.md +375 -0
  2. package/dist/ui/attach.js +6 -7
  3. package/dist/ui/compositions.d.ts +0 -8
  4. package/dist/ui/compositions.js +3 -166
  5. package/dist/ui/generators/routes.js +1 -0
  6. package/package.json +2 -2
  7. package/dist/ui/animations/anm_appear.ts +0 -13
  8. package/dist/ui/animations/anm_blur_in.ts +0 -19
  9. package/dist/ui/animations/anm_blur_out.ts +0 -18
  10. package/dist/ui/animations/anm_dismiss.ts +0 -18
  11. package/dist/ui/animations/anm_drop.ts +0 -19
  12. package/dist/ui/animations/anm_fall.ts +0 -18
  13. package/dist/ui/animations/anm_flash.ts +0 -18
  14. package/dist/ui/animations/anm_glow.ts +0 -16
  15. package/dist/ui/animations/anm_lift.ts +0 -16
  16. package/dist/ui/animations/anm_nudge.ts +0 -16
  17. package/dist/ui/animations/anm_pop_in.ts +0 -23
  18. package/dist/ui/animations/anm_pop_out.ts +0 -22
  19. package/dist/ui/animations/anm_press.ts +0 -12
  20. package/dist/ui/animations/anm_pulse.ts +0 -13
  21. package/dist/ui/animations/anm_rise.ts +0 -19
  22. package/dist/ui/animations/anm_shake.ts +0 -13
  23. package/dist/ui/animations/anm_slide_in_left.ts +0 -19
  24. package/dist/ui/animations/anm_slide_in_right.ts +0 -19
  25. package/dist/ui/animations/anm_slide_out_left.ts +0 -18
  26. package/dist/ui/animations/anm_slide_out_right.ts +0 -18
  27. package/dist/ui/animations/anm_spin.ts +0 -12
  28. package/dist/ui/animations/anm_swell.ts +0 -16
  29. package/dist/ui/animations/anm_wobble.ts +0 -24
  30. package/dist/ui/animations/anm_zoom_in.ts +0 -19
  31. package/dist/ui/animations/anm_zoom_out.ts +0 -18
  32. package/dist/ui/animations.ts +0 -308
  33. package/dist/ui/attach.ts +0 -553
  34. package/dist/ui/charts.tsx +0 -69
  35. package/dist/ui/checked.ts +0 -32
  36. package/dist/ui/collector.ts +0 -140
  37. package/dist/ui/compositions.ts +0 -613
  38. package/dist/ui/consent.ts +0 -55
  39. package/dist/ui/content.tsx +0 -27
  40. package/dist/ui/contrast.ts +0 -27
  41. package/dist/ui/controls.tsx +0 -25
  42. package/dist/ui/disclosure.tsx +0 -20
  43. package/dist/ui/dismiss-layer.ts +0 -87
  44. package/dist/ui/door.ts +0 -140
  45. package/dist/ui/drag.ts +0 -316
  46. package/dist/ui/elements/accordion.tsx +0 -47
  47. package/dist/ui/elements/accordion.works.ts +0 -66
  48. package/dist/ui/elements/aspect-ratio.tsx +0 -31
  49. package/dist/ui/elements/aspect-ratio.works.ts +0 -5
  50. package/dist/ui/elements/avatar.tsx +0 -34
  51. package/dist/ui/elements/avatar.works.ts +0 -16
  52. package/dist/ui/elements/badge.tsx +0 -33
  53. package/dist/ui/elements/badge.works.ts +0 -9
  54. package/dist/ui/elements/bars.tsx +0 -50
  55. package/dist/ui/elements/bars.works.ts +0 -29
  56. package/dist/ui/elements/box.tsx +0 -36
  57. package/dist/ui/elements/breadcrumb.tsx +0 -38
  58. package/dist/ui/elements/breadcrumb.works.ts +0 -24
  59. package/dist/ui/elements/button.tsx +0 -46
  60. package/dist/ui/elements/button.works.tsx +0 -52
  61. package/dist/ui/elements/column.tsx +0 -29
  62. package/dist/ui/elements/consent.tsx +0 -71
  63. package/dist/ui/elements/consent.works.tsx +0 -132
  64. package/dist/ui/elements/donut.tsx +0 -45
  65. package/dist/ui/elements/donut.works.ts +0 -42
  66. package/dist/ui/elements/each.tsx +0 -34
  67. package/dist/ui/elements/each.works.tsx +0 -82
  68. package/dist/ui/elements/empty.tsx +0 -33
  69. package/dist/ui/elements/field.tsx +0 -45
  70. package/dist/ui/elements/field.works.ts +0 -65
  71. package/dist/ui/elements/funnel.tsx +0 -50
  72. package/dist/ui/elements/funnel.works.ts +0 -37
  73. package/dist/ui/elements/gauge.tsx +0 -40
  74. package/dist/ui/elements/gauge.works.ts +0 -42
  75. package/dist/ui/elements/glyph.tsx +0 -30
  76. package/dist/ui/elements/glyph.works.tsx +0 -42
  77. package/dist/ui/elements/grid.tsx +0 -29
  78. package/dist/ui/elements/heatmap.tsx +0 -48
  79. package/dist/ui/elements/heatmap.works.ts +0 -24
  80. package/dist/ui/elements/hit-box.tsx +0 -47
  81. package/dist/ui/elements/hit-box.works.tsx +0 -576
  82. package/dist/ui/elements/image.tsx +0 -29
  83. package/dist/ui/elements/line-chart.tsx +0 -45
  84. package/dist/ui/elements/line-chart.works.ts +0 -41
  85. package/dist/ui/elements/link.tsx +0 -38
  86. package/dist/ui/elements/link.works.ts +0 -25
  87. package/dist/ui/elements/media.tsx +0 -57
  88. package/dist/ui/elements/menu.tsx +0 -51
  89. package/dist/ui/elements/menu.works.tsx +0 -154
  90. package/dist/ui/elements/money.tsx +0 -36
  91. package/dist/ui/elements/money.works.ts +0 -9
  92. package/dist/ui/elements/pagination.tsx +0 -39
  93. package/dist/ui/elements/pagination.works.ts +0 -20
  94. package/dist/ui/elements/progress.tsx +0 -34
  95. package/dist/ui/elements/progress.works.ts +0 -8
  96. package/dist/ui/elements/radio-group.tsx +0 -50
  97. package/dist/ui/elements/radio-group.works.ts +0 -33
  98. package/dist/ui/elements/row.tsx +0 -29
  99. package/dist/ui/elements/scatter.tsx +0 -42
  100. package/dist/ui/elements/scatter.works.ts +0 -38
  101. package/dist/ui/elements/select.tsx +0 -43
  102. package/dist/ui/elements/select.works.ts +0 -5
  103. package/dist/ui/elements/separator.tsx +0 -31
  104. package/dist/ui/elements/skeleton.tsx +0 -31
  105. package/dist/ui/elements/slider.tsx +0 -34
  106. package/dist/ui/elements/slider.works.ts +0 -5
  107. package/dist/ui/elements/sparkline.tsx +0 -37
  108. package/dist/ui/elements/sparkline.works.ts +0 -32
  109. package/dist/ui/elements/spinner.tsx +0 -33
  110. package/dist/ui/elements/stack.tsx +0 -29
  111. package/dist/ui/elements/stat.tsx +0 -36
  112. package/dist/ui/elements/stat.works.ts +0 -14
  113. package/dist/ui/elements/surface.tsx +0 -36
  114. package/dist/ui/elements/surface.works.ts +0 -9
  115. package/dist/ui/elements/table.tsx +0 -62
  116. package/dist/ui/elements/table.works.ts +0 -30
  117. package/dist/ui/elements/tabs.tsx +0 -47
  118. package/dist/ui/elements/tabs.works.ts +0 -84
  119. package/dist/ui/elements/text.tsx +0 -33
  120. package/dist/ui/elements/textarea.tsx +0 -38
  121. package/dist/ui/elements/textarea.works.ts +0 -5
  122. package/dist/ui/elements/time.tsx +0 -40
  123. package/dist/ui/elements/time.works.ts +0 -29
  124. package/dist/ui/elements/toggle.tsx +0 -34
  125. package/dist/ui/elements/toggle.works.ts +0 -5
  126. package/dist/ui/elements/tooltip.tsx +0 -40
  127. package/dist/ui/elements/tooltip.works.ts +0 -77
  128. package/dist/ui/elements/value.tsx +0 -38
  129. package/dist/ui/elements/value.works.ts +0 -9
  130. package/dist/ui/elements.ts +0 -127
  131. package/dist/ui/focus-scope.ts +0 -121
  132. package/dist/ui/fonts.ts +0 -355
  133. package/dist/ui/generators/dev.d.ts +0 -1
  134. package/dist/ui/generators/dev.js +0 -62
  135. package/dist/ui/generators/dev.ts +0 -75
  136. package/dist/ui/generators/elements.ts +0 -796
  137. package/dist/ui/generators/i18n.ts +0 -283
  138. package/dist/ui/generators/icons.ts +0 -207
  139. package/dist/ui/generators/react.ts +0 -267
  140. package/dist/ui/generators/routes.ts +0 -847
  141. package/dist/ui/generators/show.ts +0 -261
  142. package/dist/ui/generators/styles.ts +0 -677
  143. package/dist/ui/generators/variants.ts +0 -196
  144. package/dist/ui/i18n/catalogs/en.ts +0 -191
  145. package/dist/ui/i18n/format.ts +0 -221
  146. package/dist/ui/i18n/locales.ts +0 -33
  147. package/dist/ui/i18n/parse.ts +0 -315
  148. package/dist/ui/i18n/translator.ts +0 -31
  149. package/dist/ui/i18n/types.ts +0 -96
  150. package/dist/ui/icons.ts +0 -331
  151. package/dist/ui/islands/map.ts +0 -58
  152. package/dist/ui/localizations.ts +0 -75
  153. package/dist/ui/material.ts +0 -236
  154. package/dist/ui/materials/mat_bordered.ts +0 -13
  155. package/dist/ui/materials/mat_ceramic.ts +0 -13
  156. package/dist/ui/materials/mat_clay.ts +0 -13
  157. package/dist/ui/materials/mat_elevated.ts +0 -24
  158. package/dist/ui/materials/mat_flat.ts +0 -34
  159. package/dist/ui/materials/mat_glass.ts +0 -20
  160. package/dist/ui/materials/mat_lit.ts +0 -13
  161. package/dist/ui/materials/mat_moulded.ts +0 -11
  162. package/dist/ui/materials.ts +0 -197
  163. package/dist/ui/modes.ts +0 -44
  164. package/dist/ui/navigation.tsx +0 -29
  165. package/dist/ui/positioning.ts +0 -90
  166. package/dist/ui/presented.tsx +0 -119
  167. package/dist/ui/provider.tsx +0 -184
  168. package/dist/ui/render-loop.ts +0 -133
  169. package/dist/ui/roving-focus.ts +0 -161
  170. package/dist/ui/screens.ts +0 -141
  171. package/dist/ui/scroll-lock.ts +0 -28
  172. package/dist/ui/series.ts +0 -97
  173. package/dist/ui/signals.ts +0 -386
  174. package/dist/ui/styleguide.ts +0 -231
  175. package/dist/ui/styleguides.ts +0 -374
  176. package/dist/ui/themes.ts +0 -22
  177. package/dist/ui/token.ts +0 -78
  178. package/dist/ui/tokens/shadow.ts +0 -25
  179. package/dist/ui/tokens.ts +0 -234
  180. package/dist/ui/validators.ts +0 -232
  181. package/dist/ui/variant.ts +0 -349
  182. package/dist/ui/variants/var_avatar-round.ts +0 -14
  183. package/dist/ui/variants/var_bubble-me.ts +0 -14
  184. package/dist/ui/variants/var_bubble-them.ts +0 -13
  185. package/dist/ui/variants/var_button-border.ts +0 -76
  186. package/dist/ui/variants/var_button-cta.ts +0 -76
  187. package/dist/ui/variants/var_button-destructive-ghost.ts +0 -72
  188. package/dist/ui/variants/var_button-destructive.ts +0 -76
  189. package/dist/ui/variants/var_button-fill.ts +0 -76
  190. package/dist/ui/variants/var_button-ghost.ts +0 -72
  191. package/dist/ui/variants/var_button-glass.ts +0 -55
  192. package/dist/ui/variants/var_button-normal.ts +0 -76
  193. package/dist/ui/variants/var_button-quiet.ts +0 -74
  194. package/dist/ui/variants/var_card.ts +0 -17
  195. package/dist/ui/variants/var_chip.ts +0 -13
  196. package/dist/ui/variants/var_choice.ts +0 -15
  197. package/dist/ui/variants/var_framed.ts +0 -10
  198. package/dist/ui/variants/var_glyph-plain.ts +0 -11
  199. package/dist/ui/variants/var_input.ts +0 -43
  200. package/dist/ui/variants/var_link-inline.ts +0 -29
  201. package/dist/ui/variants/var_list-row.ts +0 -34
  202. package/dist/ui/variants/var_rule.ts +0 -10
  203. package/dist/ui/variants/var_skeleton-block.ts +0 -12
  204. package/dist/ui/variants/var_spinner-turning.ts +0 -12
  205. package/dist/ui/variants/var_stat-tile.ts +0 -13
  206. package/dist/ui/variants/var_tab-bar.ts +0 -15
  207. package/dist/ui/variants/var_tap-target.ts +0 -25
  208. package/dist/ui/variants.ts +0 -64
  209. package/dist/ui/wrappers.tsx +0 -429
package/dist/ui/icons.ts DELETED
@@ -1,331 +0,0 @@
1
- import type { Threaded } from '@prestack/thread/core/thread'
2
-
3
- export interface IconSet {
4
- readonly name: string
5
- readonly module: string
6
-
7
- readonly size: number
8
-
9
- readonly nativeStroke?: number
10
-
11
- readonly viewBox: number
12
- }
13
-
14
- const SETS: Readonly<Record<string, IconSet>> = {
15
- lucide: { name: 'lucide', module: 'lucide-react', size: 24, nativeStroke: 2, viewBox: 24 },
16
- phosphor: { name: 'phosphor', module: '@phosphor-icons/react', size: 24, nativeStroke: 1.5, viewBox: 256 },
17
- tabler: { name: 'tabler', module: '@tabler/icons-react', size: 24, nativeStroke: 2, viewBox: 24 },
18
- heroicons: { name: 'heroicons', module: '@heroicons/react/24/outline', size: 24, nativeStroke: 1.5, viewBox: 24 },
19
- feather: { name: 'feather', module: 'react-icons/fi', size: 24, nativeStroke: 2, viewBox: 24 },
20
- remix: { name: 'remix', module: 'react-icons/ri', size: 24, viewBox: 24 },
21
- bootstrap: { name: 'bootstrap', module: 'react-icons/bs', size: 16, viewBox: 16 },
22
- material: { name: 'material', module: 'react-icons/md', size: 24, viewBox: 24 },
23
- ionicons: { name: 'ionicons', module: 'react-icons/io5', size: 24, viewBox: 512 },
24
- }
25
-
26
- export const ICON_TARGET = { size: 24, normalizedStroke: 1.75 } as const
27
-
28
- export const DEFAULT_ICON = 'md'
29
-
30
- export function sizeRows(rows: readonly { readonly id: string; readonly is?: string }[]): readonly {
31
- readonly id: string
32
- readonly is: string
33
- }[] {
34
- return rows
35
- .filter((row): row is { id: string; is: string } => row.id !== SET_ROW && typeof row.is === 'string')
36
- .map((row) => ({ id: row.id, is: row.is }))
37
- }
38
-
39
- export function sizeNamed(name: string): string {
40
- return name.startsWith(SIZE_PREFIX) ? name : `${SIZE_PREFIX}${name}`
41
- }
42
-
43
- export function strokeFor(
44
-
45
- set: { readonly viewBox: number; readonly nativeStroke?: number },
46
- target: number = ICON_TARGET.normalizedStroke,
47
- ): number | undefined {
48
- if (set.nativeStroke === undefined) return undefined
49
- return Number(((target * set.viewBox) / ICON_TARGET.size).toFixed(3))
50
- }
51
-
52
- export const BRAND_MODULE = 'simple-icons'
53
-
54
- const DRAWN: Readonly<Record<string, Readonly<Record<string, string>>>> = {
55
-
56
- bell: { lucide: 'Bell' },
57
- star: { lucide: 'Star' },
58
- heart: { lucide: 'Heart' },
59
- share: { lucide: 'Share2' },
60
- bookmark: { lucide: 'Bookmark' },
61
- send: { lucide: 'Send' },
62
- attach: { lucide: 'Paperclip' },
63
- play: { lucide: 'Play' },
64
- pause: { lucide: 'Pause' },
65
- chevronLeft: { lucide: 'ChevronLeft' },
66
- chevronUp: { lucide: 'ChevronUp' },
67
- arrowUp: { lucide: 'ArrowUp' },
68
- arrowDown: { lucide: 'ArrowDown' },
69
- list: { lucide: 'List' },
70
- signOut: { lucide: 'LogOut' },
71
- signIn: { lucide: 'LogIn' },
72
- secure: { lucide: 'ShieldCheck' },
73
- card: { lucide: 'CreditCard' },
74
- place: { lucide: 'MapPin' },
75
- phone: { lucide: 'Phone' },
76
- globe: { lucide: 'Globe' },
77
- unlock: { lucide: 'Unlock' },
78
- warning: { lucide: 'CircleAlert' },
79
- success: { lucide: 'CircleCheck' },
80
- failure: { lucide: 'CircleX' },
81
- help: { lucide: 'CircleHelp' },
82
- save: { lucide: 'Save' },
83
- undo: { lucide: 'Undo2' },
84
- redo: { lucide: 'Redo2' },
85
- zoomIn: { lucide: 'ZoomIn' },
86
- zoomOut: { lucide: 'ZoomOut' },
87
- folder: { lucide: 'Folder' },
88
- document: { lucide: 'FileText' },
89
- people: { lucide: 'Users' },
90
- addPerson: { lucide: 'UserPlus' },
91
- tag: { lucide: 'Tag' },
92
- flag: { lucide: 'Flag' },
93
- archive: { lucide: 'Archive' },
94
- inbox: { lucide: 'Inbox' },
95
- light: { lucide: 'Sun' },
96
- dark: { lucide: 'Moon' },
97
- sound: { lucide: 'Volume2' },
98
- mute: { lucide: 'VolumeX' },
99
- microphone: { lucide: 'Mic' },
100
- camera: { lucide: 'Camera' },
101
-
102
- close: { lucide: 'X', phosphor: 'X', tabler: 'IconX', heroicons: 'XMarkIcon', feather: 'FiX', remix: 'RiCloseLine', bootstrap: 'BsX', material: 'MdClose', ionicons: 'IoClose' },
103
- search: { lucide: 'Search', phosphor: 'MagnifyingGlass', tabler: 'IconSearch', heroicons: 'MagnifyingGlassIcon', feather: 'FiSearch', remix: 'RiSearchLine', bootstrap: 'BsSearch', material: 'MdSearch', ionicons: 'IoSearch' },
104
- check: { lucide: 'Check', phosphor: 'Check', tabler: 'IconCheck', heroicons: 'CheckIcon', feather: 'FiCheck', remix: 'RiCheckLine', bootstrap: 'BsCheck', material: 'MdCheck', ionicons: 'IoCheckmark' },
105
- chevronDown: { lucide: 'ChevronDown', phosphor: 'CaretDown', tabler: 'IconChevronDown', heroicons: 'ChevronDownIcon', feather: 'FiChevronDown', remix: 'RiArrowDownSLine', bootstrap: 'BsChevronDown', material: 'MdKeyboardArrowDown', ionicons: 'IoChevronDown' },
106
- chevronRight: { lucide: 'ChevronRight', phosphor: 'CaretRight', tabler: 'IconChevronRight', heroicons: 'ChevronRightIcon', feather: 'FiChevronRight', remix: 'RiArrowRightSLine', bootstrap: 'BsChevronRight', material: 'MdKeyboardArrowRight', ionicons: 'IoChevronForward' },
107
- plus: { lucide: 'Plus', phosphor: 'Plus', tabler: 'IconPlus', heroicons: 'PlusIcon', feather: 'FiPlus', remix: 'RiAddLine', bootstrap: 'BsPlus', material: 'MdAdd', ionicons: 'IoAdd' },
108
- minus: { lucide: 'Minus', phosphor: 'Minus', tabler: 'IconMinus', heroicons: 'MinusIcon', feather: 'FiMinus', remix: 'RiSubtractLine', bootstrap: 'BsDash', material: 'MdRemove', ionicons: 'IoRemove' },
109
- menu: { lucide: 'Menu', phosphor: 'List', tabler: 'IconMenu2', heroicons: 'Bars3Icon', feather: 'FiMenu', remix: 'RiMenuLine', bootstrap: 'BsList', material: 'MdMenu', ionicons: 'IoMenu' },
110
- more: { lucide: 'MoreHorizontal', phosphor: 'DotsThree', tabler: 'IconDots', heroicons: 'EllipsisHorizontalIcon', feather: 'FiMoreHorizontal', remix: 'RiMoreLine', bootstrap: 'BsThreeDots', material: 'MdMoreHoriz', ionicons: 'IoEllipsisHorizontal' },
111
- user: { lucide: 'User', phosphor: 'User', tabler: 'IconUser', heroicons: 'UserIcon', feather: 'FiUser', remix: 'RiUserLine', bootstrap: 'BsPerson', material: 'MdPerson', ionicons: 'IoPerson' },
112
- settings: { lucide: 'Settings', phosphor: 'Gear', tabler: 'IconSettings', heroicons: 'Cog6ToothIcon', feather: 'FiSettings', remix: 'RiSettingsLine', bootstrap: 'BsGear', material: 'MdSettings', ionicons: 'IoSettings' },
113
- trash: { lucide: 'Trash2', phosphor: 'Trash', tabler: 'IconTrash', heroicons: 'TrashIcon', feather: 'FiTrash2', remix: 'RiDeleteBinLine', bootstrap: 'BsTrash', material: 'MdDelete', ionicons: 'IoTrash' },
114
- edit: { lucide: 'Pencil', phosphor: 'PencilSimple', tabler: 'IconPencil', heroicons: 'PencilIcon', feather: 'FiEdit2', remix: 'RiPencilLine', bootstrap: 'BsPencil', material: 'MdEdit', ionicons: 'IoPencil' },
115
- calendar: { lucide: 'Calendar', phosphor: 'Calendar', tabler: 'IconCalendar', heroicons: 'CalendarIcon', feather: 'FiCalendar', remix: 'RiCalendarLine', bootstrap: 'BsCalendar', material: 'MdCalendarToday', ionicons: 'IoCalendar' },
116
- clock: { lucide: 'Clock', phosphor: 'Clock', tabler: 'IconClock', heroicons: 'ClockIcon', feather: 'FiClock', remix: 'RiTimeLine', bootstrap: 'BsClock', material: 'MdAccessTime', ionicons: 'IoTime' },
117
- eye: { lucide: 'Eye', phosphor: 'Eye', tabler: 'IconEye', heroicons: 'EyeIcon', feather: 'FiEye', remix: 'RiEyeLine', bootstrap: 'BsEye', material: 'MdVisibility', ionicons: 'IoEye' },
118
- eyeOff: { lucide: 'EyeOff', phosphor: 'EyeSlash', tabler: 'IconEyeOff', heroicons: 'EyeSlashIcon', feather: 'FiEyeOff', remix: 'RiEyeOffLine', bootstrap: 'BsEyeSlash', material: 'MdVisibilityOff', ionicons: 'IoEyeOff' },
119
- alert: { lucide: 'TriangleAlert', phosphor: 'Warning', tabler: 'IconAlertTriangle', heroicons: 'ExclamationTriangleIcon', feather: 'FiAlertTriangle', remix: 'RiErrorWarningLine', bootstrap: 'BsExclamationTriangle', material: 'MdWarning', ionicons: 'IoWarning' },
120
- info: { lucide: 'Info', phosphor: 'Info', tabler: 'IconInfoCircle', heroicons: 'InformationCircleIcon', feather: 'FiInfo', remix: 'RiInformationLine', bootstrap: 'BsInfoCircle', material: 'MdInfo', ionicons: 'IoInformationCircle' },
121
- externalLink: { lucide: 'ExternalLink', phosphor: 'ArrowSquareOut', tabler: 'IconExternalLink', heroicons: 'ArrowTopRightOnSquareIcon', feather: 'FiExternalLink', remix: 'RiExternalLinkLine', bootstrap: 'BsBoxArrowUpRight', material: 'MdOpenInNew', ionicons: 'IoOpenOutline' },
122
- copy: { lucide: 'Copy', phosphor: 'Copy', tabler: 'IconCopy', heroicons: 'DocumentDuplicateIcon', feather: 'FiCopy', remix: 'RiFileCopyLine', bootstrap: 'BsClipboard', material: 'MdContentCopy', ionicons: 'IoCopy' },
123
- download: { lucide: 'Download', phosphor: 'DownloadSimple', tabler: 'IconDownload', heroicons: 'ArrowDownTrayIcon', feather: 'FiDownload', remix: 'RiDownloadLine', bootstrap: 'BsDownload', material: 'MdDownload', ionicons: 'IoDownload' },
124
- upload: { lucide: 'Upload', phosphor: 'UploadSimple', tabler: 'IconUpload', heroicons: 'ArrowUpTrayIcon', feather: 'FiUpload', remix: 'RiUploadLine', bootstrap: 'BsUpload', material: 'MdUpload', ionicons: 'IoCloudUpload' },
125
- filter: { lucide: 'Filter', phosphor: 'Funnel', tabler: 'IconFilter', heroicons: 'FunnelIcon', feather: 'FiFilter', remix: 'RiFilterLine', bootstrap: 'BsFunnel', material: 'MdFilterList', ionicons: 'IoFilter' },
126
- home: { lucide: 'House', phosphor: 'House', tabler: 'IconHome', heroicons: 'HomeIcon', feather: 'FiHome', remix: 'RiHomeLine', bootstrap: 'BsHouse', material: 'MdHome', ionicons: 'IoHome' },
127
- mail: { lucide: 'Mail', phosphor: 'Envelope', tabler: 'IconMail', heroicons: 'EnvelopeIcon', feather: 'FiMail', remix: 'RiMailLine', bootstrap: 'BsEnvelope', material: 'MdMail', ionicons: 'IoMail' },
128
- lock: { lucide: 'Lock', phosphor: 'Lock', tabler: 'IconLock', heroicons: 'LockClosedIcon', feather: 'FiLock', remix: 'RiLockLine', bootstrap: 'BsLock', material: 'MdLock', ionicons: 'IoLockClosed' },
129
- refresh: { lucide: 'RefreshCw', phosphor: 'ArrowsClockwise', tabler: 'IconRefresh', heroicons: 'ArrowPathIcon', feather: 'FiRefreshCw', remix: 'RiRefreshLine', bootstrap: 'BsArrowRepeat', material: 'MdRefresh', ionicons: 'IoRefresh' },
130
- arrowLeft: { lucide: 'ArrowLeft', phosphor: 'ArrowLeft', tabler: 'IconArrowLeft', heroicons: 'ArrowLeftIcon', feather: 'FiArrowLeft', remix: 'RiArrowLeftLine', bootstrap: 'BsArrowLeft', material: 'MdArrowBack', ionicons: 'IoArrowBack' },
131
- arrowRight: { lucide: 'ArrowRight', phosphor: 'ArrowRight', tabler: 'IconArrowRight', heroicons: 'ArrowRightIcon', feather: 'FiArrowRight', remix: 'RiArrowRightLine', bootstrap: 'BsArrowRight', material: 'MdArrowForward', ionicons: 'IoArrowForward' },
132
- }
133
-
134
- const REGISTRY = new Map<string, Readonly<Record<string, string>>>(Object.entries(DRAWN))
135
- const SET_REGISTRY = new Map<string, IconSet>(Object.entries(SETS))
136
-
137
- export function defineIconSet(set: IconSet): void {
138
- if (SET_REGISTRY.has(set.name)) throw new Error(`icon set "${set.name}" is already registered`)
139
- SET_REGISTRY.set(set.name, set)
140
- }
141
-
142
- export function defineIcon(semantic: string, names: Readonly<Record<string, string>>): void {
143
- REGISTRY.set(semantic, { ...REGISTRY.get(semantic), ...names })
144
- }
145
-
146
- export function iconSets(): readonly IconSet[] {
147
- return [...SET_REGISTRY.values()].sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0))
148
- }
149
-
150
- export function iconSet(name: string): IconSet | undefined {
151
- return SET_REGISTRY.get(name)
152
- }
153
-
154
- export function iconNames(): readonly string[] {
155
- return [...REGISTRY.keys()].sort()
156
- }
157
-
158
- export function iconIn(set: string, semantic: string): string | undefined {
159
- return REGISTRY.get(semantic)?.[set]
160
- }
161
-
162
- export function brandExport(brand: string): string {
163
- return `si${brand.charAt(0).toUpperCase()}${brand.slice(1)}`
164
- }
165
-
166
- type Row = Omit<Threaded, 'at'> & {
167
- readonly section?: string
168
- readonly is?: string
169
- readonly sets?: Readonly<Record<string, string>>
170
- }
171
-
172
- const GROUPED: Readonly<Record<string, readonly string[]>> = {
173
- navigation: ['chevronUp', 'chevronDown', 'chevronLeft', 'chevronRight', 'arrowUp', 'arrowDown', 'arrowLeft', 'arrowRight', 'home', 'menu', 'more', 'externalLink', 'list'],
174
- action: ['close', 'check', 'plus', 'minus', 'search', 'filter', 'edit', 'trash', 'save', 'undo', 'redo', 'copy', 'download', 'upload', 'share', 'send', 'attach', 'refresh', 'archive', 'zoomIn', 'zoomOut'],
175
- status: ['alert', 'info', 'warning', 'success', 'failure', 'help', 'bell', 'flag', 'tag'],
176
- account: ['user', 'people', 'addPerson', 'signIn', 'signOut', 'settings', 'lock', 'unlock', 'secure', 'eye', 'eyeOff'],
177
- content: ['document', 'folder', 'inbox', 'mail', 'calendar', 'clock', 'bookmark', 'star', 'heart', 'card', 'place', 'phone', 'globe'],
178
- media: ['play', 'pause', 'sound', 'mute', 'microphone', 'camera', 'light', 'dark'],
179
- }
180
-
181
- const sectionOf = (id: string): string => {
182
- for (const [name, held] of Object.entries(GROUPED)) if (held.includes(id)) return name
183
- return 'other'
184
- }
185
-
186
- export const SET_ROW = 'library'
187
-
188
- export const SIZE_PREFIX = 'icon-'
189
-
190
- export const holds: readonly Row[] = [
191
- {
192
- id: SET_ROW,
193
- section: 'the library',
194
- is: 'lucide',
195
- describes:
196
- 'Which library every icon is drawn from. One name, because a set answers for the whole app — swapping it redraws every icon at once, and an icon a set does not carry is refused rather than quietly missing.',
197
- version: 1,
198
- },
199
- {
200
- id: `${SIZE_PREFIX}sm`,
201
- section: 'size',
202
- is: '16px',
203
- describes:
204
- 'A symbol drawn inside a line of text, at the size that sits on the same baseline as the words around it.',
205
- version: 1,
206
- },
207
- {
208
- id: `${SIZE_PREFIX}md`,
209
- section: 'size',
210
- is: '24px',
211
- describes:
212
- 'A symbol beside wording, at the size most controls use. What anything asking for an icon is drawn at unless it says otherwise.',
213
- version: 1,
214
- },
215
- {
216
- id: `${SIZE_PREFIX}lg`,
217
- section: 'size',
218
- is: '32px',
219
- describes:
220
- 'A symbol standing on its own, where the drawing is the content rather than a marker beside it.',
221
- version: 1,
222
- },
223
-
224
- ...Object.keys(DRAWN)
225
- .sort((one, two) => {
226
- const group = sectionOf(one) < sectionOf(two) ? -1 : sectionOf(one) > sectionOf(two) ? 1 : 0
227
- return group !== 0 ? group : one < two ? -1 : one > two ? 1 : 0
228
- })
229
- .map((semantic): Row => ({
230
- id: semantic,
231
- section: sectionOf(semantic),
232
- sets: DRAWN[semantic] ?? {},
233
- describes: `The \`${semantic}\` icon, and what it is called in each library — so wording asks for the meaning and the set decides the drawing.`,
234
- version: 1,
235
- })),
236
- ]
237
-
238
- export const ICONS = {
239
- id: 'icon',
240
- at: 'thread/ui/icons.ts',
241
- describes:
242
- 'Every icon an app may draw and which library it is drawn from. A row is a meaning — close, search, the chevron that opens a thing — and holds what that meaning is called in each library, so an element asks for the meaning and never for a library’s own name for it. One row says which library answers, and changing it redraws the whole app. An icon nothing declares is refused rather than rendered as a gap, and an app adds its own rows to this same list.',
243
- version: 1,
244
- cites: [
245
- {
246
- on: '2026-08-28',
247
- said: 'Same for icons. We define icons available in icons.ts AND we make UI read from there and icons.ts pick what icon from what library.',
248
- },
249
- ],
250
- holds,
251
- } satisfies Threaded & { readonly holds: readonly Row[] }
252
-
253
- export function drawnAs(set: string, semantic: string, by: string): string {
254
- const held = REGISTRY.get(semantic)
255
- if (held === undefined) {
256
- const has = [...REGISTRY.keys()].sort().map((one) => `\`${one}\``)
257
- throw new Error(
258
- `\`${by}\` asks for the \`${semantic}\` icon, and no row declares one by that name. ` +
259
- `The declared icons are ${has.join(', ')}. Add a row for it, or name one that exists.`,
260
- )
261
- }
262
- const drawn = held[set]
263
- if (drawn !== undefined) return drawn
264
- const has = Object.keys(held).sort().map((one) => `\`${one}\``)
265
- throw new Error(
266
- `\`${by}\` asks for the \`${semantic}\` icon and the app draws from \`${set}\`, which does not carry it. ` +
267
- `It is carried by ${has.join(', ')}. Add \`${set}\` to that row, or draw from a library that has it.`,
268
- )
269
- }
270
-
271
- export function setNamed(name: string): IconSet {
272
- const found = SET_REGISTRY.get(name)
273
- if (found !== undefined) return found
274
- const has = [...SET_REGISTRY.keys()].sort().map((one) => `\`${one}\``)
275
- throw new Error(
276
- `\`${name}\` is named as the icon library, and no set by that name is registered. ` +
277
- `The registered libraries are ${has.join(', ')}.`,
278
- )
279
- }
280
-
281
- export type IconComponent = (props: {
282
- readonly size?: number
283
- readonly strokeWidth?: number
284
- readonly className?: string
285
- readonly 'aria-hidden'?: boolean
286
- readonly 'aria-label'?: string
287
- readonly role?: string
288
- }) => unknown
289
-
290
- let installed:
291
- | {
292
- readonly set: IconSet
293
- readonly drawn: Readonly<Record<string, IconComponent>>
294
- readonly sizes?: Readonly<Record<string, string>>
295
- }
296
- | undefined
297
-
298
- export function installIcons(
299
- set: IconSet,
300
- drawn: Readonly<Record<string, IconComponent>>,
301
- sizes?: Readonly<Record<string, string>>,
302
- ): void {
303
- installed = { set, drawn, ...(sizes === undefined ? {} : { sizes }) }
304
- }
305
-
306
- export function installedSet(): IconSet | undefined {
307
- return installed?.set
308
- }
309
-
310
- export function iconSize(name: string = DEFAULT_ICON): number {
311
- const held = installed?.sizes?.[sizeNamed(name)]
312
- if (held === undefined) return ICON_TARGET.size
313
- const measured = Number.parseFloat(held)
314
- return Number.isFinite(measured) ? measured : ICON_TARGET.size
315
- }
316
-
317
- const NOT_INSTALLED =
318
- 'No icons are installed. The library an app draws from and the symbols it draws are generated ' +
319
- 'from the icon rows — run `thread icons` and import what it writes, beside the stylesheet. ' +
320
- 'Without it every icon renders as an empty box, which looks like a missing glyph and is a wiring gap.'
321
-
322
- export function drawnFor(semantic: string): IconComponent {
323
- if (installed === undefined) throw new Error(NOT_INSTALLED)
324
- const component = installed.drawn[semantic]
325
- if (component !== undefined) return component
326
- const has = Object.keys(installed.drawn).sort().map((one) => `\`${one}\``)
327
- throw new Error(
328
- `\`${semantic}\` is asked for as an icon, and no row declares one by that name. ` +
329
- `The declared icons are ${has.join(', ')}. Add a row for it, or name one that exists.`,
330
- )
331
- }
@@ -1,58 +0,0 @@
1
- import { z } from 'zod'
2
-
3
- const ElementName = z
4
- .string()
5
- .regex(/^[A-Z][A-Za-z0-9]*$/, 'must be a component name, e.g. "Card"')
6
- .describe('The export name. A screen is reached by importing it, so this is its address.')
7
-
8
- export const ViewId = z
9
- .string()
10
- .regex(/^vw_[a-z][a-z0-9]*(?:_[a-z0-9]+)*$/, 'Expected a view id ("vw_" + snake_case)')
11
- .describe('The id of a declared read. A screen names one; it never states what the read does.')
12
-
13
- export const ScreenName = ElementName.describe(
14
- 'The screen’s export name — `Home`, `Pricing`, `SignIn`. What `navigateTo` names, and its whole address.',
15
- )
16
-
17
- export const ScreenSchema = z
18
- .strictObject({
19
- id: ScreenName,
20
- nameKey: z
21
- .string()
22
- .min(1)
23
- .describe('The i18n key naming this screen — in a breadcrumb, a tab, a title. Never prose (rule: every user-facing string is a key).'),
24
-
25
- path: z
26
- .string()
27
- .regex(/^\/(?:[a-z0-9-]+|\[[a-z][a-zA-Z0-9]*\])(?:\/(?:[a-z0-9-]+|\[[a-z][a-zA-Z0-9]*\]))*$|^\/$/, 'must be an App Router path, e.g. "/campaign/[id]"')
28
- .optional()
29
- .describe('Where this screen lives. `/`, `/settings`, `/campaign/[id]`. Absent for a sheet, popover, overlay or toast, which a press opens where somebody already is, so it has no address of its own.'),
30
-
31
- entry: z.boolean().default(false).describe('True when somebody can arrive here without following a link from inside the app.'),
32
-
33
- shows: z.array(ViewId).default([]).describe('Declared views this screen binds to. A screen binds; it never fetches.'),
34
- })
35
- .describe('One screen: where it lives, what it rests on, and whether somebody can arrive at it directly.')
36
- export type Screen = z.infer<typeof ScreenSchema>
37
-
38
- export const LinkSchema = z
39
- .strictObject({
40
- from: ScreenName.describe('The screen somebody is on.'),
41
- to: ScreenName.describe('The screen they arrive at.'),
42
- labelKey: z
43
- .string()
44
- .min(1)
45
- .optional()
46
- .describe('The i18n key of the button or link somebody presses to get there. Read off the press itself, and absent only when the press carries no wording.'),
47
- })
48
- .describe('One edge: somebody on `from` can reach `to`.')
49
- export type Link = z.infer<typeof LinkSchema>
50
-
51
- export const MapSchema = z
52
- .strictObject({
53
- specVersion: z.literal(1),
54
- screens: z.array(ScreenSchema).default([]),
55
- links: z.array(LinkSchema).default([]),
56
- })
57
- .describe('The map: the screens an app has and how somebody moves between them.')
58
- export type MapIsland = z.infer<typeof MapSchema>
@@ -1,75 +0,0 @@
1
- import type { Threaded } from '@prestack/thread/core/thread'
2
-
3
- type Row = Omit<Threaded, 'at'> & {
4
- readonly section?: string
5
- readonly is?: string
6
- readonly sets?: Readonly<Record<string, string>>
7
- }
8
-
9
- export function fileFor(language: string): string {
10
- return `i18n/${language}.json`
11
- }
12
-
13
- export const SOURCE_ROW = 'source'
14
-
15
- export const holds: readonly Row[] = [
16
- {
17
- id: SOURCE_ROW,
18
- section: 'the source',
19
- is: 'en',
20
- describes:
21
- 'Which language this app is written in. One name, and it has to be one of the languages declared below — everything else is a translation of it, and the wording commands ask for those and never for this one twice.',
22
- version: 1,
23
- },
24
- ]
25
-
26
- export const declares: readonly (Row & { readonly at: string })[] = [
27
- {
28
- id: 'en',
29
- at: fileFor('en'),
30
- section: 'languages',
31
- is: 'English',
32
- sets: { flag: '🇬🇧' },
33
- describes:
34
- 'English, and everything this app says in it. Named in its own language, because the list this feeds is read by somebody who cannot yet read the language the app is currently in.',
35
- version: 1,
36
- },
37
- ]
38
-
39
- export const LOCALIZATIONS: Threaded & {
40
- readonly files: string
41
- readonly holds: readonly Row[]
42
- readonly declares: readonly (Row & { readonly at: string })[]
43
- } = {
44
- id: 'localization',
45
- at: 'thread/ui/localizations.ts',
46
- files: fileFor('*'),
47
- describes:
48
- 'Every language an app is in, and which one it is written in. A row is a language tag, the language’s name in itself and a flag to show beside it, so a settings screen offering a choice is built from declarations rather than from a list somebody keeps in a component. Adding a row is what brings a wording file for that language into existence, and every key the interface names has to be answered in every language declared here — an unanswered one is a build error rather than a word that silently comes out in the wrong language.',
49
- version: 1,
50
- cites: [
51
- {
52
- on: '2026-08-29',
53
- said: 'i18n ships by default, nothing hardcoded in the UI at all. i18n is REQUIRED. We define the languages the app will have, this generates automatically the i18n files so that we can add the stuff there.',
54
- },
55
- {
56
- on: '2026-08-29',
57
- said: 'This means that we need a new Localization.ts file under UI, where we declare the languages the app is in, must be threaded on attach. This might maybe include an emoji for the country flag? And name of the language so we can reuse this in settings later',
58
- },
59
- ],
60
- holds,
61
- declares,
62
- }
63
-
64
- export function languagesIn(
65
- rows: readonly { readonly id: string; readonly is?: string; readonly sets?: Readonly<Record<string, string>> }[],
66
- ): readonly { readonly id: string; readonly name: string; readonly flag: string }[] {
67
- return rows
68
- .filter((row) => row.id !== SOURCE_ROW)
69
- .map((row) => ({ id: row.id, name: row.is ?? row.id, flag: row.sets?.flag ?? '' }))
70
- }
71
-
72
- export function sourceIn(rows: readonly { readonly id: string; readonly is?: string }[]): string | undefined {
73
- return rows.find((row) => row.id === SOURCE_ROW)?.is
74
- }
75
-