@vibes.diy/base 14.2.7 → 14.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,6 +4,7 @@ import { ovalColors } from "./VibesSwitch.styles.js";
4
4
  import { EditorTabStrip } from "./EditorTabStrip.js";
5
5
  import { ViewerStandingView } from "./ViewerStandingView.js";
6
6
  import { OptionButtons } from "./OptionButtons.js";
7
+ import { ChatMessageRow } from "./ChatMessageRow.js";
7
8
  import { ViewerTagView } from "./ViewerTagView.js";
8
9
  import { SignedOutSavePrompt } from "./SignedOutSavePrompt.js";
9
10
  import { HandlePickerMenu } from "./HandlePickerMenu.js";
@@ -13,7 +14,7 @@ import { CodingIndicator } from "./CodingIndicator.js";
13
14
  import { CopyLinkIconButton } from "./LinkLineControls.js";
14
15
  import { AttachmentButton, AttachmentChips } from "./AttachmentRow.js";
15
16
  import { useAttachments } from "../attachments/use-attachments.js";
16
- import { PeopleIcon, PersonIcon } from "./icons/index.js";
17
+ import { PeopleIcon } from "./icons/index.js";
17
18
  import { HOME_BRAND_FONT } from "./homeBrandFont.js";
18
19
  import { SAFE_AREA_TOP } from "../theme/safe-area.js";
19
20
  const UNIFIED_VIBE_CARD_DARK_VARS = `
@@ -25,6 +26,10 @@ const UNIFIED_VIBE_CARD_DARK_VARS = `
25
26
  --color-dark-background-00: #1a1a1a;
26
27
  --color-dark-background-01: #222222;
27
28
  --color-dark-background-02: #222222;
29
+ /* The global field palette follows the OS. Pin the same semantic dark
30
+ values here so an explicit .dark host also darkens fields and borders. */
31
+ --vibes-field-bg: var(--vibes-ink-deep, #141414);
32
+ --vibes-card-border: var(--vibes-gray-mid, #555555);
28
33
  --uvc-card-shadow: rgba(255,255,255,0.75);
29
34
  --uvc-amber-text: #fbbf24;
30
35
  --uvc-blue-text: #93c5fd;
@@ -34,6 +39,7 @@ const UNIFIED_VIBE_CARD_DARK_VARS = `
34
39
  --uvc-suggestion-text: #e5e7eb;
35
40
  --uvc-helper-text: #c2cbd8;
36
41
  --uvc-build-text: #bac4d2;
42
+ --uvc-nav-chat: #c4b5fd;
37
43
  --uvc-nav-edit: #fb923c;
38
44
  --uvc-nav-share: #4ade80;
39
45
  --uvc-nav-faq: #cbd5e1;
@@ -47,10 +53,10 @@ const UNIFIED_VIBE_CARD_DARK_VARS = `
47
53
  --color-light-background-02: var(--color-dark-background-02, #222222);`;
48
54
  const unifiedVibeCardDarkCss = `
49
55
  @media (prefers-color-scheme: dark) {
50
- [data-unified-vibe-card], [data-vibe-handle-menu], [data-vibe-version-menu], [data-vibe-credits-menu] {${UNIFIED_VIBE_CARD_DARK_VARS}
56
+ [data-unified-vibe-card], [data-vibe-handle-menu], [data-vibe-version-menu] {${UNIFIED_VIBE_CARD_DARK_VARS}
51
57
  }
52
58
  }
53
- .dark [data-unified-vibe-card], .dark [data-vibe-handle-menu], .dark [data-vibe-version-menu], .dark [data-vibe-credits-menu] {${UNIFIED_VIBE_CARD_DARK_VARS}
59
+ .dark [data-unified-vibe-card], .dark [data-vibe-handle-menu], .dark [data-vibe-version-menu] {${UNIFIED_VIBE_CARD_DARK_VARS}
54
60
  }
55
61
  /* Composer submit button: a blue pulsating glow while the field holds unsent
56
62
  content, nudging the user to send it — the same "something's live, act on it"
@@ -120,12 +126,9 @@ const unifiedVibeCardDarkCss = `
120
126
  animation: none;
121
127
  }
122
128
  }
123
- /* Bottom-nav circles on narrow phones: five fixed 38px circles + the 120px logo
124
- spacer + 6px gaps need 340px of row width, but a 375px viewport only leaves
125
- 327px after the card insets and row padding — and flex's default shrink would
126
- squash the circles into ovals. The circles are shrink-proof (flexShrink 0 in
127
- NavIcon) and instead step DOWN via these vars so the row keeps fitting, clear
128
- of the floating logo, on common 360–400px phones (Codex P2 on #3037). */
129
+ /* Four shrink-proof nav circles share the footer with the 120px logo spacer.
130
+ Compact size/gap steps keep them clear of the logo on narrow phones without
131
+ flex shrinking circles into ovals. */
129
132
  @media (max-width: 400px) {
130
133
  [data-unified-vibe-card] { --uvc-nav-size: 34px; --uvc-nav-gap: 4px; }
131
134
  }
@@ -154,7 +157,7 @@ const unifiedVibeCardDarkCss = `
154
157
  .vibes-edit-field:focus-within {
155
158
  border-color: var(--vibes-blue);
156
159
  }
157
- /* Rhythm (#6085): the well shares the composer's 14px outer gutter. Quiet
160
+ /* The suggestion well shares the transcript’s attributed content column. Quiet
158
161
  suggestion borders and an opaque invitation distinguish optional actions
159
162
  from the writing field. Gold upgrades retain their semantic color.
160
163
  OptionButtons outside this well keep their own presentation. */
@@ -171,8 +174,24 @@ const unifiedVibeCardDarkCss = `
171
174
  line-height: 1.5;
172
175
  }
