@moldable-ai/ui 0.2.7 → 0.2.9

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 (44) hide show
  1. package/dist/components/app-error-boundary.d.ts +26 -0
  2. package/dist/components/app-error-boundary.d.ts.map +1 -0
  3. package/dist/components/app-error-boundary.js +87 -0
  4. package/dist/components/chat/chat-input.d.ts +9 -1
  5. package/dist/components/chat/chat-input.d.ts.map +1 -1
  6. package/dist/components/chat/chat-input.js +13 -4
  7. package/dist/components/chat/chat-message.d.ts +6 -0
  8. package/dist/components/chat/chat-message.d.ts.map +1 -1
  9. package/dist/components/chat/chat-message.js +576 -157
  10. package/dist/components/chat/chat-messages.d.ts +2 -1
  11. package/dist/components/chat/chat-messages.d.ts.map +1 -1
  12. package/dist/components/chat/chat-messages.js +3 -3
  13. package/dist/components/chat/chat-panel.d.ts +25 -3
  14. package/dist/components/chat/chat-panel.d.ts.map +1 -1
  15. package/dist/components/chat/chat-panel.js +173 -49
  16. package/dist/components/chat/index.d.ts +3 -3
  17. package/dist/components/chat/index.d.ts.map +1 -1
  18. package/dist/components/chat/index.js +1 -1
  19. package/dist/components/chat/model-selector.d.ts.map +1 -1
  20. package/dist/components/chat/model-selector.js +1 -1
  21. package/dist/components/chat/tool-handlers.d.ts.map +1 -1
  22. package/dist/components/chat/tool-handlers.js +89 -1
  23. package/dist/components/markdown.d.ts.map +1 -1
  24. package/dist/components/markdown.js +18 -4
  25. package/dist/components/rich-media-player.d.ts +8 -0
  26. package/dist/components/rich-media-player.d.ts.map +1 -0
  27. package/dist/components/rich-media-player.js +99 -0
  28. package/dist/components/ui/command.d.ts +2 -1
  29. package/dist/components/ui/command.d.ts.map +1 -1
  30. package/dist/components/ui/command.js +2 -2
  31. package/dist/components/ui/resizable.d.ts +4 -1
  32. package/dist/components/ui/resizable.d.ts.map +1 -1
  33. package/dist/components/ui/resizable.js +1 -1
  34. package/dist/index.d.ts +3 -1
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +4 -1
  37. package/dist/lib/commands.d.ts +8 -0
  38. package/dist/lib/commands.d.ts.map +1 -1
  39. package/dist/lib/commands.js +18 -0
  40. package/package.json +17 -14
  41. package/src/styles/index.css +25 -0
  42. package/dist/lib/commands.test.d.ts +0 -2
  43. package/dist/lib/commands.test.d.ts.map +0 -1
  44. package/dist/lib/commands.test.js +0 -111
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { AlertTriangle, BookOpen, Check, CheckCheck, Copy, Database, Download, FileCode, FileText, FolderOpen, Globe, Info, Package, Plus, Search, Sparkles, Terminal, Trash2, X, } from 'lucide-react';
3
+ import { AlertTriangle, BookOpen, Camera, Check, CheckCheck, Copy, Database, Download, FileCode, FileText, FolderOpen, Globe, Info, Package, Plus, Search, Sparkles, Terminal, Trash2, X, } from 'lucide-react';
4
4
  import { useState } from 'react';
5
5
  import { cn } from '../../lib/utils';
6
6
  import { ThinkingTimelineMarker } from './thinking-timeline';
