@page-speed/agent-everywhere 0.4.0 → 0.6.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.
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { createContext, forwardRef, useRef, useImperativeHandle, useCallback, us
6
6
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
7
  import { AnimatePresence, motion } from 'motion/react';
8
8
  import { Markdown } from '@page-speed/markdown-to-jsx';
9
- import { SendIcon, PaperclipIcon, FileTextIcon, MicIcon, ImageIcon, XIcon, SearchIcon, UploadIcon, FileIcon, UserCircleIcon, CheckIcon, SparklesIcon, BookmarkIcon, InfoIcon, CheckCircle2Icon, AlertTriangleIcon, LightbulbIcon, BrainIcon, ChevronDownIcon, ZapIcon, CopyIcon, ThumbsUpIcon, ThumbsDownIcon, BotIcon, UserIcon, DownloadIcon, MinusIcon, TrendingDownIcon, TrendingUpIcon, ChevronUpIcon, ArrowUpDownIcon, LinkIcon, PlayIcon, ExternalLinkIcon, WandIcon, LayoutGridIcon, Grid3X3Icon, ArrowUpRightIcon, ShuffleIcon, SkipForwardIcon, ChevronLeftIcon, ChevronRightIcon, XCircleIcon, HelpCircleIcon, Minimize2Icon, Maximize2Icon, RotateCcwIcon, MessageSquareIcon, GripVerticalIcon, PanelLeftOpenIcon, PanelLeftCloseIcon, CheckCircleIcon, AlertCircleIcon, MoreHorizontalIcon, ClipboardListIcon, ArrowLeftIcon, MoreVerticalIcon, Loader2Icon, ArrowRightIcon, AlignLeftIcon, ListIcon, HashIcon, TypeIcon, ClockIcon, CoinsIcon, ActivityIcon, ArrowUpIcon, ArrowDownIcon } from 'lucide-react';
9
+ import { SendIcon, PaperclipIcon, FileTextIcon, MicIcon, ImageIcon, XIcon, SearchIcon, UploadIcon, FileIcon, UserCircleIcon, CheckIcon, SparklesIcon, BookmarkIcon, InfoIcon, CheckCircle2Icon, AlertTriangleIcon, LightbulbIcon, BrainIcon, ChevronDownIcon, ZapIcon, CopyIcon, ThumbsUpIcon, ThumbsDownIcon, BotIcon, UserIcon, DownloadIcon, MinusIcon, TrendingDownIcon, TrendingUpIcon, ChevronUpIcon, ArrowUpDownIcon, LinkIcon, PlayIcon, ExternalLinkIcon, WandIcon, LayoutGridIcon, Grid3X3Icon, ArrowUpRightIcon, ShuffleIcon, SkipForwardIcon, ChevronLeftIcon, ChevronRightIcon, XCircleIcon, HelpCircleIcon, Minimize2Icon, Maximize2Icon, RotateCcwIcon, MessageSquareIcon, GripVerticalIcon, PanelLeftOpenIcon, PanelLeftCloseIcon, CheckCircleIcon, AlertCircleIcon, MoreHorizontalIcon, ClipboardListIcon, ArrowLeftIcon, MoreVerticalIcon, Loader2, ArrowUp, PanelLeft, PanelRight, Sparkles, RotateCcw, ChevronDown, MessageSquare, Paperclip, Loader2Icon, ArrowRightIcon, AlignLeftIcon, ListIcon, HashIcon, TypeIcon, ClockIcon, CoinsIcon, ActivityIcon, ArrowUpIcon, ArrowDownIcon } from 'lucide-react';
10
10
  import { Slot } from '@radix-ui/react-slot';
11
11
  import { cva } from 'class-variance-authority';
12
12
  import * as AvatarPrimitive from '@radix-ui/react-avatar';
@@ -7196,6 +7196,7 @@ function useSemanticBuilder({
7196
7196
  onGeneratedBlocks,
7197
7197
  onUndoRequest,
7198
7198
  buildWelcomeMessage,
7199
+ seedWelcomeMessage = true,
7199
7200
  webSocketImpl
7200
7201
  }) {
7201
7202
  const [messages, setMessages] = useState([]);
@@ -7229,6 +7230,7 @@ function useSemanticBuilder({
7229
7230
  resolveSocketUrlRef.current = resolveSocketUrl;
7230
7231
  }, [resolveSocketUrl]);
7231
7232
  useEffect(() => {
7233
+ if (!seedWelcomeMessage) return;
7232
7234
  const hasContentBrief = !!contentBrief;
7233
7235
  const content = buildWelcomeMessage?.({ pageName, hasContentBrief }) ?? (hasContentBrief ? `Connected to ${pageName}. The page brief is loaded and ready. Describe the page you want and I'll stream back layout guidance and design reasoning.` : `Connected to ${pageName}. There is no saved content brief yet, so I'll work from the current page structure and brand context.`);
7234
7236
  setMessages([
@@ -7239,7 +7241,7 @@ function useSemanticBuilder({
7239
7241
  createdAt: Date.now()
7240
7242
  }
7241
7243
  ]);
7242
- }, [contentBrief, pageName, pageSlug]);
7244
+ }, [contentBrief, pageName, pageSlug, seedWelcomeMessage]);
7243
7245
  const handleEnvelope = useCallback((envelope) => {
7244
7246
  switch (envelope.type) {
7245
7247
  case "connection_ready":
@@ -7451,6 +7453,14 @@ function useSemanticBuilder({
7451
7453
  const retry = useCallback(() => {
7452
7454
  clientRef.current?.retry();
7453
7455
  }, []);
7456
+ const reset = useCallback(() => {
7457
+ pendingHiddenRequestsRef.current = 0;
7458
+ hiddenAssistantMessageIdsRef.current.clear();
7459
+ clientRef.current?.close();
7460
+ setConnectionState("idle");
7461
+ setConnectionError(null);
7462
+ setMessages([]);
7463
+ }, []);
7454
7464
  const isConnected = connectionState === "ready" || connectionState === "streaming";
7455
7465
  const isStreaming = connectionState === "streaming";
7456
7466
  const statusLabel = useMemo(() => {
@@ -7477,7 +7487,8 @@ function useSemanticBuilder({
7477
7487
  isConnected,
7478
7488
  isStreaming,
7479
7489
  sendMessage,
7480
- retry
7490
+ retry,
7491
+ reset
7481
7492
  };
7482
7493
  }
7483
7494
  var ScrollArea = React4.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
@@ -7981,6 +7992,54 @@ function FullBleedSurface({
7981
7992
  }
7982
7993
  );
7983
7994
  }
7995
+ function NativeSurface({
7996
+ children,
7997
+ input,
7998
+ title,
7999
+ subtitle,
8000
+ icon,
8001
+ headerActions,
8002
+ suggestions,
8003
+ footer,
8004
+ isLoading = false,
8005
+ autoScroll = true,
8006
+ className,
8007
+ contentClassName
8008
+ }) {
8009
+ const scrollRef = useRef(null);
8010
+ useEffect(() => {
8011
+ if (autoScroll && scrollRef.current) {
8012
+ scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
8013
+ }
8014
+ }, [children, isLoading, autoScroll]);
8015
+ const showHeader = !!title || !!headerActions || !!icon;
8016
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex h-full min-h-0 flex-col", className), children: [
8017
+ showHeader && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b px-4 py-3", children: [
8018
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
8019
+ icon,
8020
+ (title || subtitle) && /* @__PURE__ */ jsxs("div", { children: [
8021
+ title && /* @__PURE__ */ jsx("span", { className: "font-medium text-sm", children: title }),
8022
+ subtitle && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-xs", children: subtitle })
8023
+ ] })
8024
+ ] }),
8025
+ headerActions && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: headerActions })
8026
+ ] }),
8027
+ /* @__PURE__ */ jsx(
8028
+ "div",
8029
+ {
8030
+ ref: scrollRef,
8031
+ className: cn("min-h-0 flex-1 overflow-y-auto", contentClassName),
8032
+ children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 p-4", children: [
8033
+ children,
8034
+ isLoading && /* @__PURE__ */ jsx(TypingIndicator, {})
8035
+ ] })
8036
+ }
8037
+ ),
8038
+ suggestions && /* @__PURE__ */ jsx("div", { className: "border-t px-4 py-2", children: suggestions }),
8039
+ input,
8040
+ footer && /* @__PURE__ */ jsx("div", { className: "border-t px-4 py-1.5", children: footer })
8041
+ ] });
8042
+ }
7984
8043
  function AgentSurface({
7985
8044
  mode,
7986
8045
  messages,
@@ -8104,6 +8163,21 @@ function AgentSurface({
8104
8163
  children: conversation
8105
8164
  }
8106
8165
  );
8166
+ case "native":
8167
+ return /* @__PURE__ */ jsx(
8168
+ NativeSurface,
8169
+ {
8170
+ title,
8171
+ subtitle,
8172
+ icon,
8173
+ headerActions,
8174
+ input: resolvedInput,
8175
+ suggestions,
8176
+ isLoading,
8177
+ className,
8178
+ children: conversation
8179
+ }
8180
+ );
8107
8181
  case "panel":
8108
8182
  default:
8109
8183
  return /* @__PURE__ */ jsx(
@@ -8122,7 +8196,768 @@ function AgentSurface({
8122
8196
  );
8123
8197
  }
8124
8198
  }
8199
+ var NativeAgentContext = createContext(
8200
+ null
8201
+ );
8202
+ function useNativeAgent() {
8203
+ const ctx = useContext(NativeAgentContext);
8204
+ if (!ctx) {
8205
+ throw new Error(
8206
+ "useNativeAgent must be used within a <NativeAgentProvider>."
8207
+ );
8208
+ }
8209
+ return ctx;
8210
+ }
8211
+ function useNativeAgentOptional() {
8212
+ return useContext(NativeAgentContext);
8213
+ }
8214
+ var EMPTY_BLOCKS = [];
8215
+ var REASONING_STATUS = {
8216
+ active: "active",
8217
+ complete: "complete"
8218
+ };
8219
+ function toAgentMessage(message) {
8220
+ const reasoning = message.thinking ? [
8221
+ {
8222
+ id: `${message.id}__thinking`,
8223
+ label: "Thinking",
8224
+ content: message.thinking,
8225
+ status: message.streaming ? REASONING_STATUS.active : REASONING_STATUS.complete
8226
+ }
8227
+ ] : void 0;
8228
+ return {
8229
+ id: message.id,
8230
+ role: message.role,
8231
+ content: message.content,
8232
+ timestamp: new Date(message.createdAt),
8233
+ reasoning
8234
+ };
8235
+ }
8236
+ function deriveStatus(connectionState) {
8237
+ switch (connectionState) {
8238
+ case "streaming":
8239
+ return "busy";
8240
+ case "ready":
8241
+ return "online";
8242
+ case "connecting":
8243
+ return "away";
8244
+ default:
8245
+ return "offline";
8246
+ }
8247
+ }
8248
+ function NativeAgentProvider({
8249
+ children,
8250
+ socketUrl,
8251
+ resolveSocketUrl,
8252
+ websiteId,
8253
+ pageCategoryId,
8254
+ pageName = "",
8255
+ pageSlug,
8256
+ blocks,
8257
+ contentBrief,
8258
+ onGeneratedBlocks,
8259
+ webSocketImpl,
8260
+ connectionStrategy = "lazy",
8261
+ seedWelcomeMessage = false,
8262
+ onError,
8263
+ onActivate
8264
+ }) {
8265
+ const [enabled, setEnabled] = useState(connectionStrategy === "eager");
8266
+ const [isActive, setIsActive] = useState(false);
8267
+ const [input, setInput] = useState("");
8268
+ const [pendingCount, setPendingCount] = useState(0);
8269
+ const [optimistic, setOptimistic] = useState([]);
8270
+ const blocksValue = blocks ?? EMPTY_BLOCKS;
8271
+ const {
8272
+ messages: builderMessages,
8273
+ connectionState,
8274
+ connectionError,
8275
+ statusLabel,
8276
+ isConnected,
8277
+ isStreaming,
8278
+ sendMessage,
8279
+ retry,
8280
+ reset: resetSession
8281
+ } = useSemanticBuilder({
8282
+ socketUrl,
8283
+ resolveSocketUrl,
8284
+ websiteId,
8285
+ pageCategoryId,
8286
+ pageName,
8287
+ pageSlug,
8288
+ blocks: blocksValue,
8289
+ contentBrief,
8290
+ enabled,
8291
+ onGeneratedBlocks,
8292
+ webSocketImpl,
8293
+ seedWelcomeMessage
8294
+ });
8295
+ const pendingPromptsRef = useRef([]);
8296
+ const optimisticSeqRef = useRef(0);
8297
+ const onActivateRef = useRef(onActivate);
8298
+ const onErrorRef = useRef(onError);
8299
+ useEffect(() => {
8300
+ onActivateRef.current = onActivate;
8301
+ }, [onActivate]);
8302
+ useEffect(() => {
8303
+ onErrorRef.current = onError;
8304
+ }, [onError]);
8305
+ useEffect(() => {
8306
+ if (connectionError) onErrorRef.current?.(connectionError);
8307
+ }, [connectionError]);
8308
+ const markActive = useCallback(() => {
8309
+ setIsActive((prev) => {
8310
+ if (!prev) onActivateRef.current?.();
8311
+ return true;
8312
+ });
8313
+ }, []);
8314
+ useEffect(() => {
8315
+ if (connectionState !== "ready" || pendingPromptsRef.current.length === 0) {
8316
+ return;
8317
+ }
8318
+ const next = pendingPromptsRef.current.shift();
8319
+ if (next === void 0) return;
8320
+ setPendingCount(pendingPromptsRef.current.length);
8321
+ setOptimistic((prev) => prev.slice(1));
8322
+ void sendMessage(next).then((ok) => {
8323
+ if (!ok) {
8324
+ pendingPromptsRef.current.unshift(next);
8325
+ setPendingCount(pendingPromptsRef.current.length);
8326
+ }
8327
+ });
8328
+ }, [connectionState, pendingCount, sendMessage]);
8329
+ const submit = useCallback(
8330
+ (content) => {
8331
+ const text = (content ?? input).trim();
8332
+ if (!text) return;
8333
+ markActive();
8334
+ setEnabled(true);
8335
+ setInput("");
8336
+ pendingPromptsRef.current.push(text);
8337
+ setPendingCount(pendingPromptsRef.current.length);
8338
+ const id = `native-pending-${optimisticSeqRef.current}`;
8339
+ optimisticSeqRef.current += 1;
8340
+ setOptimistic((prev) => [
8341
+ ...prev,
8342
+ { id, role: "user", content: text, timestamp: /* @__PURE__ */ new Date() }
8343
+ ]);
8344
+ },
8345
+ [input, markActive]
8346
+ );
8347
+ const activate = useCallback(() => {
8348
+ markActive();
8349
+ setEnabled(true);
8350
+ }, [markActive]);
8351
+ const reset = useCallback(() => {
8352
+ pendingPromptsRef.current = [];
8353
+ setPendingCount(0);
8354
+ setOptimistic([]);
8355
+ setIsActive(false);
8356
+ setInput("");
8357
+ setEnabled(false);
8358
+ resetSession();
8359
+ }, [resetSession]);
8360
+ const messages = useMemo(
8361
+ () => [...builderMessages.map(toAgentMessage), ...optimistic],
8362
+ [builderMessages, optimistic]
8363
+ );
8364
+ const isResponding = isStreaming || pendingCount > 0 && !connectionError && connectionState !== "error" && connectionState !== "requires_shared_domain";
8365
+ const status = deriveStatus(connectionState);
8366
+ const value = useMemo(
8367
+ () => ({
8368
+ messages,
8369
+ isActive,
8370
+ isConnected,
8371
+ isResponding,
8372
+ isStreaming,
8373
+ connectionState,
8374
+ statusLabel,
8375
+ status,
8376
+ error: connectionError,
8377
+ input,
8378
+ setInput,
8379
+ submit,
8380
+ activate,
8381
+ reset,
8382
+ retry
8383
+ }),
8384
+ [
8385
+ messages,
8386
+ isActive,
8387
+ isConnected,
8388
+ isResponding,
8389
+ isStreaming,
8390
+ connectionState,
8391
+ statusLabel,
8392
+ status,
8393
+ connectionError,
8394
+ input,
8395
+ submit,
8396
+ activate,
8397
+ reset,
8398
+ retry
8399
+ ]
8400
+ );
8401
+ return /* @__PURE__ */ jsx(NativeAgentContext.Provider, { value, children });
8402
+ }
8403
+ var noop = () => {
8404
+ };
8405
+ function warnDev(message) {
8406
+ const env = globalThis.process?.env;
8407
+ if (env && env.NODE_ENV !== "production") {
8408
+ console.warn(`[AgentComposer] ${message}`);
8409
+ }
8410
+ }
8411
+ function AgentComposer({
8412
+ value: valueProp,
8413
+ onChange,
8414
+ onSubmit,
8415
+ loading: loadingProp,
8416
+ disabled: disabledProp,
8417
+ placeholder = "Ask anything\u2026",
8418
+ variant = "default",
8419
+ bare = false,
8420
+ suggestions,
8421
+ footer,
8422
+ leftActions,
8423
+ rightActions,
8424
+ autoFocus = false,
8425
+ className,
8426
+ inputClassName
8427
+ }) {
8428
+ const ctx = useNativeAgentOptional();
8429
+ const [draft, setDraft] = useState("");
8430
+ const propsControlEditing = valueProp !== void 0 || onChange !== void 0;
8431
+ const value = propsControlEditing ? valueProp ?? "" : ctx ? ctx.input : draft;
8432
+ const handleChange = propsControlEditing ? onChange ?? noop : ctx ? ctx.setInput : setDraft;
8433
+ const loading = loadingProp ?? ctx?.isResponding ?? false;
8434
+ const disabled = disabledProp ?? false;
8435
+ const handleSubmit = useCallback(() => {
8436
+ if (onSubmit) {
8437
+ onSubmit(value);
8438
+ return;
8439
+ }
8440
+ if (!propsControlEditing && ctx) {
8441
+ ctx.submit();
8442
+ return;
8443
+ }
8444
+ warnDev(
8445
+ "submit was ignored: provide `onSubmit`, or render inside a <NativeAgentProvider>."
8446
+ );
8447
+ }, [onSubmit, propsControlEditing, ctx, value]);
8448
+ return /* @__PURE__ */ jsxs(
8449
+ "div",
8450
+ {
8451
+ className: cn(
8452
+ !bare && "rounded-2xl border bg-background shadow-sm",
8453
+ className
8454
+ ),
8455
+ children: [
8456
+ suggestions && /* @__PURE__ */ jsx("div", { className: cn("px-3", bare ? "pb-2" : "pt-3"), children: suggestions }),
8457
+ /* @__PURE__ */ jsx(
8458
+ PromptInput,
8459
+ {
8460
+ value,
8461
+ onChange: handleChange,
8462
+ onSubmit: handleSubmit,
8463
+ placeholder,
8464
+ loading,
8465
+ disabled,
8466
+ variant,
8467
+ autoFocus,
8468
+ leftActions,
8469
+ rightActions,
8470
+ inputClassName
8471
+ }
8472
+ ),
8473
+ footer && /* @__PURE__ */ jsx("div", { className: "px-4 pb-2 text-muted-foreground text-xs", children: footer })
8474
+ ]
8475
+ }
8476
+ );
8477
+ }
8478
+ var EMPTY_MESSAGES = [];
8479
+ function AgentConversation({
8480
+ messages: messagesProp,
8481
+ isLoading: isLoadingProp,
8482
+ header,
8483
+ emptyState,
8484
+ showAvatars = true,
8485
+ renderMessage,
8486
+ onFeedback,
8487
+ onConfirmAction,
8488
+ autoScroll = true,
8489
+ className,
8490
+ contentClassName
8491
+ }) {
8492
+ const ctx = useNativeAgentOptional();
8493
+ const messages = useMemo(
8494
+ () => messagesProp ?? ctx?.messages ?? EMPTY_MESSAGES,
8495
+ [messagesProp, ctx?.messages]
8496
+ );
8497
+ const isLoading = isLoadingProp ?? ctx?.isResponding ?? false;
8498
+ const scrollRef = useRef(null);
8499
+ useEffect(() => {
8500
+ if (autoScroll && scrollRef.current) {
8501
+ scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
8502
+ }
8503
+ }, [messages, isLoading, autoScroll]);
8504
+ const showEmptyState = messages.length === 0 && !isLoading && !!emptyState;
8505
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex h-full min-h-0 flex-col", className), children: [
8506
+ header,
8507
+ /* @__PURE__ */ jsx(
8508
+ "div",
8509
+ {
8510
+ ref: scrollRef,
8511
+ className: cn("min-h-0 flex-1 overflow-y-auto", contentClassName),
8512
+ children: showEmptyState ? /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center p-6", children: emptyState }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 p-4", children: [
8513
+ /* @__PURE__ */ jsx(
8514
+ MessageList,
8515
+ {
8516
+ messages,
8517
+ showAvatars,
8518
+ renderMessage,
8519
+ onFeedback,
8520
+ onConfirmAction
8521
+ }
8522
+ ),
8523
+ isLoading && /* @__PURE__ */ jsx(TypingIndicator, {})
8524
+ ] })
8525
+ }
8526
+ )
8527
+ ] });
8528
+ }
8529
+ var AgentWorkspaceContext = createContext(null);
8530
+ function useAgentWorkspace() {
8531
+ const ctx = useContext(AgentWorkspaceContext);
8532
+ if (!ctx) {
8533
+ throw new Error(
8534
+ "useAgentWorkspace must be used within an <AgentWorkspace>."
8535
+ );
8536
+ }
8537
+ return ctx;
8538
+ }
8539
+ function useAgentWorkspaceOptional() {
8540
+ return useContext(AgentWorkspaceContext);
8541
+ }
8542
+ var noop2 = () => {
8543
+ };
8544
+ function warnDev2(message) {
8545
+ const env = globalThis.process?.env;
8546
+ if (env && env.NODE_ENV !== "production") {
8547
+ console.warn(`[AgentWorkspaceComposer] ${message}`);
8548
+ }
8549
+ }
8550
+ var LINE_HEIGHT_PX2 = 20;
8551
+ var VERTICAL_PADDING_PX2 = 16;
8552
+ function AgentWorkspaceComposer({
8553
+ value: valueProp,
8554
+ onChange,
8555
+ onSubmit,
8556
+ loading: loadingProp,
8557
+ disabled = false,
8558
+ placeholder = "Ask anything\u2026",
8559
+ onAttach,
8560
+ leftActions,
8561
+ minRows = 1,
8562
+ maxRows = 6,
8563
+ autoFocus = false,
8564
+ className,
8565
+ inputClassName
8566
+ }) {
8567
+ const agent = useNativeAgentOptional();
8568
+ const workspace = useAgentWorkspaceOptional();
8569
+ const [draft, setDraft] = useState("");
8570
+ const textareaRef = useRef(null);
8571
+ const propsControlEditing = valueProp !== void 0 || onChange !== void 0;
8572
+ const value = propsControlEditing ? valueProp ?? "" : agent ? agent.input : draft;
8573
+ const handleChange = propsControlEditing ? onChange ?? noop2 : agent ? agent.setInput : setDraft;
8574
+ const loading = loadingProp ?? agent?.isResponding ?? false;
8575
+ const resize = useCallback(() => {
8576
+ const el = textareaRef.current;
8577
+ if (!el) return;
8578
+ el.style.height = "auto";
8579
+ const maxHeight = maxRows * LINE_HEIGHT_PX2 + VERTICAL_PADDING_PX2;
8580
+ const minHeight = minRows * LINE_HEIGHT_PX2 + VERTICAL_PADDING_PX2;
8581
+ const next = Math.min(Math.max(el.scrollHeight, minHeight), maxHeight);
8582
+ el.style.height = `${next}px`;
8583
+ el.style.overflowY = el.scrollHeight > maxHeight ? "auto" : "hidden";
8584
+ }, [maxRows, minRows]);
8585
+ useLayoutEffect(() => {
8586
+ resize();
8587
+ }, [value, resize]);
8588
+ const handleSubmit = useCallback(
8589
+ (e) => {
8590
+ e?.preventDefault();
8591
+ if (!value.trim() || disabled || loading) return;
8592
+ workspace?.openConversation();
8593
+ if (onSubmit) {
8594
+ onSubmit(value);
8595
+ return;
8596
+ }
8597
+ if (!propsControlEditing && agent) {
8598
+ agent.submit();
8599
+ return;
8600
+ }
8601
+ warnDev2(
8602
+ "submit was ignored: provide `onSubmit`, or render inside a <NativeAgentProvider>."
8603
+ );
8604
+ },
8605
+ [value, disabled, loading, workspace, onSubmit, propsControlEditing, agent]
8606
+ );
8607
+ const handleKeyDown = useCallback(
8608
+ (e) => {
8609
+ if (e.key === "Enter" && !e.shiftKey) {
8610
+ e.preventDefault();
8611
+ handleSubmit();
8612
+ }
8613
+ },
8614
+ [handleSubmit]
8615
+ );
8616
+ const attach = leftActions ?? (onAttach ? /* @__PURE__ */ jsx(
8617
+ "button",
8618
+ {
8619
+ type: "button",
8620
+ onClick: onAttach,
8621
+ "aria-label": "Attach",
8622
+ className: "flex size-9 shrink-0 items-center justify-center rounded-xl text-muted-foreground transition-colors hover:bg-secondary hover:text-foreground",
8623
+ children: /* @__PURE__ */ jsx(Paperclip, { className: "size-4" })
8624
+ }
8625
+ ) : null);
8626
+ return /* @__PURE__ */ jsxs(
8627
+ "form",
8628
+ {
8629
+ onSubmit: handleSubmit,
8630
+ className: cn(
8631
+ "flex items-end gap-2 rounded-2xl border border-border bg-background p-2 shadow-sm transition-colors focus-within:border-foreground/30",
8632
+ className
8633
+ ),
8634
+ children: [
8635
+ attach,
8636
+ /* @__PURE__ */ jsx(
8637
+ "textarea",
8638
+ {
8639
+ ref: textareaRef,
8640
+ value,
8641
+ onChange: (e) => handleChange(e.target.value),
8642
+ onKeyDown: handleKeyDown,
8643
+ placeholder,
8644
+ disabled,
8645
+ autoFocus,
8646
+ rows: minRows,
8647
+ className: cn(
8648
+ "flex-1 resize-none border-0 bg-transparent px-1 py-2 text-sm leading-5 shadow-none outline-none",
8649
+ "placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-0",
8650
+ "disabled:cursor-not-allowed disabled:opacity-50",
8651
+ inputClassName
8652
+ )
8653
+ }
8654
+ ),
8655
+ /* @__PURE__ */ jsx(
8656
+ "button",
8657
+ {
8658
+ type: "submit",
8659
+ "aria-label": "Send",
8660
+ disabled: !value.trim() || disabled || loading,
8661
+ className: "flex size-9 shrink-0 items-center justify-center rounded-full bg-primary text-primary-foreground transition-opacity disabled:opacity-30",
8662
+ children: loading ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx(ArrowUp, { className: "size-4" })
8663
+ }
8664
+ )
8665
+ ]
8666
+ }
8667
+ );
8668
+ }
8669
+ function AgentWorkspacePanelToggle({
8670
+ side,
8671
+ open: openProp,
8672
+ onToggle,
8673
+ className
8674
+ }) {
8675
+ const workspace = useAgentWorkspaceOptional();
8676
+ const propsControlled = openProp !== void 0 || onToggle !== void 0;
8677
+ if (!propsControlled && workspace) {
8678
+ const hasPanel = side === "left" ? workspace.hasLeftPanel : workspace.hasRightPanel;
8679
+ if (!hasPanel) return null;
8680
+ }
8681
+ const open = propsControlled ? openProp ?? false : side === "left" ? workspace?.leftPanelOpen ?? false : workspace?.rightPanelOpen ?? false;
8682
+ const handleToggle = propsControlled ? onToggle : side === "left" ? workspace?.toggleLeftPanel : workspace?.toggleRightPanel;
8683
+ const Icon = side === "left" ? PanelLeft : PanelRight;
8684
+ return /* @__PURE__ */ jsx(
8685
+ "button",
8686
+ {
8687
+ type: "button",
8688
+ onClick: handleToggle,
8689
+ "aria-label": side === "left" ? "Toggle left panel" : "Toggle right panel",
8690
+ "aria-pressed": open,
8691
+ className: cn(
8692
+ "flex size-8 items-center justify-center rounded-lg transition-colors",
8693
+ open ? "bg-secondary text-foreground" : "text-muted-foreground hover:bg-secondary",
8694
+ className
8695
+ ),
8696
+ children: /* @__PURE__ */ jsx(Icon, { className: "size-4" })
8697
+ }
8698
+ );
8699
+ }
8700
+ var inertProps = (inert) => inert ? { inert: "" } : {};
8701
+ function useOpenState(controlled, defaultOpen, onChange) {
8702
+ const [internal, setInternal] = useState(defaultOpen);
8703
+ const isControlled = controlled !== void 0;
8704
+ const open = isControlled ? controlled : internal;
8705
+ const onChangeRef = useRef(onChange);
8706
+ useEffect(() => {
8707
+ onChangeRef.current = onChange;
8708
+ }, [onChange]);
8709
+ const setOpen = useCallback(
8710
+ (next) => {
8711
+ if (!isControlled) setInternal(next);
8712
+ onChangeRef.current?.(next);
8713
+ },
8714
+ [isControlled]
8715
+ );
8716
+ return [open, setOpen];
8717
+ }
8718
+ function AgentWorkspace({
8719
+ children,
8720
+ leftPanel,
8721
+ rightPanel,
8722
+ leftPanelWidth = 320,
8723
+ rightPanelWidth = 460,
8724
+ defaultLeftPanelOpen = true,
8725
+ defaultRightPanelOpen = true,
8726
+ leftPanelOpen: leftPanelOpenProp,
8727
+ rightPanelOpen: rightPanelOpenProp,
8728
+ onLeftPanelOpenChange,
8729
+ onRightPanelOpenChange,
8730
+ showPanelToggles = true,
8731
+ conversation,
8732
+ conversationOpen: conversationOpenProp,
8733
+ onConversationOpenChange,
8734
+ conversationTitle = "AI Assistant",
8735
+ showConversationHeader = true,
8736
+ composer = true,
8737
+ composerPlaceholder,
8738
+ composerHint,
8739
+ onComposerSubmit,
8740
+ className,
8741
+ contentClassName,
8742
+ conversationClassName,
8743
+ leftPanelClassName,
8744
+ rightPanelClassName,
8745
+ composerClassName
8746
+ }) {
8747
+ const agent = useNativeAgentOptional();
8748
+ const hasLeftPanel = leftPanel !== void 0 && leftPanel !== null;
8749
+ const hasRightPanel = rightPanel !== void 0 && rightPanel !== null;
8750
+ const [leftOpen, setLeftOpen] = useOpenState(
8751
+ leftPanelOpenProp,
8752
+ defaultLeftPanelOpen,
8753
+ onLeftPanelOpenChange
8754
+ );
8755
+ const [rightOpen, setRightOpen] = useOpenState(
8756
+ rightPanelOpenProp,
8757
+ defaultRightPanelOpen,
8758
+ onRightPanelOpenChange
8759
+ );
8760
+ const [conversationOpen, setConversationOpen] = useOpenState(
8761
+ conversationOpenProp,
8762
+ false,
8763
+ onConversationOpenChange
8764
+ );
8765
+ const isActive = agent?.isActive ?? false;
8766
+ const wasActiveRef = useRef(false);
8767
+ useEffect(() => {
8768
+ if (isActive !== wasActiveRef.current) {
8769
+ wasActiveRef.current = isActive;
8770
+ setConversationOpen(isActive);
8771
+ }
8772
+ }, [isActive, setConversationOpen]);
8773
+ const value = useMemo(
8774
+ () => ({
8775
+ hasLeftPanel,
8776
+ hasRightPanel,
8777
+ leftPanelOpen: leftOpen,
8778
+ rightPanelOpen: rightOpen,
8779
+ setLeftPanelOpen: setLeftOpen,
8780
+ setRightPanelOpen: setRightOpen,
8781
+ toggleLeftPanel: () => setLeftOpen(!leftOpen),
8782
+ toggleRightPanel: () => setRightOpen(!rightOpen),
8783
+ conversationOpen,
8784
+ setConversationOpen,
8785
+ openConversation: () => setConversationOpen(true),
8786
+ closeConversation: () => setConversationOpen(false)
8787
+ }),
8788
+ [
8789
+ hasLeftPanel,
8790
+ hasRightPanel,
8791
+ leftOpen,
8792
+ rightOpen,
8793
+ setLeftOpen,
8794
+ setRightOpen,
8795
+ conversationOpen,
8796
+ setConversationOpen
8797
+ ]
8798
+ );
8799
+ const showDock = composer !== false;
8800
+ const dockNode = composer === true || composer === void 0 ? /* @__PURE__ */ jsx(
8801
+ AgentWorkspaceComposer,
8802
+ {
8803
+ placeholder: composerPlaceholder,
8804
+ onSubmit: onComposerSubmit
8805
+ }
8806
+ ) : composer;
8807
+ const statusLine = agent ? agent.error ?? agent.statusLabel : null;
8808
+ return /* @__PURE__ */ jsx(AgentWorkspaceContext.Provider, { value, children: /* @__PURE__ */ jsxs(
8809
+ "div",
8810
+ {
8811
+ "data-conversation-open": conversationOpen || void 0,
8812
+ className: cn(
8813
+ "flex h-full w-full overflow-hidden bg-background text-foreground",
8814
+ className
8815
+ ),
8816
+ children: [
8817
+ hasLeftPanel && /* @__PURE__ */ jsx(
8818
+ "aside",
8819
+ {
8820
+ "data-state": leftOpen ? "open" : "collapsed",
8821
+ "aria-hidden": !leftOpen || void 0,
8822
+ className: cn(
8823
+ "relative hidden h-full shrink-0 overflow-hidden border-r border-border transition-[width] duration-300 ease-in-out lg:block",
8824
+ !leftOpen && "border-r-0",
8825
+ leftPanelClassName
8826
+ ),
8827
+ style: { width: leftOpen ? leftPanelWidth : 0 },
8828
+ children: /* @__PURE__ */ jsx("div", { className: "h-full", style: { width: leftPanelWidth }, children: leftPanel })
8829
+ }
8830
+ ),
8831
+ /* @__PURE__ */ jsxs("div", { className: "relative flex h-full min-w-0 grow flex-col", children: [
8832
+ showPanelToggles && hasLeftPanel && /* @__PURE__ */ jsx("div", { className: "absolute left-3 top-3 z-30 hidden lg:block", children: /* @__PURE__ */ jsx(
8833
+ AgentWorkspacePanelToggle,
8834
+ {
8835
+ side: "left",
8836
+ className: "border border-border/60 bg-background/80 shadow-sm backdrop-blur"
8837
+ }
8838
+ ) }),
8839
+ showPanelToggles && hasRightPanel && /* @__PURE__ */ jsx("div", { className: "absolute right-3 top-3 z-30 hidden lg:block", children: /* @__PURE__ */ jsx(
8840
+ AgentWorkspacePanelToggle,
8841
+ {
8842
+ side: "right",
8843
+ className: "border border-border/60 bg-background/80 shadow-sm backdrop-blur"
8844
+ }
8845
+ ) }),
8846
+ /* @__PURE__ */ jsxs("div", { className: "relative min-h-0 grow", children: [
8847
+ /* @__PURE__ */ jsx(
8848
+ "div",
8849
+ {
8850
+ "aria-hidden": conversationOpen || void 0,
8851
+ ...inertProps(conversationOpen),
8852
+ className: cn("h-full overflow-y-auto", contentClassName),
8853
+ children
8854
+ }
8855
+ ),
8856
+ /* @__PURE__ */ jsxs(
8857
+ "div",
8858
+ {
8859
+ "data-state": conversationOpen ? "open" : "closed",
8860
+ "aria-hidden": !conversationOpen || void 0,
8861
+ ...inertProps(!conversationOpen),
8862
+ className: cn(
8863
+ "absolute inset-0 z-20 flex flex-col bg-background transition-[opacity,transform] duration-300 ease-out",
8864
+ conversationOpen ? "translate-y-0 opacity-100" : "pointer-events-none translate-y-8 opacity-0",
8865
+ conversationClassName
8866
+ ),
8867
+ children: [
8868
+ showConversationHeader && /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center justify-between gap-3 border-b border-border bg-background px-4 py-2.5", children: [
8869
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2.5", children: [
8870
+ /* @__PURE__ */ jsx("span", { className: "flex size-7 shrink-0 items-center justify-center rounded-md bg-primary text-primary-foreground", children: /* @__PURE__ */ jsx(Sparkles, { className: "size-3.5" }) }),
8871
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
8872
+ /* @__PURE__ */ jsx("div", { className: "truncate text-sm font-medium", children: conversationTitle }),
8873
+ statusLine && /* @__PURE__ */ jsx("div", { className: "truncate text-xs text-muted-foreground", children: statusLine })
8874
+ ] })
8875
+ ] }),
8876
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [
8877
+ (hasLeftPanel || hasRightPanel) && /* @__PURE__ */ jsxs("div", { className: "mr-1 hidden items-center gap-1 border-r border-border pr-2 lg:flex", children: [
8878
+ /* @__PURE__ */ jsx(AgentWorkspacePanelToggle, { side: "left" }),
8879
+ /* @__PURE__ */ jsx(AgentWorkspacePanelToggle, { side: "right" })
8880
+ ] }),
8881
+ agent && /* @__PURE__ */ jsx(
8882
+ "button",
8883
+ {
8884
+ type: "button",
8885
+ onClick: () => agent.reset(),
8886
+ "aria-label": "Start a new conversation",
8887
+ title: "New conversation",
8888
+ className: "flex size-8 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-secondary hover:text-foreground",
8889
+ children: /* @__PURE__ */ jsx(RotateCcw, { className: "size-4" })
8890
+ }
8891
+ ),
8892
+ /* @__PURE__ */ jsx(
8893
+ "button",
8894
+ {
8895
+ type: "button",
8896
+ onClick: () => setConversationOpen(false),
8897
+ "aria-label": "Hide conversation",
8898
+ title: "Hide conversation",
8899
+ className: "flex size-8 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-secondary hover:text-foreground",
8900
+ children: /* @__PURE__ */ jsx(ChevronDown, { className: "size-4" })
8901
+ }
8902
+ )
8903
+ ] })
8904
+ ] }),
8905
+ /* @__PURE__ */ jsx("div", { className: "min-h-0 grow", children: conversation ?? /* @__PURE__ */ jsx(
8906
+ AgentConversation,
8907
+ {
8908
+ contentClassName: "mx-auto w-full max-w-3xl",
8909
+ showAvatars: true
8910
+ }
8911
+ ) })
8912
+ ]
8913
+ }
8914
+ )
8915
+ ] }),
8916
+ showDock && /* @__PURE__ */ jsx(
8917
+ "div",
8918
+ {
8919
+ className: cn(
8920
+ "relative z-30 shrink-0 border-t border-border bg-background/80 px-4 py-3 backdrop-blur",
8921
+ composerClassName
8922
+ ),
8923
+ children: /* @__PURE__ */ jsxs("div", { className: "mx-auto w-full max-w-3xl", children: [
8924
+ isActive && !conversationOpen && /* @__PURE__ */ jsx("div", { className: "mb-2 flex justify-center", children: /* @__PURE__ */ jsxs(
8925
+ "button",
8926
+ {
8927
+ type: "button",
8928
+ onClick: () => setConversationOpen(true),
8929
+ className: "inline-flex items-center gap-1.5 rounded-full border border-border bg-background px-2.5 py-1 text-xs text-muted-foreground transition-colors hover:border-foreground/20 hover:text-foreground",
8930
+ children: [
8931
+ agent?.isResponding ? /* @__PURE__ */ jsx(Loader2, { className: "size-3 animate-spin" }) : /* @__PURE__ */ jsx(MessageSquare, { className: "size-3" }),
8932
+ "View conversation"
8933
+ ]
8934
+ }
8935
+ ) }),
8936
+ dockNode,
8937
+ composerHint && /* @__PURE__ */ jsx("div", { className: "mt-2 text-center text-[11px] text-muted-foreground", children: composerHint })
8938
+ ] })
8939
+ }
8940
+ )
8941
+ ] }),
8942
+ hasRightPanel && /* @__PURE__ */ jsx(
8943
+ "aside",
8944
+ {
8945
+ "data-state": rightOpen ? "open" : "collapsed",
8946
+ "aria-hidden": !rightOpen || void 0,
8947
+ className: cn(
8948
+ "relative hidden h-full shrink-0 overflow-hidden border-l border-border transition-[width] duration-300 ease-in-out lg:block",
8949
+ !rightOpen && "border-l-0",
8950
+ rightPanelClassName
8951
+ ),
8952
+ style: { width: rightOpen ? rightPanelWidth : 0 },
8953
+ children: /* @__PURE__ */ jsx("div", { className: "h-full", style: { width: rightPanelWidth }, children: rightPanel })
8954
+ }
8955
+ )
8956
+ ]
8957
+ }
8958
+ ) });
8959
+ }
8125
8960
 
