@tulipfarm/sprout 0.3.1 → 0.5.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 (355) hide show
  1. package/README.md +75 -68
  2. package/dist/components/accordion.d.ts +2 -2
  3. package/dist/components/accordion.js +9 -9
  4. package/dist/components/agent-avatar.d.ts +27 -0
  5. package/dist/components/agent-avatar.js +67 -0
  6. package/dist/components/agent-fanout.d.ts +2 -2
  7. package/dist/components/agent-fanout.js +65 -87
  8. package/dist/components/alert-dialog.d.ts +8 -8
  9. package/dist/components/alert-dialog.js +21 -21
  10. package/dist/components/alert.d.ts +6 -6
  11. package/dist/components/alert.js +13 -13
  12. package/dist/components/aspect-ratio.d.ts +2 -2
  13. package/dist/components/aspect-ratio.js +2 -2
  14. package/dist/components/attachment.d.ts +18 -18
  15. package/dist/components/attachment.js +82 -115
  16. package/dist/components/avatar.d.ts +9 -17
  17. package/dist/components/avatar.js +14 -51
  18. package/dist/components/badge.d.ts +4 -4
  19. package/dist/components/badge.js +18 -18
  20. package/dist/components/board.d.ts +107 -0
  21. package/dist/components/board.js +640 -0
  22. package/dist/components/brand-icon.d.ts +6 -6
  23. package/dist/components/brand-icon.js +55 -63
  24. package/dist/components/breadcrumb.d.ts +11 -11
  25. package/dist/components/breadcrumb.js +16 -18
  26. package/dist/components/button-group.d.ts +6 -6
  27. package/dist/components/button-group.js +14 -14
  28. package/dist/components/button.d.ts +13 -13
  29. package/dist/components/button.js +31 -57
  30. package/dist/components/calendar.d.ts +5 -5
  31. package/dist/components/calendar.js +44 -53
  32. package/dist/components/canvas.d.ts +4 -4
  33. package/dist/components/canvas.js +39 -46
  34. package/dist/components/card.d.ts +7 -7
  35. package/dist/components/card.js +17 -17
  36. package/dist/components/carousel.d.ts +7 -7
  37. package/dist/components/carousel.js +57 -75
  38. package/dist/components/chart.d.ts +5 -112
  39. package/dist/components/chart.js +5 -172
  40. package/dist/components/chat-confirmation.d.ts +10 -10
  41. package/dist/components/chat-confirmation.js +32 -38
  42. package/dist/components/chat-input.d.ts +4 -4
  43. package/dist/components/chat-input.js +74 -81
  44. package/dist/components/chat-plan.d.ts +2 -2
  45. package/dist/components/chat-plan.js +15 -20
  46. package/dist/components/chat-sources.d.ts +8 -8
  47. package/dist/components/chat-sources.js +18 -27
  48. package/dist/components/checkbox.d.ts +1 -1
  49. package/dist/components/checkbox.js +9 -9
  50. package/dist/components/choice-list.d.ts +2 -2
  51. package/dist/components/choice-list.js +16 -18
  52. package/dist/components/code-block.d.ts +5 -5
  53. package/dist/components/code-block.js +26 -26
  54. package/dist/components/collapsible.d.ts +1 -1
  55. package/dist/components/collapsible.js +7 -11
  56. package/dist/components/combobox.d.ts +12 -6
  57. package/dist/components/combobox.js +27 -26
  58. package/dist/components/command.d.ts +13 -13
  59. package/dist/components/command.js +61 -73
  60. package/dist/components/context-menu.d.ts +5 -5
  61. package/dist/components/context-menu.js +22 -22
  62. package/dist/components/dialog.d.ts +6 -6
  63. package/dist/components/dialog.js +23 -28
  64. package/dist/components/direction.d.ts +1 -1
  65. package/dist/components/direction.js +1 -1
  66. package/dist/components/dropdown-menu.d.ts +5 -5
  67. package/dist/components/dropdown-menu.js +20 -20
  68. package/dist/components/field.d.ts +11 -11
  69. package/dist/components/field.js +41 -61
  70. package/dist/components/file-explorer.d.ts +2 -2
  71. package/dist/components/file-explorer.js +89 -105
  72. package/dist/components/file-viewer.d.ts +10 -10
  73. package/dist/components/file-viewer.js +112 -140
  74. package/dist/components/form.d.ts +7 -7
  75. package/dist/components/form.js +24 -28
  76. package/dist/components/glass-banner.d.ts +3 -3
  77. package/dist/components/glass-banner.js +21 -24
  78. package/dist/components/gradient-banner.d.ts +7 -7
  79. package/dist/components/gradient-banner.js +13 -22
  80. package/dist/components/hover-card.d.ts +4 -4
  81. package/dist/components/hover-card.js +6 -6
  82. package/dist/components/input-group.d.ts +18 -18
  83. package/dist/components/input-group.js +25 -39
  84. package/dist/components/input-otp.d.ts +14 -10
  85. package/dist/components/input-otp.js +15 -15
  86. package/dist/components/input.d.ts +3 -3
  87. package/dist/components/input.js +7 -7
  88. package/dist/components/kbd.d.ts +3 -3
  89. package/dist/components/kbd.js +5 -5
  90. package/dist/components/label.d.ts +2 -2
  91. package/dist/components/label.js +2 -2
  92. package/dist/components/mention-list.d.ts +3 -3
  93. package/dist/components/mention-list.js +10 -10
  94. package/dist/components/menubar.d.ts +6 -6
  95. package/dist/components/menubar.js +22 -22
  96. package/dist/components/message-actions.d.ts +2 -2
  97. package/dist/components/message-actions.js +29 -31
  98. package/dist/components/message-response.d.ts +3 -3
  99. package/dist/components/message-response.js +18 -18
  100. package/dist/components/message-scroller.d.ts +12 -12
  101. package/dist/components/message-scroller.js +44 -56
  102. package/dist/components/message.d.ts +11 -11
  103. package/dist/components/message.js +25 -25
  104. package/dist/components/navigation-menu.d.ts +2 -2
  105. package/dist/components/navigation-menu.js +15 -15
  106. package/dist/components/notice-banner.d.ts +2 -2
  107. package/dist/components/notice-banner.js +2 -2
  108. package/dist/components/page-content.d.ts +2 -2
  109. package/dist/components/page-content.js +2 -2
  110. package/dist/components/page-header.d.ts +1 -1
  111. package/dist/components/page-header.js +17 -17
  112. package/dist/components/pagination.d.ts +9 -9
  113. package/dist/components/pagination.js +17 -17
  114. package/dist/components/plan-outline.d.ts +2 -2
  115. package/dist/components/plan-outline.js +43 -60
  116. package/dist/components/popover.d.ts +6 -6
  117. package/dist/components/popover.js +15 -15
  118. package/dist/components/presence-cursor.d.ts +2 -2
  119. package/dist/components/presence-cursor.js +3 -3
  120. package/dist/components/progress.d.ts +1 -1
  121. package/dist/components/progress.js +8 -8
  122. package/dist/components/prompt-list.d.ts +3 -3
  123. package/dist/components/prompt-list.js +6 -6
  124. package/dist/components/questionnaire.d.ts +19 -19
  125. package/dist/components/questionnaire.js +183 -242
  126. package/dist/components/radio-group.d.ts +2 -2
  127. package/dist/components/radio-group.js +6 -6
  128. package/dist/components/reasoning-effort.d.ts +3 -3
  129. package/dist/components/reasoning-effort.js +9 -12
  130. package/dist/components/record-list.d.ts +66 -0
  131. package/dist/components/record-list.js +151 -0
  132. package/dist/components/resizable.d.ts +5 -5
  133. package/dist/components/resizable.js +76 -102
  134. package/dist/components/result-card.d.ts +4 -4
  135. package/dist/components/result-card.js +42 -53
  136. package/dist/components/result-notice.d.ts +18 -0
  137. package/dist/components/result-notice.js +29 -0
  138. package/dist/components/review-card.d.ts +6 -6
  139. package/dist/components/review-card.js +18 -18
  140. package/dist/components/rich-text-editor.d.ts +5 -5
  141. package/dist/components/rich-text-editor.js +56 -72
  142. package/dist/components/schema-diagram.d.ts +3 -3
  143. package/dist/components/schema-diagram.js +39 -53
  144. package/dist/components/scroll-area.d.ts +3 -3
  145. package/dist/components/scroll-area.js +10 -14
  146. package/dist/components/search-dialog.d.ts +6 -6
  147. package/dist/components/search-dialog.js +16 -16
  148. package/dist/components/search-field.d.ts +1 -1
  149. package/dist/components/search-field.js +11 -11
  150. package/dist/components/select.d.ts +5 -9
  151. package/dist/components/select.js +20 -20
  152. package/dist/components/selection-slot.d.ts +2 -2
  153. package/dist/components/selection-slot.js +6 -9
  154. package/dist/components/separator.d.ts +15 -15
  155. package/dist/components/separator.js +22 -27
  156. package/dist/components/settings-list.d.ts +3 -3
  157. package/dist/components/settings-list.js +5 -5
  158. package/dist/components/sheet.d.ts +6 -6
  159. package/dist/components/sheet.js +27 -27
  160. package/dist/components/sidebar.d.ts +22 -22
  161. package/dist/components/sidebar.js +57 -58
  162. package/dist/components/skeleton.d.ts +5 -5
  163. package/dist/components/skeleton.js +14 -21
  164. package/dist/components/slider.d.ts +2 -2
  165. package/dist/components/slider.js +6 -6
  166. package/dist/components/spinner.d.ts +7 -7
  167. package/dist/components/spinner.js +22 -26
  168. package/dist/components/sql-viewer.d.ts +14 -14
  169. package/dist/components/sql-viewer.js +99 -128
  170. package/dist/components/state-panel-samples.d.ts +2 -2
  171. package/dist/components/state-panel-samples.js +209 -245
  172. package/dist/components/state-panel.d.ts +2 -35
  173. package/dist/components/state-panel.js +2 -29
  174. package/dist/components/state-sample.d.ts +8 -8
  175. package/dist/components/state-sample.js +18 -31
  176. package/dist/components/switch.d.ts +3 -3
  177. package/dist/components/switch.js +7 -7
  178. package/dist/components/table-nuqs.d.ts +2 -2
  179. package/dist/components/table-nuqs.js +11 -12
  180. package/dist/components/table.d.ts +4 -4
  181. package/dist/components/table.js +166 -235
  182. package/dist/components/tabs.d.ts +2 -2
  183. package/dist/components/tabs.js +12 -12
  184. package/dist/components/textarea.d.ts +2 -2
  185. package/dist/components/textarea.js +6 -8
  186. package/dist/components/timeline.d.ts +3 -0
  187. package/dist/components/timeline.js +3 -0
  188. package/dist/components/toast.d.ts +2 -2
  189. package/dist/components/toast.js +19 -25
  190. package/dist/components/toggle-group.d.ts +4 -4
  191. package/dist/components/toggle-group.js +18 -20
  192. package/dist/components/toggle.d.ts +4 -4
  193. package/dist/components/toggle.js +8 -8
  194. package/dist/components/tool-activity.d.ts +1 -1
  195. package/dist/components/tool-activity.js +83 -110
  196. package/dist/components/toolbar-status.d.ts +2 -2
  197. package/dist/components/toolbar-status.js +7 -7
  198. package/dist/components/tooltip.d.ts +5 -5
  199. package/dist/components/tooltip.js +12 -11
  200. package/dist/components/typed-text.d.ts +2 -2
  201. package/dist/components/typed-text.js +10 -12
  202. package/dist/components/view-switcher.d.ts +51 -0
  203. package/dist/components/view-switcher.js +114 -0
  204. package/dist/index.d.ts +99 -93
  205. package/dist/index.js +99 -93
  206. package/dist/internal/agent-avatar-artwork.d.ts +4 -0
  207. package/dist/internal/agent-avatar-artwork.js +219 -0
  208. package/dist/internal/chart-kit.d.ts +142 -0
  209. package/dist/internal/chart-kit.js +200 -0
  210. package/dist/internal/charts/bars.d.ts +61 -0
  211. package/dist/internal/charts/bars.js +86 -0
  212. package/dist/internal/charts/lines.d.ts +67 -0
  213. package/dist/internal/charts/lines.js +70 -0
  214. package/dist/internal/charts/more.d.ts +77 -0
  215. package/dist/internal/charts/more.js +68 -0
  216. package/dist/internal/charts/parts.d.ts +54 -0
  217. package/dist/internal/charts/parts.js +82 -0
  218. package/dist/internal/charts/timeline-dates.d.ts +38 -0
  219. package/dist/internal/charts/timeline-dates.js +113 -0
  220. package/dist/internal/charts/timeline-window.d.ts +31 -0
  221. package/dist/internal/charts/timeline-window.js +26 -0
  222. package/dist/internal/charts/timeline.d.ts +72 -0
  223. package/dist/internal/charts/timeline.js +287 -0
  224. package/dist/internal/classes.d.ts +1 -1
  225. package/dist/internal/classes.js +7 -9
  226. package/dist/internal/code-tokens.d.ts +2 -2
  227. package/dist/internal/code-tokens.js +12 -12
  228. package/dist/internal/fieldset.d.ts +1 -1
  229. package/dist/internal/fieldset.js +6 -6
  230. package/dist/internal/file-kind.d.ts +1 -1
  231. package/dist/internal/file-kind.js +43 -51
  232. package/dist/internal/flow.d.ts +4 -4
  233. package/dist/internal/flow.js +20 -23
  234. package/dist/internal/hash.d.ts +1 -0
  235. package/dist/internal/hash.js +9 -0
  236. package/dist/internal/languages.js +999 -999
  237. package/dist/internal/message-scroller.d.ts +3 -3
  238. package/dist/internal/message-scroller.js +64 -112
  239. package/dist/internal/pressable.d.ts +1 -1
  240. package/dist/internal/pressable.js +7 -7
  241. package/dist/internal/state-panel.d.ts +35 -0
  242. package/dist/internal/state-panel.js +22 -0
  243. package/dist/internal/syntax.d.ts +2 -2
  244. package/dist/internal/syntax.js +36 -47
  245. package/dist/styles/accordion.css +3 -5
  246. package/dist/styles/agent-fanout.css +64 -60
  247. package/dist/styles/alert-dialog.css +12 -26
  248. package/dist/styles/alert.css +10 -15
  249. package/dist/styles/attachment.css +56 -98
  250. package/dist/styles/avatar.css +14 -33
  251. package/dist/styles/badge.css +11 -36
  252. package/dist/styles/board.css +571 -0
  253. package/dist/styles/brand-icon.css +7 -8
  254. package/dist/styles/breadcrumb.css +4 -7
  255. package/dist/styles/button-group.css +9 -15
  256. package/dist/styles/button.css +45 -175
  257. package/dist/styles/calendar.css +24 -36
  258. package/dist/styles/canvas.css +2 -3
  259. package/dist/styles/card.css +7 -23
  260. package/dist/styles/carousel.css +11 -12
  261. package/dist/styles/chart.css +215 -31
  262. package/dist/styles/chat-input.css +15 -39
  263. package/dist/styles/chat-plan.css +3 -4
  264. package/dist/styles/chat-sources.css +4 -6
  265. package/dist/styles/checkbox.css +11 -25
  266. package/dist/styles/choice-list.css +17 -31
  267. package/dist/styles/code-block.css +11 -25
  268. package/dist/styles/collapsible.css +10 -15
  269. package/dist/styles/combobox.css +24 -17
  270. package/dist/styles/command.css +8 -9
  271. package/dist/styles/context-menu.css +4 -2
  272. package/dist/styles/dialog.css +5 -16
  273. package/dist/styles/dropdown-menu.css +4 -2
  274. package/dist/styles/field.css +15 -21
  275. package/dist/styles/file-explorer.css +19 -33
  276. package/dist/styles/file-viewer.css +13 -17
  277. package/dist/styles/form.css +3 -3
  278. package/dist/styles/glass-banner.css +12 -47
  279. package/dist/styles/gradient-banner.css +16 -39
  280. package/dist/styles/hover-card.css +9 -15
  281. package/dist/styles/index.css +121 -112
  282. package/dist/styles/input-group.css +61 -69
  283. package/dist/styles/input-otp.css +18 -9
  284. package/dist/styles/input.css +19 -11
  285. package/dist/styles/kbd.css +1 -1
  286. package/dist/styles/label.css +9 -30
  287. package/dist/styles/mention-list.css +6 -8
  288. package/dist/styles/menubar.css +9 -8
  289. package/dist/styles/message-actions.css +1 -1
  290. package/dist/styles/message-response.css +3 -5
  291. package/dist/styles/message-scroller.css +14 -21
  292. package/dist/styles/message.css +23 -60
  293. package/dist/styles/navigation-menu.css +15 -30
  294. package/dist/styles/notice-banner.css +1 -2
  295. package/dist/styles/page-header.css +11 -10
  296. package/dist/styles/pagination.css +12 -6
  297. package/dist/styles/plan-outline.css +20 -37
  298. package/dist/styles/popover.css +10 -19
  299. package/dist/styles/presence-cursor.css +1 -2
  300. package/dist/styles/progress.css +2 -4
  301. package/dist/styles/prompt-list.css +6 -17
  302. package/dist/styles/questionnaire.css +16 -40
  303. package/dist/styles/radio-group.css +12 -23
  304. package/dist/styles/reasoning-effort.css +8 -15
  305. package/dist/styles/record-list.css +243 -0
  306. package/dist/styles/resizable.css +10 -11
  307. package/dist/styles/result-card.css +4 -4
  308. package/dist/styles/result-notice.css +80 -0
  309. package/dist/styles/review-card.css +2 -3
  310. package/dist/styles/rich-text-editor.css +4 -5
  311. package/dist/styles/schema-diagram.css +12 -17
  312. package/dist/styles/scroll-area.css +8 -10
  313. package/dist/styles/search-dialog.css +1 -5
  314. package/dist/styles/search-field.css +1 -2
  315. package/dist/styles/select.css +15 -19
  316. package/dist/styles/selection-slot.css +1 -2
  317. package/dist/styles/separator.css +7 -8
  318. package/dist/styles/settings-list.css +1 -2
  319. package/dist/styles/sheet.css +40 -73
  320. package/dist/styles/sidebar.css +32 -34
  321. package/dist/styles/skeleton.css +9 -11
  322. package/dist/styles/slider.css +11 -21
  323. package/dist/styles/spinner.css +2 -2
  324. package/dist/styles/sql-viewer.css +29 -29
  325. package/dist/styles/state-panel.css +8 -9
  326. package/dist/styles/state-sample.css +20 -33
  327. package/dist/styles/switch.css +11 -18
  328. package/dist/styles/table.css +62 -81
  329. package/dist/styles/tabs.css +13 -19
  330. package/dist/styles/textarea.css +3 -3
  331. package/dist/styles/themes/chatgpt.css +11 -23
  332. package/dist/styles/themes/claude.css +12 -24
  333. package/dist/styles/themes/cloudflare.css +11 -23
  334. package/dist/styles/themes/discord.css +11 -23
  335. package/dist/styles/themes/github.css +11 -23
  336. package/dist/styles/themes/grok.css +11 -23
  337. package/dist/styles/themes/linear.css +12 -25
  338. package/dist/styles/themes/neutral.css +11 -23
  339. package/dist/styles/themes/notion.css +11 -23
  340. package/dist/styles/themes/perplexity.css +11 -23
  341. package/dist/styles/themes/raycast.css +11 -23
  342. package/dist/styles/themes/spotify.css +11 -23
  343. package/dist/styles/themes/stripe.css +11 -23
  344. package/dist/styles/themes/supabase.css +11 -23
  345. package/dist/styles/themes/tulipfarm.css +9 -10
  346. package/dist/styles/themes/vercel.css +12 -25
  347. package/dist/styles/timeline.css +276 -0
  348. package/dist/styles/toast.css +13 -20
  349. package/dist/styles/toggle-group.css +23 -29
  350. package/dist/styles/toggle.css +14 -14
  351. package/dist/styles/tool-activity.css +7 -13
  352. package/dist/styles/toolbar-status.css +2 -7
  353. package/dist/styles/tooltip.css +15 -21
  354. package/dist/styles/view-switcher.css +297 -0
  355. package/package.json +25 -1
