@moldable-ai/ui 0.2.10 → 0.2.13

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.
@@ -3,7 +3,6 @@ type ErrorSource = 'react' | 'window' | 'promise';
3
3
  interface AppErrorBoundaryProps {
4
4
  appName: string;
5
5
  children: ReactNode;
6
- surface?: 'app' | 'widget';
7
6
  }
8
7
  interface AppErrorBoundaryState {
9
8
  error: Error | null;
@@ -1 +1 @@
1
- {"version":3,"file":"app-error-boundary.d.ts","sourceRoot":"","sources":["../../src/components/app-error-boundary.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAGjE,KAAK,WAAW,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAA;AAEjD,UAAU,qBAAqB;IAC7B,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,SAAS,CAAA;IACnB,OAAO,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAA;CAC3B;AAED,UAAU,qBAAqB;IAC7B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;IACnB,MAAM,EAAE,WAAW,CAAA;IACnB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AAiCD,qBAAa,gBAAiB,SAAQ,SAAS,CAC7C,qBAAqB,EACrB,qBAAqB,CACtB;IACC,KAAK,EAAE,qBAAqB,CAI3B;IAED,MAAM,CAAC,wBAAwB,CAC7B,KAAK,EAAE,KAAK,GACX,OAAO,CAAC,qBAAqB,CAAC;IAIjC,iBAAiB,IAAI,IAAI;IAOzB,oBAAoB,IAAI,IAAI;IAU5B,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI;IAKtD,OAAO,CAAC,iBAAiB,CAKxB;IAED,OAAO,CAAC,wBAAwB,CAI/B;IAED,OAAO,CAAC,MAAM,CAEb;IAED,OAAO,CAAC,WAAW,CAclB;IAED,MAAM;CA8CP"}
1
+ {"version":3,"file":"app-error-boundary.d.ts","sourceRoot":"","sources":["../../src/components/app-error-boundary.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAGjE,KAAK,WAAW,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAA;AAEjD,UAAU,qBAAqB;IAC7B,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED,UAAU,qBAAqB;IAC7B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;IACnB,MAAM,EAAE,WAAW,CAAA;IACnB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AAiCD,qBAAa,gBAAiB,SAAQ,SAAS,CAC7C,qBAAqB,EACrB,qBAAqB,CACtB;IACC,KAAK,EAAE,qBAAqB,CAI3B;IAED,MAAM,CAAC,wBAAwB,CAC7B,KAAK,EAAE,KAAK,GACX,OAAO,CAAC,qBAAqB,CAAC;IAIjC,iBAAiB,IAAI,IAAI;IAOzB,oBAAoB,IAAI,IAAI;IAU5B,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI;IAKtD,OAAO,CAAC,iBAAiB,CAKxB;IAED,OAAO,CAAC,wBAAwB,CAI/B;IAED,OAAO,CAAC,MAAM,CAEb;IAED,OAAO,CAAC,WAAW,CAYlB;IAED,MAAM;CA8CP"}
@@ -68,10 +68,9 @@ export class AppErrorBoundary extends Component {
68
68
  const { error, source, componentStack } = this.state;
69
69
  if (!error)
70
70
  return;
71
- const surfaceLabel = this.props.surface === 'widget' ? ' widget' : ' full app';
72
71
  const errorReport = formatError(error, source, componentStack);
73
72
  const text = [
74
- `${this.props.appName}${surfaceLabel} hit an uncaught frontend error.`,
73
+ `${this.props.appName} hit an uncaught frontend error.`,
75
74
  'Please inspect and fix the client crash.',
76
75
  errorReport,
77
76
  ].join('\n\n');
@@ -1,5 +1,5 @@
1
1
  import type { ChangeEvent, FormEvent, ReactNode, RefObject } from 'react';
2
- export type ChatSubmitMode = 'send' | 'steer' | 'queue';
2
+ export type ChatSubmitMode = 'send' | 'queue' | 'manual-steer';
3
3
  export type ChatSlashCommandOption = {
4
4
  id: string;
5
5
  name: string;
@@ -1 +1 @@
1
- {"version":3,"file":"chat-input.d.ts","sourceRoot":"","sources":["../../../src/components/chat/chat-input.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,WAAW,EACX,SAAS,EAGT,SAAS,EACT,SAAS,EACV,MAAM,OAAO,CAAA;AAKd,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAA;AAEvD,MAAM,MAAM,sBAAsB,GAAG;IACnC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,KAAK,cAAc,GAAG;IACpB,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,CAAC,CAAC,EAAE,WAAW,CAAC,mBAAmB,CAAC,KAAK,IAAI,CAAA;IAC5D,QAAQ,EAAE,CACR,CAAC,EAAE,SAAS,CAAC,eAAe,CAAC,GAAG,SAAS,EACzC,IAAI,CAAC,EAAE,cAAc,KAClB,IAAI,CAAA;IACT,YAAY,EAAE,OAAO,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,SAAS,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAA;IAChD,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,gBAAgB,CAAC,EAAE,mBAAmB,EAAE,CAAA;IACxC,uBAAuB,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9C,mBAAmB,CAAC,EAAE,SAAS,CAAA;IAC/B,cAAc,CAAC,EAAE,SAAS,CAAA;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,aAAa,CAAC,EAAE,sBAAsB,EAAE,CAAA;IACxC,oBAAoB,CAAC,EAAE,CAAC,OAAO,EAAE,sBAAsB,KAAK,IAAI,CAAA;CACjE,CAAA;AAED,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,aAAa,EACb,QAAQ,EACR,YAAY,EACZ,WAA+B,EAC/B,QAAQ,EACR,MAAM,EACN,OAAe,EACf,gBAAqB,EACrB,uBAAuB,EACvB,mBAAmB,EACnB,cAAc,EACd,cAAsB,EACtB,aAAkB,EAClB,oBAAoB,GACrB,EAAE,cAAc,2CA2KhB"}
1
+ {"version":3,"file":"chat-input.d.ts","sourceRoot":"","sources":["../../../src/components/chat/chat-input.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,WAAW,EACX,SAAS,EAGT,SAAS,EACT,SAAS,EACV,MAAM,OAAO,CAAA;AAKd,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,cAAc,CAAA;AAE9D,MAAM,MAAM,sBAAsB,GAAG;IACnC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,KAAK,cAAc,GAAG;IACpB,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,CAAC,CAAC,EAAE,WAAW,CAAC,mBAAmB,CAAC,KAAK,IAAI,CAAA;IAC5D,QAAQ,EAAE,CACR,CAAC,EAAE,SAAS,CAAC,eAAe,CAAC,GAAG,SAAS,EACzC,IAAI,CAAC,EAAE,cAAc,KAClB,IAAI,CAAA;IACT,YAAY,EAAE,OAAO,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,SAAS,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAA;IAChD,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,gBAAgB,CAAC,EAAE,mBAAmB,EAAE,CAAA;IACxC,uBAAuB,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9C,mBAAmB,CAAC,EAAE,SAAS,CAAA;IAC/B,cAAc,CAAC,EAAE,SAAS,CAAA;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,aAAa,CAAC,EAAE,sBAAsB,EAAE,CAAA;IACxC,oBAAoB,CAAC,EAAE,CAAC,OAAO,EAAE,sBAAsB,KAAK,IAAI,CAAA;CACjE,CAAA;AAED,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,aAAa,EACb,QAAQ,EACR,YAAY,EACZ,WAA+B,EAC/B,QAAQ,EACR,MAAM,EACN,OAAe,EACf,gBAAqB,EACrB,uBAAuB,EACvB,mBAAmB,EACnB,cAAc,EACd,cAAsB,EACtB,aAAkB,EAClB,oBAAoB,GACrB,EAAE,cAAc,2CAyMhB"}
@@ -1,5 +1,5 @@
1
- import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
- import { ArrowUp, Image as ImageIcon, Square, X } from 'lucide-react';
1
+ import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { ArrowUp, CornerDownRight, Image as ImageIcon, ListPlus, Square, X, } from 'lucide-react';
3
3
  import { cn } from '../../lib/utils';
4
4
  import { Button } from '../ui/button';
5
5
  import { Textarea } from '../ui/textarea';
@@ -30,8 +30,10 @@ export function ChatInput({ input, onInputChange, onSubmit, isResponding, placeh
30
30
  onSubmit(undefined, isResponding ? 'queue' : 'send');
31
31
  }
32
32
  };
33
- // Keep the stop control available while a response is in progress.
34
- const showStopButton = isResponding;
33
+ // During a response, Enter/primary submit queues follow-up text. The explicit
34
+ // Steer control is the manual override for injecting it into the active turn.
35
+ const showStopButton = isResponding && !canSubmit;
36
+ const showQueueSubmitButton = isResponding && canSubmit;
35
37
  const handleButtonClick = (e) => {
36
38
  // When rendered as stop button (type="button"), always call onStop
37
39
  // Don't re-check conditions - trust the render decision to avoid race conditions
@@ -41,6 +43,12 @@ export function ChatInput({ input, onInputChange, onSubmit, isResponding, placeh
41
43
  }
42
44
  // If type="submit", form handles submission
43
45
  };
46
+ const handleSteerClick = (e) => {
47
+ e.preventDefault();
48
+ if (!canSubmit)
49
+ return;
50
+ onSubmit(undefined, 'manual-steer');
51
+ };
44
52
  return (_jsx("div", { className: cn('isolate w-full p-2'), "data-chat-input-wrapper": true, children: _jsx("div", { className: "mx-auto max-w-5xl", children: _jsx("form", { onSubmit: (e) => {
45
53
  if (submitDisabled && !isResponding) {
46
54
  e.preventDefault();
@@ -50,9 +58,11 @@ export function ChatInput({ input, onInputChange, onSubmit, isResponding, placeh
50
58
  }, children: _jsxs("div", { className: cn('bg-background relative flex w-full flex-col rounded-3xl border', compact && !hasAttachments && 'h-14'), children: [!compact && slashCommands.length > 0 && (_jsx("div", { className: "border-border bg-popover text-popover-foreground absolute bottom-full left-4 right-4 z-20 mb-2 overflow-hidden rounded-lg border shadow-lg", children: slashCommands.map((command) => (_jsxs("button", { type: "button", className: "hover:bg-accent hover:text-accent-foreground flex min-h-12 w-full cursor-pointer items-center gap-3 px-3 py-2.5 text-left transition-colors", onMouseDown: (event) => {
51
59
  event.preventDefault();
52
60
  onSlashCommandSelect?.(command);
53
- }, children: [_jsxs("span", { className: "bg-muted text-muted-foreground rounded px-1.5 py-0.5 font-mono text-xs", children: ["/", command.name] }), _jsx("span", { className: "min-w-0 text-xs", children: command.description })] }, command.id))) })), !compact && hasAttachments && (_jsx("div", { className: "flex max-h-32 gap-2 overflow-x-auto px-3 pt-3", children: imageAttachments.map((attachment) => (_jsxs("div", { className: "bg-muted/70 border-border flex max-w-48 shrink-0 items-center gap-2 rounded-xl border p-1.5 pr-2", children: [_jsx("div", { className: "bg-background flex size-9 shrink-0 items-center justify-center overflow-hidden rounded-lg border", children: attachment.previewUrl ? (_jsx("img", { src: attachment.previewUrl, alt: "", className: "size-full object-cover", draggable: false })) : (_jsx(ImageIcon, { className: "text-muted-foreground size-4" })) }), _jsx("span", { className: "min-w-0 truncate text-xs font-medium", title: attachment.path, children: attachment.name }), onRemoveImageAttachment && (_jsx("button", { type: "button", onClick: () => onRemoveImageAttachment(attachment.id), className: "text-muted-foreground hover:bg-accent hover:text-foreground flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-md transition-colors", "aria-label": `Remove ${attachment.name}`, children: _jsx(X, { className: "size-3.5" }) }))] }, attachment.id))) })), _jsx("div", { className: cn('w-full px-5', compact ? 'py-4' : 'pb-2 pt-4'), children: _jsx(Textarea, { ref: inputRef, value: input, placeholder: placeholder, onChange: onInputChange, onKeyDown: handleKeyDown, className: cn('max-h-32 min-h-[24px] resize-none rounded-none border-0 !bg-transparent p-0 text-sm shadow-none focus:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 dark:!bg-transparent', compact && 'max-h-6 min-h-6') }) }), !compact && (_jsxs("div", { className: "flex min-h-11 items-center justify-between gap-3 px-3 pb-3", children: [_jsx("div", { className: "flex min-w-0 shrink-0 items-center justify-start gap-1", children: footerStartControls }), _jsx("div", { className: "min-w-0 flex-1" }), _jsx("div", { className: "flex min-w-0 shrink-0 items-center justify-end gap-0.5", children: footerControls }), _jsxs(Button, { type: showStopButton ? 'button' : 'submit', size: "icon", className: "bg-primary text-primary-foreground size-8 cursor-pointer rounded-full hover:opacity-70 disabled:opacity-30", disabled: !canSubmit && !isResponding, onClick: handleButtonClick, children: [showStopButton ? (_jsx(Square, { className: "fill-primary-foreground size-4 animate-pulse" })) : (_jsx(ArrowUp, { className: "size-4" })), _jsx("span", { className: "sr-only", children: showStopButton
61
+ }, children: [_jsxs("span", { className: "bg-muted text-muted-foreground rounded px-1.5 py-0.5 font-mono text-xs", children: ["/", command.name] }), _jsx("span", { className: "min-w-0 text-xs", children: command.description })] }, command.id))) })), !compact && hasAttachments && (_jsx("div", { className: "flex max-h-32 gap-2 overflow-x-auto px-3 pt-3", children: imageAttachments.map((attachment) => (_jsxs("div", { className: "bg-muted/70 border-border flex max-w-48 shrink-0 items-center gap-2 rounded-xl border p-1.5 pr-2", children: [_jsx("div", { className: "bg-background flex size-9 shrink-0 items-center justify-center overflow-hidden rounded-lg border", children: attachment.previewUrl ? (_jsx("img", { src: attachment.previewUrl, alt: "", className: "size-full object-cover", draggable: false })) : (_jsx(ImageIcon, { className: "text-muted-foreground size-4" })) }), _jsx("span", { className: "min-w-0 truncate text-xs font-medium", title: attachment.path, children: attachment.name }), onRemoveImageAttachment && (_jsx("button", { type: "button", onClick: () => onRemoveImageAttachment(attachment.id), className: "text-muted-foreground hover:bg-accent hover:text-foreground flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-md transition-colors", "aria-label": `Remove ${attachment.name}`, children: _jsx(X, { className: "size-3.5" }) }))] }, attachment.id))) })), _jsx("div", { className: cn('w-full px-5', compact ? 'py-4' : 'pb-2 pt-4'), children: _jsx(Textarea, { ref: inputRef, value: input, placeholder: placeholder, onChange: onInputChange, onKeyDown: handleKeyDown, className: cn('max-h-32 min-h-[24px] resize-none rounded-none border-0 !bg-transparent p-0 text-sm shadow-none focus:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 dark:!bg-transparent', compact && 'max-h-6 min-h-6') }) }), !compact && (_jsxs("div", { className: "flex min-h-11 items-center justify-between gap-3 px-3 pb-3", children: [_jsx("div", { className: "flex min-w-0 shrink-0 items-center justify-start gap-1", children: footerStartControls }), _jsx("div", { className: "min-w-0 flex-1" }), _jsx("div", { className: "flex min-w-0 shrink-0 items-center justify-end gap-0.5", children: footerControls }), isResponding && canSubmit && (_jsxs(Button, { type: "button", variant: "secondary", size: "sm", className: "h-8 cursor-pointer gap-1.5 rounded-full px-2.5 text-xs", onClick: handleSteerClick, children: [_jsx(CornerDownRight, { className: "size-3.5" }), _jsx("span", { children: "Steer" })] })), _jsxs(Button, { type: showStopButton ? 'button' : 'submit', size: showQueueSubmitButton ? 'sm' : 'icon', className: cn('bg-primary text-primary-foreground h-8 cursor-pointer rounded-full hover:opacity-70 disabled:opacity-30', showQueueSubmitButton
62
+ ? 'gap-1.5 px-2.5 text-xs'
63
+ : 'size-8'), disabled: !canSubmit && !isResponding, onClick: handleButtonClick, children: [showStopButton ? (_jsx(Square, { className: "fill-primary-foreground size-4 animate-pulse" })) : showQueueSubmitButton ? (_jsxs(_Fragment, { children: [_jsx(ListPlus, { className: "size-3.5" }), _jsx("span", { children: "Queue" })] })) : (_jsx(ArrowUp, { className: "size-4" })), _jsx("span", { className: "sr-only", children: showStopButton
54
64
  ? 'Stop generating'
55
65
  : isResponding
56
- ? 'Steer response'
66
+ ? 'Queue follow-up'
57
67
  : 'Send message' })] })] }))] }) }) }) }));
58
68
  }
@@ -153,7 +153,7 @@ export function ChatPanel({ messages, status, isTurnInFlight, input, onInputChan
153
153
  }
154
154
  if (!input.trim() && imageAttachments.length === 0)
155
155
  return;
156
- const submitMode = mode ?? (isResponding ? 'steer' : 'send');
156
+ const submitMode = mode ?? (isResponding ? 'queue' : 'send');
157
157
  if (!isExpanded) {
158
158
  onExpandedChange(true);
159
159
  }
@@ -1 +1 @@
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"}
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;AA4TD,eAAO,MAAM,QAAQ,uFA5DlB,mBAAmB,6CAmErB,CAAA"}
@@ -66,9 +66,7 @@ const components = {
66
66
  img({ src, alt, className, style, width }) {
67
67
  if (!src || typeof src !== 'string')
68
68
  return null;
69
- const imageWidth = typeof width === 'number' || typeof width === 'string'
70
- ? width
71
- : undefined;
69
+ const imageWidth = typeof width === 'number' || typeof width === 'string' ? width : undefined;
72
70
  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
71
  },
74
72
  audio({ src, title }) {
@@ -1 +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"}
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,2CAsPtB"}
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useCallback, useMemo, useRef, useState } from 'react';
4
3
  import { FastForward, Pause, Play, Rewind, Volume2, VolumeX, } from 'lucide-react';
4
+ import { useCallback, useMemo, useRef, useState } from 'react';
5
5
  import { cn } from '../lib/utils';
6
6
  const PLAYBACK_RATES = [0.75, 1, 1.25, 1.5, 2];
7
7
  function formatTime(value) {
@@ -94,6 +94,12 @@ export function RichMediaPlayer({ className, kind, src, title, }) {
94
94
  media.playbackRate = nextRate;
95
95
  setPlaybackRate(nextRate);
96
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 })] }) }));
97
+ const controls = (_jsxs("div", { className: cn('flex flex-col gap-2 text-white', isAudio
98
+ ? 'p-3'
99
+ : 'bg-gradient-to-b from-transparent via-black/65 to-neutral-950 px-3 pb-3 pt-10'), 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: "hover:text-primary cursor-pointer rounded-full p-1 text-white transition-colors 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 focus:w-20 focus:opacity-100 group-hover/volume:w-20 group-hover/volume: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"] })] })] }));
100
+ 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
101
+ ? 'bg-neutral-950'
102
+ : 'absolute bottom-0 left-0 right-0 transition-transform duration-300', !isAudio &&
103
+ isPlaying &&
104
+ 'translate-y-[calc(100%-36px)] group-hover/mediaplayer:translate-y-0'), children: controls })] }) }));
99
105
  }
