@tulipfarm/sprout 0.0.0-stage → 0.1.0

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 (306) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +183 -2
  3. package/dist/components/accordion.d.ts +28 -0
  4. package/dist/components/accordion.js +26 -0
  5. package/dist/components/alert-dialog.d.ts +60 -0
  6. package/dist/components/alert-dialog.js +69 -0
  7. package/dist/components/alert.d.ts +30 -0
  8. package/dist/components/alert.js +35 -0
  9. package/dist/components/aspect-ratio.d.ts +7 -0
  10. package/dist/components/aspect-ratio.js +6 -0
  11. package/dist/components/attachment.d.ts +118 -0
  12. package/dist/components/attachment.js +285 -0
  13. package/dist/components/avatar.d.ts +32 -0
  14. package/dist/components/avatar.js +73 -0
  15. package/dist/components/badge.d.ts +21 -0
  16. package/dist/components/badge.js +27 -0
  17. package/dist/components/brand-icon.d.ts +28 -0
  18. package/dist/components/brand-icon.js +78 -0
  19. package/dist/components/breadcrumb.d.ts +29 -0
  20. package/dist/components/breadcrumb.js +44 -0
  21. package/dist/components/button-group.d.ts +28 -0
  22. package/dist/components/button-group.js +36 -0
  23. package/dist/components/button.d.ts +79 -0
  24. package/dist/components/button.js +93 -0
  25. package/dist/components/calendar.d.ts +27 -0
  26. package/dist/components/calendar.js +107 -0
  27. package/dist/components/canvas.d.ts +78 -0
  28. package/dist/components/canvas.js +180 -0
  29. package/dist/components/card.d.ts +43 -0
  30. package/dist/components/card.js +42 -0
  31. package/dist/components/carousel.d.ts +57 -0
  32. package/dist/components/carousel.js +422 -0
  33. package/dist/components/chart.d.ts +112 -0
  34. package/dist/components/chart.js +172 -0
  35. package/dist/components/chat-confirmation.d.ts +42 -0
  36. package/dist/components/chat-confirmation.js +62 -0
  37. package/dist/components/chat-plan.d.ts +21 -0
  38. package/dist/components/chat-plan.js +35 -0
  39. package/dist/components/chat-sources.d.ts +64 -0
  40. package/dist/components/chat-sources.js +66 -0
  41. package/dist/components/checkbox.d.ts +16 -0
  42. package/dist/components/checkbox.js +17 -0
  43. package/dist/components/choice-list.d.ts +53 -0
  44. package/dist/components/choice-list.js +36 -0
  45. package/dist/components/code-block.d.ts +35 -0
  46. package/dist/components/code-block.js +95 -0
  47. package/dist/components/collapsible.d.ts +24 -0
  48. package/dist/components/collapsible.js +27 -0
  49. package/dist/components/combobox.d.ts +95 -0
  50. package/dist/components/combobox.js +106 -0
  51. package/dist/components/command.d.ts +94 -0
  52. package/dist/components/command.js +414 -0
  53. package/dist/components/context-menu.d.ts +57 -0
  54. package/dist/components/context-menu.js +60 -0
  55. package/dist/components/dialog.d.ts +60 -0
  56. package/dist/components/dialog.js +79 -0
  57. package/dist/components/direction.d.ts +6 -0
  58. package/dist/components/direction.js +6 -0
  59. package/dist/components/dropdown-menu.d.ts +62 -0
  60. package/dist/components/dropdown-menu.js +59 -0
  61. package/dist/components/field.d.ts +117 -0
  62. package/dist/components/field.js +150 -0
  63. package/dist/components/file-viewer.d.ts +120 -0
  64. package/dist/components/file-viewer.js +373 -0
  65. package/dist/components/form.d.ts +87 -0
  66. package/dist/components/form.js +55 -0
  67. package/dist/components/glass-banner.d.ts +27 -0
  68. package/dist/components/glass-banner.js +116 -0
  69. package/dist/components/gradient-banner.d.ts +37 -0
  70. package/dist/components/gradient-banner.js +43 -0
  71. package/dist/components/hover-card.d.ts +27 -0
  72. package/dist/components/hover-card.js +26 -0
  73. package/dist/components/input-group.d.ts +68 -0
  74. package/dist/components/input-group.js +88 -0
  75. package/dist/components/input-otp.d.ts +38 -0
  76. package/dist/components/input-otp.js +43 -0
  77. package/dist/components/input.d.ts +16 -0
  78. package/dist/components/input.js +18 -0
  79. package/dist/components/kbd.d.ts +12 -0
  80. package/dist/components/kbd.js +11 -0
  81. package/dist/components/label.d.ts +8 -0
  82. package/dist/components/label.js +10 -0
  83. package/dist/components/mention-list.d.ts +54 -0
  84. package/dist/components/mention-list.js +31 -0
  85. package/dist/components/menubar.d.ts +64 -0
  86. package/dist/components/menubar.js +68 -0
  87. package/dist/components/message-actions.d.ts +34 -0
  88. package/dist/components/message-actions.js +68 -0
  89. package/dist/components/message-response.d.ts +20 -0
  90. package/dist/components/message-response.js +46 -0
  91. package/dist/components/message-scroller.d.ts +71 -0
  92. package/dist/components/message-scroller.js +217 -0
  93. package/dist/components/message.d.ts +49 -0
  94. package/dist/components/message.js +59 -0
  95. package/dist/components/navigation-menu.d.ts +45 -0
  96. package/dist/components/navigation-menu.js +54 -0
  97. package/dist/components/notice-banner.d.ts +14 -0
  98. package/dist/components/notice-banner.js +11 -0
  99. package/dist/components/page-content.d.ts +7 -0
  100. package/dist/components/page-content.js +9 -0
  101. package/dist/components/page-header.d.ts +37 -0
  102. package/dist/components/page-header.js +53 -0
  103. package/dist/components/pagination.d.ts +29 -0
  104. package/dist/components/pagination.js +29 -0
  105. package/dist/components/plan-outline.d.ts +60 -0
  106. package/dist/components/plan-outline.js +108 -0
  107. package/dist/components/popover.d.ts +34 -0
  108. package/dist/components/popover.js +35 -0
  109. package/dist/components/progress.d.ts +27 -0
  110. package/dist/components/progress.js +35 -0
  111. package/dist/components/prompt-list.d.ts +23 -0
  112. package/dist/components/prompt-list.js +11 -0
  113. package/dist/components/questionnaire.d.ts +148 -0
  114. package/dist/components/questionnaire.js +1001 -0
  115. package/dist/components/radio-group.d.ts +16 -0
  116. package/dist/components/radio-group.js +21 -0
  117. package/dist/components/reasoning-effort.d.ts +24 -0
  118. package/dist/components/reasoning-effort.js +269 -0
  119. package/dist/components/resizable.d.ts +67 -0
  120. package/dist/components/resizable.js +536 -0
  121. package/dist/components/result-card.d.ts +58 -0
  122. package/dist/components/result-card.js +88 -0
  123. package/dist/components/review-card.d.ts +62 -0
  124. package/dist/components/review-card.js +49 -0
  125. package/dist/components/rich-text-editor.d.ts +51 -0
  126. package/dist/components/rich-text-editor.js +248 -0
  127. package/dist/components/schema-diagram.d.ts +52 -0
  128. package/dist/components/schema-diagram.js +226 -0
  129. package/dist/components/scroll-area.d.ts +22 -0
  130. package/dist/components/scroll-area.js +19 -0
  131. package/dist/components/search-dialog.d.ts +103 -0
  132. package/dist/components/search-dialog.js +64 -0
  133. package/dist/components/search-field.d.ts +17 -0
  134. package/dist/components/search-field.js +27 -0
  135. package/dist/components/select.d.ts +57 -0
  136. package/dist/components/select.js +57 -0
  137. package/dist/components/selection-slot.d.ts +18 -0
  138. package/dist/components/selection-slot.js +53 -0
  139. package/dist/components/separator.d.ts +48 -0
  140. package/dist/components/separator.js +54 -0
  141. package/dist/components/settings-list.d.ts +26 -0
  142. package/dist/components/settings-list.js +17 -0
  143. package/dist/components/sheet.d.ts +70 -0
  144. package/dist/components/sheet.js +77 -0
  145. package/dist/components/sidebar.d.ts +70 -0
  146. package/dist/components/sidebar.js +187 -0
  147. package/dist/components/skeleton.d.ts +31 -0
  148. package/dist/components/skeleton.js +32 -0
  149. package/dist/components/slider.d.ts +17 -0
  150. package/dist/components/slider.js +24 -0
  151. package/dist/components/spinner.d.ts +37 -0
  152. package/dist/components/spinner.js +43 -0
  153. package/dist/components/state-panel-samples.d.ts +25 -0
  154. package/dist/components/state-panel-samples.js +423 -0
  155. package/dist/components/state-panel.d.ts +35 -0
  156. package/dist/components/state-panel.js +29 -0
  157. package/dist/components/state-sample.d.ts +63 -0
  158. package/dist/components/state-sample.js +42 -0
  159. package/dist/components/switch.d.ts +12 -0
  160. package/dist/components/switch.js +14 -0
  161. package/dist/components/table-nuqs.d.ts +16 -0
  162. package/dist/components/table-nuqs.js +59 -0
  163. package/dist/components/table.d.ts +120 -0
  164. package/dist/components/table.js +663 -0
  165. package/dist/components/tabs.d.ts +37 -0
  166. package/dist/components/tabs.js +36 -0
  167. package/dist/components/textarea.d.ts +18 -0
  168. package/dist/components/textarea.js +19 -0
  169. package/dist/components/toast.d.ts +45 -0
  170. package/dist/components/toast.js +83 -0
  171. package/dist/components/toggle-group.d.ts +30 -0
  172. package/dist/components/toggle-group.js +39 -0
  173. package/dist/components/toggle.d.ts +27 -0
  174. package/dist/components/toggle.js +21 -0
  175. package/dist/components/tool-activity.d.ts +79 -0
  176. package/dist/components/tool-activity.js +226 -0
  177. package/dist/components/toolbar-status.d.ts +21 -0
  178. package/dist/components/toolbar-status.js +14 -0
  179. package/dist/components/tooltip.d.ts +31 -0
  180. package/dist/components/tooltip.js +99 -0
  181. package/dist/components/typed-text.d.ts +17 -0
  182. package/dist/components/typed-text.js +56 -0
  183. package/dist/index.d.ts +88 -0
  184. package/dist/index.js +88 -0
  185. package/dist/internal/brand-glyphs.d.ts +342 -0
  186. package/dist/internal/brand-glyphs.js +238 -0
  187. package/dist/internal/classes.d.ts +3 -0
  188. package/dist/internal/classes.js +20 -0
  189. package/dist/internal/fieldset.d.ts +6 -0
  190. package/dist/internal/fieldset.js +38 -0
  191. package/dist/internal/file-kind.d.ts +4 -0
  192. package/dist/internal/file-kind.js +62 -0
  193. package/dist/internal/languages.d.ts +7 -0
  194. package/dist/internal/languages.js +1010 -0
  195. package/dist/internal/message-scroller.d.ts +102 -0
  196. package/dist/internal/message-scroller.js +637 -0
  197. package/dist/internal/pressable.d.ts +2 -0
  198. package/dist/internal/pressable.js +29 -0
  199. package/dist/internal/syntax.d.ts +14 -0
  200. package/dist/internal/syntax.js +137 -0
  201. package/dist/styles/accordion.css +102 -0
  202. package/dist/styles/alert-dialog.css +198 -0
  203. package/dist/styles/alert.css +149 -0
  204. package/dist/styles/aspect-ratio.css +8 -0
  205. package/dist/styles/attachment.css +720 -0
  206. package/dist/styles/avatar.css +186 -0
  207. package/dist/styles/badge.css +178 -0
  208. package/dist/styles/brand-icon.css +104 -0
  209. package/dist/styles/breadcrumb.css +121 -0
  210. package/dist/styles/button-group.css +113 -0
  211. package/dist/styles/button.css +380 -0
  212. package/dist/styles/calendar.css +295 -0
  213. package/dist/styles/canvas.css +327 -0
  214. package/dist/styles/card.css +146 -0
  215. package/dist/styles/carousel.css +107 -0
  216. package/dist/styles/chart.css +227 -0
  217. package/dist/styles/chat-confirmation.css +29 -0
  218. package/dist/styles/chat-plan.css +109 -0
  219. package/dist/styles/chat-sources.css +191 -0
  220. package/dist/styles/checkbox.css +123 -0
  221. package/dist/styles/choice-list.css +343 -0
  222. package/dist/styles/code-block.css +211 -0
  223. package/dist/styles/collapsible.css +72 -0
  224. package/dist/styles/combobox.css +347 -0
  225. package/dist/styles/command.css +233 -0
  226. package/dist/styles/context-menu.css +151 -0
  227. package/dist/styles/dialog.css +237 -0
  228. package/dist/styles/dropdown-menu.css +150 -0
  229. package/dist/styles/field.css +341 -0
  230. package/dist/styles/file-viewer.css +278 -0
  231. package/dist/styles/form.css +155 -0
  232. package/dist/styles/glass-banner.css +308 -0
  233. package/dist/styles/gradient-banner.css +256 -0
  234. package/dist/styles/hover-card.css +95 -0
  235. package/dist/styles/index.css +262 -0
  236. package/dist/styles/input-group.css +290 -0
  237. package/dist/styles/input-otp.css +138 -0
  238. package/dist/styles/input.css +99 -0
  239. package/dist/styles/kbd.css +59 -0
  240. package/dist/styles/label.css +89 -0
  241. package/dist/styles/mention-list.css +166 -0
  242. package/dist/styles/menubar.css +226 -0
  243. package/dist/styles/message-actions.css +83 -0
  244. package/dist/styles/message-response.css +176 -0
  245. package/dist/styles/message-scroller.css +136 -0
  246. package/dist/styles/message.css +310 -0
  247. package/dist/styles/navigation-menu.css +291 -0
  248. package/dist/styles/notice-banner.css +68 -0
  249. package/dist/styles/page-content.css +21 -0
  250. package/dist/styles/page-header.css +156 -0
  251. package/dist/styles/pagination.css +72 -0
  252. package/dist/styles/plan-outline.css +393 -0
  253. package/dist/styles/popover.css +140 -0
  254. package/dist/styles/progress.css +93 -0
  255. package/dist/styles/prompt-list.css +165 -0
  256. package/dist/styles/questionnaire.css +323 -0
  257. package/dist/styles/radio-group.css +120 -0
  258. package/dist/styles/reasoning-effort.css +157 -0
  259. package/dist/styles/resizable.css +108 -0
  260. package/dist/styles/result-card.css +215 -0
  261. package/dist/styles/review-card.css +235 -0
  262. package/dist/styles/rich-text-editor.css +169 -0
  263. package/dist/styles/schema-diagram.css +419 -0
  264. package/dist/styles/scroll-area.css +77 -0
  265. package/dist/styles/search-dialog.css +363 -0
  266. package/dist/styles/search-field.css +17 -0
  267. package/dist/styles/select.css +285 -0
  268. package/dist/styles/selection-slot.css +31 -0
  269. package/dist/styles/separator.css +206 -0
  270. package/dist/styles/settings-list.css +81 -0
  271. package/dist/styles/sheet.css +361 -0
  272. package/dist/styles/sidebar.css +363 -0
  273. package/dist/styles/skeleton.css +80 -0
  274. package/dist/styles/slider.css +142 -0
  275. package/dist/styles/spinner.css +39 -0
  276. package/dist/styles/state-panel-samples.css +41 -0
  277. package/dist/styles/state-panel.css +182 -0
  278. package/dist/styles/state-sample.css +285 -0
  279. package/dist/styles/switch.css +128 -0
  280. package/dist/styles/table.css +604 -0
  281. package/dist/styles/tabs.css +223 -0
  282. package/dist/styles/textarea.css +78 -0
  283. package/dist/styles/themes/chatgpt.css +125 -0
  284. package/dist/styles/themes/claude.css +125 -0
  285. package/dist/styles/themes/cloudflare.css +125 -0
  286. package/dist/styles/themes/discord.css +125 -0
  287. package/dist/styles/themes/github.css +125 -0
  288. package/dist/styles/themes/grok.css +125 -0
  289. package/dist/styles/themes/linear.css +126 -0
  290. package/dist/styles/themes/neutral.css +125 -0
  291. package/dist/styles/themes/notion.css +125 -0
  292. package/dist/styles/themes/perplexity.css +125 -0
  293. package/dist/styles/themes/raycast.css +125 -0
  294. package/dist/styles/themes/spotify.css +125 -0
  295. package/dist/styles/themes/stripe.css +125 -0
  296. package/dist/styles/themes/supabase.css +125 -0
  297. package/dist/styles/themes/tulipfarm.css +125 -0
  298. package/dist/styles/themes/vercel.css +126 -0
  299. package/dist/styles/toast.css +197 -0
  300. package/dist/styles/toggle-group.css +161 -0
  301. package/dist/styles/toggle.css +119 -0
  302. package/dist/styles/tool-activity.css +396 -0
  303. package/dist/styles/toolbar-status.css +52 -0
  304. package/dist/styles/tooltip.css +122 -0
  305. package/dist/styles/typed-text.css +17 -0
  306. package/package.json +443 -4