@@ -1,23 +1,23 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { createContext, Suspense, useContext, useEffect, useId, useMemo, useRef, useState, } from "react";
3
- import { ArrowUpRight } from "reicon-react/icons/ArrowUpRight";
4
- import { ChevronLeft } from "reicon-react/icons/ChevronLeft";
5
- import { ChevronRight } from "reicon-react/icons/ChevronRight";
6
- import { Download } from "reicon-react/icons/Download";
7
- import { MoreH } from "reicon-react/icons/MoreH";
8
- import { SidebarRight } from "reicon-react/icons/SidebarRight";
9
- import { X } from "reicon-react/icons/X";
10
- import { cn } from "../internal/classes.js";
11
- import { fileKind } from "../internal/file-kind.js";
12
- import { Button } from "./button.js";
13
- import { CodeBlock, languageForFilename } from "./code-block.js";
14
- import { Dialog, DialogClose, DialogContent, DialogDescription, DialogTitle, } from "./dialog.js";
15
- import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "./dropdown-menu.js";
16
- import { Spinner } from "./spinner.js";
17
- import { StatePanel } from "./state-panel.js";
2
+ import { createContext, Suspense, useContext, useEffect, useId, useMemo, useRef, useState, } from 'react';
3
+ import { ArrowUpRight } from 'reicon-react/icons/ArrowUpRight';
4
+ import { ChevronLeft } from 'reicon-react/icons/ChevronLeft';
5
+ import { ChevronRight } from 'reicon-react/icons/ChevronRight';
6
+ import { Download } from 'reicon-react/icons/Download';
7
+ import { MoreH } from 'reicon-react/icons/MoreH';
8
+ import { SidebarRight } from 'reicon-react/icons/SidebarRight';
9
+ import { X } from 'reicon-react/icons/X';
10
+ import { cn } from '../internal/classes.js';
11
+ import { fileKind } from '../internal/file-kind.js';
12
+ import { Button } from './button.js';
13
+ import { CodeBlock, languageForFilename } from './code-block.js';
14
+ import { Dialog, DialogClose, DialogContent, DialogDescription, DialogTitle, } from './dialog.js';
15
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from './dropdown-menu.js';
16
+ import { Spinner } from './spinner.js';
17
+ import { StatePanel } from '../internal/state-panel.js';
18
18
  const RenderersContext = createContext({});