@@ -108,6 +108,94 @@ function appApiItemCount(result) {
108
108
  * Default tool handlers for Moldable tools
109
109
  */
110
110
  export const DEFAULT_TOOL_HANDLERS = {
111
+ viewImage: {
112
+ loadingLabel: 'Viewing image...',
113
+ marker: ThinkingTimelineMarker.Default,
114
+ inline: false,
115
+ renderLoading: (args) => {
116
+ const { imagePath } = (args ?? {});
117
+ return (_jsx(LoadingIndicator, { icon: Sparkles, children: imagePath
118
+ ? `Viewing ${getFileName(imagePath)}...`
119
+ : 'Viewing image...' }));
120
+ },
121
+ renderOutput: (output, toolCallId) => {
122
+ const result = output;
123
+ if (!result) {
124
+ return (_jsx(LoadingIndicator, { icon: Sparkles, children: "Viewing image..." }, toolCallId));
125
+ }
126
+ return (_jsxs("div", { className: "bg-muted text-muted-foreground my-1 inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(Sparkles, { className: "size-3.5 shrink-0" }), _jsx("span", { className: "min-w-0 truncate", children: result.imagePath
127
+ ? `Viewed ${getFileName(result.imagePath)}`
128
+ : 'Viewed image' })] }, toolCallId));
129
+ },
130
+ },
131
+ generateImage: {
132
+ loadingLabel: 'Generating image...',
133
+ marker: ThinkingTimelineMarker.Default,
134
+ inline: true,
135
+ renderLoading: (args) => {
136
+ const { prompt, aspectRatio } = (args ?? {});
137
+ const ratioClass = aspectRatio === 'portrait'
138
+ ? 'aspect-[3/4]'
139
+ : aspectRatio === 'story'
140
+ ? 'aspect-[9/16]'
141
+ : aspectRatio === 'landscape'
142
+ ? 'aspect-[4/3]'
143
+ : aspectRatio === 'widescreen'
144
+ ? 'aspect-video'
145
+ : 'aspect-square';
146
+ return (_jsx("figure", { className: "my-2 min-w-0 max-w-2xl", children: _jsxs("div", { className: cn('border-border bg-muted relative w-full overflow-hidden rounded-lg border', ratioClass), children: [_jsx("div", { className: "from-muted via-background/45 to-muted absolute inset-0 animate-pulse bg-gradient-to-br" }), _jsx("div", { className: "bg-foreground/5 absolute inset-0" }), _jsxs("div", { className: "text-muted-foreground absolute inset-0 flex flex-col items-center justify-center gap-2 px-6 text-center", children: [_jsx(Sparkles, { className: "size-5 animate-pulse" }), _jsx("span", { className: "max-w-full truncate text-xs font-medium", children: prompt
147
+ ? `Generating "${prompt.slice(0, 54)}${prompt.length > 54 ? '...' : ''}"`
148
+ : 'Generating image...' })] })] }) }));
149
+ },
150
+ renderOutput: (output, toolCallId) => {
151
+ const result = output;
152
+ if (!result) {
153
+ return (_jsx(LoadingIndicator, { icon: Sparkles, children: "Generating image..." }, toolCallId));
154
+ }
155
+ const parts = Array.isArray(result.value) ? result.value : [];
156
+ const image = parts.find((part) => (part.type === 'image-data' || part.type === 'media') &&
157
+ part.data &&
158
+ part.mediaType?.startsWith('image/'));
159
+ const imageSrc = result.imageUrl ??
160
+ (image?.data && image.mediaType
161
+ ? `data:${image.mediaType};base64,${image.data}`
162
+ : undefined);
163
+ if (!imageSrc) {
164
+ return (_jsxs("div", { className: "bg-muted text-muted-foreground my-1 flex max-w-full items-start gap-2 rounded-md px-2 py-1.5 text-xs", children: [_jsx(Sparkles, { className: "mt-0.5 size-3.5 shrink-0" }), _jsx("span", { className: "min-w-0 whitespace-normal break-words leading-relaxed", children: result.imagePath
165
+ ? `Generated image saved at ${result.imagePath}.`
166
+ : 'Generated image saved.' })] }, toolCallId));
167
+ }
168
+ return (_jsxs("figure", { className: "my-2 min-w-0 max-w-2xl", children: [_jsx("div", { className: "border-border bg-muted overflow-hidden rounded-lg border", children: _jsx("img", { src: imageSrc, alt: result.prompt || 'Generated image', className: "block max-h-[420px] w-full object-contain" }) }), _jsxs("figcaption", { className: "text-muted-foreground mt-1 flex min-w-0 items-center gap-1 text-xs", children: [_jsx(Sparkles, { className: "size-3 shrink-0" }), _jsx("span", { className: "truncate", children: result.imagePath
169
+ ? `${getFileName(result.imagePath)}${result.size ? ` - ${result.size}` : ''}`
170
+ : result.size || 'Generated image' })] })] }, toolCallId));
171
+ },
172
+ },
173
+ take_screenshot: {
174
+ loadingLabel: 'Taking screenshot...',
175
+ marker: ThinkingTimelineMarker.Default,
176
+ inline: true,
177
+ renderLoading: () => (_jsx(LoadingIndicator, { icon: Camera, children: "Taking screenshot..." })),
178
+ renderOutput: (output, toolCallId) => {
179
+ const result = output;
180
+ if (!result) {
181
+ return (_jsx(LoadingIndicator, { icon: Camera, children: "Taking screenshot..." }, toolCallId));
182
+ }
183
+ if (result.type === 'error-text') {
184
+ return (_jsxs("div", { className: "bg-destructive/10 text-destructive my-1 flex max-w-full items-start gap-2 rounded-md px-2 py-1.5 text-xs", children: [_jsx(Camera, { className: "mt-0.5 size-3.5 shrink-0" }), _jsx("span", { className: "min-w-0 whitespace-normal break-words leading-relaxed", children: typeof result.value === 'string'
185
+ ? result.value
186
+ : 'Screenshot failed' })] }, toolCallId));
187
+ }
188
+ const parts = Array.isArray(result.value) ? result.value : [];
189
+ const image = parts.find((part) => (part.type === 'image-data' || part.type === 'media') &&
190
+ part.data &&
191
+ part.mediaType?.startsWith('image/'));
192
+ const text = parts.find((part) => part.type === 'text')?.text;
193
+ if (!image?.data || !image.mediaType) {
194
+ return (_jsxs("div", { className: "bg-muted text-muted-foreground my-1 inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(Camera, { className: "size-3.5 shrink-0" }), _jsx("span", { className: "truncate", children: text || 'Screenshot captured' }), _jsx(Check, { className: "size-3 shrink-0 text-green-600" })] }, toolCallId));
195
+ }
196
+ return (_jsxs("figure", { className: "my-2 min-w-0 max-w-2xl", children: [_jsx("div", { className: "border-border bg-muted overflow-hidden rounded-lg border", children: _jsx("img", { src: `data:${image.mediaType};base64,${image.data}`, alt: "Current app screenshot", className: "block max-h-[420px] w-full object-contain" }) }), text && (_jsxs("figcaption", { className: "text-muted-foreground mt-1 flex items-center gap-1 text-xs", children: [_jsx(Camera, { className: "size-3" }), _jsx("span", { className: "truncate", children: text })] }))] }, toolCallId));
197
+ },
198
+ },
111
199
  listMoldableAppApi: {
112
200
  loadingLabel: 'Listing app APIs...',
113
201
  marker: ThinkingTimelineMarker.Default,
@@ -1 +1 @@
1
- {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/components/markdown.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAyB,MAAM,OAAO,CAAA;AAgF7C,KAAK,mBAAmB,GAAG;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAAA;IACvC,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAC7B,CAAA;AAoSD,eAAO,MAAM,QAAQ,uFA5DlB,mBAAmB,6CAmErB,CAAA"}
1
+ {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/components/markdown.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAyB,MAAM,OAAO,CAAA;AAiF7C,KAAK,mBAAmB,GAAG;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAAA;IACvC,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAC7B,CAAA;AA8TD,eAAO,MAAM,QAAQ,uFA5DlB,mBAAmB,6CAmErB,CAAA"}
@@ -4,6 +4,7 @@ import React, { memo, useState } from 'react';
4
4
  import ReactMarkdown from 'react-markdown';
5
5
  import { cn } from '../lib/utils';
6
6
  import { CodeBlock } from './code-block';
7
+ import { RichMediaPlayer } from './rich-media-player';
7
8
  import { Button } from './ui/button';
8
9
  import { Checkbox } from './ui/checkbox';
9
10
  import rehypeRaw from 'rehype-raw';
@@ -62,10 +63,23 @@ function processMentions(text) {
62
63
  return parts.length > 0 ? parts : [text];
63
64
  }
64
65
  const components = {
65
- img({ src, alt }) {
66
+ img({ src, alt, className, style, width }) {
66
67
  if (!src || typeof src !== 'string')
67
68
  return null;
68
- return (_jsx("span", { className: "flex justify-center", children: _jsx("img", { src: src, alt: alt ?? '', className: "h-auto max-w-full rounded-lg" }) }));
69
+ const imageWidth = typeof width === 'number' || typeof width === 'string'
70
+ ? width
71
+ : undefined;
72
+ return (_jsx("span", { className: "flex justify-center", children: _jsx("img", { src: src, alt: alt ?? '', className: cn('h-auto max-w-full rounded-lg', className), style: style, width: imageWidth }) }));
73
+ },
74
+ audio({ src, title }) {
75
+ if (!src || typeof src !== 'string')
76
+ return null;
77
+ return (_jsx(RichMediaPlayer, { kind: "audio", src: src, title: typeof title === 'string' ? title : undefined }));
78
+ },
79
+ video({ src, title }) {
80
+ if (!src || typeof src !== 'string')
81
+ return null;
82
+ return (_jsx(RichMediaPlayer, { kind: "video", src: src, title: typeof title === 'string' ? title : undefined }));
69
83
  },
70
84
  table({ children }) {
71
85
  return (_jsx("table", { className: "border-collapse border px-3 py-1", children: children }));
@@ -142,9 +156,9 @@ const components = {
142
156
  return _jsx(CodeBlock, { code: codeContent, language: language });
143
157
  }
144
158
  // Inline code - style nicely, remove prose backticks via before:/after:content-none
145
- return (_jsx("code", { className: "bg-muted text-foreground rounded-sm px-1.5 py-0.5 font-mono text-[0.9em] before:content-none after:content-none", ...props, children: children }));
159
+ return (_jsx("code", { className: "bg-muted/70 text-foreground rounded px-1 py-0.5 font-mono text-[0.92em] before:content-none after:content-none", ...props, children: children }));
146
160
  },
147
- a: ({ children, href, ...props }) => (_jsx("a", { className: "text-primary break-all font-bold hover:underline", href: href ?? '#', target: "_blank", rel: "noreferrer", ...props, children: children })),
161
+ a: ({ children, href, ...props }) => (_jsx("a", { className: "text-primary break-all font-medium hover:underline", href: href ?? '#', target: "_blank", rel: "noreferrer", ...props, children: children })),
148
162
  // Process @mentions in text and paragraph nodes
149
163
  p: ({ children, ...props }) => {
150
164
  const processedChildren = React.Children.map(children, (child) => {
@@ -0,0 +1,8 @@
1
+ export type RichMediaPlayerProps = {
2
+ className?: string;
3
+ kind: 'audio' | 'video';
4
+ src: string;
5
+ title?: string;
6
+ };
7
+ export declare function RichMediaPlayer({ className, kind, src, title, }: RichMediaPlayerProps): import("react/jsx-runtime").JSX.Element;
8
+ //# sourceMappingURL=rich-media-player.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rich-media-player.d.ts","sourceRoot":"","sources":["../../src/components/rich-media-player.tsx"],"names":[],"mappings":"AAaA,MAAM,MAAM,oBAAoB,GAAG;IACjC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,OAAO,GAAG,OAAO,CAAA;IACvB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAuBD,wBAAgB,eAAe,CAAC,EAC9B,SAAS,EACT,IAAI,EACJ,GAAG,EACH,KAAK,GACN,EAAE,oBAAoB,2CAmNtB"}
@@ -0,0 +1,99 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useCallback, useMemo, useRef, useState } from 'react';
4
+ import { FastForward, Pause, Play, Rewind, Volume2, VolumeX, } from 'lucide-react';
5
+ import { cn } from '../lib/utils';
6
+ const PLAYBACK_RATES = [0.75, 1, 1.25, 1.5, 2];
7
+ function formatTime(value) {
8
+ if (!Number.isFinite(value) || value < 0)
9
+ return '0:00';
10
+ const totalSeconds = Math.floor(value);
11
+ const hours = Math.floor(totalSeconds / 3600);
12
+ const minutes = Math.floor((totalSeconds % 3600) / 60);
13
+ const seconds = totalSeconds % 60;
14
+ if (hours > 0) {
15
+ return `${hours}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
16
+ }
17
+ return `${minutes}:${String(seconds).padStart(2, '0')}`;
18
+ }
19
+ function safeDuration(duration) {
20
+ return Number.isFinite(duration) && duration > 0 ? duration : 0;
21
+ }
22
+ export function RichMediaPlayer({ className, kind, src, title, }) {
23
+ const mediaRef = useRef(null);
24
+ const containerRef = useRef(null);
25
+ const [currentTime, setCurrentTime] = useState(0);
26
+ const [duration, setDuration] = useState(0);
27
+ const [isPlaying, setIsPlaying] = useState(false);
28
+ const [isMuted, setIsMuted] = useState(false);
29
+ const [volume, setVolume] = useState(1);
30
+ const [playbackRate, setPlaybackRate] = useState(1);
31
+ const durationValue = safeDuration(duration);
32
+ const canSeek = durationValue > 0;
33
+ const isAudio = kind === 'audio';
34
+ const mediaTitle = useMemo(() => {
35
+ if (title)
36
+ return title;
37
+ try {
38
+ return decodeURIComponent(src.split('/').pop()?.split('?')[0] ?? src);
39
+ }
40
+ catch {
41
+ return src;
42
+ }
43
+ }, [src, title]);
44
+ const setMediaElement = useCallback((element) => {
45
+ mediaRef.current = element;
46
+ }, []);
47
+ const togglePlayback = () => {
48
+ const media = mediaRef.current;
49
+ if (!media)
50
+ return;
51
+ if (media.paused) {
52
+ void media.play();
53
+ }
54
+ else {
55
+ media.pause();
56
+ }
57
+ };
58
+ const seekBy = (offset) => {
59
+ const media = mediaRef.current;
60
+ if (!media)
61
+ return;
62
+ media.currentTime = Math.max(0, Math.min(durationValue, media.currentTime + offset));
63
+ };
64
+ const seekTo = (value) => {
65
+ const media = mediaRef.current;
66
+ if (!media)
67
+ return;
68
+ media.currentTime = value;
69
+ setCurrentTime(value);
70
+ };
71
+ const changeVolume = (value) => {
72
+ const media = mediaRef.current;
73
+ if (!media)
74
+ return;
75
+ media.volume = value;
76
+ media.muted = value === 0;
77
+ setVolume(value);
78
+ setIsMuted(value === 0);
79
+ };
80
+ const toggleMuted = () => {
81
+ const media = mediaRef.current;
82
+ if (!media)
83
+ return;
84
+ const nextMuted = !media.muted;
85
+ media.muted = nextMuted;
86
+ setIsMuted(nextMuted);
87
+ };
88
+ const cyclePlaybackRate = () => {
89
+ const media = mediaRef.current;
90
+ if (!media)
91
+ return;
92
+ const currentIndex = PLAYBACK_RATES.findIndex((rate) => rate === playbackRate);
93
+ const nextRate = PLAYBACK_RATES[(currentIndex + 1) % PLAYBACK_RATES.length] ?? 1;
94
+ media.playbackRate = nextRate;
95
+ setPlaybackRate(nextRate);
96
+ };
97
+ const controls = (_jsxs("div", { className: cn('flex flex-col gap-2 text-white', isAudio ? 'p-3' : 'bg-gradient-to-b from-transparent via-black/65 to-neutral-950 px-3 pt-10 pb-3'), children: [_jsx("input", { "aria-label": "Seek", className: "h-2 w-full cursor-pointer accent-white disabled:cursor-default disabled:opacity-50", disabled: !canSeek, max: durationValue, min: 0, onChange: (event) => seekTo(Number(event.currentTarget.value)), step: 0.1, type: "range", value: Math.min(currentTime, durationValue) }), _jsxs("div", { className: "flex h-10 items-center gap-3 px-1", children: [_jsx("button", { "aria-label": isPlaying ? 'Pause' : 'Play', className: "cursor-pointer rounded-full p-1 text-white transition-colors hover:text-primary focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white", onClick: togglePlayback, type: "button", children: isPlaying ? _jsx(Pause, { className: "size-6" }) : _jsx(Play, { className: "size-6" }) }), _jsx("button", { "aria-label": "Rewind 10 seconds", className: "cursor-pointer rounded-full p-1 text-white/85 transition-colors hover:text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white", onClick: () => seekBy(-10), type: "button", children: _jsx(Rewind, { className: "size-5" }) }), _jsx("button", { "aria-label": "Forward 10 seconds", className: "cursor-pointer rounded-full p-1 text-white/85 transition-colors hover:text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white", onClick: () => seekBy(10), type: "button", children: _jsx(FastForward, { className: "size-5" }) }), _jsxs("div", { className: "group/volume flex items-center gap-2", children: [_jsx("button", { "aria-label": isMuted ? 'Unmute' : 'Mute', className: "cursor-pointer rounded-full p-1 text-white/85 transition-colors hover:text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white", onClick: toggleMuted, type: "button", children: isMuted || volume === 0 ? (_jsx(VolumeX, { className: "size-5" })) : (_jsx(Volume2, { className: "size-5" })) }), _jsx("input", { "aria-label": "Volume", className: "h-2 w-0 cursor-pointer accent-white opacity-0 transition-all group-hover/volume:w-20 group-hover/volume:opacity-100 focus:w-20 focus:opacity-100", max: 1, min: 0, onChange: (event) => changeVolume(Number(event.currentTarget.value)), step: 0.05, type: "range", value: isMuted ? 0 : volume })] }), _jsxs("span", { className: "ml-auto min-w-28 text-right font-mono text-xs text-white/80", children: [formatTime(currentTime), " / ", formatTime(durationValue)] }), _jsxs("button", { "aria-label": "Change playback speed", className: "cursor-pointer rounded px-2 py-1 font-mono text-xs text-white/85 transition-colors hover:text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white", onClick: cyclePlaybackRate, type: "button", children: [playbackRate, "x"] })] })] }));
98
+ return (_jsx("div", { className: cn('not-prose overflow-hidden rounded-lg border border-black/5 bg-neutral-950 text-white shadow-sm dark:border-white/10', className), ref: containerRef, children: _jsxs("div", { className: cn('group/mediaplayer relative w-full overflow-hidden', !isAudio && 'bg-black'), children: [isAudio ? (_jsx("audio", { "aria-label": mediaTitle, onDurationChange: (event) => setDuration(event.currentTarget.duration), onEnded: () => setIsPlaying(false), onLoadedMetadata: (event) => setDuration(event.currentTarget.duration), onPause: () => setIsPlaying(false), onPlay: () => setIsPlaying(true), onTimeUpdate: (event) => setCurrentTime(event.currentTarget.currentTime), preload: "metadata", ref: setMediaElement, src: src })) : (_jsx("video", { className: "h-auto max-h-[600px] min-h-[225px] w-full bg-black object-contain", onClick: togglePlayback, onDurationChange: (event) => setDuration(event.currentTarget.duration), onEnded: () => setIsPlaying(false), onLoadedMetadata: (event) => setDuration(event.currentTarget.duration), onPause: () => setIsPlaying(false), onPlay: () => setIsPlaying(true), onTimeUpdate: (event) => setCurrentTime(event.currentTarget.currentTime), playsInline: true, preload: "metadata", ref: setMediaElement, src: src })), _jsx("div", { className: cn(isAudio ? 'bg-neutral-950' : 'absolute right-0 bottom-0 left-0 transition-transform duration-300', !isAudio && isPlaying && 'translate-y-[calc(100%-36px)] group-hover/mediaplayer:translate-y-0'), children: controls })] }) }));
99
+ }
@@ -2,11 +2,12 @@ import * as React from 'react';
2
2
  import { Dialog } from './dialog';
3
3
  import { Command as CommandPrimitive } from 'cmdk';
4
4
  declare function Command({ className, ...props }: React.ComponentProps<typeof CommandPrimitive>): import("react/jsx-runtime").JSX.Element;
5
- declare function CommandDialog({ title, description, children, className, showCloseButton, ...props }: React.ComponentProps<typeof Dialog> & {
5
+ declare function CommandDialog({ title, description, children, className, showCloseButton, filter, ...props }: React.ComponentProps<typeof Dialog> & {
6
6
  title?: string;
7
7
  description?: string;
8
8
  className?: string;
9
9
  showCloseButton?: boolean;
10
+ filter?: React.ComponentProps<typeof CommandPrimitive>['filter'];
10
11
  }): import("react/jsx-runtime").JSX.Element;
11
12
  declare function CommandInput({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Input>): import("react/jsx-runtime").JSX.Element;
12
13
  declare function CommandList({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.List>): import("react/jsx-runtime").JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../../src/components/ui/command.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EACL,MAAM,EAKP,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,MAAM,CAAA;AAElD,iBAAS,OAAO,CAAC,EACf,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,2CAW/C;AAED,iBAAS,aAAa,CAAC,EACrB,KAAyB,EACzB,WAA8C,EAC9C,QAAQ,EACR,SAAS,EACT,eAAsB,EACtB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,GAAG;IACvC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B,2CAiBA;AAED,iBAAS,YAAY,CAAC,EACpB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,KAAK,CAAC,2CAiBrD;AAED,iBAAS,WAAW,CAAC,EACnB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,IAAI,CAAC,2CAWpD;AAED,iBAAS,YAAY,CAAC,EACpB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,KAAK,CAAC,2CAQrD;AAED,iBAAS,YAAY,CAAC,EACpB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,KAAK,CAAC,2CAWrD;AAED,iBAAS,gBAAgB,CAAC,EACxB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,SAAS,CAAC,2CAQzD;AAED,iBAAS,WAAW,CAAC,EACnB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,IAAI,CAAC,2CAWpD;AAED,iBAAS,eAAe,CAAC,EACvB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,2CAW9B;AAED,OAAO,EACL,OAAO,EACP,aAAa,EACb,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,eAAe,EACf,gBAAgB,GACjB,CAAA"}
1
+ {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../../src/components/ui/command.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EACL,MAAM,EAKP,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,MAAM,CAAA;AAElD,iBAAS,OAAO,CAAC,EACf,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,2CAW/C;AAED,iBAAS,aAAa,CAAC,EACrB,KAAyB,EACzB,WAA8C,EAC9C,QAAQ,EACR,SAAS,EACT,eAAsB,EACtB,MAAM,EACN,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,GAAG;IACvC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,MAAM,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,CAAC,QAAQ,CAAC,CAAA;CACjE,2CAoBA;AAED,iBAAS,YAAY,CAAC,EACpB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,KAAK,CAAC,2CAiBrD;AAED,iBAAS,WAAW,CAAC,EACnB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,IAAI,CAAC,2CAWpD;AAED,iBAAS,YAAY,CAAC,EACpB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,KAAK,CAAC,2CAQrD;AAED,iBAAS,YAAY,CAAC,EACpB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,KAAK,CAAC,2CAWrD;AAED,iBAAS,gBAAgB,CAAC,EACxB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,SAAS,CAAC,2CAQzD;AAED,iBAAS,WAAW,CAAC,EACnB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,IAAI,CAAC,2CAWpD;AAED,iBAAS,eAAe,CAAC,EACvB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,2CAW9B;AAED,OAAO,EACL,OAAO,EACP,aAAa,EACb,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,eAAe,EACf,gBAAgB,GACjB,CAAA"}
@@ -7,8 +7,8 @@ import { Command as CommandPrimitive } from 'cmdk';
7
7
  function Command({ className, ...props }) {
8
8
  return (_jsx(CommandPrimitive, { "data-slot": "command", className: cn('bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md', className), ...props }));
9
9
  }
10
- function CommandDialog({ title = 'Command Palette', description = 'Search for a command to run...', children, className, showCloseButton = true, ...props }) {
11
- return (_jsxs(Dialog, { ...props, children: [_jsxs(DialogHeader, { className: "sr-only", children: [_jsx(DialogTitle, { children: title }), _jsx(DialogDescription, { children: description })] }), _jsx(DialogContent, { className: cn('overflow-hidden p-0', className), showCloseButton: showCloseButton, children: _jsx(Command, { className: "[&_[cmdk-group-heading]]:text-muted-foreground **:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5", children: children }) })] }));
10
+ function CommandDialog({ title = 'Command Palette', description = 'Search for a command to run...', children, className, showCloseButton = true, filter, ...props }) {
11
+ return (_jsxs(Dialog, { ...props, children: [_jsxs(DialogHeader, { className: "sr-only", children: [_jsx(DialogTitle, { children: title }), _jsx(DialogDescription, { children: description })] }), _jsx(DialogContent, { className: cn('overflow-hidden p-0', className), showCloseButton: showCloseButton, children: _jsx(Command, { filter: filter, className: "[&_[cmdk-group-heading]]:text-muted-foreground **:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5", children: children }) })] }));
12
12
  }
13
13
  function CommandInput({ className, ...props }) {
14
14
  return (_jsxs("div", { "data-slot": "command-input-wrapper", className: "flex h-9 items-center gap-2 border-b px-3", children: [_jsx(SearchIcon, { className: "size-4 shrink-0 opacity-50" }), _jsx(CommandPrimitive.Input, { "data-slot": "command-input", className: cn('placeholder:text-muted-foreground outline-hidden flex h-10 w-full rounded-md bg-transparent py-3 text-sm disabled:cursor-not-allowed disabled:opacity-50', className), ...props })] }));
@@ -1,6 +1,9 @@
1
1
  import { type GroupProps, type PanelProps, type SeparatorProps } from 'react-resizable-panels';
2
2
  declare function ResizablePanelGroup({ className, ...props }: GroupProps): import("react/jsx-runtime").JSX.Element;
3
- declare function ResizablePanel({ ...props }: PanelProps): import("react/jsx-runtime").JSX.Element;
3
+ type ResizablePanelProps = PanelProps & {
4
+ groupResizeBehavior?: string;
5
+ };
6
+ declare function ResizablePanel({ groupResizeBehavior: _groupResizeBehavior, ...props }: ResizablePanelProps): import("react/jsx-runtime").JSX.Element;
4
7
  declare function ResizableHandle({ withHandle, className, ...props }: SeparatorProps & {
5
8
  withHandle?: boolean;
6
9
  }): import("react/jsx-runtime").JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"resizable.d.ts","sourceRoot":"","sources":["../../../src/components/ui/resizable.tsx"],"names":[],"mappings":"AAIA,OAAO,EAEL,KAAK,UAAU,EAEf,KAAK,UAAU,EAEf,KAAK,cAAc,EACpB,MAAM,wBAAwB,CAAA;AAG/B,iBAAS,mBAAmB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,UAAU,2CAW/D;AAED,iBAAS,cAAc,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,UAAU,2CAE/C;AAED,iBAAS,eAAe,CAAC,EACvB,UAAU,EACV,SAAS,EACT,GAAG,KAAK,EACT,EAAE,cAAc,GAAG;IAClB,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB,2CAiBA;AAED,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,eAAe,EAAE,CAAA"}
1
+ {"version":3,"file":"resizable.d.ts","sourceRoot":"","sources":["../../../src/components/ui/resizable.tsx"],"names":[],"mappings":"AAIA,OAAO,EAEL,KAAK,UAAU,EAEf,KAAK,UAAU,EAEf,KAAK,cAAc,EACpB,MAAM,wBAAwB,CAAA;AAG/B,iBAAS,mBAAmB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,UAAU,2CAW/D;AAED,KAAK,mBAAmB,GAAG,UAAU,GAAG;IACtC,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAC7B,CAAA;AAED,iBAAS,cAAc,CAAC,EACtB,mBAAmB,EAAE,oBAAoB,EACzC,GAAG,KAAK,EACT,EAAE,mBAAmB,2CAErB;AAED,iBAAS,eAAe,CAAC,EACvB,UAAU,EACV,SAAS,EACT,GAAG,KAAK,EACT,EAAE,cAAc,GAAG;IAClB,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB,2CAiBA;AAED,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,eAAe,EAAE,CAAA"}
@@ -6,7 +6,7 @@ import { cn } from '../../lib/utils';
6
6
  function ResizablePanelGroup({ className, ...props }) {
7
7
  return (_jsx(Group, { "data-slot": "resizable-panel-group", className: cn('flex h-full w-full data-[panel-group-direction=vertical]:flex-col', className), ...props }));
8
8
  }
9
- function ResizablePanel({ ...props }) {
9
+ function ResizablePanel({ groupResizeBehavior: _groupResizeBehavior, ...props }) {
10
10
  return _jsx(Panel, { "data-slot": "resizable-panel", ...props });
11
11
  }
12
12
  function ResizableHandle({ withHandle, className, ...props }) {
package/dist/index.d.ts CHANGED
@@ -1,11 +1,13 @@
1
1
  export { cn } from './lib/utils';
2
2
  export { ThemeProvider, useTheme, themeScript, type Theme } from './lib/theme';
3
3
  export { WorkspaceProvider, useWorkspace, WORKSPACE_HEADER, } from './lib/workspace';
4
- export { useMoldableCommands, useMoldableCommand, isInMoldable, sendToMoldable, downloadFile, type AppCommand, type CommandAction, type CommandsResponse, type CommandMessage, type DownloadFileOptions, } from './lib/commands';
4
+ export { useMoldableCommands, useMoldableCommand, useMoldableAppCommands, isInMoldable, sendToMoldable, downloadFile, type AppCommand, type CommandAction, type CommandsResponse, type CommandMessage, type DownloadFileOptions, } from './lib/commands';
5
5
  export * from './components/ui';
6
6
  export { useIsMobile } from './hooks/use-mobile';
7
7
  export { Markdown } from './components/markdown';
8
+ export { RichMediaPlayer, type RichMediaPlayerProps, } from './components/rich-media-player';
8
9
  export { CodeBlock } from './components/code-block';
9
10
  export { WidgetLayout } from './components/widget-layout';
11
+ export { AppErrorBoundary } from './components/app-error-boundary';
10
12
  export * from './components/chat';
11
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAA;AAGhC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,WAAW,EAAE,KAAK,KAAK,EAAE,MAAM,aAAa,CAAA;AAG9E,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,gBAAgB,GACjB,MAAM,iBAAiB,CAAA;AAGxB,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,mBAAmB,GACzB,MAAM,gBAAgB,CAAA;AAGvB,cAAc,iBAAiB,CAAA;AAG/B,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAGhD,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAGhD,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAGnD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAGzD,cAAc,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAA;AAGhC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,WAAW,EAAE,KAAK,KAAK,EAAE,MAAM,aAAa,CAAA;AAG9E,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,gBAAgB,GACjB,MAAM,iBAAiB,CAAA;AAGxB,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,sBAAsB,EACtB,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,mBAAmB,GACzB,MAAM,gBAAgB,CAAA;AAGvB,cAAc,iBAAiB,CAAA;AAG/B,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAGhD,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAChD,OAAO,EACL,eAAe,EACf,KAAK,oBAAoB,GAC1B,MAAM,gCAAgC,CAAA;AAGvC,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAGnD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAGzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAA;AAGlE,cAAc,mBAAmB,CAAA"}
package/dist/index.js CHANGED
@@ -5,16 +5,19 @@ export { ThemeProvider, useTheme, themeScript } from './lib/theme';
5
5
  // Export workspace
6
6
  export { WorkspaceProvider, useWorkspace, WORKSPACE_HEADER, } from './lib/workspace';
7
7
  // Export commands
8
- export { useMoldableCommands, useMoldableCommand, isInMoldable, sendToMoldable, downloadFile, } from './lib/commands';
8
+ export { useMoldableCommands, useMoldableCommand, useMoldableAppCommands, isInMoldable, sendToMoldable, downloadFile, } from './lib/commands';
9
9
  // Export UI components
10
10
  export * from './components/ui';
11
11
  // Export hooks
12
12
  export { useIsMobile } from './hooks/use-mobile';
13
13
  // Export Markdown
14
14
  export { Markdown } from './components/markdown';
15
+ export { RichMediaPlayer, } from './components/rich-media-player';
15
16
  // Export CodeBlock
16
17
  export { CodeBlock } from './components/code-block';
17
18
  // Export WidgetLayout
18
19
  export { WidgetLayout } from './components/widget-layout';
20
+ // Export app error boundary
21
+ export { AppErrorBoundary } from './components/app-error-boundary';
19
22
  // Export chat components
20
23
  export * from './components/chat';
@@ -70,6 +70,14 @@ export interface CommandMessage {
70
70
  * ```
71
71
  */
72
72
  export declare function useMoldableCommands(handlers: Record<string, (payload?: unknown) => void>): void;
73
+ /**
74
+ * Publish the commands that make sense for the app's current view.
75
+ *
76
+ * Apps should call this with a stable, memoized command list. The desktop
77
+ * command menu will prefer these commands over the app's static
78
+ * /api/moldable/commands response while the app is running.
79
+ */
80
+ export declare function useMoldableAppCommands(appId: string, commands: AppCommand[]): void;
73
81
  /**
74
82
  * Hook to register a single command handler
75
83
  * Useful when you want to register handlers in different components
@@ -1 +1 @@
1
- {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/lib/commands.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/lib/commands.ts"],"names":[],"mappings":"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"}
@@ -33,6 +33,24 @@ export function useMoldableCommands(handlers) {
33
33
  return () => window.removeEventListener('message', handleMessage);
34
34
  }, []);
35
35
  }
36
+ /**
37
+ * Publish the commands that make sense for the app's current view.
38
+ *
39
+ * Apps should call this with a stable, memoized command list. The desktop
40
+ * command menu will prefer these commands over the app's static
41
+ * /api/moldable/commands response while the app is running.
42
+ */
43
+ export function useMoldableAppCommands(appId, commands) {
44
+ useEffect(() => {
45
+ if (!isInMoldable())
46
+ return;
47
+ window.parent.postMessage({
48
+ type: 'moldable:set-app-commands',
49
+ appId,
50
+ commands,
51
+ }, '*');
52
+ }, [appId, commands]);
53
+ }
36
54
  /**
37
55
  * Hook to register a single command handler
38
56
  * Useful when you want to register handlers in different components
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moldable-ai/ui",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "description": "Shared UI components for Moldable applications",
5
5
  "author": "Desiderata LLC",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -33,8 +33,19 @@
33
33
  "src/styles",
34
34
  "LICENSE"
35
35
  ],
36
+ "scripts": {
37
+ "build": "tsc",
38
+ "dev": "tsc --watch",
39
+ "lint": "eslint . --ext .ts,.tsx --max-warnings 0",
40
+ "format": "prettier --write . --ignore-path ../../.prettierignore",
41
+ "check-types": "tsc --noEmit",
42
+ "test": "vitest run"
43
+ },
36
44
  "dependencies": {
37
45
  "@base-ui/react": "^1.1.0",
46
+ "@dnd-kit/core": "^6.3.1",
47
+ "@dnd-kit/sortable": "^10.0.0",
48
+ "@dnd-kit/utilities": "^3.2.2",
38
49
  "@hookform/resolvers": "^5.2.2",
39
50
  "@radix-ui/react-accordion": "^1.2.12",
40
51
  "@radix-ui/react-alert-dialog": "^1.1.15",
@@ -89,26 +100,18 @@
89
100
  "zod": "^4.3.5"
90
101
  },
91
102
  "devDependencies": {
103
+ "@moldable-ai/eslint-config": "workspace:*",
104
+ "@moldable-ai/prettier-config": "workspace:*",
105
+ "@moldable-ai/typescript-config": "workspace:*",
92
106
  "@types/node": "^24.0.3",
93
107
  "@types/react": "^19.1.6",
94
108
  "@types/react-dom": "^19.1.6",
95
109
  "jsdom": "^26.1.0",
96
110
  "typescript": "^5.8.3",
97
- "vitest": "^3.2.4",
98
- "@moldable-ai/typescript-config": "0.1.3",
99
- "@moldable-ai/eslint-config": "0.1.4",
100
- "@moldable-ai/prettier-config": "0.1.3"
111
+ "vitest": "^3.2.4"
101
112
  },
102
113
  "peerDependencies": {
103
114
  "react": "^19.0.0",
104
115
  "react-dom": "^19.0.0"
105
- },
106
- "scripts": {
107
- "build": "tsc",
108
- "dev": "tsc --watch",
109
- "lint": "eslint . --ext .ts,.tsx --max-warnings 0",
110
- "format": "prettier --write . --ignore-path ../../.prettierignore",
111
- "check-types": "tsc --noEmit",
112
- "test": "vitest run"
113
116
  }
114
- }
117
+ }
@@ -68,6 +68,31 @@
68
68
  --chat-safe-padding: var(--chat-safe-padding);
69
69
  }
70
70
 
71
+ @keyframes moldable-chat-shimmer {
72
+ 0% {
73
+ background-position: 140% 50%;
74
+ }
75
+ 100% {
76
+ background-position: -40% 50%;
77
+ }
78
+ }
79
+
80
+ .moldable-chat-shimmer {
81
+ color: transparent;
82
+ background: linear-gradient(
83
+ 90deg,
84
+ color-mix(in oklch, var(--muted-foreground) 72%, transparent) 0%,
85
+ color-mix(in oklch, var(--foreground) 92%, white 8%) 42%,
86
+ color-mix(in oklch, var(--foreground) 100%, white 16%) 50%,
87
+ color-mix(in oklch, var(--foreground) 92%, white 8%) 58%,
88
+ color-mix(in oklch, var(--muted-foreground) 72%, transparent) 100%
89
+ );
90
+ background-size: 240% 100%;
91
+ -webkit-background-clip: text;
92
+ background-clip: text;
93
+ animation: moldable-chat-shimmer 1.8s ease-in-out infinite;
94
+ }
95
+
71
96
  :root {
72
97
  --radius: 0.65rem;
73
98
  --background: oklch(1 0 0);
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=commands.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"commands.test.d.ts","sourceRoot":"","sources":["../../src/lib/commands.test.ts"],"names":[],"mappings":""}