8126
- export { AgentAvatar, AgentHandoff, AgentProvider, AgentSurface, AllocationBreakdown, AnalyticsDashboard, Avatar, AvatarFallback, AvatarImage, Badge, Button, ChartContainer, ChatPanel, Collapsible, CollapsibleContent2 as CollapsibleContent, CollapsibleTrigger2 as CollapsibleTrigger, ConfirmationPanel, ControlGrid, ConversationAnalytics, ConversationArtifact, DataPayloadView, DataTable, DynamicRenderer, EntityCard, FileDropZone, FloatingWidget, FullBleedSurface, FullscreenDashboard, GuidedLessonFlow, ImageGenerator, InlineSuggestionsInput, Input, ListingFeed, MediaEditorCanvas, MediaGallery, MessageActions, MessageBubble, MessageContainer, MessageContent, MessageList, MessageWithAttachments, MessageWithFeedback, MessageWithReasoning, MessageWithSteps, MetricsGrid, MobileShell, MultimodalInput, OnboardingWizard, OptionCards, OverlayModal, PerformanceMetrics, PersonaSelector, Progress, ProgressTracker, PromptInput, PromptLibrary, QuickReplies, QuizCard, RecommendationCards, ReportView, ScheduleTimeline, ScrollArea, ScrollBar, SemanticBuilderSocketClient, SentimentDisplay, SettingsPanel, SlotRenderer, SplitView, StatusBadge, SystemMessage, TemplateSelector, Textarea, Timestamp, Tooltip4 as Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, TypingIndicator, WritingAssistant, badgeVariants, buildSocketUrl, buttonVariants, calculatePercentage, cn, componentManifest, componentMap, componentRegistry, copyToClipboard, createMockBackend, debounce, delay, findComponentsByCapability, findComponentsByCategory, findComponentsBySurface, formatBytes, formatCurrency, formatNumber, formatRelativeTime, formatTime, generateId, getInitials, getManifestEntry, getSentimentBgColor, getSentimentColor, isBrowser, isInIframe, normalizeWebsiteId, parseTextWithBold, registerAllComponents, truncate, useAgent, useAgentBackend, useAgentInput, useAgentLayout, useAgentMessages, useSemanticBuilder };
8961
+ export { AgentAvatar, AgentComposer, AgentConversation, AgentHandoff, AgentProvider, AgentSurface, AgentWorkspace, AgentWorkspaceComposer, AgentWorkspacePanelToggle, AllocationBreakdown, AnalyticsDashboard, Avatar, AvatarFallback, AvatarImage, Badge, Button, ChartContainer, ChatPanel, Collapsible, CollapsibleContent2 as CollapsibleContent, CollapsibleTrigger2 as CollapsibleTrigger, ConfirmationPanel, ControlGrid, ConversationAnalytics, ConversationArtifact, DataPayloadView, DataTable, DynamicRenderer, EntityCard, FileDropZone, FloatingWidget, FullBleedSurface, FullscreenDashboard, GuidedLessonFlow, ImageGenerator, InlineSuggestionsInput, Input, ListingFeed, MediaEditorCanvas, MediaGallery, MessageActions, MessageBubble, MessageContainer, MessageContent, MessageList, MessageWithAttachments, MessageWithFeedback, MessageWithReasoning, MessageWithSteps, MetricsGrid, MobileShell, MultimodalInput, NativeAgentProvider, NativeSurface, OnboardingWizard, OptionCards, OverlayModal, PerformanceMetrics, PersonaSelector, Progress, ProgressTracker, PromptInput, PromptLibrary, QuickReplies, QuizCard, RecommendationCards, ReportView, ScheduleTimeline, ScrollArea, ScrollBar, SemanticBuilderSocketClient, SentimentDisplay, SettingsPanel, SlotRenderer, SplitView, StatusBadge, SystemMessage, TemplateSelector, Textarea, Timestamp, Tooltip4 as Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, TypingIndicator, WritingAssistant, badgeVariants, buildSocketUrl, buttonVariants, calculatePercentage, cn, componentManifest, componentMap, componentRegistry, copyToClipboard, createMockBackend, debounce, delay, findComponentsByCapability, findComponentsByCategory, findComponentsBySurface, formatBytes, formatCurrency, formatNumber, formatRelativeTime, formatTime, generateId, getInitials, getManifestEntry, getSentimentBgColor, getSentimentColor, isBrowser, isInIframe, normalizeWebsiteId, parseTextWithBold, registerAllComponents, truncate, useAgent, useAgentBackend, useAgentInput, useAgentLayout, useAgentMessages, useAgentWorkspace, useAgentWorkspaceOptional, useNativeAgent, useNativeAgentOptional, useSemanticBuilder };
8127
8962
  //# sourceMappingURL=index.js.map
8128
8963
  //# sourceMappingURL=index.js.map