19
19
  /** Registers file renderers once for every viewer below it. */
20
- export function FileRenderersProvider({ renderers, children, }) {
20
+ export function FileRenderersProvider({ renderers, children }) {
21
21
  const parent = useContext(RenderersContext);
22
22
  const value = useMemo(() => ({ ...parent, ...renderers }), [parent, renderers]);
23
23
  return _jsx(RenderersContext, { value: value, children: children });
@@ -39,8 +39,8 @@ export function googleDocument(url) {
39
39
  catch {
40
40
  return null;
41
41
  }
42
- if (parsed.protocol !== "https:" ||
43
- parsed.hostname !== "docs.google.com" ||
42
+ if (parsed.protocol !== 'https:' ||
43
+ parsed.hostname !== 'docs.google.com' ||
44
44
  parsed.username ||
45
45
  parsed.password ||
46
46
  parsed.port)
@@ -50,38 +50,35 @@ export function googleDocument(url) {
50
50
  return null;
51
51
  const [, family, id] = match;
52
52
  const canonical = `https://docs.google.com/${family}/d/${id}`;
53
- const endpoint = family === "presentation" ? "embed" : "preview";
53
+ const endpoint = family === 'presentation' ? 'embed' : 'preview';
54
54
  return {
55
55
  url: `${canonical}/edit`,
56
- embedUrl: `${canonical}/${endpoint}${family === "spreadsheets" ? "?widget=true&headers=false" : ""}`,
57
- mediaType: `application/vnd.google-apps.${family === "spreadsheets" ? "spreadsheet" : family}`,
56
+ embedUrl: `${canonical}/${endpoint}${family === 'spreadsheets' ? '?widget=true&headers=false' : ''}`,
57
+ mediaType: `application/vnd.google-apps.${family === 'spreadsheets' ? 'spreadsheet' : family}`,
58
58
  };
59
59
  }
60
60
  /** Reads the document link from a .gdoc, .gsheet, or .gslides shortcut. */
61
61
  export function googleShortcut(text) {
62
62
  const value = JSON.parse(text);
63
- if (typeof value !== "object" ||
64
- value === null ||
65
- !("url" in value) ||
66
- typeof value.url !== "string")
67
- throw new Error("This Google shortcut does not contain a document link.");
63
+ if (typeof value !== 'object' || value === null || !('url' in value) || typeof value.url !== 'string')
64
+ throw new Error('This Google shortcut does not contain a document link.');
68
65
  const document = googleDocument(value.url);
69
66
  if (!document)
70
- throw new Error("This shortcut does not contain a supported Google Docs, Sheets, or Slides link.");
67
+ throw new Error('This shortcut does not contain a supported Google Docs, Sheets, or Slides link.');
71
68
  return document.url;
72
69
  }
73
70
  const rasterTypes = new Set([
74
- "image/png",
75
- "image/jpeg",
76
- "image/gif",
77
- "image/webp",
78
- "image/avif",
79
- "image/bmp",
80
- "image/x-icon",
81
- "image/vnd.microsoft.icon",
82
- "image/heic",
83
- "image/heif",
84
- "image/tiff",
71
+ 'image/png',
72
+ 'image/jpeg',
73
+ 'image/gif',
74
+ 'image/webp',
75
+ 'image/avif',
76
+ 'image/bmp',
77
+ 'image/x-icon',
78
+ 'image/vnd.microsoft.icon',
79
+ 'image/heic',
80
+ 'image/heif',
81
+ 'image/tiff',
85
82
  ]);
86
83
  /**
87
84
  * The type to give a same-origin blob URL. Stored media types are
@@ -90,15 +87,13 @@ const rasterTypes = new Set([
90
87
  */
91
88
  export function renderableType(name, mediaType) {
92
89
  const kind = fileKind(name, mediaType);
93
- if (kind === "pdf")
94
- return "application/pdf";
90
+ if (kind === 'pdf')
91
+ return 'application/pdf';
95
92
  const type = mediaType.trim().toLowerCase();
96
- return kind === "image" && rasterTypes.has(type)
97
- ? type
98
- : "application/octet-stream";
93
+ return kind === 'image' && rasterTypes.has(type) ? type : 'application/octet-stream';
99
94
  }
100
95
  function useFileContent(file, attempt = 0, enabled = true) {
101
- const [loaded, setLoaded] = useState({ kind: "loading" });
96
+ const [loaded, setLoaded] = useState({ kind: 'loading' });
102
97
  const { source, name, mediaType } = file;
103
98
  useEffect(() => {
104
99
  if (!enabled)
@@ -107,35 +102,35 @@ function useFileContent(file, attempt = 0, enabled = true) {
107
102
  let objectUrl;
108
103
  const controller = new AbortController();
109
104
  const timeout = setTimeout(() => controller.abort(), 15000);
110
- setLoaded({ kind: "loading" });
105
+ setLoaded({ kind: 'loading' });
111
106
  const load = async () => {
112
107
  let blob;
113
- if (source.kind === "local")
108
+ if (source.kind === 'local')
114
109
  blob = source.file;
115
110
  else {
116
111
  const response = await fetch(source.url, { signal: controller.signal });
117
112
  if (!response.ok)
118
- throw new Error("The file could not be loaded. Check the link and try again.");
113
+ throw new Error('The file could not be loaded. Check the link and try again.');
119
114
  blob = await response.blob();
120
115
  }
121
116
  if (blob.size > maxViewableFileBytes)
122
- throw new Error("This file is larger than the 50 MB local viewing limit.");
117
+ throw new Error('This file is larger than the 50 MB local viewing limit.');
123
118
  const bytes = await blob.arrayBuffer();
124
119
  if (cancelled)
125
120
  return;
126
121
  objectUrl = URL.createObjectURL(new Blob([bytes], {
127
122
  type: renderableType(name, blob.type || mediaType),
128
123
  }));
129
- setLoaded({ kind: "ready", bytes, url: objectUrl });
124
+ setLoaded({ kind: 'ready', bytes, url: objectUrl });
130
125
  };
131
126
  void load()
132
127
  .catch((error) => {
133
128
  if (!cancelled)
134
129
  setLoaded({
135
- kind: "error",
136
- message: error instanceof Error && error.name !== "AbortError"
130
+ kind: 'error',
131
+ message: error instanceof Error && error.name !== 'AbortError'
137
132
  ? error.message
138
- : "The file took too long to load. Try again.",
133
+ : 'The file took too long to load. Try again.',
139
134
  });
140
135
  })
141
136
  .finally(() => clearTimeout(timeout));
@@ -151,11 +146,9 @@ function useFileContent(file, attempt = 0, enabled = true) {
151
146
  }
152
147
  const ownsArrowKeys = 'input, textarea, select, iframe, [contenteditable="true"], [role="region"], [role="slider"]';
153
148
  /** A full-screen dialog that opens a local or linked file, or steps through several. */
154
- export function FileViewer({ file, files, index = 0, onIndexChange, open, onOpenChange, renderers, finalFocus, actions, menu, panel, panelLabel = "Details", panelOpen, defaultPanelOpen, onPanelOpenChange, }) {
149
+ export function FileViewer({ file, files, index = 0, onIndexChange, open, onOpenChange, renderers, finalFocus, actions, menu, panel, panelLabel = 'Details', panelOpen, defaultPanelOpen, onPanelOpenChange, }) {
155
150
  const panelId = useId();
156
- const [ownPanelOpen, setOwnPanelOpen] = useState(() => defaultPanelOpen ??
157
- (typeof window !== "undefined" &&
158
- !!window.matchMedia?.("(min-width: 1024px)").matches));
151
+ const [ownPanelOpen, setOwnPanelOpen] = useState(() => defaultPanelOpen ?? (typeof window !== 'undefined' && !!window.matchMedia?.('(min-width: 1024px)').matches));
159
152
  const panelShown = !!panel && (panelOpen ?? ownPanelOpen);
160
153
  const togglePanel = () => {
161
154
  setOwnPanelOpen(!panelShown);
@@ -165,10 +158,9 @@ export function FileViewer({ file, files, index = 0, onIndexChange, open, onOpen
165
158
  const count = files?.length ?? 0;
166
159
  const refocus = useRef(null);
167
160
  const goTo = (target) => () => {
168
- const button = document.activeElement?.closest("[data-step]");
169
- const popup = button?.closest("[data-file-viewer]");
170
- refocus.current =
171
- button && popup ? { popup, direction: button.dataset.step ?? "" } : null;
161
+ const button = document.activeElement?.closest('[data-step]');
162
+ const popup = button?.closest('[data-file-viewer]');
163
+ refocus.current = button && popup ? { popup, direction: button.dataset.step ?? '' } : null;
172
164
  onIndexChange?.(target);
173
165
  };
174
166
  const step = files && count > 1
@@ -186,27 +178,24 @@ export function FileViewer({ file, files, index = 0, onIndexChange, open, onOpen
186
178
  if (!pending)
187
179
  return;
188
180
  const enabled = ":not(:disabled):not([aria-disabled='true'])";
189
- const target = pending.popup.querySelector(`[data-step="${pending.direction}"]${enabled}`) ?? pending.popup.querySelector(`[data-step]${enabled}`);
181
+ const target = pending.popup.querySelector(`[data-step="${pending.direction}"]${enabled}`) ??
182
+ pending.popup.querySelector(`[data-step]${enabled}`);
190
183
  target?.focus();
191
184
  }, [index]);
192
185
  const handleKeyDown = (event) => {
193
- if (!step ||
194
- event.defaultPrevented ||
195
- event.altKey ||
196
- event.ctrlKey ||
197
- event.metaKey)
186
+ if (!step || event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey)
198
187
  return;
199
- if (event.key !== "ArrowLeft" && event.key !== "ArrowRight")
188
+ if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight')
200
189
  return;
201
190
  if (event.target.closest(ownsArrowKeys))
202
191
  return;
203
- const move = event.key === "ArrowLeft" ? step.previous : step.next;
192
+ const move = event.key === 'ArrowLeft' ? step.previous : step.next;
204
193
  if (!move)
205
194
  return;
206
195
  event.preventDefault();
207
196
  move();
208
197
  };
209
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "fullscreen", showCloseButton: false, "data-file-viewer": "", "data-panel": panelShown ? "open" : undefined, className: "sprout-file-viewer", finalFocus: finalFocus, onKeyDown: handleKeyDown, children: [current && open && (_jsx(SelectedFile, { file: current, renderers: renderers, step: step, actions: actions, menu: menu, panel: panel
198
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "fullscreen", showCloseButton: false, "data-file-viewer": "", "data-panel": panelShown ? 'open' : undefined, className: "sprout-file-viewer", finalFocus: finalFocus, onKeyDown: handleKeyDown, children: [current && open && (_jsx(SelectedFile, { file: current, renderers: renderers, step: step, actions: actions, menu: menu, panel: panel
210
199
  ? {
211
200
  id: panelId,
212
201
  label: panelLabel,
@@ -216,53 +205,43 @@ export function FileViewer({ file, files, index = 0, onIndexChange, open, onOpen
216
205
  : undefined }, step ? index : undefined)), current && open && panel && (_jsx("aside", { id: panelId, "aria-label": panelLabel, hidden: !panelShown, "data-slot": "file-viewer-panel", className: "sprout-file-viewer-panel", children: panel }))] }) }));
217
206
  }
218
207
  function sourceLabel(file, google) {
219
- return google
220
- ? "Google document"
221
- : file.source.kind === "local"
222
- ? "Local file · Not uploaded"
223
- : "Linked file";
208
+ return google ? 'Google document' : file.source.kind === 'local' ? 'Local file · Not uploaded' : 'Linked file';
224
209
  }
225
210
  function SelectedFile({ file, renderers, step, actions, menu, panel, }) {
226
211
  const [attempt, setAttempt] = useState(0);
227
- const retry = () => setAttempt((value) => value + 1);
228
- const google = file.source.kind === "remote" ? googleDocument(file.source.url) : null;
212
+ const retry = () => setAttempt(value => value + 1);
213
+ const google = file.source.kind === 'remote' ? googleDocument(file.source.url) : null;
229
214
  const loaded = useFileContent(file, attempt, !google);
230
- return (_jsxs(_Fragment, { children: [_jsxs("header", { "data-slot": "file-viewer-header", className: "sprout-file-viewer-header", children: [_jsxs("div", { className: "sprout-file-viewer-heading", children: [_jsx(DialogTitle, { children: _jsx("span", { className: "sprout-file-viewer-truncate", title: file.name, children: file.name }) }), _jsx(DialogDescription, { children: _jsx("span", { className: "sprout-file-viewer-truncate", children: sourceLabel(file, !!google) }) })] }), _jsxs("div", { "data-slot": "file-viewer-controls", className: "sprout-file-viewer-controls", children: [step && (_jsxs("div", { "data-slot": "file-viewer-steps", className: "sprout-file-viewer-steps", children: [_jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Previous file", "data-step": "previous", disabled: !step.previous, onClick: step.previous, children: _jsx(ChevronLeft, { size: 16, "aria-hidden": "true" }) }), _jsxs("span", { className: "sprout-file-viewer-position", "aria-hidden": "true", children: [step.position, " of ", step.count] }), _jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Next file", "data-step": "next", disabled: !step.next, onClick: step.next, children: _jsx(ChevronRight, { size: 16, "aria-hidden": "true" }) }), _jsxs("span", { "aria-live": "polite", className: "sprout-file-viewer-sr-only", children: [file.name, ", file ", step.position, " of ", step.count] })] })), actions && (_jsx("div", { "data-slot": "file-viewer-actions", className: "sprout-file-viewer-actions", children: actions })), google ? (_jsxs(Button, { variant: "outline", size: "sm", nativeButton: false, render: _jsx("a", { href: google.url, target: "_blank", rel: "noopener noreferrer" }), children: [_jsx(ArrowUpRight, { size: 16, "aria-hidden": "true" }), "Open in Google"] })) : loaded.kind === "ready" ? (_jsxs(Button, { variant: "outline", size: "sm", "aria-label": "Download", nativeButton: false, render: _jsx("a", { href: loaded.url, download: file.name }), children: [_jsx(Download, { size: 16, "aria-hidden": "true" }), _jsx("span", { className: "sprout-file-viewer-label", children: "Download" })] })) : (_jsxs(Button, { variant: "outline", size: "sm", "aria-label": "Download", disabled: true, children: [_jsx(Download, { size: 16, "aria-hidden": "true" }), _jsx("span", { className: "sprout-file-viewer-label", children: "Download" })] })), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { render: _jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "More actions" }), children: _jsx(MoreH, { size: 16, "aria-hidden": "true" }) }), _jsxs(DropdownMenuContent, { align: "end", size: "menu", children: [_jsx(DropdownMenuItem, { onClick: retry, children: google ? "Reload viewer" : "Reload file" }), menu && (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), menu] }))] })] })] }), _jsxs("div", { "data-slot": "file-viewer-window", className: "sprout-file-viewer-window", children: [panel && (_jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": panel.label, "aria-expanded": panel.open, "aria-controls": panel.id, onClick: panel.toggle, children: _jsx(SidebarRight, { size: 16, "aria-hidden": "true" }) })), _jsx(DialogClose, { render: _jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Close file viewer" }), children: _jsx(X, { size: 18, "aria-hidden": "true" }) })] })] }), _jsx("div", { "data-slot": "viewable-file-content", className: "sprout-file-viewer-surface", children: google ? (_jsx(EmbeddedFile, { url: google.embedUrl, name: file.name, google: true, onRetry: retry }, `${google.embedUrl}:${attempt}`)) : (_jsx(FileBody, { loaded: loaded, file: file, renderers: renderers, onRetry: retry })) })] }));
215
+ return (_jsxs(_Fragment, { children: [_jsxs("header", { "data-slot": "file-viewer-header", className: "sprout-file-viewer-header", children: [_jsxs("div", { className: "sprout-file-viewer-heading", children: [_jsx(DialogTitle, { children: _jsx("span", { className: "sprout-file-viewer-truncate", title: file.name, children: file.name }) }), _jsx(DialogDescription, { children: _jsx("span", { className: "sprout-file-viewer-truncate", children: sourceLabel(file, !!google) }) })] }), _jsxs("div", { "data-slot": "file-viewer-controls", className: "sprout-file-viewer-controls", children: [step && (_jsxs("div", { "data-slot": "file-viewer-steps", className: "sprout-file-viewer-steps", children: [_jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Previous file", "data-step": "previous", disabled: !step.previous, onClick: step.previous, children: _jsx(ChevronLeft, { size: 16, "aria-hidden": "true" }) }), _jsxs("span", { className: "sprout-file-viewer-position", "aria-hidden": "true", children: [step.position, " of ", step.count] }), _jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Next file", "data-step": "next", disabled: !step.next, onClick: step.next, children: _jsx(ChevronRight, { size: 16, "aria-hidden": "true" }) }), _jsxs("span", { "aria-live": "polite", className: "sprout-file-viewer-sr-only", children: [file.name, ", file ", step.position, " of ", step.count] })] })), actions && (_jsx("div", { "data-slot": "file-viewer-actions", className: "sprout-file-viewer-actions", children: actions })), google ? (_jsxs(Button, { variant: "outline", size: "sm", nativeButton: false, render: _jsx("a", { href: google.url, target: "_blank", rel: "noopener noreferrer" }), children: [_jsx(ArrowUpRight, { size: 16, "aria-hidden": "true" }), "Open in Google"] })) : loaded.kind === 'ready' ? (_jsxs(Button, { variant: "outline", size: "sm", "aria-label": "Download", nativeButton: false, render: _jsx("a", { href: loaded.url, download: file.name }), children: [_jsx(Download, { size: 16, "aria-hidden": "true" }), _jsx("span", { className: "sprout-file-viewer-label", children: "Download" })] })) : (_jsxs(Button, { variant: "outline", size: "sm", "aria-label": "Download", disabled: true, children: [_jsx(Download, { size: 16, "aria-hidden": "true" }), _jsx("span", { className: "sprout-file-viewer-label", children: "Download" })] })), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { render: _jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "More actions" }), children: _jsx(MoreH, { size: 16, "aria-hidden": "true" }) }), _jsxs(DropdownMenuContent, { align: "end", size: "menu", children: [_jsx(DropdownMenuItem, { onClick: retry, children: google ? 'Reload viewer' : 'Reload file' }), menu && (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), menu] }))] })] })] }), _jsxs("div", { "data-slot": "file-viewer-window", className: "sprout-file-viewer-window", children: [panel && (_jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": panel.label, "aria-expanded": panel.open, "aria-controls": panel.id, onClick: panel.toggle, children: _jsx(SidebarRight, { size: 16, "aria-hidden": "true" }) })), _jsx(DialogClose, { render: _jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Close file viewer" }), children: _jsx(X, { size: 18, "aria-hidden": "true" }) })] })] }), _jsx("div", { "data-slot": "viewable-file-content", className: "sprout-file-viewer-surface", children: google ? (_jsx(EmbeddedFile, { url: google.embedUrl, name: file.name, google: true, onRetry: retry }, `${google.embedUrl}:${attempt}`)) : (_jsx(FileBody, { loaded: loaded, file: file, renderers: renderers, onRetry: retry })) })] }));
231
216
  }
