@fgv/ts-app-shell 5.1.0-4 → 5.1.0-40

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 (225) hide show
  1. package/README.md +182 -0
  2. package/dist/index.browser.js.map +1 -0
  3. package/dist/index.js.map +1 -0
  4. package/dist/packlets/ai-assist/index.js.map +1 -0
  5. package/dist/packlets/ai-assist/useAiAssist.js +225 -18
  6. package/dist/packlets/ai-assist/useAiAssist.js.map +1 -0
  7. package/dist/packlets/badge/index.js +23 -0
  8. package/dist/packlets/badge/index.js.map +1 -0
  9. package/dist/packlets/badge/renderStatusBadge.js +44 -0
  10. package/dist/packlets/badge/renderStatusBadge.js.map +1 -0
  11. package/dist/packlets/cascade/CascadeContainer.js.map +1 -0
  12. package/dist/packlets/cascade/ComparisonView.js.map +1 -0
  13. package/dist/packlets/cascade/EntityTabLayout.js.map +1 -0
  14. package/dist/packlets/cascade/MobileCascadeStack.js.map +1 -0
  15. package/dist/packlets/cascade/index.js.map +1 -0
  16. package/dist/packlets/cascade/model.js.map +1 -0
  17. package/dist/packlets/cascade/useCascadeOps.js.map +1 -0
  18. package/dist/packlets/cascade/useCascadeTransitions.js.map +1 -0
  19. package/dist/packlets/detail/DetailHelpers.js.map +1 -0
  20. package/dist/packlets/detail/index.js.map +1 -0
  21. package/dist/packlets/drop-zone/JsonDropZone.js.map +1 -0
  22. package/dist/packlets/drop-zone/index.js.map +1 -0
  23. package/dist/packlets/editing/EditFieldHelpers.js +4 -4
  24. package/dist/packlets/editing/EditFieldHelpers.js.map +1 -0
  25. package/dist/packlets/editing/MultiActionButton.js.map +1 -0
  26. package/dist/packlets/editing/NumericInput.js.map +1 -0
  27. package/dist/packlets/editing/TypeaheadInput.js.map +1 -0
  28. package/dist/packlets/editing/index.js.map +1 -0
  29. package/dist/packlets/editing/useTypeaheadMatch.js.map +1 -0
  30. package/dist/packlets/keyboard/index.js.map +1 -0
  31. package/dist/packlets/keyboard/registry.js.map +1 -0
  32. package/dist/packlets/keyboard/useKeyboardShortcuts.js.map +1 -0
  33. package/dist/packlets/messages/MessagesContext.js +79 -3
  34. package/dist/packlets/messages/MessagesContext.js.map +1 -0
  35. package/dist/packlets/messages/MessagesLogger.js +10 -31
  36. package/dist/packlets/messages/MessagesLogger.js.map +1 -0
  37. package/dist/packlets/messages/StatusBar.js +53 -28
  38. package/dist/packlets/messages/StatusBar.js.map +1 -0
  39. package/dist/packlets/messages/Toast.js +7 -4
  40. package/dist/packlets/messages/Toast.js.map +1 -0
  41. package/dist/packlets/messages/index.js +2 -2
  42. package/dist/packlets/messages/index.js.map +1 -0
  43. package/dist/packlets/messages/model.js +31 -4
  44. package/dist/packlets/messages/model.js.map +1 -0
  45. package/dist/packlets/messages/useLogReporter.js.map +1 -0
  46. package/dist/packlets/modal/ConfirmDialog.js.map +1 -0
  47. package/dist/packlets/modal/Modal.js.map +1 -0
  48. package/dist/packlets/modal/index.js.map +1 -0
  49. package/dist/packlets/print/PrintEnclosure.js.map +1 -0
  50. package/dist/packlets/print/index.js.map +1 -0
  51. package/dist/packlets/print/openPrintWindow.js.map +1 -0
  52. package/dist/packlets/responsive/ResponsiveProvider.js.map +1 -0
  53. package/dist/packlets/responsive/index.js.map +1 -0
  54. package/dist/packlets/responsive/useResponsiveLayout.js.map +1 -0
  55. package/dist/packlets/selectors/EntityRow.js.map +1 -0
  56. package/dist/packlets/selectors/PreferredSelector.js.map +1 -0
  57. package/dist/packlets/selectors/index.js.map +1 -0
  58. package/dist/packlets/sidebar/CollectionSection.js +158 -43
  59. package/dist/packlets/sidebar/CollectionSection.js.map +1 -0
  60. package/dist/packlets/sidebar/EntityList.js.map +1 -0
  61. package/dist/packlets/sidebar/FilterBar.js.map +1 -0
  62. package/dist/packlets/sidebar/FilterRow.js.map +1 -0
  63. package/dist/packlets/sidebar/GroupedEntityList.js.map +1 -0
  64. package/dist/packlets/sidebar/SearchBar.js.map +1 -0
  65. package/dist/packlets/sidebar/SidebarLayout.js.map +1 -0
  66. package/dist/packlets/sidebar/index.js.map +1 -0
  67. package/dist/packlets/theme/ThemeProvider.js.map +1 -0
  68. package/dist/packlets/theme/index.js.map +1 -0
  69. package/dist/packlets/top-bar/ModeSelector.js.map +1 -0
  70. package/dist/packlets/top-bar/TabBar.js +4 -1
  71. package/dist/packlets/top-bar/TabBar.js.map +1 -0
  72. package/dist/packlets/top-bar/index.js.map +1 -0
  73. package/dist/packlets/url-sync/index.js.map +1 -0
  74. package/dist/packlets/url-sync/useUrlSync.js.map +1 -0
  75. package/dist/styles/theme.css +194 -0
  76. package/lib/index.browser.d.ts.map +1 -0
  77. package/lib/index.browser.js.map +1 -0
  78. package/lib/index.d.ts.map +1 -0
  79. package/lib/index.js.map +1 -0
  80. package/lib/packlets/ai-assist/index.d.ts.map +1 -0
  81. package/lib/packlets/ai-assist/index.js.map +1 -0
  82. package/lib/packlets/ai-assist/useAiAssist.d.ts +26 -0
  83. package/lib/packlets/ai-assist/useAiAssist.d.ts.map +1 -0
  84. package/lib/packlets/ai-assist/useAiAssist.js +225 -18
  85. package/lib/packlets/ai-assist/useAiAssist.js.map +1 -0
  86. package/lib/packlets/badge/index.d.ts +2 -0
  87. package/lib/packlets/badge/index.d.ts.map +1 -0
  88. package/lib/packlets/badge/index.js +27 -0
  89. package/lib/packlets/badge/index.js.map +1 -0
  90. package/lib/packlets/badge/renderStatusBadge.d.ts +11 -0
  91. package/lib/packlets/badge/renderStatusBadge.d.ts.map +1 -0
  92. package/lib/packlets/badge/renderStatusBadge.js +50 -0
  93. package/lib/packlets/badge/renderStatusBadge.js.map +1 -0
  94. package/lib/packlets/cascade/CascadeContainer.d.ts.map +1 -0
  95. package/lib/packlets/cascade/CascadeContainer.js.map +1 -0
  96. package/lib/packlets/cascade/ComparisonView.d.ts.map +1 -0
  97. package/lib/packlets/cascade/ComparisonView.js.map +1 -0
  98. package/lib/packlets/cascade/EntityTabLayout.d.ts.map +1 -0
  99. package/lib/packlets/cascade/EntityTabLayout.js.map +1 -0
  100. package/lib/packlets/cascade/MobileCascadeStack.d.ts.map +1 -0
  101. package/lib/packlets/cascade/MobileCascadeStack.js.map +1 -0
  102. package/lib/packlets/cascade/index.d.ts.map +1 -0
  103. package/lib/packlets/cascade/index.js.map +1 -0
  104. package/lib/packlets/cascade/model.d.ts.map +1 -0
  105. package/lib/packlets/cascade/model.js.map +1 -0
  106. package/lib/packlets/cascade/useCascadeOps.d.ts.map +1 -0
  107. package/lib/packlets/cascade/useCascadeOps.js.map +1 -0
  108. package/lib/packlets/cascade/useCascadeTransitions.d.ts.map +1 -0
  109. package/lib/packlets/cascade/useCascadeTransitions.js.map +1 -0
  110. package/lib/packlets/detail/DetailHelpers.d.ts.map +1 -0
  111. package/lib/packlets/detail/DetailHelpers.js.map +1 -0
  112. package/lib/packlets/detail/index.d.ts.map +1 -0
  113. package/lib/packlets/detail/index.js.map +1 -0
  114. package/lib/packlets/drop-zone/JsonDropZone.d.ts.map +1 -0
  115. package/lib/packlets/drop-zone/JsonDropZone.js.map +1 -0
  116. package/lib/packlets/drop-zone/index.d.ts.map +1 -0
  117. package/lib/packlets/drop-zone/index.js.map +1 -0
  118. package/lib/packlets/editing/EditFieldHelpers.d.ts +6 -2
  119. package/lib/packlets/editing/EditFieldHelpers.d.ts.map +1 -0
  120. package/lib/packlets/editing/EditFieldHelpers.js +4 -4
  121. package/lib/packlets/editing/EditFieldHelpers.js.map +1 -0
  122. package/lib/packlets/editing/MultiActionButton.d.ts.map +1 -0
  123. package/lib/packlets/editing/MultiActionButton.js.map +1 -0
  124. package/lib/packlets/editing/NumericInput.d.ts.map +1 -0
  125. package/lib/packlets/editing/NumericInput.js.map +1 -0
  126. package/lib/packlets/editing/TypeaheadInput.d.ts.map +1 -0
  127. package/lib/packlets/editing/TypeaheadInput.js.map +1 -0
  128. package/lib/packlets/editing/index.d.ts.map +1 -0
  129. package/lib/packlets/editing/index.js.map +1 -0
  130. package/lib/packlets/editing/useTypeaheadMatch.d.ts.map +1 -0
  131. package/lib/packlets/editing/useTypeaheadMatch.js.map +1 -0
  132. package/lib/packlets/keyboard/index.d.ts.map +1 -0
  133. package/lib/packlets/keyboard/index.js.map +1 -0
  134. package/lib/packlets/keyboard/registry.d.ts.map +1 -0
  135. package/lib/packlets/keyboard/registry.js.map +1 -0
  136. package/lib/packlets/keyboard/useKeyboardShortcuts.d.ts.map +1 -0
  137. package/lib/packlets/keyboard/useKeyboardShortcuts.js.map +1 -0
  138. package/lib/packlets/messages/MessagesContext.d.ts +47 -3
  139. package/lib/packlets/messages/MessagesContext.d.ts.map +1 -0
  140. package/lib/packlets/messages/MessagesContext.js +79 -2
  141. package/lib/packlets/messages/MessagesContext.js.map +1 -0
  142. package/lib/packlets/messages/MessagesLogger.d.ts +8 -3
  143. package/lib/packlets/messages/MessagesLogger.d.ts.map +1 -0
  144. package/lib/packlets/messages/MessagesLogger.js +10 -31
  145. package/lib/packlets/messages/MessagesLogger.js.map +1 -0
  146. package/lib/packlets/messages/StatusBar.d.ts +10 -1
  147. package/lib/packlets/messages/StatusBar.d.ts.map +1 -0
  148. package/lib/packlets/messages/StatusBar.js +52 -27
  149. package/lib/packlets/messages/StatusBar.js.map +1 -0
  150. package/lib/packlets/messages/Toast.d.ts.map +1 -0
  151. package/lib/packlets/messages/Toast.js +6 -3
  152. package/lib/packlets/messages/Toast.js.map +1 -0
  153. package/lib/packlets/messages/index.d.ts +2 -2
  154. package/lib/packlets/messages/index.d.ts.map +1 -0
  155. package/lib/packlets/messages/index.js +6 -1
  156. package/lib/packlets/messages/index.js.map +1 -0
  157. package/lib/packlets/messages/model.d.ts +74 -8
  158. package/lib/packlets/messages/model.d.ts.map +1 -0
  159. package/lib/packlets/messages/model.js +32 -4
  160. package/lib/packlets/messages/model.js.map +1 -0
  161. package/lib/packlets/messages/useLogReporter.d.ts.map +1 -0
  162. package/lib/packlets/messages/useLogReporter.js.map +1 -0
  163. package/lib/packlets/modal/ConfirmDialog.d.ts.map +1 -0
  164. package/lib/packlets/modal/ConfirmDialog.js.map +1 -0
  165. package/lib/packlets/modal/Modal.d.ts.map +1 -0
  166. package/lib/packlets/modal/Modal.js.map +1 -0
  167. package/lib/packlets/modal/index.d.ts.map +1 -0
  168. package/lib/packlets/modal/index.js.map +1 -0
  169. package/lib/packlets/print/PrintEnclosure.d.ts.map +1 -0
  170. package/lib/packlets/print/PrintEnclosure.js.map +1 -0
  171. package/lib/packlets/print/index.d.ts.map +1 -0
  172. package/lib/packlets/print/index.js.map +1 -0
  173. package/lib/packlets/print/openPrintWindow.d.ts.map +1 -0
  174. package/lib/packlets/print/openPrintWindow.js.map +1 -0
  175. package/lib/packlets/responsive/ResponsiveProvider.d.ts.map +1 -0
  176. package/lib/packlets/responsive/ResponsiveProvider.js.map +1 -0
  177. package/lib/packlets/responsive/index.d.ts.map +1 -0
  178. package/lib/packlets/responsive/index.js.map +1 -0
  179. package/lib/packlets/responsive/useResponsiveLayout.d.ts.map +1 -0
  180. package/lib/packlets/responsive/useResponsiveLayout.js.map +1 -0
  181. package/lib/packlets/selectors/EntityRow.d.ts.map +1 -0
  182. package/lib/packlets/selectors/EntityRow.js.map +1 -0
  183. package/lib/packlets/selectors/PreferredSelector.d.ts.map +1 -0
  184. package/lib/packlets/selectors/PreferredSelector.js.map +1 -0
  185. package/lib/packlets/selectors/index.d.ts.map +1 -0
  186. package/lib/packlets/selectors/index.js.map +1 -0
  187. package/lib/packlets/sidebar/CollectionSection.d.ts +40 -2
  188. package/lib/packlets/sidebar/CollectionSection.d.ts.map +1 -0
  189. package/lib/packlets/sidebar/CollectionSection.js +157 -42
  190. package/lib/packlets/sidebar/CollectionSection.js.map +1 -0
  191. package/lib/packlets/sidebar/EntityList.d.ts.map +1 -0
  192. package/lib/packlets/sidebar/EntityList.js.map +1 -0
  193. package/lib/packlets/sidebar/FilterBar.d.ts.map +1 -0
  194. package/lib/packlets/sidebar/FilterBar.js.map +1 -0
  195. package/lib/packlets/sidebar/FilterRow.d.ts.map +1 -0
  196. package/lib/packlets/sidebar/FilterRow.js.map +1 -0
  197. package/lib/packlets/sidebar/GroupedEntityList.d.ts.map +1 -0
  198. package/lib/packlets/sidebar/GroupedEntityList.js.map +1 -0
  199. package/lib/packlets/sidebar/SearchBar.d.ts.map +1 -0
  200. package/lib/packlets/sidebar/SearchBar.js.map +1 -0
  201. package/lib/packlets/sidebar/SidebarLayout.d.ts.map +1 -0
  202. package/lib/packlets/sidebar/SidebarLayout.js.map +1 -0
  203. package/lib/packlets/sidebar/index.d.ts +1 -1
  204. package/lib/packlets/sidebar/index.d.ts.map +1 -0
  205. package/lib/packlets/sidebar/index.js.map +1 -0
  206. package/lib/packlets/theme/ThemeProvider.d.ts.map +1 -0
  207. package/lib/packlets/theme/ThemeProvider.js.map +1 -0
  208. package/lib/packlets/theme/index.d.ts.map +1 -0
  209. package/lib/packlets/theme/index.js.map +1 -0
  210. package/lib/packlets/top-bar/ModeSelector.d.ts.map +1 -0
  211. package/lib/packlets/top-bar/ModeSelector.js.map +1 -0
  212. package/lib/packlets/top-bar/TabBar.d.ts +12 -0
  213. package/lib/packlets/top-bar/TabBar.d.ts.map +1 -0
  214. package/lib/packlets/top-bar/TabBar.js +4 -1
  215. package/lib/packlets/top-bar/TabBar.js.map +1 -0
  216. package/lib/packlets/top-bar/index.d.ts +1 -1
  217. package/lib/packlets/top-bar/index.d.ts.map +1 -0
  218. package/lib/packlets/top-bar/index.js.map +1 -0
  219. package/lib/packlets/url-sync/index.d.ts.map +1 -0
  220. package/lib/packlets/url-sync/index.js.map +1 -0
  221. package/lib/packlets/url-sync/useUrlSync.d.ts.map +1 -0
  222. package/lib/packlets/url-sync/useUrlSync.js.map +1 -0
  223. package/lib/styles/theme.css +194 -0
  224. package/package.json +11 -7
  225. package/tailwind-preset.cjs +102 -0
