@tangle-network/ui 1.0.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 (220) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/LICENSE +21 -0
  3. package/README.md +33 -0
  4. package/dist/active-sessions-store-CeOmXgv5.d.ts +85 -0
  5. package/dist/artifact-pane-DvJyPWV4.d.ts +24 -0
  6. package/dist/auth.d.ts +74 -0
  7. package/dist/auth.js +15 -0
  8. package/dist/button-CMQuQEW_.d.ts +17 -0
  9. package/dist/chat.d.ts +232 -0
  10. package/dist/chat.js +30 -0
  11. package/dist/chunk-2NFQRQOD.js +1009 -0
  12. package/dist/chunk-2VH6PUXD.js +186 -0
  13. package/dist/chunk-34A66VBG.js +214 -0
  14. package/dist/chunk-3OI2QKFD.js +0 -0
  15. package/dist/chunk-4CLN43XT.js +45 -0
  16. package/dist/chunk-54SQQMMM.js +156 -0
  17. package/dist/chunk-5Z5ZYMOJ.js +0 -0
  18. package/dist/chunk-66BNMOVT.js +167 -0
  19. package/dist/chunk-6BGQA4BQ.js +0 -0
  20. package/dist/chunk-7UO2ZMRQ.js +133 -0
  21. package/dist/chunk-BX6AQMUS.js +183 -0
  22. package/dist/chunk-CD53GZOM.js +59 -0
  23. package/dist/chunk-CSAIKY36.js +54 -0
  24. package/dist/chunk-EEE55AVS.js +1201 -0
  25. package/dist/chunk-GYPQXTJU.js +230 -0
  26. package/dist/chunk-HFL6R6IF.js +37 -0
  27. package/dist/chunk-HJKCSXCH.js +737 -0
  28. package/dist/chunk-LISXUB4D.js +1222 -0
  29. package/dist/chunk-LQS34IGP.js +0 -0
  30. package/dist/chunk-MKTSMWVD.js +109 -0
  31. package/dist/chunk-NKDZ7GZE.js +192 -0
  32. package/dist/chunk-OEX7NZE3.js +321 -0
  33. package/dist/chunk-Q56BYXQF.js +61 -0
  34. package/dist/chunk-Q7EIIWTC.js +0 -0
  35. package/dist/chunk-REJESC5U.js +117 -0
  36. package/dist/chunk-RQGKSCEZ.js +0 -0
  37. package/dist/chunk-RQHJBTEU.js +10 -0
  38. package/dist/chunk-TMFOPHHN.js +299 -0
  39. package/dist/chunk-XGKULLYE.js +40 -0
  40. package/dist/chunk-XIHMJ7ZQ.js +614 -0
  41. package/dist/chunk-YJ2G3XO5.js +1048 -0
  42. package/dist/chunk-YNN4O57I.js +754 -0
  43. package/dist/code-block-DjXf8eOG.d.ts +19 -0
  44. package/dist/document-editor-pane-A5LT5H4N.js +12 -0
  45. package/dist/document-editor-pane-DyDEX_Zm.d.ts +124 -0
  46. package/dist/editor.d.ts +120 -0
  47. package/dist/editor.js +34 -0
  48. package/dist/files.d.ts +175 -0
  49. package/dist/files.js +20 -0
  50. package/dist/hooks.d.ts +56 -0
  51. package/dist/hooks.js +41 -0
  52. package/dist/index.d.ts +43 -0
  53. package/dist/index.js +446 -0
  54. package/dist/markdown.d.ts +15 -0
  55. package/dist/markdown.js +14 -0
  56. package/dist/message-BHWbxBtT.d.ts +15 -0
  57. package/dist/openui.d.ts +115 -0
  58. package/dist/openui.js +12 -0
  59. package/dist/parts-dj7AcUg0.d.ts +36 -0
  60. package/dist/primitives.d.ts +332 -0
  61. package/dist/primitives.js +191 -0
  62. package/dist/run-PfLmDAox.d.ts +41 -0
  63. package/dist/run.d.ts +69 -0
  64. package/dist/run.js +36 -0
  65. package/dist/sdk-hooks.d.ts +285 -0
  66. package/dist/sdk-hooks.js +31 -0
  67. package/dist/stores.d.ts +17 -0
  68. package/dist/stores.js +76 -0
  69. package/dist/tool-call-feed-Bs3MyQMT.d.ts +68 -0
  70. package/dist/tool-display-z4JcDmMQ.d.ts +32 -0
  71. package/dist/tool-previews.d.ts +48 -0
  72. package/dist/tool-previews.js +21 -0
  73. package/dist/types.d.ts +19 -0
  74. package/dist/types.js +1 -0
  75. package/dist/utils.d.ts +45 -0
  76. package/dist/utils.js +32 -0
  77. package/package.json +193 -0
  78. package/src/auth/auth.tsx +228 -0
  79. package/src/auth/index.ts +13 -0
  80. package/src/auth/login-layout.tsx +46 -0
  81. package/src/chat/agent-timeline.stories.tsx +429 -0
  82. package/src/chat/agent-timeline.tsx +360 -0
  83. package/src/chat/chat-container.tsx +486 -0
  84. package/src/chat/chat-input.stories.tsx +142 -0
  85. package/src/chat/chat-input.tsx +389 -0
  86. package/src/chat/chat-message.stories.tsx +237 -0
  87. package/src/chat/chat-message.tsx +129 -0
  88. package/src/chat/index.ts +18 -0
  89. package/src/chat/message-list.stories.tsx +336 -0
  90. package/src/chat/message-list.tsx +79 -0
  91. package/src/chat/thinking-indicator.stories.tsx +56 -0
  92. package/src/chat/thinking-indicator.tsx +30 -0
  93. package/src/chat/user-message.stories.tsx +92 -0
  94. package/src/chat/user-message.tsx +43 -0
  95. package/src/editor/document-editor-pane.tsx +351 -0
  96. package/src/editor/editor-provider.tsx +428 -0
  97. package/src/editor/editor-toolbar.tsx +130 -0
  98. package/src/editor/index.ts +31 -0
  99. package/src/editor/markdown-conversion.ts +21 -0
  100. package/src/editor/markdown-document-editor.tsx +137 -0
  101. package/src/editor/tiptap-editor.tsx +331 -0
  102. package/src/editor/use-editor.ts +221 -0
  103. package/src/files/file-artifact-pane.tsx +183 -0
  104. package/src/files/file-preview.tsx +342 -0
  105. package/src/files/file-tabs.tsx +71 -0
  106. package/src/files/file-tree.tsx +258 -0
  107. package/src/files/index.ts +17 -0
  108. package/src/files/rich-file-tree.stories.tsx +104 -0
  109. package/src/files/rich-file-tree.test.tsx +42 -0
  110. package/src/files/rich-file-tree.tsx +232 -0
  111. package/src/hooks/index.ts +10 -0
  112. package/src/hooks/use-auth.ts +153 -0
  113. package/src/hooks/use-auto-scroll.ts +59 -0
  114. package/src/hooks/use-dropdown-menu.ts +40 -0
  115. package/src/hooks/use-live-time.test.tsx +40 -0
  116. package/src/hooks/use-live-time.ts +27 -0
  117. package/src/hooks/use-realtime-session.ts +319 -0
  118. package/src/hooks/use-run-collapse-state.ts +25 -0
  119. package/src/hooks/use-run-groups.ts +111 -0
  120. package/src/hooks/use-sdk-session.ts +575 -0
  121. package/src/hooks/use-sse-stream.ts +475 -0
  122. package/src/hooks/use-tool-call-stream.ts +96 -0
  123. package/src/index.ts +14 -0
  124. package/src/lib/utils.ts +6 -0
  125. package/src/markdown/code-block.tsx +198 -0
  126. package/src/markdown/index.ts +2 -0
  127. package/src/markdown/markdown.stories.tsx +190 -0
  128. package/src/markdown/markdown.tsx +62 -0
  129. package/src/openui/index.ts +20 -0
  130. package/src/openui/openui-artifact-renderer.tsx +542 -0
  131. package/src/primitives/artifact-pane.tsx +91 -0
  132. package/src/primitives/avatar.stories.tsx +95 -0
  133. package/src/primitives/avatar.tsx +47 -0
  134. package/src/primitives/badge.stories.tsx +57 -0
  135. package/src/primitives/badge.tsx +97 -0
  136. package/src/primitives/button.stories.tsx +48 -0
  137. package/src/primitives/button.tsx +115 -0
  138. package/src/primitives/card.stories.tsx +53 -0
  139. package/src/primitives/card.tsx +98 -0
  140. package/src/primitives/code-block.stories.tsx +115 -0
  141. package/src/primitives/code-block.tsx +22 -0
  142. package/src/primitives/design-tokens.stories.tsx +162 -0
  143. package/src/primitives/dialog.stories.tsx +176 -0
  144. package/src/primitives/dialog.tsx +137 -0
  145. package/src/primitives/drop-zone.stories.tsx +123 -0
  146. package/src/primitives/drop-zone.tsx +131 -0
  147. package/src/primitives/dropdown-menu.stories.tsx +122 -0
  148. package/src/primitives/dropdown-menu.tsx +214 -0
  149. package/src/primitives/empty-state.stories.tsx +81 -0
  150. package/src/primitives/empty-state.tsx +40 -0
  151. package/src/primitives/index.ts +118 -0
  152. package/src/primitives/input.stories.tsx +113 -0
  153. package/src/primitives/input.tsx +136 -0
  154. package/src/primitives/label.stories.tsx +84 -0
  155. package/src/primitives/label.tsx +24 -0
  156. package/src/primitives/progress.stories.tsx +93 -0
  157. package/src/primitives/progress.tsx +50 -0
  158. package/src/primitives/segmented-control.test.tsx +328 -0
  159. package/src/primitives/segmented-control.tsx +154 -0
  160. package/src/primitives/select.stories.tsx +164 -0
  161. package/src/primitives/select.tsx +158 -0
  162. package/src/primitives/sidebar-drop-zone.stories.tsx +100 -0
  163. package/src/primitives/sidebar-drop-zone.tsx +149 -0
  164. package/src/primitives/skeleton.stories.tsx +79 -0
  165. package/src/primitives/skeleton.tsx +55 -0
  166. package/src/primitives/stat-card.stories.tsx +137 -0
  167. package/src/primitives/stat-card.tsx +97 -0
  168. package/src/primitives/switch.stories.tsx +85 -0
  169. package/src/primitives/switch.tsx +28 -0
  170. package/src/primitives/table.stories.tsx +170 -0
  171. package/src/primitives/table.tsx +116 -0
  172. package/src/primitives/tabs.stories.tsx +180 -0
  173. package/src/primitives/tabs.tsx +71 -0
  174. package/src/primitives/terminal-display.stories.tsx +191 -0
  175. package/src/primitives/terminal-display.tsx +189 -0
  176. package/src/primitives/theme-toggle.stories.tsx +32 -0
  177. package/src/primitives/theme-toggle.tsx +96 -0
  178. package/src/primitives/toast.stories.tsx +155 -0
  179. package/src/primitives/toast.tsx +190 -0
  180. package/src/primitives/upload-progress.stories.tsx +120 -0
  181. package/src/primitives/upload-progress.tsx +110 -0
  182. package/src/run/expanded-tool-detail.stories.tsx +182 -0
  183. package/src/run/expanded-tool-detail.tsx +186 -0
  184. package/src/run/index.ts +13 -0
  185. package/src/run/inline-thinking-item.stories.tsx +136 -0
  186. package/src/run/inline-thinking-item.tsx +120 -0
  187. package/src/run/inline-tool-item.stories.tsx +222 -0
  188. package/src/run/inline-tool-item.tsx +190 -0
  189. package/src/run/run-group.stories.tsx +322 -0
  190. package/src/run/run-group.tsx +569 -0
  191. package/src/run/run-item-primitives.tsx +17 -0
  192. package/src/run/tool-call-feed.stories.tsx +294 -0
  193. package/src/run/tool-call-feed.tsx +192 -0
  194. package/src/run/tool-call-step.stories.tsx +198 -0
  195. package/src/run/tool-call-step.tsx +240 -0
  196. package/src/sdk-hooks.ts +38 -0
  197. package/src/stores/active-sessions-store.ts +455 -0
  198. package/src/stores/chat-store.ts +43 -0
  199. package/src/stores/index.ts +2 -0
  200. package/src/tool-previews/command-preview.tsx +116 -0
  201. package/src/tool-previews/diff-preview.tsx +85 -0
  202. package/src/tool-previews/glob-results-preview.tsx +98 -0
  203. package/src/tool-previews/grep-results-preview.tsx +157 -0
  204. package/src/tool-previews/index.ts +22 -0
  205. package/src/tool-previews/preview-primitives.tsx +84 -0
  206. package/src/tool-previews/question-preview.tsx +101 -0
  207. package/src/tool-previews/web-search-preview.tsx +117 -0
  208. package/src/tool-previews/write-file-preview.tsx +80 -0
  209. package/src/types/branding.ts +11 -0
  210. package/src/types/index.ts +5 -0
  211. package/src/types/message.ts +13 -0
  212. package/src/types/parts.ts +51 -0
  213. package/src/types/run.ts +56 -0
  214. package/src/types/tool-display.ts +41 -0
  215. package/src/utils/copy-text.ts +30 -0
  216. package/src/utils/format.test.ts +43 -0
  217. package/src/utils/format.ts +56 -0
  218. package/src/utils/index.ts +10 -0
  219. package/src/utils/time-ago.ts +9 -0
  220. package/src/utils/tool-display.ts +238 -0
