@vibes.diy/base 14.3.0 → 14.3.1
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/attachments/composer-file-drop.d.ts +11 -0
- package/attachments/composer-file-drop.js +35 -0
- package/attachments/composer-file-drop.js.map +1 -0
- package/components/BuildCard.d.ts +0 -6
- package/components/BuildCard.js +5 -9
- package/components/BuildCard.js.map +1 -1
- package/components/EditorTabStrip.js +2 -2
- package/components/EditorTabStrip.js.map +1 -1
- package/components/SharePanelView.d.ts +2 -1
- package/components/SharePanelView.js +3 -4
- package/components/SharePanelView.js.map +1 -1
- package/components/SubTabStrip.d.ts +2 -1
- package/components/SubTabStrip.js +7 -5
- package/components/SubTabStrip.js.map +1 -1
- package/components/UnifiedVibeCard.d.ts +2 -0
- package/components/UnifiedVibeCard.js +482 -218
- package/components/UnifiedVibeCard.js.map +1 -1
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/index.js.map +1 -1
- package/package.json +4 -4
|
@@ -14,10 +14,13 @@ import { CodingIndicator } from "./CodingIndicator.js";
|
|
|
14
14
|
import { CopyLinkIconButton } from "./LinkLineControls.js";
|
|
15
15
|
import { AttachmentButton, AttachmentChips } from "./AttachmentRow.js";
|
|
16
16
|
import { useAttachments } from "../attachments/use-attachments.js";
|
|
17
|
+
import { composerFileDrop } from "../attachments/composer-file-drop.js";
|
|
17
18
|
import { PeopleIcon } from "./icons/index.js";
|
|
18
19
|
import { HOME_BRAND_FONT } from "./homeBrandFont.js";
|
|
19
20
|
import { SAFE_AREA_TOP } from "../theme/safe-area.js";
|
|
20
21
|
const UNIFIED_VIBE_CARD_DARK_VARS = `
|
|
22
|
+
--uvc-closed-build-bg: #28262f;
|
|
23
|
+
--uvc-closed-build-text: #b5afc2;
|
|
21
24
|
--color-dark-primary: #e0e0e0;
|
|
22
25
|
--color-dark-secondary: #aaaaaa;
|
|
23
26
|
--color-dark-decorative-00: #333333;
|
|
@@ -30,7 +33,6 @@ const UNIFIED_VIBE_CARD_DARK_VARS = `
|
|
|
30
33
|
values here so an explicit .dark host also darkens fields and borders. */
|
|
31
34
|
--vibes-field-bg: var(--vibes-ink-deep, #141414);
|
|
32
35
|
--vibes-card-border: var(--vibes-gray-mid, #555555);
|
|
33
|
-
--uvc-card-shadow: rgba(255,255,255,0.75);
|
|
34
36
|
--uvc-amber-text: #fbbf24;
|
|
35
37
|
--uvc-blue-text: #93c5fd;
|
|
36
38
|
--uvc-purple-text: #c4b5fd;
|
|
@@ -39,7 +41,6 @@ const UNIFIED_VIBE_CARD_DARK_VARS = `
|
|
|
39
41
|
--uvc-suggestion-text: #e5e7eb;
|
|
40
42
|
--uvc-helper-text: #c2cbd8;
|
|
41
43
|
--uvc-build-text: #bac4d2;
|
|
42
|
-
--uvc-nav-chat: #c4b5fd;
|
|
43
44
|
--uvc-nav-edit: #fb923c;
|
|
44
45
|
--uvc-nav-share: #4ade80;
|
|
45
46
|
--uvc-nav-faq: #cbd5e1;
|
|
@@ -51,12 +52,200 @@ const UNIFIED_VIBE_CARD_DARK_VARS = `
|
|
|
51
52
|
--color-light-background-00: var(--color-dark-background-00, #1a1a1a);
|
|
52
53
|
--color-light-background-01: var(--color-dark-background-01, #222222);
|
|
53
54
|
--color-light-background-02: var(--color-dark-background-02, #222222);`;
|
|
55
|
+
const UNIFIED_VIBE_CARD_GLASS_LIGHT_VARS = `
|
|
56
|
+
--uvc-submit-arrow:var(--vibes-cream, #fffff0);
|
|
57
|
+
--uvc-submit-fill:rgba(64,89,114,.2);
|
|
58
|
+
--uvc-notch-width: 52px;
|
|
59
|
+
--uvc-notch-opacity: .5;
|
|
60
|
+
--uvc-notch-fill: repeating-linear-gradient(0deg,rgba(255,255,255,.1) 0 .4px,transparent .4px 1px),linear-gradient(180deg,rgba(16,26,24,.65),rgba(16,26,24,.4) 30%,rgba(16,26,24,.9) 72%,rgba(16,26,24,.55));
|
|
61
|
+
--uvc-notch-shadow: inset 0 1px 0 rgba(255,255,255,.45),inset 0 -1px 1px rgba(0,0,0,.2),0 1px 2px rgba(16,26,24,.18);
|
|
62
|
+
--uvc-composer-border: rgba(16,26,24,.28);
|
|
63
|
+
--uvc-glass-fill: rgba(255,255,255,.15);
|
|
64
|
+
--uvc-glass-edge: rgba(255,255,255,.3);
|
|
65
|
+
--uvc-glass-highlight: rgba(255,255,255,.8);
|
|
66
|
+
--uvc-glass-shadow: 0 8px 32px rgba(0,0,0,.1), inset 0 1px 0 rgba(255,255,255,.5), inset 0 -1px 0 rgba(255,255,255,.1), inset 0 0 20px 10px rgba(255,255,255,1);
|
|
67
|
+
--color-light-background-00: rgba(255,255,255,.24);
|
|
68
|
+
--color-light-background-01: rgba(255,255,255,.2);
|
|
69
|
+
--uvc-suggestion-bg: rgba(255,255,255,.28);
|
|
70
|
+
--vibes-field-bg: rgba(255,255,255,.2);
|
|
71
|
+
--uvc-nav-home: #2563eb;`;
|
|
72
|
+
const UNIFIED_VIBE_CARD_GLASS_DARK_VARS = `
|
|
73
|
+
--uvc-submit-arrow:#aaa;
|
|
74
|
+
--uvc-submit-fill:#000;
|
|
75
|
+
--uvc-notch-opacity: .25;
|
|
76
|
+
--uvc-notch-width: 52px;
|
|
77
|
+
--uvc-notch-fill: repeating-linear-gradient(90deg,rgba(255,255,255,.18) 0 .6px,transparent .6px 2px),linear-gradient(180deg,rgba(230,255,245,.6),rgba(230,255,245,.3) 40%,rgba(210,239,228,.12) 58%,rgba(210,239,228,.2));
|
|
78
|
+
--uvc-notch-shadow: inset 0 0 0 1px rgba(230,255,245,.2),0 1px 2px rgba(0,0,0,.3);
|
|
79
|
+
--uvc-composer-border: rgba(210,239,228,.28);
|
|
80
|
+
--uvc-glass-fill: rgba(16,26,24,.72);
|
|
81
|
+
--uvc-glass-edge: rgba(210,239,228,.28);
|
|
82
|
+
--uvc-glass-highlight: rgba(230,255,245,.32);
|
|
83
|
+
--uvc-glass-shadow: 0 8px 32px rgba(0,0,0,.24), inset 0 1px 0 rgba(255,255,255,.22), inset 0 -1px 0 rgba(255,255,255,.05), inset 0 0 20px 4px rgba(210,239,228,.07);
|
|
84
|
+
--color-light-background-00: rgba(16,26,24,.28);
|
|
85
|
+
--color-light-background-01: rgba(16,26,24,.24);
|
|
86
|
+
--uvc-suggestion-bg: rgba(16,26,24,.32);
|
|
87
|
+
--vibes-field-bg: rgba(16,26,24,.28);
|
|
88
|
+
--uvc-nav-home: #93b4ff;`;
|
|
89
|
+
const UNIFIED_VIBE_CARD_OBSIDIAN_VARS = `
|
|
90
|
+
--uvc-glass-fill: rgba(6,9,10,.94);
|
|
91
|
+
--uvc-glass-edge: rgba(255,255,255,.12);
|
|
92
|
+
--uvc-glass-highlight: rgba(255,255,255,.25);
|
|
93
|
+
--uvc-glass-shadow: 0 4px 16px rgba(0,0,0,.28), inset 0 1px 0 rgba(255,255,255,.12), inset 0 -1px 0 rgba(255,255,255,.035), inset 0 0 12px rgba(255,255,255,.02);
|
|
94
|
+
--uvc-nav-home: #93b4ff;`;
|
|
54
95
|
const unifiedVibeCardDarkCss = `
|
|
96
|
+
[data-unified-vibe-card] {${UNIFIED_VIBE_CARD_GLASS_LIGHT_VARS}
|
|
97
|
+
}
|
|
55
98
|
@media (prefers-color-scheme: dark) {
|
|
56
|
-
[data-unified-vibe-card], [data-vibe-handle-menu], [data-vibe-version-menu] {${UNIFIED_VIBE_CARD_DARK_VARS}
|
|
99
|
+
[data-unified-vibe-card], [data-vibe-handle-menu], [data-vibe-version-menu], [data-vibe-closed-build-status] {${UNIFIED_VIBE_CARD_DARK_VARS}
|
|
100
|
+
}
|
|
101
|
+
[data-unified-vibe-card] {${UNIFIED_VIBE_CARD_GLASS_DARK_VARS}
|
|
57
102
|
}
|
|
58
103
|
}
|
|
59
|
-
.dark [data-unified-vibe-card], .dark [data-vibe-handle-menu], .dark [data-vibe-version-menu] {${UNIFIED_VIBE_CARD_DARK_VARS}
|
|
104
|
+
.dark [data-unified-vibe-card], .dark [data-vibe-handle-menu], .dark [data-vibe-version-menu], .dark [data-vibe-closed-build-status] {${UNIFIED_VIBE_CARD_DARK_VARS}
|
|
105
|
+
}
|
|
106
|
+
.dark [data-unified-vibe-card] {${UNIFIED_VIBE_CARD_GLASS_DARK_VARS}
|
|
107
|
+
}
|
|
108
|
+
/* Navigation uses clear glass in light mode and Obsidian in dark mode.
|
|
109
|
+
Give the navigation light strokes without retheming the card. */
|
|
110
|
+
[data-vibe-card-nav] {${UNIFIED_VIBE_CARD_DARK_VARS}
|
|
111
|
+
}
|
|
112
|
+
[data-vibe-switch-glass] {${UNIFIED_VIBE_CARD_OBSIDIAN_VARS}
|
|
113
|
+
}
|
|
114
|
+
[data-vibe-card-nav] {
|
|
115
|
+
--uvc-glass-fill:rgba(255,255,255,.08);
|
|
116
|
+
--uvc-glass-edge:rgba(255,255,255,.3);
|
|
117
|
+
--uvc-glass-highlight:rgba(255,255,255,.8);
|
|
118
|
+
--uvc-glass-shadow:0 4px 16px rgba(0,0,0,.1),inset 0 1px 0 rgba(255,255,255,.35),inset 0 -1px 0 rgba(255,255,255,.08);
|
|
119
|
+
--uvc-nav-home:#93b4ff;
|
|
120
|
+
}
|
|
121
|
+
/* The card's layout still includes its footer. Only its glass surface ends
|
|
122
|
+
at the former divider: retain the outer 1px border and footer's 1px top
|
|
123
|
+
border as transparent spacing so the circles and switch never move. */
|
|
124
|
+
[data-unified-vibe-card]::before {
|
|
125
|
+
content: ""; position: absolute; inset: -1px -1px 50px; border-radius: 14px;
|
|
126
|
+
}
|
|
127
|
+
/* The agent panel retains its existing 50px switch reservation, without the
|
|
128
|
+
ordinary nav row's extra divider pixel. Its body ends one pixel lower. */
|
|
129
|
+
[data-unified-vibe-card][data-vibe-agent-panel="true"]::before { bottom: 49px; }
|
|
130
|
+
[data-vibe-card-nav] { position: relative; isolation: isolate; }
|
|
131
|
+
[data-vibe-card-nav]::before {
|
|
132
|
+
content: ""; position: absolute; left: 2px; top: 50%; transform: translateY(-50%); z-index: -1;
|
|
133
|
+
width: calc(3 * var(--uvc-nav-size, 38px) + 2 * var(--uvc-nav-gap, 6px) + 12px);
|
|
134
|
+
height: calc(var(--uvc-nav-size, 38px) + 10px); border-radius: 999px;
|
|
135
|
+
}
|
|
136
|
+
[data-unified-vibe-card]::before, [data-vibe-card-nav]::before, [data-vibe-switch-glass]::before {
|
|
137
|
+
box-sizing: border-box; border: 1px solid transparent; pointer-events: none;
|
|
138
|
+
}
|
|
139
|
+
[data-unified-vibe-card]::before, [data-vibe-card-nav]::before, [data-vibe-switch-glass]::before {
|
|
140
|
+
background: linear-gradient(to right, transparent, var(--uvc-glass-highlight), transparent) top / 100% 1px no-repeat,
|
|
141
|
+
linear-gradient(to bottom, var(--uvc-glass-highlight), transparent, var(--uvc-glass-edge)) left / 1px 100% no-repeat,
|
|
142
|
+
var(--uvc-glass-fill);
|
|
143
|
+
backdrop-filter: blur(24px); -webkit-backdrop-filter: blur(24px);
|
|
144
|
+
box-shadow: var(--uvc-glass-shadow);
|
|
145
|
+
}
|
|
146
|
+
[data-vibe-card-nav]::before { backdrop-filter:blur(8px); -webkit-backdrop-filter:blur(8px); }
|
|
147
|
+
@media (prefers-color-scheme: dark) {
|
|
148
|
+
[data-vibe-card-nav] {${UNIFIED_VIBE_CARD_OBSIDIAN_VARS}}
|
|
149
|
+
[data-vibe-card-nav]::before { backdrop-filter:blur(24px); -webkit-backdrop-filter:blur(24px); }
|
|
150
|
+
}
|
|
151
|
+
.dark [data-vibe-card-nav] {${UNIFIED_VIBE_CARD_OBSIDIAN_VARS}}
|
|
152
|
+
.dark [data-vibe-card-nav]::before { backdrop-filter:blur(24px); -webkit-backdrop-filter:blur(24px); }
|
|
153
|
+
[data-vibe-card-nav] [data-vibe-nav-selected="true"] svg { stroke-width:3; }
|
|
154
|
+
/* The arrival uses the fitted glass surface, leaving the brand artwork untouched.
|
|
155
|
+
Keying the layer by status replays same-color arrivals without a JS timer. */
|
|
156
|
+
[data-vibe-switch-glass]:has([data-vibe-switch-highlight])::before { visibility:hidden; }
|
|
157
|
+
[data-vibe-switch-rest] {
|
|
158
|
+
background:linear-gradient(to right,transparent,var(--uvc-glass-highlight),transparent) top/100% 1px no-repeat,
|
|
159
|
+
linear-gradient(to bottom,var(--uvc-glass-highlight),transparent,var(--uvc-glass-edge)) left/1px 100% no-repeat,var(--uvc-glass-fill);
|
|
160
|
+
box-shadow:var(--uvc-glass-shadow);
|
|
161
|
+
animation:uvc-notification-rest 7s forwards;
|
|
162
|
+
}
|
|
163
|
+
[data-vibe-switch-highlight] {
|
|
164
|
+
--uvc-arrival-fill:color-mix(in srgb,currentColor 80%,transparent);
|
|
165
|
+
--uvc-arrival-edge:color-mix(in srgb,color-mix(in srgb,currentColor 50%,white) 60%,transparent);
|
|
166
|
+
--uvc-arrival-highlight:color-mix(in srgb,color-mix(in srgb,currentColor 20%,white) 90%,transparent);
|
|
167
|
+
background:linear-gradient(to right,transparent,var(--uvc-arrival-highlight),transparent) top/100% 1px no-repeat,
|
|
168
|
+
linear-gradient(to bottom,var(--uvc-arrival-highlight),transparent,var(--uvc-arrival-edge)) left/1px 100% no-repeat,var(--uvc-arrival-fill);
|
|
169
|
+
box-shadow:0 4px 16px rgba(0,0,0,.2),inset 0 1px 0 var(--uvc-arrival-highlight),inset 0 -1px 0 var(--uvc-arrival-edge),inset 0 0 12px rgba(255,255,255,.06);
|
|
170
|
+
animation:uvc-notification-glass 7s forwards;
|
|
171
|
+
}
|
|
172
|
+
/* Preserve the original Amber material for the yellow/pending status. */
|
|
173
|
+
[data-vibe-switch-highlight][data-arrival-amber="true"] {
|
|
174
|
+
--uvc-arrival-fill:rgba(191,142,66,.8);
|
|
175
|
+
--uvc-arrival-edge:rgba(255,229,167,.6);
|
|
176
|
+
--uvc-arrival-highlight:rgba(255,246,218,.9);
|
|
177
|
+
}
|
|
178
|
+
[data-vibe-switch-highlight], [data-vibe-switch-rest] {
|
|
179
|
+
box-sizing:border-box; border:1px solid transparent; pointer-events:none;
|
|
180
|
+
backdrop-filter:blur(24px); -webkit-backdrop-filter:blur(24px);
|
|
181
|
+
}
|
|
182
|
+
@keyframes uvc-notification-rest {
|
|
183
|
+
0%,100% { opacity:1; }
|
|
184
|
+
14.3%,71.4% { opacity:0; }
|
|
185
|
+
}
|
|
186
|
+
@keyframes uvc-notification-glass {
|
|
187
|
+
0% { opacity:0; }
|
|
188
|
+
14.3%,71.4% { opacity:1; }
|
|
189
|
+
100% { opacity:0; }
|
|
190
|
+
}
|
|
191
|
+
@media (prefers-reduced-motion:reduce) {
|
|
192
|
+
[data-vibe-switch-highlight] { animation-name:uvc-notification-glass-reduced; }
|
|
193
|
+
[data-vibe-switch-rest] { animation-name:uvc-notification-rest-reduced; }
|
|
194
|
+
}
|
|
195
|
+
@keyframes uvc-notification-rest-reduced {
|
|
196
|
+
0%,71.4% { opacity:0; }
|
|
197
|
+
100% { opacity:1; }
|
|
198
|
+
}
|
|
199
|
+
@keyframes uvc-notification-glass-reduced {
|
|
200
|
+
0%,71.4% { opacity:1; }
|
|
201
|
+
100% { opacity:0; }
|
|
202
|
+
}
|
|
203
|
+
/* Fit the painted 102px logo, excluding the SVG's transparent side margins.
|
|
204
|
+
Match the nav oval's height without moving the existing 120×44 hit target. */
|
|
205
|
+
[data-vibe-switch-glass] { isolation: isolate; }
|
|
206
|
+
[data-vibe-switch-glass]::before, [data-vibe-switch-highlight], [data-vibe-switch-rest] {
|
|
207
|
+
content: ""; position: absolute; left: 6px; right: 6px; top: calc(50% + 1px);
|
|
208
|
+
height: calc(var(--uvc-nav-size, 38px) + 10px); transform: translateY(-50%);
|
|
209
|
+
border-radius: 999px; z-index: -1;
|
|
210
|
+
}
|
|
211
|
+
[data-unified-vibe-card] form.vibes-edit-field {
|
|
212
|
+
--vibes-card-border: var(--uvc-composer-border);
|
|
213
|
+
}
|
|
214
|
+
/* The selected Frost header rule fades at both ends at half opacity,
|
|
215
|
+
without consuming any of the card's content height. */
|
|
216
|
+
[data-vibe-card-header]::after {
|
|
217
|
+
content: ""; position: absolute; bottom: 0; left: 0; right: 0; height: 1px;
|
|
218
|
+
background: linear-gradient(90deg, transparent, var(--uvc-glass-edge) 22%, var(--uvc-glass-edge) 78%, transparent);
|
|
219
|
+
opacity: .5; pointer-events: none;
|
|
220
|
+
}
|
|
221
|
+
/* A · Line: the clip edge sits under the painted switch, inside the 120px
|
|
222
|
+
hit box (whose SVG has transparent air at its left). 22px padding leaves
|
|
223
|
+
the resting strip at right:132px without changing the switch geometry. */
|
|
224
|
+
[data-vibe-closed-build-status] {
|
|
225
|
+
position: absolute; left: 12px; right: 110px; bottom: 17px;
|
|
226
|
+
z-index: 2; display: flex; justify-content: flex-end; padding-right: 22px;
|
|
227
|
+
overflow: hidden; pointer-events: none; box-sizing: border-box;
|
|
228
|
+
}
|
|
229
|
+
[data-vibe-closed-build-line] {
|
|
230
|
+
min-width: 0; max-width: 100%; box-sizing: border-box;
|
|
231
|
+
padding: 3px 5px; border-radius: 3px;
|
|
232
|
+
background: var(--uvc-closed-build-bg, #fff);
|
|
233
|
+
color: var(--uvc-closed-build-text, #6d6973);
|
|
234
|
+
font: 12px/18px Inter, system-ui, sans-serif;
|
|
235
|
+
transform: translateX(calc(100% + 22px));
|
|
236
|
+
transition: transform 240ms cubic-bezier(.22,.68,0,1);
|
|
237
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
238
|
+
}
|
|
239
|
+
[data-vibe-closed-build-line][data-revealed="true"] { transform: translateX(0); }
|
|
240
|
+
[data-vibe-closed-build-line] [data-testid="thinking-line"] {
|
|
241
|
+
display: flex; align-items: baseline; gap: 6px; min-width: 0;
|
|
242
|
+
}
|
|
243
|
+
[data-vibe-closed-build-line] [data-testid="thinking-line-glyph"] { flex-shrink: 0; }
|
|
244
|
+
[data-vibe-closed-build-line] [data-testid="thinking-line-verb"] {
|
|
245
|
+
min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
246
|
+
}
|
|
247
|
+
@media (prefers-reduced-motion: reduce) {
|
|
248
|
+
[data-vibe-closed-build-line] { transition: none; }
|
|
60
249
|
}
|
|
61
250
|
/* Composer submit button: a blue pulsating glow while the field holds unsent
|
|
62
251
|
content, nudging the user to send it — the same "something's live, act on it"
|
|
@@ -126,14 +315,14 @@ const unifiedVibeCardDarkCss = `
|
|
|
126
315
|
animation: none;
|
|
127
316
|
}
|
|
128
317
|
}
|
|
129
|
-
/*
|
|
318
|
+
/* Three shrink-proof nav circles (Home / Edit / Share) share the footer with the 120px logo spacer.
|
|
130
319
|
Compact size/gap steps keep them clear of the logo on narrow phones without
|
|
131
320
|
flex shrinking circles into ovals. */
|
|
132
321
|
@media (max-width: 400px) {
|
|
133
|
-
[data-unified-vibe-card] { --uvc-nav-size: 34px; --uvc-nav-gap: 4px; }
|
|
322
|
+
[data-unified-vibe-card], [data-vibe-switch-glass] { --uvc-nav-size: 34px; --uvc-nav-gap: 4px; }
|
|
134
323
|
}
|
|
135
324
|
@media (max-width: 360px) {
|
|
136
|
-
[data-unified-vibe-card] { --uvc-nav-size: 30px; }
|
|
325
|
+
[data-unified-vibe-card], [data-vibe-switch-glass] { --uvc-nav-size: 30px; }
|
|
137
326
|
}
|
|
138
327
|
/* Composer input row — a FIELD, not a button (Storybook audition C2, #4578).
|
|
139
328
|
It used to wear a hard 2px/3px offset shadow over the card's own grey, which
|
|
@@ -220,7 +409,7 @@ const unifiedVibeCardDarkCss = `
|
|
|
220
409
|
}
|
|
221
410
|
[data-vibe-card-editor-tabs] [role="tab"] {
|
|
222
411
|
min-height: 36px;
|
|
223
|
-
padding-inline:
|
|
412
|
+
padding-inline: 3px !important;
|
|
224
413
|
font-size: 14px !important;
|
|
225
414
|
}
|
|
226
415
|
[data-unified-vibe-card] [data-testid="build-card"] {
|
|
@@ -253,8 +442,63 @@ const unifiedVibeCardDarkCss = `
|
|
|
253
442
|
}`;
|
|
254
443
|
const CARD_VIEWPORT_HEIGHT = "80%";
|
|
255
444
|
const CARD_FULL_VIEWPORT_HEIGHT = "94%";
|
|
445
|
+
function ClosedBuildStatus({ visible, content }) {
|
|
446
|
+
const [retainedContent, setRetainedContent] = useState(visible ? content : undefined);
|
|
447
|
+
const [revealed, setRevealed] = useState(false);
|
|
448
|
+
const [reducedMotion, setReducedMotion] = useState(false);
|
|
449
|
+
useEffect(() => {
|
|
450
|
+
if (typeof window.matchMedia !== "function")
|
|
451
|
+
return;
|
|
452
|
+
const media = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
453
|
+
function updateMotion() {
|
|
454
|
+
setReducedMotion(media.matches);
|
|
455
|
+
}
|
|
456
|
+
updateMotion();
|
|
457
|
+
media.addEventListener("change", updateMotion);
|
|
458
|
+
return () => media.removeEventListener("change", updateMotion);
|
|
459
|
+
}, []);
|
|
460
|
+
useEffect(() => {
|
|
461
|
+
if (visible)
|
|
462
|
+
setRetainedContent(content);
|
|
463
|
+
}, [visible, content]);
|
|
464
|
+
useEffect(() => {
|
|
465
|
+
let firstFrame;
|
|
466
|
+
let secondFrame;
|
|
467
|
+
let exitTimer;
|
|
468
|
+
if (visible) {
|
|
469
|
+
if (reducedMotion)
|
|
470
|
+
setRevealed(true);
|
|
471
|
+
else
|
|
472
|
+
firstFrame = requestAnimationFrame(() => {
|
|
473
|
+
secondFrame = requestAnimationFrame(() => setRevealed(true));
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
else {
|
|
477
|
+
setRevealed(false);
|
|
478
|
+
if (reducedMotion)
|
|
479
|
+
setRetainedContent(undefined);
|
|
480
|
+
else
|
|
481
|
+
exitTimer = setTimeout(() => setRetainedContent(undefined), 240);
|
|
482
|
+
}
|
|
483
|
+
return () => {
|
|
484
|
+
if (firstFrame !== undefined)
|
|
485
|
+
cancelAnimationFrame(firstFrame);
|
|
486
|
+
if (secondFrame !== undefined)
|
|
487
|
+
cancelAnimationFrame(secondFrame);
|
|
488
|
+
clearTimeout(exitTimer);
|
|
489
|
+
};
|
|
490
|
+
}, [visible, reducedMotion]);
|
|
491
|
+
if (retainedContent === undefined || retainedContent === null)
|
|
492
|
+
return undefined;
|
|
493
|
+
return (React.createElement("div", { "data-vibe-closed-build-status": true, "data-testid": "closed-build-status", "aria-hidden": visible ? undefined : true, inert: !visible },
|
|
494
|
+
React.createElement("div", { "data-vibe-closed-build-line": true, "data-revealed": revealed }, retainedContent)));
|
|
495
|
+
}
|
|
256
496
|
export function UnifiedVibeCard(props) {
|
|
257
497
|
const { appTitle, appSlug, isTwinkling, isTeasing, open: controlledOpen, onOpenChange } = props;
|
|
498
|
+
const switchGlassColor = props.switchOvalColor ??
|
|
499
|
+
(props.versionLocked || props.publishState === "draft" || props.onViewDraft !== undefined
|
|
500
|
+
? ovalColors.yellow
|
|
501
|
+
: ovalColors.neutral);
|
|
258
502
|
const [internalOpen, setInternalOpen] = useState(false);
|
|
259
503
|
const open = controlledOpen ?? internalOpen;
|
|
260
504
|
const setOpen = (next) => {
|
|
@@ -413,10 +657,11 @@ export function UnifiedVibeCard(props) {
|
|
|
413
657
|
setEverOpened(true);
|
|
414
658
|
}, [open]);
|
|
415
659
|
const retained = Boolean(props.keepMounted) && everOpened;
|
|
416
|
-
const
|
|
660
|
+
const internalScrollRegionRef = useRef(null);
|
|
661
|
+
const scrollRegionRef = props.scrollContainerRef ?? internalScrollRegionRef;
|
|
417
662
|
const chipsRegionRef = useRef(null);
|
|
418
663
|
const stickToBottomRef = useRef(true);
|
|
419
|
-
const hasHistory = props.historyBody != null && props.body == null;
|
|
664
|
+
const hasHistory = (props.historyBody != null || props.streamBody != null) && props.body == null;
|
|
420
665
|
const streamLive = props.streamBody != null && props.streamSettled !== true;
|
|
421
666
|
const chipBuildLive = streamLive && props.activeChip !== undefined;
|
|
422
667
|
const visibleChips = props.activeChip === undefined || props.chips?.slice(0, 3).includes(props.activeChip) === true
|
|
@@ -571,7 +816,8 @@ export function UnifiedVibeCard(props) {
|
|
|
571
816
|
const resizeValueNow = Math.round(Math.min(Math.max(userHeight ?? cardRef.current?.offsetHeight ?? 0, RESIZE_MIN), resizeValueMax));
|
|
572
817
|
return (React.createElement(React.Fragment, null,
|
|
573
818
|
React.createElement("style", null, unifiedVibeCardDarkCss),
|
|
574
|
-
|
|
819
|
+
React.createElement(ClosedBuildStatus, { visible: open === false && hidden === false && props.closedBuildStatus !== undefined && props.closedBuildStatus !== null, content: props.closedBuildStatus }),
|
|
820
|
+
!hidden && (mounted || retained) && (React.createElement("div", { ref: cardRef, role: "dialog", "aria-label": "Vibe menu", "data-unified-vibe-card": true, "data-vibe-agent-panel": agentPanelActive, className: props.className, "aria-hidden": mounted ? undefined : true, inert: mounted ? undefined : true, style: {
|
|
575
821
|
position: "absolute",
|
|
576
822
|
left: 2,
|
|
577
823
|
right: 2,
|
|
@@ -580,12 +826,11 @@ export function UnifiedVibeCard(props) {
|
|
|
580
826
|
borderRadius: 14,
|
|
581
827
|
maxHeight: cardHeight,
|
|
582
828
|
height: cardHeight,
|
|
583
|
-
overflow: "
|
|
829
|
+
overflow: "visible",
|
|
584
830
|
display: "flex",
|
|
585
831
|
flexDirection: "column",
|
|
586
|
-
background: "
|
|
587
|
-
border: "1px solid
|
|
588
|
-
boxShadow: "0 8px 40px var(--uvc-card-shadow, rgba(0,0,0,0.75))",
|
|
832
|
+
background: "transparent",
|
|
833
|
+
border: "1px solid transparent",
|
|
589
834
|
transformOrigin: "bottom right",
|
|
590
835
|
transform: grown ? "scale(1)" : "scale(0)",
|
|
591
836
|
transition: dragging ? scaleTransition : `${scaleTransition}, max-height 0.24s ease, height 0.24s ease`,
|
|
@@ -593,6 +838,7 @@ export function UnifiedVibeCard(props) {
|
|
|
593
838
|
...(mounted ? null : { visibility: "hidden" }),
|
|
594
839
|
} },
|
|
595
840
|
React.createElement("div", { "data-vibe-card-content": true, style: {
|
|
841
|
+
position: "relative",
|
|
596
842
|
display: "flex",
|
|
597
843
|
flexDirection: "column",
|
|
598
844
|
minHeight: 0,
|
|
@@ -600,216 +846,229 @@ export function UnifiedVibeCard(props) {
|
|
|
600
846
|
opacity: grown ? 1 : 0,
|
|
601
847
|
transition: grown ? "opacity 0.18s ease 0.14s" : "opacity 0.1s ease",
|
|
602
848
|
}, className: "text-light-primary dark:text-dark-primary" },
|
|
603
|
-
React.createElement("div", { style: {
|
|
849
|
+
React.createElement("div", { "data-vibe-card-body": true, style: {
|
|
604
850
|
display: "flex",
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
userSelect: "none",
|
|
851
|
+
flexDirection: "column",
|
|
852
|
+
flex: "1 1 auto",
|
|
853
|
+
minHeight: 0,
|
|
854
|
+
overflow: "hidden",
|
|
855
|
+
borderRadius: 13,
|
|
611
856
|
} },
|
|
612
|
-
React.createElement("div", { "data-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
bottom: 0,
|
|
618
|
-
zIndex: 0,
|
|
619
|
-
cursor: "ns-resize",
|
|
620
|
-
touchAction: "none",
|
|
621
|
-
} },
|
|
622
|
-
React.createElement("span", { "aria-hidden": true, style: {
|
|
623
|
-
position: "absolute",
|
|
624
|
-
top: 4,
|
|
625
|
-
left: "50%",
|
|
626
|
-
transform: "translateX(-50%)",
|
|
627
|
-
width: 44,
|
|
628
|
-
height: 4,
|
|
629
|
-
borderRadius: 999,
|
|
630
|
-
background: "var(--color-light-decorative-01, #ddd)",
|
|
631
|
-
} })),
|
|
632
|
-
React.createElement("div", { style: {
|
|
633
|
-
position: "relative",
|
|
634
|
-
width: 30,
|
|
635
|
-
height: 30,
|
|
636
|
-
borderRadius: 8,
|
|
857
|
+
React.createElement("div", { "data-vibe-card-header": true, style: {
|
|
858
|
+
display: "flex",
|
|
859
|
+
alignItems: "center",
|
|
860
|
+
gap: 8,
|
|
861
|
+
padding: "10px 10px 10px",
|
|
637
862
|
flexShrink: 0,
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
border: "1px solid rgba(0,0,0,0.15)",
|
|
641
|
-
pointerEvents: "none",
|
|
863
|
+
position: "relative",
|
|
864
|
+
userSelect: "none",
|
|
642
865
|
} },
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
866
|
+
React.createElement("div", { "data-testid": "vibe-card-resize-handle", role: "separator", "aria-orientation": "horizontal", "aria-label": "Resize chat height", "aria-valuemin": RESIZE_MIN, "aria-valuemax": resizeValueMax, "aria-valuenow": resizeValueNow, tabIndex: 0, onPointerDown: onHandlePointerDown, onPointerMove: onHandlePointerMove, onPointerUp: endHandleDrag, onPointerCancel: cancelHandleDrag, onKeyDown: onHandleKeyDown, onDoubleClick: () => clearForcedHeight(), style: {
|
|
867
|
+
position: "absolute",
|
|
868
|
+
top: 0,
|
|
869
|
+
left: 0,
|
|
870
|
+
right: 0,
|
|
871
|
+
bottom: 0,
|
|
872
|
+
zIndex: 0,
|
|
873
|
+
cursor: "ns-resize",
|
|
874
|
+
touchAction: "none",
|
|
875
|
+
} },
|
|
876
|
+
React.createElement("span", { "aria-hidden": true, style: {
|
|
877
|
+
position: "absolute",
|
|
878
|
+
top: 4,
|
|
879
|
+
left: "50%",
|
|
880
|
+
transform: "translateX(-50%)",
|
|
881
|
+
width: "var(--uvc-notch-width, 44px)",
|
|
882
|
+
height: 4,
|
|
883
|
+
borderRadius: 999,
|
|
884
|
+
background: "var(--uvc-notch-fill)",
|
|
885
|
+
boxShadow: "var(--uvc-notch-shadow)",
|
|
886
|
+
opacity: "var(--uvc-notch-opacity)",
|
|
887
|
+
} })),
|
|
888
|
+
React.createElement("div", { style: {
|
|
889
|
+
position: "relative",
|
|
890
|
+
width: 30,
|
|
891
|
+
height: 30,
|
|
892
|
+
borderRadius: 8,
|
|
893
|
+
flexShrink: 0,
|
|
651
894
|
overflow: "hidden",
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
895
|
+
background: "var(--vibes-cream, #fffff0)",
|
|
896
|
+
border: "1px solid rgba(0,0,0,0.15)",
|
|
897
|
+
pointerEvents: "none",
|
|
898
|
+
} },
|
|
899
|
+
props.appIconUrl && (React.createElement("img", { src: props.appIconUrl, alt: "", style: { width: "100%", height: "100%", objectFit: "cover", transform: "scale(2)" } })),
|
|
900
|
+
React.createElement(CodingIndicator, { active: props.coding === true })),
|
|
901
|
+
React.createElement("div", { style: { lineHeight: 1.2, minWidth: 0, flex: "1 1 auto" } },
|
|
902
|
+
React.createElement("strong", { style: {
|
|
903
|
+
fontFamily: HOME_BRAND_FONT,
|
|
904
|
+
display: "block",
|
|
905
|
+
fontSize: 13,
|
|
906
|
+
lineHeight: 1.3,
|
|
907
|
+
overflow: "hidden",
|
|
908
|
+
textOverflow: "ellipsis",
|
|
909
|
+
whiteSpace: "nowrap",
|
|
910
|
+
} }, appTitle),
|
|
911
|
+
appSlug &&
|
|
912
|
+
(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 } },
|
|
913
|
+
props.onOpenApp === undefined ? (React.createElement("span", { className: "text-light-secondary dark:text-dark-secondary", style: {
|
|
914
|
+
fontSize: 12,
|
|
915
|
+
minWidth: 0,
|
|
916
|
+
overflow: "hidden",
|
|
917
|
+
textOverflow: "ellipsis",
|
|
918
|
+
whiteSpace: "nowrap",
|
|
919
|
+
} }, appSlug)) : (React.createElement("button", { type: "button", "data-testid": "vibe-card-slug-link", onClick: props.onOpenApp, className: "text-light-secondary dark:text-dark-secondary", style: {
|
|
920
|
+
font: "inherit",
|
|
921
|
+
fontSize: 12,
|
|
922
|
+
lineHeight: "20px",
|
|
923
|
+
overflow: "hidden",
|
|
924
|
+
textOverflow: "ellipsis",
|
|
925
|
+
whiteSpace: "nowrap",
|
|
926
|
+
minWidth: 0,
|
|
927
|
+
textDecoration: "underline",
|
|
928
|
+
textUnderlineOffset: 2,
|
|
929
|
+
background: "none",
|
|
930
|
+
border: "none",
|
|
931
|
+
padding: 0,
|
|
932
|
+
cursor: "pointer",
|
|
933
|
+
textAlign: "left",
|
|
934
|
+
} }, appSlug)),
|
|
935
|
+
props.onCopyAppUrl !== undefined && (React.createElement(CopyLinkIconButton, { onCopy: props.onCopyAppUrl, color: "currentColor" })))))),
|
|
936
|
+
(() => {
|
|
937
|
+
const badges = [
|
|
938
|
+
props.publishState === "old" ? React.createElement(DraftBadge, { key: "old", variant: "old" }) : null,
|
|
939
|
+
props.publishState === "draft" && !props.versionLocked ? React.createElement(DraftBadge, { key: "draft", variant: "draft" }) : null,
|
|
940
|
+
props.versionLocked ? React.createElement(LockedBadge, { key: "locked" }) : null,
|
|
941
|
+
!props.publishState && props.onViewDraft && !props.versionLocked ? (React.createElement(ViewDraftBadge, { key: "view-draft", onClick: props.onViewDraft })) : null,
|
|
942
|
+
].filter(Boolean);
|
|
943
|
+
return badges.length === 0 ? null : (React.createElement("div", { "data-vibe-card-header-controls": "true", style: { position: "relative", zIndex: 1, display: "flex", alignItems: "center", gap: 10, flexShrink: 0 } }, badges));
|
|
944
|
+
})(),
|
|
945
|
+
React.createElement("div", { "data-vibe-card-header-controls": "true", style: {
|
|
946
|
+
position: "relative",
|
|
947
|
+
zIndex: 1,
|
|
948
|
+
marginLeft: "auto",
|
|
949
|
+
display: "flex",
|
|
950
|
+
alignItems: "center",
|
|
951
|
+
gap: 10,
|
|
952
|
+
flexShrink: 0,
|
|
953
|
+
maxWidth: "38%",
|
|
954
|
+
minWidth: 0,
|
|
955
|
+
} }, props.hideViewerTag ? null : props.handleSlug ? (React.createElement("div", { ref: pickerWrapRef, style: { position: "relative", minWidth: 0, maxWidth: "100%" } },
|
|
956
|
+
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: {
|
|
957
|
+
background: "var(--color-light-background-01, #eee)",
|
|
958
|
+
border: "1px solid var(--color-light-decorative-01, #ddd)",
|
|
959
|
+
color: "var(--color-light-primary, #333)",
|
|
960
|
+
fontSize: 13,
|
|
961
|
+
padding: "3px 8px 3px 4px",
|
|
962
|
+
minWidth: 0,
|
|
963
|
+
maxWidth: "100%",
|
|
964
|
+
} }))) : 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 },
|
|
965
|
+
React.createElement(ViewerTagView, { slug: "?", displayName: "Account", onTagClick: () => setPickerOpen(!pickerOpen), tagExpanded: pickerOpen, tagLabel: "Account and handles" }))) : pickerInteractive ? (React.createElement("div", { ref: pickerWrapRef },
|
|
966
|
+
React.createElement(ViewerTagView, { slug: "?", displayName: "Account", onTagClick: () => setPickerOpen(!pickerOpen), tagExpanded: pickerOpen, tagLabel: "Account and handles" }))) : (React.createElement(ViewerTagView, { slug: "?", anonymous: true, onSignIn: props.onSignIn })))),
|
|
674
967
|
(() => {
|
|
675
|
-
const
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
968
|
+
const wizardBanner = (props.publishWizardActive === true || props.publishWizardError === true) && props.onPublish;
|
|
969
|
+
if (wizardBanner && props.selectedNav !== "share") {
|
|
970
|
+
return (React.createElement("div", { style: { padding: "0 14px", flexShrink: 0 } },
|
|
971
|
+
React.createElement(PublishBanner, { variant: props.publishState === "draft" || props.publishState === "old" ? props.publishState : undefined, onPublish: props.onPublish, publishing: props.publishing, wizardActive: props.publishWizardActive, wizardError: props.publishWizardError, escapeRevealed: props.publishEscapeRevealed, onPublishWithoutAccess: props.onPublishWithoutAccess, onRetryWizard: props.onRetryWizard })));
|
|
972
|
+
}
|
|
973
|
+
if (props.autoPublishError === true && props.selectedNav !== "share") {
|
|
974
|
+
return (React.createElement("div", { style: { padding: "0 14px", flexShrink: 0 } },
|
|
975
|
+
React.createElement(PublishBanner, { variant: undefined, autoPublishError: true, onRetryAutoPublish: props.onRetryAutoPublish })));
|
|
976
|
+
}
|
|
977
|
+
if (props.statusBanner && props.selectedNav !== "share") {
|
|
978
|
+
return React.createElement("div", { style: { padding: "0 14px", flexShrink: 0 } }, props.statusBanner);
|
|
979
|
+
}
|
|
980
|
+
if (props.versionLocked === true && props.selectedNav !== "share") {
|
|
981
|
+
return (React.createElement("div", { style: { padding: "0 14px", flexShrink: 0 } },
|
|
982
|
+
React.createElement(PublishBanner, { variant: undefined, locked: true, onUnlock: props.onUnlock })));
|
|
983
|
+
}
|
|
984
|
+
if (props.signedOutSavePrompt && props.selectedNav !== "share") {
|
|
985
|
+
return (React.createElement("div", { style: { padding: "0 14px", flexShrink: 0 } },
|
|
986
|
+
React.createElement(SignedOutSavePrompt, { onSignIn: props.onSignIn })));
|
|
987
|
+
}
|
|
988
|
+
if (props.selectedNav === "share")
|
|
989
|
+
return null;
|
|
990
|
+
const hasPendingVersion = props.publishState === "draft" || props.publishState === "old";
|
|
991
|
+
if (hasPendingVersion && Boolean(props.onPublish)) {
|
|
992
|
+
return (React.createElement("div", { style: { padding: "0 14px", flexShrink: 0 } },
|
|
993
|
+
React.createElement(PublishBanner, { variant: props.publishState === "draft" || props.publishState === "old" ? props.publishState : undefined, onPublish: props.onPublish, publishing: props.publishing })));
|
|
994
|
+
}
|
|
995
|
+
return null;
|
|
682
996
|
})(),
|
|
683
|
-
React.createElement("div", { "data-vibe-card-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
997
|
+
props.editorTab != null && props.selectedNav !== "share" && !agentPanelActive && (React.createElement("div", { "data-vibe-card-editor-tabs": true, style: { padding: "0 14px 6px", flexShrink: 0 } },
|
|
998
|
+
React.createElement(EditorTabStrip, { tab: props.editorTab, onTab: (t) => props.onEditorTab?.(t), standing: {
|
|
999
|
+
viewerMode: props.viewerMode,
|
|
1000
|
+
memberReadOnly: props.memberReadOnly,
|
|
1001
|
+
adminMode: props.adminMode,
|
|
1002
|
+
versionLocked: props.versionLocked,
|
|
1003
|
+
publishState: props.publishState,
|
|
1004
|
+
publishAccess: props.publishAccess,
|
|
1005
|
+
} }))),
|
|
1006
|
+
props.body == null && props.composerControls && !agentPanelActive && (React.createElement("div", { "data-testid": "vibe-card-composer-controls", "aria-hidden": streamLive ? true : undefined, inert: streamLive ? true : undefined, style: {
|
|
1007
|
+
padding: "0 14px",
|
|
1008
|
+
marginBottom: 6,
|
|
690
1009
|
flexShrink: 0,
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
React.createElement(
|
|
704
|
-
React.createElement(
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
adminMode: props.adminMode,
|
|
740
|
-
versionLocked: props.versionLocked,
|
|
741
|
-
publishState: props.publishState,
|
|
742
|
-
publishAccess: props.publishAccess,
|
|
743
|
-
} }))),
|
|
744
|
-
props.body == null && props.composerControls && !agentPanelActive && (React.createElement("div", { "data-testid": "vibe-card-composer-controls", "aria-hidden": streamLive ? true : undefined, inert: streamLive ? true : undefined, style: {
|
|
745
|
-
padding: "0 14px",
|
|
746
|
-
marginBottom: 6,
|
|
747
|
-
flexShrink: 0,
|
|
748
|
-
...(streamLive ? { visibility: "hidden" } : null),
|
|
749
|
-
} }, props.composerControls)),
|
|
750
|
-
React.createElement("div", { ref: scrollRegionRef, "data-testid": "vibe-card-scroll", style: {
|
|
751
|
-
padding: "0 14px 4px",
|
|
752
|
-
overflowY: "auto",
|
|
753
|
-
overflowX: "hidden",
|
|
754
|
-
flex: "1 1 auto",
|
|
755
|
-
minHeight: 0,
|
|
756
|
-
...((props.bodyFill && props.body) || agentPanelActive || props.body == null
|
|
757
|
-
? { display: "flex", flexDirection: "column" }
|
|
758
|
-
: null),
|
|
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)))))),
|
|
794
|
-
props.body == null && !agentPanelActive && (React.createElement("div", { style: { flexShrink: 0, padding: "0 14px 10px" } },
|
|
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 }))),
|
|
1010
|
+
...(streamLive ? { visibility: "hidden" } : null),
|
|
1011
|
+
} }, props.composerControls)),
|
|
1012
|
+
React.createElement("div", { ref: scrollRegionRef, "data-testid": "vibe-card-scroll", style: {
|
|
1013
|
+
padding: "0 14px 4px",
|
|
1014
|
+
overflowY: "auto",
|
|
1015
|
+
overflowX: "hidden",
|
|
1016
|
+
flex: "1 1 auto",
|
|
1017
|
+
minHeight: 0,
|
|
1018
|
+
...((props.bodyFill && props.body) || agentPanelActive || props.body == null
|
|
1019
|
+
? { display: "flex", flexDirection: "column" }
|
|
1020
|
+
: null),
|
|
1021
|
+
} },
|
|
1022
|
+
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)),
|
|
1023
|
+
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,
|
|
1024
|
+
React.createElement("div", { ref: chipsRegionRef, style: streamFillActive
|
|
1025
|
+
? { flex: "1 1 auto", minHeight: 0, display: "flex", flexDirection: "column" }
|
|
1026
|
+
: { flex: "1 0 auto", minHeight: 0 } },
|
|
1027
|
+
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)),
|
|
1028
|
+
React.createElement("div", { "data-testid": "vibe-card-stream-region", style: {
|
|
1029
|
+
position: "relative",
|
|
1030
|
+
...(chipBuildLive ? { display: "flex", flexDirection: "column" } : {}),
|
|
1031
|
+
...(streamFillActive ? { flex: "1 1 auto", minHeight: 0 } : null),
|
|
1032
|
+
} },
|
|
1033
|
+
props.streamBody && (React.createElement("div", { "data-testid": streamLive ? undefined : "vibe-card-stream-settled", style: streamLive
|
|
1034
|
+
? {
|
|
1035
|
+
...(chipBuildLive ? { flex: "1 1 auto" } : { position: "absolute", inset: 0 }),
|
|
1036
|
+
display: "flex",
|
|
1037
|
+
flexDirection: "column",
|
|
1038
|
+
minHeight: 0,
|
|
1039
|
+
}
|
|
1040
|
+
: { marginBottom: 8 } },
|
|
1041
|
+
React.createElement("div", { style: streamLive ? { flex: "1 1 auto", minHeight: 0, overflowY: "auto" } : undefined }, props.streamBody),
|
|
1042
|
+
props.liveDuringBuild === true && React.createElement(PendingMessages, { messages: queued }))),
|
|
1043
|
+
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" },
|
|
1044
|
+
React.createElement(ChatMessageRow, { role: "ai", rowAttrs: { "data-message-role": "suggestions" } },
|
|
1045
|
+
React.createElement("div", { className: "vibes-chip-well flex flex-col", "data-testid": "vibe-chip-set" },
|
|
1046
|
+
(props.seedOffer || props.upgradeChips) && (React.createElement("div", { className: "flex flex-col gap-[6px]", inert: props.activeChip !== undefined },
|
|
1047
|
+
props.seedOffer,
|
|
1048
|
+
props.upgradeChips)),
|
|
1049
|
+
visibleChips && visibleChips.length > 0 && (React.createElement(OptionButtons, { options: visibleChips, activeOption: props.activeChip, inSet: true, isFirst: props.activeChip === undefined, firstMessage: props.isOwner
|
|
1050
|
+
? "Suggested requests:"
|
|
1051
|
+
: "Describe a change to remix your own copy of this app:", onSelect: (o) => {
|
|
1052
|
+
const r = props.onSelectChip?.(o);
|
|
1053
|
+
return r && typeof r === "object" && "then" in r ? r.then((ok) => ok ?? false) : false;
|
|
1054
|
+
}, decorate: (option) => decorateChip(option, props) }))))))),
|
|
1055
|
+
props.streamFooter !== undefined && React.createElement("div", { className: "mx-auto w-full max-w-5xl" }, props.streamFooter)))))),
|
|
1056
|
+
props.body == null && !agentPanelActive && (React.createElement("div", { style: { flexShrink: 0, padding: "0 14px 10px" } },
|
|
1057
|
+
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 })))),
|
|
796
1058
|
agentPanelActive ? (React.createElement("div", { "aria-hidden": true, style: { display: "flex", flexShrink: 0, padding: "0 8px" } },
|
|
797
|
-
React.createElement("div", { style: { width: 120, height: 50, marginLeft: "auto" } }))) : (React.createElement("div", { style: {
|
|
1059
|
+
React.createElement("div", { style: { width: 120, height: 50, marginLeft: "auto" } }))) : (React.createElement("div", { "data-vibe-card-nav": true, style: {
|
|
798
1060
|
display: "flex",
|
|
799
1061
|
alignItems: "center",
|
|
800
1062
|
gap: "var(--uvc-nav-gap, 6px)",
|
|
801
1063
|
padding: "0 8px",
|
|
802
|
-
borderTop: "1px solid
|
|
1064
|
+
borderTop: "1px solid transparent",
|
|
803
1065
|
flexShrink: 0,
|
|
804
1066
|
} },
|
|
805
|
-
React.createElement(NavIcon, { label: "Home", color: "#2563eb", onClick: props.onHome },
|
|
1067
|
+
React.createElement(NavIcon, { label: "Home", color: "var(--uvc-nav-home, #2563eb)", onClick: props.onHome },
|
|
806
1068
|
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" },
|
|
807
1069
|
React.createElement("path", { d: "M3 12l9-9 9 9" }),
|
|
808
1070
|
React.createElement("path", { d: "M5 10v10a1 1 0 001 1h4v-6h4v6h4a1 1 0 001-1V10" }))),
|
|
809
|
-
React.createElement(NavIcon, { label: "
|
|
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 },
|
|
1071
|
+
React.createElement(NavIcon, { label: "Edit", color: "var(--uvc-nav-edit, #c2410c)", selected: ["edit", "edit-chat", "standing", "code", "files", "data", "style", "settings", "faq"].includes(props.selectedNav ?? "edit"), onClick: props.onEdit ?? props.onChat },
|
|
813
1072
|
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" },
|
|
814
1073
|
React.createElement("path", { d: "M12 20h9" }),
|
|
815
1074
|
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" }))),
|
|
@@ -917,7 +1176,7 @@ export function UnifiedVibeCard(props) {
|
|
|
917
1176
|
} },
|
|
918
1177
|
React.createElement("span", { "aria-hidden": true, style: { width: 6, height: 6, borderRadius: "50%", background: "#f59e0b" } }),
|
|
919
1178
|
"draft")),
|
|
920
|
-
React.createElement("button", { type: "button", "aria-label": open
|
|
1179
|
+
React.createElement("button", { type: "button", "data-vibe-switch-glass": true, "aria-label": open
|
|
921
1180
|
? "Close vibe menu"
|
|
922
1181
|
: (props.pendingRequestCount ?? 0) > 0
|
|
923
1182
|
?
|
|
@@ -933,10 +1192,10 @@ export function UnifiedVibeCard(props) {
|
|
|
933
1192
|
position: "relative",
|
|
934
1193
|
overflow: "visible",
|
|
935
1194
|
} },
|
|
936
|
-
React.createElement(
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
1195
|
+
switchGlassColor !== ovalColors.neutral && (React.createElement(React.Fragment, { key: `${switchGlassColor}|${props.switchOvalStatusKey ?? ""}` },
|
|
1196
|
+
React.createElement("span", { "data-vibe-switch-rest": true, "aria-hidden": "true" }),
|
|
1197
|
+
React.createElement("span", { "data-vibe-switch-highlight": true, "data-testid": "vibe-switch-highlight", "data-arrival-amber": switchGlassColor === ovalColors.yellow, "aria-hidden": "true", style: { color: switchGlassColor } }))),
|
|
1198
|
+
React.createElement(VibesSwitch, { size: 60, style: { position: "absolute", right: 0, bottom: 0, pointerEvents: "none" }, isActive: !open, isTwinkling: !open && isTwinkling, isTeasing: !open && isTeasing }),
|
|
940
1199
|
!open && (props.pendingRequestCount ?? 0) > 0 && (React.createElement("span", { "data-testid": "vibe-switch-pending-badge", "aria-hidden": "true", style: {
|
|
941
1200
|
position: "absolute",
|
|
942
1201
|
top: -4,
|
|
@@ -1218,22 +1477,24 @@ function FastPathToggle({ blessed, onClick }) {
|
|
|
1218
1477
|
function NavIcon({ label, color, selected, onClick, buttonRef, badgeCount, children, }) {
|
|
1219
1478
|
const showBadge = typeof badgeCount === "number" && badgeCount > 0;
|
|
1220
1479
|
const badgeText = showBadge ? (badgeCount > 99 ? "99+" : String(badgeCount)) : "";
|
|
1221
|
-
return (React.createElement("button", { ref: buttonRef, type: "button", "aria-label": showBadge ? `${label} (${badgeCount})` : label, title: label, onClick: onClick, style: {
|
|
1480
|
+
return (React.createElement("button", { ref: buttonRef, type: "button", "aria-label": showBadge ? `${label} (${badgeCount})` : label, title: label, "data-vibe-nav-selected": selected === true, onClick: onClick, style: {
|
|
1222
1481
|
position: "relative",
|
|
1223
1482
|
width: "var(--uvc-nav-size, 38px)",
|
|
1224
1483
|
height: "var(--uvc-nav-size, 38px)",
|
|
1225
1484
|
flexShrink: 0,
|
|
1226
1485
|
boxSizing: "border-box",
|
|
1227
1486
|
borderRadius: "50%",
|
|
1228
|
-
background: "
|
|
1487
|
+
background: "rgba(0,0,0,.5)",
|
|
1229
1488
|
display: "flex",
|
|
1230
1489
|
alignItems: "center",
|
|
1231
1490
|
justifyContent: "center",
|
|
1232
1491
|
color,
|
|
1233
1492
|
fontSize: 16,
|
|
1234
1493
|
cursor: "pointer",
|
|
1235
|
-
border:
|
|
1236
|
-
boxShadow: selected
|
|
1494
|
+
border: `${selected ? 3 : 1}px solid ${color}`,
|
|
1495
|
+
boxShadow: selected
|
|
1496
|
+
? "inset 0 4px 8px 1px rgba(0,0,0,.85), inset 0 1px 3px rgba(0,0,0,.65), inset 0 -1px 0 color-mix(in srgb,currentColor 45%,transparent)"
|
|
1497
|
+
: "none",
|
|
1237
1498
|
} },
|
|
1238
1499
|
children,
|
|
1239
1500
|
showBadge ? (React.createElement("span", { "aria-hidden": "true", style: {
|
|
@@ -1334,9 +1595,10 @@ function OtherRow({ value, onValueChange, queued, onQueuedChange, onSubmitOther,
|
|
|
1334
1595
|
const submitDisabled = attachmentsCtl.busy || (Boolean(generating) && attachmentsCtl.readyAttachments.length > 0);
|
|
1335
1596
|
const sendNow = liveDuringBuild === true && canSendNow === true && queued.length === 0 && attachmentsCtl.readyAttachments.length === 0;
|
|
1336
1597
|
const hasUnsentContent = value.trim() !== "" || attachmentsCtl.readyAttachments.length > 0;
|
|
1598
|
+
const composerDrop = composerFileDrop({ onFiles: attachmentsCtl.add });
|
|
1337
1599
|
return (React.createElement(React.Fragment, null,
|
|
1338
1600
|
React.createElement(AttachmentChips, { chips: attachmentsCtl.attachments, onRemove: attachmentsCtl.remove }),
|
|
1339
|
-
React.createElement("form", { onSubmit: (e) => {
|
|
1601
|
+
React.createElement("form", { onDragOver: composerDrop.onDragOver, onDrop: composerDrop.onDrop, onSubmit: (e) => {
|
|
1340
1602
|
e.preventDefault();
|
|
1341
1603
|
const text = value.trim();
|
|
1342
1604
|
const files = attachmentsCtl.readyAttachments;
|
|
@@ -1391,7 +1653,9 @@ function OtherRow({ value, onValueChange, queued, onQueuedChange, onSubmitOther,
|
|
|
1391
1653
|
? "Sends when the current change finishes"
|
|
1392
1654
|
: undefined, style: {
|
|
1393
1655
|
...OTHER_ROW_BUTTON_STYLE,
|
|
1394
|
-
background: "var(--
|
|
1656
|
+
background: "var(--uvc-submit-fill, rgba(64,89,114,.2))",
|
|
1657
|
+
color: "var(--uvc-submit-arrow, var(--vibes-cream, #fffff0))",
|
|
1658
|
+
boxShadow: "none",
|
|
1395
1659
|
opacity: submitDisabled ? 0.5 : 1,
|
|
1396
1660
|
cursor: submitDisabled ? "default" : "pointer",
|
|
1397
1661
|
} },
|