@agent-ui-kit/components 0.0.3

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 (271) hide show
  1. package/dist/base-styles.css +8 -0
  2. package/dist/component-styles.css +147 -0
  3. package/dist/controls/_base/index.d.ts +3 -0
  4. package/dist/controls/_base/index.js +9 -0
  5. package/dist/controls/_base/indicator-element.d.ts +132 -0
  6. package/dist/controls/_base/indicator-element.js +158 -0
  7. package/dist/controls/_base/listbox-element.d.ts +70 -0
  8. package/dist/controls/_base/listbox-element.js +74 -0
  9. package/dist/controls/_base/range-element.d.ts +120 -0
  10. package/dist/controls/_base/range-element.js +158 -0
  11. package/dist/controls/_surface/container-box.css +301 -0
  12. package/dist/controls/_surface/container.css +156 -0
  13. package/dist/controls/_token-surface/token-surface.d.ts +35 -0
  14. package/dist/controls/_token-surface/token-surface.js +97 -0
  15. package/dist/controls/attachment/attachment-meta.d.ts +8 -0
  16. package/dist/controls/attachment/attachment-meta.js +84 -0
  17. package/dist/controls/attachment/attachment.css +91 -0
  18. package/dist/controls/attachment/attachment.d.ts +40 -0
  19. package/dist/controls/attachment/attachment.js +70 -0
  20. package/dist/controls/avatar/avatar-initials.d.ts +4 -0
  21. package/dist/controls/avatar/avatar-initials.js +30 -0
  22. package/dist/controls/avatar/avatar.css +87 -0
  23. package/dist/controls/avatar/avatar.d.ts +40 -0
  24. package/dist/controls/avatar/avatar.js +77 -0
  25. package/dist/controls/badge/badge.css +175 -0
  26. package/dist/controls/badge/badge.d.ts +36 -0
  27. package/dist/controls/badge/badge.js +71 -0
  28. package/dist/controls/bar-chart/bar-chart.css +110 -0
  29. package/dist/controls/bar-chart/bar-chart.d.ts +26 -0
  30. package/dist/controls/bar-chart/bar-chart.js +80 -0
  31. package/dist/controls/bar-chart/bar-math.d.ts +31 -0
  32. package/dist/controls/bar-chart/bar-math.js +106 -0
  33. package/dist/controls/button/button.css +263 -0
  34. package/dist/controls/button/button.d.ts +61 -0
  35. package/dist/controls/button/button.js +115 -0
  36. package/dist/controls/calendar/calendar.css +495 -0
  37. package/dist/controls/calendar/calendar.d.ts +161 -0
  38. package/dist/controls/calendar/calendar.js +1004 -0
  39. package/dist/controls/card/card-content.d.ts +24 -0
  40. package/dist/controls/card/card-content.js +133 -0
  41. package/dist/controls/card/card-footer.d.ts +3 -0
  42. package/dist/controls/card/card-footer.js +16 -0
  43. package/dist/controls/card/card-header.d.ts +3 -0
  44. package/dist/controls/card/card-header.js +18 -0
  45. package/dist/controls/card/card.css +445 -0
  46. package/dist/controls/card/card.d.ts +51 -0
  47. package/dist/controls/card/card.js +61 -0
  48. package/dist/controls/checkbox/checkbox.css +204 -0
  49. package/dist/controls/checkbox/checkbox.d.ts +114 -0
  50. package/dist/controls/checkbox/checkbox.js +130 -0
  51. package/dist/controls/code/code.css +72 -0
  52. package/dist/controls/code/code.d.ts +23 -0
  53. package/dist/controls/code/code.js +40 -0
  54. package/dist/controls/color-picker/color-picker.css +209 -0
  55. package/dist/controls/color-picker/color-picker.d.ts +84 -0
  56. package/dist/controls/color-picker/color-picker.js +545 -0
  57. package/dist/controls/color-picker/color.d.ts +40 -0
  58. package/dist/controls/color-picker/color.js +159 -0
  59. package/dist/controls/column/column.css +151 -0
  60. package/dist/controls/column/column.d.ts +107 -0
  61. package/dist/controls/column/column.js +57 -0
  62. package/dist/controls/combo-box/combo-box.css +397 -0
  63. package/dist/controls/combo-box/combo-box.d.ts +133 -0
  64. package/dist/controls/combo-box/combo-box.js +680 -0
  65. package/dist/controls/command-modal/command-modal.css +226 -0
  66. package/dist/controls/command-modal/command-modal.d.ts +42 -0
  67. package/dist/controls/command-modal/command-modal.js +315 -0
  68. package/dist/controls/disclosure/disclosure.css +162 -0
  69. package/dist/controls/disclosure/disclosure.d.ts +37 -0
  70. package/dist/controls/disclosure/disclosure.js +207 -0
  71. package/dist/controls/field/field.css +140 -0
  72. package/dist/controls/field/field.d.ts +27 -0
  73. package/dist/controls/field/field.js +228 -0
  74. package/dist/controls/form-provider/form-provider.css +40 -0
  75. package/dist/controls/form-provider/form-provider.d.ts +42 -0
  76. package/dist/controls/form-provider/form-provider.js +105 -0
  77. package/dist/controls/grid/grid.css +72 -0
  78. package/dist/controls/grid/grid.d.ts +60 -0
  79. package/dist/controls/grid/grid.js +56 -0
  80. package/dist/controls/icon/icon.css +37 -0
  81. package/dist/controls/icon/icon.d.ts +25 -0
  82. package/dist/controls/icon/icon.js +55 -0
  83. package/dist/controls/index.d.ts +57 -0
  84. package/dist/controls/index.js +136 -0
  85. package/dist/controls/ladder/ladder.css +89 -0
  86. package/dist/controls/ladder/ladder.d.ts +27 -0
  87. package/dist/controls/ladder/ladder.js +78 -0
  88. package/dist/controls/list/list.css +118 -0
  89. package/dist/controls/list/list.d.ts +84 -0
  90. package/dist/controls/list/list.js +48 -0
  91. package/dist/controls/menu/menu.css +195 -0
  92. package/dist/controls/menu/menu.d.ts +44 -0
  93. package/dist/controls/menu/menu.js +248 -0
  94. package/dist/controls/modal/modal.css +99 -0
  95. package/dist/controls/modal/modal.d.ts +61 -0
  96. package/dist/controls/modal/modal.js +155 -0
  97. package/dist/controls/popover/popover.css +83 -0
  98. package/dist/controls/popover/popover.d.ts +44 -0
  99. package/dist/controls/popover/popover.js +164 -0
  100. package/dist/controls/progress/progress.css +106 -0
  101. package/dist/controls/progress/progress.d.ts +28 -0
  102. package/dist/controls/progress/progress.js +80 -0
  103. package/dist/controls/radio/radio-group.css +84 -0
  104. package/dist/controls/radio/radio-group.d.ts +152 -0
  105. package/dist/controls/radio/radio-group.js +350 -0
  106. package/dist/controls/radio/radio.css +219 -0
  107. package/dist/controls/radio/radio.d.ts +19 -0
  108. package/dist/controls/radio/radio.js +75 -0
  109. package/dist/controls/ramp/ramp.css +75 -0
  110. package/dist/controls/ramp/ramp.d.ts +29 -0
  111. package/dist/controls/ramp/ramp.js +72 -0
  112. package/dist/controls/row/row.css +152 -0
  113. package/dist/controls/row/row.d.ts +95 -0
  114. package/dist/controls/row/row.js +45 -0
  115. package/dist/controls/segment/segment.css +66 -0
  116. package/dist/controls/segment/segment.d.ts +3 -0
  117. package/dist/controls/segment/segment.js +26 -0
  118. package/dist/controls/segmented-control/segmented-control.css +189 -0
  119. package/dist/controls/segmented-control/segmented-control.d.ts +20 -0
  120. package/dist/controls/segmented-control/segmented-control.js +64 -0
  121. package/dist/controls/select/select.css +467 -0
  122. package/dist/controls/select/select.d.ts +147 -0
  123. package/dist/controls/select/select.js +538 -0
  124. package/dist/controls/slider/slider.css +231 -0
  125. package/dist/controls/slider/slider.d.ts +111 -0
  126. package/dist/controls/slider/slider.js +60 -0
  127. package/dist/controls/slider-multi/slider-multi.css +197 -0
  128. package/dist/controls/slider-multi/slider-multi.d.ts +149 -0
  129. package/dist/controls/slider-multi/slider-multi.js +380 -0
  130. package/dist/controls/sparkline/sparkline-math.d.ts +23 -0
  131. package/dist/controls/sparkline/sparkline-math.js +79 -0
  132. package/dist/controls/sparkline/sparkline.css +61 -0
  133. package/dist/controls/sparkline/sparkline.d.ts +27 -0
  134. package/dist/controls/sparkline/sparkline.js +74 -0
  135. package/dist/controls/split/constrain.d.ts +44 -0
  136. package/dist/controls/split/constrain.js +126 -0
  137. package/dist/controls/split/split-pane.css +63 -0
  138. package/dist/controls/split/split-pane.d.ts +49 -0
  139. package/dist/controls/split/split-pane.js +42 -0
  140. package/dist/controls/split/split.css +127 -0
  141. package/dist/controls/split/split.d.ts +42 -0
  142. package/dist/controls/split/split.js +364 -0
  143. package/dist/controls/stat/stat-model.d.ts +22 -0
  144. package/dist/controls/stat/stat-model.js +69 -0
  145. package/dist/controls/stat/stat.css +101 -0
  146. package/dist/controls/stat/stat.d.ts +30 -0
  147. package/dist/controls/stat/stat.js +73 -0
  148. package/dist/controls/status-stream/status-stream.css +151 -0
  149. package/dist/controls/status-stream/status-stream.d.ts +108 -0
  150. package/dist/controls/status-stream/status-stream.js +417 -0
  151. package/dist/controls/swatch/swatch.css +66 -0
  152. package/dist/controls/swatch/swatch.d.ts +28 -0
  153. package/dist/controls/swatch/swatch.js +74 -0
  154. package/dist/controls/swiper/swiper-item.css +17 -0
  155. package/dist/controls/swiper/swiper-item.d.ts +31 -0
  156. package/dist/controls/swiper/swiper-item.js +47 -0
  157. package/dist/controls/swiper/swiper-label.css +10 -0
  158. package/dist/controls/swiper/swiper-label.d.ts +4 -0
  159. package/dist/controls/swiper/swiper-label.js +20 -0
  160. package/dist/controls/swiper/swiper-paddles.css +26 -0
  161. package/dist/controls/swiper/swiper-paddles.d.ts +10 -0
  162. package/dist/controls/swiper/swiper-paddles.js +62 -0
  163. package/dist/controls/swiper/swiper-pagination.css +60 -0
  164. package/dist/controls/swiper/swiper-pagination.d.ts +31 -0
  165. package/dist/controls/swiper/swiper-pagination.js +78 -0
  166. package/dist/controls/swiper/swiper.css +153 -0
  167. package/dist/controls/swiper/swiper.d.ts +158 -0
  168. package/dist/controls/swiper/swiper.js +659 -0
  169. package/dist/controls/switch/switch.css +194 -0
  170. package/dist/controls/switch/switch.d.ts +5 -0
  171. package/dist/controls/switch/switch.js +18 -0
  172. package/dist/controls/table/table-model.d.ts +43 -0
  173. package/dist/controls/table/table-model.js +124 -0
  174. package/dist/controls/table/table.css +90 -0
  175. package/dist/controls/table/table.d.ts +29 -0
  176. package/dist/controls/table/table.js +178 -0
  177. package/dist/controls/tabs/tab-panel.d.ts +10 -0
  178. package/dist/controls/tabs/tab-panel.js +89 -0
  179. package/dist/controls/tabs/tab.d.ts +26 -0
  180. package/dist/controls/tabs/tab.js +67 -0
  181. package/dist/controls/tabs/tabs.css +181 -0
  182. package/dist/controls/tabs/tabs.d.ts +61 -0
  183. package/dist/controls/tabs/tabs.js +176 -0
  184. package/dist/controls/text/href.d.ts +26 -0
  185. package/dist/controls/text/href.js +48 -0
  186. package/dist/controls/text/text.css +328 -0
  187. package/dist/controls/text/text.d.ts +85 -0
  188. package/dist/controls/text/text.js +273 -0
  189. package/dist/controls/text-field/text-field.css +498 -0
  190. package/dist/controls/text-field/text-field.d.ts +226 -0
  191. package/dist/controls/text-field/text-field.js +905 -0
  192. package/dist/controls/textarea/textarea.css +251 -0
  193. package/dist/controls/textarea/textarea.d.ts +136 -0
  194. package/dist/controls/textarea/textarea.js +312 -0
  195. package/dist/controls/theme-provider/theme-provider.css +48 -0
  196. package/dist/controls/theme-provider/theme-provider.d.ts +59 -0
  197. package/dist/controls/theme-provider/theme-provider.js +40 -0
  198. package/dist/controls/timeline/timeline.css +33 -0
  199. package/dist/controls/timeline/timeline.d.ts +39 -0
  200. package/dist/controls/timeline/timeline.js +52 -0
  201. package/dist/controls/timeline-item/timeline-item.css +495 -0
  202. package/dist/controls/timeline-item/timeline-item.d.ts +79 -0
  203. package/dist/controls/timeline-item/timeline-item.js +360 -0
  204. package/dist/controls/toast/toast-region.css +56 -0
  205. package/dist/controls/toast/toast-region.d.ts +20 -0
  206. package/dist/controls/toast/toast-region.js +113 -0
  207. package/dist/controls/toast/toast.css +96 -0
  208. package/dist/controls/toast/toast.d.ts +28 -0
  209. package/dist/controls/toast/toast.js +191 -0
  210. package/dist/controls/toolbar/toolbar.css +125 -0
  211. package/dist/controls/toolbar/toolbar.d.ts +108 -0
  212. package/dist/controls/toolbar/toolbar.js +68 -0
  213. package/dist/controls/tooltip/tooltip.css +80 -0
  214. package/dist/controls/tooltip/tooltip.d.ts +57 -0
  215. package/dist/controls/tooltip/tooltip.js +205 -0
  216. package/dist/descriptor/component-descriptor.d.ts +112 -0
  217. package/dist/descriptor/component-descriptor.js +492 -0
  218. package/dist/descriptor/index.d.ts +1 -0
  219. package/dist/descriptor/index.js +5 -0
  220. package/dist/dom/container.d.ts +45 -0
  221. package/dist/dom/container.js +60 -0
  222. package/dist/dom/element.d.ts +98 -0
  223. package/dist/dom/element.js +221 -0
  224. package/dist/dom/form.d.ts +240 -0
  225. package/dist/dom/form.js +399 -0
  226. package/dist/dom/index.d.ts +10 -0
  227. package/dist/dom/index.js +31 -0
  228. package/dist/dom/props.d.ts +56 -0
  229. package/dist/dom/props.js +231 -0
  230. package/dist/dom/repeat.d.ts +10 -0
  231. package/dist/dom/repeat.js +202 -0
  232. package/dist/dom/template.d.ts +161 -0
  233. package/dist/dom/template.js +634 -0
  234. package/dist/dom/watch.d.ts +11 -0
  235. package/dist/dom/watch.js +82 -0
  236. package/dist/foundation-styles.css +9 -0
  237. package/dist/index.d.ts +2 -0
  238. package/dist/index.js +3 -0
  239. package/dist/reactive/graph.d.ts +65 -0
  240. package/dist/reactive/graph.js +310 -0
  241. package/dist/reactive/index.d.ts +3 -0
  242. package/dist/reactive/index.js +4 -0
  243. package/dist/reactive/scheduler.d.ts +13 -0
  244. package/dist/reactive/scheduler.js +49 -0
  245. package/dist/traits/area-drag.d.ts +12 -0
  246. package/dist/traits/area-drag.js +70 -0
  247. package/dist/traits/form-registry.d.ts +36 -0
  248. package/dist/traits/form-registry.js +108 -0
  249. package/dist/traits/index.d.ts +24 -0
  250. package/dist/traits/index.js +19 -0
  251. package/dist/traits/overlay.d.ts +50 -0
  252. package/dist/traits/overlay.js +277 -0
  253. package/dist/traits/pane-resize.d.ts +26 -0
  254. package/dist/traits/pane-resize.js +103 -0
  255. package/dist/traits/press-activation.d.ts +11 -0
  256. package/dist/traits/press-activation.js +55 -0
  257. package/dist/traits/roving-focus.d.ts +52 -0
  258. package/dist/traits/roving-focus.js +214 -0
  259. package/dist/traits/scroll-fade.d.ts +28 -0
  260. package/dist/traits/scroll-fade.js +120 -0
  261. package/dist/traits/selection-commit.d.ts +30 -0
  262. package/dist/traits/selection-commit.js +146 -0
  263. package/dist/traits/tabbable.d.ts +12 -0
  264. package/dist/traits/tabbable.js +59 -0
  265. package/dist/traits/track-user-invalid.d.ts +36 -0
  266. package/dist/traits/track-user-invalid.js +52 -0
  267. package/dist/traits/value-codec.d.ts +120 -0
  268. package/dist/traits/value-codec.js +497 -0
  269. package/dist/traits/value-drag.d.ts +18 -0
  270. package/dist/traits/value-drag.js +84 -0
  271. package/package.json +263 -0