232
217
  /** Loads a local or linked file and shows it inline, with reload and download. */
233
218
  export function ViewableFileContent({ file, renderers, className, ...props }) {
234
219
  const [attempt, setAttempt] = useState(0);
235
- const retry = () => setAttempt((value) => value + 1);
236
- const google = file.source.kind === "remote" ? googleDocument(file.source.url) : null;
237
- return (_jsx("div", { "data-slot": "viewable-file-content", ...props, className: cn("sprout-file-viewer-surface", className), children: google ? (_jsx(EmbeddedFile, { url: google.embedUrl, name: file.name, google: true, onRetry: retry }, `${google.embedUrl}:${attempt}`)) : (_jsx(LocalFile, { file: file, renderers: renderers, onRetry: retry }, attempt)) }));
220
+ const retry = () => setAttempt(value => value + 1);
221
+ const google = file.source.kind === 'remote' ? googleDocument(file.source.url) : null;
222
+ return (_jsx("div", { "data-slot": "viewable-file-content", ...props, className: cn('sprout-file-viewer-surface', className), children: google ? (_jsx(EmbeddedFile, { url: google.embedUrl, name: file.name, google: true, onRetry: retry }, `${google.embedUrl}:${attempt}`)) : (_jsx(LocalFile, { file: file, renderers: renderers, onRetry: retry }, attempt)) }));
238
223
  }