173
176
  .vibes-chip-well [data-message-role="brainstorm-options"] {
177
+ display: flex;
178
+ flex-direction: row;
179
+ flex-wrap: wrap;
174
180
  gap: 6px;
175
181
  }
182
+ .vibes-chip-well [data-testid="option-buttons-explainer"] {
183
+ flex: 0 0 100%;
184
+ margin: 0;
185
+ }
186
+ /* Decorated chips keep their adjacent owner control in the same wrapping item. */
187
+ .vibes-chip-well [data-message-role="brainstorm-options"] > div {
188
+ min-width: 0;
189
+ max-width: 100%;
190
+ }
191
+ .vibes-chip-well [data-message-role="brainstorm-options"] > div [data-chip-role="option"] {
192
+ flex: 1 1 auto;
193
+ min-width: 0;
194
+ }
176
195
  .vibes-chip-well [data-chip-role="option"]:not([data-chip-tone="gold"]) {
177
196
  background-color: var(--vibes-field-bg, #ffffff);
178
197
  border: 1px solid var(--uvc-suggestion-border, #a6adb7);
@@ -188,9 +207,10 @@ const unifiedVibeCardDarkCss = `
188
207
  border-radius: 8px;
189
208
  font-size: 13px;
190
209
  line-height: 20px;
191
- padding: 9px 10px;
210
+ padding: 3px 6px;
192
211
  min-height: 40px;
193
212
  text-align: left;
213
+ overflow-wrap: anywhere;
194
214
  }
195
215
  /* The card owns this density; other SubTabStrip and BuildCard consumers keep
196
216
  their defaults. Tab inline sizes need a scoped override, not a shared edit. */
@@ -273,14 +293,14 @@ export function UnifiedVibeCard(props) {
273
293
  cancelHidePress();
274
294
  }
275
295
  };
276
- const pickerInteractive = props.handles !== undefined;
296
+ const pickerInteractive = props.handles !== undefined || props.onAccountSettings !== undefined;
277
297
  const [internalPickerOpen, setInternalPickerOpen] = useState(false);
278
298
  const pickerOpen = props.handlePickerOpen ?? internalPickerOpen;
279
299
  const setPickerOpen = (next) => {
280
300
  if (props.handlePickerOpen === undefined)
281
301
  setInternalPickerOpen(next);
282
302
  props.onHandlePickerOpenChange?.(next);
283
- if (next)
303
+ if (next === false)
284
304
  setCreditsOpen(false);
285
305
  };
286
306
  const pickerWrapRef = useRef(null);
@@ -293,8 +313,16 @@ export function UnifiedVibeCard(props) {
293
313
  if (!pickerWrapRef.current?.contains(target) && !menuRef.current?.contains(target))
294
314
  setPickerOpen(false);
295
315
  };
296
- document.addEventListener("mousedown", onDown);
297
- return () => document.removeEventListener("mousedown", onDown);
316
+ const onBlur = () => {
317
+ if (document.activeElement?.tagName === "IFRAME")
318
+ setPickerOpen(false);
319
+ };
320
+ document.addEventListener("pointerdown", onDown);
321
+ window.addEventListener("blur", onBlur);
322
+ return () => {
323
+ document.removeEventListener("pointerdown", onDown);
324
+ window.removeEventListener("blur", onBlur);
325
+ };
298
326
  }, [pickerOpen]);
299
327
  useEffect(() => {
300
328
  if (open === false && pickerOpen === true)
@@ -307,22 +335,7 @@ export function UnifiedVibeCard(props) {
307
335
  if (props.creditsMenuOpen === undefined)
308
336
  setInternalCreditsOpen(next);
309
337
  props.onCreditsMenuOpenChange?.(next);
310
- if (next)
311
- setPickerOpen(false);
312
338
  };
313
- const creditsWrapRef = useRef(null);
314
- const creditsMenuRef = useRef(null);
315
- useEffect(() => {
316
- if (!creditsOpen)
317
- return;
318
- const onDown = (e) => {
319
- const target = e.target;
320
- if (!creditsWrapRef.current?.contains(target) && !creditsMenuRef.current?.contains(target))
321
- setCreditsOpen(false);
322
- };
323
- document.addEventListener("mousedown", onDown);
324
- return () => document.removeEventListener("mousedown", onDown);
325
- }, [creditsOpen]);
326
339
  useEffect(() => {
327
340
  if (open === false && creditsOpen === true)
328
341
  setCreditsOpen(false);
@@ -440,6 +453,25 @@ export function UnifiedVibeCard(props) {
440
453
  }, [hasHistory, mounted]);
441
454
  const [userHeight, setUserHeight] = useState(null);
442
455
  const cardRef = useRef(null);
456
+ const switchWrapRef = useRef(null);
457
+ useEffect(() => {
458
+ if (open === false)
459
+ return;
460
+ const onOutsideClick = (event) => {
461
+ if (event.defaultPrevented || event.cancelBubble)
462
+ return;
463
+ const path = event.composedPath();
464
+ if ([cardRef.current, switchWrapRef.current].some((node) => node !== null && path.includes(node)))
465
+ return;
466
+ if (path.some((node) => "hasAttribute" in node &&
467
+ typeof node.hasAttribute === "function" &&
468
+ (node.hasAttribute("data-vibe-handle-menu") || node.hasAttribute("data-vibe-version-menu"))))
469
+ return;
470
+ setOpen(false);
471
+ };
472
+ window.addEventListener("click", onOutsideClick);
473
+ return () => window.removeEventListener("click", onOutsideClick);
474
+ }, [open, controlledOpen, onOpenChange]);
443
475
  const dragStateRef = useRef(null);
444
476
  const [dragging, setDragging] = useState(false);
445
477
  const RESIZE_MIN = 220;
@@ -539,14 +571,13 @@ export function UnifiedVibeCard(props) {
539
571
  const resizeValueNow = Math.round(Math.min(Math.max(userHeight ?? cardRef.current?.offsetHeight ?? 0, RESIZE_MIN), resizeValueMax));
540
572
  return (React.createElement(React.Fragment, null,
541
573
  React.createElement("style", null, unifiedVibeCardDarkCss),
542
- open && (React.createElement("div", { "aria-hidden": true, "data-testid": "vibe-menu-backdrop", onClick: () => setOpen(false), style: { position: "fixed", inset: 0, zIndex: 1, pointerEvents: "auto" } })),
543
574
  !hidden && (mounted || retained) && (React.createElement("div", { ref: cardRef, role: "dialog", "aria-label": "Vibe menu", "data-unified-vibe-card": true, className: props.className, "aria-hidden": mounted ? undefined : true, inert: mounted ? undefined : true, style: {
544
575
  position: "absolute",
545
- left: 6,
546
- right: 6,
547
- bottom: 6,
576
+ left: 2,
577
+ right: 2,
578
+ bottom: 2,
548
579
  zIndex: 2,
549
- borderRadius: 16,
580
+ borderRadius: 14,
550
581
  maxHeight: cardHeight,
551
582
  height: cardHeight,
552
583
  overflow: "hidden",
@@ -573,7 +604,7 @@ export function UnifiedVibeCard(props) {
573
604
  display: "flex",
574
605
  alignItems: "center",
575
606
  gap: 8,
576
- padding: "14px 14px 10px",
607
+ padding: "10px 10px 10px",
577
608
  flexShrink: 0,
578
609
  position: "relative",
579
610
  userSelect: "none",
@@ -612,10 +643,18 @@ export function UnifiedVibeCard(props) {
612
643
  props.appIconUrl && (React.createElement("img", { src: props.appIconUrl, alt: "", style: { width: "100%", height: "100%", objectFit: "cover", transform: "scale(2)" } })),
613
644
  React.createElement(CodingIndicator, { active: props.coding === true })),
614
645
  React.createElement("div", { style: { lineHeight: 1.2, minWidth: 0, flex: "1 1 auto" } },
615
- React.createElement("strong", { style: { fontFamily: HOME_BRAND_FONT, display: "block", fontSize: 13, lineHeight: 1.3, overflowWrap: "anywhere" } }, appTitle),
646
+ React.createElement("strong", { style: {
647
+ fontFamily: HOME_BRAND_FONT,
648
+ display: "block",
649
+ fontSize: 13,
650
+ lineHeight: 1.3,
651
+ overflow: "hidden",
652
+ textOverflow: "ellipsis",
653
+ whiteSpace: "nowrap",
654
+ } }, appTitle),
616
655
  appSlug &&
617
- (props.onOpenApp === undefined && props.onCopyAppUrl === undefined ? (React.createElement("div", { className: "text-light-secondary dark:text-dark-secondary", style: { fontSize: 12 } }, appSlug)) : (React.createElement("div", { "data-vibe-card-header-controls": "true", "data-testid": "vibe-card-slug-line", style: { position: "relative", zIndex: 1, display: "flex", alignItems: "center", gap: 4, minWidth: 0 } },
618
- props.onOpenApp === undefined ? (React.createElement("span", { className: "text-light-secondary dark:text-dark-secondary", style: { fontSize: 12 } }, appSlug)) : (React.createElement("button", { type: "button", "data-testid": "vibe-card-slug-link", onClick: props.onOpenApp, className: "text-light-secondary dark:text-dark-secondary", style: {
656
+ (props.onOpenApp === undefined && props.onCopyAppUrl === undefined ? (React.createElement("div", { className: "text-light-secondary dark:text-dark-secondary", style: { fontSize: 12, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, appSlug)) : (React.createElement("div", { "data-vibe-card-header-controls": "true", "data-testid": "vibe-card-slug-line", style: { position: "relative", zIndex: 1, display: "flex", alignItems: "center", gap: 4, minWidth: 0 } },
657
+ props.onOpenApp === undefined ? (React.createElement("span", { className: "text-light-secondary dark:text-dark-secondary", style: { fontSize: 12, minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, appSlug)) : (React.createElement("button", { type: "button", "data-testid": "vibe-card-slug-link", onClick: props.onOpenApp, className: "text-light-secondary dark:text-dark-secondary", style: {
619
658
  font: "inherit",
620
659
  fontSize: 12,
621
660
  lineHeight: "20px",
@@ -649,18 +688,20 @@ export function UnifiedVibeCard(props) {
649
688
  alignItems: "center",
650
689
  gap: 10,
651
690
  flexShrink: 0,
652
- } },
653
- creditsEnabled && (React.createElement("div", { ref: creditsWrapRef, style: { position: "relative", flexShrink: 0, display: "flex" } },
654
- React.createElement(CreditsRingView, { remaining: props.credits?.remaining, grantIncluded: props.credits?.grantIncluded, loading: props.credits?.loading, expanded: creditsOpen, onClick: () => setCreditsOpen(!creditsOpen) }))),
655
- props.hideViewerTag ? null : props.handleSlug ? (React.createElement("div", { ref: pickerWrapRef, style: { position: "relative", flexShrink: 0 } },
656
- React.createElement(ViewerTagView, { slug: props.handleSlug, displayName: `@${props.handleSlug}`, avatarUrl: props.handleAvatarUrl, editable: Boolean(props.onPickAvatar), onPickFile: props.onPickAvatar, onTagClick: pickerInteractive ? () => setPickerOpen(!pickerOpen) : undefined, tagExpanded: pickerOpen, style: {
657
- background: "var(--color-light-background-01, #eee)",
658
- border: "1px solid var(--color-light-decorative-01, #ddd)",
659
- color: "var(--color-light-primary, #333)",
660
- fontSize: 13,
661
- padding: "3px 8px 3px 4px",
662
- flexShrink: 0,
663
- } }))) : props.viewerPending ? (React.createElement(ViewerTagView, { slug: "", pending: true })) : props.viewerNeedsHandle ? (React.createElement(ViewerTagView, { slug: "", needsHandle: true, onChooseHandle: props.onChooseHandle })) : (React.createElement(ViewerTagView, { slug: "?", anonymous: true, onSignIn: props.onSignIn })))),
691
+ maxWidth: "38%",
692
+ minWidth: 0,
693
+ } }, props.hideViewerTag ? null : props.handleSlug ? (React.createElement("div", { ref: pickerWrapRef, style: { position: "relative", minWidth: 0, maxWidth: "100%" } },
694
+ React.createElement(ViewerTagView, { slug: props.handleSlug, displayName: `@${props.handleSlug}`, avatarUrl: props.handleAvatarUrl, editable: Boolean(props.onPickAvatar), onPickFile: props.onPickAvatar, onTagClick: pickerInteractive ? () => setPickerOpen(!pickerOpen) : undefined, tagExpanded: pickerOpen, tagLabel: "Account and handles", style: {
695
+ background: "var(--color-light-background-01, #eee)",
696
+ border: "1px solid var(--color-light-decorative-01, #ddd)",
697
+ color: "var(--color-light-primary, #333)",
698
+ fontSize: 13,
699
+ padding: "3px 8px 3px 4px",
700
+ minWidth: 0,
701
+ maxWidth: "100%",
702
+ } }))) : props.viewerPending ? (React.createElement(ViewerTagView, { slug: "", pending: true })) : props.viewerNeedsHandle && !pickerInteractive ? (React.createElement(ViewerTagView, { slug: "", needsHandle: true, onChooseHandle: props.onChooseHandle })) : props.viewerNeedsHandle ? (React.createElement("div", { ref: pickerWrapRef },
703
+ React.createElement(ViewerTagView, { slug: "?", displayName: "Account", onTagClick: () => setPickerOpen(!pickerOpen), tagExpanded: pickerOpen, tagLabel: "Account and handles" }))) : pickerInteractive ? (React.createElement("div", { ref: pickerWrapRef },
704
+ React.createElement(ViewerTagView, { slug: "?", displayName: "Account", onTagClick: () => setPickerOpen(!pickerOpen), tagExpanded: pickerOpen, tagLabel: "Account and handles" }))) : (React.createElement(ViewerTagView, { slug: "?", anonymous: true, onSignIn: props.onSignIn })))),
664
705
  (() => {
665
706
  const wizardBanner = (props.publishWizardActive === true || props.publishWizardError === true) && props.onPublish;
666
707
  if (wizardBanner && props.selectedNav !== "share") {
@@ -689,13 +730,9 @@ export function UnifiedVibeCard(props) {
689
730
  return (React.createElement("div", { style: { padding: "0 14px", flexShrink: 0 } },
690
731
  React.createElement(PublishBanner, { variant: props.publishState === "draft" || props.publishState === "old" ? props.publishState : undefined, onPublish: props.onPublish, publishing: props.publishing })));
691
732
  }
692
- const isOwnerChrome = props.publishAccess !== undefined || props.publishAccessPending === true;
693
- if (!isOwnerChrome)
694
- return null;
695
- return (React.createElement("div", { style: { padding: "0 14px", flexShrink: 0 } },
696
- React.createElement(AudienceLine, { access: props.publishAccess, pending: props.publishAccessPending, onShare: props.onShare })));
733
+ return null;
697
734
  })(),
698
- props.editorTab != null && props.selectedNav !== "share" && !agentPanelActive && (React.createElement("div", { "data-vibe-card-editor-tabs": true, style: { padding: "0 14px 6px", flexShrink: 0 } },
735
+ props.editorTab != null && props.editorTab !== "edit-chat" && props.selectedNav !== "share" && !agentPanelActive && (React.createElement("div", { "data-vibe-card-editor-tabs": true, style: { padding: "0 14px 6px", flexShrink: 0 } },
699
736
  React.createElement(EditorTabStrip, { tab: props.editorTab, onTab: (t) => props.onEditorTab?.(t), standing: {
700
737
  viewerMode: props.viewerMode,
701
738
  memberReadOnly: props.memberReadOnly,
@@ -719,44 +756,49 @@ export function UnifiedVibeCard(props) {
719
756
  ...((props.bodyFill && props.body) || agentPanelActive || props.body == null
720
757
  ? { display: "flex", flexDirection: "column" }
721
758
  : null),
722
- } }, agentPanelActive ? (React.createElement("div", { style: { flex: "1 1 auto", minHeight: 0 } }, props.agentPanel)) : props.editorTab === "standing" ? (React.createElement(ViewerStandingView, { viewerMode: props.viewerMode, memberReadOnly: props.memberReadOnly, adminMode: props.adminMode, versionLocked: props.versionLocked, publishState: props.publishState, publishAccess: props.publishAccess, myGrant: props.myGrant, onToggleAdmin: props.onToggleAdmin, onUnlock: props.onUnlock })) : props.body != null ? (props.bodyFill ? (React.createElement("div", { style: { flex: "1 1 auto", minHeight: 0 } }, props.body)) : (props.body)) : (React.createElement(React.Fragment, null,
723
- React.createElement("div", { ref: chipsRegionRef, style: streamFillActive
724
- ? { flex: "1 1 auto", minHeight: 0, display: "flex", flexDirection: "column" }
725
- : { flex: "1 0 auto", minHeight: 0 } },
726
- props.historyBody && (React.createElement("div", { "data-testid": "vibe-card-history", "aria-hidden": props.streamBody ? true : undefined, inert: props.streamBody ? true : undefined, style: { marginBottom: 8, ...(props.streamBody ? { display: "none" } : null) } }, props.historyBody)),
727
- React.createElement("div", { "data-testid": "vibe-card-stream-region", style: {
728
- position: "relative",
729
- ...(chipBuildLive ? { display: "flex", flexDirection: "column" } : {}),
730
- ...(streamFillActive ? { flex: "1 1 auto", minHeight: 0 } : null),
731
- } },
732
- props.streamBody && (React.createElement("div", { "data-testid": streamLive ? undefined : "vibe-card-stream-settled", style: streamLive
733
- ? {
734
- ...(chipBuildLive ? { flex: "1 1 auto" } : { position: "absolute", inset: 0 }),
735
- display: "flex",
736
- flexDirection: "column",
737
- minHeight: 0,
738
- }
739
- : { marginBottom: 8 } },
740
- React.createElement("div", { style: streamLive ? { flex: "1 1 auto", minHeight: 0, overflowY: "auto" } : undefined }, props.streamBody),
741
- props.liveDuringBuild === true && React.createElement(PendingMessages, { messages: queued }))),
742
- React.createElement("div", { "aria-hidden": streamLive && !chipBuildLive ? true : undefined, inert: streamLive && !chipBuildLive ? true : undefined, style: streamLive && !chipBuildLive ? { visibility: "hidden" } : { flexShrink: 0 } }, (props.seedOffer || props.upgradeChips || (visibleChips && visibleChips.length > 0)) && (React.createElement("div", { className: "vibes-chip-well mt-2 flex flex-col", "data-testid": "vibe-chip-set" },
743
- (props.seedOffer || props.upgradeChips) && (React.createElement("div", { className: "flex flex-col gap-[6px]", inert: props.activeChip !== undefined },
744
- props.seedOffer,
745
- props.upgradeChips)),
746
- visibleChips && visibleChips.length > 0 && (React.createElement(OptionButtons, { options: visibleChips, activeOption: props.activeChip, inSet: true, isFirst: props.activeChip === undefined, firstMessage: props.isOwner
747
- ? "Describe a change to edit this app live:"
748
- : "Describe a change to remix your own copy of this app:", onSelect: (o) => {
749
- const r = props.onSelectChip?.(o);
750
- return r && typeof r === "object" && "then" in r ? r.then((ok) => ok ?? false) : false;
751
- }, decorate: (option) => decorateChip(option, props) })))))))))),
759
+ } },
760
+ props.retainedEditor != null && (React.createElement("div", { hidden: !props.editorVisible || agentPanelActive, inert: !props.editorVisible || agentPanelActive ? true : undefined, style: props.editorVisible && !agentPanelActive ? { flex: "1 1 auto", minHeight: 0 } : { display: "none" } }, props.retainedEditor)),
761
+ agentPanelActive ? (React.createElement("div", { style: { flex: "1 1 auto", minHeight: 0 } }, props.agentPanel)) : props.editorVisible && props.retainedEditor != null ? null : props.editorTab === "standing" ? (React.createElement(ViewerStandingView, { viewerMode: props.viewerMode, memberReadOnly: props.memberReadOnly, adminMode: props.adminMode, versionLocked: props.versionLocked, publishState: props.publishState, publishAccess: props.publishAccess, myGrant: props.myGrant, onToggleAdmin: props.onToggleAdmin, onUnlock: props.onUnlock })) : props.body != null ? (props.bodyFill ? (React.createElement("div", { style: { flex: "1 1 auto", minHeight: 0 } }, props.body)) : (props.body)) : (React.createElement(React.Fragment, null,
762
+ React.createElement("div", { ref: chipsRegionRef, style: streamFillActive
763
+ ? { flex: "1 1 auto", minHeight: 0, display: "flex", flexDirection: "column" }
764
+ : { flex: "1 0 auto", minHeight: 0 } },
765
+ props.historyBody && (React.createElement("div", { "data-testid": "vibe-card-history", "aria-hidden": props.streamBody ? true : undefined, inert: props.streamBody ? true : undefined, style: { marginBottom: 8, ...(props.streamBody ? { display: "none" } : null) } }, props.historyBody)),
766
+ React.createElement("div", { "data-testid": "vibe-card-stream-region", style: {
767
+ position: "relative",
768
+ ...(chipBuildLive ? { display: "flex", flexDirection: "column" } : {}),
769
+ ...(streamFillActive ? { flex: "1 1 auto", minHeight: 0 } : null),
770
+ } },
771
+ props.streamBody && (React.createElement("div", { "data-testid": streamLive ? undefined : "vibe-card-stream-settled", style: streamLive
772
+ ? {
773
+ ...(chipBuildLive ? { flex: "1 1 auto" } : { position: "absolute", inset: 0 }),
774
+ display: "flex",
775
+ flexDirection: "column",
776
+ minHeight: 0,
777
+ }
778
+ : { marginBottom: 8 } },
779
+ React.createElement("div", { style: streamLive ? { flex: "1 1 auto", minHeight: 0, overflowY: "auto" } : undefined }, props.streamBody),
780
+ props.liveDuringBuild === true && React.createElement(PendingMessages, { messages: queued }))),
781
+ React.createElement("div", { "aria-hidden": streamLive && !chipBuildLive ? true : undefined, inert: streamLive && !chipBuildLive ? true : undefined, style: streamLive && !chipBuildLive ? { visibility: "hidden" } : { flexShrink: 0 } }, (props.seedOffer || props.upgradeChips || (visibleChips && visibleChips.length > 0)) && (React.createElement("div", { className: "mx-auto mt-2 w-full max-w-5xl" },
782
+ React.createElement(ChatMessageRow, { role: "ai", rowAttrs: { "data-message-role": "suggestions" } },
783
+ React.createElement("div", { className: "vibes-chip-well flex flex-col", "data-testid": "vibe-chip-set" },
784
+ (props.seedOffer || props.upgradeChips) && (React.createElement("div", { className: "flex flex-col gap-[6px]", inert: props.activeChip !== undefined },
785
+ props.seedOffer,
786
+ props.upgradeChips)),
787
+ visibleChips && visibleChips.length > 0 && (React.createElement(OptionButtons, { options: visibleChips, activeOption: props.activeChip, inSet: true, isFirst: props.activeChip === undefined, firstMessage: props.isOwner
788
+ ? "Suggested requests:"
789
+ : "Describe a change to remix your own copy of this app:", onSelect: (o) => {
790
+ const r = props.onSelectChip?.(o);
791
+ return r && typeof r === "object" && "then" in r ? r.then((ok) => ok ?? false) : false;
792
+ }, decorate: (option) => decorateChip(option, props) }))))))),
793
+ props.streamFooter !== undefined && React.createElement("div", { className: "mx-auto w-full max-w-5xl" }, props.streamFooter)))))),
752
794
  props.body == null && !agentPanelActive && (React.createElement("div", { style: { flexShrink: 0, padding: "0 14px 10px" } },
753
795
  React.createElement(OtherRow, { value: draft, onValueChange: setDraft, queued: queued, onQueuedChange: setQueued, onSubmitOther: props.onSubmitOther, generating: props.generating, liveDuringBuild: props.liveDuringBuild, canSendNow: props.canSendNow, onStop: props.onStop, stopDisabled: props.stopDisabled, attachmentsCtl: attachmentsCtl }))),
754
- agentPanelActive ? (React.createElement("div", { "aria-hidden": true, style: { display: "flex", flexShrink: 0, padding: "0 12px 4px" } },
796
+ agentPanelActive ? (React.createElement("div", { "aria-hidden": true, style: { display: "flex", flexShrink: 0, padding: "0 8px" } },
755
797
  React.createElement("div", { style: { width: 120, height: 50, marginLeft: "auto" } }))) : (React.createElement("div", { style: {
756
798
  display: "flex",
757
799
  alignItems: "center",
758
800
  gap: "var(--uvc-nav-gap, 6px)",
759
- padding: "0 12px 4px",
801
+ padding: "0 8px",
760
802
  borderTop: "1px solid var(--color-light-decorative-00, #e5e5e5)",
761
803
  flexShrink: 0,
762
804
  } },
@@ -764,52 +806,74 @@ export function UnifiedVibeCard(props) {
764
806
  React.createElement("svg", { width: "17", height: "17", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.2, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true" },
765
807
  React.createElement("path", { d: "M3 12l9-9 9 9" }),
766
808
  React.createElement("path", { d: "M5 10v10a1 1 0 001 1h4v-6h4v6h4a1 1 0 001-1V10" }))),
767
- React.createElement(NavIcon, { label: "Account", color: "#7c3aed", selected: props.selectedNav === "recommended", onClick: props.onRecommended },
768
- React.createElement(PersonIcon, null)),
769
- React.createElement(NavIcon, { label: "Edit", color: "var(--uvc-nav-edit, #c2410c)", selected: ["edit", "edit-chat", "standing", "code", "data", "style", "settings"].includes(props.selectedNav ?? "edit"), onClick: props.onEdit },
809
+ React.createElement(NavIcon, { label: "Chat", color: "var(--uvc-nav-chat, #7c3aed)", selected: props.selectedNav === "edit-chat", onClick: props.onChat },
810
+ React.createElement("svg", { width: "17", height: "17", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, "aria-hidden": "true" },
811
+ React.createElement("path", { d: "M21 11a8 8 0 01-8 8H7l-4 3V11a9 9 0 0118 0Z" }))),
812
+ React.createElement(NavIcon, { label: "Edit", color: "var(--uvc-nav-edit, #c2410c)", selected: ["edit", "standing", "code", "files", "data", "style", "settings", "faq"].includes(props.selectedNav ?? "edit"), onClick: props.onEdit },
770
813
  React.createElement("svg", { width: "17", height: "17", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.2, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true" },
771
814
  React.createElement("path", { d: "M12 20h9" }),
772
815
  React.createElement("path", { d: "M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4 12.5-12.5z" }))),
773
- React.createElement(NavIcon, { label: "Share", color: "var(--uvc-nav-share, #15803d)", selected: props.selectedNav === "share", onClick: props.onShare, buttonRef: props.shareButtonRef, badgeCount: (props.shareCommentCount ?? 0) + (props.pendingRequestCount ?? 0) },
816
+ React.createElement(NavIcon, { label: "Share", color: "var(--uvc-nav-share, #15803d)", selected: props.selectedNav === "share", onClick: props.onShare, buttonRef: props.shareButtonRef, badgeCount: props.pendingRequestCount ?? 0 },
774
817
  React.createElement(PeopleIcon, null)),
775
- React.createElement(NavIcon, { label: "FAQ", color: "var(--uvc-nav-faq, #4b5563)", selected: props.selectedNav === "faq", onClick: props.onAbout }, "?"),
776
818
  React.createElement("div", { "aria-hidden": true, style: { width: 120, height: 50, flexShrink: 0, marginLeft: "auto" } })))))),
777
- open && pickerInteractive && pickerOpen && props.handleSlug && (React.createElement("div", { ref: menuRef, "data-vibe-handle-menu": true, className: "text-light-primary dark:text-dark-primary", style: {
819
+ open && pickerInteractive && pickerOpen && (React.createElement("div", { ref: menuRef, "data-vibe-handle-menu": true, className: "text-light-primary dark:text-dark-primary", style: {
778
820
  position: "fixed",
779
821
  top: `calc(${SAFE_AREA_TOP} + 16px)`,
780
822
  right: 3,
781
823
  zIndex: 4,
782
824
  pointerEvents: "auto",
783
- } },
784
- React.createElement(HandlePickerMenu, { handles: props.handles ?? [], activeSlug: props.handleSlug, busy: props.handlePickerBusy, style: { position: "static", right: "auto", top: "auto", bottom: "auto" }, onSelect: (slug) => {
785
- props.onSelectHandle?.(slug);
825
+ } }, React.createElement(HandlePickerMenu, { handles: props.handles ?? [], activeSlug: props.handleSlug, busy: props.handlePickerBusy, header: creditsEnabled ? (React.createElement("div", { style: { padding: 8 } },
826
+ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 10 } },
827
+ React.createElement(CreditsRingView, { remaining: props.credits?.remaining, grantIncluded: props.credits?.grantIncluded, loading: props.credits?.loading, expanded: creditsOpen, onClick: () => setCreditsOpen(!creditsOpen) }),
828
+ React.createElement("span", null, "Credits")),
829
+ creditsOpen && (React.createElement(CreditsDetailMenu, { plan: props.credits?.plan, remaining: props.credits?.remaining, grantRemaining: props.credits?.grantRemaining, grantIncluded: props.credits?.grantIncluded, grantResetsAt: props.credits?.grantResetsAt, purchasedRemaining: props.credits?.purchasedRemaining, loading: props.credits?.loading, style: {
830
+ position: "static",
831
+ right: "auto",
832
+ top: "auto",
833
+ bottom: "auto",
834
+ width: "100%",
835
+ minWidth: 0,
836
+ boxShadow: "none",
837
+ border: "none",
838
+ padding: 0,
839
+ }, onManage: props.onManageCredits
840
+ ? () => {
841
+ props.onManageCredits?.();
842
+ setCreditsOpen(false);
843
+ }
844
+ : undefined })))) : undefined, onSignIn: props.onSignIn, onChooseHandle: props.viewerNeedsHandle ? props.onChooseHandle : undefined, onSettings: props.onAccountSettings
845
+ ? () => {
846
+ props.onAccountSettings?.();
786
847
  setPickerOpen(false);
787
- }, onNewHandle: (handle) => {
848
+ }
849
+ : undefined, onMyVibes: props.onMyVibes
850
+ ? () => {
851
+ props.onMyVibes?.();
852
+ setPickerOpen(false);
853
+ }
854
+ : undefined, onMemberships: props.onMemberships
855
+ ? () => {
856
+ props.onMemberships?.();
857
+ setPickerOpen(false);
858
+ }
859
+ : undefined, style: { position: "static", right: "auto", top: "auto", bottom: "auto" }, onSelect: (slug) => {
860
+ props.onSelectHandle?.(slug);
861
+ setPickerOpen(false);
862
+ }, onNewHandle: props.onNewHandle
863
+ ? (handle) => {
788
864
  props.onNewHandle?.(handle);
789
865
  setPickerOpen(false);
790
- }, onLogout: props.onLogout
791
- ? () => {
792
- props.onLogout?.();
793
- setPickerOpen(false);
794
- }
795
- : undefined }))),
796
- open && creditsEnabled && creditsOpen && (React.createElement("div", { ref: creditsMenuRef, "data-vibe-credits-menu": true, className: "text-light-primary dark:text-dark-primary", style: {
797
- position: "fixed",
798
- top: `calc(${SAFE_AREA_TOP} + 16px)`,
799
- right: 3,
800
- zIndex: 4,
801
- pointerEvents: "auto",
802
- } },
803
- React.createElement(CreditsDetailMenu, { plan: props.credits?.plan, remaining: props.credits?.remaining, grantRemaining: props.credits?.grantRemaining, grantIncluded: props.credits?.grantIncluded, grantResetsAt: props.credits?.grantResetsAt, purchasedRemaining: props.credits?.purchasedRemaining, loading: props.credits?.loading, style: { position: "static", right: "auto", top: "auto", bottom: "auto" }, onManage: props.onManageCredits
804
- ? () => {
805
- props.onManageCredits?.();
806
- setCreditsOpen(false);
807
- }
808
- : undefined }))),
809
- !hidden && (React.createElement("div", { style: {
866
+ }
867
+ : undefined, onLogout: props.onLogout
868
+ ? () => {
869
+ props.onLogout?.();
870
+ setPickerOpen(false);
871
+ }
872
+ : undefined }))),
873
+ !hidden && (React.createElement("div", { ref: switchWrapRef, style: {
810
874
  position: "absolute",
811
- right: 16,
812
- bottom: 15,
875
+ right: 2,
876
+ bottom: 7,
813
877
  zIndex: 3,
814
878
  pointerEvents: "auto",
815
879
  display: "flex",
@@ -1110,31 +1174,6 @@ function PublishBanner({ variant, onPublish, publishing, locked, onUnlock, autoP
1110
1174
  opacity: publishing ? 0.6 : 1,
1111
1175
  } }, publishing ? "Publishing…" : variant === "draft" ? "Update" : "Publish this version"))));
1112
1176
  }
1113
- function AudienceLine({ access, pending, onShare, }) {
1114
- return (React.createElement("div", { "data-testid": "vibe-card-audience-line", className: "text-light-secondary dark:text-dark-secondary", style: {
1115
- display: "flex",
1116
- alignItems: "baseline",
1117
- gap: 6,
1118
- marginBottom: 10,
1119
- fontSize: 12,
1120
- lineHeight: 1.4,
1121
- } },
1122
- React.createElement("span", null, pending === true
1123
- ? "Checking who can open it…"
1124
- : access === "public"
1125
- ? "Anyone with the link can open it."
1126
- : "Only people you approve can open it."),
1127
- pending !== true && onShare && (React.createElement("button", { type: "button", onClick: onShare, "aria-label": "Change who can open this vibe", style: {
1128
- flexShrink: 0,
1129
- background: "transparent",
1130
- border: "none",
1131
- padding: 0,
1132
- font: "inherit",
1133
- color: "currentColor",
1134
- textDecoration: "underline",
1135
- cursor: "pointer",
1136
- } }, "Change"))));
1137
- }
1138
1177
  function decorateChip(option, props) {
1139
1178
  const fp = props.chipFastPaths?.[option];
1140
1179
  if (!fp)