@@ -0,0 +1,156 @@
1
+ /* container.css — the SHARED `UIContainerElement` surface seam (ADR-0015). The one stylesheet every
2
+ * container/layout `ui-*` element sits on; it is `@import`-ed FIRST (before any element sheet) by
3
+ * component-styles.css (wired in decomp s12). It owns NO per-element layout (display/flex/grid is each
4
+ * element's own `@scope` block) — only the cross-family surface. It does NOT establish query containers
5
+ * (ADR-0100 deleted the blanket `container-type: inline-size` this file used to set on the four layout
6
+ * primitives — see the seam's own banner below for why and where establishment moved).
7
+ *
8
+ * Role-purity (ADR-0008 / tokens.md): the CONSUMPTION (the two `background-*` declarations) reads ONLY the
9
+ * `--ui-container-*` seam — never a raw `--md-sys-color-*` role. The TOKEN repoints below (`--ui-container-bg:
10
+ * var(--md-sys-color-neutral-surface-high)`) are where the colour roles enter, exactly as button.css's `:where()`
11
+ * token block does — the `--md-sys-color-*` is confined to the variable-definition side, so a container holds zero
12
+ * colour opinion and forced-colors is handled in the role/system layer.
13
+ *
14
+ * Two surface axes (ADR-0015 cl.1–3), both signed `-3…3`, realized against the s1 token ladders:
15
+ * • elevation → the scheme-INVERTING base PLANE: a SOLID `--md-sys-color-neutral-surface-{lowest…highest}` step.
16
+ * • brightness → the scheme-CONSISTENT tonal shift. Two realizations (s1's AA-verified split):
17
+ * – SOLO (brightness set, elevation 0/unset): the SOLID `--md-sys-color-neutral-surface-{dimmest…brightest}`
18
+ * ladder is the base plane (no overlay) — ADR-0015 cl.1.
19
+ * – COMPOSITION (elevation≠0 AND brightness≠0): the elevation plane is the base, and brightness becomes
20
+ * a TRANSLUCENT wash (`--md-sys-color-neutral-tint-{step}`) composited alpha-over it — ADR-0015 cl.3 — so the
21
+ * `−m` wash sits over the `+n` plane and neither axis overwrites the other. (The tint roles exist
22
+ * ONLY for this case; s1 budgets their 14% alpha ceiling against the brightest dark elevation plane.)
23
+ * The single `--ui-container-bg`/`--ui-container-tint` consumption seam is fixed regardless (ADR-0015 cl.2);
24
+ * the SOLO/COMPOSITION choice is just which token each axis-state repoints to (source order below is
25
+ * load-bearing — elevation overrides the brightness base plane when both axes are set).
26
+ *
27
+ * Default (no axis set) = transparent: an UNSET container is unchanged (it passes its parent's surface
28
+ * through), so a bare layout primitive adds no plane. Container elements that want a default surface
29
+ * (card/modal) set `--ui-container-bg` in their OWN sheet (specificity wins over this `:where()`); tabs
30
+ * left this trio in ADR-0104 — a bare `<ui-tabs>` is transparent like every other container, and a plane
31
+ * is an asked-for intent via the shipped `elevation`/`brightness` axes.
32
+ *
33
+ * Standalone sheet — linked via the component-styles barrel, NEVER injected from container.ts (behaviour
34
+ * `.ts` holds no runtime style; plan §2). */
35
+
36
+ /* ════════════════ surface seam — the 8 surface-bearing container tags ════════════════ */
37
+
38
+ :where(ui-row, ui-column, ui-list, ui-grid, ui-card, ui-tabs, ui-modal, ui-toolbar) {
39
+ /* The role-pure seam, default = transparent (an unset container is unchanged — ADR-0015 cl.1). */
40
+ --ui-container-bg: transparent; /* the base plane */
41
+ --ui-container-tint: transparent; /* the tonal wash (composition only); 0 ⇒ no-op */
42
+
43
+ /* elevation=0 — the explicit neutral base. Placed FIRST so an active axis (a brightness ladder step below,
44
+ or an elevation step) overrides it; an unset container stays transparent (this rule needs the attribute). */
45
+ &[elevation='0'] {
46
+ --ui-container-bg: var(--md-sys-color-neutral-surface);
47
+ }
48
+
49
+ /* brightness SOLO → the SOLID scheme-consistent ladder as the base plane (ADR-0015 cl.1). When elevation is
50
+ ALSO set, the elevation block below overrides this base (source order) and brightness moves to the wash. */
51
+ &[brightness='-3'] {
52
+ --ui-container-bg: var(--md-sys-color-neutral-surface-dimmest);
53
+ }
54
+ &[brightness='-2'] {
55
+ --ui-container-bg: var(--md-sys-color-neutral-surface-dimmer);
56
+ }
57
+ &[brightness='-1'] {
58
+ --ui-container-bg: var(--md-sys-color-neutral-surface-dim);
59
+ }
60
+ &[brightness='1'] {
61
+ --ui-container-bg: var(--md-sys-color-neutral-surface-bright);
62
+ }
63
+ &[brightness='2'] {
64
+ --ui-container-bg: var(--md-sys-color-neutral-surface-brighter);
65
+ }
66
+ &[brightness='3'] {
67
+ --ui-container-bg: var(--md-sys-color-neutral-surface-brightest);
68
+ }
69
+
70
+ /* elevation → the SOLID scheme-inverting base plane (-3..3 → lowest…highest). After the brightness block,
71
+ so elevation WINS the base plane when both axes are set (equal specificity → later source wins). */
72
+ &[elevation='-3'] {
73
+ --ui-container-bg: var(--md-sys-color-neutral-surface-lowest);
74
+ }
75
+ &[elevation='-2'] {
76
+ --ui-container-bg: var(--md-sys-color-neutral-surface-lower);
77
+ }
78
+ &[elevation='-1'] {
79
+ --ui-container-bg: var(--md-sys-color-neutral-surface-low);
80
+ }
81
+ &[elevation='1'] {
82
+ --ui-container-bg: var(--md-sys-color-neutral-surface-high);
83
+ }
84
+ &[elevation='2'] {
85
+ --ui-container-bg: var(--md-sys-color-neutral-surface-higher);
86
+ }
87
+ &[elevation='3'] {
88
+ --ui-container-bg: var(--md-sys-color-neutral-surface-highest);
89
+ }
90
+
91
+ /* COMPOSITION tonal WASH — brightness as a translucent overlay, ONLY when elevation is non-zero (s1: the
92
+ tint roles are composition-only). Composites alpha-over the elevation plane set above → the `−m` wash over
93
+ the `+n` plane (ADR-0015 cl.3). The `:is(non-zero elevation)` gate keeps SOLO brightness on the solid
94
+ ladder; the two-attribute selector (0,2,0) outranks the base-plane repoints, but it only sets the tint. */
95
+ &:is([elevation='-3'], [elevation='-2'], [elevation='-1'], [elevation='1'], [elevation='2'], [elevation='3']) {
96
+ &[brightness='-3'] {
97
+ --ui-container-tint: var(--md-sys-color-neutral-tint-dimmest);
98
+ }
99
+ &[brightness='-2'] {
100
+ --ui-container-tint: var(--md-sys-color-neutral-tint-dimmer);
101
+ }
102
+ &[brightness='-1'] {
103
+ --ui-container-tint: var(--md-sys-color-neutral-tint-dim);
104
+ }
105
+ &[brightness='1'] {
106
+ --ui-container-tint: var(--md-sys-color-neutral-tint-bright);
107
+ }
108
+ &[brightness='2'] {
109
+ --ui-container-tint: var(--md-sys-color-neutral-tint-brighter);
110
+ }
111
+ &[brightness='3'] {
112
+ --ui-container-tint: var(--md-sys-color-neutral-tint-brightest);
113
+ }
114
+ }
115
+
116
+ /* The CONSUMPTION — role-pure, reads ONLY the `--ui-container-*` seam. The base plane is the solid
117
+ `background-color`; the tonal wash is a flat same-colour `background-image` gradient stacked ABOVE it, so
118
+ the two axes composite (ADR-0015 cl.3). `:where()` keeps these at specificity 0 so an element's own
119
+ `@scope` surface (card/tabs/modal) cleanly overrides. */
120
+ background-color: var(--ui-container-bg);
121
+ background-image: linear-gradient(var(--ui-container-tint), var(--ui-container-tint));
122
+ }
123
+
124
+ /* ════════════════ container-query seam — RE-HOMED to externally-sized boundaries (ADR-0100) ═════════════ */
125
+
126
+ /* NO blanket `container-type: inline-size` here — ADR-0100 DELETED it (it superseded ADR-0016 cl.4's
127
+ establishment sub-clause). `container-type: inline-size` applies inline-axis SIZE CONTAINMENT: the box's
128
+ intrinsic inline sizes compute AS IF IT HAD NO CONTENTS — the same anti-cycle rule that forbids an element
129
+ from size-querying itself. A layout primitive's sizing model is content-driven (shrink-wrap, no frame,
130
+ geometry.md Container/layout class), so an intrinsically-sized primitive CANNOT also be a query container:
131
+ making it one deterministically zeroes its own intrinsic size (a Row nested on a Row's main axis measured
132
+ 0px; a contained primitive also contributes 0 intrinsic size through every content-sized ANCESTOR — the
133
+ pattern-dashboard-tiles cards crushed to ~36px chrome-only via their inner contained column). No selector
134
+ can repair this in place (the harm is non-local — safety is a whole-ancestor-chain property, invisible to
135
+ CSS), so establishment moves OFF the primitives entirely.
136
+ THE LAW (ADR-0100 cl.2): `container-type: inline-size` may be declared only on a box whose inline size is
137
+ EXTERNALLY determined — definite, stretched, or track-sized — never content-derived. The `@container` rules
138
+ in row.css/column.css keep their ADR-0096-gated selectors unchanged and now resolve, as they always
139
+ mechanically did, against the nearest ANCESTOR query container — one an externally-sized composition
140
+ boundary establishes (the A2UI mount surfaces: `site/lib/canvas-surface.css`'s `.canvas-surface`,
141
+ `site/lib/a2ui-gallery.css`'s `.seed-surface`, the various page `.surface` mounts, app-shell — ADR-0084 is
142
+ the shipped model case). `ui-grid`'s auto-fit/`minmax` responsiveness is track sizing, not a query, so it is
143
+ unaffected either way. */
144
+
145
+ /* ════════════════ forced-colors (WHCM) ════════════════ */
146
+
147
+ /* Keep the surface a system colour and drop the tonal wash. The colour ROLES already carry the WHCM mapping
148
+ in the token layer (tokens.md / s1 — the tint roles map to transparent under forced-colors), so this is
149
+ mostly belt-and-braces: it guarantees a container's surface survives as `Canvas` and the wash never paints
150
+ over system text. */
151
+ @media (forced-colors: active) {
152
+ :where(ui-row, ui-column, ui-list, ui-grid, ui-card, ui-tabs, ui-modal, ui-toolbar) {
153
+ background-color: Canvas;
154
+ background-image: none;
155
+ }
156
+ }
@@ -0,0 +1,35 @@
1
+ import type { PropConfig } from '../../dom/props.js';
2
+ /** One token-surface entry — a `ui-ramp` step or a `ui-ladder` tier (SPEC-R2 Definitions). */
3
+ export interface TokenEntry {
4
+ label: string;
5
+ value: string;
6
+ }
7
+ /**
8
+ * Harden an arbitrary input into the rendered entry set (SPEC-R7/R11): a non-array input yields `[]`; each
9
+ * entry survives only as a plain object with a `string` `label` AND a `string` `value` — drop, never coerce.
10
+ * Order is preserved (positional semantics — a duplicate-label row: both entries render, the list is
11
+ * positional, not keyed). The bar-chart `cleanData` construction, generalized to a string `value` (a color/
12
+ * length lane, not a number).
13
+ */
14
+ export declare function cleanEntries(input: unknown): TokenEntry[];
15
+ /** The `steps`/`tiers` prop config — a fresh `PropConfig` each call (default `[]`, never a SHARED mutable array). */
16
+ export declare function tokenEntriesProp(): PropConfig<TokenEntry[]>;
17
+ /**
18
+ * The `--var` lane (SPEC-R2), value-NEUTRAL: a value beginning `--` routes through `var(<value>)`; any other
19
+ * string (a literal color/length, or `''`) is returned VERBATIM. Pure string routing — no resolution, no
20
+ * `getComputedStyle` (the browser resolves the returned expression wherever it lands; the readback is an
21
+ * ADR-0118 cl.2 foreseen extension, out of v1). Used by BOTH swatch/ramp's `background` and ladder's `--_mag`
22
+ * (LLD-C6) — the ONE transform, so a `--`-prefixed value never reaches a CSS property as a bare dashed-ident
23
+ * (`min(100%, --md-sys-height-md)` is invalid at computed-value time; `min(100%, var(--md-sys-height-md))` is what we want).
24
+ */
25
+ export declare function cssValue(value: string): string;
26
+ /**
27
+ * The length-router (SPEC-R11): true iff `value` is a resolvable CSS length — a `--var` counts (its own
28
+ * resolution is the browser's job, so any `--`-prefixed name is treated as renderable; ladder still routes it
29
+ * through `cssValue` before use). Uses `CSS.supports('inline-size', v)` when available (browser + modern
30
+ * jsdom) for a real platform-truth answer; falls back to the conservative unit regex above so the plain jsdom
31
+ * unit tests still bite when `CSS.supports` is absent. NOT a drop gate (SPEC-R11) — LLD-C6 uses this to ROUTE
32
+ * a non-length value to a zero-length bar (`0px`) while KEEPING the row + its printed value, matching swatch's
33
+ * invalid-color-keeps-the-datum (SPEC-R3) and ramp's invalid-color-keeps-the-cell (SPEC-R7).
34
+ */
35
+ export declare function isRenderableLength(value: string): boolean;
@@ -0,0 +1,97 @@
1
+ // token-surface.ts — the shared value-lane helper for the token-surface family (ui-swatch/ui-ramp/ui-ladder;
2
+ // LLD-C1, token-surfaces.lld.md §2; SPEC-R2/R7/R11; ADR-0118 cl.2). DOM-free, unit-testable in plain Vitest —
3
+ // no color math, no getComputedStyle (the browser is the ONE resolver, ADR-0118 cl.2/3). A single shared folder,
4
+ // not three divergent copies (the `_base`/`_surface` precedent): all three controls bind the SAME value-lane
5
+ // contract, unlike the chart family's per-control math (bar-math.ts/sparkline math stay co-located there).
6
+ //
7
+ // Owns three things:
8
+ // 1. `cleanEntries`/`tokenEntriesProp` — the hardened `{label,value}[]` codec (the bar-chart `cleanData`/
9
+ // `barDataProp` construction, generalized: `from(null) = []`, malformed JSON → `[]`, never a throw).
10
+ // 2. `cssValue` — the value-NEUTRAL `--var` lane (SPEC-R2): a `--`-prefixed value routes through `var(<value>)`
11
+ // before it ever reaches a CSS property, so a bare dashed-ident (invalid at computed-value time inside
12
+ // `min(100%, …)`) never poisons ladder's `--_mag` or swatch/ramp's `background`.
13
+ // 3. `isRenderableLength` — the length ROUTER (SPEC-R11): NOT a drop gate — ladder uses it to decide whether a
14
+ // tier's value drives a real bar or a `0px` zero-bar, the row + printed value surviving either way (the
15
+ // unified no-silent-state rule, matching swatch's invalid-color-keeps-the-datum).
16
+ /**
17
+ * Harden an arbitrary input into the rendered entry set (SPEC-R7/R11): a non-array input yields `[]`; each
18
+ * entry survives only as a plain object with a `string` `label` AND a `string` `value` — drop, never coerce.
19
+ * Order is preserved (positional semantics — a duplicate-label row: both entries render, the list is
20
+ * positional, not keyed). The bar-chart `cleanData` construction, generalized to a string `value` (a color/
21
+ * length lane, not a number).
22
+ */
23
+ export function cleanEntries(input) {
24
+ if (!Array.isArray(input))
25
+ return [];
26
+ const out = [];
27
+ for (const entry of input) {
28
+ if (entry !== null &&
29
+ typeof entry === 'object' &&
30
+ !Array.isArray(entry) &&
31
+ typeof entry.label === 'string' &&
32
+ typeof entry.value === 'string') {
33
+ const e = entry;
34
+ out.push({ label: e.label, value: e.value });
35
+ }
36
+ }
37
+ return out;
38
+ }
39
+ /**
40
+ * The safe `steps`/`tiers` codec (SPEC-R7/R11 row 1): `from(null)` (attribute absent/removed) → `[]`, NEVER
41
+ * `null`; malformed attribute JSON is caught and also falls back to `[]` — no throw ever reaches
42
+ * `attributeChangedCallback`. `dom/props.ts`'s generic `jsonType<T>()` is deliberately NOT used here — its bare
43
+ * `JSON.parse` throws on malformed attributes and maps a removed attribute to `null`, both of which the
44
+ * hardening rows forbid reaching the render path (the bar-chart `barDataProp` reasoning, restated for the
45
+ * token-surface family's string-valued entries).
46
+ */
47
+ const tokenEntriesType = {
48
+ from(attr) {
49
+ if (attr === null)
50
+ return [];
51
+ try {
52
+ return cleanEntries(JSON.parse(attr));
53
+ }
54
+ catch {
55
+ return [];
56
+ }
57
+ },
58
+ to(value) {
59
+ return JSON.stringify(value);
60
+ },
61
+ };
62
+ /** The `steps`/`tiers` prop config — a fresh `PropConfig` each call (default `[]`, never a SHARED mutable array). */
63
+ export function tokenEntriesProp() {
64
+ return { type: tokenEntriesType, default: [] };
65
+ }
66
+ /**
67
+ * The `--var` lane (SPEC-R2), value-NEUTRAL: a value beginning `--` routes through `var(<value>)`; any other
68
+ * string (a literal color/length, or `''`) is returned VERBATIM. Pure string routing — no resolution, no
69
+ * `getComputedStyle` (the browser resolves the returned expression wherever it lands; the readback is an
70
+ * ADR-0118 cl.2 foreseen extension, out of v1). Used by BOTH swatch/ramp's `background` and ladder's `--_mag`
71
+ * (LLD-C6) — the ONE transform, so a `--`-prefixed value never reaches a CSS property as a bare dashed-ident
72
+ * (`min(100%, --md-sys-height-md)` is invalid at computed-value time; `min(100%, var(--md-sys-height-md))` is what we want).
73
+ */
74
+ export function cssValue(value) {
75
+ return value.startsWith('--') ? `var(${value})` : value;
76
+ }
77
+ // A conservative CSS-length unit regex — the jsdom fallback when `CSS.supports` is unavailable (older jsdom
78
+ // builds no `CSS.supports`). Matches a signed/unsigned number (incl. a bare `0`, unitless per the CSS spec)
79
+ // followed by one of the standard absolute/relative length units, OR a bare `0`.
80
+ const LENGTH_RE = /^[+-]?\d*\.?\d+(px|em|rem|ex|ch|vw|vh|vmin|vmax|cm|mm|in|pt|pc|q|%)$|^0$/i;
81
+ /**
82
+ * The length-router (SPEC-R11): true iff `value` is a resolvable CSS length — a `--var` counts (its own
83
+ * resolution is the browser's job, so any `--`-prefixed name is treated as renderable; ladder still routes it
84
+ * through `cssValue` before use). Uses `CSS.supports('inline-size', v)` when available (browser + modern
85
+ * jsdom) for a real platform-truth answer; falls back to the conservative unit regex above so the plain jsdom
86
+ * unit tests still bite when `CSS.supports` is absent. NOT a drop gate (SPEC-R11) — LLD-C6 uses this to ROUTE
87
+ * a non-length value to a zero-length bar (`0px`) while KEEPING the row + its printed value, matching swatch's
88
+ * invalid-color-keeps-the-datum (SPEC-R3) and ramp's invalid-color-keeps-the-cell (SPEC-R7).
89
+ */
90
+ export function isRenderableLength(value) {
91
+ if (value.startsWith('--'))
92
+ return true; // a --var's resolution is the browser's job (cssValue routes it)
93
+ if (typeof CSS !== 'undefined' && typeof CSS.supports === 'function') {
94
+ return CSS.supports('inline-size', value);
95
+ }
96
+ return LENGTH_RE.test(value.trim());
97
+ }
@@ -0,0 +1,8 @@
1
+ import type { IconName } from '@agent-ui-kit/icons';
2
+ export type FileCategory = 'image' | 'audio' | 'video' | 'pdf' | 'text' | 'archive' | 'data' | 'default';
3
+ /** SPEC-R9 AC1 — case-insensitive, `;`-parameter-stripped, TOTAL (unknown/empty → 'default'). */
4
+ export declare function fileCategory(mimeType: string): FileCategory;
5
+ export declare function categoryGlyph(c: FileCategory): IconName;
6
+ export declare function categoryLabel(c: FileCategory): string;
7
+ /** SPEC-R9 AC2 — null/non-finite/negative → null (cell absent). Decimal (1000-based) B/KB/MB/GB/TB steps. */
8
+ export declare function formatBytes(size: number | null): string | null;
@@ -0,0 +1,84 @@
1
+ // attachment-meta.ts — the pure, DOM-free math for `ui-attachment` (LLD-C4, feed-family.lld.md §4;
2
+ // SPEC-R8/R9; ADR-0112 cl.4). Plain functions over strings/numbers — no DOM, no signals, unit-testable
3
+ // without a browser (the stat-model.ts / sparkline-math.ts pure-core precedent).
4
+ //
5
+ // `fileCategory` (SPEC-R9 AC1): case-insensitive, `;`-parameter-stripped mimeType → one of eight
6
+ // categories. Exact/substring matches (`application/pdf`; the data-shaped types — json/xml/csv/
7
+ // spreadsheet) are checked BEFORE the broad `image/`·`audio/`·`video/`·`text/` prefixes, because
8
+ // `text/csv` is a data type, not a text type — an ordering the prefix pass alone cannot express. The
9
+ // archive set follows, then `default` — a TOTAL function, never throws, unknown/empty mime included.
10
+ //
11
+ // `categoryGlyph`/`categoryLabel` (LLD-C4): the fixed per-category icon-name and name-fallback maps.
12
+ //
13
+ // `formatBytes` (SPEC-R9 AC2): `size` (bytes) → a decimal (1000-based) B/KB/MB/GB/TB string via the
14
+ // module-memoized default-locale `Intl.NumberFormat` (≤ 1 fraction digit); `null`/non-finite/negative →
15
+ // `null` (the size cell is absent, never a fabricated "0 B" or "NaN B").
16
+ /** Data-shaped mimeTypes that are NOT read as their broad prefix (SPEC-R9 AC1: `text/csv` → data, not text). */
17
+ const DATA_EXACT = new Set(['application/json', 'application/xml', 'text/xml']);
18
+ /** A mimeType containing either substring is data-shaped (spreadsheet formats, any `csv` variant). */
19
+ const DATA_SUBSTRING = /spreadsheet|csv/;
20
+ /** Archive/compression formats (LLD-C4: zip|tar|gzip|x-7z|rar). */
21
+ const ARCHIVE_MATCH = /zip|tar|gzip|x-7z|rar/;
22
+ /** SPEC-R9 AC1 — case-insensitive, `;`-parameter-stripped, TOTAL (unknown/empty → 'default'). */
23
+ export function fileCategory(mimeType) {
24
+ const mime = mimeType.trim().split(';', 1)[0].trim().toLowerCase();
25
+ if (mime === '')
26
+ return 'default';
27
+ if (mime === 'application/pdf')
28
+ return 'pdf';
29
+ if (DATA_EXACT.has(mime) || DATA_SUBSTRING.test(mime))
30
+ return 'data';
31
+ if (mime.startsWith('image/'))
32
+ return 'image';
33
+ if (mime.startsWith('audio/'))
34
+ return 'audio';
35
+ if (mime.startsWith('video/'))
36
+ return 'video';
37
+ if (mime.startsWith('text/'))
38
+ return 'text';
39
+ if (ARCHIVE_MATCH.test(mime))
40
+ return 'archive';
41
+ return 'default';
42
+ }
43
+ /** The glyph name per category (all vendored, SPEC-N5 — LLD-C9's icons PREP slice). */
44
+ const CATEGORY_GLYPH = {
45
+ image: 'file-image',
46
+ audio: 'file-audio',
47
+ video: 'file-video',
48
+ pdf: 'file-pdf',
49
+ text: 'file-text',
50
+ archive: 'file-zip',
51
+ data: 'file-code',
52
+ default: 'file',
53
+ };
54
+ export function categoryGlyph(c) {
55
+ return CATEGORY_GLYPH[c];
56
+ }
57
+ /** The SPEC-R8 name fallback (never an empty title). */
58
+ const CATEGORY_LABEL = {
59
+ image: 'Image',
60
+ audio: 'Audio',
61
+ video: 'Video',
62
+ pdf: 'PDF document',
63
+ text: 'Text document',
64
+ archive: 'Archive',
65
+ data: 'Data file',
66
+ default: 'File',
67
+ };
68
+ export function categoryLabel(c) {
69
+ return CATEGORY_LABEL[c];
70
+ }
71
+ const UNITS = ['B', 'KB', 'MB', 'GB', 'TB'];
72
+ const byteFormat = new Intl.NumberFormat(undefined, { maximumFractionDigits: 1 }); // module-memoized
73
+ /** SPEC-R9 AC2 — null/non-finite/negative → null (cell absent). Decimal (1000-based) B/KB/MB/GB/TB steps. */
74
+ export function formatBytes(size) {
75
+ if (size === null || !Number.isFinite(size) || size < 0)
76
+ return null;
77
+ let value = size;
78
+ let unit = 0;
79
+ while (value >= 1000 && unit < UNITS.length - 1) {
80
+ value /= 1000;
81
+ unit++;
82
+ }
83
+ return `${byteFormat.format(value)} ${UNITS[unit]}`;
84
+ }
@@ -0,0 +1,91 @@
1
+ /* attachment.css — ui-attachment, the Display-class FilePart-aligned file card (LLD-C5,
2
+ * feed-family.lld.md §4; SPEC-R9/R18/R19/R20; ADR-0112 cl.4, fork F4). Two sectioned blocks:
3
+ *
4
+ * [1] TOKEN BLOCK — `:where(ui-attachment)` declares --ui-attachment-* from the colour roles
5
+ * (--md-sys-color-{family}-{role}) + the content-icon register (--md-sys-icon-md, geometry.md's
6
+ * "Affordance vs content-icon" taxonomy — the glyph is a CONTENT icon, not an inline affordance,
7
+ * so it rides the icon ramp, never `= font`). Display class (SPEC-R20 AC2): no [size]/[scale]
8
+ * control-band lookup, no --md-sys-height-* consumption — the border/radius/floor are frame constants,
9
+ * the glyph is the fixed default icon-ramp tier (no ancestor [scale] repoint needed at v1).
10
+ * [2] STYLES BLOCK — `@scope (ui-attachment)` consumes ONLY --ui-attachment-*.
11
+ *
12
+ * Posture (fork F4): a compact card with its OWN bordered surface (--md-sys-shape-corner-base, the entry/container
13
+ * radius kin) — one-row anatomy glyph | name+meta, inline-grid + max-inline-size:100% so N-up in a
14
+ * Row(wrap) or as ui-list children both hold with zero extra code (composability, SPEC-R9).
15
+ *
16
+ * Whole-shape floor (SPEC-R18 AC1) — --ui-attachment-min-inline-size guards the slider-dot collapse shape
17
+ * in an unstyled flex row. Rhythm (gap/padding) rides the space ladder — density-responsive for free
18
+ * (SPEC-R18 AC3); the border/radius/floor stay frame constants, unaffected by [density].
19
+ *
20
+ * Truncation (SPEC-R9 AC3): the name cell is single-line ellipsis (the ADR-0106 CSS-only mechanism,
21
+ * cited — its home use here) — `min-inline-size: 0` on the body cell is what lets the name actually
22
+ * shrink to trigger it inside a grid track, not just the property alone.
23
+ *
24
+ * Forced colors (SPEC-R19): the card's border repoints to CanvasText (the boxed identity survives); the
25
+ * glyph is `<ui-icon>` (an inline SVG using currentColor by the icon adapter's own contract) and the name/
26
+ * meta are real text — both survive WHCM with no dedicated rule here.
27
+ */
28
+
29
+ /* ════════════════ [1] TOKEN BLOCK — :where(ui-attachment) declares --ui-attachment-* ════════════════ */
30
+
31
+ :where(ui-attachment) {
32
+ --ui-attachment-min-inline-size: 12em; /* the whole-shape floor (SPEC-R18 AC1) */
33
+ --ui-attachment-gap: var(--md-sys-space-sm);
34
+ --ui-attachment-pad-inline: var(--md-sys-space-sm);
35
+ --ui-attachment-pad-block: var(--md-sys-space-xs);
36
+ --ui-attachment-border: var(--md-sys-color-neutral-outline);
37
+ --ui-attachment-surface: transparent; /* rides the ambient plane; a page may lift it */
38
+ --ui-attachment-glyph: var(--md-sys-icon-md); /* content-icon register (geometry.md taxonomy), fixed tier — Display class, no [scale] row */
39
+ --ui-attachment-name-ink: var(--md-sys-color-neutral-on-surface);
40
+ --ui-attachment-meta-ink: var(--md-sys-color-neutral-on-surface-variant);
41
+ --ui-attachment-radius: var(--md-sys-shape-corner-base); /* TKT-0066 item 5 ruling: constants route through the own chain */
42
+ }
43
+
44
+ /* ════════════════ [2] STYLES BLOCK — @scope (ui-attachment) consumes ONLY --ui-attachment-* ════════════════ */
45
+
46
+ @scope (ui-attachment) {
47
+ :scope {
48
+ display: inline-grid;
49
+ grid-template-columns: auto 1fr;
50
+ align-items: center;
51
+ gap: var(--ui-attachment-gap);
52
+ padding-inline: var(--ui-attachment-pad-inline);
53
+ padding-block: var(--ui-attachment-pad-block);
54
+ border: 1px solid var(--ui-attachment-border);
55
+ border-radius: var(--ui-attachment-radius); /* the entry/container radius kin (F4) */
56
+ background: var(--ui-attachment-surface);
57
+ min-inline-size: var(--ui-attachment-min-inline-size);
58
+ max-inline-size: 100%; /* composability — never grows past its container (SPEC-R9 AC3) */
59
+ }
60
+
61
+ :scope [data-part='glyph'] {
62
+ font-size: var(--ui-attachment-glyph); /* <ui-icon> is a 1em box — this IS its size lever */
63
+ color: var(--ui-attachment-meta-ink);
64
+ }
65
+
66
+ :scope [data-part='body'] {
67
+ display: grid;
68
+ min-inline-size: 0; /* lets the name cell actually shrink to trigger the ellipsis truncation */
69
+ }
70
+
71
+ :scope [data-part='name'] {
72
+ color: var(--ui-attachment-name-ink);
73
+ white-space: nowrap;
74
+ overflow: hidden;
75
+ text-overflow: ellipsis; /* ADR-0106 mechanism, cited — its home use */
76
+ font-size: var(--md-sys-typescale-body-medium-size);
77
+ line-height: var(--md-sys-typescale-body-medium-line-height);
78
+ }
79
+
80
+ :scope [data-part='meta'] {
81
+ color: var(--ui-attachment-meta-ink);
82
+ font-size: var(--md-sys-typescale-body-small-size);
83
+ line-height: var(--md-sys-typescale-body-small-line-height);
84
+ }
85
+
86
+ @media (forced-colors: active) {
87
+ :scope {
88
+ border-color: CanvasText; /* the boxed identity survives; name/meta are real text, glyph is currentColor-based */
89
+ }
90
+ }
91
+ }
@@ -0,0 +1,40 @@
1
+ import { UIElement, type ReactiveProps } from '../../dom/index.js';
2
+ import '../icon/icon.js';
3
+ declare const props: {
4
+ filename: import("../../index.js").PropConfig<string>;
5
+ mimeType: {
6
+ attribute: string;
7
+ type: import("../../index.js").PropType<string>;
8
+ default: string;
9
+ reflect?: boolean;
10
+ };
11
+ sizeBytes: {
12
+ attribute: string;
13
+ type: import("../../index.js").PropType<number | null>;
14
+ default: number | null;
15
+ reflect?: boolean;
16
+ };
17
+ href: import("../../index.js").PropConfig<string>;
18
+ };
19
+ export interface UIAttachmentElement extends ReactiveProps<typeof props> {
20
+ }
21
+ export declare class UIAttachmentElement extends UIElement {
22
+ static props: {
23
+ filename: import("../../index.js").PropConfig<string>;
24
+ mimeType: {
25
+ attribute: string;
26
+ type: import("../../index.js").PropType<string>;
27
+ default: string;
28
+ reflect?: boolean;
29
+ };
30
+ sizeBytes: {
31
+ attribute: string;
32
+ type: import("../../index.js").PropType<number | null>;
33
+ default: number | null;
34
+ reflect?: boolean;
35
+ };
36
+ href: import("../../index.js").PropConfig<string>;
37
+ };
38
+ protected connected(): void;
39
+ }
40
+ export {};
@@ -0,0 +1,70 @@
1
+ // attachment.ts — UIAttachmentElement, the Display-class FilePart-aligned compact file card (LLD-C5,
2
+ // feed-family.lld.md §4; SPEC-R8/R9/R10; ADR-0112 cl.4, fork F4). Non-interactive, non-form-associated
3
+ // leaf (extends UIElement): no events, no keyboard contract, no `[size]`/`[scale]` control geometry
4
+ // (Display class, SPEC-R20). Component-built light DOM only — `render()` stays the inherited no-op.
5
+ //
6
+ // Anatomy (SPEC-R9/R10): a decorative `<ui-icon data-part="glyph">` (aria-hidden by the icon's OWN
7
+ // default — no label is ever set on it, so no attachment-side ARIA work is needed) followed by a
8
+ // `<span data-part="body">` holding the `name` cell (falls back to the file category's label — never an
9
+ // empty title, SPEC-R8 AC2) and, when a finite non-negative `sizeBytes` is present, a `meta` cell with the
10
+ // formatted byte string (absent, not empty, when `sizeBytes` is null/non-finite/negative). One render effect,
11
+ // whole-swap per change (the stat.ts / bar-chart posture — no interior state worth preserving on a
12
+ // four-node card). The host mints NO internals ARIA (SPEC-R10): the name/meta are real DOM text, the
13
+ // accessible datum; the glyph repeats what the name/mime already carry.
14
+ //
15
+ // SCOPE FENCE (feed-family build sequence, LLD §11 Wave M1-a vs M1-c): this file ships the METADATA
16
+ // SURFACE ONLY (LLD-C4/C5). The `href` prop exists now because SPEC-R8 mints it as one of the four
17
+ // wire-mirroring props, but its RENDERING leg — the name cell becoming a native `<a>` under the shared
18
+ // ADR-0114 gate (`controls/text/href.ts`'s `safeHref`/`LINK_REL`/`LINK_TARGET`, LLD-C6) — is DEFERRED to
19
+ // the M1-c wave (with-or-after that shared gate module is confirmed integrated fleet-wide). Until then
20
+ // `href` is inert: read into no effect, rendered nowhere. Do not wire `safeHref` here — that is LLD-C6's
21
+ // own slice, coordinated with the catalog wave (SPEC-R11 AC3's single-gate-module grep).
22
+ import { UIElement, prop } from "../../dom/index.js";
23
+ import "../icon/icon.js"; // side-effect: defines <ui-icon>, the glyph's declarative surface (the avatar/LLD-C5 sibling-control-import idiom)
24
+ import { categoryGlyph, categoryLabel, fileCategory, formatBytes } from "./attachment-meta.js";
25
+ const props = {
26
+ filename: prop.string(''), // optional on the A2aFilePart wire — empty falls back to the category label. Renamed from `name` (TKT-0069 item 1 ruling: `name` = the FORM name, reserved; the A2UI catalog keeps wire `name`, mapped in its bespoke factory)
27
+ // `attribute: 'mime-type'` is load-bearing, not stylistic: Element.setAttribute lowercases the name in
28
+ // an HTML document, so a camelCase observed-attribute name ('mimeType') would never match the always-
29
+ // lowercase real attribute and attributeChangedCallback would silently never fire (the fleet's existing
30
+ // multi-word props are either single-word or already reflect+kebab, e.g. slider-multi's valueLo/valueHi
31
+ // — this is the first non-reflected multi-word prop, so the same kebab discipline applies here too).
32
+ mimeType: { ...prop.string(''), attribute: 'mime-type' }, // drives the glyph + name fallback via fileCategory
33
+ // Named `sizeBytes`/`size-bytes`, NOT `size` (ADR-0112 Amendment 1): the fleet-wide law
34
+ // (family-coherence.test.ts's "a `size` attribute is always exactly [sm, md, lg]") reserves the literal
35
+ // name `size` for the widget-tier geometry enum every sized control shares — this is unrelated byte-count
36
+ // domain data (SPEC-R8), and reusing `size` would silently collide with that convention for any future
37
+ // reader/consumer. Same kebab-attribute discipline as `mimeType` above (multi-word, non-reflected).
38
+ sizeBytes: { ...prop.number(null), attribute: 'size-bytes' }, // bytes; NOT a wire field — embedder-supplied (SPEC-R8); null/non-finite/negative ⇒ no meta cell
39
+ // SPEC-R8's fourth wire-mirroring prop. Rendering leg DEFERRED — see the file header note (LLD-C6).
40
+ href: prop.string(''),
41
+ };
42
+ export class UIAttachmentElement extends UIElement {
43
+ static props = props;
44
+ connected() {
45
+ this.effect(() => {
46
+ const category = fileCategory(this.mimeType);
47
+ const title = this.filename || categoryLabel(category);
48
+ const size = formatBytes(this.sizeBytes);
49
+ const glyph = document.createElement('ui-icon');
50
+ glyph.setAttribute('data-part', 'glyph');
51
+ glyph.setAttribute('glyph', categoryGlyph(category));
52
+ // The name cell is a plain <span> in this pass — the LLD-C6 <a> leg is deferred (file header note).
53
+ const name = document.createElement('span');
54
+ name.setAttribute('data-part', 'name');
55
+ name.textContent = title;
56
+ const body = document.createElement('span');
57
+ body.setAttribute('data-part', 'body');
58
+ body.append(name);
59
+ if (size !== null) {
60
+ const meta = document.createElement('span');
61
+ meta.setAttribute('data-part', 'meta');
62
+ meta.textContent = size;
63
+ body.append(meta);
64
+ }
65
+ this.replaceChildren(glyph, body);
66
+ });
67
+ }
68
+ }
69
+ if (!customElements.get('ui-attachment'))
70
+ customElements.define('ui-attachment', UIAttachmentElement); // idempotent self-define
@@ -0,0 +1,4 @@
1
+ /** SPEC-R5: first grapheme of first word + first grapheme of last word (single word ⇒ one grapheme),
2
+ * locale-uppercased. Grapheme-safe via Intl.Segmenter('grapheme') with an Array.from code-point
3
+ * fallback (ledger §10.4). '' / whitespace-only ⇒ '' (the caller falls through to the glyph). */
4
+ export declare function initialsFrom(name: string): string;
@@ -0,0 +1,30 @@
1
+ // avatar-initials.ts — the pure, DOM-free initials derivation for ui-avatar (LLD-C2, feed-family.lld.md
2
+ // §3; SPEC-R5). A plain function over a string — no DOM, no signals, unit-testable without a browser
3
+ // (the stat-model.ts / attachment-meta.ts pure-core precedent).
4
+ //
5
+ // SPEC-R5: first grapheme of the first word + first grapheme of the last word (a single word yields one
6
+ // grapheme), locale-uppercased. Grapheme-safe via `Intl.Segmenter('grapheme')` with an `Array.from`
7
+ // code-point fallback for environments where Segmenter is unavailable (failure mode #4, the LLD ledger).
8
+ // '' / whitespace-only ⇒ '' — the caller (avatar.ts) falls through to the person-glyph link of the chain.
9
+ const segmenter = typeof Intl !== 'undefined' && typeof Intl.Segmenter === 'function'
10
+ ? new Intl.Segmenter(undefined, { granularity: 'grapheme' })
11
+ : null;
12
+ /** The first grapheme cluster of a non-empty string (code-point-safe fallback when Segmenter is absent). */
13
+ function firstGrapheme(word) {
14
+ if (segmenter) {
15
+ const first = segmenter.segment(word)[Symbol.iterator]().next();
16
+ return first.done ? '' : first.value.segment;
17
+ }
18
+ return Array.from(word)[0] ?? '';
19
+ }
20
+ /** SPEC-R5: first grapheme of first word + first grapheme of last word (single word ⇒ one grapheme),
21
+ * locale-uppercased. Grapheme-safe via Intl.Segmenter('grapheme') with an Array.from code-point
22
+ * fallback (ledger §10.4). '' / whitespace-only ⇒ '' (the caller falls through to the glyph). */
23
+ export function initialsFrom(name) {
24
+ const words = name.trim().split(/\s+/).filter(Boolean);
25
+ if (words.length === 0)
26
+ return '';
27
+ const first = firstGrapheme(words[0]);
28
+ const initials = words.length === 1 ? first : first + firstGrapheme(words[words.length - 1]);
29
+ return initials.toLocaleUpperCase();
30
+ }