@@ -0,0 +1,117 @@
1
+ // src/hooks/use-live-time.ts
2
+ import * as React from "react";
3
+ function useLiveTime(intervalMs = 1e3) {
4
+ const [now, setNow] = React.useState(() => Date.now());
5
+ React.useEffect(() => {
6
+ const requested = Number.isFinite(intervalMs) && intervalMs > 0 ? intervalMs : 1e3;
7
+ const delay = Math.max(requested, 100);
8
+ const id = window.setInterval(() => {
9
+ setNow(Date.now());
10
+ }, delay);
11
+ return () => {
12
+ window.clearInterval(id);
13
+ };
14
+ }, [intervalMs]);
15
+ return now;
16
+ }
17
+
18
+ // src/hooks/use-auth.ts
19
+ import * as React2 from "react";
20
+ function useAuth({
21
+ apiBaseUrl,
22
+ revalidateOnFocus = false,
23
+ shouldRetryOnError = false
24
+ }) {
25
+ const [user, setUser] = React2.useState(null);
26
+ const [isLoading, setIsLoading] = React2.useState(true);
27
+ const [error, setError] = React2.useState(null);
28
+ const retryTimerRef = React2.useRef(null);
29
+ const abortRef = React2.useRef(null);
30
+ const fetchSession = React2.useCallback(async () => {
31
+ abortRef.current?.abort();
32
+ const controller = new AbortController();
33
+ abortRef.current = controller;
34
+ setIsLoading(true);
35
+ setError(null);
36
+ try {
37
+ const res = await fetch(`${apiBaseUrl}/auth/session`, {
38
+ credentials: "include",
39
+ signal: controller.signal
40
+ });
41
+ if (!res.ok) {
42
+ throw new Error("Not authenticated");
43
+ }
44
+ const data = await res.json();
45
+ if (data.success && data.data) {
46
+ setUser(data.data);
47
+ } else {
48
+ setUser(null);
49
+ }
50
+ } catch (err) {
51
+ if (err.name === "AbortError") return;
52
+ setError(err instanceof Error ? err : new Error("Unknown error"));
53
+ setUser(null);
54
+ if (shouldRetryOnError) {
55
+ if (retryTimerRef.current) clearTimeout(retryTimerRef.current);
56
+ retryTimerRef.current = setTimeout(fetchSession, 5e3);
57
+ }
58
+ } finally {
59
+ if (!controller.signal.aborted) {
60
+ setIsLoading(false);
61
+ }
62
+ }
63
+ }, [apiBaseUrl, shouldRetryOnError]);
64
+ React2.useEffect(() => {
65
+ fetchSession();
66
+ return () => {
67
+ abortRef.current?.abort();
68
+ if (retryTimerRef.current) clearTimeout(retryTimerRef.current);
69
+ };
70
+ }, [fetchSession]);
71
+ React2.useEffect(() => {
72
+ if (!revalidateOnFocus) return;
73
+ const handleFocus = () => {
74
+ fetchSession();
75
+ };
76
+ window.addEventListener("focus", handleFocus);
77
+ return () => window.removeEventListener("focus", handleFocus);
78
+ }, [revalidateOnFocus, fetchSession]);
79
+ return {
80
+ user,
81
+ isLoading,
82
+ isError: !!error,
83
+ error,
84
+ mutate: fetchSession
85
+ };
86
+ }
87
+ function createAuthFetcher(_apiBaseUrl) {
88
+ return async function authFetcher(url, options) {
89
+ const res = await fetch(url, {
90
+ ...options,
91
+ credentials: "include",
92
+ headers: {
93
+ ...options?.headers
94
+ }
95
+ });
96
+ if (!res.ok) {
97
+ throw new Error(`Request failed with status ${res.status}`);
98
+ }
99
+ return res.json();
100
+ };
101
+ }
102
+ function useApiKey() {
103
+ const [apiKey, setApiKey] = React2.useState(null);
104
+ React2.useEffect(() => {
105
+ if (typeof window !== "undefined") {
106
+ setApiKey(localStorage.getItem("apiKey"));
107
+ }
108
+ }, []);
109
+ return apiKey;
110
+ }
111
+
112
+ export {
113
+ useLiveTime,
114
+ useAuth,
115
+ createAuthFetcher,
116
+ useApiKey
117
+ };
File without changes
@@ -0,0 +1,10 @@
1
+ // src/lib/utils.ts
2
+ import { clsx } from "clsx";
3
+ import { twMerge } from "tailwind-merge";
4
+ function cn(...inputs) {
5
+ return twMerge(clsx(inputs));
6
+ }
7
+
8
+ export {
9
+ cn
10
+ };
@@ -0,0 +1,299 @@
1
+ import {
2
+ Badge,
3
+ Card,
4
+ CardContent,
5
+ CardDescription,
6
+ CardHeader,
7
+ CardTitle,
8
+ Table,
9
+ TableBody,
10
+ TableCell,
11
+ TableHead,
12
+ TableHeader,
13
+ TableRow
14
+ } from "./chunk-GYPQXTJU.js";
15
+ import {
16
+ Button
17
+ } from "./chunk-MKTSMWVD.js";
18
+ import {
19
+ Markdown
20
+ } from "./chunk-CD53GZOM.js";
21
+ import {
22
+ CodeBlock
23
+ } from "./chunk-66BNMOVT.js";
24
+ import {
25
+ cn
26
+ } from "./chunk-RQHJBTEU.js";
27
+
28
+ // src/openui/openui-artifact-renderer.tsx
29
+ import { Fragment } from "react";
30
+ import { Minus } from "lucide-react";
31
+ import { jsx, jsxs } from "react/jsx-runtime";
32
+ var NODE_TYPES = /* @__PURE__ */ new Set([
33
+ "heading",
34
+ "text",
35
+ "badge",
36
+ "stat",
37
+ "key_value",
38
+ "code",
39
+ "markdown",
40
+ "table",
41
+ "actions",
42
+ "separator",
43
+ "stack",
44
+ "grid",
45
+ "card"
46
+ ]);
47
+ var GAP_STYLES = {
48
+ sm: "gap-2",
49
+ md: "gap-4",
50
+ lg: "gap-6"
51
+ };
52
+ var GRID_STYLES = {
53
+ 1: "grid-cols-1",
54
+ 2: "grid-cols-1 md:grid-cols-2",
55
+ 3: "grid-cols-1 md:grid-cols-3",
56
+ 4: "grid-cols-1 md:grid-cols-2 xl:grid-cols-4"
57
+ };
58
+ var ALIGN_STYLES = {
59
+ start: "items-start",
60
+ center: "items-center",
61
+ end: "items-end",
62
+ stretch: "items-stretch"
63
+ };
64
+ function formatValue(value) {
65
+ if (value === null || value === void 0) {
66
+ return /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "\u2014" });
67
+ }
68
+ if (typeof value === "boolean") {
69
+ return value ? "Yes" : "No";
70
+ }
71
+ return value;
72
+ }
73
+ function asArray(value) {
74
+ return Array.isArray(value) ? value : [];
75
+ }
76
+ function isOpenUIComponentNode(value) {
77
+ return typeof value === "object" && value !== null && "type" in value && typeof value.type === "string" && NODE_TYPES.has(value.type);
78
+ }
79
+ function renderActions(actions, onAction) {
80
+ if (actions.length === 0) return null;
81
+ return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2", children: actions.map((action) => /* @__PURE__ */ jsx(
82
+ Button,
83
+ {
84
+ type: "button",
85
+ size: "sm",
86
+ variant: action.tone ?? "outline",
87
+ disabled: action.disabled,
88
+ onClick: () => {
89
+ action.onPress?.();
90
+ onAction?.(action);
91
+ },
92
+ children: action.label
93
+ },
94
+ action.id
95
+ )) });
96
+ }
97
+ function renderNode(node, onAction) {
98
+ switch (node.type) {
99
+ case "heading": {
100
+ const level = node.level ?? 2;
101
+ const HeadingTag = `h${level}`;
102
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
103
+ node.kicker && /* @__PURE__ */ jsx("div", { className: "text-[10px] font-semibold uppercase tracking-[0.16em] text-muted-foreground", children: node.kicker }),
104
+ /* @__PURE__ */ jsx(
105
+ HeadingTag,
106
+ {
107
+ className: cn(
108
+ "font-semibold tracking-tight text-foreground",
109
+ level === 1 && "text-2xl",
110
+ level === 2 && "text-xl",
111
+ level === 3 && "text-lg",
112
+ level === 4 && "text-base"
113
+ ),
114
+ children: node.text
115
+ }
116
+ ),
117
+ node.meta && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: node.meta })
118
+ ] });
119
+ }
120
+ case "text":
121
+ return /* @__PURE__ */ jsx(
122
+ "p",
123
+ {
124
+ className: cn(
125
+ "text-sm leading-6 text-foreground",
126
+ node.tone === "muted" && "text-muted-foreground",
127
+ node.tone === "success" && "text-[var(--surface-success-text)]",
128
+ node.tone === "warning" && "text-[var(--surface-warning-text)]",
129
+ node.tone === "error" && "text-[var(--surface-danger-text)]",
130
+ node.mono && "font-mono text-[13px]"
131
+ ),
132
+ children: node.text
133
+ }
134
+ );
135
+ case "badge":
136
+ return /* @__PURE__ */ jsx(Badge, { variant: node.tone ?? "outline", children: node.label });
137
+ case "stat":
138
+ return /* @__PURE__ */ jsx(Card, { variant: "glass", className: "border-border shadow-[var(--shadow-card)]", children: /* @__PURE__ */ jsxs(CardContent, { className: "space-y-2 p-4", children: [
139
+ /* @__PURE__ */ jsx("div", { className: "text-xs uppercase tracking-[0.12em] text-muted-foreground", children: node.label }),
140
+ /* @__PURE__ */ jsx("div", { className: "text-2xl font-semibold tracking-tight text-foreground", children: node.value }),
141
+ node.change && /* @__PURE__ */ jsx(
142
+ "div",
143
+ {
144
+ className: cn(
145
+ "text-xs",
146
+ node.tone === "success" && "text-[var(--surface-success-text)]",
147
+ node.tone === "warning" && "text-[var(--surface-warning-text)]",
148
+ node.tone === "error" && "text-[var(--surface-danger-text)]",
149
+ node.tone === "info" && "text-[var(--surface-info-text)]",
150
+ !node.tone || node.tone === "default" ? "text-muted-foreground" : void 0
151
+ ),
152
+ children: node.change
153
+ }
154
+ )
155
+ ] }) });
156
+ case "key_value":
157
+ if (asArray(node.items).length === 0) {
158
+ return null;
159
+ }
160
+ return /* @__PURE__ */ jsx("dl", { className: "grid gap-3 sm:grid-cols-2", children: asArray(node.items).map((item, index) => /* @__PURE__ */ jsxs(
161
+ "div",
162
+ {
163
+ className: "rounded-[var(--radius-lg)] border border-border bg-card px-4 py-3",
164
+ children: [
165
+ /* @__PURE__ */ jsx("dt", { className: "text-[11px] uppercase tracking-[0.12em] text-muted-foreground", children: item.label }),
166
+ /* @__PURE__ */ jsx(
167
+ "dd",
168
+ {
169
+ className: cn(
170
+ "mt-1 text-sm font-medium text-foreground",
171
+ item.tone === "muted" && "text-foreground"
172
+ ),
173
+ children: formatValue(item.value)
174
+ }
175
+ )
176
+ ]
177
+ },
178
+ item.id ?? `${item.label}-${index}`
179
+ )) });
180
+ case "code":
181
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
182
+ node.title && /* @__PURE__ */ jsx("div", { className: "text-xs uppercase tracking-[0.12em] text-muted-foreground", children: node.title }),
183
+ /* @__PURE__ */ jsx(
184
+ CodeBlock,
185
+ {
186
+ code: node.code,
187
+ language: node.language,
188
+ showLineNumbers: node.showLineNumbers,
189
+ className: "border-border bg-background"
190
+ }
191
+ )
192
+ ] });
193
+ case "markdown":
194
+ return /* @__PURE__ */ jsx("div", { className: "rounded-[var(--radius-lg)] border border-border bg-card p-5", children: /* @__PURE__ */ jsx(Markdown, { className: "prose-sm max-w-none", children: node.content }) });
195
+ case "table":
196
+ if (asArray(node.columns).length === 0) {
197
+ return null;
198
+ }
199
+ return /* @__PURE__ */ jsxs("div", { className: "overflow-hidden rounded-[var(--radius-lg)] border border-border bg-card", children: [
200
+ /* @__PURE__ */ jsxs(Table, { children: [
201
+ /* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsx(TableRow, { className: "border-border", children: asArray(node.columns).map((column) => /* @__PURE__ */ jsx(
202
+ TableHead,
203
+ {
204
+ className: cn(
205
+ "h-10 text-[11px] uppercase tracking-[0.1em] text-muted-foreground",
206
+ column.align === "right" && "text-right"
207
+ ),
208
+ children: column.header
209
+ },
210
+ column.key
211
+ )) }) }),
212
+ /* @__PURE__ */ jsx(TableBody, { children: asArray(node.rows).map((row, rowIndex) => /* @__PURE__ */ jsx(TableRow, { className: "border-border", children: asArray(node.columns).map((column) => /* @__PURE__ */ jsx(
213
+ TableCell,
214
+ {
215
+ className: cn(
216
+ "py-3 text-sm text-foreground",
217
+ column.align === "right" && "text-right tabular-nums"
218
+ ),
219
+ children: formatValue(row[column.key])
220
+ },
221
+ column.key
222
+ )) }, rowIndex)) })
223
+ ] }),
224
+ node.caption && /* @__PURE__ */ jsx("div", { className: "border-t border-border px-4 py-2 text-xs text-muted-foreground", children: node.caption })
225
+ ] });
226
+ case "actions":
227
+ return renderActions(asArray(node.actions), onAction);
228
+ case "separator":
229
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
230
+ /* @__PURE__ */ jsx("div", { className: "h-px flex-1 bg-border" }),
231
+ node.label && /* @__PURE__ */ jsx("span", { className: "text-[10px] uppercase tracking-[0.14em] text-muted-foreground", children: node.label }),
232
+ /* @__PURE__ */ jsx("div", { className: "h-px flex-1 bg-border" })
233
+ ] });
234
+ case "stack":
235
+ if (asArray(node.children).length === 0) {
236
+ return null;
237
+ }
238
+ return /* @__PURE__ */ jsx(
239
+ "div",
240
+ {
241
+ className: cn(
242
+ "flex",
243
+ node.direction === "row" ? "flex-row" : "flex-col",
244
+ GAP_STYLES[node.gap ?? "md"],
245
+ ALIGN_STYLES[node.align ?? "stretch"],
246
+ node.wrap && "flex-wrap"
247
+ ),
248
+ children: asArray(node.children).map((child, index) => /* @__PURE__ */ jsx(Fragment, { children: renderNode(child, onAction) }, child.id ?? `${child.type}-${index}`))
249
+ }
250
+ );
251
+ case "grid":
252
+ if (asArray(node.children).length === 0) {
253
+ return null;
254
+ }
255
+ return /* @__PURE__ */ jsx("div", { className: cn("grid", GRID_STYLES[node.columns ?? 2], GAP_STYLES[node.gap ?? "md"]), children: asArray(node.children).map((child, index) => /* @__PURE__ */ jsx(Fragment, { children: renderNode(child, onAction) }, child.id ?? `${child.type}-${index}`)) });
256
+ case "card":
257
+ return /* @__PURE__ */ jsxs(Card, { variant: "glass", className: "border-border shadow-[var(--shadow-card)]", children: [
258
+ (node.eyebrow || node.title || node.description || node.badge || asArray(node.actions).length > 0) && /* @__PURE__ */ jsxs(CardHeader, { className: "gap-2 p-4 pb-0", children: [
259
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
260
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 space-y-1", children: [
261
+ node.eyebrow && /* @__PURE__ */ jsx("div", { className: "text-[10px] font-semibold uppercase tracking-[0.16em] text-muted-foreground", children: node.eyebrow }),
262
+ node.title && /* @__PURE__ */ jsx(CardTitle, { className: "text-base text-foreground", children: node.title }),
263
+ node.description && /* @__PURE__ */ jsx(CardDescription, { className: "text-muted-foreground", children: node.description })
264
+ ] }),
265
+ node.badge && /* @__PURE__ */ jsx(Badge, { variant: node.badge.tone ?? "outline", children: node.badge.label })
266
+ ] }),
267
+ asArray(node.actions).length > 0 && renderActions(asArray(node.actions), onAction)
268
+ ] }),
269
+ asArray(node.children).length > 0 && /* @__PURE__ */ jsx(CardContent, { className: "space-y-4 p-4", children: asArray(node.children).map((child, index) => /* @__PURE__ */ jsx(Fragment, { children: renderNode(child, onAction) }, child.id ?? `${child.type}-${index}`)) })
270
+ ] });
271
+ }
272
+ }
273
+ function OpenUIArtifactRenderer({
274
+ schema,
275
+ onAction,
276
+ className
277
+ }) {
278
+ const nodes = (Array.isArray(schema) ? schema : [schema]).filter(isOpenUIComponentNode);
279
+ if (nodes.length === 0) {
280
+ return /* @__PURE__ */ jsx(
281
+ "div",
282
+ {
283
+ className: cn(
284
+ "flex min-h-[6rem] items-center justify-center rounded-[var(--radius-xl)] border border-dashed border-border bg-card p-5 text-center",
285
+ className
286
+ ),
287
+ children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
288
+ /* @__PURE__ */ jsx("div", { className: "mx-auto flex h-10 w-10 items-center justify-center rounded-full bg-muted/50 text-muted-foreground", children: /* @__PURE__ */ jsx(Minus, { className: "h-4 w-4" }) }),
289
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-foreground", children: "No view was generated." })
290
+ ] })
291
+ }
292
+ );
293
+ }
294
+ return /* @__PURE__ */ jsx("div", { className: cn("space-y-4", className), children: nodes.map((node, index) => /* @__PURE__ */ jsx(Fragment, { children: renderNode(node, onAction) }, node.id ?? `${node.type}-${index}`)) });
295
+ }
296
+
297
+ export {
298
+ OpenUIArtifactRenderer
299
+ };
@@ -0,0 +1,40 @@
1
+ // src/utils/copy-text.ts
2
+ async function copyText(text) {
3
+ if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
4
+ try {
5
+ await navigator.clipboard.writeText(text);
6
+ return true;
7
+ } catch {
8
+ }
9
+ }
10
+ if (typeof document === "undefined") return false;
11
+ try {
12
+ const textarea = document.createElement("textarea");
13
+ textarea.value = text;
14
+ textarea.style.position = "fixed";
15
+ textarea.style.opacity = "0";
16
+ document.body.appendChild(textarea);
17
+ textarea.select();
18
+ const copied = document.execCommand("copy");
19
+ document.body.removeChild(textarea);
20
+ return copied;
21
+ } catch {
22
+ return false;
23
+ }
24
+ }
25
+
26
+ // src/utils/time-ago.ts
27
+ function timeAgo(ts) {
28
+ const secs = Math.floor((Date.now() - ts) / 1e3);
29
+ if (secs < 5) return "just now";
30
+ if (secs < 60) return `${secs}s ago`;
31
+ const mins = Math.floor(secs / 60);
32
+ if (mins < 60) return `${mins}m ago`;
33
+ const hrs = Math.floor(mins / 60);
34
+ return `${hrs}h ago`;
35
+ }
36
+
37
+ export {
38
+ copyText,
39
+ timeAgo
40
+ };