@@ -0,0 +1,637 @@
1
+ // The scroll engine behind MessageScroller: follows new output, pins a new
2
+ // turn under the top edge, keeps the reader's place when older messages are
3
+ // prepended, and reports which edges can still be scrolled to.
4
+ const epsilon = 0.5;
5
+ const autoscrollMs = 180;
6
+ export const scrollIntentKeys = new Set([
7
+ "ArrowDown",
8
+ "ArrowUp",
9
+ "End",
10
+ "Home",
11
+ "PageDown",
12
+ "PageUp",
13
+ " ",
14
+ ]);
15
+ const noEdges = { start: false, end: false };
16
+ const noVisibility = {
17
+ currentAnchorId: null,
18
+ visibleMessageIds: [],
19
+ };
20
+ function createStore(initial, equal) {
21
+ let value = initial;
22
+ const listeners = new Set();
23
+ return {
24
+ getSnapshot: () => value,
25
+ hasListeners: () => listeners.size > 0,
26
+ setSnapshot: (next) => {
27
+ if (equal(value, next))
28
+ return;
29
+ value = next;
30
+ listeners.forEach((listener) => listener());
31
+ },
32
+ subscribe: (listener, onFirst, onLast) => {
33
+ const first = listeners.size === 0;
34
+ listeners.add(listener);
35
+ if (first)
36
+ onFirst?.();
37
+ return () => {
38
+ listeners.delete(listener);
39
+ if (listeners.size === 0)
40
+ onLast?.();
41
+ };
42
+ },
43
+ };
44
+ }
45
+ const sameEdges = (a, b) => a.start === b.start && a.end === b.end;
46
+ const sameVisibility = (a, b) => a.currentAnchorId === b.currentAnchorId &&
47
+ a.visibleMessageIds.length === b.visibleMessageIds.length &&
48
+ a.visibleMessageIds.every((id, index) => id === b.visibleMessageIds[index]);
49
+ const pixels = (value) => {
50
+ const number = Number.parseFloat(value ?? "");
51
+ return Number.isFinite(number) ? number : 0;
52
+ };
53
+ function blockPadding(element) {
54
+ if (!element)
55
+ return { start: 0, end: 0 };
56
+ const style = getComputedStyle(element);
57
+ return {
58
+ start: pixels(style.paddingBlockStart || style.paddingTop),
59
+ end: pixels(style.paddingBlockEnd || style.paddingBottom),
60
+ };
61
+ }
62
+ function rowGap(element) {
63
+ if (!element)
64
+ return 0;
65
+ const style = getComputedStyle(element);
66
+ return pixels(style.rowGap === "normal" ? style.gap : style.rowGap);
67
+ }
68
+ const isAnchor = (element) => element?.dataset.scrollAnchor === "true";
69
+ function itemsOf(content, spacer) {
70
+ return Array.from(content.children).filter((child) => child instanceof HTMLElement && child !== spacer);
71
+ }
72
+ const offsetIn = (element, viewport) => element.getBoundingClientRect().top -
73
+ viewport.getBoundingClientRect().top +
74
+ viewport.scrollTop;
75
+ const topIn = (element, viewport) => element.getBoundingClientRect().top - viewport.getBoundingClientRect().top;
76
+ const maxScroll = (viewport) => Math.max(0, viewport.scrollHeight - viewport.clientHeight);
77
+ function contentHeight(content, spacer, viewport) {
78
+ const padding = blockPadding(content);
79
+ const viewportTop = viewport.getBoundingClientRect().top;
80
+ let height = padding.start + padding.end;
81
+ for (const item of itemsOf(content, spacer))
82
+ height = Math.max(height, item.getBoundingClientRect().bottom -
83
+ viewportTop +
84
+ viewport.scrollTop +
85
+ padding.end);
86
+ return height;
87
+ }
88
+ export class MessageScrollerController {
89
+ options;
90
+ stateStore = createStore(noEdges, sameEdges);
91
+ visibilityStore = createStore(noVisibility, sameVisibility);
92
+ pendingDefaultStore;
93
+ preserveScrollOnPrepend = true;
94
+ root = null;
95
+ viewport = null;
96
+ content = null;
97
+ spacer = null;
98
+ spacerGap = 0;
99
+ spacerHeight = 0;
100
+ mode;
101
+ itemCount = 0;
102
+ firstItem = null;
103
+ lastScrollTop = 0;
104
+ autoscrolling = false;
105
+ autoscrollTimer = null;
106
+ streamingTurn = null;
107
+ pendingMessage = null;
108
+ prependRestore = null;
109
+ defaultApplied = false;
110
+ messages = new Map();
111
+ visibleIds = new Set();
112
+ handledAnchors = new WeakSet();
113
+ observer = null;
114
+ stateFrame = null;
115
+ visibilityFrame = null;
116
+ pendingFrame = null;
117
+ constructor(options) {
118
+ this.options = options;
119
+ this.mode = options.autoScroll ? "following-bottom" : "free-scrolling";
120
+ this.pendingDefaultStore = createStore(options.defaultScrollPosition === "end" ||
121
+ options.defaultScrollPosition === "last-anchor", Object.is);
122
+ }
123
+ setOptions(next) {
124
+ if (next.defaultScrollPosition !== this.options.defaultScrollPosition)
125
+ this.defaultApplied = false;
126
+ this.options = next;
127
+ }
128
+ // Elements
129
+ setRoot = (element) => {
130
+ this.root = element;
131
+ if (element)
132
+ this.applyAttributes(this.stateStore.getSnapshot());
133
+ };
134
+ setViewport = (element) => {
135
+ this.viewport = element;
136
+ if (element)
137
+ this.applyAttributes(this.stateStore.getSnapshot());
138
+ };
139
+ getViewport = () => this.viewport;
140
+ setContent = (element) => {
141
+ this.content = element;
142
+ };
143
+ setSpacer = (element) => {
144
+ this.spacer = element;
145
+ this.spacerGap = rowGap(element?.parentElement ?? null);
146
+ };
147
+ registerMessage = (messageId, element, previous) => {
148
+ if (element) {
149
+ this.messages.set(messageId, element);
150
+ this.observer?.observe(element);
151
+ this.scheduleVisibility();
152
+ if (this.pendingMessage?.messageId === messageId)
153
+ this.schedulePendingFlush();
154
+ return;
155
+ }
156
+ if (previous && this.messages.get(messageId) === previous) {
157
+ this.messages.delete(messageId);
158
+ this.visibleIds.delete(messageId);
159
+ this.observer?.unobserve(previous);
160
+ this.scheduleVisibility();
161
+ }
162
+ };
163
+ // Public scrolling
164
+ scrollToStart = ({ behavior = "auto", } = {}) => {
165
+ if (!this.viewport)
166
+ return false;
167
+ this.setSpacerHeight(0);
168
+ this.streamingTurn = null;
169
+ this.mode = "free-scrolling";
170
+ this.scrollViewportTo(0, { behavior });
171
+ this.scheduleVisibility();
172
+ return true;
173
+ };
174
+ scrollToEnd = ({ behavior = "auto" } = {}) => {
175
+ const viewport = this.viewport;
176
+ if (!viewport)
177
+ return false;
178
+ this.setSpacerHeight(0);
179
+ this.streamingTurn = null;
180
+ this.mode = this.options.autoScroll ? "following-bottom" : "free-scrolling";
181
+ this.scrollViewportTo(maxScroll(viewport), {
182
+ autoscrolling: true,
183
+ behavior,
184
+ });
185
+ this.scheduleVisibility();
186
+ return true;
187
+ };
188
+ scrollToMessage = (messageId, options) => {
189
+ const element = this.messages.get(messageId);
190
+ if (element) {
191
+ this.markDefaultApplied();
192
+ this.pendingMessage = this.scrollToElement(element, options)
193
+ ? null
194
+ : { messageId, options };
195
+ return true;
196
+ }
197
+ if (this.itemCount === 0) {
198
+ this.pendingMessage = { messageId, options };
199
+ this.markDefaultApplied();
200
+ return true;
201
+ }
202
+ return false;
203
+ };
204
+ // Events from the parts
205
+ userScrollIntent = () => {
206
+ if (this.mode !== "free-scrolling") {
207
+ this.streamingTurn = null;
208
+ this.mode = "free-scrolling";
209
+ }
210
+ };
211
+ syncAfterScroll = () => {
212
+ this.commit();
213
+ this.scheduleVisibility();
214
+ this.captureFirstVisible();
215
+ };
216
+ handleResize = () => {
217
+ if (this.mode === "following-bottom" && this.options.autoScroll) {
218
+ this.scrollToEnd();
219
+ return;
220
+ }
221
+ const previousSpacer = this.spacerHeight;
222
+ if (this.reanchor()) {
223
+ if (this.options.autoScroll &&
224
+ previousSpacer > 0 &&
225
+ this.spacerHeight === 0)
226
+ this.scrollToEnd();
227
+ return;
228
+ }
229
+ this.scheduleCommit();
230
+ this.scheduleVisibility();
231
+ };
232
+ handleContentChange = () => {
233
+ const content = this.content;
234
+ if (!content)
235
+ return;
236
+ const items = itemsOf(content, this.spacer);
237
+ const previousCount = this.itemCount;
238
+ const previousFirst = this.firstItem;
239
+ this.itemCount = items.length;
240
+ this.firstItem = items[0] ?? null;
241
+ this.applyContentChange(items, previousCount, previousFirst);
242
+ this.captureFirstVisible();
243
+ };
244
+ // Mount and option changes
245
+ applyDefaultOrSettle = () => {
246
+ if (!this.applyDefault() && this.itemCount === 0)
247
+ this.pendingDefaultStore.setSnapshot(false);
248
+ };
249
+ followOnAutoScroll = () => {
250
+ if (this.options.autoScroll &&
251
+ this.mode === "following-bottom" &&
252
+ this.itemCount > 0) {
253
+ this.scrollToEnd();
254
+ return;
255
+ }
256
+ this.commit();
257
+ };
258
+ observeVisibility = () => {
259
+ if (!this.viewport || !this.visibilityStore.hasListeners())
260
+ return;
261
+ if (typeof IntersectionObserver === "undefined") {
262
+ this.scheduleVisibility();
263
+ return;
264
+ }
265
+ if (!this.observer) {
266
+ const inset = this.options.scrollMargin + this.options.scrollPreviousItemPeek;
267
+ this.observer = new IntersectionObserver((entries) => {
268
+ for (const entry of entries) {
269
+ const id = entry.target.dataset.messageId;
270
+ if (!id)
271
+ continue;
272
+ if (entry.isIntersecting)
273
+ this.visibleIds.add(id);
274
+ else
275
+ this.visibleIds.delete(id);
276
+ }
277
+ this.scheduleVisibility();
278
+ }, {
279
+ root: this.viewport,
280
+ rootMargin: `${-inset}px 0px 0px 0px`,
281
+ threshold: [0, 0.01, 0.5, 1],
282
+ });
283
+ }
284
+ this.messages.forEach((element) => this.observer?.observe(element));
285
+ this.scheduleVisibility();
286
+ };
287
+ unobserveVisibility = () => {
288
+ if (this.visibilityFrame !== null)
289
+ cancelAnimationFrame(this.visibilityFrame);
290
+ this.visibilityFrame = null;
291
+ this.observer?.disconnect();
292
+ this.observer = null;
293
+ this.visibleIds.clear();
294
+ this.visibilityStore.setSnapshot(noVisibility);
295
+ };
296
+ dispose = () => {
297
+ for (const frame of [
298
+ this.stateFrame,
299
+ this.visibilityFrame,
300
+ this.pendingFrame,
301
+ ])
302
+ if (frame !== null)
303
+ cancelAnimationFrame(frame);
304
+ this.stateFrame = this.visibilityFrame = this.pendingFrame = null;
305
+ if (this.autoscrollTimer !== null)
306
+ clearTimeout(this.autoscrollTimer);
307
+ this.autoscrollTimer = null;
308
+ this.observer?.disconnect();
309
+ this.observer = null;
310
+ };
311
+ // Internals
312
+ markDefaultApplied() {
313
+ this.defaultApplied = true;
314
+ this.pendingDefaultStore.setSnapshot(false);
315
+ }
316
+ applyContentChange(items, previousCount, previousFirst) {
317
+ if (this.flushPendingMessage())
318
+ return;
319
+ if (previousCount === 0) {
320
+ if (this.applyDefault() ||
321
+ (items.length > 0 && this.options.autoScroll && this.scrollToEnd()))
322
+ return;
323
+ this.commit();
324
+ this.scheduleVisibility();
325
+ return;
326
+ }
327
+ const firstIndex = previousFirst ? items.indexOf(previousFirst) : -1;
328
+ if (this.preserveScrollOnPrepend && firstIndex > 0) {
329
+ this.restorePrepend();
330
+ return;
331
+ }
332
+ if (items.length > previousCount) {
333
+ const anchor = items.slice(previousCount).find((item) => isAnchor(item));
334
+ if (anchor) {
335
+ const following = this.options.autoScroll && this.mode === "following-bottom";
336
+ if (following &&
337
+ items.slice(previousCount).filter((item) => isAnchor(item)).length > 1) {
338
+ this.scrollToEnd();
339
+ return;
340
+ }
341
+ this.pinAnchor(anchor);
342
+ return;
343
+ }
344
+ }
345
+ if (items.length === previousCount) {
346
+ const anchor = items.find((item) => isAnchor(item) && !this.handledAnchors.has(item));
347
+ if (anchor) {
348
+ this.pinAnchor(anchor);
349
+ return;
350
+ }
351
+ }
352
+ if (this.mode === "following-bottom" && this.options.autoScroll)
353
+ this.scrollToEnd();
354
+ else {
355
+ this.commit();
356
+ this.scheduleVisibility();
357
+ }
358
+ }
359
+ pinAnchor(anchor) {
360
+ this.scrollToElement(anchor, { align: "start" }, true);
361
+ this.handledAnchors.add(anchor);
362
+ }
363
+ applyDefault() {
364
+ const position = this.options.defaultScrollPosition;
365
+ if (this.defaultApplied || this.itemCount === 0)
366
+ return false;
367
+ let done = false;
368
+ if (position === "last-anchor") {
369
+ const content = this.content;
370
+ const viewport = this.viewport;
371
+ const anchor = content && viewport
372
+ ? itemsOf(content, this.spacer).filter(isAnchor).pop()
373
+ : undefined;
374
+ if (!content || !viewport || !anchor)
375
+ done = this.scrollToEnd();
376
+ else
377
+ done = this.measuringFrom(anchor, () => contentHeight(content, this.spacer, viewport) -
378
+ offsetIn(anchor, viewport) <=
379
+ viewport.clientHeight
380
+ ? this.scrollToEnd()
381
+ : this.scrollToElement(anchor, { align: "start" }, true));
382
+ }
383
+ else
384
+ done = position === "end" ? this.scrollToEnd() : this.scrollToStart();
385
+ if (done)
386
+ this.markDefaultApplied();
387
+ return done;
388
+ }
389
+ scrollToElement(element, options = {}, keepPreviousPeek = false) {
390
+ const content = this.content;
391
+ const viewport = this.viewport;
392
+ if (!content || !viewport || !content.contains(element))
393
+ return false;
394
+ const { align = "start", behavior = "auto", scrollMargin = this.options.scrollMargin, } = options;
395
+ const top = this.targetTop(element, align, keepPreviousPeek
396
+ ? scrollMargin + this.options.scrollPreviousItemPeek
397
+ : scrollMargin);
398
+ this.setSpacerHeight(top +
399
+ viewport.clientHeight -
400
+ this.measuringFrom(element, () => contentHeight(content, this.spacer, viewport)));
401
+ this.prependRestore = { element, viewportTop: topIn(element, viewport) };
402
+ this.mode = keepPreviousPeek ? "anchored-to-message" : "settling-jump";
403
+ this.streamingTurn = keepPreviousPeek ? element : null;
404
+ this.scrollViewportTo(top, { behavior });
405
+ this.scheduleVisibility();
406
+ return true;
407
+ }
408
+ /** Off-screen items report a 10rem estimate under `content-visibility: auto`; lay out `from` and every later item at real size while measuring. */
409
+ measuringFrom(from, measure) {
410
+ const items = this.content ? itemsOf(this.content, this.spacer) : [];
411
+ const later = items.slice(Math.max(0, items.indexOf(from)));
412
+ const previous = later.map((item) => item.style.contentVisibility);
413
+ for (const item of later)
414
+ item.style.contentVisibility = "visible";
415
+ try {
416
+ return measure();
417
+ }
418
+ finally {
419
+ later.forEach((item, index) => (item.style.contentVisibility = previous[index] ?? ""));
420
+ }
421
+ }
422
+ targetTop(element, align, margin) {
423
+ const viewport = this.viewport;
424
+ const offset = offsetIn(element, viewport);
425
+ const height = element.getBoundingClientRect().height;
426
+ const padding = blockPadding(this.spacer?.parentElement);
427
+ if (align === "center") {
428
+ const room = Math.max(0, viewport.clientHeight - padding.start - padding.end);
429
+ return offset - padding.start - (room - height) / 2 - margin;
430
+ }
431
+ if (align === "end")
432
+ return offset - viewport.clientHeight + height + padding.end + margin;
433
+ if (align === "nearest") {
434
+ const top = viewport.scrollTop + padding.start;
435
+ const bottom = viewport.scrollTop + viewport.clientHeight - padding.end;
436
+ if (offset >= top && offset + height <= bottom)
437
+ return viewport.scrollTop;
438
+ return offset < top
439
+ ? offset - padding.start - margin
440
+ : offset + height - viewport.clientHeight + padding.end + margin;
441
+ }
442
+ return offset - padding.start - margin;
443
+ }
444
+ reanchor() {
445
+ const element = this.streamingTurn;
446
+ if (!element || !element.isConnected || this.mode !== "anchored-to-message")
447
+ return false;
448
+ return this.scrollToElement(element, { align: "start" }, true);
449
+ }
450
+ flushPendingMessage() {
451
+ const pending = this.pendingMessage;
452
+ if (!pending)
453
+ return false;
454
+ const element = this.messages.get(pending.messageId);
455
+ if (!element || !this.scrollToElement(element, pending.options))
456
+ return false;
457
+ this.pendingMessage = null;
458
+ this.markDefaultApplied();
459
+ return true;
460
+ }
461
+ schedulePendingFlush() {
462
+ if (this.pendingFrame !== null)
463
+ return;
464
+ this.pendingFrame = requestAnimationFrame(() => {
465
+ this.pendingFrame = null;
466
+ if (this.flushPendingMessage())
467
+ this.captureFirstVisible();
468
+ });
469
+ }
470
+ setSpacerHeight(value) {
471
+ const spacer = this.spacer;
472
+ if (!spacer)
473
+ return;
474
+ const height = Math.max(0, Math.ceil(value));
475
+ if (this.spacerHeight === height)
476
+ return;
477
+ this.spacerHeight = height;
478
+ spacer.hidden = height === 0;
479
+ spacer.style.height = `${height}px`;
480
+ spacer.style.marginTop = height > 0 ? `${-this.spacerGap}px` : "";
481
+ }
482
+ setAutoscrolling(on) {
483
+ if (this.autoscrollTimer !== null)
484
+ clearTimeout(this.autoscrollTimer);
485
+ this.autoscrollTimer = null;
486
+ if (this.autoscrolling !== on) {
487
+ this.autoscrolling = on;
488
+ this.commit();
489
+ }
490
+ if (on)
491
+ this.autoscrollTimer = window.setTimeout(() => {
492
+ this.autoscrollTimer = null;
493
+ this.autoscrolling = false;
494
+ this.commit();
495
+ }, autoscrollMs);
496
+ }
497
+ scrollViewportTo(value, { behavior = "auto", autoscrolling = false, } = {}) {
498
+ const viewport = this.viewport;
499
+ if (!viewport)
500
+ return;
501
+ const top = Math.max(0, value);
502
+ if (Math.abs(viewport.scrollTop - top) <= epsilon) {
503
+ viewport.scrollTop = top;
504
+ this.commit();
505
+ return;
506
+ }
507
+ if (autoscrolling)
508
+ this.setAutoscrolling(true);
509
+ viewport.scrollTo({ top, behavior });
510
+ this.scheduleCommit();
511
+ }
512
+ restorePrepend() {
513
+ const restore = this.prependRestore;
514
+ const viewport = this.viewport;
515
+ if (!restore || !viewport || !restore.element.isConnected)
516
+ return false;
517
+ const delta = topIn(restore.element, viewport) - restore.viewportTop;
518
+ if (Math.abs(delta) <= epsilon)
519
+ return false;
520
+ viewport.scrollTop += delta;
521
+ restore.viewportTop = topIn(restore.element, viewport);
522
+ this.scheduleCommit();
523
+ this.scheduleVisibility();
524
+ return true;
525
+ }
526
+ captureFirstVisible() {
527
+ const content = this.content;
528
+ const viewport = this.viewport;
529
+ if (!content || !viewport) {
530
+ this.prependRestore = null;
531
+ return;
532
+ }
533
+ const bounds = viewport.getBoundingClientRect();
534
+ const first = itemsOf(content, this.spacer).find((item) => {
535
+ if (!item.dataset.messageId)
536
+ return false;
537
+ const rect = item.getBoundingClientRect();
538
+ return rect.bottom > bounds.top && rect.top < bounds.bottom;
539
+ });
540
+ this.prependRestore = first
541
+ ? { element: first, viewportTop: topIn(first, viewport) }
542
+ : null;
543
+ }
544
+ readEdges() {
545
+ const { content, viewport } = this;
546
+ if (!content || !viewport)
547
+ return noEdges;
548
+ const threshold = this.options.scrollEdgeThreshold;
549
+ const height = contentHeight(content, this.spacer, viewport);
550
+ return {
551
+ start: viewport.scrollTop > threshold,
552
+ end: height - viewport.scrollTop - viewport.clientHeight > threshold,
553
+ };
554
+ }
555
+ updateMode(edges) {
556
+ const top = this.viewport?.scrollTop ?? 0;
557
+ const scrolledUp = top < this.lastScrollTop - epsilon;
558
+ this.lastScrollTop = top;
559
+ if (this.options.autoScroll &&
560
+ !edges.end &&
561
+ this.mode !== "settling-jump" &&
562
+ this.mode !== "anchored-to-message")
563
+ this.mode = "following-bottom";
564
+ else if (this.mode === "following-bottom" &&
565
+ edges.end &&
566
+ scrolledUp &&
567
+ !this.autoscrolling)
568
+ this.mode = "free-scrolling";
569
+ }
570
+ applyAttributes(edges) {
571
+ const value = [edges.start && "start", edges.end && "end"]
572
+ .filter(Boolean)
573
+ .join(" ");
574
+ for (const element of [this.root, this.viewport]) {
575
+ if (!element)
576
+ continue;
577
+ if (value)
578
+ element.setAttribute("data-scrollable", value);
579
+ else
580
+ element.removeAttribute("data-scrollable");
581
+ element.toggleAttribute("data-autoscrolling", this.autoscrolling);
582
+ }
583
+ }
584
+ commit() {
585
+ const edges = this.readEdges();
586
+ this.updateMode(edges);
587
+ const shown = this.mode === "following-bottom" ? { ...edges, end: false } : edges;
588
+ this.applyAttributes(shown);
589
+ this.stateStore.setSnapshot(shown);
590
+ }
591
+ scheduleCommit() {
592
+ if (this.stateFrame !== null)
593
+ return;
594
+ this.stateFrame = requestAnimationFrame(() => {
595
+ this.stateFrame = null;
596
+ this.commit();
597
+ });
598
+ }
599
+ scheduleVisibility() {
600
+ if (!this.visibilityStore.hasListeners() || this.visibilityFrame !== null)
601
+ return;
602
+ this.visibilityFrame = requestAnimationFrame(() => {
603
+ this.visibilityFrame = null;
604
+ if (this.visibilityStore.hasListeners())
605
+ this.visibilityStore.setSnapshot(this.readVisibility());
606
+ });
607
+ }
608
+ readVisibility() {
609
+ const { content, viewport } = this;
610
+ if (!content || !viewport)
611
+ return noVisibility;
612
+ const bounds = viewport.getBoundingClientRect();
613
+ const line = bounds.top +
614
+ this.options.scrollMargin +
615
+ this.options.scrollPreviousItemPeek;
616
+ const measure = typeof IntersectionObserver === "undefined";
617
+ const visibleMessageIds = [];
618
+ let currentAnchorId = null;
619
+ for (const item of itemsOf(content, this.spacer)) {
620
+ const id = item.dataset.messageId;
621
+ if (!id)
622
+ continue;
623
+ const anchor = isAnchor(item);
624
+ const rect = anchor || measure ? item.getBoundingClientRect() : null;
625
+ const visible = measure && rect
626
+ ? rect.bottom > line && rect.top < bounds.bottom
627
+ : this.visibleIds.has(id);
628
+ if (visible)
629
+ visibleMessageIds.push(id);
630
+ if (anchor && rect && rect.top <= line + epsilon)
631
+ currentAnchorId = id;
632
+ }
633
+ return visibleMessageIds.length === 0 && currentAnchorId === null
634
+ ? noVisibility
635
+ : { currentAnchorId, visibleMessageIds };
636
+ }
637
+ }
@@ -0,0 +1,2 @@
1
+ import { Button } from "@base-ui/react/button";
2
+ export declare function Pressable({ nativeButton, render, ref, className, disabled, focusableWhenDisabled, onClick, ...props }: Button.Props): import("react").JSX.Element | null;
@@ -0,0 +1,29 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { Button } from "@base-ui/react/button";
4
+ import { useRender } from "@base-ui/react/use-render";
5
+ // Base UI gives non-native renders role="button"; navigation needs real link semantics.
6
+ export function Pressable({ nativeButton = true, render, ref, className, disabled = false, focusableWhenDisabled = false, onClick, ...props }) {
7
+ const link = useRender({
8
+ enabled: !nativeButton,
9
+ defaultTagName: "a",
10
+ render,
11
+ ref,
12
+ state: { disabled },
13
+ props: {
14
+ ...props,
15
+ className: typeof className === "function" ? className({ disabled }) : className,
16
+ "aria-disabled": disabled || undefined,
17
+ tabIndex: disabled && !focusableWhenDisabled ? -1 : props.tabIndex,
18
+ onClick: ((event) => {
19
+ if (disabled) {
20
+ event.preventDefault();
21
+ event.stopPropagation();
22
+ return;
23
+ }
24
+ onClick?.(event);
25
+ }),
26
+ },
27
+ });
28
+ return nativeButton ? (_jsx(Button, { ...props, render: render, ref: ref, className: className, disabled: disabled, focusableWhenDisabled: focusableWhenDisabled, onClick: onClick })) : (link);
29
+ }
@@ -0,0 +1,14 @@
1
+ import type { ThemedToken } from "shiki/core";
2
+ import { type CodeLanguage } from "./languages.js";
3
+ /** The Shiki language for a file name, from its whole name or its extension. */
4
+ export declare function languageForFilename(name: string): CodeLanguage | null;
5
+ /** Resolves a language name or alias, falling back to the file name. `null` means plain text. */
6
+ export declare function resolveCodeLanguage(language: string | undefined, filename?: string): CodeLanguage | null;
7
+ /** Longer code renders as plain text, so one huge file cannot freeze the page. */
8
+ export declare const maxHighlightedLength = 200000;
9
+ export declare function loadCodeLanguage(language: CodeLanguage): Promise<void>;
10
+ /** Languages a file needs beyond its own, such as the fences inside Markdown. */
11
+ export declare function embeddedLanguages(code: string, language: CodeLanguage): ("html" | "ruby" | "rel" | "go" | "log" | "move" | "d" | "r" | "abap" | "actionscript-3" | "ada" | "ahk" | "ahk2" | "angular-html" | "angular-ts" | "apache" | "apex" | "apl" | "applescript" | "ara" | "asciidoc" | "asm" | "astro" | "awk" | "ballerina" | "bat" | "beancount" | "berry" | "bibtex" | "bicep" | "bird2" | "blade" | "bsl" | "c" | "c3" | "cadence" | "cairo" | "chapel" | "clarity" | "clojure" | "cmake" | "cobol" | "codeowners" | "codeql" | "coffee" | "common-lisp" | "coq" | "cpp" | "crystal" | "csharp" | "css" | "csv" | "cue" | "cypher" | "dart" | "dax" | "desktop" | "diff" | "docker" | "dotenv" | "dream-maker" | "edge" | "elixir" | "elm" | "emacs-lisp" | "erb" | "erlang" | "fennel" | "fish" | "fluent" | "fortran-fixed-form" | "fortran-free-form" | "fsharp" | "gdresource" | "gdscript" | "gdshader" | "genie" | "gherkin" | "git-commit" | "git-rebase" | "gleam" | "glimmer-js" | "glimmer-ts" | "glsl" | "gn" | "gnuplot" | "graphql" | "groovy" | "hack" | "haml" | "handlebars" | "haskell" | "haxe" | "hcl" | "hjson" | "hlsl" | "html-derivative" | "http" | "hurl" | "hxml" | "hy" | "imba" | "ini" | "java" | "javascript" | "jinja" | "jison" | "json" | "json5" | "jsonc" | "jsonl" | "jsonnet" | "jssm" | "jsx" | "julia" | "just" | "kdl" | "kotlin" | "kusto" | "latex" | "lean" | "less" | "liquid" | "llvm" | "logo" | "lua" | "luau" | "make" | "markdown" | "marko" | "matlab" | "mdc" | "mdx" | "mermaid" | "mipsasm" | "mojo" | "moonbit" | "narrat" | "nextflow" | "nextflow-groovy" | "nginx" | "nim" | "nix" | "nsis" | "nushell" | "objective-c" | "objective-cpp" | "ocaml" | "odin" | "openscad" | "org" | "pascal" | "perl" | "php" | "pkl" | "plsql" | "po" | "polar" | "postcss" | "powerquery" | "powershell" | "prisma" | "prolog" | "proto" | "pug" | "puppet" | "purescript" | "python" | "qml" | "qmldir" | "qss" | "racket" | "raku" | "razor" | "rbs" | "reg" | "regexp" | "riscv" | "ron" | "rosmsg" | "rst" | "rust" | "sas" | "sass" | "scala" | "scheme" | "scss" | "sdbl" | "shaderlab" | "shellscript" | "shellsession" | "smalltalk" | "smithy" | "solidity" | "soy" | "sparql" | "splunk" | "sql" | "ssh-config" | "stata" | "stylus" | "surrealql" | "svelte" | "swift" | "system-verilog" | "systemd" | "talonscript" | "tasl" | "tcl" | "templ" | "terraform" | "tex" | "toml" | "ts-tags" | "tsv" | "tsx" | "turtle" | "twig" | "typescript" | "typespec" | "typst" | "v" | "vala" | "vb" | "verilog" | "vhdl" | "viml" | "vue" | "vue-html" | "vue-vine" | "vyper" | "wasm" | "wenyan" | "wgsl" | "wikitext" | "wit" | "wolfram" | "xml" | "xsl" | "yaml" | "zenscript" | "zig")[];
12
+ export declare function isLanguageReady(language: CodeLanguage): boolean;
13
+ /** Tokens when the language is already loaded, otherwise `null`. */
14
+ export declare function codeTokens(code: string, language: CodeLanguage): ThemedToken[][] | null;