@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,85 @@
1
+ import { UIElement, type ReactiveProps } from '../../dom/index.js';
2
+ export { SAFE_HREF_SCHEMES, safeHref, LINK_REL, LINK_TARGET } from './href.js';
3
+ declare const props: {
4
+ variant: {
5
+ reflect: true;
6
+ type: import("../../index.js").PropType<"body" | "label" | "title" | "display" | "headline" | "kicker" | "overline" | "quote" | "lead">;
7
+ default: "body" | "label" | "title" | "display" | "headline" | "kicker" | "overline" | "quote" | "lead";
8
+ attribute?: string | false;
9
+ };
10
+ size: {
11
+ reflect: true;
12
+ type: import("../../index.js").PropType<"sm" | "md" | "lg">;
13
+ default: "sm" | "md" | "lg";
14
+ attribute?: string | false;
15
+ };
16
+ as: {
17
+ reflect: true;
18
+ type: import("../../index.js").PropType<"a" | "none" | "h1" | "h2" | "h3" | "h4" | "h5" | "blockquote" | "h6" | "p" | "span">;
19
+ default: "a" | "none" | "h1" | "h2" | "h3" | "h4" | "h5" | "blockquote" | "h6" | "p" | "span";
20
+ attribute?: string | false;
21
+ };
22
+ href: {
23
+ reflect: true;
24
+ type: import("../../index.js").PropType<string>;
25
+ default: string;
26
+ attribute?: string | false;
27
+ };
28
+ truncate: {
29
+ reflect: true;
30
+ type: import("../../index.js").PropType<boolean>;
31
+ default: boolean;
32
+ attribute?: string | false;
33
+ };
34
+ emphasis: {
35
+ reflect: true;
36
+ type: import("../../index.js").PropType<boolean>;
37
+ default: boolean;
38
+ attribute?: string | false;
39
+ };
40
+ };
41
+ export interface UITextElement extends ReactiveProps<typeof props> {
42
+ }
43
+ export declare class UITextElement extends UIElement {
44
+ #private;
45
+ static props: {
46
+ variant: {
47
+ reflect: true;
48
+ type: import("../../index.js").PropType<"body" | "label" | "title" | "display" | "headline" | "kicker" | "overline" | "quote" | "lead">;
49
+ default: "body" | "label" | "title" | "display" | "headline" | "kicker" | "overline" | "quote" | "lead";
50
+ attribute?: string | false;
51
+ };
52
+ size: {
53
+ reflect: true;
54
+ type: import("../../index.js").PropType<"sm" | "md" | "lg">;
55
+ default: "sm" | "md" | "lg";
56
+ attribute?: string | false;
57
+ };
58
+ as: {
59
+ reflect: true;
60
+ type: import("../../index.js").PropType<"a" | "none" | "h1" | "h2" | "h3" | "h4" | "h5" | "blockquote" | "h6" | "p" | "span">;
61
+ default: "a" | "none" | "h1" | "h2" | "h3" | "h4" | "h5" | "blockquote" | "h6" | "p" | "span";
62
+ attribute?: string | false;
63
+ };
64
+ href: {
65
+ reflect: true;
66
+ type: import("../../index.js").PropType<string>;
67
+ default: string;
68
+ attribute?: string | false;
69
+ };
70
+ truncate: {
71
+ reflect: true;
72
+ type: import("../../index.js").PropType<boolean>;
73
+ default: boolean;
74
+ attribute?: string | false;
75
+ };
76
+ emphasis: {
77
+ reflect: true;
78
+ type: import("../../index.js").PropType<boolean>;
79
+ default: boolean;
80
+ attribute?: string | false;
81
+ };
82
+ };
83
+ protected connected(): void;
84
+ protected disconnected(): void;
85
+ }
@@ -0,0 +1,273 @@
1
+ // text.ts — UITextElement, the Display-class text primitive (ADR-0078, supersedes ADR-0025's prop schema
2
+ // + heading path). BEHAVIOUR + props + stamping + self-define ONLY.
3
+ //
4
+ // Five orthogonal axes (ADR-0078 cl.1 + ADR-0106 + ADR-0109): `variant` (the visual type ROLE — which
5
+ // --md-sys-typescale-* block text.css repoints to), `size` (sm/md/lg — the row within the role), `as`
6
+ // (document SEMANTICS — the real element STAMPED around the light-DOM children), `truncate` (ADR-0106 —
7
+ // overflow INTENT: single-line + ellipsis, CSS-only), and `emphasis` (ADR-0109 — weight INTENT: the
8
+ // platform bold register, CSS-only). `variant`/`size` carry zero semantics now; `as` alone does — the
9
+ // honest split ADR-0025's conflated `variant="h4"` (visual ROLE + heading level in one knob) could not
10
+ // make.
11
+ //
12
+ // `truncate` (ADR-0106) is CSS-only by Kim's explicit ratification ruling — no box-size measurement, no
13
+ // dimension-watching observer of any kind installed anywhere in this file (a grep-able absence is the
14
+ // ADR's own Acceptance leg — this file must never name the platform API that watches element box size).
15
+ // While `truncate` is set, the element mirrors its own trimmed `textContent` onto `title` UNCONDITIONALLY
16
+ // (present even when the text isn't actually clipped — the accepted CSS-only cost), riding the EXISTING
17
+ // render/childList observer below rather than a bespoke measurement path; an author-set `title` is never
18
+ // overwritten (presence-checked before the mirror's first write — the mirror only ever owns a title it
19
+ // minted itself).
20
+ //
21
+ // `emphasis` (ADR-0109) is schema-only — a reflected boolean, zero behavior code in this file. The
22
+ // `[emphasis]` CSS hook (text.css) repoints `--ui-text-weight` to 700 (the platform bold register);
23
+ // `font-weight` inherits, so the ADR-0078 cl.4 stamp-transparency reset already carries it into any
24
+ // stamped element for free — no second stamp leg, no observer, no effect (contrast `truncate`, whose
25
+ // non-inheriting overflow properties needed one).
26
+ //
27
+ // The hyperlink capability (ADR-0114, SPEC-R7…R13; LLD-C1/C2) — `as` gains `'a'`, a NEW reflected `href`
28
+ // string prop. `#syncLink()` is the SOLE writer of the stamp's `href`/`rel`/`target` (a grep-able
29
+ // invariant, pinned by a test: the ONE call that stamps a live href attribute lives inside `#syncLink`,
30
+ // nowhere else in this file). Every href value passes `safeHref` (`./href.ts`) — the fleet's fail-closed
31
+ // scheme allowlist — before the stamp ever carries it; denied or no-destination strips all three
32
+ // attributes (an inert placeholder, never an announced-broken link). Two call sites reach `#syncLink`,
33
+ // covering all four external write paths
34
+ // (attribute/property/factory/bound, which all converge on the reactive prop signal) PLUS the content-
35
+ // clobber re-stamp path: (1) a dedicated `connected()` effect on `href`/`as`, declared AFTER the restamp
36
+ // effect so its initial run finds the initial stamp; (2) the TAIL of `#restamp()`'s fall-through branch
37
+ // only — the `none`-branch return and the same-tag no-op never call it (see `#restamp` below). No second
38
+ // gate, no cached verdict — the gate is per-value, not per-render; `URL` parsing on a prop write is
39
+ // negligible, and ui-text gains no new observer for this feature.
40
+ //
41
+ // Content model — host-as-content STANDS (ADR-0006 + ADR-0025 cl.2): the user's light-DOM children remain
42
+ // the displayed text and the accessible name; there is still no `text` prop and no `html``` template, so
43
+ // `render()` stays the inherited void. The DEPARTURE (ADR-0078 cl.4): when `as ≠ none`, those children are
44
+ // wrapped in one real semantic element (the "stamp") — a scope-owned DOM-adoption effect, NOT the template
45
+ // system (a template would clobber the user-owned content ADR-0025 cl.2 chose void `render()` to protect).
46
+ //
47
+ // Stamping mechanism (cl.4 / B3): a connected() effect off `as` creates/replaces/unwraps the stamp, moving
48
+ // nodes — never cloning them (node identity, ADR-0022). A childList MutationObserver on the host heals the
49
+ // invariant whenever nodes land directly on the host: (a) a parser-streamed `<ui-text as="h4">…` connects
50
+ // BEFORE its children exist, so they arrive as host children; (b) the A2UI `textFactory` / any bound
51
+ // `text:{path}` write sets `host.textContent`, which destroys every child (stamp included) — the observer
52
+ // detects the detached stamp and re-stamps fresh around the new content. Both converge within a microtask;
53
+ // `as="none"` never installs a stamp at all (byte-identical DOM to today).
54
+ //
55
+ // Semantics move to the platform (cl.4): a stamped `<h4>` IS the heading (name = its content, free); the
56
+ // ADR-0025 cl.4 ElementInternals path (role=heading + ariaLevel driven by `variant`) is deleted outright —
57
+ // keeping an internals role beside a real heading child would double-announce.
58
+ //
59
+ // Display size-class (ADR-0025 cl.1): no control height, no padding-block law, no frame. `user-select` is
60
+ // ENABLED (display text is selectable — the deliberate inverse of ui-button, which disables it).
61
+ //
62
+ // Imports inward only (controls → dom): UIElement + prop + the typed-schema helpers from the dom barrel.
63
+ import { UIElement, prop } from "../../dom/index.js";
64
+ import { safeHref, LINK_REL, LINK_TARGET } from "./href.js";
65
+ // Re-exported through the control's OWN public exports-map entry (`./controls/text`, the ADR-0080 T4
66
+ // bijection — `href.ts` has no control of its own to mint a second entry for) so `@agent-ui-kit/a2ui`'s static
67
+ // validator (content-family LLD-C13, ADR-0114 cl.3) can reach `SAFE_HREF_SCHEMES` off that SAME subpath,
68
+ // with no package-boundary violation and no second exports-map entry.
69
+ export { SAFE_HREF_SCHEMES, safeHref, LINK_REL, LINK_TARGET } from "./href.js";
70
+ const props = {
71
+ // The visual type ROLE (which --md-sys-typescale-* block text.css repoints to) — the five M3 roles +
72
+ // four editorial extras (ADR-0078 cl.1/cl.2b). Zero semantic effect; REFLECTS so the [variant] repoint
73
+ // in text.css applies to JS-set values too (the ui-button `variant`/`size` precedent).
74
+ variant: {
75
+ ...prop.enum(['display', 'headline', 'title', 'body', 'label', 'kicker', 'overline', 'quote', 'lead'], 'body'),
76
+ reflect: true,
77
+ },
78
+ // The size WITHIN the role (which row of the block) — orthogonal to variant; every 9×3 cell is defined
79
+ // (cl.1). `md` is the universal default for every role.
80
+ size: { ...prop.enum(['sm', 'md', 'lg'], 'md'), reflect: true },
81
+ // Document SEMANTICS — the real element STAMPED around the light-DOM children (cl.4). `none` = no
82
+ // wrapper: the host itself is the styled node, the 80% display case, byte-identical to today's DOM.
83
+ as: {
84
+ ...prop.enum(['none', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'span', 'blockquote', 'a'], 'none'),
85
+ reflect: true,
86
+ },
87
+ // The hyperlink destination (ADR-0114, SPEC-R7) — reflected so a JS-set value is inspectable/stylable
88
+ // (the variant/size/as precedent). The HOST attribute is INERT by construction (SPEC-R9): a custom
89
+ // element is not in the `:any-link` grammar, so even a raw, unsanitized value reflected here can never
90
+ // navigate — only the gated STAMP `<a>` (`#syncLink`, below) ever carries a live `href`. Default `''` is
91
+ // "no destination", never a self-link (the gate's own no-destination rule, `href.ts`).
92
+ href: { ...prop.string(''), reflect: true },
93
+ // Overflow INTENT (ADR-0106) — the fourth orthogonal axis: single-line + ellipsis, CSS-only (text.css's
94
+ // `[truncate]` legs do the clipping). Reflects so the `[truncate]` CSS hook applies to JS-set values too
95
+ // (the variant/size/as precedent). Default `false` keeps today's wrapping — no shipped rendering change.
96
+ truncate: { ...prop.boolean(false), reflect: true },
97
+ // Weight INTENT (ADR-0109) — the fifth orthogonal axis: bold, CSS-only (text.css's `[emphasis]`
98
+ // token-block repoint does the work; no styles-block change, no stamp leg — weight inherits). Reflects
99
+ // so the `[emphasis]` CSS hook applies to JS-set values too (the variant/size/as/truncate precedent).
100
+ // Default `false` keeps today's rendering — no shipped visual change.
101
+ emphasis: { ...prop.boolean(false), reflect: true },
102
+ };
103
+ export class UITextElement extends UIElement {
104
+ static props = props;
105
+ // The stamp — the one real element wrapping the light-DOM content while `as ≠ none`; null otherwise
106
+ // (cl.4's invariant). `#`-private: nothing outside the host can observe or hold it.
107
+ #stamp = null;
108
+ // The childList observer that heals the invariant (parser streaming / textContent clobber — #heal), and
109
+ // — the SAME observer, no second one (ADR-0106) — the content-change trigger for the `title` mirror.
110
+ // Disconnected in `disconnected()` — the same "zero residue after removal" discipline `this.effect` /
111
+ // `this.listen` give for free, applied by hand since a raw platform observer isn't scope-owned.
112
+ #observer = null;
113
+ // Whether the CURRENT `title` attribute is one this instance minted (ADR-0106 cl.3). Gates both halves
114
+ // of the "never overwrite an author-set title" rule: unset while `false` (nothing to remove on
115
+ // `truncate` unset, nothing owned to overwrite on the next mint check).
116
+ #titleMinted = false;
117
+ /** The exact string the mirror last minted — ownership is checked by VALUE, not just a flag, so an
118
+ * author who sets `title` AFTER a mint is never clobbered on the next sync (review-hardened: the
119
+ * ADR's "the mirror owns only titles it minted" implemented literally). */
120
+ #mintedValue = null;
121
+ connected() {
122
+ // (1) The restamp effect — runs now (the initial `as`) and again on every `as` change (scope-owned,
123
+ // so it dies with the connection — the button.ts ariaDisabled-effect precedent).
124
+ this.effect(() => this.#restamp(this.as));
125
+ // (1b) The link-sync effect (ADR-0114, SPEC-R8) — declared AFTER the restamp effect so its initial run
126
+ // finds the initial stamp already in place; wakes on any `href` OR `as` change. This ONE effect covers
127
+ // all four external write paths (P1 attribute / P2 property / P3 factory / P4 bound) — every write path
128
+ // converges on the reactive prop signal, which is exactly what an effect subscribes to.
129
+ this.effect(() => {
130
+ this.href;
131
+ this.as;
132
+ this.#syncLink();
133
+ });
134
+ // (2) The title-mirror effect (ADR-0106) — runs now (the initial `truncate`) and again on every
135
+ // `truncate` change: mints/removes the mirror. Content-change updates (the text itself changing while
136
+ // `truncate` stays set) ride the heal observer below, not this effect.
137
+ this.effect(() => this.#syncTitle(this.truncate));
138
+ // (3) The heal observer — installed AFTER the initial restamp/title-sync above, so it never observes
139
+ // its own synchronous setup; it only fires for LATER host mutations (parser streaming, an external
140
+ // write, or a bound-text `textContent` clobber) — the ONE existing render/childList path both #heal
141
+ // and the title mirror ride (ADR-0106 cl.3 — no second observer).
142
+ this.#observer = new MutationObserver(() => {
143
+ this.#heal();
144
+ this.#syncTitle(this.truncate);
145
+ });
146
+ this.#observer.observe(this, { childList: true });
147
+ }
148
+ disconnected() {
149
+ this.#observer?.disconnect();
150
+ this.#observer = null;
151
+ }
152
+ /**
153
+ * Create/replace/unwrap the stamp for `tag`. Moves nodes — never clones them (node identity, ADR-0022):
154
+ * `none` unwraps (`replaceWith(...childNodes)` drops the stamp's content back into the host at the
155
+ * stamp's own position, in one call — no reflow of order); a same-tag call is a no-op; otherwise a fresh
156
+ * element is created, the current content nodes are moved into it, and it takes the old stamp's place
157
+ * (or is appended, when there was no prior stamp).
158
+ */
159
+ #restamp(tag) {
160
+ const prev = this.#stamp;
161
+ if (tag === 'none') {
162
+ if (prev) {
163
+ prev.replaceWith(...prev.childNodes);
164
+ this.#stamp = null;
165
+ }
166
+ return;
167
+ }
168
+ if (prev?.localName === tag)
169
+ return; // already the right element — no-op
170
+ const next = document.createElement(tag);
171
+ const source = prev ?? this;
172
+ while (source.firstChild)
173
+ next.appendChild(source.firstChild); // move, never clone (the modal.ts precedent)
174
+ if (prev)
175
+ prev.replaceWith(next);
176
+ else
177
+ this.appendChild(next);
178
+ this.#stamp = next;
179
+ // The fall-through tail ONLY (ADR-0114, SPEC-R8 AC8) — re-syncs the gated href onto the FRESH stamp.
180
+ // This is the path that covers the `textContent`-clobber re-stamp (`#heal`'s detached branch nulls the
181
+ // stamp and calls `#restamp`, which falls through here): the gated href survives every bound-text
182
+ // write. The other two exits above never call this — `none` unwraps to plain text (nothing left to
183
+ // gate) and same-tag is a no-op the (1b) href/as effect already covers.
184
+ this.#syncLink();
185
+ }
186
+ /**
187
+ * Applies the gated link state to the CURRENT stamp — the SOLE writer of the stamp's `href`/`rel`/
188
+ * `target` (ADR-0114, SPEC-R8/R11; a grep-able invariant: the one call in this file that stamps a live
189
+ * href attribute lives right here, nowhere else). A non-anchor stamp (or no stamp at all) never carries
190
+ * link attributes — `href` with `as ≠ 'a'` is inert, documented not erroring (SPEC-R7 AC2). Otherwise the
191
+ * value passes `safeHref` (`./href.ts`): denied or no-destination strips all three attributes (the
192
+ * stamped `<a>` degrades to a plain `generic` for assistive tech, per HTML-AAM — SPEC-R10); allowed
193
+ * applies the value BYTE-IDENTICAL (the gate never rewrites) plus the fixed `rel`/`target` policy
194
+ * constants (SPEC-R11 — never props).
195
+ */
196
+ #syncLink() {
197
+ const stamp = this.#stamp;
198
+ if (!stamp)
199
+ return;
200
+ if (stamp.localName !== 'a') {
201
+ // a non-anchor stamp never carries link attributes (SPEC-R7 AC2 — href without as="a" is inert)
202
+ stamp.removeAttribute('href');
203
+ stamp.removeAttribute('rel');
204
+ stamp.removeAttribute('target');
205
+ return;
206
+ }
207
+ const gated = safeHref(this.href, document.baseURI);
208
+ if (gated === null) {
209
+ stamp.removeAttribute('href');
210
+ stamp.removeAttribute('rel');
211
+ stamp.removeAttribute('target');
212
+ }
213
+ else {
214
+ stamp.setAttribute('href', gated); // byte-identical — the gate never rewrites
215
+ stamp.setAttribute('rel', LINK_REL);
216
+ stamp.setAttribute('target', LINK_TARGET);
217
+ }
218
+ }
219
+ /**
220
+ * Restore the invariant after a childList mutation lands directly on the host. A DETACHED stamp (the
221
+ * `host.textContent` clobber — every child, stamp included, replaced by one fresh text node) is nulled
222
+ * and re-stamped fresh — never reused, it holds stale content. Otherwise every stray host child (parser-
223
+ * streamed content arriving after connect) is adopted into the stamp, append order. Both branches mutate
224
+ * the host and so re-fire this very observer, but the next delivery finds the invariant satisfied — a
225
+ * no-op — so this self-converges within ≤2 passes; it can never loop forever.
226
+ */
227
+ #heal() {
228
+ const stamp = this.#stamp;
229
+ if (!stamp)
230
+ return;
231
+ if (stamp.parentNode !== this) {
232
+ this.#stamp = null;
233
+ this.#restamp(this.as);
234
+ return;
235
+ }
236
+ for (const node of Array.from(this.childNodes)) {
237
+ if (node !== stamp)
238
+ stamp.appendChild(node);
239
+ }
240
+ }
241
+ /**
242
+ * The unconditional `title` mirror (ADR-0106 cl.3, Kim's CSS-only ratification ruling — "truncate should
243
+ * be CSS-only solution. no resize-observer overkill"). While `truncate` is set, `title` tracks this
244
+ * element's own trimmed `textContent` — UNCONDITIONALLY, whether or not the text is actually clipped
245
+ * (no measurement, no dimension-watching observer: the accepted cost of staying measurement-free on a
246
+ * Display leaf).
247
+ * An author-set `title` is never overwritten: presence-checked before the mirror's first write, so once
248
+ * a real author title is found the mirror steps back permanently (until that attribute is removed,
249
+ * freeing the mirror to mint one). The mirror only ever REMOVES a title IT minted (`#titleMinted`).
250
+ */
251
+ #syncTitle(truncate) {
252
+ const current = this.getAttribute('title');
253
+ // Ownership by VALUE: the mirror owns the title only while it still holds the exact string it
254
+ // minted. An author write at ANY time (before the first mint or after one) diverges the value and
255
+ // the mirror steps back permanently (until the author removes the attribute).
256
+ const mirrorOwns = this.#titleMinted && current === this.#mintedValue;
257
+ if (!truncate) {
258
+ if (mirrorOwns)
259
+ this.removeAttribute('title');
260
+ this.#titleMinted = false;
261
+ this.#mintedValue = null;
262
+ return;
263
+ }
264
+ if (current !== null && !mirrorOwns)
265
+ return; // author-owned — never overwritten
266
+ const next = (this.textContent ?? '').trim();
267
+ this.setAttribute('title', next);
268
+ this.#titleMinted = true;
269
+ this.#mintedValue = next;
270
+ }
271
+ }
272
+ if (!customElements.get('ui-text'))
273
+ customElements.define('ui-text', UITextElement);