package/dist/index.d.ts CHANGED
@@ -1,13 +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, useMoldableAppCommands, isInMoldable, sendToMoldable, downloadFile, type AppCommand, type CommandAction, type CommandsResponse, type CommandMessage, type DownloadFileOptions, } from './lib/commands';
4
+ export { useMoldableCommands, useMoldableCommand, useMoldableAppCommands, useMoldableNavigationPop, useMoldableNavigationState, isInMoldable, sendToMoldable, pushMoldableNavigation, popMoldableNavigation, resetMoldableNavigation, downloadFile, type AppCommand, type CommandAction, type CommandsResponse, type CommandMessage, type MoldableNavigationEntry, type MoldableNavigationPopMessage, type MoldableNavigationStateMessage, type DownloadFileOptions, } from './lib/commands';
5
5
  export * from './components/ui';
6
+ export { TODAY_KIND_RANK, TODAY_KIND_TTL_MS, makeTodayItem, type TodayItem, type TodayItemKind, type TodaySurface, type TodayAction, type TodayResponse, type ResumeState, type HydratedTodayItem, type HydratedResume, } from './lib/today';
6
7
  export { useIsMobile } from './hooks/use-mobile';
7
8
  export { Markdown } from './components/markdown';
8
9
  export { RichMediaPlayer, type RichMediaPlayerProps, } from './components/rich-media-player';