239
224
  function LocalFile({ file, renderers, onRetry, }) {
240
225
  const loaded = useFileContent(file);
241
- return (_jsxs(_Fragment, { children: [loaded.kind === "ready" && (_jsxs("div", { "data-slot": "file-viewer-toolbar", className: "sprout-file-viewer-toolbar", "data-align": "end", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: onRetry, children: "Reload file" }), _jsxs(Button, { variant: "outline", size: "sm", nativeButton: false, render: _jsx("a", { href: loaded.url, download: file.name }), children: [_jsx(Download, { size: 16, "aria-hidden": "true" }), "Download"] })] })), _jsx(FileBody, { loaded: loaded, file: file, renderers: renderers, onRetry: onRetry })] }));
226
+ return (_jsxs(_Fragment, { children: [loaded.kind === 'ready' && (_jsxs("div", { "data-slot": "file-viewer-toolbar", className: "sprout-file-viewer-toolbar", "data-align": "end", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: onRetry, children: "Reload file" }), _jsxs(Button, { variant: "outline", size: "sm", nativeButton: false, render: _jsx("a", { href: loaded.url, download: file.name }), children: [_jsx(Download, { size: 16, "aria-hidden": "true" }), "Download"] })] })), _jsx(FileBody, { loaded: loaded, file: file, renderers: renderers, onRetry: onRetry })] }));
242
227
  }
