@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.cjs CHANGED
@@ -7222,6 +7222,7 @@ function useSemanticBuilder({
7222
7222
  onGeneratedBlocks,
7223
7223
  onUndoRequest,
7224
7224
  buildWelcomeMessage,
7225
+ seedWelcomeMessage = true,
7225
7226
  webSocketImpl
7226
7227
  }) {
7227
7228
  const [messages, setMessages] = React4.useState([]);
@@ -7255,6 +7256,7 @@ function useSemanticBuilder({
7255
7256
  resolveSocketUrlRef.current = resolveSocketUrl;
7256
7257
  }, [resolveSocketUrl]);
7257
7258
  React4.useEffect(() => {
7259
+ if (!seedWelcomeMessage) return;
7258
7260
  const hasContentBrief = !!contentBrief;
7259
7261
  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.`);
7260
7262
  setMessages([
@@ -7265,7 +7267,7 @@ function useSemanticBuilder({
7265
7267
  createdAt: Date.now()
7266
7268
  }
7267
7269
  ]);
7268
- }, [contentBrief, pageName, pageSlug]);
7270
+ }, [contentBrief, pageName, pageSlug, seedWelcomeMessage]);
7269
7271
  const handleEnvelope = React4.useCallback((envelope) => {
7270
7272
  switch (envelope.type) {
7271
7273
  case "connection_ready":
@@ -7477,6 +7479,14 @@ function useSemanticBuilder({
7477
7479
  const retry = React4.useCallback(() => {
7478
7480
  clientRef.current?.retry();
7479
7481
  }, []);
7482
+ const reset = React4.useCallback(() => {
7483
+ pendingHiddenRequestsRef.current = 0;
7484
+ hiddenAssistantMessageIdsRef.current.clear();
7485
+ clientRef.current?.close();
7486
+ setConnectionState("idle");
7487
+ setConnectionError(null);
7488
+ setMessages([]);
7489
+ }, []);
7480
7490
  const isConnected = connectionState === "ready" || connectionState === "streaming";
7481
7491
  const isStreaming = connectionState === "streaming";
7482
7492
  const statusLabel = React4.useMemo(() => {
@@ -7503,7 +7513,8 @@ function useSemanticBuilder({
7503
7513
  isConnected,
7504
7514
  isStreaming,
7505
7515
  sendMessage,
7506
- retry
7516
+ retry,
7517
+ reset
7507
7518
  };
7508
7519
  }
7509
7520
  var ScrollArea = React4__namespace.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
@@ -8007,6 +8018,54 @@ function FullBleedSurface({
8007
8018
  }
8008
8019
  );
8009
8020
  }
8021
+ function NativeSurface({
8022
+ children,
8023
+ input,
8024
+ title,
8025
+ subtitle,
8026
+ icon,
8027
+ headerActions,
8028
+ suggestions,
8029
+ footer,
8030
+ isLoading = false,
8031
+ autoScroll = true,
8032
+ className,
8033
+ contentClassName
8034
+ }) {
8035
+ const scrollRef = React4.useRef(null);
8036
+ React4.useEffect(() => {
8037
+ if (autoScroll && scrollRef.current) {
8038
+ scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
8039
+ }
8040
+ }, [children, isLoading, autoScroll]);
8041
+ const showHeader = !!title || !!headerActions || !!icon;
8042
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex h-full min-h-0 flex-col", className), children: [
8043
+ showHeader && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between border-b px-4 py-3", children: [
8044
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
8045
+ icon,
8046
+ (title || subtitle) && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
8047
+ title && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-sm", children: title }),
8048
+ subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-xs", children: subtitle })
8049
+ ] })
8050
+ ] }),
8051
+ headerActions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: headerActions })
8052
+ ] }),
8053
+ /* @__PURE__ */ jsxRuntime.jsx(
8054
+ "div",
8055
+ {
8056
+ ref: scrollRef,
8057
+ className: cn("min-h-0 flex-1 overflow-y-auto", contentClassName),
8058
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4 p-4", children: [
8059
+ children,
8060
+ isLoading && /* @__PURE__ */ jsxRuntime.jsx(TypingIndicator, {})
8061
+ ] })
8062
+ }
8063
+ ),
8064
+ suggestions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t px-4 py-2", children: suggestions }),
8065
+ input,
8066
+ footer && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t px-4 py-1.5", children: footer })
8067
+ ] });
8068
+ }
8010
8069
  function AgentSurface({
8011
8070
  mode,
8012
8071
  messages,
@@ -8130,6 +8189,21 @@ function AgentSurface({
8130
8189
  children: conversation
8131
8190
  }
8132
8191
  );
8192
+ case "native":
8193
+ return /* @__PURE__ */ jsxRuntime.jsx(
8194
+ NativeSurface,
8195
+ {
8196
+ title,
8197
+ subtitle,
8198
+ icon,
8199
+ headerActions,
8200
+ input: resolvedInput,
8201
+ suggestions,
8202
+ isLoading,
8203
+ className,
8204
+ children: conversation
8205
+ }
8206
+ );
8133
8207
  case "panel":
8134
8208
  default:
8135
8209
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -8148,11 +8222,777 @@ function AgentSurface({
8148
8222
  );
8149
8223
  }
8150
8224
  }
8225
+ var NativeAgentContext = React4.createContext(
8226
+ null
8227
+ );
8228
+ function useNativeAgent() {
8229
+ const ctx = React4.useContext(NativeAgentContext);
8230
+ if (!ctx) {
8231
+ throw new Error(
8232
+ "useNativeAgent must be used within a <NativeAgentProvider>."
8233
+ );
8234
+ }
8235
+ return ctx;
8236
+ }
8237
+ function useNativeAgentOptional() {
8238
+ return React4.useContext(NativeAgentContext);
8239
+ }
8240
+ var EMPTY_BLOCKS = [];
8241
+ var REASONING_STATUS = {
8242
+ active: "active",
8243
+ complete: "complete"
8244
+ };
8245
+ function toAgentMessage(message) {
8246
+ const reasoning = message.thinking ? [
8247
+ {
8248
+ id: `${message.id}__thinking`,
8249
+ label: "Thinking",
8250
+ content: message.thinking,
8251
+ status: message.streaming ? REASONING_STATUS.active : REASONING_STATUS.complete
8252
+ }
8253
+ ] : void 0;
8254
+ return {
8255
+ id: message.id,
8256
+ role: message.role,
8257
+ content: message.content,
8258
+ timestamp: new Date(message.createdAt),
8259
+ reasoning
8260
+ };
8261
+ }
8262
+ function deriveStatus(connectionState) {
8263
+ switch (connectionState) {
8264
+ case "streaming":
8265
+ return "busy";
8266
+ case "ready":
8267
+ return "online";
8268
+ case "connecting":
8269
+ return "away";
8270
+ default:
8271
+ return "offline";
8272
+ }
8273
+ }
8274
+ function NativeAgentProvider({
8275
+ children,
8276
+ socketUrl,
8277
+ resolveSocketUrl,
8278
+ websiteId,
8279
+ pageCategoryId,
8280
+ pageName = "",
8281
+ pageSlug,
8282
+ blocks,
8283
+ contentBrief,
8284
+ onGeneratedBlocks,
8285
+ webSocketImpl,
8286
+ connectionStrategy = "lazy",
8287
+ seedWelcomeMessage = false,
8288
+ onError,
8289
+ onActivate
8290
+ }) {
8291
+ const [enabled, setEnabled] = React4.useState(connectionStrategy === "eager");
8292
+ const [isActive, setIsActive] = React4.useState(false);
8293
+ const [input, setInput] = React4.useState("");
8294
+ const [pendingCount, setPendingCount] = React4.useState(0);
8295
+ const [optimistic, setOptimistic] = React4.useState([]);
8296
+ const blocksValue = blocks ?? EMPTY_BLOCKS;
8297
+ const {
8298
+ messages: builderMessages,
8299
+ connectionState,
8300
+ connectionError,
8301
+ statusLabel,
8302
+ isConnected,
8303
+ isStreaming,
8304
+ sendMessage,
8305
+ retry,
8306
+ reset: resetSession
8307
+ } = useSemanticBuilder({
8308
+ socketUrl,
8309
+ resolveSocketUrl,
8310
+ websiteId,
8311
+ pageCategoryId,
8312
+ pageName,
8313
+ pageSlug,
8314
+ blocks: blocksValue,
8315
+ contentBrief,
8316
+ enabled,
8317
+ onGeneratedBlocks,
8318
+ webSocketImpl,
8319
+ seedWelcomeMessage
8320
+ });
8321
+ const pendingPromptsRef = React4.useRef([]);
8322
+ const optimisticSeqRef = React4.useRef(0);
8323
+ const onActivateRef = React4.useRef(onActivate);
8324
+ const onErrorRef = React4.useRef(onError);
8325
+ React4.useEffect(() => {
8326
+ onActivateRef.current = onActivate;
8327
+ }, [onActivate]);
8328
+ React4.useEffect(() => {
8329
+ onErrorRef.current = onError;
8330
+ }, [onError]);
8331
+ React4.useEffect(() => {
8332
+ if (connectionError) onErrorRef.current?.(connectionError);
8333
+ }, [connectionError]);
8334
+ const markActive = React4.useCallback(() => {
8335
+ setIsActive((prev) => {
8336
+ if (!prev) onActivateRef.current?.();
8337
+ return true;
8338
+ });
8339
+ }, []);
8340
+ React4.useEffect(() => {
8341
+ if (connectionState !== "ready" || pendingPromptsRef.current.length === 0) {
8342
+ return;
8343
+ }
8344
+ const next = pendingPromptsRef.current.shift();
8345
+ if (next === void 0) return;
8346
+ setPendingCount(pendingPromptsRef.current.length);
8347
+ setOptimistic((prev) => prev.slice(1));
8348
+ void sendMessage(next).then((ok) => {
8349
+ if (!ok) {
8350
+ pendingPromptsRef.current.unshift(next);
8351
+ setPendingCount(pendingPromptsRef.current.length);
8352
+ }
8353
+ });
8354
+ }, [connectionState, pendingCount, sendMessage]);
8355
+ const submit = React4.useCallback(
8356
+ (content) => {
8357
+ const text = (content ?? input).trim();
8358
+ if (!text) return;
8359
+ markActive();
8360
+ setEnabled(true);
8361
+ setInput("");
8362
+ pendingPromptsRef.current.push(text);
8363
+ setPendingCount(pendingPromptsRef.current.length);
8364
+ const id = `native-pending-${optimisticSeqRef.current}`;
8365
+ optimisticSeqRef.current += 1;
8366
+ setOptimistic((prev) => [
8367
+ ...prev,
8368
+ { id, role: "user", content: text, timestamp: /* @__PURE__ */ new Date() }
8369
+ ]);
8370
+ },
8371
+ [input, markActive]
8372
+ );
8373
+ const activate = React4.useCallback(() => {
8374
+ markActive();
8375
+ setEnabled(true);
8376
+ }, [markActive]);
8377
+ const reset = React4.useCallback(() => {
8378
+ pendingPromptsRef.current = [];
8379
+ setPendingCount(0);
8380
+ setOptimistic([]);
8381
+ setIsActive(false);
8382
+ setInput("");
8383
+ setEnabled(false);
8384
+ resetSession();
8385
+ }, [resetSession]);
8386
+ const messages = React4.useMemo(
8387
+ () => [...builderMessages.map(toAgentMessage), ...optimistic],
8388
+ [builderMessages, optimistic]
8389
+ );
8390
+ const isResponding = isStreaming || pendingCount > 0 && !connectionError && connectionState !== "error" && connectionState !== "requires_shared_domain";
8391
+ const status = deriveStatus(connectionState);
8392
+ const value = React4.useMemo(
8393
+ () => ({
8394
+ messages,
8395
+ isActive,
8396
+ isConnected,
8397
+ isResponding,
8398
+ isStreaming,
8399
+ connectionState,
8400
+ statusLabel,
8401
+ status,
8402
+ error: connectionError,
8403
+ input,
8404
+ setInput,
8405
+ submit,
8406
+ activate,
8407
+ reset,
8408
+ retry
8409
+ }),
8410
+ [
8411
+ messages,
8412
+ isActive,
8413
+ isConnected,
8414
+ isResponding,
8415
+ isStreaming,
8416
+ connectionState,
8417
+ statusLabel,
8418
+ status,
8419
+ connectionError,
8420
+ input,
8421
+ submit,
8422
+ activate,
8423
+ reset,
8424
+ retry
8425
+ ]
8426
+ );
8427
+ return /* @__PURE__ */ jsxRuntime.jsx(NativeAgentContext.Provider, { value, children });
8428
+ }
8429
+ var noop = () => {
8430
+ };
8431
+ function warnDev(message) {
8432
+ const env = globalThis.process?.env;
8433
+ if (env && env.NODE_ENV !== "production") {
8434
+ console.warn(`[AgentComposer] ${message}`);
8435
+ }
8436
+ }
8437
+ function AgentComposer({
8438
+ value: valueProp,
8439
+ onChange,
8440
+ onSubmit,
8441
+ loading: loadingProp,
8442
+ disabled: disabledProp,
8443
+ placeholder = "Ask anything\u2026",
8444
+ variant = "default",
8445
+ bare = false,
8446
+ suggestions,
8447
+ footer,
8448
+ leftActions,
8449
+ rightActions,
8450
+ autoFocus = false,
8451
+ className,
8452
+ inputClassName
8453
+ }) {
8454
+ const ctx = useNativeAgentOptional();
8455
+ const [draft, setDraft] = React4.useState("");
8456
+ const propsControlEditing = valueProp !== void 0 || onChange !== void 0;
8457
+ const value = propsControlEditing ? valueProp ?? "" : ctx ? ctx.input : draft;
8458
+ const handleChange = propsControlEditing ? onChange ?? noop : ctx ? ctx.setInput : setDraft;
8459
+ const loading = loadingProp ?? ctx?.isResponding ?? false;
8460
+ const disabled = disabledProp ?? false;
8461
+ const handleSubmit = React4.useCallback(() => {
8462
+ if (onSubmit) {
8463
+ onSubmit(value);
8464
+ return;
8465
+ }
8466
+ if (!propsControlEditing && ctx) {
8467
+ ctx.submit();
8468
+ return;
8469
+ }
8470
+ warnDev(
8471
+ "submit was ignored: provide `onSubmit`, or render inside a <NativeAgentProvider>."
8472
+ );
8473
+ }, [onSubmit, propsControlEditing, ctx, value]);
8474
+ return /* @__PURE__ */ jsxRuntime.jsxs(
8475
+ "div",
8476
+ {
8477
+ className: cn(
8478
+ !bare && "rounded-2xl border bg-background shadow-sm",
8479
+ className
8480
+ ),
8481
+ children: [
8482
+ suggestions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("px-3", bare ? "pb-2" : "pt-3"), children: suggestions }),
8483
+ /* @__PURE__ */ jsxRuntime.jsx(
8484
+ PromptInput,
8485
+ {
8486
+ value,
8487
+ onChange: handleChange,
8488
+ onSubmit: handleSubmit,
8489
+ placeholder,
8490
+ loading,
8491
+ disabled,
8492
+ variant,
8493
+ autoFocus,
8494
+ leftActions,
8495
+ rightActions,
8496
+ inputClassName
8497
+ }
8498
+ ),
8499
+ footer && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 pb-2 text-muted-foreground text-xs", children: footer })
8500
+ ]
8501
+ }
8502
+ );
8503
+ }
8504
+ var EMPTY_MESSAGES = [];
8505
+ function AgentConversation({
8506
+ messages: messagesProp,
8507
+ isLoading: isLoadingProp,
8508
+ header,
8509
+ emptyState,
8510
+ showAvatars = true,
8511
+ renderMessage,
8512
+ onFeedback,
8513
+ onConfirmAction,
8514
+ autoScroll = true,
8515
+ className,
8516
+ contentClassName
8517
+ }) {
8518
+ const ctx = useNativeAgentOptional();
8519
+ const messages = React4.useMemo(
8520
+ () => messagesProp ?? ctx?.messages ?? EMPTY_MESSAGES,
8521
+ [messagesProp, ctx?.messages]
8522
+ );
8523
+ const isLoading = isLoadingProp ?? ctx?.isResponding ?? false;
8524
+ const scrollRef = React4.useRef(null);
8525
+ React4.useEffect(() => {
8526
+ if (autoScroll && scrollRef.current) {
8527
+ scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
8528
+ }
8529
+ }, [messages, isLoading, autoScroll]);
8530
+ const showEmptyState = messages.length === 0 && !isLoading && !!emptyState;
8531
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex h-full min-h-0 flex-col", className), children: [
8532
+ header,
8533
+ /* @__PURE__ */ jsxRuntime.jsx(
8534
+ "div",
8535
+ {
8536
+ ref: scrollRef,
8537
+ className: cn("min-h-0 flex-1 overflow-y-auto", contentClassName),
8538
+ children: showEmptyState ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center p-6", children: emptyState }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4 p-4", children: [
8539
+ /* @__PURE__ */ jsxRuntime.jsx(
8540
+ MessageList,
8541
+ {
8542
+ messages,
8543
+ showAvatars,
8544
+ renderMessage,
8545
+ onFeedback,
8546
+ onConfirmAction
8547
+ }
8548
+ ),
8549
+ isLoading && /* @__PURE__ */ jsxRuntime.jsx(TypingIndicator, {})
8550
+ ] })
8551
+ }
8552
+ )
8553
+ ] });
8554
+ }
8555
+ var AgentWorkspaceContext = React4.createContext(null);
8556
+ function useAgentWorkspace() {
8557
+ const ctx = React4.useContext(AgentWorkspaceContext);
8558
+ if (!ctx) {
8559
+ throw new Error(
8560
+ "useAgentWorkspace must be used within an <AgentWorkspace>."
8561
+ );
8562
+ }
8563
+ return ctx;
8564
+ }
8565
+ function useAgentWorkspaceOptional() {
8566
+ return React4.useContext(AgentWorkspaceContext);
8567
+ }
8568
+ var noop2 = () => {
8569
+ };
8570
+ function warnDev2(message) {
8571
+ const env = globalThis.process?.env;
8572
+ if (env && env.NODE_ENV !== "production") {
8573
+ console.warn(`[AgentWorkspaceComposer] ${message}`);
8574
+ }
8575
+ }
8576
+ var LINE_HEIGHT_PX2 = 20;
8577
+ var VERTICAL_PADDING_PX2 = 16;
8578
+ function AgentWorkspaceComposer({
8579
+ value: valueProp,
8580
+ onChange,
8581
+ onSubmit,
8582
+ loading: loadingProp,
8583
+ disabled = false,
8584
+ placeholder = "Ask anything\u2026",
8585
+ onAttach,
8586
+ leftActions,
8587
+ minRows = 1,
8588
+ maxRows = 6,
8589
+ autoFocus = false,
8590
+ className,
8591
+ inputClassName
8592
+ }) {
8593
+ const agent = useNativeAgentOptional();
8594
+ const workspace = useAgentWorkspaceOptional();
8595
+ const [draft, setDraft] = React4.useState("");
8596
+ const textareaRef = React4.useRef(null);
8597
+ const propsControlEditing = valueProp !== void 0 || onChange !== void 0;
8598
+ const value = propsControlEditing ? valueProp ?? "" : agent ? agent.input : draft;
8599
+ const handleChange = propsControlEditing ? onChange ?? noop2 : agent ? agent.setInput : setDraft;
8600
+ const loading = loadingProp ?? agent?.isResponding ?? false;
8601
+ const resize = React4.useCallback(() => {
8602
+ const el = textareaRef.current;
8603
+ if (!el) return;
8604
+ el.style.height = "auto";
8605
+ const maxHeight = maxRows * LINE_HEIGHT_PX2 + VERTICAL_PADDING_PX2;
8606
+ const minHeight = minRows * LINE_HEIGHT_PX2 + VERTICAL_PADDING_PX2;
8607
+ const next = Math.min(Math.max(el.scrollHeight, minHeight), maxHeight);
8608
+ el.style.height = `${next}px`;
8609
+ el.style.overflowY = el.scrollHeight > maxHeight ? "auto" : "hidden";
8610
+ }, [maxRows, minRows]);
8611
+ React4.useLayoutEffect(() => {
8612
+ resize();
8613
+ }, [value, resize]);
8614
+ const handleSubmit = React4.useCallback(
8615
+ (e) => {
8616
+ e?.preventDefault();
8617
+ if (!value.trim() || disabled || loading) return;
8618
+ workspace?.openConversation();
8619
+ if (onSubmit) {
8620
+ onSubmit(value);
8621
+ return;
8622
+ }
8623
+ if (!propsControlEditing && agent) {
8624
+ agent.submit();
8625
+ return;
8626
+ }
8627
+ warnDev2(
8628
+ "submit was ignored: provide `onSubmit`, or render inside a <NativeAgentProvider>."
8629
+ );
8630
+ },
8631
+ [value, disabled, loading, workspace, onSubmit, propsControlEditing, agent]
8632
+ );
8633
+ const handleKeyDown = React4.useCallback(
8634
+ (e) => {
8635
+ if (e.key === "Enter" && !e.shiftKey) {
8636
+ e.preventDefault();
8637
+ handleSubmit();
8638
+ }
8639
+ },
8640
+ [handleSubmit]
8641
+ );
8642
+ const attach = leftActions ?? (onAttach ? /* @__PURE__ */ jsxRuntime.jsx(
8643
+ "button",
8644
+ {
8645
+ type: "button",
8646
+ onClick: onAttach,
8647
+ "aria-label": "Attach",
8648
+ className: "flex size-9 shrink-0 items-center justify-center rounded-xl text-muted-foreground transition-colors hover:bg-secondary hover:text-foreground",
8649
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Paperclip, { className: "size-4" })
8650
+ }
8651
+ ) : null);
8652
+ return /* @__PURE__ */ jsxRuntime.jsxs(
8653
+ "form",
8654
+ {
8655
+ onSubmit: handleSubmit,
8656
+ className: cn(
8657
+ "flex items-end gap-2 rounded-2xl border border-border bg-background p-2 shadow-sm transition-colors focus-within:border-foreground/30",
8658
+ className
8659
+ ),
8660
+ children: [
8661
+ attach,
8662
+ /* @__PURE__ */ jsxRuntime.jsx(
8663
+ "textarea",
8664
+ {
8665
+ ref: textareaRef,
8666
+ value,
8667
+ onChange: (e) => handleChange(e.target.value),
8668
+ onKeyDown: handleKeyDown,
8669
+ placeholder,
8670
+ disabled,
8671
+ autoFocus,
8672
+ rows: minRows,
8673
+ className: cn(
8674
+ "flex-1 resize-none border-0 bg-transparent px-1 py-2 text-sm leading-5 shadow-none outline-none",
8675
+ "placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-0",
8676
+ "disabled:cursor-not-allowed disabled:opacity-50",
8677
+ inputClassName
8678
+ )
8679
+ }
8680
+ ),
8681
+ /* @__PURE__ */ jsxRuntime.jsx(
8682
+ "button",
8683
+ {
8684
+ type: "submit",
8685
+ "aria-label": "Send",
8686
+ disabled: !value.trim() || disabled || loading,
8687
+ className: "flex size-9 shrink-0 items-center justify-center rounded-full bg-primary text-primary-foreground transition-opacity disabled:opacity-30",
8688
+ children: loading ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "size-4" })
8689
+ }
8690
+ )
8691
+ ]
8692
+ }
8693
+ );
8694
+ }
8695
+ function AgentWorkspacePanelToggle({
8696
+ side,
8697
+ open: openProp,
8698
+ onToggle,
8699
+ className
8700
+ }) {
8701
+ const workspace = useAgentWorkspaceOptional();
8702
+ const propsControlled = openProp !== void 0 || onToggle !== void 0;
8703
+ if (!propsControlled && workspace) {
8704
+ const hasPanel = side === "left" ? workspace.hasLeftPanel : workspace.hasRightPanel;
8705
+ if (!hasPanel) return null;
8706
+ }
8707
+ const open = propsControlled ? openProp ?? false : side === "left" ? workspace?.leftPanelOpen ?? false : workspace?.rightPanelOpen ?? false;
8708
+ const handleToggle = propsControlled ? onToggle : side === "left" ? workspace?.toggleLeftPanel : workspace?.toggleRightPanel;
8709
+ const Icon = side === "left" ? lucideReact.PanelLeft : lucideReact.PanelRight;
8710
+ return /* @__PURE__ */ jsxRuntime.jsx(
8711
+ "button",
8712
+ {
8713
+ type: "button",
8714
+ onClick: handleToggle,
8715
+ "aria-label": side === "left" ? "Toggle left panel" : "Toggle right panel",
8716
+ "aria-pressed": open,
8717
+ className: cn(
8718
+ "flex size-8 items-center justify-center rounded-lg transition-colors",
8719
+ open ? "bg-secondary text-foreground" : "text-muted-foreground hover:bg-secondary",
8720
+ className
8721
+ ),
8722
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "size-4" })
8723
+ }
8724
+ );
8725
+ }
8726
+ var inertProps = (inert) => inert ? { inert: "" } : {};
8727
+ function useOpenState(controlled, defaultOpen, onChange) {
8728
+ const [internal, setInternal] = React4.useState(defaultOpen);
8729
+ const isControlled = controlled !== void 0;
8730
+ const open = isControlled ? controlled : internal;
8731
+ const onChangeRef = React4.useRef(onChange);
8732
+ React4.useEffect(() => {
8733
+ onChangeRef.current = onChange;
8734
+ }, [onChange]);
8735
+ const setOpen = React4.useCallback(
8736
+ (next) => {
8737
+ if (!isControlled) setInternal(next);
8738
+ onChangeRef.current?.(next);
8739
+ },
8740
+ [isControlled]
8741
+ );
8742
+ return [open, setOpen];
8743
+ }
8744
+ function AgentWorkspace({
8745
+ children,
8746
+ leftPanel,
8747
+ rightPanel,
8748
+ leftPanelWidth = 320,
8749
+ rightPanelWidth = 460,
8750
+ defaultLeftPanelOpen = true,
8751
+ defaultRightPanelOpen = true,
8752
+ leftPanelOpen: leftPanelOpenProp,
8753
+ rightPanelOpen: rightPanelOpenProp,
8754
+ onLeftPanelOpenChange,
8755
+ onRightPanelOpenChange,
8756
+ showPanelToggles = true,
8757
+ conversation,
8758
+ conversationOpen: conversationOpenProp,
8759
+ onConversationOpenChange,
8760
+ conversationTitle = "AI Assistant",
8761
+ showConversationHeader = true,
8762
+ composer = true,
8763
+ composerPlaceholder,
8764
+ composerHint,
8765
+ onComposerSubmit,
8766
+ className,
8767
+ contentClassName,
8768
+ conversationClassName,
8769
+ leftPanelClassName,
8770
+ rightPanelClassName,
8771
+ composerClassName
8772
+ }) {
8773
+ const agent = useNativeAgentOptional();
8774
+ const hasLeftPanel = leftPanel !== void 0 && leftPanel !== null;
8775
+ const hasRightPanel = rightPanel !== void 0 && rightPanel !== null;
8776
+ const [leftOpen, setLeftOpen] = useOpenState(
8777
+ leftPanelOpenProp,
8778
+ defaultLeftPanelOpen,
8779
+ onLeftPanelOpenChange
8780
+ );
8781
+ const [rightOpen, setRightOpen] = useOpenState(
8782
+ rightPanelOpenProp,
8783
+ defaultRightPanelOpen,
8784
+ onRightPanelOpenChange
8785
+ );
8786
+ const [conversationOpen, setConversationOpen] = useOpenState(
8787
+ conversationOpenProp,
8788
+ false,
8789
+ onConversationOpenChange
8790
+ );
8791
+ const isActive = agent?.isActive ?? false;
8792
+ const wasActiveRef = React4.useRef(false);
8793
+ React4.useEffect(() => {
8794
+ if (isActive !== wasActiveRef.current) {
8795
+ wasActiveRef.current = isActive;
8796
+ setConversationOpen(isActive);
8797
+ }
8798
+ }, [isActive, setConversationOpen]);
8799
+ const value = React4.useMemo(
8800
+ () => ({
8801
+ hasLeftPanel,
8802
+ hasRightPanel,
8803
+ leftPanelOpen: leftOpen,
8804
+ rightPanelOpen: rightOpen,
8805
+ setLeftPanelOpen: setLeftOpen,
8806
+ setRightPanelOpen: setRightOpen,
8807
+ toggleLeftPanel: () => setLeftOpen(!leftOpen),
8808
+ toggleRightPanel: () => setRightOpen(!rightOpen),
8809
+ conversationOpen,
8810
+ setConversationOpen,
8811
+ openConversation: () => setConversationOpen(true),
8812
+ closeConversation: () => setConversationOpen(false)
8813
+ }),
8814
+ [
8815
+ hasLeftPanel,
8816
+ hasRightPanel,
8817
+ leftOpen,
8818
+ rightOpen,
8819
+ setLeftOpen,
8820
+ setRightOpen,
8821
+ conversationOpen,
8822
+ setConversationOpen
8823
+ ]
8824
+ );
8825
+ const showDock = composer !== false;
8826
+ const dockNode = composer === true || composer === void 0 ? /* @__PURE__ */ jsxRuntime.jsx(
8827
+ AgentWorkspaceComposer,
8828
+ {
8829
+ placeholder: composerPlaceholder,
8830
+ onSubmit: onComposerSubmit
8831
+ }
8832
+ ) : composer;
8833
+ const statusLine = agent ? agent.error ?? agent.statusLabel : null;
8834
+ return /* @__PURE__ */ jsxRuntime.jsx(AgentWorkspaceContext.Provider, { value, children: /* @__PURE__ */ jsxRuntime.jsxs(
8835
+ "div",
8836
+ {
8837
+ "data-conversation-open": conversationOpen || void 0,
8838
+ className: cn(
8839
+ "flex h-full w-full overflow-hidden bg-background text-foreground",
8840
+ className
8841
+ ),
8842
+ children: [
8843
+ hasLeftPanel && /* @__PURE__ */ jsxRuntime.jsx(
8844
+ "aside",
8845
+ {
8846
+ "data-state": leftOpen ? "open" : "collapsed",
8847
+ "aria-hidden": !leftOpen || void 0,
8848
+ className: cn(
8849
+ "relative hidden h-full shrink-0 overflow-hidden border-r border-border transition-[width] duration-300 ease-in-out lg:block",
8850
+ !leftOpen && "border-r-0",
8851
+ leftPanelClassName
8852
+ ),
8853
+ style: { width: leftOpen ? leftPanelWidth : 0 },
8854
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full", style: { width: leftPanelWidth }, children: leftPanel })
8855
+ }
8856
+ ),
8857
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex h-full min-w-0 grow flex-col", children: [
8858
+ showPanelToggles && hasLeftPanel && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute left-3 top-3 z-30 hidden lg:block", children: /* @__PURE__ */ jsxRuntime.jsx(
8859
+ AgentWorkspacePanelToggle,
8860
+ {
8861
+ side: "left",
8862
+ className: "border border-border/60 bg-background/80 shadow-sm backdrop-blur"
8863
+ }
8864
+ ) }),
8865
+ showPanelToggles && hasRightPanel && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-3 top-3 z-30 hidden lg:block", children: /* @__PURE__ */ jsxRuntime.jsx(
8866
+ AgentWorkspacePanelToggle,
8867
+ {
8868
+ side: "right",
8869
+ className: "border border-border/60 bg-background/80 shadow-sm backdrop-blur"
8870
+ }
8871
+ ) }),
8872
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative min-h-0 grow", children: [
8873
+ /* @__PURE__ */ jsxRuntime.jsx(
8874
+ "div",
8875
+ {
8876
+ "aria-hidden": conversationOpen || void 0,
8877
+ ...inertProps(conversationOpen),
8878
+ className: cn("h-full overflow-y-auto", contentClassName),
8879
+ children
8880
+ }
8881
+ ),
8882
+ /* @__PURE__ */ jsxRuntime.jsxs(
8883
+ "div",
8884
+ {
8885
+ "data-state": conversationOpen ? "open" : "closed",
8886
+ "aria-hidden": !conversationOpen || void 0,
8887
+ ...inertProps(!conversationOpen),
8888
+ className: cn(
8889
+ "absolute inset-0 z-20 flex flex-col bg-background transition-[opacity,transform] duration-300 ease-out",
8890
+ conversationOpen ? "translate-y-0 opacity-100" : "pointer-events-none translate-y-8 opacity-0",
8891
+ conversationClassName
8892
+ ),
8893
+ children: [
8894
+ showConversationHeader && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center justify-between gap-3 border-b border-border bg-background px-4 py-2.5", children: [
8895
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 items-center gap-2.5", children: [
8896
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex size-7 shrink-0 items-center justify-center rounded-md bg-primary text-primary-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { className: "size-3.5" }) }),
8897
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
8898
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-sm font-medium", children: conversationTitle }),
8899
+ statusLine && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-xs text-muted-foreground", children: statusLine })
8900
+ ] })
8901
+ ] }),
8902
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [
8903
+ (hasLeftPanel || hasRightPanel) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mr-1 hidden items-center gap-1 border-r border-border pr-2 lg:flex", children: [
8904
+ /* @__PURE__ */ jsxRuntime.jsx(AgentWorkspacePanelToggle, { side: "left" }),
8905
+ /* @__PURE__ */ jsxRuntime.jsx(AgentWorkspacePanelToggle, { side: "right" })
8906
+ ] }),
8907
+ agent && /* @__PURE__ */ jsxRuntime.jsx(
8908
+ "button",
8909
+ {
8910
+ type: "button",
8911
+ onClick: () => agent.reset(),
8912
+ "aria-label": "Start a new conversation",
8913
+ title: "New conversation",
8914
+ className: "flex size-8 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-secondary hover:text-foreground",
8915
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCcw, { className: "size-4" })
8916
+ }
8917
+ ),
8918
+ /* @__PURE__ */ jsxRuntime.jsx(
8919
+ "button",
8920
+ {
8921
+ type: "button",
8922
+ onClick: () => setConversationOpen(false),
8923
+ "aria-label": "Hide conversation",
8924
+ title: "Hide conversation",
8925
+ className: "flex size-8 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-secondary hover:text-foreground",
8926
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "size-4" })
8927
+ }
8928
+ )
8929
+ ] })
8930
+ ] }),
8931
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-0 grow", children: conversation ?? /* @__PURE__ */ jsxRuntime.jsx(
8932
+ AgentConversation,
8933
+ {
8934
+ contentClassName: "mx-auto w-full max-w-3xl",
8935
+ showAvatars: true
8936
+ }
8937
+ ) })
8938
+ ]
8939
+ }
8940
+ )
8941
+ ] }),
8942
+ showDock && /* @__PURE__ */ jsxRuntime.jsx(
8943
+ "div",
8944
+ {
8945
+ className: cn(
8946
+ "relative z-30 shrink-0 border-t border-border bg-background/80 px-4 py-3 backdrop-blur",
8947
+ composerClassName
8948
+ ),
8949
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto w-full max-w-3xl", children: [
8950
+ isActive && !conversationOpen && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-2 flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs(
8951
+ "button",
8952
+ {
8953
+ type: "button",
8954
+ onClick: () => setConversationOpen(true),
8955
+ 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",
8956
+ children: [
8957
+ agent?.isResponding ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "size-3 animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquare, { className: "size-3" }),
8958
+ "View conversation"
8959
+ ]
8960
+ }
8961
+ ) }),
8962
+ dockNode,
8963
+ composerHint && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 text-center text-[11px] text-muted-foreground", children: composerHint })
8964
+ ] })
8965
+ }
8966
+ )
8967
+ ] }),
8968
+ hasRightPanel && /* @__PURE__ */ jsxRuntime.jsx(
8969
+ "aside",
8970
+ {
8971
+ "data-state": rightOpen ? "open" : "collapsed",
8972
+ "aria-hidden": !rightOpen || void 0,
8973
+ className: cn(
8974
+ "relative hidden h-full shrink-0 overflow-hidden border-l border-border transition-[width] duration-300 ease-in-out lg:block",
8975
+ !rightOpen && "border-l-0",
8976
+ rightPanelClassName
8977
+ ),
8978
+ style: { width: rightOpen ? rightPanelWidth : 0 },
8979
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full", style: { width: rightPanelWidth }, children: rightPanel })
8980
+ }
8981
+ )
8982
+ ]
8983
+ }
8984
+ ) });
8985
+ }
8151
8986
 
8152
8987
  exports.AgentAvatar = AgentAvatar;
8988
+ exports.AgentComposer = AgentComposer;
8989
+ exports.AgentConversation = AgentConversation;
8153
8990
  exports.AgentHandoff = AgentHandoff;
8154
8991
  exports.AgentProvider = AgentProvider;
8155
8992
  exports.AgentSurface = AgentSurface;
8993
+ exports.AgentWorkspace = AgentWorkspace;
8994
+ exports.AgentWorkspaceComposer = AgentWorkspaceComposer;
8995
+ exports.AgentWorkspacePanelToggle = AgentWorkspacePanelToggle;
8156
8996
  exports.AllocationBreakdown = AllocationBreakdown;
8157
8997
  exports.AnalyticsDashboard = AnalyticsDashboard;
8158
8998
  exports.Avatar = Avatar;
@@ -8196,6 +9036,8 @@ exports.MessageWithSteps = MessageWithSteps;
8196
9036
  exports.MetricsGrid = MetricsGrid;
8197
9037
  exports.MobileShell = MobileShell;
8198
9038
  exports.MultimodalInput = MultimodalInput;
9039
+ exports.NativeAgentProvider = NativeAgentProvider;
9040
+ exports.NativeSurface = NativeSurface;
8199
9041
  exports.OnboardingWizard = OnboardingWizard;
8200
9042
  exports.OptionCards = OptionCards;
8201
9043
  exports.OverlayModal = OverlayModal;
@@ -8264,6 +9106,10 @@ exports.useAgentBackend = useAgentBackend;
8264
9106
  exports.useAgentInput = useAgentInput;
8265
9107
  exports.useAgentLayout = useAgentLayout;
8266
9108
  exports.useAgentMessages = useAgentMessages;
9109
+ exports.useAgentWorkspace = useAgentWorkspace;
9110
+ exports.useAgentWorkspaceOptional = useAgentWorkspaceOptional;
9111
+ exports.useNativeAgent = useNativeAgent;
9112
+ exports.useNativeAgentOptional = useNativeAgentOptional;
8267
9113
  exports.useSemanticBuilder = useSemanticBuilder;
8268
9114
  //# sourceMappingURL=index.cjs.map
8269
9115
  //# sourceMappingURL=index.cjs.map