9
10
  export { CodeBlock } from './components/code-block';
10
- export { WidgetLayout } from './components/widget-layout';
11
11
  export { AppErrorBoundary } from './components/app-error-boundary';
12
12
  export * from './components/chat';
13
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,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"}
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,wBAAwB,EACxB,0BAA0B,EAC1B,YAAY,EACZ,cAAc,EACd,sBAAsB,EACtB,qBAAqB,EACrB,uBAAuB,EACvB,YAAY,EACZ,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,uBAAuB,EAC5B,KAAK,4BAA4B,EACjC,KAAK,8BAA8B,EACnC,KAAK,mBAAmB,GACzB,MAAM,gBAAgB,CAAA;AAGvB,cAAc,iBAAiB,CAAA;AAG/B,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,aAAa,EACb,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,cAAc,GACpB,MAAM,aAAa,CAAA;AAGpB,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,gBAAgB,EAAE,MAAM,iCAAiC,CAAA;AAGlE,cAAc,mBAAmB,CAAA"}
package/dist/index.js CHANGED
@@ -5,9 +5,11 @@ 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, useMoldableAppCommands, isInMoldable, sendToMoldable, downloadFile, } from './lib/commands';
8
+ export { useMoldableCommands, useMoldableCommand, useMoldableAppCommands, useMoldableNavigationPop, useMoldableNavigationState, isInMoldable, sendToMoldable, pushMoldableNavigation, popMoldableNavigation, resetMoldableNavigation, downloadFile, } from './lib/commands';
9
9
  // Export UI components