243
228
  function FileBody({ loaded, file, renderers, onRetry, }) {
244
- if (loaded.kind === "loading")
229
+ if (loaded.kind === 'loading')
245
230
  return _jsx(LoadingFile, {});
246
- if (loaded.kind === "error")
231
+ if (loaded.kind === 'error')
247
232
  return _jsx(FileError, { message: loaded.message, onRetry: onRetry });
248
233
  return (_jsx(FileContent, { name: file.name, mediaType: file.mediaType, bytes: loaded.bytes, url: loaded.url, onRetry: onRetry, renderers: renderers }));
249
234
  }
250
- const textKinds = [
251
- "text",
252
- "code",
253
- "json",
254
- "markdown",
255
- "google",
256
- ];
235
+ const textKinds = ['text', 'code', 'json', 'markdown', 'google'];
257
236
  /** Shows bytes you already loaded: images, PDF, text, JSON, Markdown, Google shortcuts, and more. */
258
- export function FileContent({ name, mediaType, bytes, url, onRetry, renderers: own, }) {
237
+ export function FileContent({ name, mediaType, bytes, url, onRetry, renderers: own }) {
259
238
  const renderers = useRenderers(own);
260
239
  const kind = fileKind(name, mediaType);
261
240
  const text = useMemo(() => {
262
241
  if (!textKinds.includes(kind))
263
242
  return null;
264
243
  try {
265
- return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
244
+ return new TextDecoder('utf-8', { fatal: true }).decode(bytes);
266
245
  }
267
246
  catch {
268
247
  return undefined;
@@ -270,24 +249,24 @@ export function FileContent({ name, mediaType, bytes, url, onRetry, renderers: o
270
249
  }, [bytes, kind]);
271
250
  if (text === undefined)
272
251
  return (_jsx(FileError, { message: "This file is not valid UTF-8 text. Download it to open in another application.", onRetry: onRetry }));
273
- const unavailable = (_jsx(StatePanel, { kind: "unavailable", title: "This format cannot be displayed locally", description: name.toLowerCase().endsWith(".ppt")
274
- ? "Save this older PowerPoint file as .pptx to view the slides here. You can still download the original."
275
- : "Download the original file to open it in a compatible application." }));
252
+ const unavailable = (_jsx(StatePanel, { kind: "unavailable", title: "This format cannot be displayed locally", description: name.toLowerCase().endsWith('.ppt')
253
+ ? 'Save this older PowerPoint file as .pptx to view the slides here. You can still download the original.'
254
+ : 'Download the original file to open it in a compatible application.' }));
276
255
  let body;
277
256
  if (bytes.byteLength === 0)
278
- body = (_jsx(StatePanel, { kind: "empty", title: "This file is empty", description: "The file contains no data to display." }));
279
- else if (kind === "image")
257
+ body = _jsx(StatePanel, { kind: "empty", title: "This file is empty", description: "The file contains no data to display." });
258
+ else if (kind === 'image')
280
259
  body = _jsx(ImageFile, { url: url, name: name, onRetry: onRetry });
281
- else if (kind === "pdf")
260
+ else if (kind === 'pdf')
282
261
  body = _jsx(EmbeddedFile, { url: url, name: name, onRetry: onRetry });
283
- else if (kind === "google")
284
- body = _jsx(GoogleShortcut, { text: text ?? "", name: name, onRetry: onRetry });
285
- else if (kind === "sheet")
262
+ else if (kind === 'google')
263
+ body = _jsx(GoogleShortcut, { text: text ?? '', name: name, onRetry: onRetry });
264
+ else if (kind === 'sheet')
286
265
  body = renderers.sheet ? (_jsx(Suspense, { fallback: _jsx(LoadingFile, {}), children: renderers.sheet({ name, bytes, onRetry }) })) : (unavailable);
287
- else if (kind === "document" || kind === "slides")
266
+ else if (kind === 'document' || kind === 'slides')
288
267
  body = renderers.office ? (_jsx(Suspense, { fallback: _jsx(LoadingFile, {}), children: renderers.office({ name, bytes, kind, onRetry }) })) : (unavailable);
289
268
  else if (text !== null)
290
- body = (_jsx(TextFile, { text: text, name: name, kind: kind, renderMarkdown: renderers.markdown }));
269
+ body = _jsx(TextFile, { text: text, name: name, kind: kind, renderMarkdown: renderers.markdown });
291
270
  else
292
271
  body = unavailable;
293
272
  return (_jsx("div", { "data-slot": "file-content", "data-kind": kind, className: "sprout-file-content", children: body }));
@@ -299,22 +278,17 @@ export function FileError({ message, onRetry, className, ...props }) {
299
278
  return (_jsx(StatePanel, { kind: "error", title: "Could not open this file", description: message, "data-file-state": "error", ...props, className: className, children: _jsx(Button, { variant: "outline", onClick: onRetry, children: "Try again" }) }));
300
279
  }
301
280
  function codeLanguageFor(name, kind) {
302
- return kind === "code" ? (languageForFilename(name) ?? "text") : kind;
281
+ return kind === 'code' ? (languageForFilename(name) ?? 'text') : kind;
303
282
  }
304
- const previewTextKinds = [
305
- "text",
306
- "code",
307
- "json",
308
- "markdown",
309
- ];
283
+ const previewTextKinds = ['text', 'code', 'json', 'markdown'];
310
284
  const previewBytes = 16 * 1024;
311
285
  const previewLines = 24;
312
286
  /** Whether `FilePreview` can show this file: images, text, code, JSON, and Markdown, but not Google shortcuts. */
313
287
  export function isPreviewableFile(file) {
314
- if (file.source.kind === "remote" && googleDocument(file.source.url))
288
+ if (file.source.kind === 'remote' && googleDocument(file.source.url))
315
289
  return false;
316
290
  const kind = fileKind(file.name, file.mediaType);
317
- return kind === "image" || previewTextKinds.includes(kind);
291
+ return kind === 'image' || previewTextKinds.includes(kind);
318
292
  }
319
293
  /**
320
294
  * A small, non-interactive peek at a file: the image, or the first lines
@@ -325,30 +299,30 @@ export function FilePreview({ file, className, ...props }) {
325
299
  if (!isPreviewableFile(file))
326
300
  return null;
327
301
  const kind = fileKind(file.name, file.mediaType);
328
- return (_jsx("div", { "data-slot": "file-preview", "data-kind": kind, ...props, className: cn("sprout-file-preview", className), children: kind === "image" && file.source.kind === "remote" ? (_jsx("img", { src: file.source.url, alt: file.name, className: "sprout-file-preview-image" })) : (_jsx(LoadedPreview, { file: file, kind: kind })) }));
302
+ return (_jsx("div", { "data-slot": "file-preview", "data-kind": kind, ...props, className: cn('sprout-file-preview', className), children: kind === 'image' && file.source.kind === 'remote' ? (_jsx("img", { src: file.source.url, alt: file.name, className: "sprout-file-preview-image" })) : (_jsx(LoadedPreview, { file: file, kind: kind })) }));
329
303
  }
330
- function LoadedPreview({ file, kind, }) {
304
+ function LoadedPreview({ file, kind }) {
331
305
  const loaded = useFileContent(file);
332
306
  const text = useMemo(() => {
333
- if (loaded.kind !== "ready" || kind === "image")
307
+ if (loaded.kind !== 'ready' || kind === 'image')
334
308
  return null;
335
309
  const head = new TextDecoder().decode(loaded.bytes.slice(0, previewBytes));
336
- return head.split("\n").slice(0, previewLines).join("\n");
310
+ return head.split('\n').slice(0, previewLines).join('\n');
337
311
  }, [loaded, kind]);
338
- if (loaded.kind === "loading")
312
+ if (loaded.kind === 'loading')
339
313
  return (_jsxs("span", { className: "sprout-file-preview-state", children: [_jsx(Spinner, { size: "sm", "aria-hidden": "true" }), "Loading preview\u2026"] }));
340
- if (loaded.kind === "error")
341
- return (_jsx("span", { className: "sprout-file-preview-state", children: "Preview unavailable" }));
314
+ if (loaded.kind === 'error')
315
+ return _jsx("span", { className: "sprout-file-preview-state", children: "Preview unavailable" });
342
316
  if (text === null)
343
- return (_jsx("img", { src: loaded.url, alt: file.name, className: "sprout-file-preview-image" }));
317
+ return _jsx("img", { src: loaded.url, alt: file.name, className: "sprout-file-preview-image" });
344
318
  if (!text.trim())
345
- return (_jsx("span", { className: "sprout-file-preview-state", children: "This file is empty" }));
346
- return (_jsx(CodeBlock, { code: text, language: codeLanguageFor(file.name, kind), variant: "preview", lineNumbers: false, wrap: kind === "text" || kind === "markdown" }));
319
+ return _jsx("span", { className: "sprout-file-preview-state", children: "This file is empty" });
320
+ return (_jsx(CodeBlock, { code: text, language: codeLanguageFor(file.name, kind), variant: "preview", lineNumbers: false, wrap: kind === 'text' || kind === 'markdown' }));
347
321
  }
348
322
  function TextFile({ text, name, kind, renderMarkdown, }) {
349
323
  const [source, setSource] = useState(false);
350
324
  const json = useMemo(() => {
351
- if (kind !== "json")
325
+ if (kind !== 'json')
352
326
  return null;
353
327
  try {
354
328
  return JSON.stringify(JSON.parse(text), null, 2);
@@ -357,44 +331,42 @@ function TextFile({ text, name, kind, renderMarkdown, }) {
357
331
  return undefined;
358
332
  }
359
333
  }, [kind, text]);
360
- const markdown = kind === "markdown" && renderMarkdown;
361
- const prose = kind === "text" || kind === "markdown";
362
- return (_jsxs("div", { className: "sprout-file-text", "data-format": markdown && !source ? "formatted" : "source", children: [markdown && (_jsx("div", { className: "sprout-file-text-bar", children: _jsx(Button, { variant: "outline", size: "sm", "aria-pressed": source, onClick: () => setSource((value) => !value), children: source ? "Show formatted" : "Show source" }) })), json === undefined && (_jsx("p", { role: "alert", className: "sprout-file-text-alert", children: "This file contains invalid JSON. The original text is shown below." })), markdown && !source ? (_jsx("div", { className: "sprout-file-text-scroll", tabIndex: 0, role: "region", "aria-label": name, children: _jsx("div", { className: "sprout-file-text-page", children: _jsx(Suspense, { fallback: _jsx(LoadingFile, {}), children: renderMarkdown({ text }) }) }) })) : (_jsx(CodeBlock, { code: json ?? text, language: codeLanguageFor(name, kind), variant: "file", wrap: prose }))] }));
334
+ const markdown = kind === 'markdown' && renderMarkdown;
335
+ const prose = kind === 'text' || kind === 'markdown';
336
+ return (_jsxs("div", { className: "sprout-file-text", "data-format": markdown && !source ? 'formatted' : 'source', children: [markdown && (_jsx("div", { className: "sprout-file-text-bar", children: _jsx(Button, { variant: "outline", size: "sm", "aria-pressed": source, onClick: () => setSource(value => !value), children: source ? 'Show formatted' : 'Show source' }) })), json === undefined && (_jsx("p", { role: "alert", className: "sprout-file-text-alert", children: "This file contains invalid JSON. The original text is shown below." })), markdown && !source ? (_jsx("div", { className: "sprout-file-text-scroll", tabIndex: 0, role: "region", "aria-label": name, children: _jsx("div", { className: "sprout-file-text-page", children: _jsx(Suspense, { fallback: _jsx(LoadingFile, {}), children: renderMarkdown({ text }) }) }) })) : (_jsx(CodeBlock, { code: json ?? text, language: codeLanguageFor(name, kind), variant: "file", wrap: prose }))] }));
363
337
  }
364
- function ImageFile({ url, name, onRetry, }) {
365
- const [state, setState] = useState("loading");
338
+ function ImageFile({ url, name, onRetry }) {
339
+ const [state, setState] = useState('loading');
366
340
  const [zoom, setZoom] = useState(false);
367
- if (state === "error")
341
+ if (state === 'error')
368
342
  return (_jsx(FileError, { message: "Your browser could not decode this image. Try a PNG, JPEG, GIF, WebP, AVIF, or BMP version.", onRetry: onRetry }));
369
- return (_jsxs(_Fragment, { children: [_jsx("div", { className: "sprout-file-viewer-toolbar", "data-align": "center", children: _jsx(Button, { variant: "outline", size: "sm", "aria-pressed": zoom, onClick: () => setZoom((value) => !value), children: zoom ? "Fit image" : "Actual size" }) }), state === "loading" && _jsx(LoadingFile, {}), _jsx("div", { className: "sprout-file-image-stage", "data-zoom": zoom ? "actual" : "fit", tabIndex: zoom ? 0 : undefined, role: zoom ? "region" : undefined, "aria-label": zoom ? `${name} at actual size` : undefined, children: _jsx("img", { src: url, alt: name, onLoad: () => setState("ready"), onError: () => setState("error"), className: "sprout-file-image", "data-state": state }) })] }));
343
+ return (_jsxs(_Fragment, { children: [_jsx("div", { className: "sprout-file-viewer-toolbar", "data-align": "center", children: _jsx(Button, { variant: "outline", size: "sm", "aria-pressed": zoom, onClick: () => setZoom(value => !value), children: zoom ? 'Fit image' : 'Actual size' }) }), state === 'loading' && _jsx(LoadingFile, {}), _jsx("div", { className: "sprout-file-image-stage", "data-zoom": zoom ? 'actual' : 'fit', tabIndex: zoom ? 0 : undefined, role: zoom ? 'region' : undefined, "aria-label": zoom ? `${name} at actual size` : undefined, children: _jsx("img", { src: url, alt: name, onLoad: () => setState('ready'), onError: () => setState('error'), className: "sprout-file-image", "data-state": state }) })] }));
370
344
  }
371
- function GoogleShortcut({ text, name, onRetry, }) {
345
+ function GoogleShortcut({ text, name, onRetry }) {
372
346
  let document;
373
347
  try {
374
348
  document = googleDocument(googleShortcut(text));
375
349
  }
376
350
  catch (error) {
377
- return (_jsx(FileError, { message: error instanceof Error
378
- ? error.message
379
- : "Invalid Google document shortcut.", onRetry: onRetry }));
351
+ return (_jsx(FileError, { message: error instanceof Error ? error.message : 'Invalid Google document shortcut.', onRetry: onRetry }));
380
352
  }
381
353
  if (!document)
382
354
  return null;
383
355
  return (_jsxs(_Fragment, { children: [_jsx("div", { className: "sprout-file-google-link", children: _jsx("a", { href: document.url, target: "_blank", rel: "noopener noreferrer", children: "Open in Google" }) }), _jsx(EmbeddedFile, { url: document.embedUrl, name: name, google: true, onRetry: onRetry })] }));
384
356
  }
385
357
  function EmbeddedFile({ url, name, google = false, onRetry, }) {
386
- const [state, setState] = useState("loading");
358
+ const [state, setState] = useState('loading');
387
359
  const timer = useRef(null);
388
360
  useEffect(() => {
389
- timer.current = setTimeout(() => setState((value) => (value === "loading" ? "slow" : value)), 12000);
361
+ timer.current = setTimeout(() => setState(value => (value === 'loading' ? 'slow' : value)), 12000);
390
362
  return () => {
391
363
  if (timer.current)
392
364
  clearTimeout(timer.current);
393
365
  };
394
366
  }, []);
395
- return (_jsxs("div", { "data-slot": "file-embed", "data-state": state, className: "sprout-file-embed", children: [google && (_jsx("p", { className: "sprout-file-embed-notice", children: "Google controls access to this document. If it cannot open here, use Open in Google to sign in or request access." })), state === "loading" && (_jsx("div", { className: "sprout-file-embed-loading", children: _jsx(LoadingFile, {}) })), state === "slow" && (_jsxs("div", { className: "sprout-file-embed-slow", children: [_jsx("p", { role: "status", children: "The embedded viewer is taking longer than expected." }), _jsx(Button, { variant: "outline", size: "sm", onClick: onRetry, children: "Reload viewer" })] })), _jsx("iframe", { title: name, src: url, className: "sprout-file-embed-frame", referrerPolicy: "no-referrer", onLoad: () => {
367
+ return (_jsxs("div", { "data-slot": "file-embed", "data-state": state, className: "sprout-file-embed", children: [google && (_jsx("p", { className: "sprout-file-embed-notice", children: "Google controls access to this document. If it cannot open here, use Open in Google to sign in or request access." })), state === 'loading' && (_jsx("div", { className: "sprout-file-embed-loading", children: _jsx(LoadingFile, {}) })), state === 'slow' && (_jsxs("div", { className: "sprout-file-embed-slow", children: [_jsx("p", { role: "status", children: "The embedded viewer is taking longer than expected." }), _jsx(Button, { variant: "outline", size: "sm", onClick: onRetry, children: "Reload viewer" })] })), _jsx("iframe", { title: name, src: url, className: "sprout-file-embed-frame", referrerPolicy: "no-referrer", onLoad: () => {
396
368
  if (timer.current)
397
369
  clearTimeout(timer.current);
398
- setState("loaded");
399
- }, onError: () => setState("slow"), allowFullScreen: true })] }));
370
+ setState('loaded');
371
+ }, onError: () => setState('slow'), allowFullScreen: true })] }));
400
372
  }
@@ -1,5 +1,5 @@
1
- import { type ReactElement, type ReactNode } from "react";
2
- import { type ButtonBlockerFix as ActionBlockerFix, type ButtonState as ActionControlState } from "./button.js";
1
+ import { type ReactElement, type ReactNode } from 'react';
2
+ import { type ButtonBlockerFix as ActionBlockerFix, type ButtonState as ActionControlState } from './button.js';
3
3
  export declare const formFooterLayoutNames: readonly ["bar", "stack", "compact"];
4
4
  export declare const formSectionLevelNames: readonly [2, 3];
5
5
  export type FormFooterLayout = (typeof formFooterLayoutNames)[number];
@@ -12,7 +12,7 @@ export interface FormProps {
12
12
  /** Locks every Field, ChoiceGroup, and CheckboxField inside. */
13
13
  disabled?: boolean | undefined;
14
14
  /** Return `"invalid"` after revealing errors; Form then focuses the first invalid control. */
15
- onSubmit: () => "invalid" | void;
15
+ onSubmit: () => 'invalid' | void;
16
16
  /** The form's accessible name. */
17
17
  label?: string | undefined;
18
18
  className?: string | undefined;
@@ -23,7 +23,7 @@ export interface FormProps {
23
23
  * while `disabled`, and moves focus to the first invalid control after a
24
24
  * failed submit.
25
25
  */
26
- export declare function Form({ id, disabled, onSubmit, label, className, children, }: FormProps): import("react").JSX.Element;
26
+ export declare function Form({ id, disabled, onSubmit, label, className, children }: FormProps): import("react").JSX.Element;
27
27
  export interface FormSectionProps {
28
28
  title: string;
29
29
  description?: string | undefined;
@@ -37,17 +37,17 @@ export interface FormSectionProps {
37
37
  children: ReactNode;
38
38
  }
39
39
  /** A bordered, named region of a form with a title, description, and optional meta badge. */
40
- export declare function FormSection({ title, description, meta, label, id, level, className, children, }: FormSectionProps): import("react").JSX.Element;
40
+ export declare function FormSection({ title, description, meta, label, id, level, className, children }: FormSectionProps): import("react").JSX.Element;
41
41
  export type FormCancel =
42
42
  /** Shown only while the form is dirty and not busy. */
43
43
  {
44
- kind: "reset";
44
+ kind: 'reset';
45
45
  label: string;
46
46
  onReset: () => void;
47
47
  }
48
48
  /** A link element, such as a router `<Link>`, rendered through Button `render`. */
49
49
  | {
50
- kind: "leave";
50
+ kind: 'leave';
51
51
  label: string;
52
52
  link: ReactElement;
53
53
  };