@@ -0,0 +1,194 @@
1
+ /*
2
+ * @fgv/ts-app-shell — Default theme
3
+ *
4
+ * Defines all semantic design tokens as CSS custom properties.
5
+ * Import this file once at your application entry point:
6
+ *
7
+ * import '@fgv/ts-app-shell/theme.css';
8
+ *
9
+ * Then add the Tailwind preset in your tailwind.config.js:
10
+ *
11
+ * const appShellPreset = require('@fgv/ts-app-shell/tailwind-preset');
12
+ * module.exports = { presets: [appShellPreset], ... };
13
+ *
14
+ * Dark mode: put class="dark" on <html> (or any ancestor).
15
+ * The ThemeProvider component handles this automatically.
16
+ *
17
+ * All variables use the --fgv- prefix and can be overridden by the consumer.
18
+ */
19
+
20
+ /* ============================================================
21
+ * LIGHT MODE (default / :root)
22
+ * ============================================================ */
23
+ :root {
24
+ /* --- Surfaces --- */
25
+ /* Base application surface (page background) */
26
+ --fgv-surface: #ffffff;
27
+ /* Slightly elevated surface (cards, panels) */
28
+ --fgv-surface-raised: #f3f4f6;
29
+ /* Alternate surface (zebra rows, secondary panels) */
30
+ --fgv-surface-alt: #f9fafb;
31
+ /* Modal / dialog backdrop overlay */
32
+ --fgv-backdrop: rgba(0, 0, 0, 0.45);
33
+ /* Hover highlight background (subtle, interactive rows) */
34
+ --fgv-hover: #f3f4f6;
35
+ /* Selected item background (listbox rows, picker items) */
36
+ --fgv-selected: #eff6ff;
37
+
38
+ /* --- Text --- */
39
+ /* High-emphasis body text — minimum 7:1 on --fgv-surface (WCAG AAA) */
40
+ --fgv-text-primary: #111827;
41
+ /* Medium-emphasis secondary text — ~5.4:1 on #fff (WCAG AA) */
42
+ --fgv-text-secondary: #374151;
43
+ /* De-emphasized muted text — ~3.5:1, use only for decorative / supporting copy */
44
+ --fgv-text-muted: #6b7280;
45
+ /* Faintest ghost text — intentionally low contrast, only for truly ornamental content */
46
+ --fgv-text-faint: #9ca3af;
47
+ /* Star / favorite accent (golden) */
48
+ --fgv-text-star: #d97706;
49
+
50
+ /* --- Borders --- */
51
+ /* Default divider / input border */
52
+ --fgv-border: #d1d5db;
53
+ /* Subtle divider (row separators, inner section borders) */
54
+ --fgv-border-subtle: #e5e7eb;
55
+
56
+ /* --- Brand colors --- */
57
+ /* Primary action color (buttons, active nav, links) */
58
+ --fgv-brand-primary: #2563eb;
59
+ /* Darker tonal variant (pressed state, dark top-bar) */
60
+ --fgv-brand-secondary: #1d4ed8;
61
+ /* Accent / highlight (hover text on dark surfaces, checkmarks) */
62
+ --fgv-brand-accent: #3b82f6;
63
+
64
+ /* --- Focus ring --- */
65
+ --fgv-focus-ring: #3b82f6;
66
+ /* Selected item left-border accent */
67
+ --fgv-selected-border: #2563eb;
68
+
69
+ /* --- Status: error / danger --- */
70
+ --fgv-status-error-bg: #fef2f2;
71
+ --fgv-status-error-surface: #fee2e2;
72
+ --fgv-status-error-btn: #dc2626;
73
+ --fgv-status-error-btn-hover: #b91c1c;
74
+ --fgv-status-error-icon: #dc2626;
75
+ --fgv-status-error-border: #fca5a5;
76
+ --fgv-status-error-accent: #ef4444;
77
+ --fgv-status-error-text: #b91c1c;
78
+ --fgv-status-error-strong: #991b1b;
79
+
80
+ /* --- Status: warning --- */
81
+ --fgv-status-warning-bg: #fffbeb;
82
+ --fgv-status-warning-surface: #fef3c7;
83
+ --fgv-status-warning-btn: #d97706;
84
+ --fgv-status-warning-btn-hover: #b45309;
85
+ --fgv-status-warning-icon: #d97706;
86
+ --fgv-status-warning-border: #fcd34d;
87
+ --fgv-status-warning-accent: #f59e0b;
88
+ --fgv-status-warning-text: #92400e;
89
+ --fgv-status-warning-strong: #78350f;
90
+
91
+ /* --- Status: info --- */
92
+ --fgv-status-info-bg: #eff6ff;
93
+ --fgv-status-info-surface: #dbeafe;
94
+ --fgv-status-info-btn: #2563eb;
95
+ --fgv-status-info-btn-hover: #1d4ed8;
96
+ --fgv-status-info-icon: #3b82f6;
97
+ --fgv-status-info-border: #93c5fd;
98
+ --fgv-status-info-accent: #60a5fa;
99
+ --fgv-status-info-text: #1e40af;
100
+ --fgv-status-info-strong: #1e3a8a;
101
+
102
+ /* --- Status: success --- */
103
+ --fgv-status-success-bg: #f0fdf4;
104
+ --fgv-status-success-surface: #dcfce7;
105
+ --fgv-status-success-btn: #16a34a;
106
+ --fgv-status-success-btn-hover: #15803d;
107
+ --fgv-status-success-icon: #16a34a;
108
+ --fgv-status-success-border: #86efac;
109
+ --fgv-status-success-accent: #4ade80;
110
+ --fgv-status-success-text: #166534;
111
+ --fgv-status-success-strong: #14532d;
112
+ }
113
+
114
+ /* ============================================================
115
+ * DARK MODE (.dark on any ancestor element)
116
+ * ============================================================ */
117
+ .dark {
118
+ /* --- Surfaces --- */
119
+ --fgv-surface: #1f2937;
120
+ --fgv-surface-raised: #374151;
121
+ --fgv-surface-alt: #111827;
122
+ --fgv-backdrop: rgba(0, 0, 0, 0.65);
123
+ --fgv-hover: #374151;
124
+ --fgv-selected: #1e3a5f;
125
+
126
+ /* --- Text --- */
127
+ /* ~14:1 on --fgv-surface dark (WCAG AAA) */
128
+ --fgv-text-primary: #f9fafb;
129
+ /* ~8:1 on dark surface (WCAG AA) */
130
+ --fgv-text-secondary: #d1d5db;
131
+ /* ~4.5:1 on dark surface (WCAG AA) */
132
+ --fgv-text-muted: #9ca3af;
133
+ /* Faint ghost text — intentionally low contrast */
134
+ --fgv-text-faint: #6b7280;
135
+ /* Star accent — brightened slightly for dark bg */
136
+ --fgv-text-star: #fbbf24;
137
+
138
+ /* --- Borders --- */
139
+ --fgv-border: #4b5563;
140
+ --fgv-border-subtle: #374151;
141
+
142
+ /* --- Brand colors --- */
143
+ --fgv-brand-primary: #3b82f6;
144
+ --fgv-brand-secondary: #1e3a5f;
145
+ --fgv-brand-accent: #60a5fa;
146
+
147
+ /* --- Focus ring --- */
148
+ --fgv-focus-ring: #60a5fa;
149
+ --fgv-selected-border: #3b82f6;
150
+
151
+ /* --- Status: error / danger --- */
152
+ --fgv-status-error-bg: #450a0a;
153
+ --fgv-status-error-surface: #7f1d1d;
154
+ --fgv-status-error-btn: #dc2626;
155
+ --fgv-status-error-btn-hover: #b91c1c;
156
+ --fgv-status-error-icon: #f87171;
157
+ --fgv-status-error-border: #7f1d1d;
158
+ --fgv-status-error-accent: #ef4444;
159
+ --fgv-status-error-text: #fca5a5;
160
+ --fgv-status-error-strong: #fecaca;
161
+
162
+ /* --- Status: warning --- */
163
+ --fgv-status-warning-bg: #451a03;
164
+ --fgv-status-warning-surface: #78350f;
165
+ --fgv-status-warning-btn: #d97706;
166
+ --fgv-status-warning-btn-hover: #b45309;
167
+ --fgv-status-warning-icon: #fbbf24;
168
+ --fgv-status-warning-border: #78350f;
169
+ --fgv-status-warning-accent: #f59e0b;
170
+ --fgv-status-warning-text: #fde68a;
171
+ --fgv-status-warning-strong: #fef3c7;
172
+
173
+ /* --- Status: info --- */
174
+ --fgv-status-info-bg: #0c1d3e;
175
+ --fgv-status-info-surface: #1e3a5f;
176
+ --fgv-status-info-btn: #3b82f6;
177
+ --fgv-status-info-btn-hover: #2563eb;
178
+ --fgv-status-info-icon: #60a5fa;
179
+ --fgv-status-info-border: #1e3a5f;
180
+ --fgv-status-info-accent: #93c5fd;
181
+ --fgv-status-info-text: #bfdbfe;
182
+ --fgv-status-info-strong: #dbeafe;
183
+
184
+ /* --- Status: success --- */
185
+ --fgv-status-success-bg: #052e16;
186
+ --fgv-status-success-surface: #14532d;
187
+ --fgv-status-success-btn: #16a34a;
188
+ --fgv-status-success-btn-hover: #15803d;
189
+ --fgv-status-success-icon: #4ade80;
190
+ --fgv-status-success-border: #14532d;
191
+ --fgv-status-success-accent: #86efac;
192
+ --fgv-status-success-text: #bbf7d0;
193
+ --fgv-status-success-strong: #dcfce7;
194
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.browser.d.ts","sourceRoot":"","sources":["../src/index.browser.ts"],"names":[],"mappings":"AACA,cAAc,SAAS,CAAC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.browser.js","sourceRoot":"","sources":["../src/index.browser.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,8DAA8D;AAC9D,0CAAwB","sourcesContent":["// Browser entry point - re-exports everything from main index\nexport * from './index';\n"]}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,cAAc,oBAAoB,CAAC;AAGnC,cAAc,qBAAqB,CAAC;AAGpC,cAAc,qBAAqB,CAAC;AAGpC,cAAc,kBAAkB,CAAC;AAGjC,cAAc,qBAAqB,CAAC;AAGpC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,sBAAsB,CAAC;AAGrC,cAAc,sBAAsB,CAAC;AAGrC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,sBAAsB,CAAC;AAGrC,cAAc,kBAAkB,CAAC;AAGjC,cAAc,kBAAkB,CAAC;AAGjC,cAAc,uBAAuB,CAAC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;;;;;;;;;;;;;;;AAEH,qBAAqB;AACrB,qDAAmC;AAEnC,wDAAwD;AACxD,sDAAoC;AAEpC,mDAAmD;AACnD,sDAAoC;AAEpC,eAAe;AACf,mDAAiC;AAEjC,6BAA6B;AAC7B,sDAAoC;AAEpC,mDAAmD;AACnD,qDAAmC;AAEnC,2BAA2B;AAC3B,qDAAmC;AAEnC,wCAAwC;AACxC,uDAAqC;AAErC,uEAAuE;AACvE,uDAAqC;AAErC,mDAAmD;AACnD,qDAAmC;AAEnC,2CAA2C;AAC3C,oDAAkC;AAElC,yCAAyC;AACzC,uDAAqC;AAErC,+BAA+B;AAC/B,mDAAiC;AAEjC,0BAA0B;AAC1B,mDAAiC;AAEjC,0CAA0C;AAC1C,wDAAsC","sourcesContent":["/**\n * \\@fgv/ts-app-shell - Shared React UI primitives for application shells.\n *\n * Provides reusable components for:\n * - Column cascade (master-detail drill-down)\n * - Compact sidebar with flyout filter selectors\n * - Toast notifications and log message panel\n * - Command palette\n * - Keybinding registry\n *\n * @packageDocumentation\n */\n\n// Top bar components\nexport * from './packlets/top-bar';\n\n// Messages infrastructure (context, toasts, status bar)\nexport * from './packlets/messages';\n\n// URL hash synchronization for mode/tab navigation\nexport * from './packlets/url-sync';\n\n// Modal dialog\nexport * from './packlets/modal';\n\n// Keyboard shortcut registry\nexport * from './packlets/keyboard';\n\n// Sidebar layout, search, filters, and entity list\nexport * from './packlets/sidebar';\n\n// Column cascade container\nexport * from './packlets/cascade';\n\n// Selectors (PreferredSelector popover)\nexport * from './packlets/selectors';\n\n// JSON drop zone (generic drop/paste target with converter validation)\nexport * from './packlets/drop-zone';\n\n// Generic edit field primitives for entity editors\nexport * from './packlets/editing';\n\n// Generic detail-view primitive components\nexport * from './packlets/detail';\n\n// AI assist hook (generic, context-free)\nexport * from './packlets/ai-assist';\n\n// Print popup window enclosure\nexport * from './packlets/print';\n\n// Theme provider and hook\nexport * from './packlets/theme';\n\n// Responsive layout detection and context\nexport * from './packlets/responsive';\n"]}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/packlets/ai-assist/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,qBAAqB,EACrB,WAAW,EACX,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACxB,MAAM,eAAe,CAAC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/packlets/ai-assist/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,6CAOuB;AANrB,oHAAA,qBAAqB,OAAA;AACrB,0GAAA,WAAW,OAAA","sourcesContent":["/**\n * AI assist packlet — generic hook for AI-assisted entity generation.\n * @packageDocumentation\n */\n\nexport {\n checkForAiErrorObject,\n useAiAssist,\n type IAiAssistAction,\n type IAiAssistResult,\n type IUseAiAssistParams,\n type IUseAiAssistResult\n} from './useAiAssist';\n"]}
@@ -57,6 +57,32 @@ export interface IUseAiAssistResult {
57
57
  * @returns Success with the validated entity, or failure.
58
58
  */
59
59
  readonly generateDirect: <TEntity>(provider: AiAssist.AiProviderId, prompt: AiAssist.AiPrompt, convert: (from: unknown) => Result<TEntity>, tools?: ReadonlyArray<AiAssist.AiServerToolConfig>) => Promise<Result<IAiAssistResult<TEntity>>>;
60
+ /**
61
+ * Execute an image-generation action: calls the provider's image API and
62
+ * returns the generated images.
63
+ * @returns Success with the image generation response, or failure.
64
+ */
65
+ readonly generateImages: (provider: AiAssist.AiProviderId, params: AiAssist.IAiImageGenerationParams, signal?: AbortSignal) => Promise<Result<AiAssist.IAiImageGenerationResponse>>;
66
+ /**
67
+ * List the models available from a provider, optionally filtered by capability.
68
+ * @returns Success with the resolved model list, or failure (no silent fallback).
69
+ */
70
+ readonly listModels: (provider: AiAssist.AiProviderId, capability?: AiAssist.AiModelCapability, signal?: AbortSignal) => Promise<Result<ReadonlyArray<AiAssist.IAiModelInfo>>>;
71
+ /**
72
+ * Stream a chat completion from the provider. The `onEvent` callback fires
73
+ * for every event (text deltas, tool progress, errors); the returned
74
+ * promise resolves with the aggregated final text and truncation flag on
75
+ * success, or with `Result.fail` when the stream couldn't be started or
76
+ * ended in a terminal error event.
77
+ */
78
+ readonly streamDirect: (provider: AiAssist.AiProviderId, prompt: AiAssist.AiPrompt, onEvent: (event: AiAssist.IAiStreamEvent) => void, options?: {
79
+ readonly tools?: ReadonlyArray<AiAssist.AiServerToolConfig>;
80
+ readonly messagesBefore?: ReadonlyArray<AiAssist.IChatMessage>;
81
+ readonly signal?: AbortSignal;
82
+ }) => Promise<Result<{
83
+ readonly fullText: string;
84
+ readonly truncated: boolean;
85
+ }>>;
60
86
  }
61
87
  /**
62
88
  * Checks whether a parsed AI response is an error object (with an "error" field)
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAiAssist.d.ts","sourceRoot":"","sources":["../../../src/packlets/ai-assist/useAiAssist.ts"],"names":[],"mappings":"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"}
@@ -20,6 +20,13 @@
20
20
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  * SOFTWARE.
22
22
  */
23
+ var __asyncValues = (this && this.__asyncValues) || function (o) {
24
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
25
+ var m = o[Symbol.asyncIterator], i;
26
+ return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
27
+ function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
28
+ function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
29
+ };
23
30
  Object.defineProperty(exports, "__esModule", { value: true });
24
31
  exports.checkForAiErrorObject = checkForAiErrorObject;
25
32
  exports.useAiAssist = useAiAssist;
@@ -37,6 +44,35 @@ const ts_utils_1 = require("@fgv/ts-utils");
37
44
  // ============================================================================
38
45
  // Helpers
39
46
  // ============================================================================
47
+ /**
48
+ * Resolves the API key for a dispatcher call, honoring `descriptor.needsSecret`.
49
+ *
50
+ * - When `descriptor.needsSecret === false` (e.g. ollama, openai-compat),
51
+ * returns the configured key if one happens to be set in the keystore, or
52
+ * an empty string otherwise. ts-extras omits the `Authorization` header
53
+ * entirely for empty keys, which self-hosted servers expect.
54
+ * - When `descriptor.needsSecret !== false`, requires `secretName`, an
55
+ * unlocked keystore, and a successful key fetch.
56
+ *
57
+ * @internal
58
+ */
59
+ function resolveApiKey(descriptor, providerConfig, keyStore) {
60
+ var _a;
61
+ const { provider, secretName } = providerConfig;
62
+ if (descriptor.needsSecret === false) {
63
+ if (!secretName || !(keyStore === null || keyStore === void 0 ? void 0 : keyStore.isUnlocked)) {
64
+ return (0, ts_utils_1.succeed)('');
65
+ }
66
+ return (0, ts_utils_1.succeed)((_a = keyStore.getApiKey(secretName).orDefault('')) !== null && _a !== void 0 ? _a : '');
67
+ }
68
+ if (!secretName) {
69
+ return (0, ts_utils_1.fail)(`Provider "${provider}" has no secret name configured`);
70
+ }
71
+ if (!keyStore) {
72
+ return (0, ts_utils_1.fail)('No keystore available');
73
+ }
74
+ return keyStore.getApiKey(secretName).withErrorFormat((m) => `Failed to get API key: ${m}`);
75
+ }
40
76
  /**
41
77
  * Checks whether a parsed AI response is an error object (with an "error" field)
42
78
  * rather than a valid entity. AI prompts instruct the model to return
@@ -76,11 +112,15 @@ function useAiAssist(params) {
76
112
  ? settings.defaultProvider
77
113
  : (_c = (_b = providers[0]) === null || _b === void 0 ? void 0 : _b.provider) !== null && _c !== void 0 ? _c : 'copy-paste';
78
114
  return providers.map((config) => {
79
- var _a, _b;
115
+ var _a;
80
116
  let isAvailable = true;
81
117
  let unavailableReason;
82
- if (config.provider !== 'copy-paste') {
83
- // API-based providers need a secret name and unlocked keystore with that secret
118
+ const descriptor = ts_extras_1.AiAssist.getProviderDescriptor(config.provider).orDefault();
119
+ // Self-hosted / no-secret providers (e.g. ollama, openai-compat) skip
120
+ // the keystore check entirely — they're available as long as the
121
+ // descriptor exists. Providers with `needsSecret: true` still require
122
+ // a configured secretName and an unlocked keystore.
123
+ if (config.provider !== 'copy-paste' && (descriptor === null || descriptor === void 0 ? void 0 : descriptor.needsSecret) !== false) {
84
124
  if (!config.secretName) {
85
125
  isAvailable = false;
86
126
  unavailableReason = 'No API key secret configured';
@@ -101,7 +141,7 @@ function useAiAssist(params) {
101
141
  }
102
142
  }
103
143
  }
104
- const label = (_b = (_a = ts_extras_1.AiAssist.getProviderDescriptor(config.provider).orDefault()) === null || _a === void 0 ? void 0 : _a.buttonLabel) !== null && _b !== void 0 ? _b : config.provider;
144
+ const label = (_a = descriptor === null || descriptor === void 0 ? void 0 : descriptor.buttonLabel) !== null && _a !== void 0 ? _a : config.provider;
105
145
  return {
106
146
  provider: config.provider,
107
147
  label,
@@ -131,16 +171,9 @@ function useAiAssist(params) {
131
171
  return (0, ts_utils_1.fail)(descriptorResult.message);
132
172
  }
133
173
  const descriptor = descriptorResult.value;
134
- if (!providerConfig.secretName) {
135
- return (0, ts_utils_1.fail)(`Provider "${provider}" has no secret name configured`);
136
- }
137
- if (!keyStore) {
138
- return (0, ts_utils_1.fail)('No keystore available');
139
- }
140
- // Get API key from keystore
141
- const apiKeyResult = keyStore.getApiKey(providerConfig.secretName);
174
+ const apiKeyResult = resolveApiKey(descriptor, providerConfig, keyStore);
142
175
  if (apiKeyResult.isFailure()) {
143
- return (0, ts_utils_1.fail)(`Failed to get API key: ${apiKeyResult.message}`);
176
+ return (0, ts_utils_1.fail)(apiKeyResult.message);
144
177
  }
145
178
  setIsWorking(true);
146
179
  try {
@@ -149,13 +182,17 @@ function useAiAssist(params) {
149
182
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
150
183
  // Resolve effective tools: per-call override > settings defaults > none
151
184
  const effectiveTools = ts_extras_1.AiAssist.resolveEffectiveTools(descriptor, providerConfig.tools, tools);
152
- // Call the API — through proxy if configured, otherwise direct
185
+ // Call the API — through proxy if configured, otherwise direct.
186
+ // The unified request orders history before the current turn; the
187
+ // correction-retry turns naturally append after the current user turn.
188
+ const base = prompt.toRequest();
153
189
  const completionParams = {
154
190
  descriptor,
155
191
  apiKey: apiKeyResult.value,
156
- prompt,
157
- additionalMessages: correctionMessages.length > 0 ? correctionMessages : undefined,
192
+ system: base.system,
193
+ messages: [...base.messages, ...correctionMessages],
158
194
  modelOverride: providerConfig.model,
195
+ endpoint: providerConfig.endpoint,
159
196
  logger,
160
197
  tools: effectiveTools.length > 0 ? effectiveTools : undefined
161
198
  };
@@ -211,13 +248,183 @@ function useAiAssist(params) {
211
248
  return (0, ts_utils_1.fail)(`AI response validation failed after ${maxAttempts} attempts: ${entityResult.message}`);
212
249
  }
213
250
  }
214
- // Unreachable, but TypeScript needs it
251
+ /* c8 ignore next 2 - unreachable: every loop iteration returns; TypeScript needs the trailing return */
215
252
  return (0, ts_utils_1.fail)('AI generation failed');
216
253
  }