10
10
  export * from './components/ui';
11
+ // Export Today contract
12
+ export { TODAY_KIND_RANK, TODAY_KIND_TTL_MS, makeTodayItem, } from './lib/today';
11
13
  // Export hooks
12
14
  export { useIsMobile } from './hooks/use-mobile';
13
15
  // Export Markdown
@@ -15,8 +17,6 @@ export { Markdown } from './components/markdown';
15
17
  export { RichMediaPlayer, } from './components/rich-media-player';
16
18
  // Export CodeBlock
17
19
  export { CodeBlock } from './components/code-block';
18
- // Export WidgetLayout
19
- export { WidgetLayout } from './components/widget-layout';
20
20
  // Export app error boundary
21
21
  export { AppErrorBoundary } from './components/app-error-boundary';
22
22
  // Export chat components
@@ -58,6 +58,23 @@ export interface CommandMessage {
58
58
  command: string;
59
59
  payload?: unknown;
60
60
  }
61
+ export interface MoldableNavigationEntry {
62
+ id: string;
63
+ title?: string;
64
+ data?: unknown;
65
+ }
66
+ export interface MoldableNavigationPopMessage {
67
+ type: 'moldable:navigation-pop';
68
+ entry?: MoldableNavigationEntry;
69
+ depth: number;
70
+ }
71
+ export interface MoldableNavigationStateMessage {
72
+ type: 'moldable:navigation-state';
73
+ depth: number;
74
+ canGoBack: boolean;
75
+ entries: MoldableNavigationEntry[];
76
+ }
77
+ export type MoldableNavigationPopHandler = (message: MoldableNavigationPopMessage) => void;
61
78
  /**
62
79
  * Hook for apps to handle commands from the desktop
63
80
  *
@@ -83,6 +100,35 @@ export declare function useMoldableAppCommands(appId: string, commands: AppComma
83
100
  * Useful when you want to register handlers in different components
84
101
  */
