@ciridae/ui-agent 0.3.2 → 0.3.6

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 (250) hide show
  1. package/README.md +8 -2
  2. package/dist/ciridae-agent.css +5 -0
  3. package/dist/ciridae-agent.css.d.ts +2 -0
  4. package/dist/components/agentic/agent-card.d.ts +28 -0
  5. package/dist/components/agentic/agent-card.js +27 -0
  6. package/dist/components/agentic/agent-composer.d.ts +42 -0
  7. package/dist/components/agentic/agent-composer.js +89 -0
  8. package/dist/components/agentic/agent-context.d.ts +28 -0
  9. package/dist/components/agentic/agent-context.js +102 -0
  10. package/dist/components/agentic/agent-plan.js +1 -1
  11. package/dist/components/agentic/approval-request.d.ts +1 -1
  12. package/dist/components/agentic/assistant-mark-icon.d.ts +9 -0
  13. package/dist/components/agentic/assistant-mark-icon.js +12 -0
  14. package/dist/components/agentic/attachments-actions.d.ts +10 -0
  15. package/dist/components/agentic/attachments-actions.js +142 -0
  16. package/dist/components/agentic/attachments-context.d.ts +25 -0
  17. package/dist/components/agentic/attachments-context.js +196 -0
  18. package/dist/components/agentic/attachments-model.d.ts +23 -0
  19. package/dist/components/agentic/attachments-model.js +226 -0
  20. package/dist/components/agentic/attachments-preview.d.ts +7 -0
  21. package/dist/components/agentic/attachments-preview.js +25 -0
  22. package/dist/components/agentic/attachments-types.d.ts +87 -0
  23. package/dist/components/agentic/attachments-types.js +1 -0
  24. package/dist/components/agentic/attachments-ui.d.ts +14 -0
  25. package/dist/components/agentic/attachments-ui.js +90 -0
  26. package/dist/components/agentic/attachments.d.ts +5 -124
  27. package/dist/components/agentic/attachments.js +5 -591
  28. package/dist/components/agentic/audio-player.d.ts +16 -0
  29. package/dist/components/agentic/audio-player.js +21 -0
  30. package/dist/components/agentic/breathing-grid-icon.css +45 -0
  31. package/dist/components/agentic/breathing-grid-icon.css.d.ts +2 -0
  32. package/dist/components/agentic/breathing-grid-icon.d.ts +6 -0
  33. package/dist/components/agentic/breathing-grid-icon.js +7 -0
  34. package/dist/components/agentic/canvas.d.ts +10 -0
  35. package/dist/components/agentic/canvas.js +22 -0
  36. package/dist/components/agentic/code-block.js +1 -1
  37. package/dist/components/agentic/commit.d.ts +57 -0
  38. package/dist/components/agentic/commit.js +85 -0
  39. package/dist/components/agentic/connection.d.ts +3 -0
  40. package/dist/components/agentic/connection.js +8 -0
  41. package/dist/components/agentic/controls.d.ts +6 -0
  42. package/dist/components/agentic/controls.js +7 -0
  43. package/dist/components/agentic/conversation.d.ts +1 -1
  44. package/dist/components/agentic/edge.d.ts +8 -0
  45. package/dist/components/agentic/edge.js +65 -0
  46. package/dist/components/agentic/effort-selector.d.ts +43 -0
  47. package/dist/components/agentic/effort-selector.js +47 -0
  48. package/dist/components/agentic/environment-variables.d.ts +32 -0
  49. package/dist/components/agentic/environment-variables.js +66 -0
  50. package/dist/components/agentic/jsx-preview.d.ts +19 -0
  51. package/dist/components/agentic/jsx-preview.js +34 -0
  52. package/dist/components/agentic/message.d.ts +4 -4
  53. package/dist/components/agentic/message.js +48 -8
  54. package/dist/components/agentic/mic-selector.d.ts +58 -0
  55. package/dist/components/agentic/mic-selector.js +173 -0
  56. package/dist/components/agentic/node.d.ts +26 -0
  57. package/dist/components/agentic/node.js +29 -0
  58. package/dist/components/agentic/open-in-chat.d.ts +9 -0
  59. package/dist/components/agentic/open-in-chat.js +8 -0
  60. package/dist/components/agentic/package-info.d.ts +30 -0
  61. package/dist/components/agentic/package-info.js +44 -0
  62. package/dist/components/agentic/panel.d.ts +6 -0
  63. package/dist/components/agentic/panel.js +7 -0
  64. package/dist/components/agentic/persona.d.ts +16 -0
  65. package/dist/components/agentic/persona.js +116 -0
  66. package/dist/components/agentic/prompt-input-select.js +1 -1
  67. package/dist/components/agentic/prompt-input.d.ts +9 -9
  68. package/dist/components/agentic/prompt-input.js +7 -7
  69. package/dist/components/agentic/reasoning.d.ts +1 -1
  70. package/dist/components/agentic/reasoning.js +2 -2
  71. package/dist/components/agentic/run-checkpoint.d.ts +1 -1
  72. package/dist/components/agentic/shimmer.d.ts +2 -2
  73. package/dist/components/agentic/speech-input.d.ts +24 -0
  74. package/dist/components/agentic/speech-input.js +30 -0
  75. package/dist/components/agentic/stack-trace.d.ts +27 -0
  76. package/dist/components/agentic/stack-trace.js +76 -0
  77. package/dist/components/agentic/suggestion.d.ts +12 -0
  78. package/dist/components/agentic/suggestion.js +15 -0
  79. package/dist/components/agentic/test-results.d.ts +51 -0
  80. package/dist/components/agentic/test-results.js +83 -0
  81. package/dist/components/agentic/theme.d.ts +5 -0
  82. package/dist/components/agentic/theme.js +31 -0
  83. package/dist/components/agentic/tool.d.ts +1 -1
  84. package/dist/components/agentic/toolbar.d.ts +6 -0
  85. package/dist/components/agentic/toolbar.js +7 -0
  86. package/dist/components/agentic/transcription.d.ts +20 -0
  87. package/dist/components/agentic/transcription.js +32 -0
  88. package/dist/components/agentic/use-controllable-state.d.ts +6 -0
  89. package/dist/components/agentic/use-controllable-state.js +13 -0
  90. package/dist/components/agentic/voice-selector.d.ts +69 -0
  91. package/dist/components/agentic/voice-selector.js +122 -0
  92. package/dist/components/blocks/artifact-workbench.d.ts +9 -0
  93. package/dist/components/blocks/artifact-workbench.js +28 -0
  94. package/dist/components/blocks/tool-approval-panel.d.ts +11 -0
  95. package/dist/components/blocks/tool-approval-panel.js +11 -0
  96. package/dist/components/blocks/voice-agent-panel.d.ts +14 -0
  97. package/dist/components/blocks/voice-agent-panel.js +60 -0
  98. package/dist/components/blocks/workflow-canvas-panel.d.ts +18 -0
  99. package/dist/components/blocks/workflow-canvas-panel.js +106 -0
  100. package/dist/entries/agent-card.d.ts +1 -0
  101. package/dist/entries/agent-card.js +1 -0
  102. package/dist/entries/agent-composer.d.ts +1 -0
  103. package/dist/entries/agent-composer.js +1 -0
  104. package/dist/entries/agent-context.d.ts +1 -0
  105. package/dist/entries/agent-context.js +1 -0
  106. package/dist/entries/agent-plan.d.ts +1 -0
  107. package/dist/entries/agent-plan.js +1 -0
  108. package/dist/entries/agent-queue.d.ts +1 -0
  109. package/dist/entries/agent-queue.js +1 -0
  110. package/dist/entries/agent-task.d.ts +1 -0
  111. package/dist/entries/agent-task.js +1 -0
  112. package/dist/entries/agentic-types.d.ts +1 -0
  113. package/dist/entries/agentic-types.js +1 -0
  114. package/dist/entries/approval-request.d.ts +1 -0
  115. package/dist/entries/approval-request.js +1 -0
  116. package/dist/entries/artifact-workbench.d.ts +1 -0
  117. package/dist/entries/artifact-workbench.js +1 -0
  118. package/dist/entries/artifact.d.ts +1 -0
  119. package/dist/entries/artifact.js +1 -0
  120. package/dist/entries/assistant-mark-icon.d.ts +1 -0
  121. package/dist/entries/assistant-mark-icon.js +1 -0
  122. package/dist/entries/attachments-actions.d.ts +1 -0
  123. package/dist/entries/attachments-actions.js +1 -0
  124. package/dist/entries/attachments-context.d.ts +1 -0
  125. package/dist/entries/attachments-context.js +1 -0
  126. package/dist/entries/attachments-model.d.ts +1 -0
  127. package/dist/entries/attachments-model.js +1 -0
  128. package/dist/entries/attachments-preview.d.ts +1 -0
  129. package/dist/entries/attachments-preview.js +1 -0
  130. package/dist/entries/attachments-types.d.ts +1 -0
  131. package/dist/entries/attachments-types.js +1 -0
  132. package/dist/entries/attachments-ui.d.ts +1 -0
  133. package/dist/entries/attachments-ui.js +1 -0
  134. package/dist/entries/attachments.d.ts +1 -0
  135. package/dist/entries/attachments.js +1 -0
  136. package/dist/entries/audio-player.d.ts +1 -0
  137. package/dist/entries/audio-player.js +1 -0
  138. package/dist/entries/breathing-grid-icon.d.ts +1 -0
  139. package/dist/entries/breathing-grid-icon.js +1 -0
  140. package/dist/entries/canvas.d.ts +1 -0
  141. package/dist/entries/canvas.js +1 -0
  142. package/dist/entries/clipboard.d.ts +1 -0
  143. package/dist/entries/clipboard.js +1 -0
  144. package/dist/entries/code-block.d.ts +1 -0
  145. package/dist/entries/code-block.js +1 -0
  146. package/dist/entries/commit.d.ts +1 -0
  147. package/dist/entries/commit.js +1 -0
  148. package/dist/entries/connection.d.ts +1 -0
  149. package/dist/entries/connection.js +1 -0
  150. package/dist/entries/controls.d.ts +1 -0
  151. package/dist/entries/controls.js +1 -0
  152. package/dist/entries/conversation.d.ts +1 -0
  153. package/dist/entries/conversation.js +1 -0
  154. package/dist/entries/edge.d.ts +1 -0
  155. package/dist/entries/edge.js +1 -0
  156. package/dist/entries/effort-selector.d.ts +1 -0
  157. package/dist/entries/effort-selector.js +1 -0
  158. package/dist/entries/environment-variables.d.ts +1 -0
  159. package/dist/entries/environment-variables.js +1 -0
  160. package/dist/entries/file-tree.d.ts +1 -0
  161. package/dist/entries/file-tree.js +1 -0
  162. package/dist/entries/image.d.ts +1 -0
  163. package/dist/entries/image.js +1 -0
  164. package/dist/entries/inline-citation.d.ts +1 -0
  165. package/dist/entries/inline-citation.js +1 -0
  166. package/dist/entries/jsx-preview.d.ts +1 -0
  167. package/dist/entries/jsx-preview.js +1 -0
  168. package/dist/entries/message-branch.d.ts +1 -0
  169. package/dist/entries/message-branch.js +1 -0
  170. package/dist/entries/message.d.ts +1 -0
  171. package/dist/entries/message.js +1 -0
  172. package/dist/entries/mic-selector.d.ts +1 -0
  173. package/dist/entries/mic-selector.js +1 -0
  174. package/dist/entries/model-selector.d.ts +1 -0
  175. package/dist/entries/model-selector.js +1 -0
  176. package/dist/entries/node.d.ts +1 -0
  177. package/dist/entries/node.js +1 -0
  178. package/dist/entries/open-in-chat.d.ts +1 -0
  179. package/dist/entries/open-in-chat.js +1 -0
  180. package/dist/entries/package-info.d.ts +1 -0
  181. package/dist/entries/package-info.js +1 -0
  182. package/dist/entries/panel.d.ts +1 -0
  183. package/dist/entries/panel.js +1 -0
  184. package/dist/entries/persona.d.ts +1 -0
  185. package/dist/entries/persona.js +1 -0
  186. package/dist/entries/prompt-input-action-menu.d.ts +1 -0
  187. package/dist/entries/prompt-input-action-menu.js +1 -0
  188. package/dist/entries/prompt-input-command.d.ts +1 -0
  189. package/dist/entries/prompt-input-command.js +1 -0
  190. package/dist/entries/prompt-input-hover-card.d.ts +1 -0
  191. package/dist/entries/prompt-input-hover-card.js +1 -0
  192. package/dist/entries/prompt-input-select.d.ts +1 -0
  193. package/dist/entries/prompt-input-select.js +1 -0
  194. package/dist/entries/prompt-input-tabs.d.ts +1 -0
  195. package/dist/entries/prompt-input-tabs.js +1 -0
  196. package/dist/entries/prompt-input.d.ts +1 -0
  197. package/dist/entries/prompt-input.js +1 -0
  198. package/dist/entries/reasoning.d.ts +1 -0
  199. package/dist/entries/reasoning.js +1 -0
  200. package/dist/entries/run-checkpoint.d.ts +1 -0
  201. package/dist/entries/run-checkpoint.js +1 -0
  202. package/dist/entries/run-trace.d.ts +1 -0
  203. package/dist/entries/run-trace.js +1 -0
  204. package/dist/entries/sandbox.d.ts +1 -0
  205. package/dist/entries/sandbox.js +1 -0
  206. package/dist/entries/schema-display.d.ts +1 -0
  207. package/dist/entries/schema-display.js +1 -0
  208. package/dist/entries/shimmer.d.ts +1 -0
  209. package/dist/entries/shimmer.js +1 -0
  210. package/dist/entries/snippet.d.ts +1 -0
  211. package/dist/entries/snippet.js +1 -0
  212. package/dist/entries/sources.d.ts +1 -0
  213. package/dist/entries/sources.js +1 -0
  214. package/dist/entries/speech-input.d.ts +1 -0
  215. package/dist/entries/speech-input.js +1 -0
  216. package/dist/entries/stack-trace.d.ts +1 -0
  217. package/dist/entries/stack-trace.js +1 -0
  218. package/dist/entries/suggestion.d.ts +1 -0
  219. package/dist/entries/suggestion.js +1 -0
  220. package/dist/entries/terminal.d.ts +1 -0
  221. package/dist/entries/terminal.js +1 -0
  222. package/dist/entries/test-results.d.ts +1 -0
  223. package/dist/entries/test-results.js +1 -0
  224. package/dist/entries/theme.d.ts +1 -0
  225. package/dist/entries/theme.js +1 -0
  226. package/dist/entries/tool-approval-panel.d.ts +1 -0
  227. package/dist/entries/tool-approval-panel.js +1 -0
  228. package/dist/entries/tool.d.ts +1 -0
  229. package/dist/entries/tool.js +1 -0
  230. package/dist/entries/toolbar.d.ts +1 -0
  231. package/dist/entries/toolbar.js +1 -0
  232. package/dist/entries/transcription.d.ts +1 -0
  233. package/dist/entries/transcription.js +1 -0
  234. package/dist/entries/types.d.ts +1 -0
  235. package/dist/entries/types.js +1 -0
  236. package/dist/entries/use-controllable-state.d.ts +1 -0
  237. package/dist/entries/use-controllable-state.js +1 -0
  238. package/dist/entries/voice-agent-panel.d.ts +1 -0
  239. package/dist/entries/voice-agent-panel.js +1 -0
  240. package/dist/entries/voice-selector.d.ts +1 -0
  241. package/dist/entries/voice-selector.js +1 -0
  242. package/dist/entries/web-preview.d.ts +1 -0
  243. package/dist/entries/web-preview.js +1 -0
  244. package/dist/entries/workflow-canvas-panel.d.ts +1 -0
  245. package/dist/entries/workflow-canvas-panel.js +1 -0
  246. package/dist/style-imports.d.ts +3 -0
  247. package/dist/styled-system/styles.css +422 -0
  248. package/dist/styled-system/styles.css.d.ts +2 -0
  249. package/dist/styles.css +7 -0
  250. package/package.json +17 -137