217
254
  finally {
218
255
  setIsWorking(false);
219
256
  }
220
257
  }, [settings, keyStore, logger]);
221
- return { actions, isWorking, copyPrompt, generateDirect };
258
+ const generateImages = (0, react_1.useCallback)(async (provider, params, signal) => {
259
+ const providerConfig = settings === null || settings === void 0 ? void 0 : settings.providers.find((p) => p.provider === provider);
260
+ if (!providerConfig) {
261
+ return (0, ts_utils_1.fail)(`Provider "${provider}" not configured`);
262
+ }
263
+ const descriptorResult = ts_extras_1.AiAssist.getProviderDescriptor(provider);
264
+ if (descriptorResult.isFailure()) {
265
+ return (0, ts_utils_1.fail)(descriptorResult.message);
266
+ }
267
+ const descriptor = descriptorResult.value;
268
+ if (!ts_extras_1.AiAssist.supportsImageGeneration(descriptor)) {
269
+ return (0, ts_utils_1.fail)(`Provider "${provider}" does not support image generation`);
270
+ }
271
+ const apiKeyResult = resolveApiKey(descriptor, providerConfig, keyStore);
272
+ if (apiKeyResult.isFailure()) {
273
+ return (0, ts_utils_1.fail)(apiKeyResult.message);
274
+ }
275
+ setIsWorking(true);
276
+ try {
277
+ const requestParams = {
278
+ descriptor,
279
+ apiKey: apiKeyResult.value,
280
+ params,
281
+ modelOverride: providerConfig.model,
282
+ endpoint: providerConfig.endpoint,
283
+ logger,
284
+ signal
285
+ };
286
+ const useProxy = !!(settings === null || settings === void 0 ? void 0 : settings.proxyUrl) && (settings.proxyAllProviders === true || descriptor.corsRestricted);
287
+ const result = useProxy
288
+ ? await ts_extras_1.AiAssist.callProxiedImageGeneration(settings.proxyUrl, requestParams)
289
+ : await ts_extras_1.AiAssist.callProviderImageGeneration(requestParams);
290
+ if (result.isFailure()) {
291
+ logger === null || logger === void 0 ? void 0 : logger.error(`AI image generation failed: ${result.message}`);
292
+ }
293
+ return result;
294
+ }
295
+ finally {
296
+ setIsWorking(false);
297
+ }
298
+ }, [settings, keyStore, logger]);
299
+ const listModels = (0, react_1.useCallback)(async (provider, capability, signal) => {
300
+ const providerConfig = settings === null || settings === void 0 ? void 0 : settings.providers.find((p) => p.provider === provider);
301
+ if (!providerConfig) {
302
+ return (0, ts_utils_1.fail)(`Provider "${provider}" not configured`);
303
+ }
304
+ const descriptorResult = ts_extras_1.AiAssist.getProviderDescriptor(provider);
305
+ if (descriptorResult.isFailure()) {
306
+ return (0, ts_utils_1.fail)(descriptorResult.message);
307
+ }
308
+ const descriptor = descriptorResult.value;
309
+ const apiKeyResult = resolveApiKey(descriptor, providerConfig, keyStore);
310
+ if (apiKeyResult.isFailure()) {
311
+ return (0, ts_utils_1.fail)(apiKeyResult.message);
312
+ }
313
+ const requestParams = Object.assign(Object.assign({ descriptor, apiKey: apiKeyResult.value }, (capability !== undefined ? { capability } : {})), { endpoint: providerConfig.endpoint, logger,
314
+ signal });
315
+ const useProxy = !!(settings === null || settings === void 0 ? void 0 : settings.proxyUrl) && (settings.proxyAllProviders === true || descriptor.corsRestricted);
316
+ const result = useProxy
317
+ ? await ts_extras_1.AiAssist.callProxiedListModels(settings.proxyUrl, requestParams)
318
+ : await ts_extras_1.AiAssist.callProviderListModels(requestParams);
319
+ if (result.isFailure()) {
320
+ logger === null || logger === void 0 ? void 0 : logger.error(`AI list-models failed: ${result.message}`);
321
+ }
322
+ return result;
323
+ }, [settings, keyStore, logger]);
324
+ const streamDirect = (0, react_1.useCallback)(async (provider, prompt, onEvent, options) => {
325
+ var _a, e_1, _b, _c;
326
+ var _d;
327
+ const providerConfig = settings === null || settings === void 0 ? void 0 : settings.providers.find((p) => p.provider === provider);
328
+ if (!providerConfig) {
329
+ return (0, ts_utils_1.fail)(`Provider "${provider}" not configured`);
330
+ }
331
+ const descriptorResult = ts_extras_1.AiAssist.getProviderDescriptor(provider);
332
+ if (descriptorResult.isFailure()) {
333
+ return (0, ts_utils_1.fail)(descriptorResult.message);
334
+ }
335
+ const descriptor = descriptorResult.value;
336
+ const apiKeyResult = resolveApiKey(descriptor, providerConfig, keyStore);
337
+ if (apiKeyResult.isFailure()) {
338
+ return (0, ts_utils_1.fail)(apiKeyResult.message);
339
+ }
340
+ const effectiveTools = ts_extras_1.AiAssist.resolveEffectiveTools(descriptor, providerConfig.tools, options === null || options === void 0 ? void 0 : options.tools);
341
+ // Order prior history before the current user turn (the unified request shape).
342
+ const base = prompt.toRequest();
343
+ const requestParams = {
344
+ descriptor,
345
+ apiKey: apiKeyResult.value,
346
+ system: base.system,
347
+ messages: [...((_d = options === null || options === void 0 ? void 0 : options.messagesBefore) !== null && _d !== void 0 ? _d : []), ...base.messages],
348
+ modelOverride: providerConfig.model,
349
+ endpoint: providerConfig.endpoint,
350
+ logger,
351
+ tools: effectiveTools.length > 0 ? effectiveTools : undefined,
352
+ signal: options === null || options === void 0 ? void 0 : options.signal
353
+ };
354
+ const useProxy = !!(settings === null || settings === void 0 ? void 0 : settings.proxyUrl) && (settings.proxyAllProviders === true || descriptor.streamingCorsRestricted);
355
+ const openResult = useProxy
356
+ ? await ts_extras_1.AiAssist.callProxiedCompletionStream(settings.proxyUrl, requestParams)
357
+ : await ts_extras_1.AiAssist.callProviderCompletionStream(requestParams);
358
+ if (openResult.isFailure()) {
359
+ logger === null || logger === void 0 ? void 0 : logger.error(`AI streaming failed to start: ${openResult.message}`);
360
+ return (0, ts_utils_1.fail)(openResult.message);
361
+ }
362
+ setIsWorking(true);
363
+ let fullText = '';
364
+ let truncated = false;
365
+ let terminalError;
366
+ let receivedTerminalEvent = false;
367
+ try {
368
+ try {
369
+ for (var _e = true, _f = __asyncValues(openResult.value), _g; _g = await _f.next(), _a = _g.done, !_a; _e = true) {
370
+ _c = _g.value;
371
+ _e = false;
372
+ const event = _c;
373
+ try {
374
+ onEvent(event);
375
+ }
376
+ catch (err) {
377
+ // Consumer callback threw (e.g. setState-after-unmount). Convert
378
+ // into a terminal failure so streamDirect always resolves to a
379
+ // Result instead of rejecting and breaking its contract.
380
+ terminalError = `AI stream event handler failed: ${err instanceof Error ? err.message : String(err)}`;
381
+ receivedTerminalEvent = true;
382
+ break;
383
+ }
384
+ if (event.type === 'text-delta') {
385
+ fullText += event.delta;
386
+ }
387
+ else if (event.type === 'done') {
388
+ fullText = event.fullText;
389
+ truncated = event.truncated;
390
+ receivedTerminalEvent = true;
391
+ }
392
+ else if (event.type === 'error') {
393
+ terminalError = event.message;
394
+ receivedTerminalEvent = true;
395
+ }
396
+ }
397
+ }
398
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
399
+ finally {
400
+ try {
401
+ if (!_e && !_a && (_b = _f.return)) await _b.call(_f);
402
+ }
403
+ finally { if (e_1) throw e_1.error; }
404
+ }
405
+ }
406
+ finally {
407
+ setIsWorking(false);
408
+ }
409
+ if (terminalError !== undefined) {
410
+ logger === null || logger === void 0 ? void 0 : logger.error(`AI streaming ended in error: ${terminalError}`);
411
+ return (0, ts_utils_1.fail)(terminalError);
412
+ }
413
+ if (!receivedTerminalEvent) {
414
+ const message = 'AI stream ended without a terminal done or error event';
415
+ logger === null || logger === void 0 ? void 0 : logger.error(message);
416
+ return (0, ts_utils_1.fail)(message);
417
+ }
418
+ return (0, ts_utils_1.succeed)({ fullText, truncated });
419
+ }, [settings, keyStore, logger]);
420
+ return {
421
+ actions,
422
+ isWorking,
423
+ copyPrompt,
424
+ generateDirect,
425
+ generateImages,
426
+ listModels,
427
+ streamDirect
428
+ };
222
429
  }
223
430
  //# sourceMappingURL=useAiAssist.js.map