85
102
  export declare function useMoldableCommand(commandId: string, handler: (payload?: unknown) => void): void;
103
+ /**
104
+ * Push a view onto Moldable's desktop-level navigation stack.
105
+ *
106
+ * Apps should call this when moving from a root view into a nested view. The
107
+ * desktop header back button will pop this stack before returning home.
108
+ */
109
+ export declare function pushMoldableNavigation(entry?: Omit<MoldableNavigationEntry, 'id'> & {
110
+ id?: string;
111
+ }): string;
112
+ /**
113
+ * Pop one view from Moldable's desktop-level navigation stack.
114
+ *
115
+ * Use this when the app's own in-view back control has already returned to the
116
+ * previous view, so the desktop header stays in sync.
117
+ */
118
+ export declare function popMoldableNavigation(entryId?: string): void;
119
+ /**
120
+ * Clear Moldable's desktop-level navigation stack for the current app.
121
+ */
122
+ export declare function resetMoldableNavigation(): void;
123
+ /**
124
+ * Listen for the desktop header back button requesting that the app pop one
125
+ * nested view. The desktop has already popped its stack when this fires.
126
+ */
127
+ export declare function useMoldableNavigationPop(handler: MoldableNavigationPopHandler): void;
128
+ /**
129
+ * Listen for stack state updates from Moldable desktop.
130
+ */
131
+ export declare function useMoldableNavigationState(handler: (message: MoldableNavigationStateMessage) => void): void;
86
132
  /**
87
133
  * Utility to check if running inside Moldable desktop (iframe)
88
134
  */