@@ -0,0 +1,196 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /* eslint-disable react-refresh/only-export-components */
3
+ import * as React from "react";
4
+ import { captureBrowserScreenshot, fileToAttachment, getAttachmentError, getInitialScreenshotStatus, isScreenshotSupported, isIgnoredScreenshotError, revokeObjectUrls, revokeOwnedObjectUrls, revokeRemovedObjectUrls, } from "./attachments-model";
5
+ const AttachmentControllerContext = React.createContext(null);
6
+ const AttachmentsContext = React.createContext(null);
7
+ const AttachmentItemContext = React.createContext(null);
8
+ function AttachmentProvider({ accept, attachments, children, defaultAttachments = [], maxAttachmentSizeMb = 10, maxAttachments = 4, multiple = true, onAttachmentError, onAttachmentsChange, onCaptureScreenshot, }) {
9
+ const fileInputRef = React.useRef(null);
10
+ const { files, filesRef, ownedObjectUrlsRef, updateAttachments } = useAttachmentFileState({
11
+ attachments,
12
+ defaultAttachments,
13
+ onAttachmentsChange,
14
+ });
15
+ const { add, clear, error, openFileDialog, remove } = useAttachmentMutations({
16
+ accept,
17
+ fileInputRef,
18
+ filesRef,
19
+ maxAttachmentSizeMb,
20
+ maxAttachments,
21
+ onAttachmentError,
22
+ ownedObjectUrlsRef,
23
+ updateAttachments,
24
+ });
25
+ const { screenshotStatus, takeScreenshot } = useAttachmentScreenshot({
26
+ add,
27
+ onCaptureScreenshot,
28
+ });
29
+ const contextValue = React.useMemo(() => ({
30
+ accept,
31
+ add,
32
+ clear,
33
+ error,
34
+ files,
35
+ maxAttachmentSizeMb,
36
+ maxAttachments,
37
+ openFileDialog,
38
+ remove,
39
+ screenshotStatus,
40
+ takeScreenshot,
41
+ }), [
42
+ accept,
43
+ add,
44
+ clear,
45
+ error,
46
+ files,
47
+ maxAttachmentSizeMb,
48
+ maxAttachments,
49
+ openFileDialog,
50
+ remove,
51
+ screenshotStatus,
52
+ takeScreenshot,
53
+ ]);
54
+ return (_jsxs(AttachmentControllerContext.Provider, { value: contextValue, children: [_jsx("input", { "aria-hidden": "true", accept: accept, className: "hidden", multiple: multiple, onChange: (event) => {
55
+ if (event.currentTarget.files)
56
+ add(event.currentTarget.files);
57
+ event.currentTarget.value = "";
58
+ }, ref: fileInputRef, tabIndex: -1, type: "file" }), children] }));
59
+ }
60
+ function useAttachmentFileState({ attachments, defaultAttachments, onAttachmentsChange, }) {
61
+ const isControlled = attachments !== undefined;
62
+ const isControlledRef = React.useRef(isControlled);
63
+ const [localAttachments, setLocalAttachments] = React.useState(defaultAttachments);
64
+ const files = attachments ?? localAttachments;
65
+ const filesRef = React.useRef(files);
66
+ const ownedObjectUrlsRef = React.useRef(new Set());
67
+ React.useEffect(() => {
68
+ const previous = filesRef.current;
69
+ if (previous !== files) {
70
+ if (isControlled)
71
+ revokeRemovedObjectUrls(previous, files, ownedObjectUrlsRef.current);
72
+ filesRef.current = files;
73
+ }
74
+ }, [files, isControlled]);
75
+ React.useEffect(() => {
76
+ isControlledRef.current = isControlled;
77
+ }, [isControlled]);
78
+ React.useEffect(() => () => {
79
+ if (isControlledRef.current)
80
+ revokeOwnedObjectUrls(ownedObjectUrlsRef.current);
81
+ else
82
+ revokeObjectUrls(filesRef.current);
83
+ }, []);
84
+ const updateAttachments = React.useCallback((updater) => {
85
+ const previous = filesRef.current;
86
+ const next = updater(previous);
87
+ filesRef.current = next;
88
+ if (isControlled) {
89
+ revokeRemovedObjectUrls(previous, next, ownedObjectUrlsRef.current);
90
+ }
91
+ else {
92
+ revokeRemovedObjectUrls(previous, next);
93
+ setLocalAttachments(next);
94
+ }
95
+ onAttachmentsChange?.(next);
96
+ }, [isControlled, onAttachmentsChange]);
97
+ return { files, filesRef, ownedObjectUrlsRef, updateAttachments };
98
+ }
99
+ function useAttachmentMutations({ accept, fileInputRef, filesRef, maxAttachmentSizeMb, maxAttachments, onAttachmentError, ownedObjectUrlsRef, updateAttachments, }) {
100
+ const [error, setError] = React.useState();
101
+ const reportError = React.useCallback((nextError) => {
102
+ setError(nextError);
103
+ onAttachmentError?.(nextError);
104
+ }, [onAttachmentError]);
105
+ const add = React.useCallback((fileList, options) => {
106
+ const incoming = Array.from(fileList);
107
+ if (incoming.length === 0)
108
+ return;
109
+ const validationError = getAttachmentError({
110
+ accept,
111
+ currentCount: filesRef.current.length,
112
+ files: incoming,
113
+ maxAttachmentSizeMb,
114
+ maxAttachments,
115
+ });
116
+ if (validationError) {
117
+ reportError(validationError);
118
+ return;
119
+ }
120
+ const nextAttachments = incoming.map((file) => {
121
+ const attachment = fileToAttachment(file, options?.kind ?? "file");
122
+ if (attachment.url?.startsWith("blob:"))
123
+ ownedObjectUrlsRef.current.add(attachment.url);
124
+ return attachment;
125
+ });
126
+ setError(undefined);
127
+ updateAttachments((current) => [...current, ...nextAttachments]);
128
+ if (fileInputRef.current)
129
+ fileInputRef.current.value = "";
130
+ }, [
131
+ accept,
132
+ fileInputRef,
133
+ filesRef,
134
+ maxAttachmentSizeMb,
135
+ maxAttachments,
136
+ ownedObjectUrlsRef,
137
+ reportError,
138
+ updateAttachments,
139
+ ]);
140
+ const remove = React.useCallback((id) => {
141
+ updateAttachments((current) => current.filter((attachment) => attachment.id !== id));
142
+ }, [updateAttachments]);
143
+ const clear = React.useCallback(() => {
144
+ updateAttachments(() => []);
145
+ }, [updateAttachments]);
146
+ const openFileDialog = React.useCallback(() => {
147
+ fileInputRef.current?.click();
148
+ }, [fileInputRef]);
149
+ return { add, clear, error, openFileDialog, remove };
150
+ }
151
+ function useAttachmentScreenshot({ add, onCaptureScreenshot, }) {
152
+ const [screenshotStatus, setScreenshotStatus] = React.useState(() => getInitialScreenshotStatus(onCaptureScreenshot));
153
+ React.useEffect(() => {
154
+ setScreenshotStatus(getInitialScreenshotStatus(onCaptureScreenshot));
155
+ }, [onCaptureScreenshot]);
156
+ const takeScreenshot = React.useCallback(async () => {
157
+ setScreenshotStatus("capturing");
158
+ try {
159
+ const screenshot = onCaptureScreenshot
160
+ ? await onCaptureScreenshot()
161
+ : await captureBrowserScreenshot();
162
+ if (screenshot)
163
+ add([screenshot], { kind: "screenshot" });
164
+ setScreenshotStatus(isScreenshotSupported() || onCaptureScreenshot ? "idle" : "unavailable");
165
+ }
166
+ catch (screenshotError) {
167
+ if (isIgnoredScreenshotError(screenshotError)) {
168
+ setScreenshotStatus("idle");
169
+ return;
170
+ }
171
+ setScreenshotStatus("error");
172
+ }
173
+ }, [add, onCaptureScreenshot]);
174
+ return { screenshotStatus, takeScreenshot };
175
+ }
176
+ function useAttachments() {
177
+ const context = React.useContext(AttachmentControllerContext);
178
+ if (!context) {
179
+ throw new Error("useAttachments must be used within AttachmentProvider.");
180
+ }
181
+ return context;
182
+ }
183
+ function useOptionalAttachments() {
184
+ return React.useContext(AttachmentControllerContext);
185
+ }
186
+ function useAttachmentsContext() {
187
+ return React.useContext(AttachmentsContext) ?? { variant: "grid" };
188
+ }
189
+ function useAttachmentContext() {
190
+ const context = React.useContext(AttachmentItemContext);
191
+ if (!context) {
192
+ throw new Error("Attachment components must be used within Attachment.");
193
+ }
194
+ return context;
195
+ }
196
+ export { AttachmentControllerContext, AttachmentItemContext, AttachmentProvider, AttachmentsContext, useAttachmentContext, useAttachments, useAttachmentsContext, useOptionalAttachments, };
@@ -0,0 +1,23 @@
1
+ import type { AgenticPromptAttachmentKind } from "./types";
2
+ import type { AgenticAttachment, AttachmentData, AttachmentError, AttachmentMediaCategory, AttachmentProviderProps, AttachmentScreenshotStatus, AttachmentVariant } from "./attachments-types";
3
+ export declare function prepareAttachmentsForSubmit(attachments: AgenticAttachment[]): Promise<AgenticAttachment[]>;
4
+ export declare function fileToAttachment(file: File, kind?: AgenticPromptAttachmentKind): AgenticAttachment;
5
+ export declare function getMediaCategory(data: AttachmentData): AttachmentMediaCategory;
6
+ export declare function getAttachmentLabel(data: AttachmentData): string;
7
+ export declare function getAttachmentError({ accept, currentCount, files, maxAttachmentSizeMb, maxAttachments, }: {
8
+ accept: string | undefined;
9
+ currentCount: number;
10
+ files: File[];
11
+ maxAttachmentSizeMb: number;
12
+ maxAttachments: number;
13
+ }): AttachmentError | undefined;
14
+ export declare function captureBrowserScreenshot(): Promise<File | null>;
15
+ export declare function isScreenshotSupported(): boolean;
16
+ export declare function getInitialScreenshotStatus(onCaptureScreenshot?: AttachmentProviderProps["onCaptureScreenshot"]): AttachmentScreenshotStatus;
17
+ export declare function isIgnoredScreenshotError(error: unknown): boolean;
18
+ export declare function revokeRemovedObjectUrls(previousAttachments: AgenticAttachment[], nextAttachments: AgenticAttachment[], ownedUrls?: Set<string>): void;
19
+ export declare function revokeObjectUrls(attachments: AgenticAttachment[]): void;
20
+ export declare function revokeOwnedObjectUrls(ownedUrls: Set<string>): void;
21
+ export declare function getAttachmentMeta(attachment: AttachmentData | undefined): string | null;
22
+ export declare function getAttachmentRemoveSize(variant: AttachmentVariant): "icon-sm" | "icon-xs";
23
+ export declare function getScreenshotLabel(status: AttachmentScreenshotStatus): "Capturing screenshot" | "Screenshot unavailable" | "Screenshot failed" | "Add screenshot";
@@ -0,0 +1,226 @@
1
+ export async function prepareAttachmentsForSubmit(attachments) {
2
+ return await Promise.all(attachments.map(attachmentToSubmitAttachment));
3
+ }
4
+ export function fileToAttachment(file, kind = "file") {
5
+ return {
6
+ file,
7
+ filename: file.name,
8
+ id: createAttachmentId(),
9
+ kind,
10
+ mediaType: file.type,
11
+ name: file.name,
12
+ size: file.size,
13
+ status: "complete",
14
+ type: "file",
15
+ url: URL.createObjectURL(file),
16
+ };
17
+ }
18
+ export function getMediaCategory(data) {
19
+ if (data.type === "source-document" || data.kind === "source")
20
+ return "source";
21
+ const mediaType = data.mediaType ?? "";
22
+ if (mediaType.startsWith("image/"))
23
+ return "image";
24
+ if (mediaType.startsWith("video/"))
25
+ return "video";
26
+ if (mediaType.startsWith("audio/"))
27
+ return "audio";
28
+ if (mediaType.startsWith("application/") || mediaType.startsWith("text/"))
29
+ return "document";
30
+ return "unknown";
31
+ }
32
+ export function getAttachmentLabel(data) {
33
+ if (data.type === "source-document" || data.kind === "source") {
34
+ return data.title || data.filename || data.name || "Source";
35
+ }
36
+ const category = getMediaCategory(data);
37
+ return data.filename || data.name || (category === "image" ? "Image" : "Attachment");
38
+ }
39
+ async function attachmentToSubmitAttachment(attachment) {
40
+ if (!attachment.url?.startsWith("blob:"))
41
+ return attachment;
42
+ const dataUrl = await convertBlobUrlToDataUrl(attachment.url);
43
+ return {
44
+ ...attachment,
45
+ url: dataUrl ?? attachment.url,
46
+ };
47
+ }
48
+ async function convertBlobUrlToDataUrl(url) {
49
+ try {
50
+ const response = await fetch(url);
51
+ const blob = await response.blob();
52
+ return await new Promise((resolve) => {
53
+ const reader = new FileReader();
54
+ reader.onloadend = () => {
55
+ const result = reader.result;
56
+ resolve(typeof result === "string" ? result : null);
57
+ };
58
+ reader.onerror = () => resolve(null);
59
+ reader.readAsDataURL(blob);
60
+ });
61
+ }
62
+ catch {
63
+ return null;
64
+ }
65
+ }
66
+ export function getAttachmentError({ accept, currentCount, files, maxAttachmentSizeMb, maxAttachments, }) {
67
+ if (currentCount + files.length > maxAttachments) {
68
+ return { code: "max_attachments", message: `Attach up to ${maxAttachments} files.` };
69
+ }
70
+ const oversizedFile = files.find((file) => file.size > maxAttachmentSizeMb * 1024 * 1024);
71
+ if (oversizedFile) {
72
+ return {
73
+ code: "max_attachment_size",
74
+ message: `${oversizedFile.name} is larger than ${maxAttachmentSizeMb} MB.`,
75
+ };
76
+ }
77
+ const unsupportedFile = files.find((file) => !isAcceptedFile(file, accept));
78
+ if (unsupportedFile) {
79
+ return { code: "accept", message: `${unsupportedFile.name} is not an accepted file type.` };
80
+ }
81
+ return undefined;
82
+ }
83
+ function isAcceptedFile(file, accept) {
84
+ if (!accept)
85
+ return true;
86
+ const rules = accept
87
+ .split(",")
88
+ .map((rule) => rule.trim().toLowerCase())
89
+ .filter(Boolean);
90
+ const fileName = file.name.toLowerCase();
91
+ const fileType = file.type.toLowerCase();
92
+ return rules.some((rule) => {
93
+ if (rule === "*/*")
94
+ return true;
95
+ if (rule.startsWith("."))
96
+ return fileName.endsWith(rule);
97
+ if (rule.endsWith("/*"))
98
+ return fileType.startsWith(rule.slice(0, -1));
99
+ return fileType === rule;
100
+ });
101
+ }
102
+ export async function captureBrowserScreenshot() {
103
+ if (!isScreenshotSupported())
104
+ return null;
105
+ let stream = null;
106
+ const video = document.createElement("video");
107
+ video.muted = true;
108
+ video.playsInline = true;
109
+ try {
110
+ stream = await navigator.mediaDevices.getDisplayMedia({ audio: false, video: true });
111
+ video.srcObject = stream;
112
+ await new Promise((resolve, reject) => {
113
+ video.onloadedmetadata = () => resolve();
114
+ video.onerror = () => reject(new Error("Failed to load screen stream."));
115
+ });
116
+ await video.play();
117
+ if (!video.videoWidth || !video.videoHeight)
118
+ return null;
119
+ const canvas = document.createElement("canvas");
120
+ canvas.width = video.videoWidth;
121
+ canvas.height = video.videoHeight;
122
+ const context = canvas.getContext("2d");
123
+ if (!context)
124
+ return null;
125
+ context.drawImage(video, 0, 0, canvas.width, canvas.height);
126
+ const blob = await new Promise((resolve) => {
127
+ canvas.toBlob(resolve, "image/png");
128
+ });
129
+ if (!blob)
130
+ return null;
131
+ return new File([blob], `screenshot-${getScreenshotTimestamp()}.png`, {
132
+ lastModified: Date.now(),
133
+ type: "image/png",
134
+ });
135
+ }
136
+ finally {
137
+ if (stream) {
138
+ for (const track of stream.getTracks())
139
+ track.stop();
140
+ }
141
+ video.pause();
142
+ video.srcObject = null;
143
+ }
144
+ }
145
+ export function isScreenshotSupported() {
146
+ return typeof navigator !== "undefined" && !!navigator.mediaDevices?.getDisplayMedia;
147
+ }
148
+ export function getInitialScreenshotStatus(onCaptureScreenshot) {
149
+ return onCaptureScreenshot || isScreenshotSupported() ? "idle" : "unavailable";
150
+ }
151
+ export function isIgnoredScreenshotError(error) {
152
+ return (error instanceof DOMException &&
153
+ (error.name === "NotAllowedError" || error.name === "AbortError"));
154
+ }
155
+ export function revokeRemovedObjectUrls(previousAttachments, nextAttachments, ownedUrls) {
156
+ const nextUrls = new Set(nextAttachments.map((attachment) => attachment.url).filter(Boolean));
157
+ const removedAttachments = previousAttachments.filter((attachment) => attachment.url &&
158
+ !nextUrls.has(attachment.url) &&
159
+ (!ownedUrls || ownedUrls.has(attachment.url)));
160
+ revokeObjectUrls(removedAttachments);
161
+ if (ownedUrls) {
162
+ for (const attachment of removedAttachments) {
163
+ if (attachment.url)
164
+ ownedUrls.delete(attachment.url);
165
+ }
166
+ }
167
+ }
168
+ export function revokeObjectUrls(attachments) {
169
+ for (const attachment of attachments) {
170
+ if (attachment.url?.startsWith("blob:"))
171
+ URL.revokeObjectURL(attachment.url);
172
+ }
173
+ }
174
+ export function revokeOwnedObjectUrls(ownedUrls) {
175
+ for (const url of ownedUrls) {
176
+ URL.revokeObjectURL(url);
177
+ }
178
+ ownedUrls.clear();
179
+ }
180
+ export function getAttachmentMeta(attachment) {
181
+ if (!attachment)
182
+ return null;
183
+ if (attachment.error)
184
+ return attachment.error;
185
+ const parts = [getMediaLabel(attachment), formatFileSize(attachment.size)].filter(Boolean);
186
+ return parts.join(" · ");
187
+ }
188
+ function getMediaLabel(attachment) {
189
+ if (attachment.kind === "screenshot")
190
+ return "Screenshot";
191
+ if (attachment.kind === "source" || attachment.type === "source-document")
192
+ return "Source";
193
+ if (!attachment.mediaType)
194
+ return "File";
195
+ const mediaTypeParts = attachment.mediaType.split("/");
196
+ return mediaTypeParts[mediaTypeParts.length - 1]?.toUpperCase() ?? "File";
197
+ }
198
+ function formatFileSize(size) {
199
+ if (size === undefined)
200
+ return undefined;
201
+ if (size < 1024)
202
+ return `${size} B`;
203
+ if (size < 1024 * 1024)
204
+ return `${Math.round(size / 1024)} KB`;
205
+ return `${(size / 1024 / 1024).toFixed(1)} MB`;
206
+ }
207
+ export function getAttachmentRemoveSize(variant) {
208
+ if (variant === "list")
209
+ return "icon-sm";
210
+ return "icon-xs";
211
+ }
212
+ export function getScreenshotLabel(status) {
213
+ if (status === "capturing")
214
+ return "Capturing screenshot";
215
+ if (status === "unavailable")
216
+ return "Screenshot unavailable";
217
+ if (status === "error")
218
+ return "Screenshot failed";
219
+ return "Add screenshot";
220
+ }
221
+ function getScreenshotTimestamp() {
222
+ return new Date().toISOString().replace(/[:.]/g, "-").replace("T", "_").replace("Z", "");
223
+ }
224
+ function createAttachmentId() {
225
+ return globalThis.crypto?.randomUUID?.() ?? `attachment-${Date.now()}-${Math.random()}`;
226
+ }
@@ -0,0 +1,7 @@
1
+ import type { AgenticPromptAttachmentStatus } from "./types";
2
+ import type { AttachmentData, AttachmentMediaCategory, AttachmentVariant } from "./attachments-types";
3
+ declare function renderAttachmentPreview(data: AttachmentData, mediaCategory: AttachmentMediaCategory, variant: AttachmentVariant): import("@types/react").JSX.Element | null;
4
+ declare function AttachmentStatus({ status }: {
5
+ status?: AgenticPromptAttachmentStatus;
6
+ }): import("@types/react").JSX.Element | null;
7
+ export { AttachmentStatus, renderAttachmentPreview };
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /* eslint-disable react-refresh/only-export-components */
3
+ import { CheckIcon, CircleAlertIcon } from "lucide-react";
4
+ import { getAttachmentLabel } from "./attachments-model";
5
+ import { Spinner } from "@ciridae/ui/spinner";
6
+ import { cn } from "@ciridae/ui/utils";
7
+ function renderAttachmentPreview(data, mediaCategory, variant) {
8
+ if (mediaCategory === "image" && data.url) {
9
+ return (_jsx("img", { alt: getAttachmentLabel(data), className: cn("size-full object-cover", variant !== "grid" && "rounded"), height: variant === "grid" ? 96 : 20, src: data.url, width: variant === "grid" ? 96 : 20 }));
10
+ }
11
+ if (mediaCategory === "video" && data.url) {
12
+ return _jsx("video", { className: "size-full object-cover", muted: true, src: data.url });
13
+ }
14
+ return null;
15
+ }
16
+ function AttachmentStatus({ status }) {
17
+ if (status === "uploading")
18
+ return (_jsx(Spinner, { className: "size-3.5 shrink-0 text-muted-foreground", "data-slot": "attachment-status" }));
19
+ if (status === "complete")
20
+ return (_jsx(CheckIcon, { className: "size-3.5 shrink-0 text-muted-foreground", "data-slot": "attachment-status" }));
21
+ if (status === "error")
22
+ return (_jsx(CircleAlertIcon, { className: "size-3.5 shrink-0 text-destructive", "data-slot": "attachment-status" }));
23
+ return null;
24
+ }
25
+ export { AttachmentStatus, renderAttachmentPreview };
@@ -0,0 +1,87 @@
1
+ import type * as React from "react";
2
+ import type { AgenticPromptAttachment, AgenticPromptAttachmentKind } from "./types";
3
+ import type { Button } from "@ciridae/ui/button";
4
+ import type { HoverCard, HoverCardContent, HoverCardTrigger } from "@ciridae/ui/hover-card";
5
+ export type AgenticAttachment = AgenticPromptAttachment & {
6
+ file?: File;
7
+ filename?: string;
8
+ title?: string;
9
+ type?: "file" | "source-document";
10
+ };
11
+ export type AttachmentData = AgenticAttachment;
12
+ export type AttachmentMediaCategory = "image" | "video" | "audio" | "document" | "source" | "unknown";
13
+ export type AttachmentVariant = "grid" | "inline" | "list";
14
+ export type AttachmentErrorCode = "accept" | "max_attachments" | "max_attachment_size";
15
+ export type AttachmentError = {
16
+ code: AttachmentErrorCode;
17
+ message: string;
18
+ };
19
+ export type AttachmentAddOptions = {
20
+ kind?: AgenticPromptAttachmentKind;
21
+ };
22
+ export type AttachmentScreenshotStatus = "idle" | "capturing" | "unavailable" | "error";
23
+ export type AttachmentControllerContextValue = {
24
+ accept?: string;
25
+ add: (files: File[] | FileList, options?: AttachmentAddOptions) => void;
26
+ clear: () => void;
27
+ error?: AttachmentError;
28
+ files: AgenticAttachment[];
29
+ maxAttachmentSizeMb?: number;
30
+ maxAttachments?: number;
31
+ openFileDialog: () => void;
32
+ remove: (id: string) => void;
33
+ screenshotStatus: AttachmentScreenshotStatus;
34
+ takeScreenshot: () => Promise<void>;
35
+ };
36
+ export type AttachmentProviderProps = {
37
+ accept?: string;
38
+ attachments?: AgenticAttachment[];
39
+ children: React.ReactNode;
40
+ defaultAttachments?: AgenticAttachment[];
41
+ maxAttachmentSizeMb?: number;
42
+ maxAttachments?: number;
43
+ multiple?: boolean;
44
+ onAttachmentError?: (error: AttachmentError) => void;
45
+ onAttachmentsChange?: (attachments: AgenticAttachment[]) => void;
46
+ onCaptureScreenshot?: () => File | null | Promise<File | null>;
47
+ };
48
+ export type AttachmentsProps = React.ComponentProps<"div"> & {
49
+ variant?: AttachmentVariant;
50
+ };
51
+ export type AttachmentProps = React.ComponentProps<"div"> & {
52
+ attachment?: AttachmentData;
53
+ data?: AttachmentData;
54
+ onRemove?: () => void;
55
+ };
56
+ export type AttachmentPreviewProps = React.ComponentProps<"div"> & {
57
+ fallbackIcon?: React.ReactNode;
58
+ };
59
+ export type AttachmentInfoProps = React.ComponentProps<"div"> & {
60
+ showMediaType?: boolean;
61
+ };
62
+ export type AttachmentRemoveProps = React.ComponentProps<typeof Button> & {
63
+ attachmentId?: string;
64
+ label?: string;
65
+ };
66
+ export type AttachmentHoverCardProps = React.ComponentProps<typeof HoverCard>;
67
+ export type AttachmentHoverCardTriggerProps = React.ComponentProps<typeof HoverCardTrigger>;
68
+ export type AttachmentHoverCardContentProps = React.ComponentProps<typeof HoverCardContent>;
69
+ export type AttachmentEmptyProps = React.ComponentProps<"div">;
70
+ export type AttachmentListProps = AttachmentsProps;
71
+ export type AttachmentNameProps = React.ComponentProps<"span">;
72
+ export type AttachmentMetaProps = React.ComponentProps<"span"> & {
73
+ attachment?: AttachmentData;
74
+ data?: AttachmentData;
75
+ };
76
+ export type AttachmentActionRenderAs = "button" | "menu-item";
77
+ export type AttachmentActionAddFilesProps = React.ComponentProps<typeof Button> & {
78
+ label?: string;
79
+ renderAs?: AttachmentActionRenderAs;
80
+ tooltip?: string;
81
+ };
82
+ export type AttachmentActionAddScreenshotProps = React.ComponentProps<typeof Button> & {
83
+ label?: string;
84
+ renderAs?: AttachmentActionRenderAs;
85
+ status?: AttachmentScreenshotStatus;
86
+ tooltip?: string;
87
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
1
+ import * as React from "react";
2
+ import type { AttachmentEmptyProps, AttachmentHoverCardContentProps, AttachmentHoverCardProps, AttachmentHoverCardTriggerProps, AttachmentInfoProps, AttachmentListProps, AttachmentMetaProps, AttachmentNameProps, AttachmentPreviewProps, AttachmentProps, AttachmentsProps } from "./attachments-types";
3
+ declare function Attachments({ variant, className, children, ...props }: AttachmentsProps): React.JSX.Element | null;
4
+ declare function AttachmentList({ variant, ...props }: AttachmentListProps): React.JSX.Element;
5
+ declare function Attachment({ attachment, children, className, data, onRemove, ...props }: AttachmentProps): React.JSX.Element | null;
6
+ declare function AttachmentPreview({ fallbackIcon, className, ...props }: AttachmentPreviewProps): React.JSX.Element;
7
+ declare function AttachmentInfo({ showMediaType, className, ...props }: AttachmentInfoProps): React.JSX.Element | null;
8
+ declare function AttachmentName({ className, children, ...props }: AttachmentNameProps): React.JSX.Element;
9
+ declare function AttachmentMeta({ attachment, children, className, data, ...props }: AttachmentMetaProps): React.JSX.Element;
10
+ declare function AttachmentHoverCard({ closeDelay, openDelay, ...props }: AttachmentHoverCardProps): React.JSX.Element;
11
+ declare function AttachmentHoverCardTrigger(props: AttachmentHoverCardTriggerProps): React.JSX.Element;
12
+ declare function AttachmentHoverCardContent({ align, className, ...props }: AttachmentHoverCardContentProps): React.JSX.Element;
13
+ declare function AttachmentEmpty({ className, children, ...props }: AttachmentEmptyProps): React.JSX.Element;
14
+ export { Attachment, AttachmentEmpty, AttachmentHoverCard, AttachmentHoverCardContent, AttachmentHoverCardTrigger, AttachmentInfo, AttachmentList, AttachmentMeta, AttachmentName, AttachmentPreview, Attachments, };