@@ -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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"}
@@ -70,6 +70,76 @@ export function useMoldableCommand(commandId, handler) {
70
70
  return () => window.removeEventListener('message', handleMessage);
71
71
  }, [commandId]);
72
72
  }
73
+ /**
74
+ * Push a view onto Moldable's desktop-level navigation stack.
75
+ *
76
+ * Apps should call this when moving from a root view into a nested view. The
77
+ * desktop header back button will pop this stack before returning home.
78
+ */
79
+ export function pushMoldableNavigation(entry = {}) {
80
+ const id = entry.id ?? makeRequestId('nav');
81
+ sendToMoldable({
82
+ type: 'moldable:navigation-push',
83
+ entry: {
84
+ ...entry,
85
+ id,
86
+ },
87
+ });
88
+ return id;
89
+ }
90
+ /**
91
+ * Pop one view from Moldable's desktop-level navigation stack.
92
+ *
93
+ * Use this when the app's own in-view back control has already returned to the
94
+ * previous view, so the desktop header stays in sync.
95
+ */
96
+ export function popMoldableNavigation(entryId) {
97
+ sendToMoldable({
98
+ type: 'moldable:navigation-pop',
99
+ entryId,
100
+ });
101
+ }
102
+ /**
103
+ * Clear Moldable's desktop-level navigation stack for the current app.
104
+ */
105
+ export function resetMoldableNavigation() {
106
+ sendToMoldable({
107
+ type: 'moldable:navigation-reset',
108
+ });
109
+ }
110
+ /**
111
+ * Listen for the desktop header back button requesting that the app pop one
112
+ * nested view. The desktop has already popped its stack when this fires.
113
+ */
114
+ export function useMoldableNavigationPop(handler) {
115
+ const handlerRef = useRef(handler);
116
+ handlerRef.current = handler;
117
+ useEffect(() => {
118
+ const handleMessage = (event) => {
119
+ if (event.data?.type !== 'moldable:navigation-pop')
120
+ return;
121
+ handlerRef.current(event.data);
122
+ };
123
+ window.addEventListener('message', handleMessage);
124
+ return () => window.removeEventListener('message', handleMessage);
125
+ }, []);
126
+ }
127
+ /**
128
+ * Listen for stack state updates from Moldable desktop.
129
+ */
130
+ export function useMoldableNavigationState(handler) {
131
+ const handlerRef = useRef(handler);
132
+ handlerRef.current = handler;
133
+ useEffect(() => {
134
+ const handleMessage = (event) => {
135
+ if (event.data?.type !== 'moldable:navigation-state')
136
+ return;
137
+ handlerRef.current(event.data);
138
+ };
139
+ window.addEventListener('message', handleMessage);
140
+ return () => window.removeEventListener('message', handleMessage);
141
+ }, []);
142
+ }
73
143
  /**
74
144
  * Utility to check if running inside Moldable desktop (iframe)
75
145
  */
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Today contract — the shared shape apps use to contribute to the Moldable
3
+ * "Today" home view, and the host uses to aggregate + triage them.
4
+ *
5
+ * See prds/today-view.prd.md. The guiding rule: silence is the default. An app
6
+ * contributes an item ONLY when something earns attention (a resume point, an
7
+ * in-flight job, a blocker, a time-sensitive nudge). It does not mirror an
8
+ * always-on overview.
9
+ */
10
+ /**
11
+ * Why a contribution earns attention. Drives ranking, styling, and default decay.
12
+ * (prds/today-view.prd.md §3)
13
+ */
14
+ export type TodayItemKind = 'resume' | 'active' | 'blocked' | 'timely' | 'threshold' | 'milestone' | 'agent-activity';
15
+ /** How an item renders. Most items are `text`; `widget` is the rare live case. */
16
+ export type TodaySurface = 'text' | 'nudge' | 'metric' | 'widget';
17
+ /**
18
+ * An action a user can take directly from a Today card. Reuses the app-command
19
+ * vocabulary (navigate/message) plus Today-specific affordances.
20
+ */
21
+ export type TodayAction =
22
+ /** Open the contributing app (optionally deep-linked to a route/entity). */
23
+ {
24
+ type: 'open-app';
25
+ label: string;
26
+ deepLink?: string;
27
+ }
28
+ /** Call one of the app's own `/api/moldable/rpc` methods in place. */
29
+ | {
30
+ type: 'rpc';
31
+ label: string;
32
+ method: string;
33
+ params?: unknown;
34
+ /** If set, confirm with the user before running (destructive actions). */
35
+ confirm?: string;
36
+ /** Optional toast/label shown on success. */
37
+ successLabel?: string;
38
+ }
39
+ /** Drop a prompt into the unified chat to follow up / interrogate. */
40
+ | {
41
+ type: 'message';
42
+ label: string;
43
+ prompt: string;
44
+ }
45
+ /** Navigate the app to a specific path (app-command `navigate` semantics). */
46
+ | {
47
+ type: 'navigate';
48
+ label: string;
49
+ path: string;
50
+ }
51
+ /** Dismiss the item (host-handled; honors `cadence`). */
52
+ | {
53
+ type: 'dismiss';
54
+ label?: string;
55
+ };
56
+ /**
57
+ * A single Today contribution. Apps return zero or more of these from
58
+ * `GET /api/moldable/today`. `appId` is attached by the host (it knows which
59
+ * app it queried), so apps never set it.
60
+ */
61
+ export interface TodayItem {
62
+ /**
63
+ * Stable id for this *logical* item (e.g. `resume:lesson:hanon` or
64
+ * `timer:active`). Used for dedupe + persisting dismissals across refreshes.
65
+ */
66
+ id: string;
67
+ kind: TodayItemKind;
68
+ /** Defaults to `text`. */
69
+ surface?: TodaySurface;
70
+ /** One scannable line. ("Resume Lesson 2 of 4: Hanon Hand Agility") */
71
+ title: string;
72
+ /** Optional second line of context. */
73
+ subtitle?: string;
74
+ /** Emoji or lucide icon name; falls back to the app icon. */
75
+ icon?: string;
76
+ /** Optional accent color (CSS value) for emphasis. */
77
+ accentColor?: string;
78
+ /** Ordered actions; the first is treated as primary. */
79
+ actions?: TodayAction[];
80
+ /**
81
+ * For `surface: 'widget'` — a temporary, actionable embed summoned only for
82
+ * this item. `path` is served by the app.
83
+ */
84
+ widget?: {
85
+ path: string;
86
+ height?: number;
87
+ };
88
+ /** App hint, 0–100. The host has final say over ordering (PRD §6). */
89
+ priority?: number;
90
+ /** ISO timestamp; defaults to now when omitted. */
91
+ createdAt?: string;
92
+ /** ISO timestamp; host applies a per-kind default when omitted. */
93
+ expiresAt?: string;
94
+ /** Defaults to true. */
95
+ dismissible?: boolean;
96
+ /** Anti-repeat policy. `once` = don't resurface same day after dismiss. */
97
+ cadence?: 'once' | 'daily' | 'while-true';
98
+ /** Optional grouping label the agent/host may use to cluster items. */
99
+ groupHint?: string;
100
+ }
101
+ /**
102
+ * The most recent "pick up where you left off" point for an app. Powers the
103
+ * Resume rail. Distinct from a `resume`-kind item so the host can render it as
104
+ * a first-class, always-available rail rather than an attention card.
105
+ */
106
+ export interface ResumeState {
107
+ title: string;
108
+ subtitle?: string;
109
+ icon?: string;
110
+ /** Where opening the app should land. */
111
+ deepLink?: string;
112
+ /** ISO timestamp of last interaction; used to rank the rail by recency. */
113
+ lastTouchedAt?: string;
114
+ }
115
+ /** Response shape for `GET /api/moldable/today`. */
116
+ export interface TodayResponse {
117
+ items: TodayItem[];
118
+ resume?: ResumeState | null;
119
+ /** ISO timestamp the app generated this; host may use for caching. */
120
+ generatedAt?: string;
121
+ }
122
+ /** Host-side: a TodayItem enriched with which app produced it. */
123
+ export interface HydratedTodayItem extends TodayItem {
124
+ appId: string;
125
+ appName: string;
126
+ appIcon?: string;
127
+ appIconPath?: string;
128
+ }
129
+ /** Host-side resume entry (ResumeState + provenance). */
130
+ export interface HydratedResume extends ResumeState {
131
+ appId: string;
132
+ appName: string;
133
+ appIcon?: string;
134
+ appIconPath?: string;
135
+ }
136
+ /**
137
+ * Kind → ranking weight (higher surfaces first). The host's deterministic
138
+ * triage baseline; the Today agent may re-rank, but this is the floor.
139
+ * (prds/today-view.prd.md §6)
140
+ */
141
+ export declare const TODAY_KIND_RANK: Record<TodayItemKind, number>;
142
+ /** Kind → default time-to-live (ms) when an item omits `expiresAt`. */
143
+ export declare const TODAY_KIND_TTL_MS: Record<TodayItemKind, number>;
144
+ /**
145
+ * Normalize a partial item into a complete TodayItem with sensible defaults.
146
+ * Apps building items by hand can use this, or just return literal JSON — the
147
+ * host applies the same defaults defensively (see `normalizeTodayItem`).
148
+ */
149
+ export declare function makeTodayItem(item: Omit<TodayItem, 'createdAt' | 'surface' | 'dismissible'> & Partial<Pick<TodayItem, 'createdAt' | 'surface' | 'dismissible'>>): TodayItem;
150
+ //# sourceMappingURL=today.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"today.d.ts","sourceRoot":"","sources":["../../src/lib/today.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Today contract — the shared shape apps use to contribute to the Moldable
3
+ * "Today" home view, and the host uses to aggregate + triage them.
4
+ *
5
+ * See prds/today-view.prd.md. The guiding rule: silence is the default. An app
6
+ * contributes an item ONLY when something earns attention (a resume point, an
7
+ * in-flight job, a blocker, a time-sensitive nudge). It does not mirror an
8
+ * always-on overview.
9
+ */
10
+ /**
11
+ * Kind → ranking weight (higher surfaces first). The host's deterministic
12
+ * triage baseline; the Today agent may re-rank, but this is the floor.
13
+ * (prds/today-view.prd.md §6)
14
+ */
15
+ export const TODAY_KIND_RANK = {
16
+ blocked: 100,
17
+ timely: 90,
18
+ active: 80,
19
+ resume: 70,
20
+ threshold: 60,
21
+ 'agent-activity': 40,
22
+ milestone: 20,
23
+ };
24
+ /** Kind → default time-to-live (ms) when an item omits `expiresAt`. */
25
+ export const TODAY_KIND_TTL_MS = {
26
+ blocked: 24 * 60 * 60 * 1000,
27
+ timely: 6 * 60 * 60 * 1000,
28
+ active: 2 * 60 * 60 * 1000,
29
+ resume: 14 * 24 * 60 * 60 * 1000,
30
+ threshold: 24 * 60 * 60 * 1000,
31
+ 'agent-activity': 48 * 60 * 60 * 1000,
32
+ milestone: 24 * 60 * 60 * 1000,
33
+ };
34
+ /**
35
+ * Normalize a partial item into a complete TodayItem with sensible defaults.
36
+ * Apps building items by hand can use this, or just return literal JSON — the
37
+ * host applies the same defaults defensively (see `normalizeTodayItem`).
38
+ */
39
+ export function makeTodayItem(item) {
40
+ const createdAt = item.createdAt ?? new Date().toISOString();
41
+ const expiresAt = item.expiresAt ??
42
+ new Date(Date.parse(createdAt) + TODAY_KIND_TTL_MS[item.kind]).toISOString();
43
+ return {
44
+ surface: 'text',
45
+ dismissible: true,
46
+ ...item,
47
+ createdAt,
48
+ expiresAt,
49
+ };
50
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moldable-ai/ui",
3
- "version": "0.2.10",
3
+ "version": "0.2.13",
4
4
  "description": "Shared UI components for Moldable applications",
5
5
  "author": "Desiderata LLC",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -101,7 +101,7 @@
101
101
  --card-foreground: oklch(0.141 0.005 285.823);
102
102
  --popover: oklch(1 0 0);
103
103
  --popover-foreground: oklch(0.141 0.005 285.823);
104
- --primary: oklch(0.7145 0.185 45.03);
104
+ --primary: oklch(0.205 0 0);
105
105
  --primary-foreground: oklch(0.985 0 0);
106
106
  --secondary: oklch(0.967 0.001 286.375);
107
107
  --secondary-foreground: oklch(0.21 0.006 285.885);
@@ -154,8 +154,8 @@
154
154
  --card-foreground: oklch(0.985 0 0);
155
155
  --popover: oklch(0.269 0.0018 286.28);
156
156
  --popover-foreground: oklch(0.985 0 0);
157
- --primary: oklch(0.7145 0.185 45.03);
158
- --primary-foreground: oklch(0.985 0 0);
157
+ --primary: oklch(0.985 0 0);
158
+ --primary-foreground: oklch(0.205 0 0);
159
159
  --secondary: oklch(0.3375 0.0035 286.21);
160
160
  --secondary-foreground: oklch(0.985 0 0);
161
161
  --muted: oklch(0.3375 0.0035 286.21);