1 2!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="ea5d659a-f734-5a99-962b-fd02b29dfbea")}catch(e){}}(); 3const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/SiteViews-DrsHYaEK.js","assets/index-Bv8xuwng.js","assets/index-CWThiHjA.css","assets/index-C0ROMDRt.js","assets/index-Bj-Tdrlr.js","assets/index-CbDwmQ86.js","assets/AlertDialog-DKoWGq1L.js","assets/HeaderSitePicker-BQ3o99R_.js","assets/landingDestination-DvFBkeeK.js","assets/index-DyCuF-Ol.js","assets/createLucideIcon-5bFtwJ8F.js","assets/index-DyMO7Qub.js","assets/index-CgW23jqL.js","assets/styles-CwIrWvkA.css"])))=>i.map(i=>d[i]); 4import { r as reactExports, i as usePostHog, j as jsxRuntimeExports, B as Button, c as cn$1, R as React, a as useAuth, _ as _t, k as useMutation, a8 as useQuery, n as navigate, d as namedRoutes, a9 as reactDomExports, aa as useFloating, ab as offset, ac as shift, ad as flip, ae as size, af as arrow, ag as hide, ah as limitShift, ai as autoUpdate, aj as ReactDOM, b as useMutation$1, h as useLazyQuery, ak as clsx, g as gql, al as RECORD_ACQUISITION, am as stashSurveyAnswer, f as useQuery$1, e as useLocation, an as __vitePreload, ao as useApolloClient } from "./index-Bv8xuwng.js"; 5import { c as cn, C as Check, s as siteIdentityText, h as hideOthers, R as ReactRemoveScroll, a as Root2$2, T as Trigger, b as Portal2, d as Content2$1, I as Item2 } from "./HeaderSitePicker-BQ3o99R_.js"; 6import { M as Metadata } from "./Metadata-DVDCZJgT.js"; 7import { F as FileText, C as ChevronLeft, g as generateId, u as useUploadImage, a as getFreshToken, b as getTokenExpirationInfo, R as ResetButton, A as ArrowDown, c as ChevronsDown, d as ChevronsUp, G as Gallery, e as useBreakpoint, f as useRecordLastViewedWebsite, I as ImpersonationReviewBanner, S as SiteCaseNoticeBanner, P as PaymentPastDueBanner, h as ChatErrorBoundary } from "./useRecordLastViewedWebsite-CnCuCtnj.js"; 8import { c as commerceSetupCopy, o as openCommerceWebsiteSetup, C as CommerceSetupCheckedAt, u as useCommerceSetupRefresh, a as commerceSetupNeedsPolling, b as openCommerceStripeDashboard } from "./CommerceSetupCheckedAt-BSwtY7Ui.js"; 9import { C as Card, d as CardContent } from "./Card-C12BVEU3.js"; 10import { D as Dialog, a as DialogContent, c as DialogTitle, b as DialogHeader, d as DialogDescription, e as DialogFooter } from "./Dialog-Coyn2AE3.js"; 11import { T as Textarea } from "./Textarea-DUWZ_F-N.js"; 12import { T as TooltipProvider, a as Tooltip, b as TooltipTrigger, c as TooltipContent } from "./Tooltip-B4vM7gok.js"; 13import { G as Globe, R as Rocket, P as PublishBlockedModal } from "./PublishBlockedModal-CMrnNTuT.js"; 14import { c as createLucideIcon } from "./createLucideIcon-5bFtwJ8F.js"; 15import { U as User } from "./user-DSqjZOH1.js"; 16import { E as Eye } from "./eye-DgExikkT.js"; 17import { I as Image } from "./image-D-MEnABj.js"; 18import { I as Info } from "./info-C-I9rgjO.js"; 19import { S as Search } from "./search-B5_IJnHE.js"; 20import { C as ChevronRight } from "./chevron-right-DfOUACDI.js"; 21import { L as LoaderCircle } from "./loader-circle-NFrOwZ3p.js"; 22import { C as CircleAlert, T as TriangleAlert } from "./triangle-alert-D7W416og.js"; 23import { M as Markdown, r as remarkGfm } from "./index-DXVraWYZ.js"; 24import { X } from "./x-CJvENK3D.js"; 25import { M as MessageSquare } from "./message-square-29qClUa_.js"; 26import { C as ChevronUp } from "./chevron-up-KHSg5zQ5.js"; 27import { R as RefreshCw } from "./refresh-cw-CMxI2avz.js"; 28import { P as Pencil } from "./pencil-DW93RWca.js"; 29import { S as ShieldCheck } from "./shield-check-gSvVll7c.js"; 30import { R as ReactNavbar } from "./ReactNavbar-C7WhcDeQ.js"; 31import { c as composeEventHandlers, u as useSize } from "./index-DyMO7Qub.js"; 32import { u as useComposedRefs, c as createContextScope, a as useLayoutEffect2, b as useControllableState } from "./index-Bj-Tdrlr.js"; 33import { u as useEscapeKeydown, a as useId, A as AlertDialog, b as AlertDialogContent, c as AlertDialogHeader, d as AlertDialogTitle, e as AlertDialogDescription, f as AlertDialogFooter, g as AlertDialogCancel, h as AlertDialogAction } from "./AlertDialog-DKoWGq1L.js"; 34import { I as Input } from "./Input-Crzez_vD.js"; 35import { L as Label } from "./Label-B4OPMqgJ.js"; 36import { C as ChevronDown } from "./chevron-down-CL642vyV.js"; 37import "./landingDestination-DvFBkeeK.js"; 38import "./index-DyCuF-Ol.js"; 39import "./Tabs-7vkR9jPC.js"; 40import "./index-s9LeGatc.js"; 41import "./load-parse-D6ADZb7m.js"; 42import "./trash-2-CZVMywOH.js"; 43import "./IconBase-B6twkb8E.js"; 44/** 45 * @license lucide-react v0.456.0 - ISC 46 * 47 * This source code is licensed under the ISC license. 48 * See the LICENSE file in the root directory of this source tree. 49 */ 50const Activity = createLucideIcon("Activity", [ 51 [ 52 "path", 53 { 54 d: "M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l
54-2.35 8.36A2 2 0 0 1 4.49 12H2", 55 key: "169zse" 56 } 57 ] 58]); 59/** 60 * @license lucide-react v0.456.0 - ISC 61 * 62 * This source code is licensed under the ISC license. 63 * See the LICENSE file in the root directory of this source tree. 64 */ 65const AppWindow = createLucideIcon("AppWindow", [ 66 ["rect", { x: "2", y: "4", width: "20", height: "16", rx: "2", key: "izxlao" }], 67 ["path", { d: "M10 4v4", key: "pp8u80" }], 68 ["path", { d: "M2 8h20", key: "d11cs7" }], 69 ["path", { d: "M6 4v4", key: "1svtjw" }] 70]); 71/** 72 * @license lucide-react v0.456.0 - ISC 73 * 74 * This source code is licensed under the ISC license. 75 * See the LICENSE file in the root directory of this source tree. 76 */ 77const ArrowUp = createLucideIcon("ArrowUp", [ 78 ["path", { d: "m5 12 7-7 7 7", key: "hav0vg" }], 79 ["path", { d: "M12 19V5", key: "x0mq9r" }] 80]); 81/** 82 * @license lucide-react v0.456.0 - ISC 83 * 84 * This source code is licensed under the ISC license. 85 * See the LICENSE file in the root directory of this source tree. 86 */ 87const Banknote = createLucideIcon("Banknote", [ 88 ["rect", { width: "20", height: "12", x: "2", y: "6", rx: "2", key: "9lu3g6" }], 89 ["circle", { cx: "12", cy: "12", r: "2", key: "1c9p78" }], 90 ["path", { d: "M6 12h.01M18 12h.01", key: "113zkx" }] 91]); 92/** 93 * @license lucide-react v0.456.0 - ISC 94 * 95 * This source code is licensed under the ISC license. 96 * See the LICENSE file in the root directory of this source tree. 97 */ 98const BookOpen = createLucideIcon("BookOpen", [ 99 ["path", { d: "M12 7v14", key: "1akyts" }], 100 [ 101 "path", 102 { 103 d: "M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z", 104 key: "ruj8y" 105 } 106 ] 107]); 108/** 109 * @license lucide-react v0.456.0 - ISC 110 * 111 * This source code is licensed under the ISC license. 112 * See the LICENSE file in the root directory of this source tree. 113 */ 114const Box = createLucideIcon("Box", [ 115 [ 116 "path", 117 { 118 d: "M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z", 119 key: "hh9hay" 120 } 121 ], 122 ["path", { d: "m3.3 7 8.7 5 8.7-5", key: "g66t2b" }], 123 ["path", { d: "M12 22V12", key: "d0xqtd" }] 124]); 125/** 126 * @license lucide-react v0.456.0 - ISC 127 * 128 * This source code is licensed under the ISC license. 129 * See the LICENSE file in the root directory of this source tree. 130 */ 131const Camera = createLucideIcon("Camera", [ 132 [ 133 "path", 134 { 135 d: "M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z", 136 key: "1tc9qg" 137 } 138 ], 139 ["circle", { cx: "12", cy: "13", r: "3", key: "1vg3eu" }] 140]); 141/** 142 * @license lucide-react v0.456.0 - ISC 143 * 144 * This source code is licensed under the ISC license. 145 * See the LICENSE file in the root directory of this source tree. 146 */ 147const ChartNoAxesColumn = createLucideIcon("ChartNoAxesColumn", [ 148 ["line", { x1: "18", x2: "18", y1: "20", y2: "10", key: "1xfpm4" }], 149 ["line", { x1: "12", x2: "12", y1: "20", y2: "4", key: "be30l9" }], 150 ["line", { x1: "6", x2: "6", y1: "20", y2: "14", key: "1r4le6" }] 151]); 152/** 153 * @license lucide-react v0.456.0 - ISC 154 * 155 * This source code is licensed under the ISC license. 156 * See the LICENSE file in the root directory of this source tree. 157 */ 158const ClipboardList = createLucideIcon("ClipboardList", [ 159 ["rect", { width: "8", height: "4", x: "8", y: "2", rx: "1", ry: "1", key: "tgr4d6" }], 160 [ 161 "path", 162 { 163 d: "M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2", 164 key: "116196" 165 } 166 ], 167 ["path", { d: "M12 11h4", key: "1jrz19" }], 168 ["path", { d: "M12 16h4", key: "n85exb" }], 169 ["path", { d: "M8 11h.01", key: "1dfujw" }], 170 ["path", { d: "M8 16h.01", key: "18s6g9" }] 171]); 172/** 173 * @license lucide-react v0.456.0 - ISC 174 * 175 * This source code is licensed under the ISC license. 176 * See the LICENSE file in the root directory of this source tree. 177 */ 178const Compass = createLucideIcon("Compass", [ 179 [ 180 "path", 181 { 182 d: "m16.24 7.76-1.804 5.411a2 2 0 0 1-1.265 1.265L7.76 16.24l1.804-5.411a2 2 0 0 1 1.265-1.265z", 183 key: "9ktpf1" 184 } 185 ], 186 ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }] 187]); 188/** 189 * @license lucide-react v0.456.0 - ISC 190 * 191 * This source code is licensed under the ISC license. 192 * See the LICENSE file in the root directory of this source tree. 193 */ 194const CornerDownLeft = createLucideIcon("CornerDownLeft", [ 195 ["polyline", { points: "9 10 4 15 9 20", key: "r3jprv" }], 196 ["path", { d: "M20 4v7a4 4 0 0 1-4 4H4", key: "6o5b7l" }] 197]); 198/** 199 * @license lucide-react v0.456.0 - ISC 200 * 201 * This source code is licensed under the ISC license. 202 * See the LICENSE file in the root directory of this source tree. 203 */ 204const CreditCard = createLucideIcon("CreditCard", [ 205 ["rect", { width: "20", height: "14", x: "2", y: "5", rx: "2", key: "ynyp8z" }], 206 ["line", { x1: "2", x2: "22", y1: "10", y2: "10", key: "1b3vmo" }] 207]); 208/** 209 * @license lucide-react v0.456.0 - ISC 210 * 211 * This source code is licensed under the ISC license. 212 * See the LICENSE file in the root directory of this source tree. 213 */ 214const EyeOff = createLucideIcon("EyeOff", [ 215 [ 216 "path", 217 { 218 d: "M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49", 219 key: "ct8e1f" 220 } 221 ], 222 ["path", { d: "M14.084 14.158a3 3 0 0 1-4.242-4.242", key: "151rxh" }], 223 [ 224 "path", 225 { 226 d: "M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143", 227 key: "13bj9a" 228 } 229 ], 230 ["path", { d: "m2 2 20 20", key: "1ooewy" }] 231]); 232/** 233 * @license lucide-react v0.456.0 - ISC 234 * 235 * This source code is licensed under the ISC license. 236 * See the LICENSE file in the root directory of this source tree. 237 */ 238const FilePen = createLucideIcon("FilePen", [ 239 ["path", { d: "M12.5 22H18a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v9.5", key: "1couwa" }], 240 ["path", { d: "M14 2v4a2 2 0 0 0 2 2h4", key: "tnqrlb" }], 241 [ 242 "path", 243 { 244 d: "M13.378 15.626a1 1 0 1 0-3.004-3.004l-5.01 5.012a2 2 0 0 0-.506.854l-.837 2.87a.5.5 0 0 0 .62.62l2.87-.837a2 2 0 0 0 .854-.506z", 245 key: "1y4qbx" 246 } 247 ] 248]); 249/** 250 * @license lucide-react v0.456.0 - ISC 251 * 252 * This source code is licensed under the ISC license. 253 * See the LICENSE file in the root directory of this source tree. 254 */ 255const FilePlus2 = createLucideIcon("FilePlus2", [ 256 ["path", { d: "M4 22h14a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v4", key: "1pf5j1" }], 257 ["path", { d: "M14 2v4a2 2 0 0 0 2 2h4", key: "tnqrlb" }], 258 ["path", { d: "M3 15h6", key: "4e2qda" }], 259 ["path", { d: "M6 12v6", key: "1u72j0" }] 260]); 261/** 262 * @license lucide-react v0.456.0 - ISC 263 * 264 * This source code is licensed under the ISC license. 265 * See the LICENSE file in the root directory of this source tree. 266 */ 267const Files = createLucideIcon("Files", [ 268 ["path", { d: "M20 7h-3a2 2 0 0 1-2-2V2", key: "x099mo" }], 269 ["path", { d: "M9 18a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h7l4 4v10a2 2 0 0 1-2 2Z", key: "18t6ie" }], 270 ["path", { d: "M3 7.6v12.8A1.6 1.6 0 0 0 4.6 22h9.8", key: "1nja0z" }] 271]); 272/** 273 * @license lucide-react v0.456.0 - ISC 274 * 275 * This source code is licensed under the ISC license. 276 * See the LICENSE file in the root directory of this source tree. 277 */ 278const House = createLucideIcon("House", [ 279 ["path", { d: "M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8", key: "5wwlr5" }], 280 [ 281 "path", 282 { 283 d: "M3 10a2 2 0 0 1 .709-1.528l7-5.999a2 2 0 0 1 2.582 0l7 5.999A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z", 284 key: "1d0kgt" 285 } 286 ] 287]); 288/** 289 * @license lucide-react v0.456.0 - ISC 290 * 291 * This source code is licensed under the ISC license. 292 * See the LICENSE file in the root directory of this source tree. 293 */ 294const Images = createLucideIcon("Images", [ 295 ["path", { d: "M18 22H4a2 2 0 0 1-2-2V6", key: "pblm9e" }], 296 ["path", { d: "m22 13-1.296-1.296a2.41 2.41 0 0 0-3.408 0L11 18", key: "nf6bnh" }], 297 ["circle", { cx: "12", cy: "8", r: "2", key: "1822b1" }], 298 ["rect", { width: "16", height: "16", x: "6", y: "2", rx: "2", key: "12espp" }] 299]); 300/** 301 * @license lucide-react v0.456.0 - ISC 302 * 303 * This source code is licensed under the ISC license. 304 * See the LICENSE file in the root directory of this source tree. 305 */ 306const LayoutGrid = createLucideIcon("LayoutGrid", [ 307 ["rect", { width: "7", height: "7", x: "3", y: "3", rx: "1", key: "1g98yp" }], 308 ["rect", { width: "7", height: "7", x: "14", y: "3", rx: "1", key: "6d4xhi" }], 309 ["rect", { width: "7", height: "7", x: "14", y: "14", rx: "1", key: "nxv5o0" }
309], 310 ["rect", { width: "7", height: "7", x: "3", y: "14", rx: "1", key: "1bb6yr" }] 311]); 312/** 313 * @license lucide-react v0.456.0 - ISC 314 * 315 * This source code is licensed under the ISC license. 316 * See the LICENSE file in the root directory of this source tree. 317 */ 318const LayoutTemplate = createLucideIcon("LayoutTemplate", [ 319 ["rect", { width: "18", height: "7", x: "3", y: "3", rx: "1", key: "f1a2em" }], 320 ["rect", { width: "9", height: "7", x: "3", y: "14", rx: "1", key: "jqznyg" }], 321 ["rect", { width: "5", height: "7", x: "16", y: "14", rx: "1", key: "q5h2i8" }] 322]); 323/** 324 * @license lucide-react v0.456.0 - ISC 325 * 326 * This source code is licensed under the ISC license. 327 * See the LICENSE file in the root directory of this source tree. 328 */ 329const Monitor = createLucideIcon("Monitor", [ 330 ["rect", { width: "20", height: "14", x: "2", y: "3", rx: "2", key: "48i651" }], 331 ["line", { x1: "8", x2: "16", y1: "21", y2: "21", key: "1svkeh" }], 332 ["line", { x1: "12", x2: "12", y1: "17", y2: "21", key: "vw1qmm" }] 333]); 334/** 335 * @license lucide-react v0.456.0 - ISC 336 * 337 * This source code is licensed under the ISC license. 338 * See the LICENSE file in the root directory of this source tree. 339 */ 340const MousePointerClick = createLucideIcon("MousePointerClick", [ 341 ["path", { d: "M14 4.1 12 6", key: "ita8i4" }], 342 ["path", { d: "m5.1 8-2.9-.8", key: "1go3kf" }], 343 ["path", { d: "m6 12-1.9 2", key: "mnht97" }], 344 ["path", { d: "M7.2 2.2 8 5.1", key: "1cfko1" }], 345 [ 346 "path", 347 { 348 d: "M9.037 9.69a.498.498 0 0 1 .653-.653l11 4.5a.5.5 0 0 1-.074.949l-4.349 1.041a1 1 0 0 0-.74.739l-1.04 4.35a.5.5 0 0 1-.95.074z", 349 key: "s0h3yz" 350 } 351 ] 352]); 353/** 354 * @license lucide-react v0.456.0 - ISC 355 * 356 * This source code is licensed under the ISC license. 357 * See the LICENSE file in the root directory of this source tree. 358 */ 359const PackageCheck = createLucideIcon("PackageCheck", [ 360 ["path", { d: "m16 16 2 2 4-4", key: "gfu2re" }], 361 [ 362 "path", 363 { 364 d: "M21 10V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l2-1.14", 365 key: "e7tb2h" 366 } 367 ], 368 ["path", { d: "m7.5 4.27 9 5.15", key: "1c824w" }], 369 ["polyline", { points: "3.29 7 12 12 20.71 7", key: "ousv84" }], 370 ["line", { x1: "12", x2: "12", y1: "22", y2: "12", key: "a4e8g8" }] 371]); 372/** 373 * @license lucide-react v0.456.0 - ISC 374 * 375 * This source code is licensed under the ISC license. 376 * See the LICENSE file in the root directory of this source tree. 377 */ 378const Package = createLucideIcon("Package", [ 379 [ 380 "path", 381 { 382 d: "M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73z", 383 key: "1a0edw" 384 } 385 ], 386 ["path", { d: "M12 22V12", key: "d0xqtd" }], 387 ["path", { d: "m3.3 7 7.703 4.734a2 2 0 0 0 1.994 0L20.7 7", key: "yx3hmr" }], 388 ["path", { d: "m7.5 4.27 9 5.15", key: "1c824w" }] 389]); 390/** 391 * @license lucide-react v0.456.0 - ISC 392 * 393 * This source code is licensed under the ISC license. 394 * See the LICENSE file in the root directory of this source tree. 395 */ 396const Palette = createLucideIcon("Palette", [ 397 ["circle", { cx: "13.5", cy: "6.5", r: ".5", fill: "currentColor", key: "1okk4w" }], 398 ["circle", { cx: "17.5", cy: "10.5", r: ".5", fill: "currentColor", key: "f64h9f" }], 399 ["circle", { cx: "8.5", cy: "7.5", r: ".5", fill: "currentColor", key: "fotxhn" }], 400 ["circle", { cx: "6.5", cy: "12.5", r: ".5", fill: "currentColor", key: "qy21gx" }], 401 [ 402 "path", 403 { 404 d: "M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.926 0 1.648-.746 1.648-1.688 0-.437-.18-.835-.437-1.125-.29-.289-.438-.652-.438-1.125a1.64 1.64 0 0 1 1.668-1.668h1.996c3.051 0 5.555-2.503 5.555-5.554C21.965 6.012 17.461 2 12 2z", 405 key: "12rzf8" 406 } 407 ] 408]); 409/** 410 * @license lucide-react v0.456.0 - ISC 411 * 412 * This source code is licensed under the ISC license. 413 * See the LICENSE file in the root directory of this source tree. 414 */ 415const Paperclip = createLucideIcon("Paperclip", [ 416 [ 417 "path", 418 { 419 d: "m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48", 420 key: "1u3ebp" 421 } 422 ] 423]); 424/** 425 * @license lucide-react v0.456.0 - ISC 426 * 427 * This source code is licensed under the ISC license. 428 * See the LICENSE file in the root directory of this source tree. 429 */ 430const Settings = createLucideIcon("Settings", [ 431 [ 432 "path", 433 { 434 d: "M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z", 435 key: "1qme2f" 436 } 437 ], 438 ["circle", { cx: "12", cy: "12", r: "3", key: "1v7zrd" }] 439]); 440/** 441 * @license lucide-react v0.456.0 - ISC 442 * 443 * This source code is licensed under the ISC license. 444 * See the LICENSE file in the root directory of this source tree. 445 */ 446const ShoppingBag = createLucideIcon("ShoppingBag", [ 447 ["path", { d: "M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4Z", key: "hou9p0" }], 448 ["path", { d: "M3 6h18", key: "d0wm0j" }], 449 ["path", { d: "M16 10a4 4 0 0 1-8 0", key: "1ltviw" }] 450]); 451/** 452 * @license lucide-react v0.456.0 - ISC 453 * 454 * This source code is licensed under the ISC license. 455 * See the LICENSE file in the root directory of this source tree. 456 */ 457const Smartphone = createLucideIcon("Smartphone", [ 458 ["rect", { width: "14", height: "20", x: "5", y: "2", rx: "2", ry: "2", key: "1yt0o3" }], 459 ["path", { d: "M12 18h.01", key: "mhygvu" }] 460]); 461/** 462 * @license lucide-react v0.456.0 - ISC 463 * 464 * This source code is licensed under the ISC license. 465 * See the LICENSE file in the root directory of this source tree. 466 */ 467const Sparkles = createLucideIcon("Sparkles", [ 468 [ 469 "path", 470 { 471 d: "M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z", 472 key: "4pj2yx" 473 } 474 ], 475 ["path", { d: "M20 3v4", key: "1olli1" }
475], 476 ["path", { d: "M22 5h-4", key: "1gvqau" }], 477 ["path", { d: "M4 17v2", key: "vumght" }], 478 ["path", { d: "M5 18H3", key: "zchphs" }] 479]); 480/** 481 * @license lucide-react v0.456.0 - ISC 482 * 483 * This source code is licensed under the ISC license. 484 * See the LICENSE file in the root directory of this source tree. 485 */ 486const SquareTerminal = createLucideIcon("SquareTerminal", [ 487 ["path", { d: "m7 11 2-2-2-2", key: "1lz0vl" }], 488 ["path", { d: "M11 13h4", key: "1p7l4v" }], 489 ["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2", key: "1m3agn" }] 490]); 491/** 492 * @license lucide-react v0.456.0 - ISC 493 * 494 * This source code is licensed under the ISC license. 495 * See the LICENSE file in the root directory of this source tree. 496 */ 497const Square = createLucideIcon("Square", [ 498 ["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }] 499]); 500/** 501 * @license lucide-react v0.456.0 - ISC 502 * 503 * This source code is licensed under the ISC license. 504 * See the LICENSE file in the root directory of this source tree. 505 */ 506const Store = createLucideIcon("Store", [ 507 ["path", { d: "m2 7 4.41-4.41A2 2 0 0 1 7.83 2h8.34a2 2 0 0 1 1.42.59L22 7", key: "ztvudi" }], 508 ["path", { d: "M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8", key: "1b2hhj" }], 509 ["path", { d: "M15 22v-4a2 2 0 0 0-2-2h-2a2 2 0 0 0-2 2v4", key: "2ebpfo" }], 510 ["path", { d: "M2 7h20", key: "1fcdvo" }], 511 [ 512 "path", 513 { 514 d: "M22 7v3a2 2 0 0 1-2 2a2.7 2.7 0 0 1-1.59-.63.7.7 0 0 0-.82 0A2.7 2.7 0 0 1 16 12a2.7 2.7 0 0 1-1.59-.63.7.7 0 0 0-.82 0A2.7 2.7 0 0 1 12 12a2.7 2.7 0 0 1-1.59-.63.7.7 0 0 0-.82 0A2.7 2.7 0 0 1 8 12a2.7 2.7 0 0 1-1.59-.63.7.7 0 0 0-.82 0A2.7 2.7 0 0 1 4 12a2 2 0 0 1-2-2V7", 515 key: "6c3vgh" 516 } 517 ] 518]); 519/** 520 * @license lucide-react v0.456.0 - ISC 521 * 522 * This source code is licensed under the ISC license. 523 * See the LICENSE file in the root directory of this source tree. 524 */ 525const Tablet = createLucideIcon("Tablet", [ 526 ["rect", { width: "16", height: "20", x: "4", y: "2", rx: "2", ry: "2", key: "76otgf" }], 527 ["line", { x1: "12", x2: "12.01", y1: "18", y2: "18", key: "1dp563" }] 528]); 529/** 530 * @license lucide-react v0.456.0 - ISC 531 * 532 * This source code is licensed under the ISC license. 533 * See the LICENSE file in the root directory of this source tree. 534 */ 535const TrendingUp = createLucideIcon("TrendingUp", [ 536 ["polyline", { points: "22 7 13.5 15.5 8.5 10.5 2 17", key: "126l90" }], 537 ["polyline", { points: "16 7 22 7 22 13", key: "kwv8wd" }] 538]); 539/** 540 * @license lucide-react v0.456.0 - ISC 541 * 542 * This source code is licensed under the ISC license. 543 * See the LICENSE file in the root directory of this source tree. 544 */
545const Truck = createLucideIcon("Truck", [ 546 ["path", { d: "M14 18V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v11a1 1 0 0 0 1 1h2", key: "wrbu53" }], 547 ["path", { d: "M15 18H9", key: "1lyqi6" }], 548 [ 549 "path", 550 { 551 d: "M19 18h2a1 1 0 0 0 1-1v-3.65a1 1 0 0 0-.22-.624l-3.48-4.35A1 1 0 0 0 17.52 8H14", 552 key: "lysw3i" 553 } 554 ], 555 ["circle", { cx: "17", cy: "18", r: "2", key: "332jqn" }], 556 ["circle", { cx: "7", cy: "18", r: "2", key: "19iecd" }] 557]); 558/** 559 * @license lucide-react v0.456.0 - ISC 560 * 561 * This source code is licensed under the ISC license. 562 * See the LICENSE file in the root directory of this source tree. 563 */ 564const Type = createLucideIcon("Type", [ 565 ["polyline", { points: "4 7 4 4 20 4 20 7", key: "1nosan" }], 566 ["line", { x1: "9", x2: "15", y1: "20", y2: "20", key: "swin9y" }], 567 ["line", { x1: "12", x2: "12", y1: "4", y2: "20", key: "1tx1rr" }] 568]); 569/** 570 * @license lucide-react v0.456.0 - ISC 571 * 572 * This source code is licensed under the ISC license. 573 * See the LICENSE file in the root directory of this source tree. 574 */ 575const Users = createLucideIcon("Users", [ 576 ["path", { d: "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2", key: "1yyitq" }], 577 ["circle", { cx: "9", cy: "7", r: "4", key: "nufk8" }], 578 ["path", { d: "M22 21v-2a4 4 0 0 0-3-3.87", key: "kshegd" }], 579 ["path", { d: "M16 3.13a4 4 0 0 1 0 7.75", key: "1da9ce" }] 580]); 581async function postSessionMessage(url, body, getToken, signal) { 582 var _a; 583 const deadline = Date.now() + 6 * 6e4; 584 const serialized = JSON.stringify(body); 585 while (!signal.aborted) { 586 const token = await getToken(); 587 if (!token) throw new Error("Please sign in again to send your message."); 588 const response = await fetch(url, { 589 method: "POST", 590 headers: { 591 "Content-Type": "application/json", 592 Authorization: `Bearer ${token}` 593 }, 594 body: serialized, 595 signal 596 }); 597 if (response.status !== 202) return response; 598 await ((_a = response.body) == null ? void 0 : _a.cancel()); 599 if (Date.now() >= deadline) throw new Error("Chat history is still saving. Please retry your message in a moment."); 600 await new Promise((resolve, reject) => { 601 const aborted = () => { 602 clearTimeout(timer); 603 reject(new DOMException("Aborted", "AbortError")); 604 }; 605 const timer = setTimeout(() => { 606 signal.removeEventListener("abort", aborted); 607 resolve(); 608 }, 2e3); 609 signal.addEventListener("abort", aborted, { 610 once: true 611 }); 612 if (signal.aborted) aborted(); 613 }); 614 } 615 throw new DOMException("Aborted", "AbortError"); 616} 617function sessionUploadWarning(data) { 618 if (data.status === "failed" || data.status === "truncated") return { 619 reason: "session_upload", 620 message: data.status === "truncated" ? "This conversation reached its history limit. Some recent chat history could not be saved. Your website changes are saved separately." : "Some recent chat history could not be saved. Your website changes are saved separately." 621 }; 622 return data.warning === true ? { 623 reason: "session_size" 624 } : null; 625} 626async function resumeChatRestore(options) { 627 var _a; 628 let pending = options.pending; 629 if (pending && (pending.websiteId !== options.websiteId || pending.preMessageCommitHash !== options.preMessageCommitHash)) throw new Error("Finish retrying the pending conversation restore before choosing another point."); 630 let result = pending ? await options.advance(pending) : await options.start(); 631 const deadline = options.deadline ?? Date.now() + 6 * 6e4; 632 while ((result == null ? void 0 : result.pending) && result.admissionId && result.sourceSessionId) { 633 pending = { 634 websiteId: options.websiteId, 635 preMessageCommitHash: options.preMessageCommitHash, 636 admissionId: result.admissionId, 637 sourceSessionId: result.sourceSessionId 638 }; 639 options.remember(pending); 640 if (Date.now() >= deadline) return result; 641 await (((_a = options.wait) == null ? void 0 : _a.call(options)) ?? new Promise((resolve) => setTimeout(resolve, 2e3))); 642 result = await options.advance(pending); 643 } 644 if (result && !result.pending) options.remember(null); 645 return result; 646} 647const ownerSide = (f) => f.role === "OWNER" || f.role === "ADMIN"; 648const SITE_SURFACES = [{ 649 surface: "website", 650 label: "Website", 651 hint: "Your live site", 652 icon: Globe, 653 // The site itself is never gated: a viewer who can reach the editor at all 654 // can look at the site. Everything below needs edit access first. 655 allowed: () => true 656}, { 657 surface: "orders", 658 label: "Orders", 659 hint: "Sales and shipping", 660 icon: ShoppingBag, 661 allowed: (f) => f.canEdit && ownerSide(f) && f.ownerCommerceEnabled 662}, { 663 surface: "products", 664 label: "Products", 665 hint: "What you sell", 666 icon: Package, 667 // Editor tier, not owner tier: a catalog is the site's content. 668 allowed: (f) => f.canEdit && f.ownerCommerceEnabled 669}, { 670 surface: "leads", 671 label: "Leads", 672 hint: "People who reached out", 673 icon: Users, 674 allowed: (f) => f.canEdit && ownerSide(f) && f.ownerCommerceEnabled 675}, { 676 surface: "invoices", 677 label: "Invoices", 678 hint: "What youâve billed", 679 icon: FileText, 680 allowed: (f) => f.canEdit && ownerSide(f) && f.ownerCommerceEnabled 681}, { 682 surface: "analytics", 683 label: "Analytics", 684 hint: "Who visits, and from where", 685 icon: ChartNoAxesColumn, 686 /** 687 * DELIBERATELY OUTSIDE `ownerCommerceEnabled`. Analytics shipped behind a 688 * rollout flag of its own; that flag is gone and it is on for every site, 689 * including ones that sell nothing. "Payments off, analytics on" is a real 690 * combination the switcher has to render rather than hiding wholesale. 691 * 692 * It does NOT control collection â the prod-serve-website workers gate 693 * that independently, so a site not yet collecting renders the honest 694 * pre-collection state rather than a broken panel. 695 */ 696 allowed: (f) => f.canEdit && ownerSide(f) 697}]; 698const SITE_SURFACE_NAMES = SITE_SURFACES.map((s) => s.surface); 699const SITE_SURFACE_NAME_SET = new Set(SITE_SURFACE_NAMES); 700const isSiteSurfaceName = (view) => SITE_SURFACE_NAME_SET.has(view); 701const editorSiteSurfaces = (facts, hasSiteToken) => SITE_SURFACES.filter((s) => s.surface === "website" || hasSiteToken && s.allowed(facts)); 702const canOpenSiteSurface = (view, facts, hasSiteToken) => editorSiteSurfaces(facts, hasSiteToken).some((s) => s.surface === view); 703function isToolPart(part) { 704 return typeof part.type === "string" && part.type.startsWith("tool-"); 705} 706function getToolCallId(part) { 707 if (!isToolPart(part)) return void 0; 708 return part.toolCallId; 709} 710function makeToolInputPart(fields) { 711 return { 712 type: `tool-${fields.name}`, 713 toolCallId: fields.toolCallId, 714 state: "input-available", 715 input: fields.input, 716 parentToolUseId: fields.parentToolUseId ?? null 717 }; 718} 719function applyToolResult(part, result, agentOutput) { 720 const nextAgentOutput = agentOutput ?? part.agentOutput; 721 if (result.isError) { 722 return { 723 type: part.type, 724 toolCallId: part.toolCallId, 725 input: part.input, 726 state: "output-error", 727 errorText: typeof result.output === "string" ? result.output : JSON.stringify(result.output ?? ""), 728 parentToolUseId: part.parentToolUseId ?? null, 729 agentOutput: nextAgentOutput 730 }; 731 } 732 return { 733 type: part.type, 734 toolCallId: part.toolCallId, 735 input: part.input,
736 state: "output-available", 737 output: result.output, 738 parentToolUseId: part.parentToolUseId ?? null, 739 agentOutput: nextAgentOutput 740 }; 741} 742const genId = () => crypto.randomUUID(); 743function partDtoToChatPart(dto) { 744 if (dto.type === "text") { 745 if (typeof dto.text !== "string") return null; 746 return { 747 type: "text", 748 text: dto.text 749 }; 750 } 751 if (dto.type.startsWith("tool-")) { 752 if (!dto.toolCallId) return null; 753 const parentToolUseId = dto.parentToolUseId ?? null; 754 let agentOutput; 755 if (dto.type === "tool-Agent" && dto.agentOutput && typeof dto.agentOutput === "object") { 756 const ao = dto.agentOutput; 757 if (ao.status === "completed" || ao.status === "async_launched" || ao.status === "sub_agent_entered") { 758 agentOutput = { 759 status: ao.status, 760 totalToolUseCount: ao.totalToolUseCount, 761 totalDurationMs: ao.totalDurationMs, 762 totalTokens: ao.totalTokens 763 }; 764 } 765 } 766 const base = { 767 type: dto.type, 768 toolCallId: dto.toolCallId, 769 parentToolUseId, 770 agentOutput 771 }; 772 if (dto.state === "output-available") { 773 const part2 = { 774 ...base, 775 state: "output-available", 776 input: dto.input ?? {}, 777 output: dto.output 778 }; 779 return part2; 780 } 781 if (dto.state === "output-error") { 782 const part2 = { 783 ...base, 784 state: "output-error", 785 input: dto.input ?? {}, 786 errorText: typeof dto.output === "string" ? dto.output : JSON.stringify(dto.output ?? "") 787 }; 788 return part2; 789 } 790 const part = { 791 ...base, 792 state: "input-available", 793 input: dto.input ?? {} 794 }; 795 return part; 796 } 797 return null; 798} 799function historyMessageToChatMessage(dto) { 800 const parts = []; 801 for (const partDto of dto.parts ?? []) { 802 const part = partDtoToChatPart(partDto); 803 if (part) parts.push(part); 804 } 805 return { 806 id: dto.id, 807 role: dto.role, 808 parts, 809 // metadata is stored as opaque JSON on the GraphQL side; pass 810 // through as-is so existing consumers (e.g., commitHash badges) see 811 // the same object they did before. 812 metadata: dto.metadata ?? void 0 813 }; 814} 815function historyToChatMessages(dtos) { 816 if (!dtos) return []; 817 return dtos.map(historyMessageToChatMessage); 818} 819function normalizeQuestions(raw) { 820 const questions = normalizeQuestionList(raw); 821 if (questions.length === 0) { 822 return [{ 823 question: "The assistant asked a question, but it arrived empty. Type your answer below.", 824 header: "", 825 options: [], 826 multiSelect: false 827 }]; 828 } 829 return questions; 830} 831function normalizeQuestionList(raw) { 832 if (!Array.isArray(raw)) return []; 833 return raw.filter((q) => !!q && typeof q === "object").map((q, i) => { 834 const header = typeof q.header === "string" ? q.header : ""; 835 const question = typeof q.question === "string" && q.question ? q.question : header || `Question ${i + 1}`; 836 const options = Array.isArray(q.options) ? q.options.filter((o) => !!o && typeof o === "object" && typeof o.label === "string").map((o) => ({ 837 label: o.label, 838 description: typeof o.description === "string" ? o.description : "" 839 })) : []; 840 return { 841 question, 842 header, 843 options, 844 multiSelect: q.multiSelect === true 845 }; 846 }); 847} 848const isDev = false; 849let sseSeq = 0; 850const turnStartTimes = /* @__PURE__ */ new Map(); 851function sseLog(seq, ...args) { 852} 853function sseWarn(seq, ...args) { 854} 855function createTurnContext() { 856 return { 857 seq: ++sseSeq, 858 assistantMsgId: genId(), 859 assistantCreated: false, 860 hasAccumulatedPartials: false, 861 lastEventId: -1 862 }; 863} 864function processShowViewEvent(data, pageMountTimeMs, lastShowViewRef, onShowView) { 865 const emittedAt = typeof data.emittedAt === "number" ? data.emittedAt : 0; 866 if (!emittedAt || emittedAt < pageMountTimeMs) return; 867 const nseq = typeof data.nseq === "number" ? data.nseq : null; 868 const last = lastShowViewRef.current; 869 if (emittedAt === last.emittedAt || nseq !== null && nseq <= last.nseq) { 870 return; 871 } 872 const view = typeof data.view === "string" ? data.view : ""; 873 if (!isSiteSurfaceName(view)) return; 874 lastShowViewRef.current = { 875 nseq: nseq ?? last.nseq, 876 emittedAt 877 }; 878 const params = data.params; 879 const orderId = typeof (params == null ? void 0 : params.orderId) === "string" ? params.orderId : void 0; 880 const invoiceId = typeof (params == null ? void 0 : params.invoiceId) === "string" ? params.invoiceId : void 0; 881 onShowView == null ? void 0 : onShowView(view, orderId || invoiceId ? { 882 ...orderId ? { 883 orderId 884 } : {}, 885 ...invoiceId ? { 886 invoiceId 887 } : {} 888 } : void 0); 889} 890function processEvent(type, data, ctx, deps) { 891 var _a, _b, _c, _d, _e, _f, _g; 892 if (typeof data.seq === "number") { 893 ctx.lastEventId = data.seq; 894 } 895 sseLog(ctx.seq, `event:${type}`, type === "text" || type === "partial" ? { 896 len: (_a = data.text) == null ? void 0 : _a.length 897 } : type === "session" ? { 898 sessionId: data.sessionId 899 } : type === "tool_use" ? { 900 tool: data.name 901 } : type === "commit" ? { 902 hash: (_b = data.hash) == null ? void 0 : _b.slice(0, 8) 903 } : type === "debug" ? data : {}); 904 const ensureAssistantMessage = () => { 905 if (ctx.assistantCreated) return; 906 ctx.assistantCreated = true; 907 sseLog(ctx.seq, "creating assistant message", { 908 id: ctx.assistantMsgId 909 }); 910 deps.setMessages((prev) => [...prev, { 911 id: ctx.assistantMsgId, 912 role: "assistant", 913 parts: [] 914 }]); 915 }; 916 const updateAssistantMessage = (mutate, label) => { 917 deps.setMessages((prev) => { 918 const updated = [...prev]; 919 for (let i = updated.length - 1; i >= 0; i--) { 920 if (updated[i].id === ctx.assistantMsgId) { 921 updated[i] = mutate(updated[i]); 922 return updated; 923 } 924 } 925 sseWarn(ctx.seq, `updateAssistantMessage: assistant msg ${ctx.assistantMsgId} not found${label ? ` (${label})` : ""} â dropping update`, { 926 messageIds: prev.map((m) => m.id), 927 role: prev.map((m) => m.role) 928 }); 929 return prev; 930 }); 931 }; 932 const appendText = (newText) => { 933 if (!newText) return; 934 ctx.hasAccumulatedPartials = true; 935 updateAssistantMessage((msg) => { 936 var _a2, _b2, _c2; 937 const parts = [...msg.parts]; 938 const lastIdx = parts.length - 1; 939 const last = lastIdx >= 0 ? parts[lastIdx] : null; 940 let action; 941 if (last && last.type === "text") { 942 action = "extend"; 943 parts[lastIdx] = { 944 ...last, 945 type: "text", 946 text: (last.text || "") + newText 947 }; 948 } else { 949 action = "push"; 950 parts.push({ 951 type: "text", 952 text: newText 953 }); 954 } 955 sseLog(ctx.seq, "appendText", { 956 action, 957 newTextHead: newText.slice(0, 16), 958 newTextLen: newText.length, 959 priorPartsCount: msg.parts.length, 960 priorLastType: last == null ? void 0 : last.type, 961 priorLastTextLen: (_a2 = last == null ? void 0 : last.text) == null ? void 0 : _a2.length, 962 resultPartsCount: parts.length, 963 resultLastTextLen: (_c2 = (_b2 = parts[parts.length - 1]) == null ? void 0 : _b2.text) == null ? void 0 : _c2.length 964 }); 965 return { 966 ...msg, 967 parts 968 }; 969 }); 970 }; 971 switch (type) { 972 case "session": 973 deps.sessionIdRef.current = data.sessionId; 974 ensureAssistantMessage(); 975 break; 976 case "user_message": 977 { 978 const text = data.text; 979 deps.setMessages((prev) => { 980 let lastUserIdx = -1; 981 for (let i = prev.length - 1; i >= 0; i--) { 982 if (prev[i].role === "user") { 983 lastUserIdx = i; 984 break; 985 } 986 } 987 if (lastUserIdx >= 0) { 988 const lastText = (prev[lastUserIdx].parts || []).filter((p) => p.type === "text").map((p) => p.text || "").join(""); 989 if (lastText === text) { 990 return lastUserIdx + 1 < prev.length ? prev.slice(0, lastUserIdx + 1) : prev; 991 } 992 } 993 return [...prev, { 994 id: genId(), 995 role: "user", 996 parts: [{ 997 type: "text", 998 text 999 }] 1000 }]; 1001 }); 1002 } 1003 break; 1004 case "partial": 1005 deps.setThinking(null); 1006 ensureAssistantMessage(); 1007 appendText(data.text); 1008 break; 1009 case "text": 1010 deps.setThinking(null); 1011 ensureAssistantMessage(); 1012 if (!ctx.hasAccumulatedPartials) { 1013 appendText(data.text); 1014 } else { 1015 sseLog(ctx.seq, "dropping duplicate text event (partials already streamed)"); 1016 } 1017 break; 1018 case "tool_use": 1019 deps.setThinking(null); 1020 ensureAssistantMessage(); 1021 { 1022 const toolCallId = data.id; 1023 const toolName = data.name; 1024 const input = data.input ?? {}; 1025 const parentToolUseId = typeof data.parentToolUseId === "string" ? data.parentToolUseId : null; 1026 sseLog(ctx.seq, "tool_use", { 1027 name: toolName, 1028 toolCallId, 1029 inputKeys: Object.keys(input).length, 1030 parent: (parentToolUseId == null ? void 0 : parentToolUseId.slice(-8)) ?? null 1031 }); 1032 updateAssistantMessage((msg) => { 1033 const existingIdx = msg.parts.findIndex((p) => isToolPart(p) && getToolCallId(p) === toolCallId); 1034 if (existingIdx !== -1) { 1035 const updated = [...msg.parts]; 1036 const existing = updated[existingIdx]; 1037 const merged = { 1038 ...existing, 1039 type: `tool-${toolName}`, 1040 toolCallId, 1041 input, 1042 // Keep an already-set parentToolUseId if the new event 1043 // omits one (e.g. an old client or a re-broadcast path 1044 // that lost the field). Otherwise trust the new value. 1045 parentToolUseId: parentToolUseId ?? existing.parentToolUseId ?? null 1046 }; 1047 updated[existingIdx] = merged; 1048 return { 1049 ...msg, 1050 parts: updated 1051 }; 1052 } 1053 const newPart = makeToolInputPart({ 1054 name: toolName, 1055 toolCallId, 1056 input, 1057 parentToolUseId 1058 }); 1059 return { 1060 ...msg, 1061 parts: [...msg.parts, newPart] 1062 }; 1063 }, `tool_use ${toolName}`); 1064 } 1065 break; 1066 case "tool_result": 1067 { 1068 const targetId = data.toolUseId; 1069 const toolNameFromServer = typeof data.toolName === "string" ? data.toolName : null; 1070 const result = { 1071 output: data.content, 1072 isError: Boolean(data.isError) 1073 }; 1074 const agentOutput = data.agentOutput && typeof data.agentOutput === "object" ? data.agentOutput : void 0; 1075 const parentToolUseId = typeof data.parentToolUseId === "string" ? data.parentToolUseId : null; 1076 sseLog(ctx.seq, "tool_result apply", { 1077 toolUseId: targetId, 1078 toolName: toolNameFromServer, 1079 isError: result.isError, 1080 hasAgentOutput: Boolean(agentOutput) 1081 }); 1082 updateAssistantMessage((msg) => { 1083 let matched = false;
1084 const newParts = msg.parts.map((p) => { 1085 if (isToolPart(p) && getToolCallId(p) === targetId) { 1086 matched = true; 1087 return applyToolResult(p, result, agentOutput); 1088 } 1089 return p; 1090 }); 1091 if (!matched) { 1092 sseWarn(ctx.seq, `orphan tool_result â no matching tool_use for ${targetId}; synthesizing ${toolNameFromServer ?? "Unknown"} row`); 1093 const placeholder = applyToolResult({ 1094 type: `tool-${toolNameFromServer ?? "Unknown"}`, 1095 toolCallId: targetId, 1096 input: {}, 1097 parentToolUseId 1098 }, result, agentOutput); 1099 newParts.push(placeholder); 1100 } 1101 return { 1102 ...msg, 1103 parts: newParts 1104 }; 1105 }, `tool_result ${targetId}`); 1106 } 1107 break; 1108 case "tool_discard": 1109 { 1110 const ids = Array.isArray(data.toolUseIds) ? data.toolUseIds : []; 1111 if (ids.length === 0) break; 1112 const discard = new Set(ids); 1113 sseLog(ctx.seq, "tool_discard", { 1114 count: ids.length 1115 }); 1116 updateAssistantMessage((msg) => ({ 1117 ...msg, 1118 parts: msg.parts.filter((p) => !(isToolPart(p) && discard.has(getToolCallId(p) ?? ""))) 1119 })); 1120 } 1121 break; 1122 case "commit": 1123 updateAssistantMessage((msg) => ({ 1124 ...msg, 1125 metadata: { 1126 ...msg.metadata || {}, 1127 commitHash: data.hash, 1128 commitMessage: data.message, 1129 pushed: data.pushed 1130 } 1131 })); 1132 break; 1133 case "approval_request": 1134 deps.setPendingApproval({ 1135 approvalId: data.approvalId, 1136 toolName: data.toolName, 1137 description: data.description 1138 }); 1139 break; 1140 case "ask_user_question": 1141 deps.setPendingQuestion({ 1142 interactionId: data.interactionId, 1143 questions: normalizeQuestions(data.questions) 1144 }); 1145 break; 1146 case "session_upload_status": { 1147 const warning = sessionUploadWarning(data); 1148 if (warning && (!data.emittedAt || Number(data.emittedAt) >= deps.pageMountTimeMs)) (_c = deps.onSessionWarning) == null ? void 0 : _c.call(deps, warning); 1149 break; 1150 } 1151 case "session_warning": 1152 sseLog(ctx.seq, "session_warning", data); 1153 { 1154 const emittedAt = typeof data.emittedAt === "number" ? data.emittedAt : 0; 1155 if (emittedAt && emittedAt < deps.pageMountTimeMs) { 1156 break; 1157 } 1158 (_d = deps.onSessionWarning) == null ? void 0 : _d.call(deps, { 1159 bytes: typeof data.bytes === "number" ? data.bytes : void 0, 1160 reason: typeof data.reason === "string" ? data.reason : void 0, 1161 message: typeof data.message === "string" ? data.message : void 0 1162 }); 1163 } 1164 break; 1165 case "show_view": 1166 processShowViewEvent(data, deps.pageMountTimeMs, deps.lastShowViewRef, deps.onShowView); 1167 break; 1168 case "chat_reset": 1169 { 1170 const resetAt = typeof data.resetAt === "number" ? data.resetAt : 0; 1171 if (resetAt < deps.pageMountTimeMs || resetAt <= deps.lastResetAtRef.current) { 1172 sseLog(ctx.seq, "chat_reset ignored (stale replay)", { 1173 eventResetAt: resetAt, 1174 pageMount: deps.pageMountTimeMs, 1175 currentResetAt: deps.lastResetAtRef.current 1176 }); 1177 break; 1178 } 1179 sseLog(ctx.seq, "chat_reset received, clearing messages"); 1180 deps.setMessages([]); 1181 deps.setPendingApproval(null); 1182 deps.setPendingQuestion(null); 1183 deps.setThinking(null); 1184 deps.setMaxTurnsHit(false); 1185 deps.setHistoryError(null); 1186 deps.sessionIdRef.current = null; 1187 deps.lastResetAtRef.current = resetAt; 1188 (_e = deps.onChatReset) == null ? void 0 : _e.call(deps); 1189 } 1190 break; 1191 case "compaction": { 1192 const preTokens = typeof data.preTokens === "number" ? data.preTokens : null; 1193 const tokenSuffix = preTokens ? ` (${(preTokens / 1e3).toFixed(0)}K tokens condensed)` : ""; 1194 const marker = { 1195 id: `compaction-${Date.now()}`, 1196 role: "assistant", 1197 parts: [{ 1198 type: "text", 1199 text: `â Conversation summarized to manage context length${tokenSuffix} â` 1200 }] 1201 }; 1202 deps.setMessages((prev) => { 1203 let lastUserIdx = -1; 1204 for (let i = prev.length - 1; i >= 0; i--) { 1205 if (prev[i].role === "user") { 1206 lastUserIdx = i; 1207 break; 1208 } 1209 } 1210 const tail = lastUserIdx >= 0 ? prev.slice(lastUserIdx) : []; 1211 return [marker, ...tail]; 1212 }); 1213 break; 1214 } 1215 case "result": 1216 deps.setThinking(null); 1217 if (data.subtype === "error_max_turns") { 1218 sseLog(ctx.seq, "maxTurns hit â surfacing Continue affordance"); 1219 deps.setMaxTurnsHit(true); 1220 } 1221 break; 1222 case "thinking_start": { 1223 ensureAssistantMessage(); 1224 const now = Date.now(); 1225 deps.setThinking({ 1226 active: true, 1227 startedAt: now, 1228 chars: 0, 1229 lastDeltaAt: now 1230 }); 1231 updateAssistantMessage((msg) => ({ 1232 ...msg, 1233 parts: [...msg.parts, { 1234 type: "thinking", 1235 text: "", 1236 // Mark as in-progress via a dummy duration of -1 so the 1237 // UI can distinguish "still thinking" (animated) from 1238 // "done" (static duration badge). 1239 thinkingDurationMs: -1, 1240 // Client-only: stamp start time so thinking_stop can 1241 // compute the elapsed duration without a second round 1242 // trip through the floating-indicator state. 1243 _startedAt: now 1244 }] 1245 }), "thinking_start"); 1246 break; 1247 } 1248 case "thinking_delta": {
1249 const chars = typeof data.chars === "number" ? data.chars : 0; 1250 const text = typeof data.text === "string" ? data.text : ""; 1251 const now = Date.now(); 1252 deps.setThinking((prev) => prev ? { 1253 ...prev, 1254 chars: prev.chars + chars, 1255 lastDeltaAt: now 1256 } : null); 1257 if (text) { 1258 updateAssistantMessage((msg) => { 1259 const parts = [...msg.parts]; 1260 const lastIdx = parts.length - 1; 1261 const last = lastIdx >= 0 ? parts[lastIdx] : null; 1262 if (last && last.type === "thinking") { 1263 parts[lastIdx] = { 1264 ...last, 1265 type: "thinking", 1266 text: (last.text || "") + text 1267 }; 1268 } else { 1269 parts.push({ 1270 type: "thinking", 1271 text, 1272 thinkingDurationMs: -1 1273 }); 1274 } 1275 return { 1276 ...msg, 1277 parts 1278 }; 1279 }, "thinking_delta"); 1280 } 1281 break; 1282 } 1283 case "thinking_stop": { 1284 const now = Date.now(); 1285 updateAssistantMessage((msg) => { 1286 const parts = [...msg.parts]; 1287 const lastIdx = parts.length - 1; 1288 const last = lastIdx >= 0 ? parts[lastIdx] : null; 1289 if (last && last.type === "thinking") { 1290 const durationMs = last._startedAt ? now - last._startedAt : 0; 1291 parts[lastIdx] = { 1292 ...last, 1293 thinkingDurationMs: durationMs 1294 }; 1295 } 1296 return { 1297 ...msg, 1298 parts 1299 }; 1300 }, "thinking_stop"); 1301 deps.setThinking(null); 1302 break; 1303 } 1304 case "error": 1305 deps.setThinking(null); 1306 if (data.type === "session_expired") { 1307 (_f = deps.onSessionExpired) == null ? void 0 : _f.call(deps, typeof data.message === "string" ? data.message : ""); 1308 } else { 1309 (_g = deps.onError) == null ? void 0 : _g.call(deps, Object.assign(new Error(data.message || "Agent error"), { 1310 type: typeof data.type === "string" ? data.type : void 0 1311 })); 1312 } 1313 break; 1314 } 1315} 1316async function consumeSseStream(body, ctx, onEvent, signal) { 1317 const reader = body.getReader(); 1318 const decoder = new TextDecoder(); 1319 const parseFrame = (frame) => { 1320 let eventType = ""; 1321 const dataLines = []; 1322 for (const line of frame.split("\n")) { 1323 if (line.startsWith("event: ")) { 1324 eventType = line.slice(7).trim(); 1325 } else if (line.startsWith("data: ")) { 1326 dataLines.push(line.slice(6)); 1327 } 1328 } 1329 if (dataLines.length === 0) return; 1330 try { 1331 const parsed = JSON.parse(dataLines.join("\n")); 1332 onEvent(eventType, parsed); 1333 } catch (e) { 1334 sseWarn(ctx.seq, "Failed to parse event data:", e); 1335 } 1336 }; 1337 let buffer = ""; 1338 try { 1339 while (true) { 1340 if (signal == null ? void 0 : signal.aborted) break; 1341 const { 1342 done, 1343 value 1344 } = await reader.read(); 1345 if (done) break; 1346 buffer += decoder.decode(value, { 1347 stream: true 1348 }); 1349 let frameEnd = buffer.indexOf("\n\n"); 1350 while (frameEnd !== -1) { 1351 const frame = buffer.slice(0, frameEnd); 1352 buffer = buffer.slice(frameEnd + 2); 1353 parseFrame(frame); 1354 frameEnd = buffer.indexOf("\n\n"); 1355 } 1356 } 1357 buffer += decoder.decode(); 1358 if (buffer.trim()) parseFrame(buffer); 1359 } finally { 1360 try { 1361 reader.releaseLock(); 1362 } catch { 1363 } 1364 } 1365} 1366const RETRYABLE_AGENT_ERROR_TYPES = /* @__PURE__ */ new Set(["api_error", "stream_parse_error"]); 1367const AUTH_REQUIRED_MESSAGE = "Authentication required. Please refresh the page."; 1368function useAgentSSE(options) { 1369 const { 1370 publicId, 1371 websiteId, 1372 getToken, 1373 onFinish, 1374 onError, 1375 onSessionWarning, 1376 onSessionExpired, 1377 onChatReset, 1378 onNavigate, 1379 onShowView 1380 } = options; 1381 const [messages, setMessages] = reactExports.useState([]); 1382 const [status, setStatusRaw] = reactExports.useState("idle"); 1383 const MESSAGE_FLUSH_MS = 80; 1384 const pendingMessageUpdatesRef = reactExports.useRef([]); 1385 const messageFlushTimerRef = reactExports.useRef(null); 1386 const flushMessagesNow = reactExports.useCallback(() => { 1387 if (messageFlushTimerRef.current !== null) { 1388 clearTimeout(messageFlushTimerRef.current); 1389 messageFlushTimerRef.current = null; 1390 } 1391 const updates = pendingMessageUpdatesRef.current; 1392 if (updates.length === 0) return; 1393 pendingMessageUpdatesRef.current = []; 1394 setMessages((prev) => updates.reduce((acc, u) => u(acc), prev)); 1395 }, []); 1396 const queueMessagesUpdate = reactExports.useCallback((update) => { 1397 pendingMessageUpdatesRef.current.push(typeof update === "function" ? update : () => update); 1398 if (messageFlushTimerRef.current === null) { 1399 messageFlushTimerRef.current = setTimeout(() => { 1400 messageFlushTimerRef.current = null; 1401 flushMessagesNow(); 1402 }, MESSAGE_FLUSH_MS); 1403 } 1404 }, [flushMessagesNow]); 1405 const setStatus = reactExports.useCallback((s) => { 1406 if (s === "idle" || s === "error") flushMessagesNow(); 1407 setStatusRaw(s); 1408 }, [flushMessagesNow]); 1409 const setMessagesOrdered = reactExports.useCallback((update) => { 1410 queueMessagesUpdate(update); 1411 flushMessagesNow(); 1412 }, [queueMessagesUpdate, flushMessagesNow]); 1413 reactExports.useEffect(() => { 1414 return () => { 1415 if (messageFlushTimerRef.current !== null) { 1416 clearTimeout(messageFlushTimerRef.current); 1417 messageFlushTimerRef.current = null; 1418 } 1419 pendingMessageUpdatesRef.current = []; 1420 }; 1421 }, []); 1422 const prevStatusRef = reactExports.useRef(status); 1423 reactExports.useEffect(() => { 1424 const prev = prevStatusRef.current; 1425 prevStatusRef.current = status; 1426 const turnEnded = (prev === "streaming" || prev === "connecting") && status !== "streaming" && status !== "connecting"; 1427 if (!turnEnded) return; 1428 setMessagesOrdered((prevMsgs) => { 1429 let lastAssistantIdx = -1; 1430 for (let i = prevMsgs.length - 1; i >= 0; i--) { 1431 if (prevMsgs[i].role === "assistant") { 1432 lastAssistantIdx = i; 1433 break; 1434 } 1435 } 1436 if (lastAssistantIdx === -1) return prevMsgs; 1437 const m = prevMsgs[lastAssistantIdx]; 1438 let msgChanged = false;
1439 const parts = m.parts.map((p) => { 1440 if (isToolPart(p) && p.type !== "tool-Agent" && !p.parentToolUseId && (p.state === "input-available" || p.state === "input-streaming")) { 1441 msgChanged = true; 1442 return { 1443 ...p, 1444 state: "output-available", 1445 output: p.output ?? "" 1446 }; 1447 } 1448 return p; 1449 }); 1450 if (!msgChanged) return prevMsgs; 1451 const next = [...prevMsgs]; 1452 next[lastAssistantIdx] = { 1453 ...m, 1454 parts 1455 }; 1456 return next; 1457 }); 1458 }, [status]); 1459 const [thinking, setThinking] = reactExports.useState(null); 1460 const [pendingApproval, setPendingApproval] = reactExports.useState(null); 1461 const [maxTurnsHit, setMaxTurnsHit] = reactExports.useState(false); 1462 const [approvalSubmitting, setApprovalSubmitting] = reactExports.useState(null); 1463 const [pendingQuestion, setPendingQuestion] = reactExports.useState(null); 1464 const [historyLoading, setHistoryLoading] = reactExports.useState(true); 1465 const [historyError, setHistoryError] = reactExports.useState(null); 1466 const [historyLoadAttempt, setHistoryLoadAttempt] = reactExports.useState(0); 1467 const posthogRef = reactExports.useRef(usePostHog()); 1468 const abortRef = reactExports.useRef(null); 1469 const currentTurnIdRef = reactExports.useRef(null); 1470 const sessionIdRef = reactExports.useRef(null); 1471 const pendingAdmissionRef = reactExports.useRef(null); 1472 const pendingResetIdRef = reactExports.useRef(null); 1473 const lastEventIdRef = reactExports.useRef(-1); 1474 const lastResetAtRef = reactExports.useRef(0); 1475 const pageMountTimeRef = reactExports.useRef(Date.now()); 1476 const historyAppliedRef = reactExports.useRef(false); 1477 const pendingResumeTurnIdRef = reactExports.useRef(null); 1478 const previewDomain = "landingsite.dev"; 1479 const baseUrl = `https://${publicId}.${previewDomain}`; 1480 const gatewayUrl = `${baseUrl}/__agent/chat`; 1481 const resumeStreamUrl = `${baseUrl}/__agent/resume-stream`; 1482 const activeTurnUrl = `${baseUrl}/__agent/active-turn`; 1483 const notificationStreamUrl = `${baseUrl}/__agent/events`; 1484 const notificationLastEventIdRef = reactExports.useRef(-1); 1485 const lastShowViewRef = reactExports.useRef({ 1486 nseq: -1, 1487 emittedAt: 0 1488 }); 1489 const loadHistoryFromSandbox = reactExports.useCallback(async () => { 1490 const token = await getToken(); 1491 if (!token) return null; 1492 try { 1493 const res = await fetch(`${baseUrl}/__agent/history`, { 1494 headers: { 1495 Authorization: `Bearer ${token}` 1496 } 1497 }); 1498 if (!res.ok) { 1499 console.warn("[useAgentSSE] /__agent/history non-OK:", res.status); 1500 return null; 1501 } 1502 const body = await res.json(); 1503 return { 1504 messages: body.messages ?? [], 1505 sessionId: body.sessionId ?? null, 1506 source: body.source ?? "unknown" 1507 }; 1508 } catch (err) { 1509 console.warn("[useAgentSSE] /__agent/history failed:", err); 1510 return null; 1511 } 1512 }, [baseUrl, getToken]); 1513 const openResumeStream = reactExports.useCallback(async (turnId, startingLastEventId = -1) => { 1514 var _a; 1515 if (currentTurnIdRef.current === turnId) { 1516 return; 1517 } 1518 (_a = abortRef.current) == null ? void 0 : _a.abort(); 1519 currentTurnIdRef.current = turnId; 1520 setStatus("connecting"); 1521 let token = null; 1522 try { 1523 token = await getToken(); 1524 } catch (err) { 1525 sseWarn(0, "openResumeStream getToken threw:", err.message); 1526 } 1527 if (!token) { 1528 onError == null ? void 0 : onError(new Error("No auth token available")); 1529 setStatus("idle"); 1530 currentTurnIdRef.current = null; 1531 return; 1532 } 1533 const ctx = createTurnContext(); 1534 turnStartTimes.set(ctx.seq, Date.now()); 1535 ctx.lastEventId = startingLastEventId; 1536 sseLog(ctx.seq, "openResumeStream", { 1537 turnId, 1538 startingLastEventId 1539 }); 1540 const controller = new AbortController(); 1541 abortRef.current = controller; 1542 setStatus("streaming"); 1543 const MAX_ATTEMPTS = 6; 1544 const BACKOFFS_MS = [500, 1e3, 2e3, 4e3, 8e3, 15e3]; 1545 let attempt = 0; 1546 while (attempt < MAX_ATTEMPTS) { 1547 if (controller.signal.aborted) break;
1548 try { 1549 const url = `${resumeStreamUrl}?turnId=${encodeURIComponent(turnId)}&lastEventId=${encodeURIComponent(ctx.lastEventId)}`; 1550 sseLog(ctx.seq, `resume connect attempt=${attempt} lastEventId=${ctx.lastEventId}`); 1551 const response = await fetch(url, { 1552 headers: { 1553 Authorization: `Bearer ${token}` 1554 }, 1555 signal: controller.signal 1556 }); 1557 if (response.status === 409) { 1558 sseLog(ctx.seq, "resume 409: turn ended or stale"); 1559 break; 1560 } 1561 if (!response.ok) { 1562 throw new Error(`Resume request failed (${response.status})`); 1563 } 1564 if (!response.body) throw new Error("No response body"); 1565 attempt = 0; 1566 await consumeSseStream(response.body, ctx, (type, data) => { 1567 sseLog(ctx.seq, `resume recv type=${type} turnId=${turnId} visibility=${typeof document !== "undefined" ? document.visibilityState : "n/a"}`); 1568 processEvent(type, data, ctx, { 1569 setMessages: queueMessagesUpdate, 1570 setPendingApproval, 1571 setPendingQuestion, 1572 setThinking, 1573 setMaxTurnsHit, 1574 setHistoryError, 1575 sessionIdRef, 1576 pageMountTimeMs: pageMountTimeRef.current, 1577 lastResetAtRef, 1578 onError, 1579 onSessionWarning, 1580 onSessionExpired, 1581 onChatReset, 1582 onShowView, 1583 lastShowViewRef 1584 }); 1585 }, controller.signal); 1586 lastEventIdRef.current = ctx.lastEventId; 1587 sseLog(ctx.seq, "resume stream end (clean)"); 1588 break; 1589 } catch (err) { 1590 if (err.name === "AbortError") { 1591 sseLog(ctx.seq, "resume aborted"); 1592 break; 1593 } 1594 attempt++; 1595 if (attempt >= MAX_ATTEMPTS) { 1596 sseWarn(ctx.seq, "resume giving up after max attempts"); 1597 setStatus("error"); 1598 onError == null ? void 0 : onError(new Error("Lost connection to the agent. Refresh to reconnect.")); 1599 return; 1600 } 1601 const delay = BACKOFFS_MS[Math.min(attempt - 1, BACKOFFS_MS.length - 1)]; 1602 const jittered = delay + Math.floor(Math.random() * 250); 1603 sseWarn(ctx.seq, `resume error, retrying in ${jittered}ms (attempt ${attempt})`, err.message); 1604 await new Promise((resolve) => { 1605 const timer = setTimeout(resolve, jittered); 1606 const onAbort = () => { 1607 clearTimeout(timer); 1608 resolve(); 1609 }; 1610 controller.signal.addEventListener("abort", onAbort, { 1611 once: true 1612 }); 1613 }); 1614 } 1615 } 1616 if (!controller.signal.aborted) { 1617 setStatus("idle"); 1618 } 1619 abortRef.current = null; 1620 if (currentTurnIdRef.current === turnId) { 1621 currentTurnIdRef.current = null; 1622 } 1623 }, [resumeStreamUrl, getToken, onError, onSessionWarning, onSessionExpired, onChatReset, onNavigate, onShowView]); 1624 const openNotificationStream = reactExports.useCallback(async (signal) => { 1625 var _a; 1626 const BACKOFFS_MS = [500, 1e3, 2e3, 4e3, 8e3, 15e3]; 1627 const IDLE_POLL_MS = 3e4; 1628 let attempt = 0; 1629 while (!signal.aborted) { 1630 try { 1631 const token = await getToken(); 1632 if (!token) { 1633 attempt++; 1634 const delay = BACKOFFS_MS[Math.min(attempt - 1, BACKOFFS_MS.length - 1)]; 1635 const jittered = delay + Math.floor(Math.random() * 250); 1636 await new Promise((resolve) => { 1637 const timer = setTimeout(resolve, jittered); 1638 const onAbort = () => { 1639 clearTimeout(timer); 1640 resolve(); 1641 }; 1642 signal.addEventListener("abort", onAbort, { 1643 once: true 1644 }); 1645 }); 1646 continue; 1647 } 1648 const url = `${notificationStreamUrl}?lastEventId=${encodeURIComponent(notificationLastEventIdRef.current)}`; 1649 if (isDev) ; 1650 const response = await fetch(url, { 1651 headers: { 1652 Authorization: `Bearer ${token}` 1653 }, 1654 signal 1655 }); 1656 if (!response.ok) { 1657 if (response.status === 401 || response.status === 403) { 1658 console.warn("[useAgentSSE] Notification stream auth failed"); 1659 return; 1660 } 1661 throw new Error(`Notification stream failed (${response.status})`); 1662 } 1663 if (response.status === 204 || response.headers.get("X-Sandbox-State") === "idle") { 1664 try { 1665 await ((_a = response.body) == null ? void 0 : _a.cancel()); 1666 } catch { 1667 } 1668 attempt = 0; 1669 const jittered = IDLE_POLL_MS + Math.floor(Math.random() * 1e3); 1670 if (isDev) ; 1671 await new Promise((resolve) => { 1672 const timer = setTimeout(resolve, jittered); 1673 const onAbort = () => { 1674 clearTimeout(timer); 1675 resolve(); 1676 }; 1677 signal.addEventListener("abort", onAbort, { 1678 once: true 1679 }); 1680 }); 1681 continue; 1682 } 1683 if (!response.body) throw new Error("No response body"); 1684 attempt = 0; 1685 const reader = response.body.getReader(); 1686 const decoder = new TextDecoder(); 1687 let buffer = ""; 1688 const parseFrame = (frame) => { 1689 let eventType = "";
1690 const dataLines = []; 1691 for (const line of frame.split("\n")) { 1692 if (line.startsWith("event: ")) { 1693 eventType = line.slice(7).trim(); 1694 } else if (line.startsWith("data: ")) { 1695 dataLines.push(line.slice(6)); 1696 } 1697 } 1698 if (dataLines.length === 0) return; 1699 try { 1700 const parsed = JSON.parse(dataLines.join("\n")); 1701 console.log(`[useAgentSSE] notification recv type=${eventType} nseq=${(parsed == null ? void 0 : parsed.nseq) ?? "?"} visibility=${typeof document !== "undefined" ? document.visibilityState : "n/a"}`); 1702 handleNotificationEvent(eventType, parsed); 1703 } catch (e) { 1704 console.warn("[useAgentSSE] notification parse error:", e); 1705 } 1706 }; 1707 while (!signal.aborted) { 1708 const { 1709 done, 1710 value 1711 } = await reader.read(); 1712 if (done) break; 1713 console.log(`[useAgentSSE] notification chunk bytes=${(value == null ? void 0 : value.byteLength) ?? 0} visibility=${typeof document !== "undefined" ? document.visibilityState : "n/a"}`); 1714 buffer += decoder.decode(value, { 1715 stream: true 1716 }); 1717 let frameEnd = buffer.indexOf("\n\n"); 1718 while (frameEnd !== -1) { 1719 const frame = buffer.slice(0, frameEnd); 1720 buffer = buffer.slice(frameEnd + 2); 1721 parseFrame(frame); 1722 frameEnd = buffer.indexOf("\n\n"); 1723 } 1724 } 1725 if (!signal.aborted) { 1726 if (isDev) ; 1727 continue; 1728 } 1729 return; 1730 } catch (err) { 1731 if (signal.aborted || err.name === "AbortError") { 1732 return; 1733 } 1734 attempt++; 1735 const delay = BACKOFFS_MS[Math.min(attempt - 1, BACKOFFS_MS.length - 1)]; 1736 const jittered = delay + Math.floor(Math.random() * 250); 1737 await new Promise((resolve) => { 1738 const timer = setTimeout(resolve, jittered); 1739 const onAbort = () => { 1740 clearTimeout(timer); 1741 resolve(); 1742 }; 1743 signal.addEventListener("abort", onAbort, { 1744 once: true 1745 }); 1746 }); 1747 } 1748 } 1749 }, [notificationStreamUrl, getToken]); 1750 function handleNotificationEvent(type, data) { 1751 if (typeof data.nseq === "number") { 1752 notificationLastEventIdRef.current = data.nseq; 1753 } 1754 switch (type) { 1755 case "chat_reset": 1756 { 1757 const resetAt = typeof data.resetAt === "number" ? data.resetAt : 0; 1758 if (resetAt < pageMountTimeRef.current) { 1759 break; 1760 } 1761 if (resetAt <= lastResetAtRef.current) { 1762 break; 1763 } 1764 setMessagesOrdered([]); 1765 setPendingApproval(null); 1766 setPendingQuestion(null); 1767 setThinking(null); 1768 setMaxTurnsHit(false); 1769 setHistoryError(null); 1770 sessionIdRef.current = null; 1771 lastResetAtRef.current = resetAt; 1772 onChatReset == null ? void 0 : onChatReset(); 1773 } 1774 break; 1775 case "turn_started": 1776 { 1777 const turnId = data.turnId; 1778 if (!turnId) break; 1779 const startedAt = data.startedAt; 1780 if (startedAt && new Date(startedAt).getTime() < pageMountTimeRef.current) { 1781 break; 1782 } 1783 if (abortRef.current !== null && currentTurnIdRef.current === null) { 1784 break; 1785 } 1786 if (!historyAppliedRef.current) { 1787 pendingResumeTurnIdRef.current = turnId; 1788 break; 1789 } 1790 void openResumeStream(turnId, -1); 1791 } 1792 break; 1793 case "turn_ended": 1794 break; 1795 case "session_upload_status": { 1796 const warning = sessionUploadWarning(data); 1797 if (warning && (!data.emittedAt || Number(data.emittedAt) >= pageMountTimeRef.current)) onSessionWarning == null ? void 0 : onSessionWarning(warning); 1798 break; 1799 } 1800 case "session_warning": 1801 { 1802 const emittedAt = typeof data.emittedAt === "number" ? data.emittedAt : 0; 1803 if (emittedAt && emittedAt < pageMountTimeRef.current) { 1804 break; 1805 } 1806 onSessionWarning == null ? void 0 : onSessionWarning({
1807 bytes: typeof data.bytes === "number" ? data.bytes : void 0, 1808 reason: typeof data.reason === "string" ? data.reason : void 0, 1809 message: typeof data.message === "string" ? data.message : void 0 1810 }); 1811 } 1812 break; 1813 case "navigate": 1814 { 1815 const emittedAt = typeof data.emittedAt === "number" ? data.emittedAt : 0; 1816 if (!emittedAt || emittedAt < pageMountTimeRef.current) { 1817 break; 1818 } 1819 const target = typeof data.target === "string" ? data.target : void 0; 1820 if (target && target.length > 0 && target.startsWith("/") && !target.startsWith("//")) { 1821 onNavigate == null ? void 0 : onNavigate(target); 1822 } else { 1823 console.warn("[useAgentSSE] navigate event dropped â unsafe or missing target", { 1824 target 1825 }); 1826 } 1827 } 1828 break; 1829 case "show_view": 1830 processShowViewEvent(data, pageMountTimeRef.current, lastShowViewRef, onShowView); 1831 break; 1832 case "error": 1833 console.warn("[useAgentSSE] notification stream error event:", data); 1834 if (data.type === "subscriber_too_late") { 1835 notificationLastEventIdRef.current = -1; 1836 } else if (data.type === "session_expired") { 1837 onSessionExpired == null ? void 0 : onSessionExpired(typeof data.message === "string" ? data.message : ""); 1838 } 1839 break; 1840 } 1841 } 1842 reactExports.useEffect(() => {
1843 let cancelled = false; 1844 if (!websiteId || !publicId) return; 1845 async function loadAndResume() { 1846 var _a; 1847 setHistoryLoading(true); 1848 setHistoryError(null); 1849 historyAppliedRef.current = false; 1850 pendingResumeTurnIdRef.current = null; 1851 const token = await getToken(); 1852 const activeTurnP = token ? fetch(activeTurnUrl, { 1853 headers: { 1854 Authorization: `Bearer ${token}` 1855 } 1856 }).then((r) => r.ok ? r.json() : null).catch(() => null) : Promise.resolve(null); 1857 const BACKOFFS_MS = [1e3, 1e3, 1e3, 1e3, 1e3, 1e3, 1e3, 1e3, 1e3, 1e3, 2e3, 4e3, 8e3]; 1858 let historyData = null; 1859 const DEFINITIVE_HISTORY_SOURCES = /* @__PURE__ */ new Set(["sandbox-disk", "sandbox-restored", "sandbox-empty", "history-expired"]); 1860 const historyStartedAt = Date.now(); 1861 let historyAttempts = 0; 1862 let lastHistorySource = "fetch-failed"; 1863 for (let attempt = 0; attempt <= BACKOFFS_MS.length; attempt++) { 1864 if (cancelled) return; 1865 const r = await loadHistoryFromSandbox(); 1866 if (cancelled) return; 1867 historyAttempts = attempt + 1; 1868 lastHistorySource = (r == null ? void 0 : r.source) ?? "fetch-failed"; 1869 if (r && DEFINITIVE_HISTORY_SOURCES.has(r.source)) { 1870 historyData = r; 1871 break; 1872 } 1873 if (attempt === BACKOFFS_MS.length) break; 1874 console.log("[useAgentSSE] history transient, retrying", { 1875 attempt: attempt + 1, 1876 source: (r == null ? void 0 : r.source) ?? "fetch-failed", 1877 delayMs: BACKOFFS_MS[attempt] 1878 }); 1879 await new Promise((resolve) => { 1880 setTimeout(resolve, BACKOFFS_MS[attempt]); 1881 }); 1882 } 1883 if (cancelled) return; 1884 try { 1885 (_a = posthogRef.current) == null ? void 0 : _a.capture("preview_boot", { 1886 step: historyData ? "chat_history_loaded" : "chat_history_failed", 1887 websiteId, 1888 publicId, 1889 durationMs: Date.now() - historyStartedAt, 1890 attempts: historyAttempts, 1891 source: lastHistorySource, 1892 sinceNavigationMs: Math.round(performance.now()) 1893 }); 1894 } catch { 1895 } 1896 const rawHistory = (historyData == null ? void 0 : historyData.messages) ?? null; 1897 const historySource = (historyData == null ? void 0 : historyData.source) ?? "fetch-failed"; 1898 const historySessionId = (historyData == null ? void 0 : historyData.sessionId) ?? null; 1899 if (!historyData) { 1900 console.warn("[useAgentSSE] sandbox /__agent/history failed after retries"); 1901 } 1902 console.log("[useAgentSSE] history loaded", { 1903 source: historySource, 1904 sessionId: historySessionId, 1905 count: (rawHistory == null ? void 0 : rawHistory.length) ?? 0 1906 }); 1907 if (rawHistory && rawHistory.length > 0) { 1908 const history = historyToChatMessages(rawHistory); 1909 console.log("[useAgentSSE] history adapted", { 1910 count: history.length 1911 }); 1912 if (history.length > 0) { 1913 setMessagesOrdered((prev) => prev.length > 0 ? prev : history); 1914 if (historySessionId) { 1915 sessionIdRef.current = historySessionId; 1916 } 1917 } 1918 } 1919 if (!historyData) { 1920 setHistoryError("Couldn't load chat history. The preview server may be starting up."); 1921 } 1922 setHistoryLoading(false); 1923 historyAppliedRef.current = true; 1924 const activeTurn = await activeTurnP; 1925 if (cancelled) return; 1926 if (activeTurn && typeof activeTurn.lastResetAt === "number") { 1927 lastResetAtRef.current = activeTurn.lastResetAt; 1928 } 1929 if (activeTurn && activeTurn.active && activeTurn.turnId) { 1930 void openResumeStream(activeTurn.turnId, -1); 1931 } 1932 if (pendingResumeTurnIdRef.current) { 1933 const pendingTurnId = pendingResumeTurnIdRef.current; 1934 pendingResumeTurnIdRef.current = null; 1935 void openResumeStream(pendingTurnId, -1); 1936 } 1937 } 1938 void loadAndResume(); 1939 return () => { 1940 cancelled = true; 1941 }; 1942 }, [websiteId, publicId, historyLoadAttempt]); 1943 reactExports.useEffect(() => { 1944 if (!websiteId || !publicId) return; 1945 const controller = new AbortController(); 1946 void openNotificationStream(controller.signal); 1947 const onVisibility = () => { 1948 console.log(`[useAgentSSE] visibility â ${document.visibilityState} at ${(/* @__PURE__ */ new Date()).toISOString()}`); 1949 }; 1950 document.addEventListener("visibilitychange", onVisibility); 1951 return () => { 1952 controller.abort(); 1953 document.removeEventListener("visibilitychange", onVisibility); 1954 }; 1955 }, [websiteId, publicId]); 1956 const sendMessage = reactExports.useCallback(async (message) => { 1957 var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j; 1958 const ctx = createTurnContext(); 1959 turnStartTimes.set(ctx.seq, Date.now()); 1960 sseLog(ctx.seq, "sendMessage", { 1961 messageId: message.id 1962 });
1963 setMaxTurnsHit(false); 1964 setMessagesOrdered((prev) => [...prev, message]); 1965 setStatus("connecting"); 1966 let token = null; 1967 try { 1968 token = await getToken(); 1969 } catch (err) { 1970 sseWarn(ctx.seq, "getToken threw:", err.message); 1971 } 1972 if (!token) { 1973 onError == null ? void 0 : onError(new Error("No auth token available")); 1974 setStatus("idle"); 1975 return "failed"; 1976 } 1977 (_a = abortRef.current) == null ? void 0 : _a.abort(); 1978 const controller = new AbortController(); 1979 abortRef.current = controller; 1980 sseLog(ctx.seq, "connecting"); 1981 let streamingBegan = false; 1982 try { 1983 const messageText = ((_b = message.parts) == null ? void 0 : _b.filter((p) => p.type === "text").map((p) => p.text).join("\n")) || ""; 1984 const fileParts = ((_c = message.parts) == null ? void 0 : _c.flatMap((p) => { 1985 if (p.type !== "file") return []; 1986 const url = p.url; 1987 const mediaType = p.mediaType; 1988 if (typeof url !== "string" || typeof mediaType !== "string" || !url.startsWith("https://")) { 1989 return []; 1990 } 1991 return [{ 1992 url, 1993 mediaType 1994 }]; 1995 })) || []; 1996 sseLog(ctx.seq, "fetching", { 1997 url: gatewayUrl, 1998 fileParts: fileParts.length 1999 }); 2000 const intent = JSON.stringify({ 2001 websiteId, 2002 message: messageText, 2003 fileParts 2004 }); 2005 if (((_d = pendingAdmissionRef.current) == null ? void 0 : _d.intent) !== intent) { 2006 pendingAdmissionRef.current = { 2007 intent, 2008 id: crypto.randomUUID() 2009 }; 2010 } 2011 const response = await postSessionMessage(gatewayUrl, { 2012 message: messageText, 2013 sessionId: sessionIdRef.current, 2014 websiteId, 2015 admissionId: pendingAdmissionRef.current.id, 2016 fileParts: fileParts.length > 0 ? fileParts : void 0 2017 }, getToken, controller.signal); 2018 if (response.status === 409) { 2019 const errBody = await response.json().catch(() => ({})); 2020 if (["admission_not_activated", "admission_owner_changed", "stale_generation", "admission_ended"].includes(((_e = errBody.error) == null ? void 0 : _e.type) || "")) pendingAdmissionRef.current = null; 2021 if (!["turn_active", "turn_already_active"].includes(((_f = errBody.error) == null ? void 0 : _f.type) || "")) { 2022 throw new Error(((_g = errBody.error) == null ? void 0 : _g.message) || "Your message could not start yet. Please retry."); 2023 } 2024 pendingAdmissionRef.current = null; 2025 const existingTurnId = (_h = errBody.error) == null ? void 0 : _h.turnId; 2026 sseLog(ctx.seq, "turn already active; subscribing", { 2027 existingTurnId 2028 }); 2029 setMessagesOrdered((prev) => prev.filter((m) => m.id !== message.id)); 2030 setStatus("idle"); 2031 if (existingTurnId) { 2032 void openResumeStream(existingTurnId, -1); 2033 } 2034 return "turn_active"; 2035 } 2036 if (!response.ok) { 2037 const errorBody = await response.text(); 2038 let errorMsg = `Chat request failed (${response.status})`; 2039 try { 2040 const parsed = JSON.parse(errorBody); 2041 if ((_i = parsed.error) == null ? void 0 : _i.message) errorMsg = parsed.error.message; 2042 } catch (e) { 2043 sseWarn(ctx.seq, "Failed to parse error response:", e); 2044 } 2045 if (response.status === 402) { 2046 errorMsg = "You have run out of chat credits."; 2047 } 2048 throw new Error(errorMsg); 2049 } 2050 if ((_j = response.headers.get("Content-Type")) == null ? void 0 : _j.includes("application/json")) { 2051 const replay = await response.json(); 2052 if (!replay.alreadyActivated) throw new Error("Your message has not started yet. Please retry."); 2053 const recovered = await loadHistoryFromSandbox(); 2054 if (controller.signal.aborted || abortRef.current !== controller) return "started"; 2055 if (!recovered || !["sandbox-disk", "sandbox-restored", "sandbox-empty", "history-expired"].includes(recovered.source)) throw new Error("Your message finished, but its history is still loading. Please retry.");
2056 if (["sandbox-empty", "history-expired"].includes(recovered.source)) { 2057 setMessagesOrdered((prev) => prev.filter((m) => m.id !== message.id)); 2058 sessionIdRef.current = null; 2059 onSessionWarning == null ? void 0 : onSessionWarning(sessionUploadWarning({ 2060 status: "failed" 2061 })); 2062 } else { 2063 setMessagesOrdered(historyToChatMessages(recovered.messages)); 2064 if (recovered.sessionId) sessionIdRef.current = recovered.sessionId; 2065 } 2066 pendingAdmissionRef.current = null; 2067 setStatus("idle"); 2068 return "started"; 2069 } 2070 if (!response.body) throw new Error("No response body"); 2071 pendingAdmissionRef.current = null; 2072 sseLog(ctx.seq, "streaming started"); 2073 setStatus("streaming"); 2074 streamingBegan = true; 2075 await consumeSseStream(response.body, ctx, (type, data) => processEvent(type, data, ctx, { 2076 setMessages: queueMessagesUpdate, 2077 setPendingApproval, 2078 setPendingQuestion, 2079 setThinking, 2080 setMaxTurnsHit, 2081 setHistoryError, 2082 sessionIdRef, 2083 pageMountTimeMs: pageMountTimeRef.current, 2084 lastResetAtRef, 2085 onError, 2086 onSessionWarning, 2087 onSessionExpired, 2088 onChatReset, 2089 onShowView, 2090 lastShowViewRef 2091 }), controller.signal); 2092 lastEventIdRef.current = ctx.lastEventId; 2093 sseLog(ctx.seq, "stream end");
2094 try { 2095 const activeRes = await fetch(activeTurnUrl, { 2096 headers: { 2097 Authorization: `Bearer ${token}` 2098 }, 2099 signal: controller.signal 2100 }); 2101 if (activeRes.ok) { 2102 const activeBody = await activeRes.json(); 2103 if (activeBody.active && activeBody.turnId) { 2104 sseLog(ctx.seq, "POST stream ended mid-turn; switching to resume", { 2105 turnId: activeBody.turnId, 2106 lastEventId: ctx.lastEventId 2107 }); 2108 void openResumeStream(activeBody.turnId, ctx.lastEventId); 2109 return "started"; 2110 } 2111 } 2112 } catch (err) { 2113 if (err.name !== "AbortError") { 2114 sseWarn(ctx.seq, "active-turn check failed:", err.message); 2115 } 2116 } 2117 setStatus("idle"); 2118 setThinking(null); 2119 setMessagesOrdered((prev) => { 2120 const lastMsg = prev[prev.length - 1]; 2121 if ((lastMsg == null ? void 0 : lastMsg.id) === ctx.assistantMsgId) { 2122 onFinish == null ? void 0 : onFinish({ 2123 message: lastMsg 2124 }); 2125 } 2126 return prev; 2127 }); 2128 return "started"; 2129 } catch (err) { 2130 if (err.name === "AbortError") { 2131 sseLog(ctx.seq, "aborted"); 2132 setStatus("idle"); 2133 return streamingBegan ? "started" : "failed"; 2134 } 2135 if (streamingBegan) { 2136 try { 2137 const activeRes = await fetch(activeTurnUrl, { 2138 headers: { 2139 Authorization: `Bearer ${token}` 2140 } 2141 }); 2142 if (activeRes.ok) { 2143 const activeBody = await activeRes.json(); 2144 if (activeBody.active && activeBody.turnId) { 2145 sseLog(ctx.seq, "POST stream errored mid-turn; switching to resume", { 2146 turnId: activeBody.turnId, 2147 lastEventId: ctx.lastEventId, 2148 cause: err.message 2149 }); 2150 void openResumeStream(activeBody.turnId, ctx.lastEventId); 2151 return "started"; 2152 } 2153 } 2154 } catch (probeErr) { 2155 sseWarn(ctx.seq, "mid-turn active-turn probe failed:", probeErr.message); 2156 } 2157 } 2158 sseWarn(ctx.seq, "error:", err.message); 2159 setStatus("error"); 2160 if (ctx.assistantCreated) { 2161 setMessagesOrdered((prev) => prev.filter((m) => m.id !== ctx.assistantMsgId)); 2162 } 2163 onError == null ? void 0 : onError(err instanceof Error ? err : new Error(String(err))); 2164 return "failed"; 2165 } finally { 2166 abortRef.current = null; 2167 } 2168 }, [gatewayUrl, activeTurnUrl, websiteId, getToken, loadHistoryFromSandbox, onFinish, onError, onSessionWarning, onSessionExpired, onChatReset, onNavigate, onShowView, openResumeStream]); 2169 const respondToApproval = reactExports.useCallback(async (approvalId, approved) => { 2170 const token = await getToken(); 2171 if (!token) return; 2172 setApprovalSubmitting({ 2173 approvalId, 2174 approved 2175 }); 2176 try { 2177 const res = await fetch(`${baseUrl}/__agent/approve`, { 2178 method: "POST", 2179 headers: { 2180 "Content-Type": "application/json", 2181 Authorization: `Bearer ${token}` 2182 }, 2183 body: JSON.stringify({ 2184 approvalId, 2185 approved 2186 }) 2187 }); 2188 if (res.ok) { 2189 setPendingApproval(null); 2190 } else { 2191 onError == null ? void 0 : onError(new Error("Failed to send approval. Please try again.")); 2192 } 2193 } catch (err) { 2194 onError == null ? void 0 : onError(new Error("Failed to send approval. Please try again.")); 2195 } finally { 2196 setApprovalSubmitting(null); 2197 } 2198 }, [baseUrl, getToken, onError]); 2199 const respondToQuestion = reactExports.useCallback(async (interactionId, answers) => { 2200 const token = await getToken(); 2201 if (!token) return; 2202 try { 2203 const res = await fetch(`${baseUrl}/__agent/answer-questions`, { 2204 method: "POST", 2205 headers: { 2206 "Content-Type": "application/json", 2207 Authorization: `Bearer ${token}` 2208 }, 2209 body: JSON.stringify({ 2210 interactionId, 2211 answers 2212 }) 2213 }); 2214 if (res.ok) { 2215 setPendingQuestion(null); 2216 } else if (res.status === 404) { 2217 setPendingQuestion(null); 2218 } else { 2219 onError == null ? void 0 : onError(new Error("Failed to send answers. Please try again.")); 2220 } 2221 } catch (err) { 2222 onError == null ? void 0 : onError(new Error("Failed to send answers. Please try again.")); 2223 } 2224 }, [baseUrl, getToken, onError]); 2225 const stop = reactExports.useCallback(() => { 2226 var _a; 2227 void (async () => {
2228 try { 2229 const token = await getToken(); 2230 if (!token) return; 2231 await fetch(`${baseUrl}/__agent/stop`, { 2232 method: "POST", 2233 headers: { 2234 "Content-Type": "application/json", 2235 Authorization: `Bearer ${token}` 2236 }, 2237 body: JSON.stringify({ 2238 websiteId 2239 }) 2240 }); 2241 } catch { 2242 } 2243 })(); 2244 (_a = abortRef.current) == null ? void 0 : _a.abort(); 2245 setStatus("idle"); 2246 }, [baseUrl, getToken, websiteId]); 2247 const clearHistory = reactExports.useCallback(async () => { 2248 var _a; 2249 (_a = abortRef.current) == null ? void 0 : _a.abort(); 2250 abortRef.current = null; 2251 currentTurnIdRef.current = null; 2252 pendingResetIdRef.current || (pendingResetIdRef.current = crypto.randomUUID()); 2253 try { 2254 const token = await getToken(); 2255 if (!token) { 2256 throw new Error(AUTH_REQUIRED_MESSAGE); 2257 } 2258 const res = await fetch(`${baseUrl}/__agent/reset`, { 2259 method: "POST", 2260 headers: { 2261 "Content-Type": "application/json", 2262 Authorization: `Bearer ${token}` 2263 }, 2264 body: JSON.stringify({ 2265 websiteId, 2266 resetId: pendingResetIdRef.current 2267 }) 2268 }); 2269 if (!res.ok) throw new Error("Chat could not be reset. Please retry."); 2270 const body = await res.json(); 2271 pendingResetIdRef.current = null; 2272 pendingAdmissionRef.current = null; 2273 setMessagesOrdered([]); 2274 setPendingApproval(null); 2275 setPendingQuestion(null); 2276 setThinking(null); 2277 setMaxTurnsHit(false); 2278 sessionIdRef.current = null; 2279 lastEventIdRef.current = -1; 2280 setHistoryError(null); 2281 if (typeof body.resetAt === "number") { 2282 lastResetAtRef.current = body.resetAt; 2283 } 2284 } catch (error) { 2285 onError == null ? void 0 : onError(error instanceof Error ? error : new Error("Chat could not be reset.")); 2286 throw error; 2287 } finally { 2288 setStatus("idle"); 2289 } 2290 }, [baseUrl, websiteId]); 2291 const retryHistory = reactExports.useCallback(() => { 2292 setHistoryError(null); 2293 setHistoryLoadAttempt((n) => n + 1); 2294 }, []); 2295 return { 2296 messages, 2297 sendMessage, 2298 stop, 2299 status, 2300 isLoading: status === "connecting" || status === "streaming", 2301 // Ordered wrapper (queue + flush) so external mutations (e.g. restore) 2302 // can never apply against a stale base while stream updates are queued. 2303 setMessages: setMessagesOrdered, 2304 clearHistory, 2305 pendingApproval, 2306 approvalSubmitting, 2307 respondToApproval, 2308 pendingQuestion, 2309 respondToQuestion, 2310 historyLoading, 2311 historyError, 2312 retryHistory, 2313 thinking, 2314 maxTurnsHit 2315 }; 2316} 2317function getMessageStackPosition(messages, index) { 2318 var _a, _b, _c; 2319 const role = (_a = messages[index]) == null ? void 0 : _a.role; 2320 if (role !== "user" && role !== "assistant") return null; 2321 const prevRole = index > 0 ? (_b = messages[index - 1]) == null ? void 0 : _b.role : null; 2322 const nextRole = index < messages.length - 1 ? (_c = messages[index + 1]) == null ? void 0 : _c.role : null; 2323 const continuesPrev = prevRole === role; 2324 const continuesNext = nextRole === role; 2325 if (!continuesPrev && !continuesNext) return "solo"; 2326 if (!continuesPrev && continuesNext) return "first"; 2327 if (continuesPrev && continuesNext) return "middle"; 2328 return "last"; 2329} 2330function messageContinuesStack(messages, index) { 2331 var _a, _b; 2332 if (index <= 0) return false; 2333 return ((_a = messages[index]) == null ? void 0 : _a.role) === ((_b = messages[index - 1]) == null ? void 0 : _b.role); 2334} 2335function getChatBubbleRadiusClass(role, position) { 2336 switch (position) { 2337 case "solo": 2338 return "rounded-tl-[16px] rounded-tr-[4px] rounded-br-[16px] rounded-bl-[16px]"; 2339 case "first": 2340 return "rounded-tl-[16px] rounded-tr-[4px] rounded-br-[4px] rounded-bl-[16px]"; 2341 case "middle": 2342 return "rounded-tl-[16px] rounded-tr-[4px] rounded-br-[4px] rounded-bl-[16px]"; 2343 case "last": 2344 return "rounded-tl-[16px] rounded-tr-[4px] rounded-br-[16px] rounded-bl-[16px]"; 2345 } 2346} 2347const TAG_TO_NOUN = { 2348 // Interactive 2349 button: "button", 2350 a: "link", 2351 input: "input", 2352 textarea: "input", 2353 select: "dropdown", 2354 option: "option", 2355 form: "form", 2356 fieldset: "form group", 2357 label: "label", 2358 legend: "label", 2359 // Media 2360 img: "image", 2361 picture: "image", 2362 figure: "figure", 2363 figcaption: "caption", 2364 video: "video", 2365 audio: "audio", 2366 svg: "icon", 2367 iframe: "embed", 2368 // Headings & text 2369 h1: "heading", 2370 h2: "heading", 2371 h3: "heading", 2372 h4: "heading", 2373 h5: "heading", 2374 h6: "heading", 2375 p: "text", 2376 blockquote: "quote", 2377 span: "text", 2378 strong: "text", 2379 b: "text", 2380 em: "text", 2381 i: "text", 2382 small: "text", 2383 mark: "highlight",
2384 code: "code", 2385 pre: "code block", 2386 // Lists & tables 2387 ul: "list", 2388 ol: "list", 2389 li: "list item", 2390 dl: "list", 2391 dt: "list item", 2392 dd: "list item", 2393 table: "table", 2394 thead: "table", 2395 tbody: "table", 2396 tfoot: "table", 2397 tr: "table row", 2398 td: "table cell", 2399 th: "table cell", 2400 // Layout / structural 2401 nav: "navigation", 2402 header: "header", 2403 footer: "footer", 2404 main: "main content", 2405 aside: "sidebar", 2406 article: "article", 2407 section: "section", 2408 div: "section", 2409 // Misc 2410 details: "expandable", 2411 summary: "summary", 2412 hr: "divider", 2413 br: "line break" 2414}; 2415function friendlyElementNoun(tag) { 2416 return TAG_TO_NOUN[tag.toLowerCase()] ?? "element"; 2417} 2418function formatFriendlyLabel(tag, text) { 2419 const noun = friendlyElementNoun(tag); 2420 const trimmed = (text ?? "").trim(); 2421 if (!trimmed) return noun; 2422 return `${noun} â "${trimmed}"`; 2423} 2424const TOOL_ICONS = { 2425 // ââ file / code ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 2426 Read: BookOpen, 2427 Write: FilePen, 2428 // feather + paper 2429 Edit: FilePen, 2430 MultiEdit: FilePen, 2431 editPage: FilePen, 2432 Bash: SquareTerminal, 2433 // >_ 2434 // ââ search / lookup ââââââââââââââââââââââââââââââââââââââââââââââââââââââ 2435 Glob: Search, 2436 Grep: Search, 2437 WebSearch: Search, 2438 WebFetch: Search, 2439 websiteSemanticSearch: Search, 2440 pageGrepSearch: Search, 2441 "mcp__landingsite__website_semantic_search": Search, 2442 "mcp__landingsite__page_grep_search": Search, 2443 // ââ docs âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 2444 "mcp__landingsite-docs__search_docs": Files, 2445 // multiple pages 2446 // ââ structure / content ââââââââââââââââââââââââââââââââââââââââââââââââââ 2447 addNewSection: AppWindow, 2448 // application window 2449 addNewPage: FilePlus2, 2450 // default â review in harness 2451 "mcp__landingsite__create_form": ClipboardList, 2452 // default 2453 // ââ navigation âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 2454 navigateToPage: Compass, 2455 // default 2456 navigateToSettings: Settings, 2457 // default 2458 // ââ info / status ââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 2459 "mcp__landingsite__get_publishing_status": Activity, 2460 // default 2461 "mcp__landingsite__get_website_info": Info, 2462 // default 2463 "mcp__landingsite__get_domain_info": Globe, 2464 // default 2465 "mcp__landingsite__list_user_websites_and_domains": LayoutGrid, 2466 // default 2467 // ââ media ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 2468 "mcp__landingsite__search_for_images": Image, 2469 // default 2470 "mcp__landingsite__screenshot_preview": Camera, 2471 // default 2472 // ââ publish ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 2473 "mcp__landingsite__publish_website": Rocket, 2474 // default 2475 "mcp__landingsite__unpublish_website": EyeOff, 2476 // default 2477 // ââ business / merchant views ââââââââââââââââââââââââââââââââââââââââââââ 2478 "mcp__landingsite__show_view": Eye, 2479 // swaps a merchant view into the preview 2480 "mcp__landingsite__get_business_summary": Store, 2481 "mcp__landingsite__sales_summary": TrendingUp, 2482 "mcp__landingsite__list_orders": Package, 2483 "mcp__landingsite__get_order": Package, 2484 "mcp__landingsite__list_people": Users, 2485 "mcp__landingsite__get_person": User, 2486 "mcp__landingsite__get_next_payout": Banknote, 2487 "mcp__landingsite__mark_order_fulfilled": PackageCheck 2488}; 2489function getToolIcon(toolName) { 2490 return TOOL_ICONS[toolName] ?? null; 2491} 2492const SANDBOX_ROOT = /^\/home\/user\/website\//; 2493function stripHome(path) { 2494 return asString(path).replace(SANDBOX_ROOT, ""); 2495} 2496function truncate(str, max) { 2497 if (typeof str !== "string") return ""; 2498 if (str.length <= max) return str; 2499 return str.slice(0, max) + "â¦"; 2500} 2501function formatBytes(bytes) { 2502 if (bytes < 1024) return `${bytes}B`; 2503 if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`; 2504 return `${(bytes / (1024 * 1024)).toFixed(1)}MB`; 2505} 2506function asString(v) { 2507 if (typeof v === "string") return v; 2508 if (v == null) return ""; 2509 try { 2510 return JSON.stringify(v); 2511 } catch { 2512 return String(v); 2513 } 2514} 2515function countLines(str) { 2516 if (!str) return 0; 2517 const lines = str.split("\n"); 2518 if (lines[lines.length - 1] === "") lines.pop(); 2519 return lines.length; 2520} 2521function firstLine(str) { 2522 const idx = str.indexOf("\n"); 2523 return idx === -1 ? str : str.slice(0, idx); 2524} 2525function DimLabel({ 2526 children 2527}) { 2528 return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-gray-400", children }); 2529} 2530function HeaderPath({ 2531 path 2532}) { 2533 return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-gray-700", children: stripHome(path) }); 2534} 2535function CountSummary({ 2536 label, 2537 count: count2, 2538 bytes 2539}) { 2540 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[11px] text-gray-600", children: [ 2541 label, 2542 " ", 2543 count2, 2544 " line", 2545 count2 === 1 ? "" : "s",
2546 bytes !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-gray-400", children: [ 2547 " (", 2548 formatBytes(bytes), 2549 ")" 2550 ] }) 2551 ] }); 2552} 2553function MiniPatch({ 2554 before, 2555 after 2556}) { 2557 if (!before && !after) return null; 2558 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-h-96 overflow-auto rounded border border-gray-200 bg-white font-mono text-[11px]", children: [ 2559 before !== void 0 && before !== "" && /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "whitespace-pre border-b border-gray-100 bg-red-50 px-2 py-1 text-red-700", children: prefixLines(before, "â ") }), 2560 after !== void 0 && after !== "" && /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "whitespace-pre bg-green-50 px-2 py-1 text-green-700", children: prefixLines(after, "+ ") }) 2561 ] }); 2562} 2563function prefixLines(str, prefix) { 2564 const trimmed = str.endsWith("\n") ? str.slice(0, -1) : str; 2565 return trimmed.split("\n").map((line) => prefix + line).join("\n"); 2566} 2567const ReadRenderer = { 2568 header: (input) => { 2569 const path = input == null ? void 0 : input.file_path; 2570 if (!path) return null; 2571 const offset2 = input == null ? void 0 : input.offset; 2572 const limit = input == null ? void 0 : input.limit; 2573 return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
2574 /* @__PURE__ */ jsxRuntimeExports.jsx(HeaderPath, { path }), 2575 offset2 != null && limit != null && /* @__PURE__ */ jsxRuntimeExports.jsxs(DimLabel, { children: [ 2576 " ", 2577 "· lines ", 2578 offset2, 2579 "â", 2580 offset2 + limit 2581 ] }) 2582 ] }); 2583 }, 2584 result: ({ 2585 output, 2586 input 2587 }) => { 2588 var _a; 2589 const text = asString(output); 2590 if (!text) return null; 2591 const lines = countLines(text); 2592 const bytes = new Blob([text]).size; 2593 const path = asString(input == null ? void 0 : input.file_path); 2594 const ext = (_a = path.split(".").pop()) == null ? void 0 : _a.toLowerCase(); 2595 const isImage = ext ? ["png", "jpg", "jpeg", "gif", "webp", "svg"].includes(ext) : false; 2596 if (isImage) return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[11px] text-gray-600", children: "Read image" }); 2597 if (ext === "pdf") return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[11px] text-gray-600", children: "Read PDF" }); 2598 return /* @__PURE__ */ jsxRuntimeExports.jsx(CountSummary, { label: "Read", count: lines, bytes }); 2599 } 2600}; 2601const WriteRenderer = { 2602 header: (input) => { 2603 const path = input == null ? void 0 : input.file_path; 2604 const content = input == null ? void 0 : input.content; 2605 if (!path) return null; 2606 const bytes = content ? new Blob([content]).size : void 0; 2607 return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
2608 /* @__PURE__ */ jsxRuntimeExports.jsx(HeaderPath, { path }), 2609 bytes !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(DimLabel, { children: [ 2610 " · ", 2611 formatBytes(bytes) 2612 ] }) 2613 ] }); 2614 }, 2615 result: ({ 2616 input 2617 }) => { 2618 const content = input == null ? void 0 : input.content; 2619 if (!content) return null; 2620 const lines = countLines(content); 2621 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1.5", children: [ 2622 /* @__PURE__ */ jsxRuntimeExports.jsx(CountSummary, { label: "Wrote", count: lines }), 2623 /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "max-h-96 overflow-auto whitespace-pre rounded border border-gray-200 bg-white p-2 font-mono text-[11px] leading-relaxed text-gray-800", children: content }) 2624 ] }); 2625 } 2626}; 2627const EditRenderer = { 2628 header: (input) => { 2629 const path = input == null ? void 0 : input.file_path; 2630 return path ? /* @__PURE__ */ jsxRuntimeExports.jsx(HeaderPath, { path }) : null; 2631 }, 2632 result: ({ 2633 input 2634 }) => { 2635 const before = input == null ? void 0 : input.old_string; 2636 const after = input == null ? void 0 : input.new_string; 2637 if (!before && !after) return null; 2638 return /* @__PURE__ */ jsxRuntimeExports.jsx(MiniPatch, { before, after }); 2639 } 2640}; 2641const MultiEditRenderer = { 2642 header: (input) => { 2643 const path = input == null ? void 0 : input.file_path; 2644 const edits = input == null ? void 0 : input.edits; 2645 const n = Array.isArray(edits) ? edits.length : 0; 2646 if (!path) return null; 2647 return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
2648 /* @__PURE__ */ jsxRuntimeExports.jsx(HeaderPath, { path }), 2649 n > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(DimLabel, { children: [ 2650 " ", 2651 "· ", 2652 n, 2653 " edit", 2654 n === 1 ? "" : "s" 2655 ] }) 2656 ] }); 2657 }, 2658 result: ({ 2659 input 2660 }) => { 2661 const edits = input == null ? void 0 : input.edits; 2662 if (!Array.isArray(edits) || edits.length === 0) return null; 2663 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1.5", children: [ 2664 edits.slice(0, 3).map((edit, i) => /* @__PURE__ */ jsxRuntimeExports.jsx(MiniPatch, { before: edit.old_string, after: edit.new_string }, i)), 2665 edits.length > 3 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[11px] text-gray-500", children: [ 2666 "+ ", 2667 edits.length - 3, 2668 " more edit", 2669 edits.length - 3 === 1 ? "" : "s" 2670 ] }) 2671 ] }); 2672 } 2673}; 2674const GlobRenderer = { 2675 displayName: "Search", 2676 progressLabel: () => "Searchingâ¦", 2677 header: (input) => { 2678 const pattern = input == null ? void 0 : input.pattern; 2679 const path = input == null ? void 0 : input.path; 2680 return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-gray-700", children: [ 2681 pattern && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 2682 "pattern:", 2683 " ",
2684 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-gray-900", children: [ 2685 '"', 2686 truncate(pattern, 40), 2687 '"' 2688 ] }) 2689 ] }), 2690 path && /* @__PURE__ */ jsxRuntimeExports.jsxs(DimLabel, { children: [ 2691 ' · path: "', 2692 stripHome(path), 2693 '"' 2694 ] }) 2695 ] }); 2696 }, 2697 result: ({ 2698 output 2699 }) => { 2700 const text = asString(output); 2701 if (!text) return null; 2702 const files = text.split("\n").filter(Boolean); 2703 if (files.length === 0) return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[11px] text-gray-500", children: "No matches" }); 2704 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[11px]", children: [ 2705 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-1 text-gray-600", children: [ 2706 "Found ", 2707 files.length, 2708 " file", 2709 files.length === 1 ? "" : "s" 2710 ] }), 2711 /* @__PURE__ */ jsxRuntimeExports.jsxs("ul", { className: "space-y-0.5 font-mono text-gray-700", children: [ 2712 files.slice(0, 20).map((f, i) => /* @__PURE__ */ jsxRuntimeExports.jsx("li", { className: "truncate", children: stripHome(f) }, i)), 2713 files.length > 20 && /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { className: "text-gray-400", children: [ 2714 "+ ", 2715 files.length - 20, 2716 " more" 2717 ] }) 2718 ] }) 2719 ] }); 2720 } 2721}; 2722const GrepRenderer = { 2723 displayName: "Search", 2724 progressLabel: () => "Searchingâ¦", 2725 header: (input) => { 2726 const pattern = input == null ? void 0 : input.pattern; 2727 const path = input == null ? void 0 : input.path; 2728 return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-gray-700", children: [ 2729 pattern && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 2730 "pattern:", 2731 " ",
2732 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-gray-900", children: [ 2733 '"', 2734 truncate(pattern, 40), 2735 '"' 2736 ] }) 2737 ] }), 2738 path && /* @__PURE__ */ jsxRuntimeExports.jsxs(DimLabel, { children: [ 2739 ' · path: "', 2740 stripHome(path), 2741 '"' 2742 ] }) 2743 ] }); 2744 }, 2745 result: ({ 2746 output, 2747 input 2748 }) => { 2749 const mode = (input == null ? void 0 : input.output_mode) ?? "files_with_matches"; 2750 const text = asString(output); 2751 if (!text) return null; 2752 const lines = text.split("\n").filter(Boolean); 2753 if (lines.length === 0) return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[11px] text-gray-500", children: "No matches" }); 2754 if (mode === "count") { 2755 const total = lines.reduce((sum, l) => { 2756 const m = l.match(/:(\d+)$/); 2757 return sum + (m ? parseInt(m[1], 10) : 0); 2758 }, 0); 2759 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[11px] text-gray-600", children: [ 2760 total, 2761 " match", 2762 total === 1 ? "" : "es", 2763 " in ", 2764 lines.length, 2765 " file", 2766 lines.length === 1 ? "" : "s" 2767 ] }); 2768 } 2769 if (mode === "content") { 2770 return /* @__PURE__ */ jsxRuntimeExports.jsxs("pre", { className: "max-h-48 overflow-auto whitespace-pre-wrap break-all rounded bg-white p-1.5 font-mono text-[11px] text-gray-800", children: [ 2771 lines.slice(0, 30).join("\n"), 2772 lines.length > 30 && ` 2773⦠+ ${lines.length - 30} more` 2774 ] }); 2775 } 2776 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[11px]", children: [
2777 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-1 text-gray-600", children: [ 2778 "Found matches in ", 2779 lines.length, 2780 " file", 2781 lines.length === 1 ? "" : "s" 2782 ] }), 2783 /* @__PURE__ */ jsxRuntimeExports.jsxs("ul", { className: "space-y-0.5 font-mono text-gray-700", children: [ 2784 lines.slice(0, 20).map((f, i) => /* @__PURE__ */ jsxRuntimeExports.jsx("li", { className: "truncate", children: stripHome(f) }, i)), 2785 lines.length > 20 && /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { className: "text-gray-400", children: [ 2786 "+ ", 2787 lines.length - 20, 2788 " more" 2789 ] }) 2790 ] }) 2791 ] }); 2792 } 2793}; 2794const BashRenderer = { 2795 // Once the command has streamed in, keep it in the header while it runs â 2796 // a long-running command (a polling loop) otherwise reads as an opaque 2797 // "Runningâ¦" with no way to see what it's waiting on. 2798 progressLabel: (input) => (input == null ? void 0 : input.command) ? null : "Runningâ¦", 2799 hideDefaultInput: true, 2800 header: (input) => { 2801 const cmd = input == null ? void 0 : input.command; 2802 if (!cmd) return null; 2803 return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "truncate font-mono text-gray-800", children: [ 2804 "$ ", 2805 truncate(firstLine(cmd), 120) 2806 ] }); 2807 }, 2808 result: ({ 2809 output, 2810 input 2811 }) => { 2812 const cmd = (input == null ? void 0 : input.command) ?? ""; 2813 const text = asString(output); 2814 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1.5", children: [
2815 cmd && /* @__PURE__ */ jsxRuntimeExports.jsxs("pre", { className: "whitespace-pre-wrap break-all rounded bg-gray-900 p-1.5 font-mono text-[11px] text-gray-100", children: [ 2816 "$ ", 2817 cmd 2818 ] }), 2819 text && /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "max-h-64 overflow-auto whitespace-pre-wrap break-all rounded bg-white p-1.5 font-mono text-[11px] text-gray-800", children: text }), 2820 text && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[10px] text-gray-400", children: [ 2821 countLines(text), 2822 " lines · ", 2823 formatBytes(new Blob([text]).size) 2824 ] }) 2825 ] }); 2826 } 2827}; 2828const WebFetchRenderer = { 2829 progressLabel: () => "Fetchingâ¦", 2830 header: (input) => { 2831 const url = input == null ? void 0 : input.url; 2832 if (!url) return null; 2833 return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-gray-700", children: [ 2834 "url: ", 2835 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-gray-900", children: [ 2836 '"', 2837 truncate(url, 60), 2838 '"' 2839 ] }) 2840 ] }); 2841 }, 2842 result: ({ 2843 output 2844 }) => { 2845 const text = asString(output); 2846 if (!text) return null; 2847 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[11px] text-gray-600", children: [ 2848 "Received", 2849 " ",
2850 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-gray-400", children: [ 2851 "(", 2852 formatBytes(new Blob([text]).size), 2853 ")" 2854 ] }) 2855 ] }); 2856 } 2857}; 2858const WebSearchRenderer = { 2859 progressLabel: () => "Searchingâ¦", 2860 header: (input) => { 2861 const query = input == null ? void 0 : input.query; 2862 if (!query) return null; 2863 return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-gray-700", children: [ 2864 "query: ", 2865 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-gray-900", children: [ 2866 '"', 2867 truncate(query, 60), 2868 '"' 2869 ] }) 2870 ] }); 2871 }, 2872 result: ({ 2873 output 2874 }) => { 2875 const text = asString(output); 2876 if (!text) return null; 2877 const blocks = text.split(/\n{2,}/).filter(Boolean); 2878 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[11px] text-gray-600", children: [ 2879 "Found ", 2880 blocks.length, 2881 " result", 2882 blocks.length === 1 ? "" : "s" 2883 ] }); 2884 } 2885}; 2886const SearchDocsRenderer = { 2887 displayName: "Search Help Docs", 2888 progressLabel: () => "Searching help docsâ¦", 2889 header: (input) => { 2890 const query = input == null ? void 0 : input.query; 2891 if (!query) return null; 2892 return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-gray-700", children: [ 2893 "query: ",
2894 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-gray-900", children: [ 2895 '"', 2896 truncate(query, 60), 2897 '"' 2898 ] }) 2899 ] }); 2900 }, 2901 result: ({ 2902 output 2903 }) => { 2904 let raw = output; 2905 if (typeof raw === "string") { 2906 try { 2907 raw = JSON.parse(raw); 2908 } catch { 2909 } 2910 } 2911 if (Array.isArray(raw)) { 2912 const first = raw[0]; 2913 if ((first == null ? void 0 : first.type) === "text" && typeof first.text === "string") { 2914 try { 2915 raw = JSON.parse(first.text); 2916 } catch { 2917 } 2918 } 2919 } else if (raw && typeof raw === "object" && "content" in raw) { 2920 const content = raw.content; 2921 const first = Array.isArray(content) ? content[0] : null; 2922 if ((first == null ? void 0 : first.type) === "text" && typeof first.text === "string") { 2923 try { 2924 raw = JSON.parse(first.text); 2925 } catch { 2926 } 2927 } 2928 } 2929 let hits = null; 2930 if (raw && typeof raw === "object" && "hits" in raw) { 2931 hits = raw.hits; 2932 } 2933 const count2 = Array.isArray(hits) ? hits.length : 0; 2934 if (count2 === 0) { 2935 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[11px] text-gray-600", children: "No matching docs found." }); 2936 } 2937 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[11px] text-gray-600", children: [ 2938 "Found ", 2939 count2, 2940 " doc", 2941 count2 === 1 ? "" : "s" 2942 ] }); 2943 } 2944}; 2945const AskUserQuestionRenderer = { 2946 displayName: "Question", 2947 progressLabel: () => "Waiting for your answerâ¦", 2948 hideDefaultInput: true, 2949 header: (input) => { 2950 const raw = input == null ? void 0 : input.questions; 2951 const questions = (Array.isArray(raw) ? raw : []).filter((q) => !!q && typeof q === "object"); 2952 if (questions.length === 0) return null; 2953 if (questions.length === 1) { 2954 const label = questions[0].question || questions[0].header || ""; 2955 return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-gray-700", children: truncate(label, 60) }); 2956 } 2957 return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-gray-700", children: [ 2958 questions.length, 2959 " question", 2960 questions.length === 1 ? "" : "s",
2961 /* @__PURE__ */ jsxRuntimeExports.jsxs(DimLabel, { children: [ 2962 " ", 2963 "·", 2964 " ", 2965 questions.map((q) => q.header || "").filter(Boolean).join(", ") 2966 ] }) 2967 ] }); 2968 }, 2969 result: ({ 2970 output 2971 }) => { 2972 let parsed = null; 2973 if (output && typeof output === "object") { 2974 parsed = output; 2975 } else if (typeof output === "string") { 2976 try { 2977 parsed = JSON.parse(output); 2978 } catch { 2979 } 2980 } 2981 if ((parsed == null ? void 0 : parsed.questions) && (parsed == null ? void 0 : parsed.answers)) { 2982 const { 2983 questions, 2984 answers 2985 } = parsed; 2986 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-1 text-[11px]", children: questions.map((q, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 2987 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-gray-500", children: q.question }), 2988 " â ", 2989 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-medium text-green-700", children: answers[q.question] || "(skipped)" }) 2990 ] }, i)) }); 2991 } 2992 const text = asString(output); 2993 if (!text) return null; 2994 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[11px] text-gray-600", children: truncate(text, 200) }); 2995 } 2996}; 2997function unwrapMcpJson(output) { 2998 let raw = output; 2999 if (typeof raw === "string") { 3000 try { 3001 raw = JSON.parse(raw); 3002 } catch { 3003 return output; 3004 } 3005 } 3006 if (Array.isArray(raw)) { 3007 const first = raw[0]; 3008 if ((first == null ? void 0 : first.type) === "text" && typeof first.text === "string") { 3009 try { 3010 return JSON.parse(first.text); 3011 } catch { 3012 return first.text; 3013 } 3014 } 3015 } else if (raw && typeof raw === "object" && "content" in raw) { 3016 const content = raw.content; 3017 const first = Array.isArray(content) ? content[0] : null; 3018 if ((first == null ? void 0 : first.type) === "text" && typeof first.text === "string") { 3019 try { 3020 return JSON.parse(first.text); 3021 } catch { 3022 return first.text; 3023 } 3024 } 3025 } 3026 return raw; 3027} 3028const GetPublishingStatusRenderer = { 3029 displayName: "Publishing Status", 3030 progressLabel: () => "Checking publishing statusâ¦", 3031 hideDefaultInput: true, 3032 header: () => null, 3033 result: ({ 3034 output 3035 }) => { 3036 const data = unwrapMcpJson(output); 3037 if (!data || typeof data === "string") { 3038 const text = asString(output);
3039 return text ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[11px] text-gray-600", children: truncate(text, 200) }) : null; 3040 } 3041 const status = data.publishingStatus || "Unknown"; 3042 const statusColor = status === "Completed" ? "text-green-700" : status === "Failed" ? "text-red-700" : status === "In Progress" ? "text-blue-700" : "text-gray-700"; 3043 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-0.5 text-[11px] text-gray-700", children: [ 3044 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 3045 "Status:", 3046 " ", 3047 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: cn("font-medium", statusColor), children: status }), 3048 typeof data.publishingProgress === "number" && /* @__PURE__ */ jsxRuntimeExports.jsxs(DimLabel, { children: [ 3049 " · ", 3050 data.publishingProgress, 3051 "%" 3052 ] }) 3053 ] }), 3054 data.publishingStep && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 3055 /* @__PURE__ */ jsxRuntimeExports.jsx(DimLabel, { children: "Step:" }), 3056 " ", 3057 data.publishingStep 3058 ] }), 3059 data.publishingErrorMessage && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-red-700", children: [
3060 /* @__PURE__ */ jsxRuntimeExports.jsx(DimLabel, { children: "Error:" }), 3061 " ", 3062 truncate(data.publishingErrorMessage, 200) 3063 ] }) 3064 ] }); 3065 } 3066}; 3067const GetWebsiteInfoRenderer = { 3068 displayName: "Website Info", 3069 progressLabel: () => "Looking up websiteâ¦", 3070 header: (input) => { 3071 const websiteId = input == null ? void 0 : input.websiteId; 3072 if (!websiteId) { 3073 return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-gray-500", children: "current website" }); 3074 } 3075 return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-gray-700", children: [ 3076 /* @__PURE__ */ jsxRuntimeExports.jsx(DimLabel, { children: "id:" }), 3077 " ", 3078 truncate(websiteId, 40) 3079 ] }); 3080 }, 3081 result: ({ 3082 output 3083 }) => { 3084 var _a; 3085 const data = unwrapMcpJson(output); 3086 if (!data || typeof data === "string") { 3087 const text = asString(output); 3088 return text ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[11px] text-gray-600", children: truncate(text, 200) }) : null; 3089 } 3090 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-0.5 text-[11px] text-gray-700", children: [ 3091 data.name && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
3092 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-medium text-gray-900", children: data.name }), 3093 data.websiteType && /* @__PURE__ */ jsxRuntimeExports.jsxs(DimLabel, { children: [ 3094 " · ", 3095 data.websiteType 3096 ] }) 3097 ] }), 3098 data.previewUrl && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 3099 /* @__PURE__ */ jsxRuntimeExports.jsx(DimLabel, { children: "Preview:" }), 3100 " ", 3101 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono", children: data.previewUrl }) 3102 ] }), 3103 ((_a = data.Domain) == null ? void 0 : _a.domainName) && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 3104 /* @__PURE__ */ jsxRuntimeExports.jsx(DimLabel, { children: "Linked domain:" }), 3105 " ", 3106 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono", children: data.Domain.domainName }), 3107 data.Domain.verificationStatus && /* @__PURE__ */ jsxRuntimeExports.jsxs(DimLabel, { children: [ 3108 " · ", 3109 data.Domain.verificationStatus 3110 ] }) 3111 ] }),
3112 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 3113 /* @__PURE__ */ jsxRuntimeExports.jsx(DimLabel, { children: "Plan:" }), 3114 " ", 3115 data.stripePlan || "Free", 3116 /* @__PURE__ */ jsxRuntimeExports.jsxs(DimLabel, { children: [ 3117 " ", 3118 "· ", 3119 data.hasBeenPublished ? "published" : "not published", 3120 data.publishingStatus ? ` (${data.publishingStatus})` : "" 3121 ] }) 3122 ] }) 3123 ] }); 3124 } 3125}; 3126const GetDomainInfoRenderer = { 3127 displayName: "Domain Info", 3128 progressLabel: () => "Looking up domainâ¦", 3129 header: (input) => { 3130 const name = input == null ? void 0 : input.domainName; 3131 const id = input == null ? void 0 : input.domainId; 3132 if (name) { 3133 return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-gray-900", children: name }); 3134 } 3135 if (id) { 3136 return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-gray-700", children: [ 3137 /* @__PURE__ */ jsxRuntimeExports.jsx(DimLabel, { children: "id:" }), 3138 " ", 3139 truncate(id, 40) 3140 ] }); 3141 } 3142 return null; 3143 }, 3144 result: ({ 3145 output 3146 }) => { 3147 var _a, _b; 3148 const data = unwrapMcpJson(output); 3149 if (!data || typeof data === "string") { 3150 const text = asString(output);
3151 return text ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[11px] text-gray-600", children: truncate(text, 200) }) : null; 3152 } 3153 const verification = data.verificationStatus || "Unknown"; 3154 const verificationColor = verification === "Active" ? "text-green-700" : /failed/i.test(verification) ? "text-red-700" : "text-blue-700"; 3155 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-0.5 text-[11px] text-gray-700", children: [ 3156 data.domainName && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "font-mono text-gray-900", children: [ 3157 data.domainName, 3158 data.isSubdomain && /* @__PURE__ */ jsxRuntimeExports.jsx(DimLabel, { children: " · subdomain" }) 3159 ] }), 3160 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 3161 /* @__PURE__ */ jsxRuntimeExports.jsx(DimLabel, { children: "Verification:" }), 3162 " ", 3163 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: cn("font-medium", verificationColor), children: verification }) 3164 ] }), 3165 data.purchaseStatus && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
3166 /* @__PURE__ */ jsxRuntimeExports.jsx(DimLabel, { children: "Purchase:" }), 3167 " ", 3168 data.purchaseStatus, 3169 data.purchasedThroughLandingsite && /* @__PURE__ */ jsxRuntimeExports.jsx(DimLabel, { children: " · via Landingsite" }) 3170 ] }), 3171 data.nameserverStatus && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 3172 /* @__PURE__ */ jsxRuntimeExports.jsx(DimLabel, { children: "Nameservers:" }), 3173 " ", 3174 data.nameserverStatus, 3175 data.configurationMethod && /* @__PURE__ */ jsxRuntimeExports.jsxs(DimLabel, { children: [ 3176 " · ", 3177 data.configurationMethod 3178 ] }) 3179 ] }), 3180 data.domainExpireDate && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 3181 /* @__PURE__ */ jsxRuntimeExports.jsx(DimLabel, { children: "Expires:" }), 3182 " ", 3183 new Date(data.domainExpireDate).toLocaleDateString(), 3184 data.isRenewalActive === false && ((_a = data.lifecycle) == null ? void 0 : _a.stage) !== "renewal_scheduled" && /* @__PURE__ */ jsxRuntimeExports.jsx(DimLabel, { children: " · renewal off" }) 3185 ] }), 3186 ((_b = data.website) == null ? void 0 : _b.name) && /* @__P
3186URE__ */ jsxRuntimeExports.jsxs("div", { children: [ 3187 /* @__PURE__ */ jsxRuntimeExports.jsx(DimLabel, { children: "Linked website:" }), 3188 " ", 3189 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-gray-900", children: data.website.name }) 3190 ] }) 3191 ] }); 3192 } 3193}; 3194const CreateFormRenderer = { 3195 displayName: "Create Form", 3196 progressLabel: (input) => { 3197 const name = input == null ? void 0 : input.name; 3198 return name ? `Creating "${truncate(name, 40)}"â¦` : "Creating formâ¦"; 3199 }, 3200 header: (input) => { 3201 const name = input == null ? void 0 : input.name; 3202 if (!name) return null; 3203 return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-gray-900", children: [ 3204 '"', 3205 truncate(name, 60), 3206 '"' 3207 ] }); 3208 } 3209}; 3210const ShowViewRenderer = { 3211 displayName: "Show View", 3212 progressLabel: (input) => { 3213 const view = input == null ? void 0 : input.view; 3214 return view ? `Opening the ${view} viewâ¦` : "Opening viewâ¦"; 3215 }, 3216 header: (input) => { 3217 const view = input == null ? void 0 : input.view; 3218 if (!view) return null; 3219 const orderId = input == null ? void 0 : input.order_id; 3220 return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-gray-900", children: [ 3221 view, 3222 orderId && /* @__PURE__ */ jsxRuntimeExports.jsxs(DimLabel, { children: [ 3223 " · order ", 3224 truncate(orderId, 24) 3225 ] }) 3226 ] }); 3227 } 3228}; 3229const SearchForImagesRenderer = { 3230 displayName: "Search Images", 3231 progressLabel: (input) => { 3232 const description = input == null ? void 0 : input.description; 3233 return description ? `Searching for "${truncate(description, 40)}"â¦` : "Searching for imagesâ¦"; 3234 }, 3235 header: (input) => { 3236 const description = input == null ? void 0 : input.description; 3237 if (!description) return null; 3238 return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-gray-900", children: [ 3239 '"', 3240 truncate(description, 60), 3241 '"' 3242 ] }); 3243 }, 3244 result: ({ 3245 output 3246 }) => { 3247 const data = unwrapMcpJson(output); 3248 if (!data) return null; 3249 if (typeof data === "string") { 3250 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[11px] text-gray-600", children: truncate(data, 200) }); 3251 } 3252 const images = Array.isArray(data) ? data : data.data || []; 3253 if (images.length === 0) { 3254 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[11px] text-gray-500", children: "No images found." }); 3255 } 3256 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1.5 text-[11px] text-gray-700", children: [
3257 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-gray-500", children: [ 3258 images.length, 3259 " image", 3260 images.length === 1 ? "" : "s", 3261 " found" 3262 ] }), 3263 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-3 gap-1.5", children: images.slice(0, 6).map((img, i) => img.url ? /* @__PURE__ */ jsxRuntimeExports.jsx("a", { href: img.url, target: "_blank", rel: "noopener noreferrer", className: "block overflow-hidden rounded border border-gray-200 bg-white", children: /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: img.url, alt: img.altText || "", className: "aspect-video w-full object-cover", loading: "lazy" }) }, i) : null) }) 3264 ] }); 3265 } 3266}; 3267const ScreenshotPreviewRenderer = { 3268 displayName: "Screenshot Preview", 3269 progressLabel: (input) => { 3270 const reason = input == null ? void 0 : input.reason; 3271 return reason ? `Capturing: ${truncate(reason, 40)}â¦` : "Capturing screenshotâ¦"; 3272 }, 3273 header: (input) => { 3274 const path = (input == null ? void 0 : input.path) ?? "/"; 3275 const viewport = (input == null ? void 0 : input.viewport) ?? "both"; 3276 const viewportLabel = viewport === "both" ? "desktop + mobile" : viewport; 3277 return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-gray-700", children: [ 3278 path,
3279 /* @__PURE__ */ jsxRuntimeExports.jsxs(DimLabel, { children: [ 3280 " · ", 3281 viewportLabel 3282 ] }) 3283 ] }); 3284 }, 3285 result: ({ 3286 output 3287 }) => { 3288 let parsed = output; 3289 if (typeof parsed === "string") { 3290 try { 3291 parsed = JSON.parse(parsed); 3292 } catch { 3293 } 3294 } 3295 const parts = Array.isArray(parsed) ? parsed : parsed && typeof parsed === "object" && "content" in parsed ? parsed.content : null; 3296 if (!Array.isArray(parts)) return null; 3297 const items = []; 3298 let pendingCaption = null; 3299 const resolveImageSrc = (p) => { 3300 const src = p.source; 3301 if ((src == null ? void 0 : src.type) === "url" && typeof src.url === "string") return src.url; 3302 if ((src == null ? void 0 : src.type) === "base64" && src.data && src.media_type) { 3303 return `data:${src.media_type};base64,${src.data}`; 3304 } 3305 const data = p.data; 3306 if (typeof data === "string") { 3307 const media = p.mediaType ?? p.media_type ?? "image/png"; 3308 return `data:${media};base64,${data}`; 3309 } 3310 return null; 3311 }; 3312 for (const p of parts) { 3313 if ((p == null ? void 0 : p.type) === "text" && typeof p.text === "string") { 3314 if (pendingCaption !== null) { 3315 items.push({ 3316 kind: "caption-only", 3317 text: pendingCaption 3318 }); 3319 } 3320 pendingCaption = p.text; 3321 } else if ((p == null ? void 0 : p.type) === "image") { 3322 const src = resolveImageSrc(p); 3323 if (src === null) continue; 3324 if (pendingCaption !== null) { 3325 items.push({ 3326 kind: "pair", 3327 text: pendingCaption, 3328 src 3329 }); 3330 pendingCaption = null; 3331 } else { 3332 items.push({ 3333 kind: "image-only", 3334 src 3335 }); 3336 } 3337 } 3338 } 3339 if (pendingCaption !== null) { 3340 items.push({ 3341 kind: "caption-only", 3342 text: pendingCaption 3343 }); 3344 } 3345 if (items.length === 0) return null; 3346 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-2", children: items.map((item, i) => { 3347 if (item.kind === "caption-only") { 3348 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[11px] text-gray-600", children: truncate(item.text, 240) }, i); 3349 } 3350 if (item.kind === "image-only") { 3351 return /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: item.src, alt: "Preview screenshot", className: "max-w-full rounded border border-gray-200" }, i); 3352 } 3353 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [
3354 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[11px] text-gray-600", children: truncate(item.text, 240) }), 3355 /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: item.src, alt: "Preview screenshot", className: "max-w-full rounded border border-gray-200" }) 3356 ] }, i); 3357 }) }); 3358 } 3359}; 3360const PublishWebsiteRenderer = { 3361 displayName: "Publish Website", 3362 progressLabel: () => "Publishing websiteâ¦", 3363 hideDefaultInput: true, 3364 header: () => null 3365}; 3366const UnpublishWebsiteRenderer = { 3367 displayName: "Unpublish Website", 3368 progressLabel: () => "Unpublishing websiteâ¦", 3369 hideDefaultInput: true, 3370 header: () => null 3371}; 3372const ListUserWebsitesAndDomainsRenderer = { 3373 displayName: "My Sites & Domains", 3374 progressLabel: () => "Listing your websites and domainsâ¦", 3375 hideDefaultInput: true, 3376 header: () => null, 3377 result: ({ 3378 output 3379 }) => { 3380 const data = unwrapMcpJson(output); 3381 if (!data || typeof data === "string") { 3382 const text = asString(output); 3383 return text ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[11px] text-gray-600", children: truncate(text, 200) }) : null; 3384 } 3385 const owned = data.ownedWebsites || []; 3386 const member = data.memberWebsites || []; 3387 const domains = data.ownedDomains || []; 3388 const MAX = 5; 3389 const pluralize = (n, s) => `${n} ${s}${n === 1 ? "" : "s"}`; 3390 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1 text-[11px] text-gray-700", children: [
3391 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-gray-500", children: [ 3392 pluralize(owned.length, "owned website"), 3393 " · ", 3394 pluralize(member.length, "shared website"), 3395 " · ", 3396 pluralize(domains.length, "domain") 3397 ] }), 3398 owned.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-0.5", children: [ 3399 /* @__PURE__ */ jsxRuntimeExports.jsx(DimLabel, { children: "Owned:" }), 3400 owned.slice(0, MAX).map((w, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "pl-2", children: [ 3401 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-gray-900", children: w.name || "(unnamed)" }), 3402 w.previewUrl && /* @__PURE__ */ jsxRuntimeExports.jsxs(DimLabel, { children: [ 3403 " · ", 3404 w.previewUrl 3405 ] }) 3406 ] }, i)), 3407 owned.length > MAX && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "pl-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(DimLabel, { children: [ 3408 "â¦and ", 3409 owned.length - MAX, 3410 " more" 3411 ] }) }) 3412 ] }), 3413 member.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-0.5", children: [
3414 /* @__PURE__ */ jsxRuntimeExports.jsx(DimLabel, { children: "Shared:" }), 3415 member.slice(0, MAX).map((w, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "pl-2", children: [ 3416 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-gray-900", children: w.name || "(unnamed)" }), 3417 w.memberRole && /* @__PURE__ */ jsxRuntimeExports.jsxs(DimLabel, { children: [ 3418 " · ", 3419 w.memberRole 3420 ] }), 3421 w.previewUrl && /* @__PURE__ */ jsxRuntimeExports.jsxs(DimLabel, { children: [ 3422 " · ", 3423 w.previewUrl 3424 ] }) 3425 ] }, i)), 3426 member.length > MAX && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "pl-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(DimLabel, { children: [ 3427 "â¦and ", 3428 member.length - MAX, 3429 " more" 3430 ] }) }) 3431 ] }), 3432 domains.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-0.5", children: [
3433 /* @__PURE__ */ jsxRuntimeExports.jsx(DimLabel, { children: "Domains:" }), 3434 domains.slice(0, MAX).map((d, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "pl-2", children: [ 3435 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-gray-900", children: d.domainName || "(unknown)" }), 3436 d.verificationStatus && /* @__PURE__ */ jsxRuntimeExports.jsxs(DimLabel, { children: [ 3437 " · ", 3438 d.verificationStatus 3439 ] }) 3440 ] }, i)), 3441 domains.length > MAX && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "pl-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(DimLabel, { children: [ 3442 "â¦and ", 3443 domains.length - MAX, 3444 " more" 3445 ] }) }) 3446 ] }) 3447 ] }); 3448 } 3449}; 3450const toolRenderers = { 3451 Read: ReadRenderer, 3452 Write: WriteRenderer, 3453 Edit: EditRenderer, 3454 MultiEdit: MultiEditRenderer, 3455 Glob: GlobRenderer, 3456 Grep: GrepRenderer, 3457 Bash: BashRenderer, 3458 WebFetch: WebFetchRenderer, 3459 WebSearch: WebSearchRenderer, 3460 AskUserQuestion: AskUserQuestionRenderer, 3461 "mcp__landingsite-docs__search_docs": SearchDocsRenderer, 3462 mcp__landingsite__get_publishing_status: GetPublishingStatusRenderer, 3463 mcp__landingsite__get_website_info: GetWebsiteInfoRenderer, 3464 mcp__landingsite__get_domain_info: GetDomainInfoRenderer, 3465 mcp__landingsite__list_user_websites_and_domains: ListUserWebsitesAndDomainsRenderer, 3466 mcp__landingsite__create_form: CreateFormRenderer, 3467 mcp__landingsite__show_view: ShowViewRenderer, 3468 mcp__landingsite__search_for_images: SearchForImagesRenderer, 3469 mcp__landingsite__screenshot_preview: ScreenshotPreviewRenderer, 3470 mcp__landingsite__publish_website: PublishWebsiteRenderer, 3471 mcp__landingsite__unpublish_website: UnpublishWebsiteRenderer 3472}; 3473function getToolRenderer(name) { 3474 return toolRenderers[name]; 3475} 3476const MIN_INFLIGHT_MS = 500; 3477const expandedToolCallIds = /* @__PURE__ */ new Set(); 3478function getToolName$1(type) { 3479 return type.replace(/^tool-/, ""); 3480} 3481const BUILT_IN_TOOL_NAMES = /* @__PURE__ */ new Set(["Read", "Write", "Edit", "MultiEdit", "Glob", "Grep", "Bash", "WebFetch", "WebSearch", "Task", "TodoWrite", "NotebookEdit", "BashOutput", "KillShell"]); 3482function humanizeToolName(name) { 3483 if (name.startsWith("mcp__")) { 3484 const segments = name.split("__"); 3485 const toolPart = segments[segments.length - 1]; 3486 return toolPart.split("_").filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" "); 3487 } 3488 if (BUILT_IN_TOOL_NAMES.has(name)) return name; 3489 const spaced = name.replace(/([a-z0-9])([A-Z])/g, "$1 $2"); 3490 return spaced.charAt(0).toUpperCase() + spaced.slice(1); 3491} 3492function formatOutput(output) { 3493 if (output === void 0 || output === null) return ""; 3494 if (typeof output === "string") return output; 3495 try { 3496 return JSON.stringify(output, null, 2); 3497 } catch { 3498 return String(output); 3499 } 3500} 3501function cleanErrorText(text) { 3502 const match = text.match(/^<tool_use_error>([\s\S]*)<\/tool_use_error>$/); 3503 return match ? match[1].trim() : text; 3504} 3505function ToolRow({ 3506 part 3507}) { 3508 var _a; 3509 const toolName = getToolName$1(part.type); 3510 const renderer = getToolRenderer(toolName); 3511 const displayName = (renderer == null ? void 0 : renderer.displayName) ?? humanizeToolName(toolName); 3512 const actuallyInFlight = part.state === "input-streaming" || part.state === "input-available"; 3513 const isError = part.state === "output-error"; 3514 const errorCandidate = part.errorText && part.errorText.trim() || formatOutput(part.output).trim(); 3515 const rawErrorText = isError ? errorCandidate || `${displayName} failed but no error details were returned by the tool.` : null; 3516 const errorMessage = rawErrorText ? cleanErrorText(rawErrorText) : null; 3517 const [minDurationElapsed, setMinDurationElapsed] = reactExports.useState(false); 3518 reactExports.useEffect(() => { 3519 const t = setTimeout(() => setMinDurationElapsed(true), MIN_INFLIGHT_MS); 3520 return () => clearTimeout(t); 3521 }, []); 3522 const isInFlight = actuallyInFlight || !isError && !minDurationElapsed;
3523 const isSuccess = !isInFlight && part.state === "output-available"; 3524 const [isExpanded, setIsExpandedState] = reactExports.useState(() => expandedToolCallIds.has(part.toolCallId)); 3525 const setIsExpanded = (next) => { 3526 setIsExpandedState((prev) => { 3527 const value = typeof next === "function" ? next(prev) : next; 3528 if (value) expandedToolCallIds.add(part.toolCallId); 3529 else expandedToolCallIds.delete(part.toolCallId); 3530 return value; 3531 }); 3532 }; 3533 const ToolGlyph = getToolIcon(toolName); 3534 const icon = isInFlight ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "h-[15px] w-[15px] shrink-0 animate-spin text-blue-600" }) : isError ? /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { className: "h-[15px] w-[15px] shrink-0 text-red-500" }) : ToolGlyph ? /* @__PURE__ */ jsxRuntimeExports.jsx(ToolGlyph, { className: "h-[15px] w-[15px] shrink-0 text-gray-400" }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex h-[15px] w-[15px] shrink-0 items-center justify-center", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "h-[5px] w-[5px] rounded-full bg-gray-300" }) }); 3535 const header = (renderer == null ? void 0 : renderer.header(part.input)) ?? null; 3536 const progressLabel = isInFlight && ((_a = renderer == null ? void 0 : renderer.progressLabel) == null ? void 0 : _a.call(renderer, part.input)); 3537 const rendererResult = isSuccess && (renderer == null ? void 0 : renderer.result) ? renderer.result({ 3538 input: part.input, 3539 output: part.output 3540 }) : null; 3541 const hasRendererResult = rendererResult !== null && rendererResult !== void 0; 3542 const showDefaultOutput = !hasRendererResult && !isInFlight && (isError || part.output !== void 0 && part.output !== null); 3543 const showDefaultInput = part.input !== void 0 && part.input !== null && (!(renderer == null ? void 0 : renderer.hideDefaultInput) || !hasRendererResult); 3544 return ( 3545 // Quiet activity-log row: no heavy border/box chrome. Error rows use 3546 // red text/hover so they stand out without a full red card. 3547 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: cn("group relative text-[13.5px]", isError && "text-red-700"), children: [
3548 /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { type: "button", onClick: () => setIsExpanded((v) => !v), className: cn("flex w-full cursor-pointer items-center gap-[9px] rounded-md px-1.5 py-1 text-left font-mono", "hover:bg-[#f5f6f8] active:bg-gray-100", isError && "hover:bg-red-50/60"), children: [ 3549 icon, 3550 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: cn("font-semibold", isError ? "text-red-700" : "text-gray-600"), children: displayName }), 3551 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: cn("min-w-0 flex-1 truncate", isError ? "text-red-500" : isInFlight ? "text-gray-500" : "text-gray-400"), children: progressLabel || header }), 3552 /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: cn("h-3.5 w-3.5 shrink-0 text-gray-300 transition-transform", isExpanded && "rotate-90 text-gray-400") }) 3553 ] }), 3554 isExpanded && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-0.5 rounded bg-gray-50 px-2.5 py-1.5 text-xs", children: [ 3555 hasRendererResult ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: rendererResult }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 3556 showDefaultInput && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-1.5", children: [
3557 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-0.5 text-[10px] uppercase tracking-wide text-gray-400", children: "Input" }), 3558 /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "max-h-48 overflow-auto whitespace-pre-wrap break-all rounded bg-white p-1.5 font-mono text-[11px] text-gray-800", children: JSON.stringify(part.input, null, 2) }) 3559 ] }), 3560 showDefaultOutput && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 3561 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn("mb-0.5 text-[10px] uppercase tracking-wide", isError ? "text-red-500" : "text-gray-400"), children: isError ? "Error" : "Output" }), 3562 /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: cn("max-h-48 overflow-auto whitespace-pre-wrap break-all rounded p-1.5 font-mono text-[11px]", isError ? "bg-red-50 text-red-700" : "bg-white text-gray-800"), children: isError ? errorMessage : formatOutput(part.output) }) 3563 ] }) 3564 ] }), 3565 hasRendererResult && isError && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-1.5", children: [
3566 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-0.5 text-[10px] uppercase tracking-wide text-red-500", children: "Error" }), 3567 /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "max-h-48 overflow-auto whitespace-pre-wrap break-all rounded bg-red-50 p-1.5 font-mono text-[11px] text-red-700", children: errorMessage }) 3568 ] }) 3569 ] }) 3570 ] }) 3571 ); 3572} 3573function getToolName(type) { 3574 return type.replace(/^tool-/, ""); 3575} 3576function partHint(part) { 3577 var _a, _b; 3578 const name = getToolName(part.type); 3579 if (name === "Read") { 3580 const p = (_a = part.input) == null ? void 0 : _a.file_path; 3581 return p ? stripHome(p) : null; 3582 } 3583 const pattern = (_b = part.input) == null ? void 0 : _b.pattern; 3584 return pattern ?? null; 3585} 3586function CollapsedReadSearchRow({ 3587 parts 3588}) { 3589 const hasError = parts.length > 0 && parts.every((p) => p.state === "output-error"); 3590 const anyInFlight = parts.some((p) => p.state === "input-streaming" || p.state === "input-available"); 3591 const [isExpanded, setIsExpanded] = reactExports.useState(false); 3592 const reads = parts.filter((p) => p.type === "tool-Read").length; 3593 const searches = parts.length - reads; 3594 const RollupGlyph = reads > 0 && searches === 0 ? BookOpen : Search; 3595 const icon = hasError ? /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { className: "h-[15px] w-[15px] shrink-0 text-red-500" }
3595) : anyInFlight ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "h-[15px] w-[15px] shrink-0 animate-spin text-blue-600" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(RollupGlyph, { className: "h-[15px] w-[15px] shrink-0 text-gray-400" }); 3596 const label = reads > 0 && searches > 0 ? `Read ${reads} and searched ${searches}` : reads > 0 ? `Read ${reads} file${reads === 1 ? "" : "s"}` : `${searches} search${searches === 1 ? "" : "es"}`; 3597 const preview = parts.map(partHint).find((h) => !!h) ?? null; 3598 return ( 3599 // Quiet rollup row â no border/box, matches ToolRow chrome. 3600 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: cn("group relative text-[13.5px]", hasError && "text-red-700"), children: [ 3601 /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { type: "button", onClick: () => setIsExpanded((v) => !v), className: cn("flex w-full cursor-pointer items-center gap-2 rounded-md px-1.5 py-1 text-left font-mono", "hover:bg-[#f5f6f8] active:bg-gray-100", hasError && "hover:bg-red-50/60"), children: [ 3602 icon,
3603 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: cn("font-semibold", hasError ? "text-red-700" : "text-gray-600"), children: label }), 3604 preview && !isExpanded && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0 flex-1 truncate text-gray-400", children: preview }), 3605 /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: cn("ml-auto h-3.5 w-3.5 shrink-0 text-gray-300 transition-transform", isExpanded && "rotate-90 text-gray-400") }) 3606 ] }), 3607 isExpanded && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-0.5 space-y-0 rounded bg-gray-50 px-2 py-1", children: parts.map((part, i) => /* @__PURE__ */ jsxRuntimeExports.jsx(ToolRow, { part: { 3608 type: part.type, 3609 toolCallId: part.toolCallId || `rollup-${i}`, 3610 state: part.state || "input-available", 3611 input: part.input, 3612 output: part.output, 3613 errorText: part.errorText 3614 } }, part.toolCallId || `rollup-${i}`)) }) 3615 ] }) 3616 ); 3617} 3618const GROUPABLE = /* @__PURE__ */ new Set(["tool-Read", "tool-Glob", "tool-Grep"]); 3619const MIN_GROUP_SIZE = 2; 3620function groupReadSearchParts(parts) { 3621 const groups = []; 3622 let run = []; 3623 let runStart = 0; 3624 const flushRun = () => { 3625 if (run.length === 0) return; 3626 if (run.length >= MIN_GROUP_SIZE) { 3627 groups.push({ 3628 kind: "collapsed", 3629 parts: run, 3630 startIndex: runStart 3631 }); 3632 } else { 3633 run.forEach((part, i) => groups.push({ 3634 kind: "single", 3635 part, 3636 index: runStart + i 3637 })); 3638 } 3639 run = []; 3640 }; 3641 parts.forEach((part, i) => { 3642 if (part.type && GROUPABLE.has(part.type)) { 3643 if (run.length === 0) runStart = i; 3644 run.push(part); 3645 } else { 3646 flushRun(); 3647 groups.push({ 3648 kind: "single", 3649 part, 3650 index: i 3651 }); 3652 } 3653 }); 3654 flushRun(); 3655 return groups; 3656} 3657function formatDuration$1(ms) { 3658 if (ms < 1e3) return `${ms}ms`; 3659 const secs = ms / 1e3; 3660 if (secs < 60) return `${secs.toFixed(1)}s`; 3661 const mins = Math.floor(secs / 60); 3662 const rem = Math.floor(secs % 60); 3663 return `${mins}m ${rem}s`; 3664} 3665function ThinkingRow({ 3666 part 3667}) { 3668 const isInFlight = part.thinkingDurationMs === -1; 3669 const text = part.text ?? ""; 3670 const [isExpanded, setIsExpanded] = reactExports.useState(false); 3671 if (isInFlight) return null; 3672 if (text.length === 0) return null; 3673 const label = part.thinkingDurationMs && part.thinkingDurationMs > 0 ? `Worked for ${formatDuration$1(part.thinkingDurationMs)}` : "Worked"; 3674 return ( 3675 // Quiet activity-log row matching ToolRow chrome â no border/box. 3676 // Resolved-state: text + chevron only, muted (gray-400), no leading icon 3677 // and no horizontal rule â the prototype's "Worked for Ns âº" treatment. 3678 // Tight spacing (py-0.5) so the collapsed header sits close to the prose 3679 // it precedes. 3680 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "group relative text-sm", children: [
3681 /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { type: "button", onClick: () => setIsExpanded((v) => !v), className: "flex w-full cursor-pointer items-center gap-1.5 rounded-md px-1.5 py-0.5 text-left hover:bg-[#f5f6f8] active:bg-gray-100", children: [ 3682 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-gray-400", children: label }), 3683 /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: cn("ml-0.5 h-3.5 w-3.5 shrink-0 text-gray-300 transition-transform", isExpanded && "rotate-90 text-gray-400") }) 3684 ] }), 3685 isExpanded && text.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-0.5 rounded bg-gray-50 px-3 py-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn("prose prose-sm prose-neutral max-w-none text-[12px]", "italic text-gray-600"), children: /* @__PURE__ */ jsxRuntimeExports.jsx(Markdown, { remarkPlugins: [remarkGfm], children: text }) }) }) 3686 ] }) 3687 ); 3688} 3689function FaviconIcon({ 3690 hostname 3691}) { 3692 const [status, setStatus] = reactExports.useState("loading"); 3693 const src = `https://www.google.com/s2/favicons?domain=${hostname}&sz=64`; 3694 return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex shrink-0 items-center", style: { 3695 width: 14, 3696 height: 14 3697 }, children: [ 3698 status !== "loaded" && /* @__PURE__ */ jsxRuntimeExports.jsx(Globe, { "aria-hidden": "true", className: "h-[14px] w-[14px] text-gray-500" }), 3699 status !== "error" && /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src, alt: "", referrerPolicy: "no-referrer", onLoad: () => setStatus("loaded"), onError: () => setStatus("error"), className: "rounded-[3px]", style: { 3700 width: 14, 3701 height: 14, 3702 display: status === "loaded" ? "inline-block" : "none" 3703 } }) 3704 ] }); 3705} 3706const markdownComponents = { 3707 // Absolute http(s) links: favicon-or-globe icon + blue link text, new tab. 3708 // Relative/anchor links (#â¦): rendered as plain inline text to avoid broken 3709 // navigation inside the chat panel. 3710 a({ 3711 href, 3712 children 3713 }) { 3714 const isAbsolute = href && /^https?:\/\//i.test(href); 3715 if (!isAbsolute) { 3716 return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-blue-600", children }); 3717 } 3718 let hostname = ""; 3719 try { 3720 hostname = new URL(href).hostname; 3721 } catch { 3722 } 3723 return /* @__PURE__ */ jsxRuntimeExports.jsxs("a", { href, target: "_blank", rel: "noreferrer", className: "inline-flex items-baseline gap-1 font-medium text-blue-600 hover:underline", children: [ 3724 hostname && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "inline-flex translate-y-[2px] items-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx(FaviconIcon, { hostname }) }), 3725 children 3726 ] }); 3727 }, 3728 // Inline code: light gray-100 chip with monospace font so tool/skill names 3729 // read as chips. react-markdown v9+ removed the `inline` prop, so we detect 3730 // inline vs. block ourselves: fenced blocks carry a `language-*` className, 3731 // and any block code spans newlines. Everything else is inline â chip. 3732 code({ 3733 className, 3734 children, 3735 ...props 3736 }) { 3737 const isFenced = typeof className === "string" && /\blanguage-/.test(className); 3738 const hasNewline = /\n/.test(Array.isArray(children) ? children.join("") : String(children ?? "")); 3739 if (!isFenced && !hasNewline) { 3740 return /* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "rounded-[5px] bg-gray-100 px-1.5 py-px font-mono text-[0.85em] text-gray-700 before:content-none after:content-none", children }); 3741 } 3742 return /* @__PURE__ */ jsxRuntimeExports.jsx("code", { className, ...props, children }); 3743 }, 3744 // Section headings â pinned to the prototype's flat treatment (15px / 700, 3745 // near-black, 16px top / 8px bottom margin) instead of riding the prose-sm 3746 // heading scale, which renders them too small and only semibold. 3747 h1: ({ 3748 children 3749 }) => /* @__PURE__ */ jsxRuntimeExports.jsx("h1", { className: "mb-2 mt-4 text-[16px] font-bold text-gray-900", children }), 3750 h2: ({ 3751 children 3752 }) => /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "mb-2 mt-4 text-[15px] font-bold text-gray-900", children }), 3753 h3: ({ 3754 children 3755 }) => /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "mb-2 mt-4 text-[15px] font-bold text-gray-900", children }), 3756 h4: ({ 3757 children 3758 }) => /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "mb-2 mt-4 text-[15px] font-bold text-gray-900", children }) 3759}; 3760function renderGroup(group, keyPrefix) { 3761 if (group.kind === "collapsed") { 3762 return /* @__PURE__ */ jsxRuntimeExports.jsx(CollapsedReadSearchRow, { parts: group.parts }, `${keyPrefix}rollup-${group.startIndex}`); 3763 } 3764 const part = group.part; 3765 const index = group.index; 3766 if (part.type === "text" && part.text) { 3767 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "message-part-text", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Markdown, { remarkPlugins: [remarkGfm], components: markdownComponents, children: part.text }) }, `${keyPrefix}text-${index}`); 3768 } 3769 if (part.type === "thinking") { 3770 return /* @__PURE__ */ jsxRuntimeExports.jsx(ThinkingRow, { part: { 3771 type: "thinking", 3772 text: part.text, 3773 thinkingDurationMs: part.thinkingDurationMs 3774 } }, `${keyPrefix}thinking-${index}`); 3775 } 3776 if (typeof part.type === "string" && part.type.startsWith("tool-")) { 3777 return /* @__PURE__ */ jsxRuntimeExports.jsx(ToolRow, { part: { 3778 type: part.type, 3779 toolCallId: part.toolCallId || `${index}`, 3780 state: part.state || "input-available", 3781 input: part.input, 3782 output: part.output 3783 } }, `${keyPrefix}tool-${part.toolCallId || index}`); 3784 } 3785 return null; 3786} 3787function isActivityGroup(group) { 3788 if (group.kind === "collapsed") return true; 3789 const { 3790 part 3791 }
3791 = group; 3792 if (part.type === "thinking") return true; 3793 if (typeof part.type === "string" && part.type.startsWith("tool-")) return true; 3794 return false; 3795} 3796function renderSandboxPartsWithReadGrouping(parts, keyPrefix = "") { 3797 const renderGroups = groupReadSearchParts(parts); 3798 const segments = []; 3799 let currentRun = null; 3800 let currentRunStart = 0; 3801 const flushRun = () => { 3802 if (currentRun && currentRun.length > 0) { 3803 segments.push({ 3804 kind: "activity-run", 3805 groups: currentRun, 3806 startIdx: currentRunStart 3807 }); 3808 currentRun = null; 3809 } 3810 }; 3811 renderGroups.forEach((group, i) => { 3812 if (isActivityGroup(group)) { 3813 if (!currentRun) { 3814 currentRun = []; 3815 currentRunStart = i; 3816 } 3817 currentRun.push(group); 3818 } else { 3819 flushRun(); 3820 segments.push({ 3821 kind: "text-group", 3822 group, 3823 idx: i 3824 }); 3825 } 3826 }); 3827 flushRun(); 3828 return segments.map((segment) => { 3829 if (segment.kind === "text-group") { 3830 return renderGroup(segment.group, keyPrefix); 3831 } 3832 const runKey = `${keyPrefix}run-${segment.startIdx}`; 3833 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "my-2", children: segment.groups.map((g) => renderGroup(g, keyPrefix)) }, runKey); 3834 }); 3835} 3836const PREVIEW_COUNT = 3; 3837function deriveDescription(input) { 3838 if (!input) return "exploring"; 3839 const desc = input.description; 3840 if (typeof desc === "string" && desc.trim().length > 0) return desc.trim(); 3841 const prompt = input.prompt; 3842 if (typeof prompt === "string" && prompt.trim().length > 0) { 3843 const firstLine2 = prompt.trim().split("\n")[0] ?? ""; 3844 if (firstLine2.length > 0) return firstLine2; 3845 } 3846 const sub = input.subagent_type; 3847 if (typeof sub === "string" && sub.trim().length > 0) return sub.trim(); 3848 return "exploring"; 3849} 3850function formatDuration(ms) { 3851 if (!ms || ms <= 0) return ""; 3852 const totalSec = Math.round(ms / 1e3); 3853 if (totalSec < 60) return `${totalSec}s`; 3854 const min = Math.floor(totalSec / 60); 3855 const sec = totalSec % 60; 3856 return sec === 0 ? `${min}m` : `${min}m ${sec}s`; 3857} 3858function formatTokens(n) { 3859 if (!n || n <= 0) return ""; 3860 if (n >= 1e3) { 3861 const k = (n / 1e3).toFixed(1).replace(/\.0$/, ""); 3862 return `${k}k tokens`; 3863 } 3864 return `${n} tokens`; 3865} 3866function ExploreRow({ 3867 agentPart, 3868 childParts 3869}) { 3870 const description = deriveDescription(agentPart.input); 3871 const agentOutput = agentPart.agentOutput; 3872 const isDone = agentPart.state === "output-available"; 3873 const isCompleted = isDone && (agentOutput == null ? void 0 : agentOutput.status) === "completed"; 3874 const isError = agentPart.state === "output-error"; 3875 const isRunning = !isDone && !isError; 3876 const [isExpanded, setIsExpanded] = reactExports.useState(false); 3877 const toolChildren = childParts.filter((p) => typeof p.type === "string" && p.type.startsWith("tool-")); 3878 const totalToolCount = (agentOutput == null ? void 0 : agentOutput.totalToolUseCount) ?? toolChildren.length; 3879 const overflowCount = Math.max(0, toolChildren.length - PREVIEW_COUNT); 3880 const previewParts = toolChildren.slice(-3); 3881 const headerIcon = isError ? /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { className: "h-[15px] w-[15px] text-red-500" }
3881) : isRunning ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "h-[15px] w-[15px] animate-spin text-blue-600" }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex h-[15px] w-[15px] items-center justify-center", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "h-[5px] w-[5px] rounded-full bg-gray-300" }) }); 3882 const footerPieces = []; 3883 if (isDone && totalToolCount > 0) { 3884 footerPieces.push(`${totalToolCount} tool use${totalToolCount === 1 ? "" : "s"}`); 3885 if (agentOutput) { 3886 const tokensStr = formatTokens(agentOutput.totalTokens); 3887 if (tokensStr) footerPieces.push(tokensStr); 3888 const durStr = formatDuration(agentOutput.totalDurationMs); 3889 if (durStr) footerPieces.push(durStr); 3890 } 3891 } 3892 return ( 3893 // Quiet Explore container â drop the heavy purple border/box chrome. 3894 // A subtle left rail is applied by the renderParts wrapper around the 3895 // whole activity run. The Explore group itself gets a faint left 3896 // accent to distinguish nested children from top-level rows. 3897 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: cn("group relative text-[13.5px]", isError && "text-red-700"), "data-explore-row": true, "data-explore-state": isCompleted ? "done" : isError ? "error" : "running", children: [
3898 /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { type: "button", onClick: () => setIsExpanded((v) => !v), className: cn("flex w-full cursor-pointer items-center gap-2 rounded-md px-1.5 py-1 text-left font-mono", "hover:bg-[#f5f6f8] active:bg-gray-100", isError && "hover:bg-red-50/60"), children: [ 3899 headerIcon, 3900 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: cn("font-semibold", isError ? "text-red-700" : "text-gray-600"), children: [ 3901 "Explore(", 3902 description, 3903 ")" 3904 ] }), 3905 /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: cn("ml-auto h-3.5 w-3.5 shrink-0 text-gray-300 transition-transform", isExpanded && "rotate-90 text-gray-400") }) 3906 ] }), 3907 !isExpanded && !isError && previewParts.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ml-7 space-y-0", children: [ 3908 renderSandboxPartsWithReadGrouping(previewParts, "explore-prev-"), 3909 overflowCount > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "py-0.5 pl-2 text-xs italic text-gray-400", children: [ 3910 "+", 3911 overflowCount, 3912 " more tool use", 3913 overflowCount === 1 ? "" : "s" 3914 ] }) 3915 ] }),
3916 isDone && !isExpanded && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "pl-9 py-0.5 text-gray-400 italic", children: footerPieces.length > 0 ? `Done (${footerPieces.join(" · ")})` : "Done" }), 3917 isError && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "pl-9 py-0.5 text-red-500 italic", children: "Explore failed" }), 3918 (isExpanded || isError) && childParts.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ml-7 space-y-0", children: renderSandboxPartsWithReadGrouping(childParts, "explore-full-") }) 3919 ] }) 3920 ); 3921} 3922function groupByParentAgent(parts) { 3923 const out = []; 3924 const openAgents = /* @__PURE__ */ new Map(); 3925 let topBuffer = []; 3926 const flushTop = () => { 3927 if (topBuffer.length === 0) return; 3928 out.push({ 3929 kind: "top", 3930 parts: topBuffer 3931 }); 3932 topBuffer = []; 3933 }; 3934 for (const part of parts) { 3935 if (part.type === "tool-Agent" && part.toolCallId) { 3936 flushTop(); 3937 out.push({ 3938 kind: "agent", 3939 agentPart: part, 3940 children: [] 3941 }); 3942 openAgents.set(part.toolCallId, out.length - 1); 3943 continue; 3944 } 3945 const parent = part.parentToolUseId; 3946 if (parent) { 3947 const idx = openAgents.get(parent); 3948 if (idx !== void 0) { 3949 const group = out[idx]; 3950 if (group.kind === "agent") { 3951 group.children.push(part); 3952 continue; 3953 } 3954 } 3955 } 3956 topBuffer.push(part); 3957 } 3958 flushTop(); 3959 return out; 3960} 3961function settleStaleToolSpinners(parts) { 3962 let lastTextIdx = -1; 3963 for (let i = parts.length - 1; i >= 0; i--) { 3964 const p = parts[i]; 3965 if (p.type === "text" && (p.text || "").trim().length > 0) { 3966 lastTextIdx = i; 3967 break; 3968 } 3969 } 3970 if (lastTextIdx <= 0) return parts; 3971 let changed = false; 3972 const next = parts.map((p, i) => { 3973 if (i < lastTextIdx && typeof p.type === "string" && p.type.startsWith("tool-") && p.type !== "tool-Agent" && !p.parentToolUseId && (p.state === "input-available" || p.state === "input-streaming")) { 3974 changed = true; 3975 return { 3976 ...p, 3977 state: "output-available" 3978 }; 3979 } 3980 return p; 3981 }); 3982 return changed ? next : parts; 3983} 3984function extractText(parts) { 3985 return parts.filter((p) => p.type === "text" && typeof p.text === "string").map((p) => p.text).join(""); 3986} 3987function extractPickerSummary(rawText) { 3988 if (!rawText.includes("Selected element HTML:")) return null; 3989 const match = rawText.match(/Selected element HTML:\s*```[^\n]*\n([\s\S]*?)\n```/); 3990 if (!match) return null; 3991 const html = match[1].trim(); 3992 const tagMatch = html.match(/^<([a-zA-Z][a-zA-Z0-9-]*)/); 3993 if (!tagMatch) return null; 3994 const tag = tagMatch[1].toLowerCase(); 3995 const plain = html.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim(); 3996 const preview = plain.length > 40 ? plain.slice(0, 40).trimEnd() + "â¦" : plain; 3997 return { 3998 tag, 3999 preview 4000 }; 4001} 4002function UserMessage({ 4003 message, 4004 stackPosition = "solo", 4005 onRestore, 4006 isRestoring, 4007 isAIResponding 4008}) { 4009 var _a, _b, _c, _d, _e; 4010 const preMessageCommitHash = (_a = message.metadata) == null ? void 0 : _a.preMessageCommitHash; 4011 const showRestoreButton = onRestore && preMessageCommitHash && !isAIResponding; 4012 const rawText = extractText(message.parts); 4013 const pickerSummary = extractPickerSummary(rawText); 4014 const displayText = rawText.replace(/<context for the AI>[\s\S]*?<\/context for the AI>\s*/g, "").trim(); 4015 const attachedMedia = ((_b = message.metadata) == null ? void 0 : _b.userUploadedMedia) ?? []; 4016 const attachedMediaTypes = ((_c = message.metadata) == null ? void 0 : _c.galleryMediaTypes) ?? []; 4017 const attachedMediaPreviews = ((_d = message.metadata) == null ? void 0 : _d.userUploadedMediaPreviews) ?? []; 4018 const attachedMediaNames = ((_e = message.metadata) == null ? void 0 : _e.userUploadedMediaNames) ?? []; 4019 const hasAttachedContext = !!pickerSummary || attachedMedia.length > 0; 4020 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex w-fit min-w-0 max-w-full flex-col items-end gap-1.5", "data-role": "user", "data-message-id": message.id, children: [
4021 showRestoreButton && /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { variant: "outline", size: "sm", className: cn("absolute -bottom-3 right-2 z-10 h-6 rounded-full border-gray-200 bg-white px-3 text-xs shadow-sm hover:bg-gray-50", isRestoring && "cursor-not-allowed opacity-50"), onClick: () => !isRestoring && onRestore(message.id, preMessageCommitHash), disabled: isRestoring, children: [ 4022 /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { className: "mr-1 h-3 w-3", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6" }) }), 4023 "Restore" 4024 ] }), 4025 hasAttachedContext && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex w-fit max-w-full flex-col gap-2 rounded-[16px] border border-gray-300 bg-white px-3 py-2.5", children: [ 4026 pickerSummary && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "inline-flex w-fit max-w-full items-center gap-1.5 text-xs font-medium text-gray-600", "aria-label": pickerSummary.preview ? `Attached element: ${friendlyElementNoun(pickerSummary.tag)} '${pickerSummary.preview}'` : `Attached element: ${friendlyElementNoun(pickerSummary.tag)}`, children: [
4027 /* @__PURE__ */ jsxRuntimeExports.jsx(MousePointerClick, { className: "h-3.5 w-3.5 shrink-0 text-gray-400", "aria-hidden": true }), 4028 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: formatFriendlyLabel(pickerSummary.tag, pickerSummary.preview) }) 4029 ] }), 4030 attachedMedia.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-wrap gap-2", children: attachedMedia.map((src, idx) => { 4031 var _a2, _b2; 4032 const mediaType = attachedMediaTypes[idx]; 4033 const isVideo = mediaType === "video" || mediaType === "lottie"; 4034 const isPdf = src.endsWith(".pdf") || src.includes(".pdf?") || ((_a2 = attachedMediaNames[idx]) == null ? void 0 : _a2.toLowerCase().endsWith(".pdf")); 4035 const isModel = src.endsWith(".glb") || src.includes(".glb?") || ((_b2 = attachedMediaNames[idx]) == null ? void 0 : _b2.toLowerCase().endsWith(".glb")); 4036 if (isVideo) { 4037 return /* @__PURE__ */ jsxRuntimeExports.jsx("video", { src: attachedMediaPreviews[idx] || src, autoPlay: true, loop: true, muted: true, playsInline: true, className: "h-24 rounded border border-gray-200 bg-black/5" }, src + idx); 4038 } 4039 if (isPdf || isModel) { 4040 const Icon = isModel ? Box : FileText; 4041 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex h-24 w-20 flex-col items-center justify-center rounded border border-gray-200 bg-muted px-2", children: [
4042 /* @__PURE__ */ jsxRuntimeExports.jsx(Icon, { className: "h-7 w-7 text-muted-foreground" }), 4043 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "mt-1 w-full truncate text-center text-[10px] text-muted-foreground", children: attachedMediaNames[idx] || (isModel ? "3D model" : "PDF") }) 4044 ] }, src + idx); 4045 } 4046 return /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src, alt: `attachment ${idx + 1}`, className: "h-24 rounded border border-gray-200" }, src + idx); 4047 }) }) 4048 ] }), 4049 displayText && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn("flex min-w-0 max-w-full flex-col overflow-x-auto bg-[#f1f1f3] px-[15px] py-[9px] text-[#2a2d31]", getChatBubbleRadiusClass("user", stackPosition), "prose prose-sm prose-neutral break-words text-[15px] dark:prose-invert"), children: /* @__PURE__ */ jsxRuntimeExports.jsx(Markdown, { remarkPlugins: [remarkGfm], children: displayText }) }) 4050 ] }); 4051} 4052function AssistantMessage({ 4053 message 4054}) { 4055 if (!message.parts || message.parts.length === 0) { 4056 return null; 4057 } 4058 const visibleParts = settleStaleToolSpinners(message.parts.filter((p) => p.type !== "tool-TodoWrite")); 4059 if (visibleParts.length === 0) { 4060 return null; 4061 } 4062 const hasAnyContent = visibleParts.some((p) => { 4063 if (p.type === "text") return (p.text || "").length > 0; 4064 return true; 4065 }); 4066 if (!hasAnyContent) { 4067 return null; 4068 } 4069 const outerGroups = groupByParentAgent(visibleParts); 4070 return ( 4071 // Assistant turns render as document flow directly on the panel surface â 4072 // no bg-muted enclosure. Full width, left-aligned. 4073 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex w-full flex-col", "data-role": "assistant", "data-message-id": message.id, children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "prose prose-sm prose-neutral max-w-none break-words px-1 py-1 text-pretty text-[15px] leading-[1.55] dark:prose-invert", children: outerGroups.map((group, groupIdx) => { 4074 if (group.kind === "agent") { 4075 return /* @__PURE__ */ jsxRuntimeExports.jsx(ExploreRow, { agentPart: group.agentPart, childParts: group.children }, `explore-${group.agentPart.toolCallId || groupIdx}`); 4076 } 4077 return /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Fragment, { children: renderSandboxPartsWithReadGrouping(group.parts, `g${groupIdx}-`) }, `top-${groupIdx}`); 4078 }) }) }) 4079 ); 4080} 4081const SandboxChatMessage = reactExports.memo(function SandboxChatMessage2(props) { 4082 const { 4083 message 4084 } = props; 4085 if (message.role === "user") { 4086 return /* @__PURE__ */ jsxRuntimeExports.jsx(UserMessage, { ...props }); 4087 } 4088 if (message.role === "assistant") { 4089 return /* @__PURE__ */ jsxRuntimeExports.jsx(AssistantMessage, { message }); 4090 } 4091 return null; 4092}); 4093function SandboxWaitingIndicator() { 4094 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex w-full flex-col", "data-role": "assistant", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center px-1 py-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-shimmer text-sm font-medium", children: "Thinking" }) }) }); 4095} 4096const TICK_INTERVAL_MS = 250; 4097const ELAPSED_BADGE_MIN_MS = 2e3; 4098const CHARS_PER_TOKEN = 4; 4099function formatElapsed(ms) { 4100 if (ms < 1e3) return `${ms}ms`; 4101 const secs = ms / 1e3; 4102 if (secs < 60) return `${secs.toFixed(1)}s`; 4103 const mins = Math.floor(secs / 60); 4104 const remSecs = Math.floor(secs % 60); 4105 return `${mins}m ${remSecs}s`; 4106} 4107function ThinkingIndicator({ 4108 thinking 4109}) { 4110 const [, setTick] = reactExports.useState(0); 4111 reactExports.useEffect(() => { 4112 if (!(thinking == null ? void 0 : thinking.active)) return; 4113 const id = setInterval(() => setTick((n) => n + 1), TICK_INTERVAL_MS); 4114 return () => clearInterval(id); 4115 }, [thinking == null ? void 0 : thinking.active]); 4116 if (!(thinking == null ? void 0 : thinking.active)) return null; 4117 const now = Date.now(); 4118 const elapsedMs = now - thinking.startedAt; 4119 const showElapsed = elapsedMs >= ELAPSED_BADGE_MIN_MS; 4120 const tokens = Math.round(thinking.chars / CHARS_PER_TOKEN); 4121 const labelText = showElapsed ? `Working for ${formatElapsed(elapsedMs)}` : "Thinkingâ¦"; 4122 return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4123 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mx-4 flex items-center gap-2 px-1 py-1 text-xs", children: [ 4124 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-shimmer text-sm font-medium tabular-nums", children: labelText }), 4125 tokens > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "tabular-nums text-gray-400", children: [ 4126 "· ~", 4127 tokens.toLocaleString(), 4128 " tokens" 4129 ] }) 4130 ] }), 4131 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mx-4 mb-3 mt-1 h-px bg-gray-200/70" }) 4132 ] }); 4133} 4134const RECENTLY_COMPLETED_TTL_MS = 3e4; 4135const MAX_VISIBLE_ROWS = 10; 4136function parseTodos(input) { 4137 const todos = input == null ? void 0 : input.todos; 4138 if (!Array.isArray(todos)) return null; 4139 const parsed = todos.filter((t) => typeof t === "object" && t !== null && typeof t.content === "string" && ["pending", "in_progress", "completed"].includes(t.status)); 4140 return parsed.length > 0 ? parsed : null; 4141} 4142function getLatestTodos(messages) { 4143 let lastUserIdx = -1; 4144 for (let i = messages.length - 1; i >= 0; i--) { 4145 if (messages[i].role === "user") { 4146 lastUserIdx = i; 4147 break; 4148 } 4149 } 4150 for (let i = messages.length - 1; i > lastUserIdx; i--) { 4151 const msg = messages[i]; 4152 if (msg.role !== "assistant" || !msg.parts) continue; 4153 for (let j = msg.parts.length - 1; j >= 0; j--) { 4154 const part = msg.parts[j]; 4155 if ((part == null ? void 0 : part.type) === "tool-TodoWrite") { 4156 const todos = parseTodos(part.input); 4157 if (todos) return todos; 4158 } 4159 } 4160 } 4161 return null; 4162} 4163function useRecentlyCompleted(todos) { 4164 const completedAtRef = reactExports.useRef(/* @__PURE__ */ new Map()); 4165 const initialSnapshotRef = reactExports.useRef(null); 4166 const [, forceRender] = reactExports.useState(0); 4167 const now = Date.now(); 4168 const map = completedAtRef.current; 4169 if (todos) { 4170 if (initialSnapshotRef.current === null) { 4171 initialSnapshotRef.current = new Set(todos.filter((t) => t.status === "completed").map((t) => t.content)); 4172 } 4173 const currentCompleted = new Set(todos.filter((t) => t.status === "completed").map((t) => t.content)); 4174 for (const key of map.keys()) { 4175 if (!currentCompleted.has(key)) map.delete(key); 4176 } 4177 for (const key of currentCompleted) { 4178 if (!map.has(key) && !initialSnapshotRef.current.has(key)) { 4179 map.set(key, now); 4180 } 4181 } 4182 } else { 4183 map.clear(); 4184 initialSnapshotRef.current = null; 4185 } 4186 let nextExpiry = Infinity; 4187 for (const ts of map.values()) { 4188 const expiresAt = ts + RECENTLY_COMPLETED_TTL_MS; 4189 if (expiresAt > now && expiresAt < nextExpiry) nextExpiry = expiresAt; 4190 } 4191 reactExports.useEffect(() => { 4192 if (nextExpiry === Infinity) return; 4193 const timeout = setTimeout(() => forceRender((n) => n + 1), Math.max(0, nextExpiry - Date.now()) + 50); 4194 return () => clearTimeout(timeout); 4195 }, [nextExpiry]); 4196 return (task) => { 4197 var _a; 4198 if (task.status !== "completed") return true; 4199 if ((_a = initialSnapshotRef.current) == null ? void 0 : _a.has(task.content)) return false; 4200 const completedAt = map.get(task.content); 4201 if (completedAt === void 0) return true; 4202 return now - completedAt < RECENTLY_COMPLETED_TTL_MS; 4203 }; 4204} 4205function formatHeader(total, done, inProgress, open) { 4206 const parts = [`${done} done`, `${inProgress} in progress`, `${open} open`]; 4207 return `${total} task${total === 1 ? "" : "s"} (${parts.join(", ")})`; 4208} 4209function TodoChecklist({ 4210 messages 4211}) { 4212 const todos = getLatestTodos(messages); 4213 const isVisible = useRecentlyCompleted(todos); 4214 if (!todos) return null; 4215 const visible = todos.filter(isVisible).slice(0, MAX_VISIBLE_ROWS); 4216 if (visible.length === 0) return null; 4217 const done = todos.filter((t) => t.status === "completed").length; 4218 const inProgress = todos.filter((t) => t.status === "in_progress").length; 4219 const open = todos.filter((t) => t.status === "pending").length; 4220 return ( 4221 // In-stream inline card: no shadow/backdrop-blur floating panel. 4222 // Subtle border + barely-there bg, consistent with the quiet activity-log 4223 // aesthetic from Phase 2 and the SystemCard family from Phase 3.
4224 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mx-4 mb-2 rounded-md border border-gray-100 bg-gray-50/60 px-3 py-2", children: [ 4225 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-1.5", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "tabular-nums text-[10px] font-semibold uppercase tracking-wide text-gray-400", children: formatHeader(todos.length, done, inProgress, open) }) }), 4226 /* @__PURE__ */ jsxRuntimeExports.jsx("ul", { className: "space-y-1 text-[12px]", children: visible.map((todo, i) => { 4227 const isDone = todo.status === "completed"; 4228 const isActive = todo.status === "in_progress"; 4229 const label = isActive && todo.activeForm ? todo.activeForm : todo.content; 4230 return /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { className: "flex items-start gap-2 leading-snug", children: [ 4231 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "mt-px flex h-3.5 w-3.5 shrink-0 items-center justify-center", "aria-hidden": true, children: isDone ? ( 4232 // Done items recede â a muted check, no green.
4233 /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "h-3.5 w-3.5 text-gray-400", strokeWidth: 2.5 }) 4234 ) : isActive ? ( 4235 // The one live item keeps a restrained blue (the chat's 4236 // in-flight accent) â a small, subtle box, not the old sky. 4237 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "h-2 w-2 rounded-[2px] bg-blue-500" }) 4238 ) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "h-2.5 w-2.5 rounded-[3px] border-[1.5px] border-gray-300" }) }), 4239 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: cn("flex-1", isDone && "text-gray-400 line-through", isActive && "font-medium text-gray-900", !isDone && !isActive && "text-gray-600"), children: label }) 4240 ] }, i); 4241 }) }) 4242 ] }) 4243 ); 4244} 4245const cardContainerClasses = { 4246 info: "border-gray-200 bg-gray-50/70", 4247 "pending-action": "border-amber-200/80 bg-amber-50/40", 4248 success: "border-gray-200 bg-gray-50/70", 4249 warning: "border-amber-200/70 bg-amber-50/30", 4250 danger: "border-red-200/80 bg-red-50/40" 4251}; 4252const iconClasses = { 4253 info: "text-gray-500", 4254 "pending-action": "text-amber-500", 4255 success: "text-emerald-500", 4256 warning: "text-amber-500", 4257 danger: "text-red-500" 4258}; 4259const titleClasses = { 4260 info: "text-gray-900", 4261 "pending-action": "text-gray-900", 4262 success: "text-gray-900", 4263 warning: "text-gray-900", 4264 danger: "text-gray-900" 4265}; 4266const bodyClasses = { 4267 info: "text-gray-600", 4268 "pending-action": "text-gray-600", 4269 success: "text-gray-600", 4270 warning: "text-gray-600", 4271 danger: "text-gray-600" 4272}; 4273const lineContainerClasses = { 4274 info: "border-gray-200 bg-gray-50/70 text-gray-600", 4275 "pending-action": "border-amber-200/80 bg-amber-50/40 text-gray-700", 4276 success: "border-gray-200 bg-gray-50/70 text-gray-600", 4277 warning: "border-amber-200/70 bg-amber-50/30 text-gray-700", 4278 danger: "border-red-200/80 bg-red-50/40 text-gray-700" 4279}; 4280const dismissClasses = { 4281 info: "text-gray-400 hover:text-gray-600", 4282 "pending-action": "text-gray-400 hover:text-gray-600", 4283 success: "text-gray-400 hover:text-gray-600", 4284 warning: "text-gray-400 hover:text-gray-600", 4285 danger: "text-gray-400 hover:text-gray-600" 4286}; 4287function SystemCard({ 4288 accent, 4289 icon, 4290 title, 4291 children, 4292 actions, 4293 className 4294}) { 4295 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn$1("mx-4 rounded-lg border p-4 state-card-fade", cardContainerClasses[accent], className), children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-start gap-3", children: [
4296 icon && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: cn$1("mt-0.5 flex-shrink-0", iconClasses[accent]), children: icon }), 4297 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1", children: [ 4298 title && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: cn$1("text-sm font-medium", titleClasses[accent]), children: title }), 4299 children && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn$1("text-sm", title ? "mt-1" : "", bodyClasses[accent]), children }), 4300 actions && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-3", children: actions }) 4301 ] }) 4302 ] }) }); 4303} 4304function SystemLine({ 4305 accent, 4306 children, 4307 onDismiss, 4308 className 4309}) { 4310 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: cn$1("mx-4 flex items-start gap-2 rounded-lg border p-2 text-sm state-card-fade", lineContainerClasses[accent], className), children: [ 4311 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1", children }), 4312 onDismiss && /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onClick: onDismiss, "aria-label": "Dismiss", className: cn$1("flex-shrink-0", dismissClasses[accent]), children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "h-4 w-4" }) }) 4313 ] }); 4314} 4315function AskUserQuestionCard({ 4316 pendingQuestion, 4317 onSubmit 4318}) { 4319 const { 4320 questions, 4321 interactionId 4322 } = pendingQuestion; 4323 const [currentIdx, setCurrentIdx] = reactExports.useState(0); 4324 const [answers, setAnswers] = reactExports.useState({}); 4325 const [multiSelections, setMultiSelections] = reactExports.useState({}); 4326 const [otherText, setOtherText] = reactExports.useState({}); 4327 const [showOtherInput, setShowOtherInput] = reactExports.useState({}); 4328 const [isSubmitting, setIsSubmitting] = reactExports.useState(false); 4329 const question = questions[currentIdx]; 4330 const isLast = currentIdx === questions.length - 1; 4331 const allAnswered = questions.every((q) => answers[q.question]); 4332 const handleSelect = reactExports.useCallback((q, label) => { 4333 if (q.multiSelect) { 4334 const current = new Set(multiSelections[q.question] || []); 4335 if (current.has(label)) { 4336 current.delete(label); 4337 } else { 4338 current.add(label); 4339 } 4340 setMultiSelections((prev) => ({ 4341 ...prev, 4342 [q.question]: current 4343 })); 4344 if (current.size > 0) { 4345 setAnswers((prev) => ({ 4346 ...prev, 4347 [q.question]: Array.from(current).join(", ") 4348 })); 4349 } else { 4350 setAnswers((prev) => { 4351 const copy = { 4352 ...prev 4353 }; 4354 delete copy[q.question]; 4355 return copy; 4356 }); 4357 } 4358 setShowOtherInput((prev) => ({ 4359 ...prev, 4360 [q.question]: false 4361 })); 4362 } else { 4363 setAnswers((prev) => ({ 4364 ...prev, 4365 [q.question]: label 4366 })); 4367 setShowOtherInput((prev) => ({ 4368 ...prev, 4369 [q.question]: false 4370 })); 4371 if (!isLast) { 4372 setTimeout(() => setCurrentIdx((i) => i + 1), 150); 4373 } 4374 } 4375 }, [isLast, multiSelections]); 4376 const handleOtherSubmit = reactExports.useCallback((q) => { 4377 const text = (otherText[q.question] || "").trim(); 4378 if (!text) return; 4379 setAnswers((prev) => ({ 4380 ...prev, 4381 [q.question]: text 4382 })); 4383 setShowOtherInput((prev) => ({ 4384 ...prev, 4385 [q.question]: false 4386 })); 4387 if (!isLast) { 4388 setTimeout(() => setCurrentIdx((i) => i + 1), 150); 4389 } 4390 }, [otherText, isLast]); 4391 const handleSubmitAll = reactExports.useCallback(async () => { 4392 if (isSubmitting) return; 4393 setIsSubmitting(true); 4394 try { 4395 await onSubmit(interactionId, answers); 4396 } finally { 4397 setIsSubmitting(false); 4398 } 4399 }, [interactionId, answers, onSubmit, isSubmitting]); 4400 const selectedForCurrent = question.multiSelect ? multiSelections[question.question] || /* @__PURE__ */ new Set() : new Set(answers[question.question] ? [answers[question.question]] : []); 4401 return /* @__PURE__ */ jsxRuntimeExports.jsxs(SystemCard, { accent: "info", children: [ 4402 questions.length >
4402 1 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-3 flex items-center gap-1", children: questions.map((q, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { type: "button", onClick: () => setCurrentIdx(i), className: cn$1("flex items-center gap-1 rounded-full px-2.5 py-1 text-xs font-medium transition-colors", i === currentIdx ? "bg-blue-600 text-white" : answers[q.question] ? "bg-blue-200 text-blue-800" : "bg-white text-gray-600 hover:bg-gray-100"), children: [ 4403 answers[q.question] && i !== currentIdx && /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "h-3 w-3" }), 4404 q.header || `Q${i + 1}` 4405 ] }, i)) }), 4406 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mb-3 text-sm font-medium text-gray-900", children: question.question }), 4407 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-3 space-y-2", children: [ 4408 question.options.map((opt) => { 4409 const isSelected = selectedForCurrent.has(opt.label); 4410 return /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { type: "button", onClick: () => handleSelect(question, opt.label), className: cn$1("flex w-full items-start gap-3 rounded-lg border p-3 text-left transition-colors", isSelected ? "border-blue-400 bg-blue-100" : "border-gray-200 bg-white hover:border-blue-300 hover:bg-blue-50/50"), children: [
4411 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn$1("mt-0.5 flex h-4 w-4 flex-shrink-0 items-center justify-center rounded border", question.multiSelect ? "rounded" : "rounded-full", isSelected ? "border-blue-600 bg-blue-600" : "border-gray-300"), children: isSelected && /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "h-3 w-3 text-white" }) }), 4412 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 flex-1", children: [ 4413 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm font-medium text-gray-900", children: opt.label }), 4414 opt.description && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-0.5 text-xs text-gray-500", children: opt.description }) 4415 ] }) 4416 ] }, opt.label); 4417 }), 4418 showOtherInput[question.question] ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-lg border border-gray-300 bg-white p-3", children: [ 4419 /* @__PURE__ */ jsxRuntimeExports.jsx("input", { type: "text", className: "w-full rounded border border-gray-200 px-2 py-1.5 text-sm focus:border-blue-400 focus:outline-none", placeholder: "Type your answerâ¦", value: otherText[question.question] || "", onChange: (e) => setOtherText((prev) => ({ 4420 ...prev, 4421 [question.question]: e.target.value 4422 })), onKeyDown: (e) => { 4423 if (e.key === "Enter") handleOtherSubmit(question); 4424 if (e.key === "Escape") setShowOtherInput((prev) => ({ 4425 ...prev, 4426 [question.question]: false 4427 })); 4428 }, autoFocus: true }),
4429 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-2 flex gap-2", children: [ 4430 /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { size: "sm", onClick: () => handleOtherSubmit(question), disabled: !(otherText[question.question] || "").trim(), children: "Confirm" }), 4431 /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { size: "sm", variant: "ghost", onClick: () => setShowOtherInput((prev) => ({ 4432 ...prev, 4433 [question.question]: false 4434 })), children: "Cancel" }) 4435 ] }) 4436 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { type: "button", onClick: () => setShowOtherInput((prev) => ({ 4437 ...prev, 4438 [question.question]: true 4439 })), className: "flex w-full items-center gap-3 rounded-lg border border-dashed border-gray-200 p-3 text-left text-sm text-gray-500 transition-colors hover:border-blue-300 hover:text-gray-700", children: [ 4440 /* @__PURE__ */ jsxRuntimeExports.jsx(MessageSquare, { className: "h-4 w-4 flex-shrink-0" }), 4441 "Otherâ¦" 4442 ] }) 4443 ] }), 4444 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
4445 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [ 4446 currentIdx > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { size: "sm", variant: "ghost", onClick: () => setCurrentIdx((i) => i - 1), children: [ 4447 /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronLeft, { className: "mr-1 h-3 w-3" }), 4448 "Back" 4449 ] }), 4450 !isLast && answers[question.question] && /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { size: "sm", variant: "ghost", onClick: () => setCurrentIdx((i) => i + 1), children: [ 4451 "Next", 4452 /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "ml-1 h-3 w-3" }) 4453 ] }) 4454 ] }), 4455 (isLast || allAnswered) && /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { size: "sm", onClick: handleSubmitAll, disabled: !allAnswered || isSubmitting, children: [ 4456 isSubmitting && /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "mr-1.5 h-3 w-3 animate-spin" }), 4457 isSubmitting ? "Sendingâ¦" : "Submit answers" 4458 ] }) 4459 ] }) 4460 ] }); 4461} 4462function SelectedElementChip({ 4463 selectedElement, 4464 onClear 4465}) { 4466 if (!selectedElement) { 4467 return null; 4468 } 4469 const { 4470 tag, 4471 textPreview 4472 } = selectedElement; 4473 const hasPreview = textPreview !== ""; 4474 const noun = friendlyElementNoun(tag); 4475 const label = formatFriendlyLabel(tag, textPreview); 4476 const ariaLabel = hasPreview ? `Selected ${noun} '${textPreview}'` : `Selected ${noun}`; 4477 const handleKeyDown = (event) => { 4478 if (event.key === "Backspace" || event.key === "Delete") { 4479 event.preventDefault(); 4480 onClear(); 4481 } 4482 }; 4483 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { role: "button", tabIndex: 0, "aria-label": ariaLabel, onKeyDown: handleKeyDown, className: cn$1( 4484 // White carded chip, min-height so very-narrow chips don't collapse; 4485 // hard max-width so long text previews truncate instead of spanning 4486 // the composer. 4487 "inline-flex min-h-[28px] max-w-[220px] items-center gap-1.5 rounded-[12px]", 4488 "border border-[#1B1A22]/[0.08] bg-white px-2.5 py-1 text-xs text-foreground", 4489 "shadow-[0_1px_2px_rgba(27,26,34,0.06)]", 4490 "focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1" 4491 ), children: [ 4492 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: label }), 4493 /* @__PURE__ */ jsxRuntimeExports.jsx( 4494 Button, 4495 { 4496 type: "button", 4497 size: "icon", 4498 variant: "ghost", 4499 "aria-label": "Remove selection", 4500 onClick: onClear, 4501 className: "h-6 w-6 shrink-0 rounded-full p-0 text-muted-foreground hover:bg-background hover:text-foreground", 4502 children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "h-3 w-3" }) 4503 } 4504 ) 4505 ] }); 4506} 4507function ComposerAttachmentTray({ 4508 selectedElement, 4509 images, 4510 onClearSelection, 4511 onRemoveImage 4512}) { 4513 const hasAttachments = Boolean(selectedElement) || images.length > 0; 4514 const rowRef = reactExports.useRef(null); 4515 const [areaHeight, setAreaHeight] = reactExports.useState(0); 4516 reactExports.useLayoutEffect(() => { 4517 if (!hasAttachments) { 4518 setAreaHeight(0); 4519 return; 4520 } 4521 const row = rowRef.current; 4522 if (!row) { 4523 return; 4524 } 4525 const measure = () => setAreaHeight(row.scrollHeight); 4526 measure(); 4527 if (typeof ResizeObserver === "undefined") { 4528 return; 4529 } 4530 const ro = new ResizeObserver(measure); 4531 ro.observe(row); 4532 return () => ro.disconnect(); 4533 }, [hasAttachments, images.length, selectedElement]); 4534 return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 4535 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { "aria-hidden": true, className: cn("composer-tray-bg", hasAttachments && "is-expanded") }), 4536 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn("composer-chip-area", hasAttachments && "is-open"), style: { 4537 height: areaHeight 4538 }, "aria-hidden": !hasAttachments, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { ref: rowRef, className: "composer-chip-row flex flex-col gap-2 px-2 pb-2 pt-1.5", children: [
4539 selectedElement && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-wrap items-start gap-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "composer-chip-enter", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectedElementChip, { selectedElement, onClear: onClearSelection }) }) }), 4540 images.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-wrap items-start gap-2", children: images.map((item) => { 4541 var _a, _b, _c, _d, _e, _f, _g, _h; 4542 const isGetty = item.galleryMediaSource === "iStock"; 4543 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "composer-chip-enter relative shrink-0", children: [ 4544 item.galleryMediaType === "video" || item.galleryMediaType === "lottie" ? /* @__PURE__ */ jsxRuntimeExports.jsx("video", { src: item.imagePreview, className: "h-14 w-14 rounded-[12px] border border-[#1B1A22]/[0.08] object-cover shadow-[0_1px_2px_rgba(27,26,34,0.06)]", autoPlay: true, loop: true, muted: true, playsInline: true, onContextMenu: isGetty ? (e) => e.preventDefault() : void 0 }) : ((_a = item.imageFile) == null ? void 0 : _a.type) === "application/pdf" ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex h-14 w-14 flex-col items-center justify-center rounded-[12px] border border-[#1B1A22]/[0.08] bg-white px-1 shadow-[0_1px_2px_rgba(27,26,34,0.06)]", children: [
4545 /* @__PURE__ */ jsxRuntimeExports.jsx(FileText, { className: "h-5 w-5 text-muted-foreground" }), 4546 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "mt-0.5 w-full truncate text-center text-[10px] text-muted-foreground", children: ((_b = item.imageFile) == null ? void 0 : _b.name) || "PDF" }) 4547 ] }) : ((_d = (_c = item.imageFile) == null ? void 0 : _c.name) == null ? void 0 : _d.toLowerCase().endsWith(".glb")) ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex h-14 w-14 flex-col items-center justify-center rounded-[12px] border border-[#1B1A22]/[0.08] bg-white px-1 shadow-[0_1px_2px_rgba(27,26,34,0.06)]", children: [ 4548 /* @__PURE__ */ jsxRuntimeExports.jsx(Box, { className: "h-5 w-5 text-muted-foreground" }), 4549 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "mt-0.5 w-full truncate text-center text-[10px] text-muted-foreground", children: ((_e = item.imageFile) == null ? void 0 : _e.name) || "3D model" }) 4550 ] }) : ((_f = item.imageFile) == null ? void 0 : _f.type) === "image/heic" || ((_g = item.imageFile) == null ? void 0 : _g.type) === "image/heif" ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex h-14 w-14 flex-col items-center justify-center rounded-[12px] border border-[#1B1A22]/[0.08] bg-white px-1 shadow-[0_1px_2px_rgba(27,26,34,0.06)]", children: [
4551 /* @__PURE__ */ jsxRuntimeExports.jsx(Image, { className: "h-5 w-5 text-muted-foreground" }), 4552 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "mt-0.5 w-full truncate text-center text-[10px] text-muted-foreground", children: ((_h = item.imageFile) == null ? void 0 : _h.name) || "HEIC" }) 4553 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: item.imagePreview, alt: "Preview", className: "h-14 w-14 rounded-[12px] border border-[#1B1A22]/[0.08] object-cover shadow-[0_1px_2px_rgba(27,26,34,0.06)]", onContextMenu: isGetty ? (e) => e.preventDefault() : void 0 }), 4554 item.error && /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [ 4555 /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute inset-0 flex cursor-default flex-col items-center justify-center rounded-[12px] bg-red-900/60", children: [
4556 /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { className: "h-4 w-4 text-red-200" }), 4557 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "mt-0.5 px-0.5 text-center text-[10px] font-medium text-red-100", children: item.isFlagged ? "Not allowed" : "Error" }) 4558 ] }) }), 4559 /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "max-w-xs text-sm", children: item.error }) }) 4560 ] }) }), 4561 item.uploading ? /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { type: "button", className: "group absolute -right-1.5 -top-1.5 flex h-5 w-5 items-center justify-center rounded-full border border-[#1B1A22]/[0.08] bg-white shadow-sm hover:bg-muted", onClick: () => onRemoveImage(item.index), children: [ 4562 /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "h-3 w-3 animate-spin text-muted-foreground group-hover:hidden" }), 4563 /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "hidden h-3 w-3 group-hover:block" }) 4564 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { type: "button", size: "icon", variant: "ghost", className: "absolute -right-1.5 -top-1.5 h-5 w-5 rounded-full border border-[#1B1A22]/[0.08] bg-white p-0 shadow-sm hover:bg-muted", onClick: () => onRemoveImage(item.index), children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "h-3 w-3" }) }) 4565 ] }, item.index); 4566 }) }) 4567 ] }) }) 4568 ] }); 4569} 4570const trayShellClasses = { 4571 danger: "border-red-200/80 bg-red-50/80", 4572 warning: "border-amber-200/80 bg-amber-50/70", 4573 info: "border-gray-200/90 bg-slate-50/95" 4574}; 4575const bubbleBorderClasses = { 4576 danger: "border-red-200/75 text-red-950", 4577 warning: "border-amber-200/75 text-amber-950", 4578 info: "border-gray-200/85 text-gray-800" 4579}; 4580const actionClasses = { 4581 danger: "text-red-700 hover:text-red-800", 4582 warning: "text-amber-700 hover:text-amber-800", 4583 info: "text-gray-700 hover:text-gray-900" 4584}; 4585function NoticeTray({ 4586 notice, 4587 children 4588}) { 4589 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { role: "group", "aria-live": notice.severity === "info" ? "polite" : "assertive", className: cn("state-card-fade rounded-[14px] border shadow-[0_1px_2px_rgba(27,26,34,0.04),0_12px_30px_-20px_rgba(27,26,34,0.16)]", trayShellClasses[notice.severity]), children: [ 4590 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-start gap-2.5 px-3 pb-2.5 pt-2", children: [
4591 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "mt-px flex-shrink-0", children: notice.icon }), 4592 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 flex-1", children: [ 4593 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-start gap-2", children: [ 4594 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "min-w-0 flex-1 text-[13px] font-medium leading-5 text-gray-900", children: notice.title }), 4595 (notice.onMinimize || notice.onDismiss) && // Both controls live here, not just minimize: a dismissible 4596 // notice whose body says "dismiss this" is a dead end without 4597 // an X, and the tray is where the body is actually read. The 4598 // shared wrapper carries the edge offsets so a notice offering 4599 // only one of them still sits flush with the corner. 4600 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "-mr-1 -mt-0.5 flex flex-shrink-0 items-center", children: [ 4601 notice.onMinimize && /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", "aria-label": "Minimize", title: "Minimize", onClick: notice.onMinimize, className: "rounded p-1 text-gray-400 transition-colors hover:text-gray-600", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronUp, { className: "h-3.5 w-3.5" }) }),
4602 notice.onDismiss && /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", "aria-label": "Dismiss", title: "Dismiss", onClick: notice.onDismiss, className: "rounded p-1 text-gray-400 transition-colors hover:text-gray-600", children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "h-3.5 w-3.5" }) }) 4603 ] }) 4604 ] }), 4605 notice.body && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-0.5 text-[13px] leading-5 text-gray-600", children: notice.body }), 4606 notice.action && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { size: "sm", variant: "outline", className: "h-7 px-3 text-xs", onClick: notice.action.onClick, disabled: notice.action.disabled, children: notice.action.label }) }) 4607 ] }) 4608 ] }), 4609 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "relative -mx-px -mb-px", children }) 4610 ] }); 4611} 4612function NoticeBubble({ 4613 notice, 4614 delayMs 4615}) { 4616 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { 4617 animationDelay: `${delayMs}ms` 4618 }, className: cn("notice-bubble-enter flex items-center gap-2.5 rounded-[12px] border bg-white/95 px-3.5 py-2 text-[13px]", "shadow-[0_1px_2px_rgba(27,26,34,0.05),0_8px_20px_-14px_rgba(27,26,34,0.16)]", bubbleBorderClasses[notice.severity]), children: [ 4619 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex shrink-0 items-center", children: notice.icon }),
4620 notice.onRestore ? /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onClick: notice.onRestore, "aria-label": "Show details", title: "Show details", className: "min-w-0 flex-1 truncate text-left hover:underline", children: notice.title }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0 flex-1 truncate", children: notice.title }), 4621 notice.action && /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onClick: notice.action.onClick, disabled: notice.action.disabled, className: cn("shrink-0 font-medium hover:underline disabled:cursor-not-allowed disabled:text-gray-400 disabled:no-underline", actionClasses[notice.severity]), children: notice.action.label }), 4622 notice.onDismiss && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 4623 notice.action && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { "aria-hidden": true, className: "h-3.5 w-px shrink-0 bg-gray-200" }), 4624 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", "aria-label": "Dismiss", onClick: notice.onDismiss, className: "shrink-0 text-gray-400 transition-colors hover:text-gray-600", children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "h-3.5 w-3.5" }) }) 4625 ] }) 4626 ] }); 4627} 4628function ComposerNoticeStack({ 4629 tray, 4630 bubbles, 4631 children 4632}) { 4633 const visualBubbles = [...bubbles].reverse(); 4634 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2", children: [ 4635 visualBubbles.map((notice, visualIndex) => /* @__PURE__ */ jsxRuntimeExports.jsx(NoticeBubble, { notice, delayMs: visualIndex * 60 }, notice.id)), 4636 tray ? /* @__PURE__ */ jsxRuntimeExports.jsx(NoticeTray, { notice: tray, children }) : children 4637 ] }); 4638} 4639const SEVERITY_RANK = { 4640 danger: 3, 4641 warning: 2, 4642 info: 1 4643}; 4644const seqFor = (map, id) => map.get(id) ?? 0; 4645function pickTrayAndBubbles(notices, minimizedIds) { 4646 if (notices.length === 0) return { 4647 tray: null, 4648 bubbles: [] 4649 }; 4650 const byActivation = (a, b) => b.activatedSeq - a.activatedSeq; 4651 const trayCandidates = minimizedIds && minimizedIds.size > 0 ? notices.filter((notice) => !minimizedIds.has(notice.id)) : notices; 4652 if (trayCandidates.length === 0) { 4653 return { 4654 tray: null, 4655 bubbles: [...notices].sort(byActivation) 4656 }; 4657 } 4658 const sortedForTray = [...trayCandidates].sort((a, b) => { 4659 const severityDelta = SEVERITY_RANK[b.severity] - SEVERITY_RANK[a.severity]; 4660 if (severityDelta !== 0) return severityDelta; 4661 return b.activatedSeq - a.activatedSeq; 4662 }); 4663 const tray = sortedForTray[0]; 4664 const bubbles = notices.filter((notice) => notice.id !== tray.id).sort(byActivation); 4665 return { 4666 tray, 4667 bubbles 4668 }; 4669} 4670function getActiveComposerNoticeIds({ 4671 isOutOfCredits, 4672 isSessionExpired, 4673 viteError, 4674 sessionLostNotice, 4675 needsPaymentsSetup, 4676 needsPaymentsUpgrade, 4677 paymentsDisabled, 4678 commerceSetup, 4679 postageUncharged, 4680 currencyRestated 4681}) { 4682 return [isOutOfCredits ? "out-of-credits" : null, isSessionExpired ? "session-expired" : null, viteError ? "vite-error" : null, sessionLostNotice ? "session-lost" : null, needsPaymentsSetup || needsPaymentsUpgrade || paymentsDisabled || (commerceSetup == null ? void 0 : commerceSetup.paymentState) === "ACTIVE" && commerceSetup.payoutState !== "ACTIVE" ? "payments-setup" : null, postageUncharged ? "postage-uncharged" : null, currencyRestated ? "currency-restated" : null].filter((id) => id !== null); 4683} 4684function currencyLabel(code) { 4685 const upper = code.toUpperCase(); 4686 try { 4687 const name = new Intl.DisplayNames(["en"], { 4688 type: "currency" 4689 }).of(upper); 4690 return !name || name === upper ? upper : `${upper} (${name})`; 4691 } catch { 4692 return upper; 4693 } 4694} 4695function paymentsNoticeCopy(args) { 4696 const owner = args.canStartPaymentsOnboarding; 4697 if (owner && !args.needsPaymentsUpgrade && args.commerceSetup) { 4698 return commerceSetupCopy(args.commerceSetup); 4699 } 4700 if (args.paymentsDisabled) { 4701 return { 4702 title: args.paymentsForInvoicing ? "Invoices canât be sent right now." : "Your store canât take payments right now.", 4703 body: owner ? "Stripe has turned payments off for your account â usually a review or a missing document. Check your Str
4703ipe dashboard for what they need, or contact support if nothing is waiting there." : "Stripe has turned payments off for the site ownerâs account. The owner needs to check their Stripe dashboard, or contact support." 4704 }; 4705 } 4706 if (args.paymentsForInvoicing) { 4707 const title2 = "You canât send invoices yet."; 4708 if (args.needsPaymentsUpgrade) { 4709 return { 4710 title: title2, 4711 body: owner ? "Invoicing is on the Pro plan. Upgrade and I can send the invoice you asked for." : "Invoicing is on the Pro plan â the siteâs owner can upgrade to switch it on." 4712 }; 4713 } 4714 if (!owner) { 4715 return { 4716 title: title2, 4717 body: "Invoices are paid into the site ownerâs Stripe account, which isnât set up yet. The owner needs to set up payments â or transfer the site to you if youâre the one who should be paid." 4718 }; 4719 } 4720 return { 4721 title: title2, 4722 body: args.paymentsPending ? "Your Stripe setup isnât finished yet. Pick up where you left off â once Stripe confirms your account, ask me again and Iâll send the invoice." : "Connect Stripe to send invoices. It takes about five minutes â youâll add your details with Stripe, who handle the payments, and what your clients pay goes straight to your own bank." 4723 }; 4724 } 4725 const title = "Your store canât take payments yet."; 4726 if (args.needsPaymentsUpgrade) { 4727 return { 4728 title, 4729 body: owner ? "Youâve added something to sell, but taking payments (and publishing your site) needs a paid plan. Upgrade and you can switch the buy buttons on." : "Thereâs something to sell here, but taking payments needs a paid plan â the siteâs owner can upgrade to switch the buy buttons on." 4730 }; 4731 } 4732 if (!owner) { 4733 return { 4734 title, 4735 body: "Thereâs something to sell here, but the buy buttons stay switched off until the siteâs owner sets up a payment account." 4736 }; 4737 } 4738 return { 4739 title, 4740 body: args.paymentsPending ? "Your Stripe setup isnât finished yet. Pick up where you left off â once Stripe confirms your account, the buy buttons switch on." : "Youâve added something to sell, but the buy buttons stay switched off until you set up your payment account. It only takes a few minutes â youâll add your details with Stripe, who handle the payments, and money from sales goes straight to your own bank." 4741 }; 4742} 4743function buildComposerNotices(args) { 4744 var _a, _b, _c; 4745 const notices = []; 4746 if (args.isOutOfCredits) { 4747 notices.push({ 4748 id: "out-of-credits", 4749 severity: "danger", 4750 icon: /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { className: "h-[15px] w-[15px] text-red-500" }), 4751 title: "You've run out of chat credits.", 4752 action: { 4753 label: "Add 200 edits for $10", 4754 onClick: args.onAddCredits 4755 }, 4756 activatedSeq: seqFor(args.activatedSeqById, "out-of-credits") 4757 }); 4758 } 4759 if (args.isSessionExpired) { 4760 notices.push({ 4761 id: "session-expired", 4762 severity: "warning", 4763 icon: /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { className: "h-[15px] w-[15px] text-amber-500" }), 4764 title: "Your session needs to refresh.", 4765 body: args.hasBufferedMessage ? "Send a new message or click Try Again to continue." : "Send a new message to continue.", 4766 action: args.hasBufferedMessage ? { 4767 label: "Try Again", 4768 onClick: args.onTryAgain, 4769 disabled: args.actionDisabled 4770 } : void 0, 4771 activatedSeq: seqFor(args.activatedSeqById, "session-expired") 4772 }); 4773 } 4774 if (args.viteError) { 4775 notices.push({ 4776 id: "vite-error", 4777 severity: "warning", 4778 icon: /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "h-[15px] w-[15px] text-amber-500" }), 4779 title: "Your website preview has an error", 4780 body: "The preview won't show your latest changes until it's fixed. You don't need to fix it yourself - just click the button below.", 4781 action: { 4782 label: "Have AI fix this", 4783 onClick: args.onFixVite, 4784 disabled: args.actionDisabled 4785 }, 4786 activatedSeq: seqFor(args.activatedSeqById, "vite-error") 4787 }); 4788 } 4789 if (args.needsPaymentsSetup || args.needsPaymentsUpgrade || args.paymentsDisabled || ((_a = args.commerceSetup) == null ? void 0 : _a.paymentState) === "ACTIVE" && args.commerceSetup.payoutState !== "ACTIVE") { 4790 const upgrade = args.needsPaymentsUpgrade; 4791 const stripeCopy = !upgrade && args.canStartPaymentsOnboarding && args.commerceSetup ? commerceSetupCopy(args.commerceSetup) : null; 4792 const copy = paymentsNoticeCopy(args); 4793 const title = args.commerceSetupRefreshError && !upgrade ? "We couldnât check your Str
4793ipe status" : copy.title; 4794 const body = args.commerceSetupRefreshError && !upgrade ? "The last status may be out of date. Check again to find out whether Stripe needs more information or your account is ready." : copy.body; 4795 let action; 4796 if (args.commerceSetupRefreshError && !upgrade && args.onRefreshCommerceSetup) { 4797 action = { 4798 label: args.commerceSetupRefreshing ? "Checking Stripeâ¦" : "Check status", 4799 onClick: args.onRefreshCommerceSetup, 4800 disabled: args.commerceSetupRefreshing 4801 }; 4802 } else if (stripeCopy) { 4803 const onClick = { 4804 continue: args.onSetUpPayments, 4805 dashboard: args.onOpenStripeDashboard, 4806 refresh: args.onRefreshCommerceSetup, 4807 none: void 0, 4808 website: openCommerceWebsiteSetup 4809 }[stripeCopy.action]; 4810 if (onClick) { 4811 action = { 4812 label: stripeCopy.action === "refresh" && args.commerceSetupRefreshing ? "Checking Stripeâ¦" : stripeCopy.actionLabel, 4813 onClick, 4814 disabled: args.paymentsActionDisabled || args.commerceSetupRefreshing 4815 }; 4816 } 4817 } else if (!args.paymentsDisabled && args.canStartPaymentsOnboarding) { 4818 action = upgrade ? { 4819 label: "View upgrade options", 4820 onClick: args.onUpgradePlan 4821 } : { 4822 label: args.paymentsPending ? "Continue Stripe setup" : "Connect with Stripe", 4823 onClick: args.onSetUpPayments, 4824 disabled: args.paymentsActionDisabled 4825 }; 4826 } 4827 notices.push({ 4828 id: "payments-setup", 4829 severity: "info",
4830 icon: args.paymentsForInvoicing ? /* @__PURE__ */ jsxRuntimeExports.jsx(FileText, { className: "h-[15px] w-[15px] text-blue-500" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(CreditCard, { className: "h-[15px] w-[15px] text-blue-500" }), 4831 title, 4832 body: stripeCopy || args.canStartPaymentsOnboarding && !upgrade ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 4833 body, 4834 /* @__PURE__ */ jsxRuntimeExports.jsx(CommerceSetupCheckedAt, { checkedAt: (_b = args.commerceSetup) == null ? void 0 : _b.checkedAt, refreshing: !!args.commerceSetupRefreshing }) 4835 ] }) : body, 4836 action, 4837 ...args.paymentsNoticeMinimized ? { 4838 onRestore: args.onToggleMinimizePayments 4839 } : { 4840 onMinimize: args.onToggleMinimizePayments 4841 }, 4842 activatedSeq: seqFor(args.activatedSeqById, "payments-setup") 4843 }); 4844 } 4845 if (args.postageUncharged) { 4846 notices.push({ 4847 id: "postage-uncharged", 4848 severity: "info", 4849 icon: /* @__PURE__ */ jsxRuntimeExports.jsx(Truck, { className: "h-[15px] w-[15px] text-blue-500" }), 4850 title: "Delivery is free on every order.", 4851 body: "Your checkout asks buyers for a delivery address but doesnât charge them anything to ship â so postage comes out of your own pocket on every sale. Tell me in the chat what flat rate you want to charge for delivery and Iâll set it up. If free delivery is deliberate, dismiss this.", 4852 onDismiss: args.onDismissPostage, 4853 activatedSeq: seqFor(args.activatedSeqById, "postage-uncharged") 4854 }); 4855 } 4856 if (args.currencyRestated) { 4857 const to = args.currencyRestated.to.toUpperCase(); 4858 const from = ((_c = args.currencyRestated.from) == null ? void 0 : _c.toUpperCase()) ?? null; 4859 notices.push({ 4860 id: "currency-restated", 4861 severity: "warning", 4862 icon: /* @__PURE__ */ jsxRuntimeExports.jsx(Banknote, { className: "h-[15px] w-[15px] text-amber-500" }), 4863 title: `Your prices are now in ${to}.`, 4864 // No worked example on purpose. "89 became 89" holds for usd->mxn but 4865 // lies for a move across decimal classes (usd->jpy takes 8900 minor 4866 // units from $89.00 to JPY 8900), and this notice exists precisely 4867 // because prices are already hard to eyeball. Describe the mechanism, 4868 // let them read their own numbers. 4869 // "follows your Stripe account" rather than "connecting Stripe": the 4870 // same push also fires on a later account.updated (a merchant changing 4871 // their Stripe country), and a merchant who connected weeks ago would 4872 // read the narrower wording as being about someone else. 4873 body: `This store's selling currency follows your Stripe account, and it just moved${from ? ` from ${from}` : ""} to ${currencyLabel(args.currencyRestated.to)}. Your amounts were carried straight across instead of being converted at an exchange rate, so every price now means something different from when you set it. Check they are still what you mean to charge â tell me the right ones in the chat and I will update them. Worth doing now: once a real order comes in, the currency is locked for good.`, 4874 onDismiss: args.onDismissCurrency, 4875 activatedSeq: seqFor(args.activatedSeqById, "currency-restated") 4876 }); 4877 } 4878 if (args.sessionLostNotice) { 4879 notices.push({ 4880 id: "session-lost", 4881 severity: "info", 4882 icon: /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "h-[15px] w-[15px] text-gray-500" }), 4883 title: args.sessionLostNotice, 4884 onDismiss: args.onDismissSessionLost, 4885 activatedSeq: seqFor(args.activatedSeqById, "session-lost") 4886 }); 4887 } 4888 return notices; 4889} 4890function buildPickerContextSection({ 4891 outerHTML, 4892 path, 4893 sourceLoc 4894}) { 4895 const loc = sourceLoc ? ` 4896Source location (file:line:col): ${sourceLoc} 4897` : ""; 4898 return ` 4899User is on page with path "${path}" 4900 4901Selected element HTML: 4902\`\`\` 4903${outerHTML} 4904\`\`\` 4905${loc}`; 4906} 4907const TRUNCATED_COMMENT = " truncated "; 4908function collectDescendantsReverseDepthFirst(root) { 4909 const result = []; 4910 const stack = []; 4911 for (let i = root.childNodes.length - 1; i >= 0; i--) { 4912 stack.push(root.childNodes[i]); 4913 } 4914 while (stack.length > 0) { 4915 const node = stack.pop(); 4916 result.push(node); 4917 if (node.nodeType === 1) { 4918 const el = node; 4919 for (let i = el.childNodes.length - 1; i >= 0; i--) { 4920 stack.push(el.childNodes[i]); 4921 } 4922 } 4923 } 4924 return result.reverse(); 4925} 4926const textEncoder = new TextEncoder();
4927const byteLength = (s) => textEncoder.encode(s).length; 4928function truncateOuterHtml(html, capBytes = 2e5) { 4929 if (byteLength(html) <= capBytes) { 4930 return { 4931 html, 4932 truncated: false, 4933 rejected: false 4934 }; 4935 } 4936 const doc = new DOMParser().parseFromString(html, "text/html"); 4937 const root = doc.body.firstElementChild; 4938 if (!root) { 4939 return { 4940 html: "", 4941 truncated: false, 4942 rejected: true 4943 }; 4944 } 4945 const descendants = collectDescendantsReverseDepthFirst(root); 4946 let firstRemoval = true; 4947 let truncated = false; 4948 for (const node of descendants) { 4949 if (!node.parentNode) continue; 4950 if (firstRemoval) { 4951 const comment = doc.createComment(TRUNCATED_COMMENT); 4952 node.parentNode.replaceChild(comment, node); 4953 firstRemoval = false; 4954 } else { 4955 node.parentNode.removeChild(node); 4956 } 4957 truncated = true; 4958 if (byteLength(root.outerHTML) <= capBytes) { 4959 return { 4960 html: root.outerHTML, 4961 truncated: true, 4962 rejected: false 4963 }; 4964 } 4965 } 4966 if (byteLength(root.outerHTML) > capBytes) { 4967 return { 4968 html: "", 4969 truncated: false, 4970 rejected: true 4971 }; 4972 } 4973 return { 4974 html: root.outerHTML, 4975 truncated, 4976 rejected: false 4977 }; 4978} 4979function buildAgentMessage(snapshot, context) { 4980 var _a; 4981 const { 4982 text: messageText, 4983 images, 4984 selectedElement, 4985 currentPath 4986 } = snapshot; 4987 const { 4988 websitePlan, 4989 isAgencyUser, 4990 website, 4991 currentUserId, 4992 tokenMinutesLeft, 4993 isInitialCreation, 4994 generateId: generateId$1 = generateId 4995 } = context; 4996 const validImages = images == null ? void 0 : images.filter((media) => media.cdnUrl && !media.error); 4997 let contextBlock = "<context for the AI>\n"; 4998 if (isAgencyUser) { 4999 contextBlock += "User is on Agency plan: Yes\n"; 5000 } else { 5001 contextBlock += `Website plan: ${websitePlan} 5002`; 5003 } 5004 contextBlock += `Current page (the page the user is viewing in the preview): ${currentPath} 5005`; 5006 const hasImages = validImages && validImages.length > 0; 5007 if (hasImages) { 5008 contextBlock += "\nUser uploaded media:\n\n"; 5009 validImages.forEach((media, index) => { 5010 var _a2, _b, _c; 5011 if (media.cdnUrl) { 5012 const isModel = media.mimeType === "model/gltf-binary" || ((_b = (_a2 = media.imageFile) == null ? void 0 : _a2.name) == null ? void 0 : _b.toLowerCase().endsWith(".glb")); 5013 const mediaType = isModel ? "3D model" : media.galleryMediaType || "image"; 5014 const isVideo = mediaType === "video" || mediaType === "lottie"; 5015 contextBlock += `${media.isGalleryImage ? "Gallery" : "Uploaded"} ${mediaType} ${index + 1}: 5016`; 5017 if (isVideo) { 5018 contextBlock += `Video URL: ${media.cdnUrl} 5019`; 5020 if (media.posterUrl) { 5021 contextBlock += `Poster URL: ${media.posterUrl} 5022`; 5023 } 5024 contextBlock += `Type: Cloudflare Stream video â render with the LandingsiteVideo component (src = Video URL, poster = Poster URL). The video may still be encoding for a short time; LandingsiteVideo handles that. 5025`; 5026 } else if (isModel) { 5027 contextBlock += `Model URL: ${media.cdnUrl} 5028`; 5029 if (media.r2Key) { 5030 contextBlock += `r2Key: ${media.r2Key} 5031`; 5032 } 5033 contextBlock += `Type: glTF binary (.glb) 3D model. Show it in an interactive 3D viewer (e.g. Google's <model-viewer> web component with auto-rotate and camera-controls), loading the model from the Model URL above. It is not an image â never put it in <img> or LandingsiteImage. 5034`; 5035 } else { 5036 const baseUrl = media.cdnUrl.replace(/[?&]w=\d+/, ""); 5037 contextBlock += `Image URL: ${baseUrl} 5038`; 5039 } 5040 if ((_c = media.imageFile) == null ? void 0 : _c.name) { 5041 contextBlock += `Filename: ${media.imageFile.name} 5042`; 5043 } 5044 if (media.description) { 5045 contextBlock += `Description: ${media.description} 5046`; 5047 } 5048 contextBlock += "\n"; 5049 } 5050 }); 5051 const hasImageMedia = validImages.some((m) => m.cdnUrl && !["video", "lottie"].includes(m.galleryMediaType || "")); 5052 if (hasImageMedia) { 5053 contextBlock += "When placing images on the website, append ?w= with the appropriate width for the layout context. Available widths: 160, 320, 480, 768, 1200, 1920. Choose based on the image's rendered size â e.g. ?w=1920 for full-width heroes, ?w=768 for content columns, ?w=480 for cards/thumbnails, ?w=320 for small icons/avatars.\n\n"; 5054 } 5055 const hasUserUploads = validImages.some((m) => { 5056 var _a2, _b; 5057 return !m.isGalleryImage && m.cdnUrl && ((_b = (_a2 = m.imageFile) == null ? void 0 : _a2.type) == null ? void 0 : _b.startsWith("image/")); 5058 }); 5059 if (hasUserUploads) { 5060 contextBlock += "When placing images on the website, use the Description provided above as the alt attribute. If no description is provided, write a descriptive alt attribute based on the image content and how it's being used on the site. Do not leave alt empty or use generic placeholders.\n\n"; 5061 } 5062 const hasFilenames = validImages.some((m) => { 5063 var _a2; 5064 return (_a2 = m.imageFile) == null ? void 0 : _a2.name; 5065 }); 5066 if (hasFilenames) { 5067 contextBlock += "The Filename shown for each upload is the name the user gave it and often signals where or what the image is meant for. Use it together with the Description as a hint for the image's intended placement and subject when deciding where each image goes.\n\n"; 5068 } 5069 } 5070 const domain = website == null ? void 0 : website.Domain; 5071 if (website == null ? void 0 : website.analyticsId) { 5072 contextBlock += ` 5073Website Analytics ID: ${website.analyticsId} 5074`; 5075 } 5076 if (domain) { 5077 contextBlock += "\nDomain Information:\n"; 5078 contextBlock += `- Domain ID: ${domain.id} 5079`; 5080 contextBlock += `- Domain Name: ${domain.domainName} 5081`; 5082 contextBlock += `- Verification Status: ${domain.verificationStatus} 5083`; 5084 if (!((_a = domain.configurationMethod) == null ? void 0 : _a.startsWith("EXTERNAL_DNS"))) { 5085 contextBlock += `- Nameserver Status: ${domain.nameserverStatus} 5086`; 5087 } 5088 if (domain.configurationMethod) { 5089 const methodLabels = { 5090 LANDINGSITE_NAMESERVERS: "Using Landingsite nameservers", 5091 EXTERNAL_DNS_ROOT: "External DNS (root domain)", 5092 EXTERNAL_DNS_SUBDOMAIN: "External DNS (subdomain)" 5093 }; 5094 contextBlock += `- Configuration Method: ${methodLabels[domain.configurationMethod] || domain.configurationMethod} 5095`; 5096 } 5097 if (domain.purchaseType) { 5098 contextBlock += `- Purchase Type: ${domain.purchaseType === "landingsite" ? "Purchased through Landingsite" : "External domain"} 5099`; 5100 } 5101 } else { 5102 contextBlock += "\nDomain: No custom domain connected\n"; 5103 } 5104 let selectionShipped = false;
5105 let selectionRejected = false; 5106 if (selectedElement) { 5107 const truncated = truncateOuterHtml(selectedElement.outerHTML); 5108 if (truncated.rejected) { 5109 selectionRejected = true; 5110 } else { 5111 contextBlock += buildPickerContextSection({ 5112 outerHTML: truncated.html, 5113 path: selectedElement.path, 5114 sourceLoc: selectedElement.sourceLoc 5115 }); 5116 selectionShipped = true; 5117 } 5118 } 5119 contextBlock += "</context for the AI>\n\n"; 5120 const fileParts = (validImages || []).filter((m) => { 5121 var _a2, _b, _c, _d; 5122 return !m.isGalleryImage && !m.error && m.cdnUrl && (((_b = (_a2 = m.imageFile) == null ? void 0 : _a2.type) == null ? void 0 : _b.startsWith("image/")) || ((_c = m.imageFile) == null ? void 0 : _c.type) === "application/pdf") && ((_d = m.imageFile) == null ? void 0 : _d.type) !== "image/svg+xml"; 5123 }).map((m) => ({ 5124 type: "file", 5125 mediaType: m.imageFile.type, 5126 url: m.cdnUrl 5127 })); 5128 const message = { 5129 role: "user", 5130 parts: [{ 5131 type: "text", 5132 text: contextBlock + messageText 5133 }, ...fileParts], 5134 id: generateId$1(), 5135 metadata: { 5136 userIdForAnalyticsOnly: currentUserId, 5137 isPaidWebsite: websitePlan !== "Free", 5138 websitePlan, 5139 publicId: (website == null ? void 0 : website.publicId) || void 0, 5140 // For container API calls 5141 tokenMinutesLeft, 5142 userUploadedMedia: (validImages == null ? void 0 : validImages.map((media) => media.cdnUrl || media.imagePreview)) || [], 5143 userUploadedMediaNames: (validImages == null ? void 0 : validImages.map((media) => { 5144 var _a2; 5145 return ((_a2 = media.imageFile) == null ? void 0 : _a2.name) || ""; 5146 })) || [], 5147 galleryMediaTypes: (validImages == null ? void 0 : validImages.map((media) => media.galleryMediaType || "image")) || [], 5148 // Raw (watermarked) preview URL, used to play the clip immediately 5149 // in the chat bubble â the live site still gets the clean version. 5150 userUploadedMediaPreviews: (validImages == null ? void 0 : validImages.map((media) => media.imagePreview || "")) || [], 5151 // Flag for the initial website creation message - can't restore to this 5152 ...isInitialCreation && { 5153 isInitialCreation: true 5154 } 5155 } 5156 }; 5157 return { 5158 message, 5159 selectionShipped, 5160 selectionRejected 5161 }; 5162} 5163function mergeAssistantRuns(messages, prevCache) { 5164 const nextCache = /* @__PURE__ */ new Map(); 5165 const merged = []; 5166 let i = 0; 5167 while (i < messages.length) { 5168 const msg = messages[i]; 5169 if (msg.role !== "assistant") { 5170 merged.push(msg); 5171 i++; 5172 continue; 5173 } 5174 let j = i + 1; 5175 while (j < messages.length && messages[j].role === "assistant") { 5176 j++; 5177 } 5178 const run = messages.slice(i, j); 5179 if (run.length === 1) { 5180 merged.push(msg); 5181 } else { 5182 const key = `${msg.id}:${i}`; 5183 const cached = prevCache.get(key); 5184 const unchanged = cached !== void 0 && cached.inputs.length === run.length && cached.inputs.every((m, k) => m === run[k]); 5185 if (unchanged) { 5186 nextCache.set(key, cached); 5187 merged.push(cached.output); 5188 } else { 5189 const output = { 5190 ...msg, 5191 parts: run.flatMap((m) => m.parts || []), 5192 metadata: run.reduce((md, m) => ({ 5193 ...md, 5194 ...m.metadata || {} 5195 }), {}) 5196 }; 5197 nextCache.set(key, { 5198 inputs: run, 5199 output 5200 }); 5201 merged.push(output); 5202 } 5203 } 5204 i = j; 5205 } 5206 return { 5207 merged, 5208 nextCache 5209 }; 5210} 5211function emptyQueue() { 5212 return { 5213 items: [], 5214 generation: 0 5215 }; 5216} 5217function snapshotImage(image) { 5218 const imagePreview = image.imagePreview.startsWith("blob:") && image.cdnUrl ? image.cdnUrl : image.imagePreview; 5219 return { 5220 index: image.index, 5221 imageFile: image.imageFile, 5222 imagePreview, 5223 s3Url: image.s3Url, 5224 cdnUrl: image.cdnUrl, 5225 r2Key: image.r2Key, 5226 mimeType: image.mimeType, 5227 assetId: image.assetId, 5228 isFlagged: image.isFlagged, 5229 isGalleryImage: image.isGalleryImage, 5230 galleryMediaId: image.galleryMediaId, 5231 galleryMediaSource: image.galleryMediaSource, 5232 galleryMediaType: image.galleryMediaType, 5233 posterUrl: image.posterUrl, 5234 description: image.description, 5235 uploading: false 5236 }; 5237} 5238function enqueue(state, input) { 5239 const item = { 5240 id: generateId(), 5241 text: input.text, 5242 images: input.images.map(snapshotImage), 5243 selectedElement: input.selectedElement, 5244 currentPath: input.currentPath, 5245 queuedAt: input.now ?? Date.now() 5246 }; 5247 return { 5248 ...state, 5249 items: [...state.items, item] 5250 }; 5251} 5252function updateText(state, id, text) { 5253 if (text.trim().length === 0) return state; 5254 const index = state.items.findIndex((item) => item.id === id); 5255 if (index === -1) return state; 5256 const items = state.items.slice(); 5257 items[index] = { 5258 ...items[index], 5259 text 5260 }; 5261 return { 5262 ...state, 5263 items 5264 }; 5265} 5266function removeItem(state, id) { 5267 const items = state.items.filter((item) => item.id !== id); 5268 if (items.length === state.items.length) return state; 5269 return { 5270 ...state, 5271 items 5272 }; 5273} 5274function shiftHead(state) { 5275 if (state.items.length === 0) return { 5276 head: null, 5277 state 5278 }; 5279 const [head, ...rest] = state.items; 5280 return { 5281 head, 5282 state: { 5283 ...state, 5284 items: rest 5285 } 5286 }; 5287} 5288function unshift(state, item) { 5289 return { 5290 ...state, 5291 items: [item, ...state.items] 5292 }; 5293} 5294function clearQueue(state) { 5295 return { 5296 items: [], 5297 generation: state.generation + 1 5298 }; 5299} 5300function shouldRequeueAfterConflict(capturedGeneration, currentGeneration) { 5301 return capturedGeneration === currentGeneration; 5302} 5303function mergeQueueIntoComposer(items, draft) { 5304 const parts = items.map((item) => item.text); 5305 if (draft.text.trim().length > 0) parts.push(draft.text); 5306 const images = items.flatMap((item) => item.images); 5307 const selections = items.map((item) => item.selectedElement).filter((sel) => sel !== null); 5308 const selectedElement = draft.hasSelection ? null : selections[0] ?? null; 5309 const droppedSelections = selections.length - (selectedElement ? 1 : 0); 5310 return { 5311 text: items.length === 0 ? draft.text : parts.join("\n\n"), 5312 images, 5313 selectedElement, 5314 droppedSelections 5315 }; 5316} 5317function mergeOut(state, draft) { 5318 return { 5319 merged: mergeQueueIntoComposer(state.items, draft), 5320 state: clearQueue(state) 5321 }; 5322} 5323function composerMode(flags) { 5324 const locked = flags.chatLimitReached || flags.restoring || !flags.containerReady || !flags.canEdit || flags.pendingApproval || flags.pendingQuestion || flags.inlineEditInProgress; 5325 if (locked) return "locked"; 5326 const busy = flags.status === "connecting" || flags.status === "streaming" || flags.submitting; 5327 return busy ? "busy" : "idle"; 5328} 5329const DRAIN_PAUSED_REASONS = ["approval", "question", "editing", "max-turns", "error", "chat-limit", "no-access", "restoring", "container"]; 5330function drainGate(inputs) { 5331 if (inputs.pendingApproval) return "approval"; 5332 if (inputs.pendingQuestion) return "question"; 5333 if (inputs.status === "connecting" || inputs.status === "streaming" || inputs.submitting || inputs.stopLatched) { 5334 return "wait"; 5335 } 5336 if (inputs.editing) return "editing"; 5337 if (inputs.maxTurnsHit) return "max-turns"; 5338 if (inputs.panelError) return "error"; 5339 if (inputs.chatLimitReached) return "chat-limit"; 5340 if (!inputs.canEdit) return "no-access"; 5341 if (inputs.restoring) return "restoring"; 5342 if (!inputs.containerReady) return "container"; 5343 return "drain"; 5344} 5345function drainTransition(previous, current) { 5346 return current === "drain" && previous !== "drain"; 5347} 5348const PAUSED_REASON_COPY = { 5349 approval: "Waiting for you to approve or deny the request above", 5350 question: "Waiting for you to answer the question above", 5351 editing: "Finish editing to send", 5352 "max-turns": "Paused â click Continue or Send now", 5353 error: "Paused until the error above is resolved", 5354 "chat-limit": "Paused â chat limit reached", 5355 restoring: "Paused while restoring", 5356 "no-access": "Paused â you no longer have edit access to this site", 5357 container: "Waiting for the preview server" 5358}; 5359function QueuedMessageListImpl({ 5360 items, 5361 mode, 5362 pausedReason, 5363 onUpdateText, 5364 onRemove, 5365 onSendNow, 5366 onEditingChange 5367}) { 5368 if (items.length === 0) return null; 5369 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative z-[1] flex flex-col gap-1.5 px-1.5 pb-1.5 pt-1.5", children: [
5370 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { role: "list", "aria-label": "Queued messages", className: "flex max-h-[40vh] flex-col gap-1.5 overflow-y-auto", children: items.map((item, index) => /* @__PURE__ */ jsxRuntimeExports.jsx(QueuedMessageRow, { item, position: index + 1, canSendNow: mode === "idle", onUpdateText, onRemove, onSendNow, onEditingChange }, item.id)) }), 5371 pausedReason && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "px-1 text-[11px] leading-4 text-muted-foreground", children: PAUSED_REASON_COPY[pausedReason] }) 5372 ] }); 5373} 5374function QueuedMessageRowImpl({ 5375 item, 5376 position, 5377 canSendNow, 5378 onUpdateText, 5379 onRemove, 5380 onSendNow, 5381 onEditingChange 5382}) { 5383 const [editing, setEditing] = reactExports.useState(false); 5384 const [draft, setDraft] = reactExports.useState(item.text); 5385 const editorRef = reactExports.useRef(null); 5386 const editingRef = reactExports.useRef(false); 5387 const onEditingChangeRef = reactExports.useRef(onEditingChange); 5388 onEditingChangeRef.current = onEditingChange; 5389 reactExports.useEffect(() => { 5390 if (!editing) return; 5391 const el = editorRef.current; 5392 if (!el) return; 5393 el.focus(); 5394 el.setSelectionRange(el.value.length, el.value.length); 5395 }, [editing]); 5396 reactExports.useEffect(() => () => { 5397 var _a; 5398 if (editingRef.current) { 5399 editingRef.current = false; 5400 (_a = onEditingChangeRef.current) == null ? void 0 : _a.call(onEditingChangeRef, null); 5401 } 5402 }, []); 5403 const startEdit = () => { 5404 setDraft(item.text); 5405 editingRef.current = true; 5406 setEditing(true); 5407 onEditingChange == null ? void 0 : onEditingChange(item.id); 5408 }; 5409 const cancelEdit = () => { 5410 setDraft(item.text); 5411 editingRef.current = false; 5412 setEditing(false); 5413 onEditingChange == null ? void 0 : onEditingChange(null); 5414 }; 5415 const saveEdit = () => { 5416 if (!editingRef.current) return; 5417 editingRef.current = false; 5418 const next = draft; 5419 setEditing(false); 5420 onEditingChange == null ? void 0 : onEditingChange(null); 5421 if (next.trim().length === 0 || next === item.text) { 5422 setDraft(item.text); 5423 return; 5424 } 5425 onUpdateText(item.id, next); 5426 }; 5427 const handleSendNow = () => { 5428 saveEdit(); 5429 onSendNow(item.id); 5430 }; 5431 const handleEditorKeyDown = (event) => { 5432 if (event.key === "Escape") { 5433 event.preventDefault(); 5434 cancelEdit(); 5435 return; 5436 } 5437 if (event.key === "Enter" && !event.shiftKey) { 5438 event.preventDefault(); 5439 saveEdit(); 5440 } 5441 }; 5442 const elementLabel = item.selectedElement ? formatFriendlyLabel(item.selectedElement.tag, item.selectedElement.textPreview) : null; 5443 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { role: "listitem", "aria-label": `Queued message ${position}`, className: cn$1("flex flex-col gap-1.5 rounded-[12px] border border-[#1B1A22]/[0.08] bg-white px-2.5 py-2", "shadow-[0_1px_2px_rgba(27,26,34,0.06)]"), children: [ 5444 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-start gap-2", children: [
5445 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "min-w-0 flex-1", children: editing ? /* @__PURE__ */ jsxRuntimeExports.jsx(Textarea, { ref: editorRef, "aria-label": "Edit queued message text", value: draft, rows: 2, onChange: (event) => setDraft(event.target.value), onKeyDown: handleEditorKeyDown, onBlur: saveEdit, className: "min-h-[40px] resize-none rounded-[8px] border-[#1B1A22]/[0.08] px-2 py-1.5 text-sm focus-visible:ring-1 focus-visible:ring-offset-0" }) : /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "line-clamp-3 whitespace-pre-wrap break-words text-sm text-foreground", children: item.text }) }), 5446 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex shrink-0 items-center gap-0.5", children: [ 5447 /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [ 5448 /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "inline-flex", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { type: "button", size: "icon", variant: "ghost", "aria-label": "Send now", disabled: !canSendNow, onClick: handleSendNow, className: "h-6 w-6 rounded-full p-0 text-muted-foreground hover:bg-background hover:text-foreground disabled:opacity-40", children: /* @__PURE__ */ jsxRuntimeExports.jsx(CornerDownLeft, { className: "h-3.5 w-3.5" }) }) }) }),
5449 /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { side: "top", align: "center", children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: canSendNow ? "Send now" : "Sends automatically when the AI finishes" }) }) 5450 ] }) }), 5451 /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { type: "button", size: "icon", variant: "ghost", "aria-label": "Edit queued message", "aria-pressed": editing, onClick: editing ? cancelEdit : startEdit, className: "h-6 w-6 rounded-full p-0 text-muted-foreground hover:bg-background hover:text-foreground", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Pencil, { className: "h-3.5 w-3.5" }) }), 5452 /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { type: "button", size: "icon", variant: "ghost", "aria-label": "Remove queued message", onClick: () => onRemove(item.id), className: "h-6 w-6 rounded-full p-0 text-muted-foreground hover:bg-background hover:text-foreground", children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "h-3.5 w-3.5" }) }) 5453 ] }) 5454 ] }), 5455 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-1.5 text-[11px] leading-4 text-muted-foreground", children: [ 5456 item.images.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-wrap items-center gap-1", children: item.images.map((image) => /* @__PURE__ */ jsxRuntimeExports.jsx(QueuedThumbnail, { image }, image.index)) }), 5457 elementLabel && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex max-w-[220px] items-center gap-1 rounded-[8px] border border-[#1B1A22]/[0.08] bg-white px-1.5 py-0.5", children: [
5458 /* @__PURE__ */ jsxRuntimeExports.jsx(MousePointerClick, { className: "h-3 w-3 shrink-0", "aria-hidden": true }), 5459 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: elementLabel }) 5460 ] }) 5461 ] }) 5462 ] }); 5463} 5464const THUMB_CLASS = "h-10 w-10 rounded-[8px] border border-[#1B1A22]/[0.08] object-cover"; 5465const TILE_CLASS = "flex h-10 w-10 items-center justify-center rounded-[8px] border border-[#1B1A22]/[0.08] bg-white"; 5466function QueuedThumbnail({ 5467 image 5468}) { 5469 var _a, _b; 5470 const fileName = ((_a = image.imageFile) == null ? void 0 : _a.name) ?? ""; 5471 const fileType = ((_b = image.imageFile) == null ? void 0 : _b.type) ?? ""; 5472 if (image.galleryMediaType === "video" || image.galleryMediaType === "lottie") { 5473 return /* @__PURE__ */ jsxRuntimeExports.jsx("video", { src: image.imagePreview, poster: image.posterUrl, className: THUMB_CLASS, autoPlay: true, loop: true, muted: true, playsInline: true, "aria-label": "Queued video attachment" }); 5474 } 5475 if (fileType === "application/pdf") { 5476 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: TILE_CLASS, title: fileName || "PDF", "aria-label": "Queued PDF attachment", children: /* @__PURE__ */ jsxRuntimeExports.jsx(FileText, { className: "h-4 w-4 text-muted-foreground" }) }); 5477 } 5478 if (fileName.toLowerCase().endsWith(".glb")) { 5479 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: TILE_CLASS, title: fileName || "3D model", "aria-label": "Queued 3D model attachment", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Box, { className: "h-4 w-4 text-muted-foreground" }) }); 5480 } 5481 if (fileType === "image/heic" || fileType === "image/heif") { 5482 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: TILE_CLASS, title: fileName || "HEIC", "aria-label": "Queued HEIC attachment", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Image, { className: "h-4 w-4 text-muted-foreground" }) }); 5483 } 5484 return /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: image.imagePreview, alt: "Queued attachment", className: THUMB_CLASS }); 5485} 5486const QueuedMessageRow = React.memo(QueuedMessageRowImpl); 5487const QueuedMessageList = React.memo(QueuedMessageListImpl); 5488const PAYMENTS_DATA_TOOLS = /* @__PURE__ */ new Set(["create_product", "update_product", "delete_product", "set_product_variants", "generate_variants", "update_variant", "set_variant_inventory", "set_product_subscription", "moderate_review", "reply_to_review", "update_review_settings"]); 5489const COMMERCE_INTENT_TOOLS = /* @__PURE__ */ new Set(["create_invoice", "create_retainer"]); 5490const BUSINESS_DATA_TOOLS = /* @__PURE__ */ new Set([ 5491 "create_product", 5492 "update_product", 5493 "set_product_variants", 5494 "set_variant_inventory", 5495 "set_shipping_countries", 5496 "set_shipping_rates", 5497 "mark_order_fulfilled", 5498 // Subscriptions (U9/U11): cancel mutates rows the SubscriptionsView has 5499 // fetched; list/get land here too so a turn that merely inspected 5500 // subscriptions still re-renders the freshest mirror state in the view. 5501 "list_subscriptions", 5502 "get_subscription", 5503 "cancel_subscription", 5504 // Invoicing (landingsite-modal/agent/src/tools/invoices.ts + retainers.ts): 5505 // every mutation changes rows an open Invoices view has fetched. The view 5506 // polls nothing of its own, so without these a "send Bob another invoice" 5507 // while the view is open leaves it stale until the merchant reopens it. 5508 "create_invoice", 5509 "void_invoice", 5510 "resend_invoice", 5511 "remind_invoice", 5512 "snooze_invoice", 5513 "set_invoice_reminders", 5514 "mark_invoice_paid", 5515 "mark_invoice_uncollectible", 5516 "update_invoice_recipient", 5517 "update_customer", 5518 "update_invoice_settings", 5519 "create_retainer", 5520 "update_retainer", 5521 "skip_next_retainer_cycle", 5522 "pause_retainer", 5523 "resume_retainer", 5524 "cancel_retainer" 5525]); 5526function turnEndRefreshTargets(toolNames) { 5527 const out = { 5528 preview: false, 5529 commerceIntent: false, 5530 business: false 5531 }; 5532 for (const name of toolNames) { 5533 if (PAYMENTS_DATA_TOOLS.has(name)) { 5534 out.preview = true; 5535 out.commerceIntent = true; 5536 } 5537 if (COMMERCE_INTENT_TOOLS.has(name)) out.commerceIntent = true; 5538 if (BUSINESS_DATA_TOOLS.has(name)) out.business = true; 5539 } 5540 return out; 5541} 5542function completedToolNamesInTurn(messages) { 5543 var _a; 5544 let start = 0; 5545 for (let i = messages.length - 1; i >= 0; i--) { 5546 if (messages[i].role === "user") { 5547 start = i + 1; 5548 break; 5549 } 5550 } 5551 const names = []; 5552 for (const m of messages.slice(start)) { 5553 if (m.role !== "assistant") continue; 5554 const parts = m.parts; 5555 for (const p of parts ?? []) { 5556 if (!((_a = p == null ? void 0 : p.type) == null ? void 0 : _a.startsWith("tool-"))) continue;
5557 if (p.state !== "output-available") continue; 5558 names.push(p.type.split("__").pop() ?? ""); 5559 } 5560 } 5561 return names; 5562} 5563const RESTORE_TO_CHAT_MESSAGE = { 5564 "kind": "Document", 5565 "definitions": [{ 5566 "kind": "OperationDefinition", 5567 "operation": "mutation", 5568 "name": { 5569 "kind": "Name", 5570 "value": "RestoreToChatMessageMutation" 5571 }, 5572 "variableDefinitions": [{ 5573 "kind": "VariableDefinition", 5574 "variable": { 5575 "kind": "Variable", 5576 "name": { 5577 "kind": "Name", 5578 "value": "websiteId" 5579 } 5580 }, 5581 "type": { 5582 "kind": "NonNullType", 5583 "type": { 5584 "kind": "NamedType", 5585 "name": { 5586 "kind": "Name", 5587 "value": "String" 5588 } 5589 } 5590 }, 5591 "directives": [] 5592 }, { 5593 "kind": "VariableDefinition", 5594 "variable": { 5595 "kind": "Variable", 5596 "name": { 5597 "kind": "Name", 5598 "value": "messageId" 5599 } 5600 }, 5601 "type": { 5602 "kind": "NonNullType", 5603 "type": { 5604 "kind": "NamedType", 5605 "name": { 5606 "kind": "Name", 5607 "value": "String" 5608 } 5609 } 5610 }, 5611 "directives": [] 5612 }, { 5613 "kind": "VariableDefinition", 5614 "variable": { 5615 "kind": "Variable", 5616 "name": { 5617 "kind": "Name", 5618 "value": "preMessageCommitHash" 5619 } 5620 }, 5621 "type": { 5622 "kind": "NonNullType", 5623 "type": { 5624 "kind": "NamedType", 5625 "name": { 5626 "kind": "Name", 5627 "value": "String" 5628 } 5629 } 5630 }, 5631 "directives": [] 5632 }], 5633 "directives": [], 5634 "selectionSet": { 5635 "kind": "SelectionSet", 5636 "selections": [{ 5637 "kind": "Field", 5638 "name": { 5639 "kind": "Name", 5640 "value": "restoreToChatMessage" 5641 }, 5642 "arguments": [{ 5643 "kind": "Argument", 5644 "name": { 5645 "kind": "Name", 5646 "value": "websiteId" 5647 }, 5648 "value": { 5649 "kind": "Variable", 5650 "name": { 5651 "kind": "Name", 5652 "value": "websiteId" 5653 } 5654 } 5655 }, { 5656 "kind": "Argument", 5657 "name": { 5658 "kind": "Name", 5659 "value": "messageId" 5660 }, 5661 "value": { 5662 "kind": "Variable", 5663 "name": { 5664 "kind": "Name", 5665 "value": "messageId" 5666 } 5667 } 5668 }, { 5669 "kind": "Argument", 5670 "name": { 5671 "kind": "Name", 5672 "value": "preMessageCommitHash" 5673 }, 5674 "value": { 5675 "kind": "Variable", 5676 "name": { 5677 "kind": "Name", 5678 "value": "preMessageCommitHash" 5679 } 5680 } 5681 }], 5682 "directives": [], 5683 "selectionSet": { 5684 "kind": "SelectionSet", 5685 "selections": [{ 5686 "kind": "Field", 5687 "name": { 5688 "kind": "Name", 5689 "value": "success" 5690 }, 5691 "arguments": [], 5692 "directives": [] 5693 }, { 5694 "kind": "Field", 5695 "name": { 5696 "kind": "Name", 5697 "value": "error" 5698 }, 5699 "arguments": [], 5700 "directives": [] 5701 }, { 5702 "kind": "Field", 5703 "name": { 5704 "kind": "Name", 5705 "value": "pending" 5706 }, 5707 "arguments": [], 5708 "directives": [] 5709 }, { 5710 "kind": "Field", 5711 "name": { 5712 "kind": "Name", 5713 "value": "admissionId" 5714 }, 5715 "arguments": [], 5716 "directives": [] 5717 }, { 5718 "kind": "Field", 5719 "name": { 5720 "kind": "Name", 5721 "value": "sourceSessionId" 5722 }, 5723 "arguments": [], 5724 "directives": [] 5725 }] 5726 } 5727 }] 5728 } 5729 }], 5730 "loc": { 5731 "start": 0, 5732 "end": 361, 5733 "source": { 5734 "body": "\n mutation RestoreToChatMessageMutation(\n $websiteId: String!\n $messageId: String!\n $preMessageCommitHash: String!\n ) {\n restoreToChatMessage(\n websiteId: $websiteId\n messageId: $messageId\n preMessageCommitHash: $preMessageCommitHash\n ) {\n success\n error\n pending\n admissionId\n sourceSessionId\n }\n }\n", 5735 "name": "GraphQL request", 5736 "locationOffset": { 5737 "line": 1, 5738 "column": 1 5739 } 5740 } 5741 } 5742}; 5743const CONTINUE_CHAT_RESTORE = { 5744 "kind": "Document", 5745 "definitions": [{ 5746 "kind": "OperationDefinition", 5747 "operation": "mutation", 5748 "name": { 5749 "kind": "Name", 5750 "value": "ContinueChatRestoreMutation" 5751 }, 5752 "variableDefinitions": [{ 5753 "kind": "VariableDefinition", 5754 "variable": { 5755 "kind": "Variable", 5756 "name": { 5757 "kind": "Name", 5758 "value": "websiteId" 5759 } 5760 }, 5761 "type": { 5762 "kind": "NonNullType", 5763 "type": { 5764 "kind": "NamedType", 5765 "name": { 5766 "kind": "Name", 5767 "value": "String" 5768 } 5769 } 5770 }, 5771 "directives": [] 5772 }, {
5773 "kind": "VariableDefinition", 5774 "variable": { 5775 "kind": "Variable", 5776 "name": { 5777 "kind": "Name", 5778 "value": "preMessageCommitHash" 5779 } 5780 }, 5781 "type": { 5782 "kind": "NonNullType", 5783 "type": { 5784 "kind": "NamedType", 5785 "name": { 5786 "kind": "Name", 5787 "value": "String" 5788 } 5789 } 5790 }, 5791 "directives": [] 5792 }, { 5793 "kind": "VariableDefinition", 5794 "variable": { 5795 "kind": "Variable", 5796 "name": { 5797 "kind": "Name", 5798 "value": "admissionId" 5799 } 5800 }, 5801 "type": { 5802 "kind": "NonNullType", 5803 "type": { 5804 "kind": "NamedType", 5805 "name": { 5806 "kind": "Name", 5807 "value": "String" 5808 } 5809 } 5810 }, 5811 "directives": [] 5812 }, { 5813 "kind": "VariableDefinition", 5814 "variable": { 5815 "kind": "Variable", 5816 "name": { 5817 "kind": "Name", 5818 "value": "sourceSessionId" 5819 } 5820 }, 5821 "type": { 5822 "kind": "NonNullType", 5823 "type": { 5824 "kind": "NamedType", 5825 "name": { 5826 "kind": "Name", 5827 "value": "String" 5828 } 5829 } 5830 }, 5831 "directives": [] 5832 }], 5833 "directives": [], 5834 "selectionSet": { 5835 "kind": "SelectionSet", 5836 "selections": [{ 5837 "kind": "Field", 5838 "name": { 5839 "kind": "Name", 5840 "value": "continueChatRestore" 5841 }, 5842 "arguments": [{ 5843 "kind": "Argument", 5844 "name": { 5845 "kind": "Name", 5846 "value": "websiteId" 5847 }, 5848 "value": { 5849 "kind": "Variable", 5850 "name": { 5851 "kind": "Name", 5852 "value": "websiteId" 5853 } 5854 } 5855 }, { 5856 "kind": "Argument", 5857 "name": { 5858 "kind": "Name", 5859 "value": "preMessageCommitHash" 5860 }, 5861 "value": { 5862 "kind": "Variable", 5863 "name": { 5864 "kind": "Name", 5865 "value": "preMessageCommitHash" 5866 } 5867 } 5868 }, { 5869 "kind": "Argument", 5870 "name": { 5871 "kind": "Name", 5872 "value": "admissionId" 5873 }, 5874 "value": { 5875 "kind": "Variable", 5876 "name": { 5877 "kind": "Name", 5878 "value": "admissionId" 5879 } 5880 } 5881 }, { 5882 "kind": "Argument", 5883 "name": { 5884 "kind": "Name", 5885 "value": "sourceSessionId" 5886 }, 5887 "value": { 5888 "kind": "Variable", 5889 "name": { 5890 "kind": "Name", 5891 "value": "sourceSessionId" 5892 } 5893 } 5894 }], 5895 "directives": [], 5896 "selectionSet": { 5897 "kind": "SelectionSet", 5898 "selections": [{ 5899 "kind": "Field", 5900 "name": { 5901 "kind": "Name", 5902 "value": "success" 5903 }, 5904 "arguments": [], 5905 "directives": [] 5906 }, { 5907 "kind": "Field", 5908 "name": { 5909 "kind": "Name", 5910 "value": "error" 5911 }, 5912 "arguments": [], 5913 "directives": [] 5914 }, { 5915 "kind": "Field", 5916 "name": { 5917 "kind": "Name", 5918 "value": "pending" 5919 }, 5920 "arguments": [], 5921 "directives": [] 5922 }, { 5923 "kind": "Field", 5924 "name": { 5925 "kind": "Name", 5926 "value": "admissionId" 5927 }, 5928 "arguments": [], 5929 "directives": [] 5930 }, { 5931 "kind": "Field", 5932 "name": { 5933 "kind": "Name", 5934 "value": "sourceSessionId" 5935 }, 5936 "arguments": [], 5937 "directives": [] 5938 }] 5939 } 5940 }] 5941 } 5942 }], 5943 "loc": { 5944 "start": 0, 5945 "end": 435, 5946 "source": { 5947 "body": "\n mutation ContinueChatRestoreMutation(\n $websiteId: String!\n $preMessageCommitHash: String!\n $admissionId: String!\n $sourceSessionId: String!\n ) {\n continueChatRestore(\n websiteId: $websiteId\n preMessageCommitHash: $preMessageCommitHash\n admissionId: $admissionId\n sourceSessionId: $sourceSessionId\n ) {\n success\n error\n pending\n admissionId\n sourceSessionId\n }\n }\n", 5948 "name": "GraphQL request", 5949 "locationOffset": { 5950 "line": 1, 5951 "column": 1 5952 } 5953 } 5954 } 5955}; 5956const WEBSITE_COMMERCE_INTENT = { 5957 "kind": "Document", 5958 "definitions": [{ 5959 "kind": "OperationDefinition", 5960 "operation": "query", 5961 "name": { 5962 "kind": "Name", 5963 "value": "ReactChatPanelWebsiteCommerceIntent" 5964 }, 5965 "variableDefinitions": [{ 5966 "kind": "VariableDefinition", 5967 "variable": { 5968 "kind": "Variable", 5969 "name": { 5970 "kind": "Name", 5971 "value": "websiteId" 5972 } 5973 }, 5974 "type": { 5975 "kind": "NonNullType", 5976 "type": { 5977 "kind": "NamedType", 5978 "name": { 5979 "kind": "Name", 5980 "value": "String" 5981 } 5982 } 5983 }, 5984 "directives": [] 5985 }], 5986 "directives": [], 5987 "selectionSet": { 5988 "kind": "SelectionSet", 5989 "selections": [{ 5990 "kind": "Field", 5991 "name": { 5992 "kind": "Name", 5993 "value": "websiteCommerceIntent" 5994 }, 5995 "arguments": [{ 5996 "kind": "Argument", 5997 "name": { 5998 "kind": "Name", 5999 "value": "websiteId" 6000 }, 6001 "value": { 6002 "kind": "Variable", 6003 "name": { 6004 "kind": "Name", 6005 "value": "websiteId" 6006 } 6007 } 6008 }], 6009 "directives": [], 6010 "selectionSet": { 6011 "kind": "SelectionSet", 6012 "selections": [{ 6013 "kind": "Field", 6014 "name": { 6015 "kind": "Name", 6016 "value": "needsPaymentsSetup" 6017 }, 6018 "arguments": [], 6019 "directives": [] 6020 }, { 6021 "kind": "Field", 6022 "name": { 6023 "kind": "Name", 6024 "value": "paymentsActive" 6025 }, 6026 "arguments": [], 6027 "directives": [] 6028 }, { 6029 "kind": "Field", 6030 "name": { 6031 "kind": "Name", 6032 "value": "commerceSetup" 6033 }, 6034 "arguments": [], 6035 "directives": [], 6036 "selectionSet": { 6037 "kind": "SelectionSet", 6038 "selections": [{ 6039 "kind": "Field", 6040 "name": { 6041 "kind": "Name", 6042 "value": "websiteReview" 6043 }, 6044 "arguments": [], 6045 "directives": [], 6046 "selectionSet": { 6047 "kind": "SelectionSet", 6048 "selections": [{ 6049 "kind": "Field", 6050 "name": { 6051 "kind": "Name", 6052 "value": "url" 6053 }, 6054 "arguments": [], 6055 "directives": [] 6056 }, { 6057 "kind": "Field", 6058 "name": { 6059 "kind": "Name", 6060 "value": "state" 6061 }, 6062 "arguments": [], 6063 "directives": [] 6064 }, { 6065 "kind": "Field", 6066 "name": { 6067 "kind": "Name", 6068 "value": "issues" 6069 }, 6070 "arguments": [], 6071 "directives": [] 6072 }] 6073 } 6074 }, { 6075 "kind": "Field", 6076 "name": { 6077 "kind": "Name", 6078 "value": "paymentState" 6079 }, 6080 "arguments": [], 6081 "directives": [] 6082 }, { 6083 "kind": "Field", 6084 "name": { 6085 "kind": "Name", 6086 "value": "payoutState" 6087 }, 6088 "arguments": [], 6089 "directives": [] 6090 }, { 6091 "kind": "Field", 6092 "name": { 6093 "kind": "Name", 6094 "value": "detailsSubmitted" 6095 }, 6096 "arguments": [], 6097 "directives": [] 6098 }, { 6099 "kind": "Field", 6100 "name": { 6101 "kind": "Name", 6102 "value": "paymentRequirements" 6103 }, 6104 "arguments": [], 6105 "directives": [] 6106 }, { 6107 "kind": "Field", 6108 "name": { 6109 "kind": "Name", 6110 "value": "payoutRequirements" 6111 }, 6112 "arguments": [], 6113 "directives": [] 6114 }, { 6115 "kind": "Field", 6116 "name": { 6117 "kind": "Name", 6118 "value": "requirementsDeadline" 6119 }, 6120 "arguments": [], 6121 "directives": [] 6122 }, { 6123 "kind": "Field", 6124 "name": { 6125 "kind": "Name", 6126 "value": "checkedAt" 6127 }, 6128 "arguments": [], 6129 "directives": [] 6130 }] 6131 } 6132 }, { 6133 "kind": "Field", 6134 "name": { 6135 "kind": "Name", 6136 "value": "needsUpgrade" 6137 }, 6138 "arguments": [], 6139 "directives": [] 6140 }, { 6141 "kind": "Field", 6142 "name": { 6143 "kind": "Name", 6144 "value": "paymentsDisabled" 6145 }, 6146 "arguments": [], 6147 "directives": [] 6148 }, { 6149 "kind": "Field", 6150 "name": { 6151 "kind": "Name", 6152 "value": "hasProducts" 6153 }, 6154 "arguments": [], 6155 "directives": [] 6156 }, { 6157 "kind": "Field", 6158 "name": { 6159 "kind": "Name", 6160 "value": "invoiceIntent" 6161 }, 6162 "arguments": [], 6163 "directives": [] 6164 }, { 6165 "kind": "Field", 6166 "name": { 6167 "kind": "Name", 6168 "value": "paymentsPending" 6169 }, 6170 "arguments": [], 6171 "directives": [] 6172 }, { 6173 "kind": "Field", 6174 "name": { 6175 "kind": "Name", 6176 "value": "postageUncharged" 6177 }, 6178 "arguments": [], 6179 "directives": [] 6180 }, { 6181 "kind": "Field", 6182 "name": { 6183 "kind": "Name", 6184 "value": "currencyRestatedFrom" 6185 }, 6186 "arguments": [], 6187 "directives": [] 6188 }, { 6189 "kind": "Field", 6190 "name": { 6191 "kind": "Name", 6192 "value": "currencyRestatedTo" 6193 }, 6194 "arguments": [], 6195 "directives": [] 6196 }, { 6197 "kind": "Field", 6198 "name": { 6199 "kind": "Name", 6200 "value": "canStartOnboarding" 6201 }, 6202 "arguments": [], 6203 "directives": [] 6204 }] 6205 } 6206 }] 6207 } 6208 }], 6209 "loc": { 6210 "start": 0, 6211 "end": 656, 6212 "source": { 6213 "body": "\n query ReactChatPanelWebsiteCommerceIntent($websiteId: String!) {\n websiteCommerceIntent(websiteId: $websiteId) {\n needsPaymentsSetup\n paymentsActive\n commerceSetup {\n websiteReview {\n url\n state\n issues\n }\n paymentState\n payoutState\n detailsSubmitted\n paymentRequirements\n payoutRequirements\n requirementsDeadline\n checkedAt\n }\n needsUpgrade\n paymentsDisabled\n hasProducts\n invoiceIntent\n paymentsPending\n postageUncharged\n currencyRestatedFrom\n currencyRestatedTo\n canStartOnboarding\n }\n }\n", 6214 "name": "GraphQL request", 6215 "locationOffset": { 6216 "line": 1, 6217 "column": 1 6218 } 6219 } 6220 } 6221}; 6222const SYNC_STRIPE_CONNECT_STATUS_FROM_CHAT = { 6223 "kind": "Document", 6224 "definitions": [{ 6225 "kind": "OperationDefinition", 6226 "operation": "mutation", 6227 "name": { 6228 "kind": "Name", 6229 "value": "SyncStripeConnectStatusFromChat" 6230 }, 6231 "variableDefinitions": [{ 6232 "kind": "VariableDefinition", 6233 "variable": { 6234 "kind": "Variable", 6235 "name": { 6236 "kind": "Name", 6237 "value": "polling" 6238 } 6239 }, 6240 "type": { 6241 "kind": "NamedType", 6242 "name": { 6243 "kind": "Name", 6244 "value": "Boolean" 6245 } 6246 }, 6247 "directives": [] 6248 }], 6249 "directives": [], 6250 "selectionSet": { 6251 "kind": "SelectionSet", 6252 "selections": [{ 6253 "kind": "Field", 6254 "name": { 6255 "kind": "Name", 6256 "value": "syncStripeConnectStatus" 6257 }, 6258 "arguments": [{ 6259 "kind": "Argument", 6260 "name": { 6261 "kind": "Name", 6262 "value": "polling" 6263 }, 6264 "value": { 6265 "kind": "Variable", 6266 "name": { 6267 "kind": "Name", 6268 "value": "polling" 6269 } 6270 } 6271 }], 6272 "directives": [], 6273 "selectionSet": { 6274 "kind": "SelectionSet", 6275 "selections": [{ 6276 "kind": "Field", 6277 "name": { 6278 "kind": "Name", 6279 "value": "hasAccount" 6280 }, 6281 "arguments": [], 6282 "directives": [] 6283 }, { 6284 "kind": "Field", 6285 "name": { 6286 "kind": "Name", 6287 "value": "chargesEnabled" 6288 }, 6289 "arguments": [], 6290 "directives": [] 6291 }, { 6292 "kind": "Field", 6293 "name": { 6294 "kind": "Name", 6295 "value": "detailsSubmitted" 6296 }, 6297 "arguments": [], 6298 "directives": [] 6299 }] 6300 } 6301 }] 6302 } 6303 }], 6304 "loc": { 6305 "start": 0, 6306 "end": 185, 6307 "source": { 6308 "body": "\n mutation SyncStripeConnectStatusFromChat($polling: Boolean) {\n syncStripeConnectStatus(polling: $polling) {\n hasAccount\n chargesEnabled\n detailsSubmitted\n }\n }\n", 6309 "name": "GraphQL request", 6310 "locationOffset": { 6311 "line": 1, 6312 "column": 1 6313 } 6314 } 6315 } 6316}; 6317const ENABLE_MERCHANT_PAYMENTS_FROM_CHAT = { 6318 "kind": "Document", 6319 "definitions": [{ 6320 "kind": "OperationDefinition", 6321 "operation": "mutation", 6322 "name": { 6323 "kind": "Name", 6324 "value": "EnableMerchantPaymentsFromChat" 6325 }, 6326 "variableDefinitions": [{ 6327 "kind": "VariableDefinition", 6328 "variable": { 6329 "kind": "Variable", 6330 "name": { 6331 "kind": "Name", 6332 "value": "returnTo" 6333 } 6334 }, 6335 "type": { 6336 "kind": "NamedType", 6337 "name": { 6338 "kind": "Name", 6339 "value": "String" 6340 } 6341 }, 6342 "directives": [] 6343 }, {
6344 "kind": "VariableDefinition", 6345 "variable": { 6346 "kind": "Variable", 6347 "name": { 6348 "kind": "Name", 6349 "value": "websiteId" 6350 } 6351 }, 6352 "type": { 6353 "kind": "NamedType", 6354 "name": { 6355 "kind": "Name", 6356 "value": "String" 6357 } 6358 }, 6359 "directives": [] 6360 }], 6361 "directives": [], 6362 "selectionSet": { 6363 "kind": "SelectionSet", 6364 "selections": [{ 6365 "kind": "Field", 6366 "name": { 6367 "kind": "Name", 6368 "value": "enableMerchantPayments" 6369 }, 6370 "arguments": [{ 6371 "kind": "Argument", 6372 "name": { 6373 "kind": "Name", 6374 "value": "returnTo" 6375 }, 6376 "value": { 6377 "kind": "Variable", 6378 "name": { 6379 "kind": "Name", 6380 "value": "returnTo" 6381 } 6382 } 6383 }, { 6384 "kind": "Argument", 6385 "name": { 6386 "kind": "Name", 6387 "value": "websiteId" 6388 }, 6389 "value": { 6390 "kind": "Variable", 6391 "name": { 6392 "kind": "Name", 6393 "value": "websiteId" 6394 } 6395 } 6396 }], 6397 "directives": [], 6398 "selectionSet": { 6399 "kind": "SelectionSet", 6400 "selections": [{ 6401 "kind": "Field", 6402 "name": { 6403 "kind": "Name", 6404 "value": "url" 6405 }, 6406 "arguments": [], 6407 "directives": [] 6408 }] 6409 } 6410 }] 6411 } 6412 }], 6413 "loc": { 6414 "start": 0, 6415 "end": 188, 6416 "source": { 6417 "body": "\n mutation EnableMerchantPaymentsFromChat(\n $returnTo: String\n $websiteId: String\n ) {\n enableMerchantPayments(returnTo: $returnTo, websiteId: $websiteId) {\n url\n }\n }\n", 6418 "name": "GraphQL request", 6419 "locationOffset": { 6420 "line": 1, 6421 "column": 1 6422 } 6423 } 6424 } 6425}; 6426const FILE_TOOL_VERBS = { 6427 Edit: "Editing", 6428 MultiEdit: "Editing", 6429 Write: "Editing", 6430 Read: "Reading" 6431}; 6432const ReactChatPanel = reactExports.forwardRef(({ 6433 websiteId, 6434 isContainerReady = true, 6435 website, 6436 expanded = true, 6437 onToggleChatPanel, 6438 onRefetchWebsite, 6439 myWebsiteAccess, 6440 isHiddenForLoading, 6441 onAIRespondingChange, 6442 localAgencyCreditsDelta = 0, 6443 onAgencyCreditUsed, 6444 localAiChatMessagesDelta = 0, 6445 onAiChatMessageUsed, 6446 onCommitHashChange, 6447 onAgentBusyChange, 6448 onPaymentsDataChanged, 6449 onBusinessDataChanged, 6450 onShowView, 6451 inlineEditInProgress = false, 6452 pickModeActive = false, 6453 selectedElement = null, 6454 currentPath = "/", 6455 onTogglePickMode, 6456 onClearSelection, 6457 onRestoreSelection, 6458 isPickerAvailable = false, 6459 isSelectorReady = false, 6460 viteError = null 6461}, ref) => { 6462 var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p; 6463 const [loading, setLoading] = reactExports.useState(false); 6464 const [messagesLoaded, setMessagesLoaded] = reactExports.useState(false); 6465 const shouldReloadMessagesRef = reactExports.useRef(false); 6466 const { 6467 getToken, 6468 isAuthenticated, 6469 currentUser, 6470 hasRole 6471 } = useAuth(); 6472 const isAdmin = hasRole(["support"]); 6473 const posthog = usePostHog(); 6474 const [isExpanded, setIsExpanded] = reactExports.useState(expanded); 6475 const [userInitiatedAnyTurn, setUserInitiatedAnyTurn] = reactExports.useState(false); 6476 const [isSubmitting, setIsSubmitting] = reactExports.useState(false); 6477 const [dispatchInFlight, setDispatchInFlight] = reactExports.useState(false); 6478 const [queue, setQueue] = reactExports.useState(emptyQueue); 6479 const queueRef = reactExports.useRef(queue); 6480 reactExports.useEffect(() => { 6481 queueRef.current = queue; 6482 }, [queue]); 6483 const [editingQueuedId, setEditingQueuedId] = reactExports.useState(null); 6484 const discardQueue = reactExports.useCallback(() => { 6485 const hadItems = queueRef.current.items.length > 0; 6486 queueRef.current = clearQueue(queueRef.current); 6487 setQueue(clearQueue); 6488 setEditingQueuedId(null); 6489 if (hadItems) _t("Queued messages discarded"); 6490 }, []); 6491 const [stopLatched, setStopLatched] = reactExports.useState(false); 6492 const submitSafetyTimerRef = reactExports.useRef(null); 6493 const releaseSubmitFlag = reactExports.useCallback(() => { 6494 setIsSubmitting(false); 6495 if (submitSafetyTimerRef.current) { 6496 clearTimeout(submitSafetyTimerRef.current); 6497 submitSafetyTimerRef.current = null; 6498 } 6499 }, []); 6500 const [maxStepsReached, setMaxStepsReached] = reactExports.useState(false); 6501 const [chatSessionInProgress, setChatSessionInProgress] = reactExports.useState(false); 6502 const [showResetSuggestion, setShowResetSuggestion] = reactExports.useState(false); 6503 const [sessionLostNotice, setSessionLostNotice] = reactExports.useState(null); 6504 const [chatErrorMessage, setChatErrorMessage] = reactExports.useState(""); 6505 const [isOverloadedError, setIsOverloadedError] = reactExports.useState(false); 6506 const [isOutOfCredits, setIsOutOfCredits] = reactExports.useState(false); 6507 const [isSessionExpired, setIsSessionExpired] = reactExports.useState(false); 6508 const lastUserMessageRef = reactExports.useRef(""); 6509 const lastDispatchedSnapshotRef = reactExports.useRef(null); 6510 const hasSetSpecificErrorRef = reactExports.useRef(false); 6511 const [isSummarizing, setIsSummarizing] = reactExports.useState(false); 6512 const [inputValue, setInputValue] = reactExports.useState(""); 6513 const [isRestoring, setIsRestoring] = reactExports.useState(false); 6514 const pendingChatRestoreRef = reactExports.useRef(null);
6515 const [chatStopped, setChatStopped] = reactExports.useState(false); 6516 const [isDragActive, setIsDragActive] = reactExports.useState(false); 6517 const [restoreMutation] = useMutation(RESTORE_TO_CHAT_MESSAGE); 6518 const [continueRestoreMutation] = useMutation(CONTINUE_CHAT_RESTORE); 6519 const { 6520 data: commerceIntentData, 6521 refetch: refetchCommerceIntent 6522 } = useQuery(WEBSITE_COMMERCE_INTENT, { 6523 variables: { 6524 websiteId 6525 }, 6526 skip: !websiteId, 6527 pollInterval: 6e4, 6528 fetchPolicy: "cache-and-network" 6529 }); 6530 const needsPaymentsSetup = Boolean((_a = commerceIntentData == null ? void 0 : commerceIntentData.websiteCommerceIntent) == null ? void 0 : _a.needsPaymentsSetup); 6531 const needsPaymentsUpgrade = Boolean((_b = commerceIntentData == null ? void 0 : commerceIntentData.websiteCommerceIntent) == null ? void 0 : _b.needsUpgrade); 6532 const canStartPaymentsOnboarding = Boolean((_c = commerceIntentData == null ? void 0 : commerceIntentData.websiteCommerceIntent) == null ? void 0 : _c.canStartOnboarding); 6533 const paymentsForInvoicing = Boolean((_d = commerceIntentData == null ? void 0 : commerceIntentData.websiteCommerceIntent) == null ? void 0 : _d.invoiceIntent) && !((_e = commerceIntentData == null ? void 0 : commerceIntentData.websiteCommerceIntent) == null ? void 0 : _e.hasProducts); 6534 const paymentsPending = Boolean((_f = commerceIntentData == null ? void 0 : commerceIntentData.websiteCommerceIntent) == null ? void 0 : _f.paymentsPending); 6535 const paymentsDisabled = Boolean((_g = commerceIntentData == null ? void 0 : commerceIntentData.websiteCommerceIntent) == null ? void 0 : _g.paymentsDisabled); 6536 const postageUnchargedFromServer = Boolean((_h = commerceIntentData == null ? void 0 : commerceIntentData.websiteCommerceIntent) == null ? void 0 : _h.postageUncharged); 6537 const currencyRestatedTo = ((_i = commerceIntentData == null ? void 0 : commerceIntentData.websiteCommerceIntent) == null ? void 0 : _i.currencyRestatedTo) ?? null; 6538 const currencyRestatedFrom = ((_j = commerceIntentData == null ? void 0 : commerceIntentData.websiteCommerceIntent) == null ? void 0 : _j.currencyRestatedFrom) ?? null; 6539 const [enablePaymentsFromChat] = useMutation(ENABLE_MERCHANT_PAYMENTS_FROM_CHAT); 6540 const [syncStripeConnectFromChat] = useMutation(SYNC_STRIPE_CONNECT_STATUS_FROM_CHAT); 6541 const [isStartingPaymentsSetup, setIsStartingPaymentsSetup] = reactExports.useState(false); 6542 const startingPaymentsRef = reactExports.useRef(false); 6543 const commerceSetup = (_k = commerceIntentData == null ? void 0 : commerceIntentData.websiteCommerceIntent) == null ? void 0 : _k.commerceSetup; 6544 const hasCommerceIntent = Boolean(((_l = commerceIntentData == null ? void 0 : commerceIntentData.websiteCommerceIntent) == null ? void 0 : _l.hasProducts) || ((_m = commerceIntentData == null ? void 0 : commerceIntentData.websiteCommerceIntent) == null ? void 0 : _m.invoiceIntent)); 6545 const setupRefresh = useCommerceSetupRefresh({ 6546 scopeKey: websiteId, 6547 enabled: canStartPaymentsOnboarding && hasCommerceIntent && (paymentsPending || paymentsDisabled || !!commerceSetup || !!((_n = commerceIntentData == null ? void 0 : commerceIntentData.websiteCommerceIntent) == null ? void 0 : _n.paymentsActive)), 6548 needsPolling: commerceSetupNeedsPolling(commerceSetup), 6549 load: async ({ 6550 polling 6551 }) => { 6552 await syncStripeConnectFromChat({ 6553 variables: { 6554 polling 6555 } 6556 }); 6557 await refetchCommerceIntent(); 6558 } 6559 }); 6560 const visibleCommerceSetup = hasCommerceIntent && canStartPaymentsOnboarding ? commerceSetup : null; 6561 reactExports.useEffect(() => { 6562 const onPageShow = (e) => { 6563 if (e.persisted) { 6564 startingPaymentsRef.current = false; 6565 setIsStartingPaymentsSetup(false); 6566 } 6567 }; 6568 window.addEventListener("pageshow", onPageShow); 6569 return () => window.removeEventListener("pageshow", onPageShow); 6570 }, []); 6571 const handleSetUpPayments = reactExports.useCallback(async () => { 6572 var _a2; 6573 if (startingPaymentsRef.current) return; 6574 startingPaymentsRef.current = true; 6575 setIsStartingPaymentsSetup(true); 6576 try { 6577 const { 6578 data 6579 } = await enablePaymentsFromChat({ 6580 variables: { 6581 websiteId, 6582 returnTo: window.location.href 6583 } 6584 }); 6585 const url = (_a2 = data == null ? void 0 : data.enableMerchantPayments) == null ? void 0 : _a2.url; 6586 if (url) { 6587 window.location.href = url; 6588 return; 6589 } 6590 _t.error("Could not start payment setup. Please try again."); 6591 startingPaymentsRef.current = false;
6592 setIsStartingPaymentsSetup(false); 6593 } catch (err) { 6594 _t.error(err instanceof Error ? err.message : "Could not start payment setup. Please try again."); 6595 startingPaymentsRef.current = false; 6596 setIsStartingPaymentsSetup(false); 6597 } 6598 }, [enablePaymentsFromChat, websiteId]); 6599 const messagesEndRef = reactExports.useRef(null); 6600 const messagesContainerRef = reactExports.useRef(null); 6601 const textareaRef = reactExports.useRef(null); 6602 const fileInputRef = reactExports.useRef(null); 6603 const [isGalleryOpen, setIsGalleryOpen] = reactExports.useState(false); 6604 const { 6605 images, 6606 isUploading: isUploadingImages, 6607 handleFileChange, 6608 handleGalleryMediaSelection, 6609 handleRemoveImage, 6610 handleClearImages, 6611 handleRestoreImages 6612 } = useUploadImage(websiteId, (rejectedFiles, message) => { 6613 if (message) { 6614 _t.error(message); 6615 } else { 6616 const fileTypes = rejectedFiles.map((f) => f.type).join(", "); 6617 _t.error(`${rejectedFiles.length} file(s) rejected. Only images and PDFs are allowed. Rejected types: ${fileTypes}`); 6618 } 6619 }, (error, _fileName) => { 6620 _t.error(error.message || "Failed to publish image"); 6621 }, { 6622 processGalleryVideos: true 6623 }); 6624 const handleInternalGalleryMediaSelection = (media) => { 6625 setIsGalleryOpen(false); 6626 const mediaData = JSON.stringify({ 6627 id: media.id, 6628 src: media.src, 6629 type: media.type 6630 }); 6631 handleGalleryMediaSelection({ 6632 ...media, 6633 dataMedia: mediaData 6634 }); 6635 }; 6636 const adjustTextareaHeight = reactExports.useCallback(() => { 6637 if (textareaRef.current) { 6638 textareaRef.current.style.height = "44px"; 6639 const scrollHeight = textareaRef.current.scrollHeight; 6640 const maxHeight = 120; 6641 const newHeight = Math.min(scrollHeight, maxHeight); 6642 textareaRef.current.style.height = `${newHeight}px`; 6643 } 6644 }, []); 6645 reactExports.useEffect(() => { 6646 adjustTextareaHeight(); 6647 }, [inputValue, adjustTextareaHeight]); 6648 const [pickerLiveMessage, setPickerLiveMessage] = reactExports.useState(""); 6649 const prevPickModeActiveRef = reactExports.useRef(pickModeActive); 6650 reactExports.useEffect(() => { 6651 const prev = prevPickModeActiveRef.current; 6652 if (!prev && pickModeActive) { 6653 setPickerLiveMessage("Element picker active"); 6654 } else if (prev && !pickModeActive) { 6655 setPickerLiveMessage("Element picker cancelled"); 6656 } 6657 prevPickModeActiveRef.current = pickModeActive; 6658 }, [pickModeActive]); 6659 const prevSelectedElementRef = reactExports.useRef(selectedElement); 6660 reactExports.useEffect(() => { 6661 const prev = prevSelectedElementRef.current; 6662 if (!prev && selectedElement) { 6663 posthog == null ? void 0 : posthog.capture("picker_element_selected", { 6664 tag: selectedElement.tag 6665 }); 6666 } 6667 prevSelectedElementRef.current = selectedElement; 6668 }, [selectedElement, posthog]); 6669 const pickerButtonHidden = !isPickerAvailable; 6670 const pickerButtonDisabled = !isSelectorReady; 6671 const pickerButtonTooltip = pickerButtonDisabled ? "Preview loading â try again in a moment" : "Pick an element"; 6672 const handlePickerButtonClick = reactExports.useCallback(() => { 6673 if (pickerButtonDisabled || !onTogglePickMode) return; 6674 const next = !pickModeActive; 6675 posthog == null ? void 0 : posthog.capture("picker_button_clicked", { 6676 pick_mode_active: next 6677 }); 6678 onTogglePickMode(); 6679 }, [pickerButtonDisabled, onTogglePickMode, pickModeActive, posthog]); 6680 const handleChipClear = reactExports.useCallback(() => { 6681 onClearSelection == null ? void 0 : onClearSelection(); 6682 requestAnimationFrame(() => { 6683 var _a2; 6684 (_a2 = textareaRef.current) == null ? void 0 : _a2.focus(); 6685 }); 6686 }, [onClearSelection]); 6687 const autoScrollActiveRef = reactExports.useRef(false); 6688 const userInteractedRef = reactExports.useRef(false); 6689 const rafIdRef = reactExports.useRef(null); 6690 const isPinnedToBottomRef = reactExports.useRef(true); 6691 const [showScrollToBottom, setShowScrollToBottom] = reactExports.useState(false); 6692 const FREE_PLAN_CHAT_LIMIT = 10; 6693 const websitePlan = (website == null ? void 0 : website.stripePlan) || "Free"; 6694 const aiChatMessagesUsed = ((website == null ? void 0 : website.aiChatMessagesUsed) || 0) + localAiChatMessagesDelta; 6695 const monthlyUsed = (website == null ? void 0 : website.chatMonthlyUsed) || 0; 6696 const monthlyAllotment = (website == null ? void 0 : website.chatMonthlyAllotment) || 0; 6697 const purchasedCredits = (website == null ? void 0 : website.chatPurchasedCredits) || 0; 6698 const isAgencyUser = ((_o = website == null ? void 0 : website.user) == null ? void 0 : _o.isAgencyPlan) === true; 6699 const agencyTotalCredits = (((_p = website == null ? void 0 : website.user) == null ? void 0 : _p.agencyTotalCredits) || 0) + localAgencyCreditsDelta; 6700 const isFree = websitePlan === "Free"; 6701 const websiteCreditsRemaining = Math.max(0, monthlyAllotment + purchasedCredits - monthlyUsed); 6702 let remainingChats; 6703 if (isAgencyUser) { 6704 if (!isFree && websiteCreditsRemaining > 0) { 6705 remainingChats = websiteCreditsRemaining; 6706 } else { 6707 remainingChats = agencyTotalCredits; 6708 } 6709 } else { 6710 remainingChats = isFree ? Math.max(0, FREE_PLAN_CHAT_LIMIT - aiChatMessagesUsed) + purchasedCredits : Math.max(0, monthlyAllotment + purchasedCredits - monthlyUsed); 6711 } 6712 const isChatLimitReached = isFree && !isAgencyUser && !isAdmin && aiChatMessagesUsed >= FREE_PLAN_CHAT_LIMIT && purchasedCredits <= 0; 6713 const sseChat = useAgentSSE({ 6714 publicId: (website == null ? void 0 : website.publicId) || "", 6715 websiteId, 6716 getToken: async () => { 6717 const token = await getToken(); 6718 return token || null; 6719 }, 6720 onError: (error) => { 6721 console.error("[SSE Agent] Error:", error.message); 6722 setChatErrorMessage(error.message); 6723 const type = error.type; 6724 setIsOverloadedError(!!type && RETRYABLE_AGENT_ERROR_TYPES.has(type)); 6725 if (error.message.toLowerCase().includes("credits") || error.message.toLowerCase().includes("out of")) { 6726 setIsOutOfCredits(true); 6727 } 6728 }, 6729 onSessionWarning: ({ 6730 reason, 6731 message 6732 }) => { 6733 if (reason === "session_lost" || reason === "session_upload") { 6734 setSessionLostNotice(message || "Previous chat history couldn't be restored. The assistant won't remember earlier turns in this conversation."); 6735 } else { 6736 setShowResetSuggestion(true); 6737 } 6738 }, 6739 onSessionExpired: (message) => { 6740 setIsSessionExpired(true); 6741 const fallback = lastUserMessageRef.current ? "Your session needs to refresh â send a new message or click Try Again to continue." : "Your session needs to refresh â send a new message to continue."; 6742 setChatErrorMessage(message || fallback); 6743 }, 6744 onChatReset: () => { 6745 setShowResetSuggestion(false); 6746 setSessionLostNotice(null); 6747 setChatErrorMessage(""); 6748 setIsOverloadedError(false); 6749 setIsSessionExpired(false); 6750 discardQueue(); 6751 }, 6752 onNavigate: (target) => { 6753 navigate(target); 6754 }, 6755 // The page coordinator owns the merchant-view swap; forward the 6756 // page-stable callback as-is (the hook dedups + staleness-filters). 6757 onShowView 6758 }); 6759 const agentMessages = sseChat.messages; 6760 const agentMessagesRef = reactExports.useRef(agentMessages); 6761 reactExports.useEffect(() => { 6762 agentMessagesRef.current = agentMessages; 6763 }, [agentMessages]); 6764 const sendAgentMessage = sseChat.sendMessage; 6765 const stopAgentChat = sseChat.stop; 6766 const agentChatStatus = sseChat.status; 6767 const agentIsLoading = sseChat.isLoading; 6768 const agentBusy = agentChatStatus === "connecting" || agentChatStatus === "streaming"; 6769 reactExports.useEffect(() => { 6770 onAgentBusyChange == null ? void 0 : onAgentBusyChange(agentBusy); 6771 }, [agentBusy, onAgentBusyChange]); 6772 const prevAgentBusyRef = reactExports.useRef(false); 6773 reactExports.useEffect(() => { 6774 const prev = prevAgentBusyRef.current; 6775 prevAgentBusyRef.current = agentBusy; 6776 if (!(prev && !agentBusy)) return; 6777 const targets = turnEndRefreshTargets(completedToolNamesInTurn(agentMessagesRef.current)); 6778 if (targets.preview) onPaymentsDataChanged == null ? void 0 : onPaymentsDataChanged(); 6779 if (targets.commerceIntent) refetchCommerceIntent(); 6780 }, [agentBusy, onPaymentsDataChanged, refetchCommerceIntent]); 6781 const prevAgentBusyForBusinessRef = reactExports.useRef(false); 6782 reactExports.useEffect(() => { 6783 const prev = prevAgentBusyForBusinessRef.current; 6784 prevAgentBusyForBusinessRef.current = agentBusy; 6785 if (!(prev && !agentBusy)) return; 6786 const targets = turnEndRefreshTargets(completedToolNamesInTurn(agentMessagesRef.current)); 6787 if (targets.business) onBusinessDataChanged == null ? void 0 : onBusinessDataChanged(); 6788 }, [agentBusy, onBusinessDataChanged]); 6789 const setAgentMessages = sseChat.setMessages; 6790 const clearAgentHistory = sseChat.clearHistory;
6791 const pendingApproval = sseChat.pendingApproval; 6792 const approvalSubmitting = sseChat.approvalSubmitting; 6793 const respondToApproval = sseChat.respondToApproval; 6794 const pendingQuestion = sseChat.pendingQuestion; 6795 const respondToQuestion = sseChat.respondToQuestion; 6796 const thinking = sseChat.thinking; 6797 const maxTurnsHit = sseChat.maxTurnsHit; 6798 const mergedRunCacheRef = reactExports.useRef(/* @__PURE__ */ new Map()); 6799 const displayMessages = reactExports.useMemo(() => { 6800 const { 6801 merged, 6802 nextCache 6803 } = mergeAssistantRuns(agentMessages, mergedRunCacheRef.current); 6804 mergedRunCacheRef.current = nextCache; 6805 return merged; 6806 }, [agentMessages]); 6807 const isAIResponding = sseChat.status === "streaming"; 6808 reactExports.useEffect(() => { 6809 if (isAIResponding && isSubmitting) { 6810 setIsSubmitting(false); 6811 if (submitSafetyTimerRef.current) { 6812 clearTimeout(submitSafetyTimerRef.current); 6813 submitSafetyTimerRef.current = null; 6814 } 6815 } 6816 }, [isAIResponding, isSubmitting]); 6817 reactExports.useEffect(() => { 6818 return () => { 6819 if (submitSafetyTimerRef.current) { 6820 clearTimeout(submitSafetyTimerRef.current); 6821 } 6822 }; 6823 }, []); 6824 const isFirstGenerationInProgress = (website == null ? void 0 : website.websiteType) === "react" && !(website == null ? void 0 : website.homepageGeneratedAt) && !userInitiatedAnyTurn; 6825 reactExports.useEffect(() => { 6826 onAIRespondingChange == null ? void 0 : onAIRespondingChange(isAIResponding); 6827 }, [isAIResponding, onAIRespondingChange]); 6828 const wasAIRespondingRef = reactExports.useRef(isAIResponding); 6829 reactExports.useEffect(() => { 6830 const wasResponding = wasAIRespondingRef.current; 6831 wasAIRespondingRef.current = isAIResponding; 6832 if (!wasResponding || isAIResponding) return; 6833 const active = document.activeElement; 6834 const focusable = !active || active === document.body || active === textareaRef.current; 6835 if (!focusable) return; 6836 requestAnimationFrame(() => { 6837 var _a2; 6838 (_a2 = textareaRef.current) == null ? void 0 : _a2.focus(); 6839 }); 6840 }, [isAIResponding]); 6841 reactExports.useEffect(() => { 6842 if (!websiteId) return; 6843 setLoading(sseChat.historyLoading); 6844 if (!sseChat.historyLoading) { 6845 setMessagesLoaded(true); 6846 } 6847 }, [websiteId, sseChat.historyLoading]); 6848 const lastReportedCommitHashRef = reactExports.useRef(null); 6849 reactExports.useEffect(() => { 6850 var _a2; 6851 if (!onCommitHashChange || agentMessages.length === 0) return; 6852 for (let i = agentMessages.length - 1; i >= 0; i--) { 6853 const msg = agentMessages[i]; 6854 if (msg.role === "assistant" && ((_a2 = msg.metadata) == null ? void 0 : _a2.commitHash)) { 6855 const hash = msg.metadata.commitHash; 6856 if (hash !== lastReportedCommitHashRef.current) { 6857 lastReportedCommitHashRef.current = hash; 6858 onCommitHashChange(hash); 6859 } 6860 break; 6861 } 6862 } 6863 }, [agentMessages, onCommitHashChange]); 6864 const scrollToBottom = reactExports.useCallback((delay = 50) => { 6865 setTimeout(() => { 6866 var _a2; 6867 (_a2 = messagesEndRef.current) == null ? void 0 : _a2.scrollIntoView({ 6868 behavior: "smooth" 6869 }); 6870 }, delay); 6871 }, []); 6872 const stopAlignLastUserAutoScroll = reactExports.useCallback(() => { 6873 if (rafIdRef.current) { 6874 cancelAnimationFrame(rafIdRef.current); 6875 rafIdRef.current = null; 6876 } 6877 autoScrollActiveRef.current = false; 6878 }, []); 6879 const alignLastUserAutoScrollStep = reactExports.useCallback(() => { 6880 const container = messagesContainerRef.current; 6881 if (!container || !autoScrollActiveRef.current) { 6882 stopAlignLastUserAutoScroll(); 6883 return; 6884 } 6885 if (userInteractedRef.current) { 6886 stopAlignLastUserAutoScroll(); 6887 return; 6888 } 6889 const userMessages = container.querySelectorAll('[data-role="user"]'); 6890 const lastUserMessage = userMessages[userMessages.length - 1]; 6891 if (!lastUserMessage) { 6892 stopAlignLastUserAutoScroll(); 6893 return; 6894 } 6895 const lastUserRect = lastUserMessage.getBoundingClientRect(); 6896 const containerRect = container.getBoundingClientRect(); 6897 const targetTop = containerRect.top + 5; 6898 const currentUserTop = lastUserRect.top; 6899 if (currentUserTop <= targetTop) { 6900 stopAlignLastUserAutoScroll(); 6901 return; 6902 } 6903 const scrollDelta = Math.min(24, currentUserTop - targetTop); 6904 container.scrollTop += scrollDelta; 6905 rafIdRef.current = requestAnimationFrame(alignLastUserAutoScrollStep); 6906 }, [stopAlignLastUserAutoScroll]); 6907 const startAlignLastUserAutoScroll = reactExports.useCallback(() => { 6908 const container = messagesContainerRef.current; 6909 if (!container) return; 6910 const userMessages = container.querySelectorAll('[data-role="user"]');
6911 const lastUserMessage = userMessages[userMessages.length - 1]; 6912 if (!lastUserMessage) { 6913 scrollToBottom(50); 6914 return; 6915 } 6916 const lastUserRect = lastUserMessage.getBoundingClientRect(); 6917 const containerRect = container.getBoundingClientRect(); 6918 const targetTop = containerRect.top + 5; 6919 if (lastUserRect.top <= targetTop) { 6920 return; 6921 } 6922 userInteractedRef.current = false; 6923 autoScrollActiveRef.current = true; 6924 rafIdRef.current = requestAnimationFrame(alignLastUserAutoScrollStep); 6925 }, [alignLastUserAutoScrollStep, scrollToBottom]); 6926 const checkScrollPosition = reactExports.useCallback(() => { 6927 const container = messagesContainerRef.current; 6928 if (!container) return; 6929 const { 6930 scrollTop, 6931 scrollHeight, 6932 clientHeight 6933 } = container; 6934 const isAtBottom = scrollTop + clientHeight >= scrollHeight - 10; 6935 const shouldShow = !isAtBottom && agentMessages.length > 0; 6936 isPinnedToBottomRef.current = isAtBottom; 6937 setShowScrollToBottom(shouldShow); 6938 }, [agentMessages.length]); 6939 reactExports.useEffect(() => { 6940 const timeoutId = setTimeout(() => { 6941 checkScrollPosition(); 6942 }, 200); 6943 return () => clearTimeout(timeoutId); 6944 }, [agentMessages, checkScrollPosition]); 6945 reactExports.useLayoutEffect(() => { 6946 if (!isPinnedToBottomRef.current) return; 6947 const container = messagesContainerRef.current; 6948 if (!container) return; 6949 container.scrollTop = container.scrollHeight; 6950 }, [agentMessages]); 6951 reactExports.useEffect(() => { 6952 const container = messagesContainerRef.current; 6953 if (!container) return; 6954 const handleUserInteraction = () => { 6955 userInteractedRef.current = true; 6956 stopAlignLastUserAutoScroll(); 6957 }; 6958 container.addEventListener("wheel", handleUserInteraction, { 6959 passive: true 6960 }); 6961 container.addEventListener("touchstart", handleUserInteraction, { 6962 passive: true 6963 }); 6964 container.addEventListener("mousedown", handleUserInteraction); 6965 return () => { 6966 container.removeEventListener("wheel", handleUserInteraction); 6967 container.removeEventListener("touchstart", handleUserInteraction); 6968 container.removeEventListener("mousedown", handleUserInteraction); 6969 stopAlignLastUserAutoScroll(); 6970 }; 6971 }, [stopAlignLastUserAutoScroll]); 6972 const sendMessageWithAuth = reactExports.useCallback(async (message) => { 6973 setChatStopped(false); 6974 return await sendAgentMessage(message); 6975 }, [sendAgentMessage]); 6976 const dispatchSnapshot = reactExports.useCallback(async (snapshot, options) => { 6977 const messageText = snapshot.text; 6978 if (!messageText.trim()) return; 6979 if (!isContainerReady) { 6980 console.warn("Cannot submit while container is starting"); 6981 return; 6982 } 6983 if (agentChatStatus === "streaming") { 6984 console.warn("Cannot submit while AI is already responding"); 6985 return; 6986 } 6987 setIsSubmitting(true); 6988 if (submitSafetyTimerRef.current) { 6989 clearTimeout(submitSafetyTimerRef.current); 6990 } 6991 submitSafetyTimerRef.current = setTimeout(() => { 6992 setIsSubmitting(false); 6993 submitSafetyTimerRef.current = null; 6994 }, 15e3); 6995 setDispatchInFlight(true); 6996 try { 6997 if (!(options == null ? void 0 : options.isInitialCreation)) { 6998 setUserInitiatedAnyTurn(true); 6999 } 7000 setTimeout(() => { 7001 scrollToBottom(100); 7002 }, 100); 7003 setTimeout(() => { 7004 startAlignLastUserAutoScroll(); 7005 }, 50); 7006 setMaxStepsReached(false); 7007 setIsOutOfCredits(false); 7008 setChatSessionInProgress(false); 7009 setChatErrorMessage(""); 7010 setIsOverloadedError(false); 7011 setIsSessionExpired(false); 7012 const previousLastUserMessage = lastUserMessageRef.current; 7013 const previousLastSnapshot = lastDispatchedSnapshotRef.current; 7014 lastUserMessageRef.current = messageText; 7015 lastDispatchedSnapshotRef.current = snapshot; 7016 let token = null; 7017 try { 7018 token = await getFreshToken(getToken, 5); 7019 } catch (error) { 7020 console.error("[ReactChatPanel] Token refresh failed:", error); 7021 } 7022 if (!token) { 7023 lastUserMessageRef.current = previousLastUserMessage; 7024 lastDispatchedSnapshotRef.current = previousLastSnapshot; 7025 setChatErrorMessage(AUTH_REQUIRED_MESSAGE); 7026 releaseSubmitFlag(); 7027 return void 0; 7028 } 7029 if ((options == null ? void 0 : options.generation) !== void 0 && options.generation !== queueRef.current.generation) { 7030 lastUserMessageRef.current = previousLastUserMessage; 7031 lastDispatchedSnapshotRef.current = previousLastSnapshot; 7032 releaseSubmitFlag(); 7033 return "cancelled"; 7034 } 7035 const tokenInfo = getTokenExpirationInfo(token); 7036 const tokenMinutesLeft = tokenInfo.valid ? tokenInfo.minutesUntilExpiration : -1; 7037 const { 7038 message, 7039 selectionShipped, 7040 selectionRejected 7041 } = buildAgentMessage(snapshot, { 7042 websitePlan, 7043 isAgencyUser, 7044 website: { 7045 publicId: website == null ? void 0 : website.publicId, 7046 analyticsId: website == null ? void 0 : website.analyticsId, 7047 Domain: website == null ? void 0 : website.Domain 7048 }, 7049 currentUserId: currentUser == null ? void 0 : currentUser.id, 7050 tokenMinutesLeft, 7051 isInitialCreation: options == null ? void 0 : options.isInitialCreation 7052 }); 7053 if (selectionRejected) { 7054 _t.error("Selected element is too large to attach â try picking a smaller element"); 7055 setPickerLiveMessage("Selected element is too large to attach â try picking a smaller element"); 7056 }
7057 try { 7058 const result = await sendMessageWithAuth(message); 7059 releaseSubmitFlag(); 7060 if (result === "turn_active") { 7061 lastUserMessageRef.current = previousLastUserMessage; 7062 lastDispatchedSnapshotRef.current = previousLastSnapshot; 7063 return result; 7064 } 7065 if (result === "failed") { 7066 return result; 7067 } 7068 if (selectionShipped) { 7069 posthog == null ? void 0 : posthog.capture("picker_message_sent", { 7070 tag: snapshot.selectedElement.tag 7071 }); 7072 } 7073 if (isAgencyUser && onAgencyCreditUsed) { 7074 if (isFree || websiteCreditsRemaining <= 0) { 7075 onAgencyCreditUsed(); 7076 } 7077 } 7078 onAiChatMessageUsed == null ? void 0 : onAiChatMessageUsed(); 7079 return result; 7080 } catch (error) { 7081 console.error("Error sending message:", error); 7082 setChatErrorMessage(error instanceof Error ? error.message : String(error)); 7083 releaseSubmitFlag(); 7084 return "failed"; 7085 } 7086 } finally { 7087 setDispatchInFlight(false); 7088 } 7089 }, [websitePlan, currentUser, getToken, sendMessageWithAuth, startAlignLastUserAutoScroll, scrollToBottom, agentChatStatus, website == null ? void 0 : website.publicId, website == null ? void 0 : website.analyticsId, website == null ? void 0 : website.Domain, isAgencyUser, isFree, websiteCreditsRemaining, onAgencyCreditUsed, onAiChatMessageUsed, posthog, releaseSubmitFlag]); 7090 const selectedElementRef = reactExports.useRef(selectedElement); 7091 selectedElementRef.current = selectedElement; 7092 const submitMessage = reactExports.useCallback(async (messageText, options) => { 7093 if (!messageText.trim()) return; 7094 const snapshot = { 7095 text: messageText, 7096 images, 7097 selectedElement, 7098 currentPath 7099 }; 7100 if (snapshot.selectedElement) { 7101 onClearSelection == null ? void 0 : onClearSelection(); 7102 } 7103 handleClearImages(); 7104 const result = await dispatchSnapshot(snapshot, options); 7105 if (result === void 0) { 7106 const settled = (snapshot.images ?? []).filter((image) => !image.uploading && !image.error); 7107 if (settled.length > 0) { 7108 handleRestoreImages(settled.map(snapshotImage)); 7109 } 7110 if (snapshot.selectedElement && !selectedElementRef.current) { 7111 onRestoreSelection == null ? void 0 : onRestoreSelection(snapshot.selectedElement); 7112 } 7113 } 7114 return result; 7115 }, [images, selectedElement, currentPath, onClearSelection, onRestoreSelection, handleClearImages, handleRestoreImages, dispatchSnapshot]); 7116 const enqueueText = reactExports.useCallback((text) => { 7117 if (!text.trim()) return; 7118 const hasSelection = !!selectedElement; 7119 const snapshotImages = images.filter((image) => !image.error && !image.uploading); 7120 setQueue((state) => enqueue(state, { 7121 text, 7122 images: snapshotImages, 7123 selectedElement: selectedElement ?? null, 7124 currentPath 7125 })); 7126 if (hasSelection) { 7127 onClearSelection == null ? void 0 : onClearSelection(); 7128 } 7129 if (images.some((image) => image.uploading)) { 7130 images.filter((image) => !image.uploading).forEach((image) => handleRemoveImage(image.index)); 7131 } else { 7132 handleClearImages(); 7133 } 7134 setPickerLiveMessage("Message queued â it will send when the AI finishes"); 7135 posthog == null ? void 0 : posthog.capture("chat_message_queued", { 7136 queue_length: queueRef.current.items.length + 1, 7137 has_images: snapshotImages.length > 0, 7138 has_selection: hasSelection 7139 }); 7140 }, [images, selectedElement, currentPath, onClearSelection, handleClearImages, handleRemoveImage, posthog]); 7141 const submitOrQueue = reactExports.useCallback((text, options) => { 7142 const busy = agentChatStatus === "connecting" || agentChatStatus === "streaming" || isSubmitting || dispatchInFlight; 7143 if (busy) { 7144 enqueueText(text); 7145 return; 7146 } 7147 void submitMessage(text, options); 7148 }, [agentChatStatus, isSubmitting, dispatchInFlight, enqueueText, submitMessage]); 7149 const sendQueued = reactExports.useCallback(async (item, state, via) => { 7150 const outcome = await dispatchSnapshot({ 7151 text: item.text, 7152 images: [...item.images], 7153 selectedElement: item.selectedElement, 7154 currentPath: item.currentPath 7155 }, {
7156 generation: state.generation 7157 }); 7158 if (outcome === "started") { 7159 posthog == null ? void 0 : posthog.capture("chat_queued_message_sent", { 7160 queue_length_remaining: state.items.length, 7161 has_images: item.images.length > 0, 7162 has_selection: !!item.selectedElement, 7163 via 7164 }); 7165 return; 7166 } 7167 if (outcome === "turn_active" || outcome === void 0) { 7168 setQueue((current) => shouldRequeueAfterConflict(state.generation, current.generation) ? unshift(current, item) : current); 7169 } 7170 }, [dispatchSnapshot, posthog]); 7171 const handleContinueAfterMaxTurns = reactExports.useCallback(async () => { 7172 if (agentIsLoading) return; 7173 const message = { 7174 role: "user", 7175 parts: [{ 7176 type: "text", 7177 text: "Continue" 7178 }], 7179 id: generateId(), 7180 metadata: { 7181 userIdForAnalyticsOnly: currentUser == null ? void 0 : currentUser.id, 7182 isPaidWebsite: websitePlan !== "Free", 7183 websitePlan, 7184 publicId: (website == null ? void 0 : website.publicId) || void 0 7185 } 7186 }; 7187 setDispatchInFlight(true); 7188 try { 7189 await sendMessageWithAuth(message); 7190 } catch (err) { 7191 console.error("[ReactChatPanel] Continue failed:", err); 7192 } finally { 7193 setDispatchInFlight(false); 7194 } 7195 }, [agentIsLoading, currentUser == null ? void 0 : currentUser.id, websitePlan, website == null ? void 0 : website.publicId, sendMessageWithAuth]); 7196 reactExports.useImperativeHandle(ref, () => ({ 7197 sendUserMessage: (message, options) => { 7198 submitOrQueue(message, options); 7199 }, 7200 reloadMessages: () => { 7201 shouldReloadMessagesRef.current = true; 7202 setMessagesLoaded(false); 7203 } 7204 }), [submitOrQueue]); 7205 reactExports.useEffect(() => { 7206 setIsExpanded(expanded); 7207 }, [expanded]); 7208 reactExports.useEffect(() => { 7209 onToggleChatPanel == null ? void 0 : onToggleChatPanel(isExpanded); 7210 }, [isExpanded, onToggleChatPanel]); 7211 const handleClearAgentHistory = async () => { 7212 let token = null; 7213 try { 7214 token = await getToken(); 7215 } catch (error) { 7216 console.error("[ReactChatPanel] Token lookup for reset failed:", error); 7217 } 7218 if (!token) { 7219 setChatErrorMessage(AUTH_REQUIRED_MESSAGE); 7220 return; 7221 } 7222 discardQueue(); 7223 try { 7224 await clearAgentHistory(); 7225 } catch { 7226 return; 7227 } 7228 setShowResetSuggestion(false); 7229 setSessionLostNotice(null); 7230 setChatErrorMessage(""); 7231 setIsOverloadedError(false); 7232 setIsSessionExpired(false); 7233 hasSetSpecificErrorRef.current = false; 7234 lastUserMessageRef.current = ""; 7235 lastDispatchedSnapshotRef.current = null; 7236 }; 7237 const handleRetryLastMessage = reactExports.useCallback(async () => { 7238 if (!lastUserMessageRef.current) return; 7239 setChatErrorMessage(""); 7240 setIsOverloadedError(false); 7241 setIsSessionExpired(false); 7242 if (agentMessages.length > 0 && setAgentMessages) { 7243 const lastMessage = agentMessages[agentMessages.length - 1]; 7244 if (lastMessage.role === "user") { 7245 const updatedMessages = agentMessages.slice(0, -1); 7246 setAgentMessages(updatedMessages); 7247 } 7248 } 7249 const snapshot = lastDispatchedSnapshotRef.current; 7250 if (snapshot && snapshot.text === lastUserMessageRef.current) { 7251 await dispatchSnapshot(snapshot); 7252 return; 7253 } 7254 await submitMessage(lastUserMessageRef.current); 7255 }, [agentMessages, setAgentMessages, submitMessage, dispatchSnapshot]); 7256 const handleFixViteError = reactExports.useCallback(async () => { 7257 if (!viteError) return; 7258 if (isAIResponding || isRestoring || isChatLimitReached || !isContainerReady || !(myWebsiteAccess == null ? void 0 : myWebsiteAccess.canEdit)) { 7259 return; 7260 } 7261 const lines = ["Please fix the build error in my website preview so it can show my latest changes.", ""]; 7262 if (viteError.file) lines.push(`File: ${viteError.file}`); 7263 if (viteError.message) lines.push(`Error: ${viteError.message}`); 7264 if (viteError.path) lines.push(`Page where this happened: ${viteError.path}`); 7265 if (viteError.frame) { 7266 lines.push("", "Code frame from the build tool:", "```", viteError.frame, "```"); 7267 } 7268 await submitMessage(lines.join("\n")); 7269 }, [viteError, isAIResponding, isRestoring, isChatLimitReached, isContainerReady, myWebsiteAccess == null ? void 0 : myWebsiteAccess.canEdit, submitMessage]); 7270 const mode = composerMode({ 7271 status: agentChatStatus,
7272 submitting: isSubmitting || dispatchInFlight, 7273 chatLimitReached: isChatLimitReached, 7274 restoring: isRestoring, 7275 containerReady: isContainerReady, 7276 canEdit: !!(myWebsiteAccess == null ? void 0 : myWebsiteAccess.canEdit), 7277 pendingApproval: !!pendingApproval, 7278 pendingQuestion: !!pendingQuestion, 7279 inlineEditInProgress 7280 }); 7281 const composerLocked = mode === "locked"; 7282 const panelError = !!chatErrorMessage || isSessionExpired || isOutOfCredits; 7283 const canResetFromError = !isOverloadedError; 7284 const drainDecision = drainGate({ 7285 status: agentChatStatus, 7286 pendingApproval: !!pendingApproval, 7287 pendingQuestion: !!pendingQuestion, 7288 maxTurnsHit, 7289 panelError, 7290 chatLimitReached: isChatLimitReached, 7291 canEdit: !!(myWebsiteAccess == null ? void 0 : myWebsiteAccess.canEdit), 7292 restoring: isRestoring, 7293 containerReady: isContainerReady, 7294 submitting: isSubmitting || dispatchInFlight, 7295 stopLatched, 7296 // A queued row being edited, or the user editing text directly in the 7297 // preview: either way the queued AI edit must wait for them to finish. 7298 editing: editingQueuedId !== null || inlineEditInProgress 7299 }); 7300 const pausedReason = queue.items.length > 0 && DRAIN_PAUSED_REASONS.includes(drainDecision) ? drainDecision : null; 7301 reactExports.useEffect(() => { 7302 if (stopLatched && agentChatStatus === "idle") setStopLatched(false); 7303 }, [stopLatched, agentChatStatus]); 7304 const prevDecisionRef = reactExports.useRef(null); 7305 reactExports.useEffect(() => { 7306 const prev = prevDecisionRef.current; 7307 prevDecisionRef.current = drainDecision; 7308 if (!drainTransition(prev, drainDecision)) return; 7309 const { 7310 head, 7311 state 7312 } = shiftHead(queueRef.current); 7313 if (!head) return; 7314 queueRef.current = state; 7315 setQueue(state); 7316 void sendQueued(head, state, "drain"); 7317 }, [drainDecision, sendQueued]); 7318 const enqueueComposer = () => { 7319 const text = inputValue.trim(); 7320 if (!text) return; 7321 enqueueText(text); 7322 setInputValue(""); 7323 if (textareaRef.current) { 7324 textareaRef.current.style.height = "44px"; 7325 } 7326 }; 7327 const handleFormSubmit = async (e) => { 7328 e.preventDefault(); 7329 if (!inputValue.trim()) return; 7330 if (composerLocked) return; 7331 if (mode === "busy" && isUploadingImages) return; 7332 if (pickModeActive && !selectedElement) { 7333 onTogglePickMode == null ? void 0 : onTogglePickMode(); 7334 _t(mode === "busy" ? "Pick mode cancelled â message queued without selection" : "Pick mode cancelled â message sent without selection"); 7335 } 7336 if (mode === "busy") { 7337 enqueueComposer(); 7338 return; 7339 } 7340 const message = inputValue.trim(); 7341 setInputValue(""); 7342 if (textareaRef.current) { 7343 textareaRef.current.style.height = "44px"; 7344 } 7345 await submitMessage(message); 7346 }; 7347 const handleKeyDown = (e) => { 7348 if (e.key === "Enter" && !e.shiftKey) { 7349 e.preventDefault(); 7350 if (!inputValue.trim() || composerLocked) { 7351 return; 7352 } 7353 handleFormSubmit(e); 7354 } 7355 }; 7356 const handleUpdateQueuedText = reactExports.useCallback((id, text) => { 7357 const state = updateText(queueRef.current, id, text); 7358 if (state === queueRef.current) return; 7359 queueRef.current = state; 7360 setQueue(state); 7361 }, []); 7362 const handleRemoveQueued = reactExports.useCallback((id) => setQueue((state) => removeItem(state, id)), []); 7363 const handleSendQueuedNow = reactExports.useCallback((id) => { 7364 const current = queueRef.current; 7365 const item = current.items.find((entry) => entry.id === id); 7366 if (!item) return; 7367 const state = removeItem(current, id); 7368 queueRef.current = state; 7369 setQueue(state); 7370 void sendQueued(item, state, "send_now"); 7371 }, [sendQueued]); 7372 const handleStopAgentChat = async () => { 7373 try { 7374 setChatStopped(true); 7375 setStopLatched(true); 7376 const queuedCount = queueRef.current.items.length; 7377 const { 7378 merged, 7379 state: emptied 7380 } = mergeOut(queueRef.current, { 7381 text: inputValue, 7382 hasSelection: !!selectedElement 7383 }); 7384 queueRef.current = emptied; 7385 setQueue(emptied); 7386 setEditingQueuedId(null); 7387 if (queuedCount > 0) { 7388 setInputValue(merged.text); 7389 if (merged.images.length > 0) { 7390 handleRestoreImages(merged.images); 7391 } 7392 if (merged.selectedElement) { 7393 onRestoreSelection == null ? void 0 : onRestoreSelection(merged.selectedElement); 7394 } 7395 if (merged.droppedSelections > 0) { 7396 _t(merged.droppedSelections === 1 ? "A queued element selection was dropped â only one can be attached" : `${merged.droppedSelections} queued element selections were dropped â only one can be attached`); 7397 } 7398 posthog == null ? void 0 : posthog.capture("chat_queue_returned_on_
7398stop", { 7399 queue_length: queuedCount, 7400 has_images: merged.images.length > 0, 7401 has_selection: !!merged.selectedElement 7402 }); 7403 } 7404 stopAgentChat(); 7405 if (agentMessages.length > 0 && setAgentMessages) { 7406 const lastMessage = agentMessages[agentMessages.length - 1]; 7407 if (lastMessage.role === "assistant") { 7408 const updatedMessages = [...agentMessages]; 7409 updatedMessages[updatedMessages.length - 1] = { 7410 ...lastMessage, 7411 metadata: { 7412 ...lastMessage.metadata, 7413 aborted: true, 7414 abortedAt: (/* @__PURE__ */ new Date()).toISOString() 7415 } 7416 }; 7417 setAgentMessages(updatedMessages); 7418 } 7419 } 7420 setTimeout(() => { 7421 setChatSessionInProgress(false); 7422 }, 500); 7423 } catch (error) { 7424 console.error("Error stopping agent chat:", error); 7425 setChatSessionInProgress(false); 7426 } 7427 }; 7428 const handleRestore = reactExports.useCallback(async (messageId, preMessageCommitHash) => { 7429 const messagesNow = agentMessagesRef.current; 7430 const messageIndex = messagesNow.findIndex((m) => m.id === messageId); 7431 if (messageIndex === -1) { 7432 _t.error("Message not found"); 7433 return; 7434 } 7435 const restoredMessage = messagesNow[messageIndex]; 7436 const confirmed = window.confirm(`Restore ${siteIdentityText({ 7437 id: websiteId, 7438 name: website == null ? void 0 : website.name, 7439 publicId: website == null ? void 0 : website.publicId 7440 })} to this point? 7441 7442This will: 7443⢠Roll back your code to this version 7444⢠Remove this message and all after it 7445⢠Let you edit and re-submit this message 7446 7447Any uncommitted changes will be lost.`); 7448 if (!confirmed) return; 7449 setIsRestoring(true); 7450 discardQueue(); 7451 try { 7452 const restore = await resumeChatRestore({ 7453 websiteId, 7454 preMessageCommitHash, 7455 pending: pendingChatRestoreRef.current, 7456 remember: (pending) => { 7457 pendingChatRestoreRef.current = pending; 7458 }, 7459 start: async () => { 7460 var _a2; 7461 return (_a2 = (await restoreMutation({ 7462 variables: { 7463 websiteId, 7464 messageId, 7465 preMessageCommitHash 7466 } 7467 })).data) == null ? void 0 : _a2.restoreToChatMessage; 7468 }, 7469 advance: async (variables) => { 7470 var _a2; 7471 return (_a2 = (await continueRestoreMutation({ 7472 variables 7473 })).data) == null ? void 0 : _a2.continueChatRestore; 7474 } 7475 }); 7476 if (restore == null ? void 0 : restore.success) { 7477 let messageText = ""; 7478 if (restoredMessage.parts) { 7479 for (const part of restoredMessage.parts) { 7480 if (part.type === "text" && part.text) { 7481 messageText = part.text; 7482 const contextMatch = messageText.match(/<context for the AI>[\s\S]*?<\/context for the AI>\s*/); 7483 if (contextMatch) { 7484 messageText = messageText.replace(contextMatch[0], "").trim(); 7485 } 7486 break; 7487 } 7488 } 7489 } 7490 const remainingMessages = messagesNow.slice(0, messageIndex); 7491 setAgentMessages(remainingMessages); 7492 setInputValue(messageText); 7493 await new Promise((resolve) => setTimeout(resolve, 500)); 7494 setTimeout(() => { 7495 var _a2; 7496 (_a2 = textareaRef.current) == null ? void 0 : _a2.focus(); 7497 }, 100); 7498 _t.success("Restored! Edit your message below and re-submit when ready."); 7499 } else { 7500 _t.error((restore == null ? void 0 : restore.pending) ? "Conversation restore is still saving. Please retry shortly." : (restore == null ? void 0 : restore.error) || "Failed to restore"); 7501 } 7502 } catch (error) { 7503 console.error("Restore error:", error); 7504 _t.error(error instanceof Error ? error.message : "Failed to restore"); 7505 } finally { 7506 setIsRestoring(false); 7507 } 7508 }, [restoreMutation, continueRestoreMutation, websiteId, setAgentMessages, discardQueue]); 7509 const pinnedStatusText = reactExports.useMemo(() => { 7510 var _a2; 7511 if (!agentIsLoading) return null; 7512 const lastMsg = agentMessages[agentMessages.length - 1]; 7513 if ((lastMsg == null ? void 0 : lastMsg.role) === "assistant" && lastMsg.parts) { 7514 const parts = lastMsg.parts; 7515 for (let i = parts.length - 1; i >= 0; i--) { 7516 const p = parts[i]; 7517 if (!((_a2 = p == null ? void 0 : p.type) == null ? void 0 : _a2.startsWith("tool-"))) continue; 7518 const state = p.state; 7519 if (state !== "input-streaming" && state !== "input-available") continue; 7520 const toolName = p.type.replace(/^tool-/, ""); 7521 const input = p.input ?? {}; 7522 const fileVerb = FILE_TOOL_VERBS[toolName]; 7523 if (fileVerb) { 7524 const filePath = input.file_path ?? input.path; 7525 const target = filePath ? stripHome(filePath) : "file"; 7526 return `${fileVerb} ${target}`; 7527 } 7528 const renderer = getToolRenderer(toolName); 7529 if (renderer == null ? void 0 : renderer.progressLabel) { 7530 const label = renderer.progressLabel(input); 7531 if (label) return label; 7532 } 7533 if (renderer == null ? void 0 : renderer.displayName) return renderer.displayName; 7534 return humanizeToolName(toolName) || toolName; 7535 } 7536 } 7537 if (thinking == null ? void 0 : thinking.active) return null; 7538 return "Workingâ¦"; 7539 }, [agentMessages, agentIsLoading, thinking == null ? void 0 : thinking.active]); 7540 const composerNoticeSeqRef = reactExports.useRef(0); 7541 const composerNoticeSeqByIdRef = reactExports.useRef(/* @__PURE__ */ new Map()); 7542 const stuckViteError = agentIsLoading ? null : viteError; 7543 const postageNoticeDismissedKey = `ls_postage_notice_dismissed_${websiteId}`; 7544 const [postageNoticeDismissed, setPostageNoticeDismissed] = reactExports.useState(() => { 7545 try {
7546 return localStorage.getItem(postageNoticeDismissedKey) === "1"; 7547 } catch { 7548 return false; 7549 } 7550 }); 7551 const handleDismissPostage = reactExports.useCallback(() => { 7552 setPostageNoticeDismissed(true); 7553 try { 7554 localStorage.setItem(postageNoticeDismissedKey, "1"); 7555 } catch { 7556 } 7557 }, [postageNoticeDismissedKey]); 7558 const postageUncharged = postageUnchargedFromServer && !postageNoticeDismissed; 7559 const currencyNoticeKey = `ls_currency_notice_dismissed_${websiteId}_${currencyRestatedFrom ?? ""}_${currencyRestatedTo ?? ""}`; 7560 const dismissedCurrencyKeysRef = reactExports.useRef(/* @__PURE__ */ new Set()); 7561 const [currencyDismissNonce, setCurrencyDismissNonce] = reactExports.useState(0); 7562 const handleDismissCurrency = reactExports.useCallback(() => { 7563 dismissedCurrencyKeysRef.current.add(currencyNoticeKey); 7564 try { 7565 localStorage.setItem(currencyNoticeKey, "1"); 7566 } catch { 7567 } 7568 setCurrencyDismissNonce((n) => n + 1); 7569 }, [currencyNoticeKey]); 7570 const currencyRestated = reactExports.useMemo(() => { 7571 if (!currencyRestatedTo) return null; 7572 if (dismissedCurrencyKeysRef.current.has(currencyNoticeKey)) return null; 7573 try { 7574 if (localStorage.getItem(currencyNoticeKey) === "1") return null; 7575 } catch { 7576 } 7577 return { 7578 from: currencyRestatedFrom, 7579 to: currencyRestatedTo 7580 }; 7581 }, [currencyNoticeKey, currencyRestatedTo, currencyRestatedFrom, currencyDismissNonce]); 7582 const activeComposerNoticeIds = reactExports.useMemo(() => getActiveComposerNoticeIds({ 7583 isOutOfCredits, 7584 isSessionExpired, 7585 viteError: stuckViteError, 7586 sessionLostNotice, 7587 needsPaymentsSetup, 7588 commerceSetup: visibleCommerceSetup, 7589 needsPaymentsUpgrade, 7590 paymentsDisabled, 7591 postageUncharged, 7592 currencyRestated 7593 }), [isOutOfCredits, isSessionExpired, stuckViteError, sessionLostNotice, visibleCommerceSetup, needsPaymentsSetup, needsPaymentsUpgrade, paymentsDisabled, postageUncharged, currencyRestated]); 7594 const composerNoticeSeqById = reactExports.useMemo(() => { 7595 const active = new Set(activeComposerNoticeIds); 7596 for (const id of active) { 7597 if (!composerNoticeSeqByIdRef.current.has(id)) { 7598 composerNoticeSeqRef.current += 1; 7599 composerNoticeSeqByIdRef.current.set(id, composerNoticeSeqRef.current); 7600 } 7601 } 7602 for (const id of composerNoticeSeqByIdRef.current.keys()) { 7603 if (!active.has(id)) composerNoticeSeqByIdRef.current.delete(id); 7604 } 7605 return new Map(composerNoticeSeqByIdRef.current); 7606 }, [activeComposerNoticeIds]); 7607 const paymentsNoticeMinimizedKey = `ls_payments_notice_min_${websiteId}`; 7608 const [paymentsNoticeMinimized, setPaymentsNoticeMinimized] = reactExports.useState(() => { 7609 try { 7610 return localStorage.getItem(paymentsNoticeMinimizedKey) === "1"; 7611 } catch { 7612 return false; 7613 } 7614 }); 7615 const handleToggleMinimizePayments = reactExports.useCallback(() => { 7616 setPaymentsNoticeMinimized((prev) => { 7617 const next = !prev; 7618 try { 7619 if (next) localStorage.setItem(paymentsNoticeMinimizedKey, "1"); 7620 else localStorage.removeItem(paymentsNoticeMinimizedKey); 7621 } catch { 7622 } 7623 return next; 7624 }); 7625 }, [paymentsNoticeMinimizedKey]); 7626 const minimizedNoticeIds = reactExports.useMemo(() => new Set(paymentsNoticeMinimized ? ["payments-setup"] : []), [paymentsNoticeMinimized]); 7627 const composerNotices = reactExports.useMemo(() => buildComposerNotices({ 7628 isOutOfCredits, 7629 isSessionExpired, 7630 hasBufferedMessage: Boolean(lastUserMessageRef.current), 7631 viteError: stuckViteError, 7632 sessionLostNotice, 7633 needsPaymentsSetup, 7634 needsPaymentsUpgrade, 7635 canStartPaymentsOnboarding, 7636 paymentsForInvoicing, 7637 paymentsPending, 7638 paymentsDisabled, 7639 commerceSetup: visibleCommerceSetup, 7640 commerceSetupRefreshing: setupRefresh.refreshing, 7641 commerceSetupRefreshError: setupRefresh.refreshError, 7642 onRefreshCommerceSetup: setupRefresh.refresh, 7643 onOpenStripeDashboard: openCommerceStripeDashboard, 7644 activatedSeqById: composerNoticeSeqById, 7645 onAddCredits: () => navigate(namedRoutes.reactWebsiteChatSettingsTab({ 7646 id: websiteId, 7647 tab: "plan" 7648 })), 7649 onTryAgain: handleRetryLastMessage, 7650 onFixVite: handleFixViteError, 7651 onDismissSessionLost: () => setSessionLostNotice(null), 7652 onSetUpPayments: handleSetUpPayments, 7653 onToggleMinimizePayments: handleToggleMinimizePayments,
7654 paymentsNoticeMinimized, 7655 postageUncharged, 7656 onDismissPostage: handleDismissPostage, 7657 currencyRestated, 7658 onDismissCurrency: handleDismissCurrency, 7659 onUpgradePlan: () => navigate(namedRoutes.reactWebsiteChatSettingsTab({ 7660 id: websiteId, 7661 tab: "plan" 7662 })), 7663 paymentsActionDisabled: isStartingPaymentsSetup, 7664 actionDisabled: isAIResponding || isRestoring || isChatLimitReached || !isContainerReady || !(myWebsiteAccess == null ? void 0 : myWebsiteAccess.canEdit) 7665 }), [isOutOfCredits, isSessionExpired, stuckViteError, sessionLostNotice, needsPaymentsSetup, needsPaymentsUpgrade, canStartPaymentsOnboarding, paymentsForInvoicing, paymentsPending, paymentsDisabled, visibleCommerceSetup, setupRefresh.refreshing, setupRefresh.refreshError, setupRefresh.refresh, postageUncharged, handleDismissPostage, currencyRestated, handleDismissCurrency, handleSetUpPayments, handleToggleMinimizePayments, paymentsNoticeMinimized, isStartingPaymentsSetup, composerNoticeSeqById, websiteId, handleRetryLastMessage, handleFixViteError, isAIResponding, isRestoring, isChatLimitReached, isContainerReady, myWebsiteAccess == null ? void 0 : myWebsiteAccess.canEdit]); 7666 const { 7667 tray: composerNoticeTray, 7668 bubbles: composerNoticeBubbles 7669 } = reactExports.useMemo(() => pickTrayAndBubbles(composerNotices, minimizedNoticeIds), [composerNotices, minimizedNoticeIds]); 7670 const handleChatAreaDrop = (e) => { 7671 e.preventDefault(); 7672 e.stopPropagation(); 7673 setIsDragActive(false); 7674 if (composerLocked) return; 7675 const files = e.dataTransfer.files; 7676 if ((files == null ? void 0 : files.length) > 0) { 7677 handleFileChange(files); 7678 } 7679 }; 7680 const handleChatAreaDragOver = (e) => { 7681 e.preventDefault(); 7682 e.stopPropagation(); 7683 if (!composerLocked) setIsDragActive(true); 7684 }; 7685 const handleChatAreaDragEnter = (e) => { 7686 e.preventDefault(); 7687 e.stopPropagation(); 7688 if (!composerLocked) setIsDragActive(true); 7689 }; 7690 const handleChatAreaDragLeave = (e) => { 7691 e.preventDefault(); 7692 e.stopPropagation(); 7693 if (!e.currentTarget.contains(e.relatedTarget)) { 7694 setIsDragActive(false); 7695 } 7696 }; 7697 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: cn$1("h-full overflow-hidden", isHiddenForLoading ? "w-0" : "w-full"), children: [ 7698 /* @__PURE__ */ jsxRuntimeExports.jsxs(Card, { className: cn$1("relative flex h-full w-full flex-col rounded-none border-0 bg-white shadow-none", !isExpanded && "border-0 bg-transparent shadow-none"), onDrop: handleChatAreaDrop, onDragOver: handleChatAreaDragOver, onDragEnter: handleChatAreaDragEnter, onDragLeave: handleChatAreaDragLeave, children: [
7699 isDragActive && !composerLocked && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "pointer-events-none absolute inset-0 z-40 overflow-hidden bg-gradient-to-br from-blue-50/95 to-indigo-50/95 p-3 backdrop-blur-md", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex h-full w-full items-center justify-center rounded-[14px] border-2 border-dashed border-blue-300/70 bg-white/95 px-4 py-3 shadow-lg animate-in fade-in-0 zoom-in-95 duration-200", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [ 7700 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-gradient-to-br from-blue-500 to-indigo-600 shadow-md", children: /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { className: "h-5 w-5 text-white", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" }) }) }),
7701 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 text-left", children: [ 7702 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-sm font-semibold leading-tight text-gray-900", children: "Drop files here" }), 7703 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs leading-snug text-gray-600", children: "Add images or documents to your conversation" }) 7704 ] }) 7705 ] }) }) }), 7706 isRestoring && isExpanded && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 z-50 flex items-center justify-center rounded-lg bg-white/95 backdrop-blur-sm", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-w-sm rounded-lg border bg-white p-6 text-center shadow-lg", children: [ 7707 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-4", children: /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "mx-auto h-12 w-12 animate-spin text-primary" }) }), 7708 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "mb-2 text-lg font-semibold", children: "Restoring..." }),
7709 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-600", children: "Rolling back code and chat history. This may take a few seconds." }) 7710 ] }) }), 7711 isChatLimitReached && isExpanded && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 z-50 flex items-center justify-center rounded-lg bg-white/95 backdrop-blur-sm", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-w-sm rounded-lg border bg-white p-6 text-center shadow-lg", children: [ 7712 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-4", children: /* @__PURE__ */ jsxRuntimeExports.jsx(MessageSquare, { className: "mx-auto h-12 w-12 text-gray-400" }) }), 7713 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "mb-2 text-lg font-semibold", children: "Chat Limit Reached" }), 7714 /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "mb-4 text-sm text-gray-600", children: [ 7715 "You've used all ", 7716 FREE_PLAN_CHAT_LIMIT, 7717 " free chats. Upgrade to get more AI conversations." 7718 ] }),
7719 /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { onClick: () => { 7720 navigate(namedRoutes.reactWebsiteChatSettingsTab({ 7721 id: websiteId, 7722 tab: "plan" 7723 })); 7724 }, className: "w-full", children: "Upgrade Plan" }) 7725 ] }) }), 7726 isExpanded && /* @__PURE__ */ jsxRuntimeExports.jsxs(React.Fragment, { children: [ 7727 /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { ref: messagesContainerRef, className: "relative flex-grow overflow-y-auto p-0 pt-2", onScroll: checkScrollPosition, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col px-4 py-1", children: [ 7728 loading && !agentIsLoading && /* @__PURE__ */ jsxRuntimeExports.jsx(SandboxWaitingIndicator, {}), 7729 !loading && sseChat.historyError && /* @__PURE__ */ jsxRuntimeExports.jsx(SystemCard, { accent: "warning", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { className: "h-5 w-5" }), title: "Couldn't load chat history", className: "my-6", actions: ( 7730 // Reset Chat too: ResetChatWidget only renders with 7731 // messages, so a session whose history never loads 7732 // would otherwise have no way to start over.
7733 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [ 7734 /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { size: "sm", variant: "outline", onClick: () => sseChat.retryHistory(), children: "Try again" }), 7735 /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { size: "sm", variant: "outline", onClick: handleClearAgentHistory, children: "Reset Chat" }), 7736 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-gray-500", children: "Your website changes are saved" }) 7737 ] }) 7738 ), children: sseChat.historyError }), 7739 !loading && !sseChat.historyError && agentMessages.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center justify-center py-12 text-center", children: [ 7740 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-4 rounded-full bg-gray-50 p-4", children: /* @__PURE__ */ jsxRuntimeExports.jsx(MessageSquare, { className: "h-8 w-8 text-gray-300" }) }),
7741 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "mb-2 text-lg font-medium text-gray-400", children: "Start a conversation" }), 7742 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "max-w-xs text-sm text-gray-300", children: "Ask me to edit your website, add components, or make any changes you'd like" }) 7743 ] }), 7744 displayMessages.map((message, index) => { 7745 const stackPosition = getMessageStackPosition(displayMessages, index) ?? "solo"; 7746 const continuesStack = messageContinuesStack(displayMessages, index); 7747 const staggerStep = Math.min(index, 4); 7748 const staggerDelay = `${staggerStep * 20}ms`; 7749 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn$1("msg-row-enter flex w-full min-w-0", message.role === "user" && "justify-end", index > 0 && (continuesStack ? "mt-1" : "mt-4")), style: { 7750 animationDelay: staggerDelay 7751 }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(SandboxChatMessage, { message, stackPosition, onRestore: (myWebsiteAccess == null ? void 0 : myWebsiteAccess.canEdit) ? handleRestore : void 0, isRestoring, isAIResponding }) }, `${message.id}-${index}`); 7752 }),
7753 /* @__PURE__ */ jsxRuntimeExports.jsx(TodoChecklist, { messages: agentMessages }), 7754 /* @__PURE__ */ jsxRuntimeExports.jsx(ThinkingIndicator, { thinking }), 7755 pendingApproval && /* @__PURE__ */ jsxRuntimeExports.jsx(SystemCard, { accent: "pending-action", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(ShieldCheck, { className: "h-5 w-5" }), title: "Approval required", actions: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [ 7756 /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { size: "sm", disabled: !!approvalSubmitting, onClick: () => respondToApproval(pendingApproval.approvalId, true), children: (approvalSubmitting == null ? void 0 : approvalSubmitting.approved) === true ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 7757 /* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: "fa-solid fa-spinner fa-spin mr-2" }), 7758 "Approvingâ¦" 7759 ] }) : "Approve" }), 7760 /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { size: "sm", variant: "outline", disabled: !!approvalSubmitting, onClick: () => respondToApproval(pendingApproval.approvalId, false), children: (approvalSubmitting == null ? void 0 : approvalSubmitting.approved) === false ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
7761 /* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: "fa-solid fa-spinner fa-spin mr-2" }), 7762 "Denyingâ¦" 7763 ] }) : "Deny" }) 7764 ] }), children: pendingApproval.description }), 7765 pendingQuestion && /* @__PURE__ */ jsxRuntimeExports.jsx(AskUserQuestionCard, { pendingQuestion, onSubmit: respondToQuestion }), 7766 maxTurnsHit && !agentIsLoading && !pendingApproval && !pendingQuestion && /* @__PURE__ */ jsxRuntimeExports.jsx(SystemCard, { accent: "info", actions: /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { size: "sm", onClick: handleContinueAfterMaxTurns, children: "Continue" }), children: "The assistant paused after a long run. Continue if it was on the right track, or send a new message to redirect." }), 7767 !chatStopped && agentIsLoading && // The pinned live-status line (above the composer) and the 7768 // in-stream ThinkingIndicator already surface the working / 7769 // thinking state. Suppress this message-list placeholder 7770 // whenever either is showing, so two shimmers never stack 7771 // (e.g. "Thinking" over "Workingâ¦"). 7772 !(pinnedStatusText && isExpanded) && !(thinking == null ? void 0 : thinking.active) && // Show the waiting indicator until the current turn's 7773 // assistant message actually has content. It's hidden 7774 // once the first text/tool arrives â at that point the 7775 // assistant bubble renders its own progress (spinner 7776 // icons on in-flight tool rows). 7777 (() => { 7778 const last = agentMessages[agentMessages.length - 1]; 7779 if ((last == null ? void 0 : last.role) !== "assistant") return /* @__PURE__ */ jsxRuntimeExports.jsx(SandboxWaitingIndicator, {}); 7780 const hasContent = (last.parts || []).some((p) => { 7781 if (p.type === "text") return (p.text || "").length > 0; 7782 return true; 7783 }); 7784 return hasContent ? null : /* @__PURE__ */ jsxRuntimeExports.jsx(SandboxWaitingIndicator, {}); 7785 })(), 7786 isSummarizing && /* @__PURE__ */ jsxRuntimeExports.jsx(SystemCard, { accent: "info", icon: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-5 w-5 animate-spin rounded-full border-2 border-blue-500 border-t-transparent" }), title: "Summarizing conversation...", children: "This usually takes a few seconds" }),
7787 maxStepsReached && /* @__PURE__ */ jsxRuntimeExports.jsx(SystemLine, { accent: "warning", children: "The AI has reached its maximum number of steps. Please try rephrasing your request or breaking it into smaller tasks." }), 7788 showResetSuggestion && /* @__PURE__ */ jsxRuntimeExports.jsx(SystemLine, { accent: "warning", children: "The chat history is getting too long. Please reset the chat soon for the best performance." }), 7789 chatSessionInProgress && /* @__PURE__ */ jsxRuntimeExports.jsx(SystemLine, { accent: "info", children: "Please wait for the current chat response to complete before sending another message." }), 7790 chatErrorMessage && !isSessionExpired && !isOutOfCredits && /* @__PURE__ */ jsxRuntimeExports.jsx(SystemCard, { accent: "danger", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { className: "h-4 w-4" }), title: chatErrorMessage, actions: canResetFromError || lastUserMessageRef.current ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2", children: [ 7791 canResetFromError && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
7792 /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { size: "sm", variant: "outline", className: "h-7 border-red-300 bg-white px-3 text-xs text-red-700 hover:bg-red-100", onClick: handleClearAgentHistory, children: "Reset Chat" }), 7793 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-red-600", children: "Your website changes are saved" }) 7794 ] }), 7795 isOverloadedError && lastUserMessageRef.current && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [ 7796 /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { size: "sm", variant: "outline", className: "h-7 border-red-300 bg-white px-3 text-xs text-red-700 hover:bg-red-100", onClick: handleRetryLastMessage, children: "Try again" }), 7797 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-red-600", children: "The AI service may be temporarily busy" }) 7798 ] }) 7799 ] }) : void 0 }), 7800 agentMessages.length > 0 && !isAIResponding && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-end px-1 py-0.5", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ResetButton, { onReset: handleClearAgentHistory }) }),
7801 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: messagesEndRef }) 7802 ] }) }), 7803 showScrollToBottom && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "pointer-events-none sticky bottom-0 z-10 flex justify-end pb-2 pr-4", style: { 7804 marginTop: "-3rem" 7805 }, children: /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => scrollToBottom(), className: "pointer-events-auto flex h-10 w-10 items-center justify-center rounded-full bg-primary text-primary-foreground shadow-lg transition-all hover:bg-primary/90 hover:shadow-xl", "aria-label": "Scroll to bottom", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowDown, { className: "h-5 w-5" }) }) }) 7806 ] }), 7807 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white pb-2", children: [ 7808 pinnedStatusText && isExpanded && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "state-card-fade flex items-center px-4 py-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-shimmer text-xs font-medium", children: pinnedStatusText }) }),
7809 !isExpanded && isChatLimitReached && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between border-b border-red-200 bg-red-50 px-4 py-3", children: [ 7810 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [ 7811 /* @__PURE__ */ jsxRuntimeExports.jsx(MessageSquare, { className: "h-4 w-4 text-red-500" }), 7812 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-medium text-red-700", children: "Chat limit reached" }) 7813 ] }), 7814 /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { size: "sm", onClick: () => { 7815 navigate(namedRoutes.reactWebsiteChatSettingsTab({ 7816 id: websiteId, 7817 tab: "plan" 7818 })); 7819 }, className: "h-7 px-3 text-xs", children: "Upgrade" }) 7820 ] }), 7821 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-2", children: [ 7822 !isChatLimitReached && isFree && !isAgencyUser && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-2 flex items-center justify-between px-4 text-xs text-gray-500", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [ 7823 remainingChats, 7824 " free chat", 7825 remainingChats !== 1 ? "s" : "", 7826 " remaining" 7827 ] }) }),
7828 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-2 flex justify-end px-4 lg:hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { variant: "ghost", size: "icon", onClick: () => setIsExpanded(!isExpanded), className: "h-6 w-6 text-gray-400 hover:text-gray-600", children: isExpanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronsDown, { className: "h-4 w-4" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronsUp, { className: "h-4 w-4" }) }) }), 7829 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative mx-2 flex flex-col space-y-2", children: [ 7830 !isContainerReady && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 rounded-md bg-gray-50 px-3 py-2 text-sm text-gray-500", children: [ 7831 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-3 w-3 animate-spin rounded-full border-2 border-gray-400 border-t-transparent" }), 7832 "Preparing preview server..." 7833 ] }), 7834 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { "aria-live": "polite", "aria-atomic": "true", className: "sr-only", children: pickerLiveMessage }),
7835 /* @__PURE__ */ jsxRuntimeExports.jsx(ComposerNoticeStack, { tray: composerNoticeTray, bubbles: composerNoticeBubbles, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex flex-col", children: [ 7836 /* @__PURE__ */ jsxRuntimeExports.jsx(QueuedMessageList, { items: queue.items, mode, pausedReason, onUpdateText: handleUpdateQueuedText, onRemove: handleRemoveQueued, onSendNow: handleSendQueuedNow, onEditingChange: setEditingQueuedId }), 7837 /* @__PURE__ */ jsxRuntimeExports.jsx(ComposerAttachmentTray, { selectedElement, images, onClearSelection: handleChipClear, onRemoveImage: handleRemoveImage }), 7838 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: cn$1("relative z-[1] rounded-[14px] border border-[#1B1A22]/[0.07] bg-white shadow-[inset_0_1px_0_rgba(255,255,255,0.9),0_1px_2px_rgba(27,26,34,0.03),0_2px_6px_-2px_rgba(27,26,34,0.04),0_10px_24px_-16px_rgba(27,26,34,0.10)] transition-all", composerLocked && "bg-muted"), children: [ 7839 inlineEditInProgress && /* @__P
7839URE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 z-10 flex items-center justify-center rounded-[14px] bg-background/80 backdrop-blur-[1px]", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "px-4 text-center text-sm font-medium text-muted-foreground", children: "Finish your edits in the preview to chat" }) }), 7840 /* @__PURE__ */ jsxRuntimeExports.jsx(Textarea, { ref: textareaRef, className: cn$1("min-h-[44px] w-full resize-none overflow-y-auto rounded-t-md border-0 px-3 py-3 text-[16px] sm:text-sm", "bg-transparent placeholder:text-muted-foreground", "focus-visible:ring-0 focus-visible:ring-offset-0"), placeholder: !isContainerReady ? "Waiting for preview server..." : pendingApproval ? "Waiting for you to approve or deny the request aboveâ¦" : pendingQuestion ? "Waiting for you to answer the question aboveâ¦" : isChatLimitReached || isRestoring || !(myWebsiteAccess == null ? void 0 : myWebsiteAccess.canEdit) ? "" : mode === "busy" ? "Queue your next messageâ¦" : "What would you like to change...", disabled: composerLocked, rows: 1, value: inputValue, onChange: (e) => { 7841 setInputValue(e.target.value); 7842 }, onKeyDown: handleKeyDown, onFocus: () => setIsExpanded(true), onPaste: (e) => { 7843 var _a2, _b2; 7844 const items = Array.from(((_a2 = e.clipboardData) == null ? void 0 : _a2.items) ?? []); 7845 const imageFiles = items.filter((it) => it.kind === "file" && it.type.startsWith("image/")).map((it) => it.getAsFile()).filter((f) => !!f); 7846 if (imageFiles.length === 0) return; 7847 e.preventDefault(); 7848 const text = ((_b2 = e.clipboardData) == null ? void 0 : _b2.getData("text/plain")) ?? ""; 7849 if (text) document.execCommand("insertText", false, text); 7850 const extForMime = { 7851 jpeg: "jpg", 7852 "svg+xml": "svg", 7853 "vnd.microsoft.icon": "ico" 7854 }; 7855 const dt = new DataTransfer(); 7856 imageFiles.forEach((f) => { 7857 const subtype = f.type.split("/")[1] || "png"; 7858 const ext = extForMime[subtype] ?? subtype; 7859 const named = f.name && f.name !== "image.png" ? f : new File([f], `pasted-${Date.now()}.${ext}`, { 7860 type: f.type 7861 }); 7862 dt.items.add(named); 7863 }); 7864 handleFileChange(dt.files); 7865 } }),
7866 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between px-3 py-2", children: [ 7867 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1", children: [ 7868 /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [ 7869 /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { size: "icon", variant: "ghost", type: "button", "aria-label": "Upload images", className: "h-8 w-8 text-muted-foreground hover:text-foreground disabled:opacity-50", disabled: composerLocked, onClick: () => { 7870 var _a2; 7871 return (_a2 = fileInputRef.current) == null ? void 0 : _a2.click(); 7872 }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Paperclip, { className: "h-5 w-5" }) }) }), 7873 /* @__P
7873URE__ */ jsxRuntimeExports.jsx(TooltipContent, { side: "top", align: "center", children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: "Upload images" }) }) 7874 ] }) }), 7875 /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [ 7876 /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { size: "icon", variant: "ghost", type: "button", "aria-label": "Open gallery", className: "h-8 w-8 text-muted-foreground hover:text-foreground disabled:opacity-50", disabled: composerLocked, onClick: () => setIsGalleryOpen(true), children: /* @__PURE__ */ jsxRuntimeExports.jsx(Images, { className: "h-5 w-5" }) }) }), 7877 /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { side: "top", align: "center", children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: "Open gallery" }) }) 7878 ] }) }), 7879 !pickerButtonHidden && /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
7880 /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { size: "icon", variant: "ghost", type: "button", "aria-pressed": pickModeActive, "aria-label": pickerButtonTooltip, onClick: handlePickerButtonClick, disabled: pickerButtonDisabled, className: cn$1( 7881 "h-8 w-8 text-muted-foreground hover:text-foreground disabled:opacity-50", 7882 // Active-state via color only â no motion 7883 // (avoids prefers-reduced-motion carve-outs). 7884 pickModeActive && "hover:bg-primary/15 bg-primary/10 text-primary hover:text-primary" 7885 ), children: /* @__PURE__ */ jsxRuntimeExports.jsx(MousePointerClick, { className: "h-5 w-5" }) }) }), 7886 /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { side: "top", align: "center", children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: pickerButtonTooltip }) }) 7887 ] }) }) 7888 ] }),
7889 isAIResponding || isSubmitting ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1", children: [ 7890 isAIResponding && isFirstGenerationInProgress ? /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [ 7891 /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "inline-flex", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { size: "icon", variant: "ghost", type: "button", "aria-label": "Building your websiteâ¦", disabled: true, className: "h-8 w-8 rounded-full text-gray-400 disabled:opacity-100", style: { 7892 background: "linear-gradient(180deg, #F3F4F6 0%, #E5E7EB 100%)", 7893 boxShadow: "inset 0 0 0 1px rgba(0,0,0,0.06)" 7894 }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Square, { className: "h-3.5 w-3.5", fill: "currentColor", strokeWidth: 0 }) }) }) }),
7895 /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { side: "top", align: "center", children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: "Hang tight â your website is still building." }) }) 7896 ] }) }) : isAIResponding ? ( 7897 // Stop is a secondary action â keep it quiet 7898 // (muted gray, not a glowing primary button). 7899 /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { size: "icon", variant: "ghost", type: "button", "aria-label": "Stop", onClick: handleStopAgentChat, className: "h-8 w-8 rounded-full border border-gray-200 bg-white text-gray-500 hover:bg-gray-100 hover:text-gray-700 active:scale-[0.96]", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Square, { className: "h-3 w-3", fill: "currentColor", strokeWidth: 0 }) }) 7900 ) : ( 7901 // Sent, waiting for the turn to start streaming 7902 // â a working spinner so the composer reads as 7903 // busy; the message cannot be re-submitted, but 7904 // the next one can be queued beside it.
7905 /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { size: "icon", variant: "ghost", type: "button", "aria-label": "Sendingâ¦", disabled: true, className: "h-8 w-8 rounded-full text-muted-foreground disabled:opacity-100", children: /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "h-4 w-4 animate-spin" }) }) 7906 ), 7907 /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [ 7908 /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "inline-flex", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 7909 Button, 7910 { 7911 size: "icon", 7912 variant: "ghost", 7913 type: "button", 7914 "aria-label": "Queue message", 7915 className: "h-8 w-8 text-muted-foreground hover:text-foreground disabled:opacity-50", 7916 disabled: mode !== "busy" || isUploadingImages || !inputValue.trim(), 7917 onClick: handleFormSubmit, 7918 children: /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowUp, { className: "h-5 w-5" }) 7919 } 7920 ) }) }),
7921 /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { side: "top", align: "center", children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: "Queue message â sends when the AI finishes" }) }) 7922 ] }) }) 7923 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { size: "icon", variant: "ghost", className: "h-8 w-8 text-muted-foreground hover:text-foreground disabled:opacity-50", disabled: isUploadingImages || isRestoring || !inputValue.trim() && images.length === 0 || isChatLimitReached || !(myWebsiteAccess == null ? void 0 : myWebsiteAccess.canEdit) || !!pendingApproval || !!pendingQuestion, onClick: handleFormSubmit, children: /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowUp, { className: "h-5 w-5" }) }) 7924 ] }) 7925 ] }) 7926 ] }) }), 7927 /* @__PURE__ */ jsxRuntimeExports.jsx("input", { ref: fileInputRef, type: "file", accept: ".jpg,.jpeg,.png,.gif,.webp,.bmp,.svg,.heic,.heif,.pdf,.glb", className: "hidden", multiple: true, onChange: (e) => { 7928 if (e.target.files) { 7929 handleFileChange(e.target.files); 7930 } 7931 } }) 7932 ] }),
7933 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "whitespace-nowrap px-4 pt-2 text-center text-[10px] text-gray-500 sm:whitespace-normal sm:text-xs sm:[text-wrap:balance]", children: "AI can make mistakes. Changes may not always apply as expected." }) 7934 ] }) 7935 ] }) 7936 ] }), 7937 /* @__PURE__ */ jsxRuntimeExports.jsx(Dialog, { open: isGalleryOpen && !!website, onOpenChange: (open) => { 7938 if (!open) setIsGalleryOpen(false); 7939 }, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogContent, { "aria-describedby": void 0, className: "h-[min(90vh,900px)] w-[min(95vw,1280px)] max-w-none overflow-y-auto p-6 [&>button.absolute]:hidden", children: [ 7940 /* @__PURE__ */ jsxRuntimeExports.jsx(DialogTitle, { className: "sr-only", children: "Gallery" }), 7941 website && /* @__PURE__ */ jsxRuntimeExports.jsx(Gallery, { websiteId: website.id, publicId: website.publicId || void 0, initialGalleryType: "images", initialKeyword: website.businessName || website.name || "", existingMedia: [], onClose: () => setIsGalleryOpen(false), onMediaSelected: handleInternalGalleryMediaSelection }) 7942 ] }) }) 7943 ] }); 7944}); 7945ReactChatPanel.displayName = "ReactChatPanel"; 7946const ReactChatPanel$1 = React.memo(ReactChatPanel); 7947const PublishWhileAgentBusyModal = ({ 7948 open, 7949 onPublish, 7950 onWait 7951}) => { 7952 if (!open) return null; 7953 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4", role: "dialog", "aria-modal": "true", "aria-labelledby": "publish-while-busy-title", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full max-w-md rounded-2xl bg-white p-8 shadow-2xl", children: [ 7954 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-6", children: [
7955 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-blue-50", children: /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "h-6 w-6 animate-spin text-blue-600" }) }), 7956 /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { id: "publish-while-busy-title", className: "mb-2 text-xl font-bold text-gray-900", children: "The AI is still working" }), 7957 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm leading-relaxed text-gray-600", children: "If you publish now, the changes the AI is making right now won't be on your live site. We recommend waiting until it finishes, then publishing." }) 7958 ] }), 7959 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-3", children: [ 7960 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: onWait, className: "w-full rounded-lg bg-gradient-to-r from-blue-600 to-indigo-600 px-6 py-3 font-semibold text-white shadow-md transition-all hover:from-blue-700 hover:to-indigo-700 hover:shadow-lg", children: "Wait" }),
7961 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: onPublish, className: "w-full rounded-lg border border-gray-300 px-6 py-3 font-medium text-gray-700 transition-colors hover:bg-gray-50", children: "Publish anyway" }) 7962 ] }) 7963 ] }) }); 7964}; 7965// @__NO_SIDE_EFFECTS__ 7966function createSlot(ownerName) { 7967 const SlotClone = /* @__PURE__ */ createSlotClone(ownerName); 7968 const Slot2 = reactExports.forwardRef((props, forwardedRef) => { 7969 const { children, ...slotProps } = props; 7970 const childrenArray = reactExports.Children.toArray(children); 7971 const slottable = childrenArray.find(isSlottable); 7972 if (slottable) { 7973 const newElement = slottable.props.children; 7974 const newChildren = childrenArray.map((child) => { 7975 if (child === slottable) { 7976 if (reactExports.Children.count(newElement) > 1) return reactExports.Children.only(null); 7977 return reactExports.isValidElement(newElement) ? newElement.props.children : null; 7978 } else { 7979 return child; 7980 } 7981 }); 7982 return /* @__PURE__ */ jsxRuntimeExports.jsx(SlotClone, { ...slotProps, ref: forwardedRef, children: reactExports.isValidElement(newElement) ? reactExports.cloneElement(newElement, void 0, newChildren) : null }); 7983 } 7984 return /* @__PURE__ */ jsxRuntimeExports.jsx(SlotClone, { ...slotProps, ref: forwardedRef, children }); 7985 }); 7986 Slot2.displayName = `${ownerName}.Slot`; 7987 return Slot2; 7988} 7989// @__NO_SIDE_EFFECTS__ 7990function createSlotClone(ownerName) { 7991 const SlotClone = reactExports.forwardRef((props, forwardedRef) => { 7992 const { children, ...slotProps } = props; 7993 const childrenRef = reactExports.isValidElement(children) ? getElementRef$1(children) : void 0; 7994 const ref = useComposedRefs(childrenRef, forwardedRef); 7995 if (reactExports.isValidElement(children)) { 7996 const props2 = mergeProps(slotProps, children.props); 7997 if (children.type !== reactExports.Fragment) { 7998 props2.ref = ref; 7999 } 8000 return reactExports.cloneElement(children, props2); 8001 } 8002 return reactExports.Children.count(children) > 1 ? reactExports.Children.only(null) : null; 8003 }); 8004 SlotClone.displayName = `${ownerName}.SlotClone`; 8005 return SlotClone; 8006} 8007var SLOTTABLE_IDENTIFIER = Symbol("radix.slottable"); 8008function isSlottable(child) { 8009 return reactExports.isValidElement(child) && typeof child.type === "function" && "__radixId" in child.type && child.type.__radixId === SLOTTABLE_IDENTIFIER; 8010} 8011function mergeProps(slotProps, childProps) { 8012 const overrideProps = { ...childProps }; 8013 for (const propName in childProps) { 8014 const slotPropValue = slotProps[propName]; 8015 const childPropValue = childProps[propName]; 8016 const isHandler = /^on[A-Z]/.test(propName); 8017 if (isHandler) { 8018 if (slotPropValue && childPropValue) { 8019 overrideProps[propName] = (...args) => { 8020 const result = childPropValue(...args); 8021 slotPropValue(...args); 8022 return result; 8023 }; 8024 } else if (slotPropValue) { 8025 overrideProps[propName] = slotPropValue; 8026 } 8027 } else if (propName === "style") { 8028 overrideProps[propName] = { ...slotPropValue, ...childPropValue }; 8029 } else if (propName === "className") { 8030 overrideProps[propName] = [slotPropValue, childPropValue].filter(Boolean).join(" "); 8031 } 8032 } 8033 return { ...slotProps, ...overrideProps }; 8034} 8035function getElementRef$1(element) { 8036 var _a, _b; 8037 let getter = (_a = Object.getOwnPropertyDescriptor(element.props, "ref")) == null ? void 0 : _a.get; 8038 let mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning; 8039 if (mayWarn) { 8040 return element.ref; 8041 } 8042 getter = (_b = Object.getOwnPropertyDescriptor(element, "ref")) == null ? void 0 : _b.get; 8043 mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning; 8044 if (mayWarn) { 8045 return element.props.ref; 8046 } 8047 return element.props.ref || element.ref; 8048} 8049var NODES = [ 8050 "a", 8051 "button", 8052 "div", 8053 "form", 8054 "h2", 8055 "h3", 8056 "img", 8057 "input", 8058 "label", 8059 "li", 8060 "nav", 8061 "ol", 8062 "p", 8063 "select", 8064 "span", 8065 "svg", 8066 "ul" 8067]; 8068var Primitive = NODES.reduce((primitive, node) => { 8069 const Slot2 = /* @__PURE__ */ createSlot(`Primitive.${node}`); 8070 const Node = reactExports.forwardRef((props, forwardedRef) => { 8071 const { asChild, ...primitiveProps } = props; 8072 const Comp = asChild ? Slot2 : node; 8073 if (typeof window !== "undefined") { 8074 window[Symbol.for("radix-ui")] = true; 8075 } 8076 return /* @__PURE__ */ jsxRuntimeExports.jsx(Comp, { ...primitiveProps, ref: forwardedRef }); 8077 }); 8078 Node.displayName = `Primitive.${node}`; 8079 return { ...primitive, [node]: Node }; 8080}, {}); 8081function dispatchDiscreteCustomEvent(target, event) { 8082 if (target) reactDomExports.flushSync(() => target.dispatchEvent(event)); 8083} 8084function useCallbackRef(callback) { 8085 const callbackRef = reactExports.useRef(callback); 8086 reactExports.useEffect(() => { 8087 callbackRef.current = callback; 8088 }); 8089 return reactExports.useMemo(() => (...args) => { 8090 var _a; 8091 return (_a = callbackRef.current) == null ? void 0 : _a.call(callbackRef, ...args); 8092 }, []); 8093} 8094var DISMISSABLE_LAYER_NAME = "DismissableLayer"; 8095var CONTEXT_UPDATE = "dismissableLayer.update"; 8096var POINTER_DOWN_OUTSIDE = "dismissableLayer.pointerDownOutside"; 8097var FOCUS_OUTSIDE = "dismissableLayer.focusOutside"; 8098var originalBodyPointerEvents; 8099var DismissableLayerContext = reactExports.createContext({ 8100 layers: /* @__PURE__ */ new Set(), 8101 layersWithOutsidePointerEventsDisabled: /* @__PURE__ */ new Set(), 8102 branches: /* @__PURE__ */ new Set() 8103}); 8104var DismissableLayer = reactExports.forwardRef( 8105 (props, forwardedRef) => { 8106 const { 8107 disableOutsidePointerEvents = false, 8108 onEscapeKeyDown, 8109 onPointerDownOutside, 8110 onFocusOutside, 8111 onInteractOutside, 8112 onDismiss, 8113 ...layerProps 8114 } = props; 8115 const context = reactExports.useContext(DismissableLayerContext); 8116 const [node, setNode] = reactExports.useState(null); 8117 const ownerDocument = (node == null ? void 0 : node.ownerDocument) ?? (globalThis == null ? void 0 : globalThis.document); 8118 const [, force] = reactExports.useState({});
vendor: 5,804 bytes, lines 8119-8245
8119 const composedRefs = useComposedRefs(forwardedRef, (node2) => setNode(node2)); 8120 const layers = Array.from(context.layers); 8121 const [highestLayerWithOutsidePointerEventsDisabled] = [...context.layersWithOutsidePointerEventsDisabled].slice(-1); 8122 const highestLayerWithOutsidePointerEventsDisabledIndex = layers.indexOf(highestLayerWithOutsidePointerEventsDisabled); 8123 const index = node ? layers.indexOf(node) : -1; 8124 const isBodyPointerEventsDisabled = context.layersWithOutsidePointerEventsDisabled.size > 0; 8125 const isPointerEventsEnabled = index >= highestLayerWithOutsidePointerEventsDisabledIndex; 8126 const pointerDownOutside = usePointerDownOutside((event) => { 8127 const target = event.target; 8128 const isPointerDownOnBranch = [...context.branches].some((branch) => branch.contains(target)); 8129 if (!isPointerEventsEnabled || isPointerDownOnBranch) return; 8130 onPointerDownOutside == null ? void 0 : onPointerDownOutside(event); 8131 onInteractOutside == null ? void 0 : onInteractOutside(event); 8132 if (!event.defaultPrevented) onDismiss == null ? void 0 : onDismiss(); 8133 }, ownerDocument); 8134 const focusOutside = useFocusOutside((event) => { 8135 const target = event.target; 8136 const isFocusInBranch = [...context.branches].some((branch) => branch.contains(target)); 8137 if (isFocusInBranch) return; 8138 onFocusOutside == null ? void 0 : onFocusOutside(event); 8139 onInteractOutside == null ? void 0 : onInteractOutside(event); 8140 if (!event.defaultPrevented) onDismiss == null ? void 0 : onDismiss(); 8141 }, ownerDocument); 8142 useEscapeKeydown((event) => { 8143 const isHighestLayer = index === context.layers.size - 1; 8144 if (!isHighestLayer) return; 8145 onEscapeKeyDown == null ? void 0 : onEscapeKeyDown(event); 8146 if (!event.defaultPrevented && onDismiss) { 8147 event.preventDefault(); 8148 onDismiss(); 8149 } 8150 }, ownerDocument); 8151 reactExports.useEffect(() => { 8152 if (!node) return; 8153 if (disableOutsidePointerEvents) { 8154 if (context.layersWithOutsidePointerEventsDisabled.size === 0) { 8155 originalBodyPointerEvents = ownerDocument.body.style.pointerEvents; 8156 ownerDocument.body.style.pointerEvents = "none"; 8157 } 8158 context.layersWithOutsidePointerEventsDisabled.add(node); 8159 } 8160 context.layers.add(node); 8161 dispatchUpdate(); 8162 return () => { 8163 if (disableOutsidePointerEvents && context.layersWithOutsidePointerEventsDisabled.size === 1) { 8164 ownerDocument.body.style.pointerEvents = originalBodyPointerEvents; 8165 } 8166 }; 8167 }, [node, ownerDocument, disableOutsidePointerEvents, context]); 8168 reactExports.useEffect(() => { 8169 return () => { 8170 if (!node) return; 8171 context.layers.delete(node); 8172 context.layersWithOutsidePointerEventsDisabled.delete(node); 8173 dispatchUpdate(); 8174 }; 8175 }, [node, context]); 8176 reactExports.useEffect(() => { 8177 const handleUpdate = () => force({}); 8178 document.addEventListener(CONTEXT_UPDATE, handleUpdate); 8179 return () => document.removeEventListener(CONTEXT_UPDATE, handleUpdate); 8180 }, []); 8181 return /* @__PURE__ */ jsxRuntimeExports.jsx( 8182 Primitive.div, 8183 { 8184 ...layerProps, 8185 ref: composedRefs, 8186 style: { 8187 pointerEvents: isBodyPointerEventsDisabled ? isPointerEventsEnabled ? "auto" : "none" : void 0, 8188 ...props.style 8189 }, 8190 onFocusCapture: composeEventHandlers(props.onFocusCapture, focusOutside.onFocusCapture), 8191 onBlurCapture: composeEventHandlers(props.onBlurCapture, focusOutside.onBlurCapture), 8192 onPointerDownCapture: composeEventHandlers( 8193 props.onPointerDownCapture, 8194 pointerDownOutside.onPointerDownCapture 8195 ) 8196 } 8197 ); 8198 } 8199); 8200DismissableLayer.displayName = DISMISSABLE_LAYER_NAME; 8201var BRANCH_NAME = "DismissableLayerBranch"; 8202var DismissableLayerBranch = reactExports.forwardRef((props, forwardedRef) => { 8203 const context = reactExports.useContext(DismissableLayerContext); 8204 const ref = reactExports.useRef(null); 8205 const composedRefs = useComposedRefs(forwardedRef, ref); 8206 reactExports.useEffect(() => { 8207 const node = ref.current; 8208 if (node) { 8209 context.branches.add(node); 8210 return () => { 8211 context.branches.delete(node); 8212 }; 8213 } 8214 }, [context.branches]); 8215 return /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.div, { ...props, ref: composedRefs }); 8216}); 8217DismissableLayerBranch.displayName = BRANCH_NAME; 8218function usePointerDownOutside(onPointerDownOutside, ownerDocument = globalThis == null ? void 0 : globalThis.document) { 8219 const handlePointerDownOutside = useCallbackRef(onPointerDownOutside); 8220 const isPointerInsideReactTreeRef = reactExports.useRef(false); 8221 const handleClickRef = reactExports.useRef(() => { 8222 }); 8223 reactExports.useEffect(() => { 8224 const handlePointerDown = (event) => { 8225 if (event.target && !isPointerInsideReactTreeRef.current) { 8226 let handleAndDispatchPointerDownOutsideEvent2 = function() { 8227 handleAndDispatchCustomEvent( 8228 POINTER_DOWN_OUTSIDE, 8229 handlePointerDownOutside, 8230 eventDetail, 8231 { discrete: true } 8232 ); 8233 }; 8234 const eventDetail = { originalEvent: event }; 8235 if (event.pointerType === "touch") { 8236 ownerDocument.removeEventListener("click", handleClickRef.current); 8237 handleClickRef.current = handleAndDispatchPointerDownOutsideEvent2; 8238 ownerDocument.addEventListener("click", handleClickRef.current, { once: true }); 8239 } else { 8240 handleAndDispatchPointerDownOutsideEvent2(); 8241 } 8242 } else { 8243 ownerDocument.removeEventListener("click", handleClickRef.current); 8244 } 8245 isPointerInsideReactTreeRef.current = false;
vendor: 9,270 bytes, lines 8246-8473
8246 }; 8247 const timerId = window.setTimeout(() => { 8248 ownerDocument.addEventListener("pointerdown", handlePointerDown); 8249 }, 0); 8250 return () => { 8251 window.clearTimeout(timerId); 8252 ownerDocument.removeEventListener("pointerdown", handlePointerDown); 8253 ownerDocument.removeEventListener("click", handleClickRef.current); 8254 }; 8255 }, [ownerDocument, handlePointerDownOutside]); 8256 return { 8257 // ensures we check React component tree (not just DOM tree) 8258 onPointerDownCapture: () => isPointerInsideReactTreeRef.current = true 8259 }; 8260} 8261function useFocusOutside(onFocusOutside, ownerDocument = globalThis == null ? void 0 : globalThis.document) { 8262 const handleFocusOutside = useCallbackRef(onFocusOutside); 8263 const isFocusInsideReactTreeRef = reactExports.useRef(false); 8264 reactExports.useEffect(() => { 8265 const handleFocus = (event) => { 8266 if (event.target && !isFocusInsideReactTreeRef.current) { 8267 const eventDetail = { originalEvent: event }; 8268 handleAndDispatchCustomEvent(FOCUS_OUTSIDE, handleFocusOutside, eventDetail, { 8269 discrete: false 8270 }); 8271 } 8272 }; 8273 ownerDocument.addEventListener("focusin", handleFocus); 8274 return () => ownerDocument.removeEventListener("focusin", handleFocus); 8275 }, [ownerDocument, handleFocusOutside]); 8276 return { 8277 onFocusCapture: () => isFocusInsideReactTreeRef.current = true, 8278 onBlurCapture: () => isFocusInsideReactTreeRef.current = false 8279 }; 8280} 8281function dispatchUpdate() { 8282 const event = new CustomEvent(CONTEXT_UPDATE); 8283 document.dispatchEvent(event); 8284} 8285function handleAndDispatchCustomEvent(name, handler, detail, { discrete }) { 8286 const target = detail.originalEvent.target; 8287 const event = new CustomEvent(name, { bubbles: false, cancelable: true, detail }); 8288 if (handler) target.addEventListener(name, handler, { once: true }); 8289 if (discrete) { 8290 dispatchDiscreteCustomEvent(target, event); 8291 } else { 8292 target.dispatchEvent(event); 8293 } 8294} 8295var count = 0; 8296function useFocusGuards() { 8297 reactExports.useEffect(() => { 8298 const edgeGuards = document.querySelectorAll("[data-radix-focus-guard]"); 8299 document.body.insertAdjacentElement("afterbegin", edgeGuards[0] ?? createFocusGuard()); 8300 document.body.insertAdjacentElement("beforeend", edgeGuards[1] ?? createFocusGuard()); 8301 count++; 8302 return () => { 8303 if (count === 1) { 8304 document.querySelectorAll("[data-radix-focus-guard]").forEach((node) => node.remove()); 8305 } 8306 count--; 8307 }; 8308 }, []); 8309} 8310function createFocusGuard() { 8311 const element = document.createElement("span"); 8312 element.setAttribute("data-radix-focus-guard", ""); 8313 element.tabIndex = 0; 8314 element.style.outline = "none"; 8315 element.style.opacity = "0"; 8316 element.style.position = "fixed"; 8317 element.style.pointerEvents = "none"; 8318 return element; 8319} 8320var AUTOFOCUS_ON_MOUNT = "focusScope.autoFocusOnMount"; 8321var AUTOFOCUS_ON_UNMOUNT = "focusScope.autoFocusOnUnmount"; 8322var EVENT_OPTIONS = { bubbles: false, cancelable: true }; 8323var FOCUS_SCOPE_NAME = "FocusScope"; 8324var FocusScope = reactExports.forwardRef((props, forwardedRef) => { 8325 const { 8326 loop = false, 8327 trapped = false, 8328 onMountAutoFocus: onMountAutoFocusProp, 8329 onUnmountAutoFocus: onUnmountAutoFocusProp, 8330 ...scopeProps 8331 } = props; 8332 const [container, setContainer] = reactExports.useState(null); 8333 const onMountAutoFocus = useCallbackRef(onMountAutoFocusProp); 8334 const onUnmountAutoFocus = useCallbackRef(onUnmountAutoFocusProp); 8335 const lastFocusedElementRef = reactExports.useRef(null); 8336 const composedRefs = useComposedRefs(forwardedRef, (node) => setContainer(node)); 8337 const focusScope = reactExports.useRef({ 8338 paused: false, 8339 pause() { 8340 this.paused = true; 8341 }, 8342 resume() { 8343 this.paused = false; 8344 } 8345 }).current; 8346 reactExports.useEffect(() => { 8347 if (trapped) { 8348 let handleFocusIn2 = function(event) { 8349 if (focusScope.paused || !container) return; 8350 const target = event.target; 8351 if (container.contains(target)) { 8352 lastFocusedElementRef.current = target; 8353 } else { 8354 focus(lastFocusedElementRef.current, { select: true }); 8355 } 8356 }, handleFocusOut2 = function(event) { 8357 if (focusScope.paused || !container) return; 8358 const relatedTarget = event.relatedTarget; 8359 if (relatedTarget === null) return; 8360 if (!container.contains(relatedTarget)) { 8361 focus(lastFocusedElementRef.current, { select: true }); 8362 } 8363 }, handleMutations2 = function(mutations) { 8364 const focusedElement = document.activeElement; 8365 if (focusedElement !== document.body) return; 8366 for (const mutation of mutations) { 8367 if (mutation.removedNodes.length > 0) focus(container); 8368 } 8369 }; 8370 document.addEventListener("focusin", handleFocusIn2); 8371 document.addEventListener("focusout", handleFocusOut2); 8372 const mutationObserver = new MutationObserver(handleMutations2); 8373 if (container) mutationObserver.observe(container, { childList: true, subtree: true }); 8374 return () => { 8375 document.removeEventListener("focusin", handleFocusIn2); 8376 document.removeEventListener("focusout", handleFocusOut2); 8377 mutationObserver.disconnect(); 8378 }; 8379 } 8380 }, [trapped, container, focusScope.paused]); 8381 reactExports.useEffect(() => { 8382 if (container) { 8383 focusScopesStack.add(focusScope); 8384 const previouslyFocusedElement = document.activeElement; 8385 const hasFocusedCandidate = container.contains(previouslyFocusedElement); 8386 if (!hasFocusedCandidate) { 8387 const mountEvent = new CustomEvent(AUTOFOCUS_ON_MOUNT, EVENT_OPTIONS); 8388 container.addEventListener(AUTOFOCUS_ON_MOUNT, onMountAutoFocus); 8389 container.dispatchEvent(mountEvent); 8390 if (!mountEvent.defaultPrevented) { 8391 focusFirst(removeLinks(getTabbableCandidates(container)), { select: true }); 8392 if (document.activeElement === previouslyFocusedElement) { 8393 focus(container); 8394 } 8395 } 8396 } 8397 return () => { 8398 container.removeEventListener(AUTOFOCUS_ON_MOUNT, onMountAutoFocus); 8399 setTimeout(() => { 8400 const unmountEvent = new CustomEvent(AUTOFOCUS_ON_UNMOUNT, EVENT_OPTIONS); 8401 container.addEventListener(AUTOFOCUS_ON_UNMOUNT, onUnmountAutoFocus); 8402 container.dispatchEvent(unmountEvent); 8403 if (!unmountEvent.defaultPrevented) { 8404 focus(previouslyFocusedElement ?? document.body, { select: true }); 8405 } 8406 container.removeEventListener(AUTOFOCUS_ON_UNMOUNT, onUnmountAutoFocus); 8407 focusScopesStack.remove(focusScope); 8408 }, 0); 8409 }; 8410 } 8411 }, [container, onMountAutoFocus, onUnmountAutoFocus, focusScope]); 8412 const handleKeyDown = reactExports.useCallback( 8413 (event) => { 8414 if (!loop && !trapped) return; 8415 if (focusScope.paused) return; 8416 const isTabKey = event.key === "Tab" && !event.altKey && !event.ctrlKey && !event.metaKey; 8417 const focusedElement = document.activeElement; 8418 if (isTabKey && focusedElement) { 8419 const container2 = event.currentTarget; 8420 const [first, last] = getTabbableEdges(container2); 8421 const hasTabbableElementsInside = first && last; 8422 if (!hasTabbableElementsInside) { 8423 if (focusedElement === container2) event.preventDefault(); 8424 } else { 8425 if (!event.shiftKey && focusedElement === last) { 8426 event.preventDefault(); 8427 if (loop) focus(first, { select: true }); 8428 } else if (event.shiftKey && focusedElement === first) { 8429 event.preventDefault(); 8430 if (loop) focus(last, { select: true }); 8431 } 8432 } 8433 } 8434 }, 8435 [loop, trapped, focusScope.paused] 8436 ); 8437 return /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.div, { tabIndex: -1, ...scopeProps, ref: composedRefs, onKeyDown: handleKeyDown }); 8438}); 8439FocusScope.displayName = FOCUS_SCOPE_NAME; 8440function focusFirst(candidates, { select = false } = {}) { 8441 const previouslyFocusedElement = document.activeElement; 8442 for (const candidate of candidates) { 8443 focus(candidate, { select }); 8444 if (document.activeElement !== previouslyFocusedElement) return; 8445 } 8446} 8447function getTabbableEdges(container) { 8448 const candidates = getTabbableCandidates(container); 8449 const first = findVisible(candidates, container); 8450 const last = findVisible(candidates.reverse(), container); 8451 return [first, last]; 8452} 8453function getTabbableCandidates(container) { 8454 const nodes = []; 8455 const walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, { 8456 acceptNode: (node) => { 8457 const isHiddenInput = node.tagName === "INPUT" && node.type === "hidden"; 8458 if (node.disabled || node.hidden || isHiddenInput) return NodeFilter.FILTER_SKIP; 8459 return node.tabIndex >= 0 ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP; 8460 } 8461 }); 8462 while (walker.nextNode()) nodes.push(walker.currentNode); 8463 return nodes; 8464} 8465function findVisible(elements, container) { 8466 for (const element of elements) { 8467 if (!isHidden(element, { upTo: container })) return element; 8468 } 8469} 8470function isHidden(node, { upTo }) { 8471 if (getComputedStyle(node).visibility === "hidden") return true; 8472 while (node) { 8473 if (upTo !== void 0 && node === upTo) return false;
vendor: 5,482 bytes, lines 8474-8615
8474 if (getComputedStyle(node).display === "none") return true; 8475 node = node.parentElement; 8476 } 8477 return false; 8478} 8479function isSelectableInput(element) { 8480 return element instanceof HTMLInputElement && "select" in element; 8481} 8482function focus(element, { select = false } = {}) { 8483 if (element && element.focus) { 8484 const previouslyFocusedElement = document.activeElement; 8485 element.focus({ preventScroll: true }); 8486 if (element !== previouslyFocusedElement && isSelectableInput(element) && select) 8487 element.select(); 8488 } 8489} 8490var focusScopesStack = createFocusScopesStack(); 8491function createFocusScopesStack() { 8492 let stack = []; 8493 return { 8494 add(focusScope) { 8495 const activeFocusScope = stack[0]; 8496 if (focusScope !== activeFocusScope) { 8497 activeFocusScope == null ? void 0 : activeFocusScope.pause(); 8498 } 8499 stack = arrayRemove(stack, focusScope); 8500 stack.unshift(focusScope); 8501 }, 8502 remove(focusScope) { 8503 var _a; 8504 stack = arrayRemove(stack, focusScope); 8505 (_a = stack[0]) == null ? void 0 : _a.resume(); 8506 } 8507 }; 8508} 8509function arrayRemove(array, item) { 8510 const updatedArray = [...array]; 8511 const index = updatedArray.indexOf(item); 8512 if (index !== -1) { 8513 updatedArray.splice(index, 1); 8514 } 8515 return updatedArray; 8516} 8517function removeLinks(items) { 8518 return items.filter((item) => item.tagName !== "A"); 8519} 8520var NAME = "Arrow"; 8521var Arrow$1 = reactExports.forwardRef((props, forwardedRef) => { 8522 const { children, width = 10, height = 5, ...arrowProps } = props; 8523 return /* @__PURE__ */ jsxRuntimeExports.jsx( 8524 Primitive.svg, 8525 { 8526 ...arrowProps, 8527 ref: forwardedRef, 8528 width, 8529 height, 8530 viewBox: "0 0 30 10", 8531 preserveAspectRatio: "none", 8532 children: props.asChild ? children : /* @__PURE__ */ jsxRuntimeExports.jsx("polygon", { points: "0,0 30,0 15,10" }) 8533 } 8534 ); 8535}); 8536Arrow$1.displayName = NAME; 8537var Root = Arrow$1; 8538var POPPER_NAME = "Popper"; 8539var [createPopperContext, createPopperScope] = createContextScope(POPPER_NAME); 8540var [PopperProvider, usePopperContext] = createPopperContext(POPPER_NAME); 8541var Popper = (props) => { 8542 const { __scopePopper, children } = props; 8543 const [anchor, setAnchor] = reactExports.useState(null); 8544 return /* @__PURE__ */ jsxRuntimeExports.jsx(PopperProvider, { scope: __scopePopper, anchor, onAnchorChange: setAnchor, children }); 8545}; 8546Popper.displayName = POPPER_NAME; 8547var ANCHOR_NAME$1 = "PopperAnchor"; 8548var PopperAnchor = reactExports.forwardRef( 8549 (props, forwardedRef) => { 8550 const { __scopePopper, virtualRef, ...anchorProps } = props; 8551 const context = usePopperContext(ANCHOR_NAME$1, __scopePopper); 8552 const ref = reactExports.useRef(null); 8553 const composedRefs = useComposedRefs(forwardedRef, ref); 8554 reactExports.useEffect(() => { 8555 context.onAnchorChange((virtualRef == null ? void 0 : virtualRef.current) || ref.current); 8556 }); 8557 return virtualRef ? null : /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.div, { ...anchorProps, ref: composedRefs }); 8558 } 8559); 8560PopperAnchor.displayName = ANCHOR_NAME$1; 8561var CONTENT_NAME$1 = "PopperContent"; 8562var [PopperContentProvider, useContentContext] = createPopperContext(CONTENT_NAME$1); 8563var PopperContent = reactExports.forwardRef( 8564 (props, forwardedRef) => { 8565 var _a, _b, _c, _d, _e, _f; 8566 const { 8567 __scopePopper, 8568 side = "bottom", 8569 sideOffset = 0, 8570 align = "center", 8571 alignOffset = 0, 8572 arrowPadding = 0, 8573 avoidCollisions = true, 8574 collisionBoundary = [], 8575 collisionPadding: collisionPaddingProp = 0, 8576 sticky = "partial", 8577 hideWhenDetached = false, 8578 updatePositionStrategy = "optimized", 8579 onPlaced, 8580 ...contentProps 8581 } = props; 8582 const context = usePopperContext(CONTENT_NAME$1, __scopePopper); 8583 const [content, setContent] = reactExports.useState(null); 8584 const composedRefs = useComposedRefs(forwardedRef, (node) => setContent(node)); 8585 const [arrow$1, setArrow] = reactExports.useState(null); 8586 const arrowSize = useSize(arrow$1); 8587 const arrowWidth = (arrowSize == null ? void 0 : arrowSize.width) ?? 0; 8588 const arrowHeight = (arrowSize == null ? void 0 : arrowSize.height) ?? 0; 8589 const desiredPlacement = side + (align !== "center" ? "-" + align : ""); 8590 const collisionPadding = typeof collisionPaddingProp === "number" ? collisionPaddingProp : { top: 0, right: 0, bottom: 0, left: 0, ...collisionPaddingProp }; 8591 const boundary = Array.isArray(collisionBoundary) ? collisionBoundary : [collisionBoundary]; 8592 const hasExplicitBoundaries = boundary.length > 0; 8593 const detectOverflowOptions = { 8594 padding: collisionPadding, 8595 boundary: boundary.filter(isNotNull), 8596 // with `strategy: 'fixed'`, this is the only way to get it to respect boundaries 8597 altBoundary: hasExplicitBoundaries 8598 }; 8599 const { refs, floatingStyles, placement, isPositioned, middlewareData } = useFloating({ 8600 // default to `fixed` strategy so users don't have to pick and we also avoid focus scroll issues 8601 strategy: "fixed", 8602 placement: desiredPlacement, 8603 whileElementsMounted: (...args) => { 8604 const cleanup = autoUpdate(...args, { 8605 animationFrame: updatePositionStrategy === "always" 8606 }); 8607 return cleanup; 8608 }, 8609 elements: { 8610 reference: context.anchor 8611 }, 8612 middleware: [ 8613 offset({ mainAxis: sideOffset + arrowHeight, alignmentAxis: alignOffset }), 8614 avoidCollisions && shift({ 8615 mainAxis: true,
8616 crossAxis: false, 8617 limiter: sticky === "partial" ? limitShift() : void 0, 8618 ...detectOverflowOptions 8619 }), 8620 avoidCollisions && flip({ ...detectOverflowOptions }), 8621 size({ 8622 ...detectOverflowOptions, 8623 apply: ({ elements, rects, availableWidth, availableHeight }) => { 8624 const { width: anchorWidth, height: anchorHeight } = rects.reference; 8625 const contentStyle = elements.floating.style; 8626 contentStyle.setProperty("--radix-popper-available-width", `${availableWidth}px`); 8627 contentStyle.setProperty("--radix-popper-available-height", `${availableHeight}px`); 8628 contentStyle.setProperty("--radix-popper-anchor-width", `${anchorWidth}px`); 8629 contentStyle.setProperty("--radix-popper-anchor-height", `${anchorHeight}px`); 8630 } 8631 }), 8632 arrow$1 && arrow({ element: arrow$1, padding: arrowPadding }), 8633 transformOrigin({ arrowWidth, arrowHeight }), 8634 hideWhenDetached && hide({ strategy: "referenceHidden", ...detectOverflowOptions }) 8635 ] 8636 }); 8637 const [placedSide, placedAlign] = getSideAndAlignFromPlacement(placement); 8638 const handlePlaced = useCallbackRef(onPlaced); 8639 useLayoutEffect2(() => { 8640 if (isPositioned) { 8641 handlePlaced == null ? void 0 : handlePlaced(); 8642 } 8643 }, [isPositioned, handlePlaced]); 8644 const arrowX = (_a = middlewareData.arrow) == null ? void 0 : _a.x; 8645 const arrowY = (_b = middlewareData.arrow) == null ? void 0 : _b.y; 8646 const cannotCenterArrow = ((_c = middlewareData.arrow) == null ? void 0 : _c.centerOffset) !== 0; 8647 const [contentZIndex, setContentZIndex] = reactExports.useState(); 8648 useLayoutEffect2(() => { 8649 if (content) setContentZIndex(window.getComputedStyle(content).zIndex); 8650 }, [content]); 8651 return /* @__PURE__ */ jsxRuntimeExports.jsx( 8652 "div", 8653 { 8654 ref: refs.setFloating, 8655 "data-radix-popper-content-wrapper": "", 8656 style: { 8657 ...floatingStyles, 8658 transform: isPositioned ? floatingStyles.transform : "translate(0, -200%)", 8659 // keep off the page when measuring 8660 minWidth: "max-content", 8661 zIndex: contentZIndex, 8662 ["--radix-popper-transform-origin"]: [ 8663 (_d = middlewareData.transformOrigin) == null ? void 0 : _d.x, 8664 (_e = middlewareData.transformOrigin) == null ? void 0 : _e.y 8665 ].join(" "), 8666 // hide the content if using the hide middleware and should be hidden 8667 // set visibility to hidden and disable pointer events so the UI behaves 8668 // as if the PopperContent isn't there at all 8669 ...((_f = middlewareData.hide) == null ? void 0 : _f.referenceHidden) && { 8670 visibility: "hidden", 8671 pointerEvents: "none" 8672 } 8673 }, 8674 dir: props.dir, 8675 children: /* @__PURE__ */ jsxRuntimeExports.jsx( 8676 PopperContentProvider, 8677 { 8678 scope: __scopePopper, 8679 placedSide, 8680 onArrowChange: setArrow, 8681 arrowX, 8682 arrowY, 8683 shouldHideArrow: cannotCenterArrow, 8684 children: /* @__PURE__ */ jsxRuntimeExports.jsx( 8685 Primitive.div, 8686 { 8687 "data-side": placedSide, 8688 "data-align": placedAlign, 8689 ...contentProps, 8690 ref: composedRefs, 8691 style: { 8692 ...contentProps.style, 8693 // if the PopperContent hasn't been placed yet (not all measurements done) 8694 // we prevent animations so that users's animation don't kick in too early referring wrong sides 8695 animation: !isPositioned ? "none" : void 0 8696 } 8697 } 8698 ) 8699 } 8700 ) 8701 } 8702 ); 8703 } 8704); 8705PopperContent.displayName = CONTENT_NAME$1; 8706var ARROW_NAME$1 = "PopperArrow"; 8707var OPPOSITE_SIDE = { 8708 top: "bottom", 8709 right: "left", 8710 bottom: "top", 8711 left: "right" 8712}; 8713var PopperArrow = reactExports.forwardRef(function PopperArrow2(props, forwardedRef) { 8714 const { __scopePopper, ...arrowProps } = props; 8715 const contentContext = useContentContext(ARROW_NAME$1, __scopePopper); 8716 const baseSide = OPPOSITE_SIDE[contentContext.placedSide]; 8717 return ( 8718 // we have to use an extra wrapper because `ResizeObserver` (used by `useSize`) 8719 // doesn't report size as we'd expect on SVG elements. 8720 // it reports their bounding box which is effectively the largest path inside the SVG.
vendor: 12,509 bytes, lines 8721-9034
8721 /* @__PURE__ */ jsxRuntimeExports.jsx( 8722 "span", 8723 { 8724 ref: contentContext.onArrowChange, 8725 style: { 8726 position: "absolute", 8727 left: contentContext.arrowX, 8728 top: contentContext.arrowY, 8729 [baseSide]: 0, 8730 transformOrigin: { 8731 top: "", 8732 right: "0 0", 8733 bottom: "center 0", 8734 left: "100% 0" 8735 }[contentContext.placedSide], 8736 transform: { 8737 top: "translateY(100%)", 8738 right: "translateY(50%) rotate(90deg) translateX(-50%)", 8739 bottom: `rotate(180deg)`, 8740 left: "translateY(50%) rotate(-90deg) translateX(50%)" 8741 }[contentContext.placedSide], 8742 visibility: contentContext.shouldHideArrow ? "hidden" : void 0 8743 }, 8744 children: /* @__PURE__ */ jsxRuntimeExports.jsx( 8745 Root, 8746 { 8747 ...arrowProps, 8748 ref: forwardedRef, 8749 style: { 8750 ...arrowProps.style, 8751 // ensures the element can be measured correctly (mostly for if SVG) 8752 display: "block" 8753 } 8754 } 8755 ) 8756 } 8757 ) 8758 ); 8759}); 8760PopperArrow.displayName = ARROW_NAME$1; 8761function isNotNull(value) { 8762 return value !== null; 8763} 8764var transformOrigin = (options) => ({ 8765 name: "transformOrigin", 8766 options, 8767 fn(data) { 8768 var _a, _b, _c; 8769 const { placement, rects, middlewareData } = data; 8770 const cannotCenterArrow = ((_a = middlewareData.arrow) == null ? void 0 : _a.centerOffset) !== 0; 8771 const isArrowHidden = cannotCenterArrow; 8772 const arrowWidth = isArrowHidden ? 0 : options.arrowWidth; 8773 const arrowHeight = isArrowHidden ? 0 : options.arrowHeight; 8774 const [placedSide, placedAlign] = getSideAndAlignFromPlacement(placement); 8775 const noArrowAlign = { start: "0%", center: "50%", end: "100%" }[placedAlign]; 8776 const arrowXCenter = (((_b = middlewareData.arrow) == null ? void 0 : _b.x) ?? 0) + arrowWidth / 2; 8777 const arrowYCenter = (((_c = middlewareData.arrow) == null ? void 0 : _c.y) ?? 0) + arrowHeight / 2; 8778 let x = ""; 8779 let y = ""; 8780 if (placedSide === "bottom") { 8781 x = isArrowHidden ? noArrowAlign : `${arrowXCenter}px`; 8782 y = `${-arrowHeight}px`; 8783 } else if (placedSide === "top") { 8784 x = isArrowHidden ? noArrowAlign : `${arrowXCenter}px`; 8785 y = `${rects.floating.height + arrowHeight}px`; 8786 } else if (placedSide === "right") { 8787 x = `${-arrowHeight}px`; 8788 y = isArrowHidden ? noArrowAlign : `${arrowYCenter}px`; 8789 } else if (placedSide === "left") { 8790 x = `${rects.floating.width + arrowHeight}px`; 8791 y = isArrowHidden ? noArrowAlign : `${arrowYCenter}px`; 8792 } 8793 return { data: { x, y } }; 8794 } 8795}); 8796function getSideAndAlignFromPlacement(placement) { 8797 const [side, align = "center"] = placement.split("-"); 8798 return [side, align]; 8799} 8800var Root2$1 = Popper; 8801var Anchor = PopperAnchor; 8802var Content = PopperContent; 8803var Arrow = PopperArrow; 8804var PORTAL_NAME$1 = "Portal"; 8805var Portal$1 = reactExports.forwardRef((props, forwardedRef) => { 8806 var _a; 8807 const { container: containerProp, ...portalProps } = props; 8808 const [mounted, setMounted] = reactExports.useState(false); 8809 useLayoutEffect2(() => setMounted(true), []); 8810 const container = containerProp || mounted && ((_a = globalThis == null ? void 0 : globalThis.document) == null ? void 0 : _a.body); 8811 return container ? ReactDOM.createPortal(/* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.div, { ...portalProps, ref: forwardedRef }), container) : null; 8812}); 8813Portal$1.displayName = PORTAL_NAME$1; 8814function useStateMachine(initialState, machine) { 8815 return reactExports.useReducer((state, event) => { 8816 const nextState = machine[state][event]; 8817 return nextState ?? state; 8818 }, initialState); 8819} 8820var Presence = (props) => { 8821 const { present, children } = props; 8822 const presence = usePresence(present); 8823 const child = typeof children === "function" ? children({ present: presence.isPresent }) : reactExports.Children.only(children); 8824 const ref = useComposedRefs(presence.ref, getElementRef(child)); 8825 const forceMount = typeof children === "function"; 8826 return forceMount || presence.isPresent ? reactExports.cloneElement(child, { ref }) : null; 8827}; 8828Presence.displayName = "Presence"; 8829function usePresence(present) { 8830 const [node, setNode] = reactExports.useState(); 8831 const stylesRef = reactExports.useRef(null); 8832 const prevPresentRef = reactExports.useRef(present); 8833 const prevAnimationNameRef = reactExports.useRef("none"); 8834 const initialState = present ? "mounted" : "unmounted"; 8835 const [state, send] = useStateMachine(initialState, { 8836 mounted: { 8837 UNMOUNT: "unmounted", 8838 ANIMATION_OUT: "unmountSuspended" 8839 }, 8840 unmountSuspended: { 8841 MOUNT: "mounted", 8842 ANIMATION_END: "unmounted" 8843 }, 8844 unmounted: { 8845 MOUNT: "mounted" 8846 } 8847 }); 8848 reactExports.useEffect(() => { 8849 const currentAnimationName = getAnimationName(stylesRef.current); 8850 prevAnimationNameRef.current = state === "mounted" ? currentAnimationName : "none"; 8851 }, [state]); 8852 useLayoutEffect2(() => { 8853 const styles = stylesRef.current; 8854 const wasPresent = prevPresentRef.current; 8855 const hasPresentChanged = wasPresent !== present; 8856 if (hasPresentChanged) { 8857 const prevAnimationName = prevAnimationNameRef.current; 8858 const currentAnimationName = getAnimationName(styles); 8859 if (present) { 8860 send("MOUNT"); 8861 } else if (currentAnimationName === "none" || (styles == null ? void 0 : styles.display) === "none") { 8862 send("UNMOUNT"); 8863 } else { 8864 const isAnimating = prevAnimationName !== currentAnimationName; 8865 if (wasPresent && isAnimating) { 8866 send("ANIMATION_OUT"); 8867 } else { 8868 send("UNMOUNT"); 8869 } 8870 } 8871 prevPresentRef.current = present; 8872 } 8873 }, [present, send]); 8874 useLayoutEffect2(() => { 8875 if (node) { 8876 let timeoutId; 8877 const ownerWindow = node.ownerDocument.defaultView ?? window; 8878 const handleAnimationEnd = (event) => { 8879 const currentAnimationName = getAnimationName(stylesRef.current); 8880 const isCurrentAnimation = currentAnimationName.includes(event.animationName); 8881 if (event.target === node && isCurrentAnimation) { 8882 send("ANIMATION_END"); 8883 if (!prevPresentRef.current) { 8884 const currentFillMode = node.style.animationFillMode; 8885 node.style.animationFillMode = "forwards"; 8886 timeoutId = ownerWindow.setTimeout(() => { 8887 if (node.style.animationFillMode === "forwards") { 8888 node.style.animationFillMode = currentFillMode; 8889 } 8890 }); 8891 } 8892 } 8893 }; 8894 const handleAnimationStart = (event) => { 8895 if (event.target === node) { 8896 prevAnimationNameRef.current = getAnimationName(stylesRef.current); 8897 } 8898 }; 8899 node.addEventListener("animationstart", handleAnimationStart); 8900 node.addEventListener("animationcancel", handleAnimationEnd); 8901 node.addEventListener("animationend", handleAnimationEnd); 8902 return () => { 8903 ownerWindow.clearTimeout(timeoutId); 8904 node.removeEventListener("animationstart", handleAnimationStart); 8905 node.removeEventListener("animationcancel", handleAnimationEnd); 8906 node.removeEventListener("animationend", handleAnimationEnd); 8907 }; 8908 } else { 8909 send("ANIMATION_END"); 8910 } 8911 }, [node, send]); 8912 return { 8913 isPresent: ["mounted", "unmountSuspended"].includes(state), 8914 ref: reactExports.useCallback((node2) => { 8915 stylesRef.current = node2 ? getComputedStyle(node2) : null; 8916 setNode(node2); 8917 }, []) 8918 }; 8919} 8920function getAnimationName(styles) { 8921 return (styles == null ? void 0 : styles.animationName) || "none"; 8922} 8923function getElementRef(element) { 8924 var _a, _b; 8925 let getter = (_a = Object.getOwnPropertyDescriptor(element.props, "ref")) == null ? void 0 : _a.get; 8926 let mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning; 8927 if (mayWarn) { 8928 return element.ref; 8929 } 8930 getter = (_b = Object.getOwnPropertyDescriptor(element, "ref")) == null ? void 0 : _b.get; 8931 mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning; 8932 if (mayWarn) { 8933 return element.props.ref; 8934 } 8935 return element.props.ref || element.ref; 8936} 8937var POPOVER_NAME = "Popover"; 8938var [createPopoverContext, createPopoverScope] = createContextScope(POPOVER_NAME, [ 8939 createPopperScope 8940]); 8941var usePopperScope = createPopperScope(); 8942var [PopoverProvider, usePopoverContext] = createPopoverContext(POPOVER_NAME); 8943var Popover$1 = (props) => { 8944 const { 8945 __scopePopover, 8946 children, 8947 open: openProp, 8948 defaultOpen, 8949 onOpenChange, 8950 modal = false 8951 } = props; 8952 const popperScope = usePopperScope(__scopePopover); 8953 const triggerRef = reactExports.useRef(null); 8954 const [hasCustomAnchor, setHasCustomAnchor] = reactExports.useState(false); 8955 const [open, setOpen] = useControllableState({ 8956 prop: openProp, 8957 defaultProp: defaultOpen ?? false, 8958 onChange: onOpenChange, 8959 caller: POPOVER_NAME 8960 }); 8961 return /* @__PURE__ */ jsxRuntimeExports.jsx(Root2$1, { ...popperScope, children: /* @__PURE__ */ jsxRuntimeExports.jsx( 8962 PopoverProvider, 8963 { 8964 scope: __scopePopover, 8965 contentId: useId(), 8966 triggerRef, 8967 open, 8968 onOpenChange: setOpen, 8969 onOpenToggle: reactExports.useCallback(() => setOpen((prevOpen) => !prevOpen), [setOpen]), 8970 hasCustomAnchor, 8971 onCustomAnchorAdd: reactExports.useCallback(() => setHasCustomAnchor(true), []), 8972 onCustomAnchorRemove: reactExports.useCallback(() => setHasCustomAnchor(false), []), 8973 modal, 8974 children 8975 } 8976 ) }); 8977}; 8978Popover$1.displayName = POPOVER_NAME; 8979var ANCHOR_NAME = "PopoverAnchor"; 8980var PopoverAnchor$1 = reactExports.forwardRef( 8981 (props, forwardedRef) => { 8982 const { __scopePopover, ...anchorProps } = props; 8983 const context = usePopoverContext(ANCHOR_NAME, __scopePopover); 8984 const popperScope = usePopperScope(__scopePopover); 8985 const { onCustomAnchorAdd, onCustomAnchorRemove } = context; 8986 reactExports.useEffect(() => { 8987 onCustomAnchorAdd(); 8988 return () => onCustomAnchorRemove(); 8989 }, [onCustomAnchorAdd, onCustomAnchorRemove]); 8990 return /* @__PURE__ */ jsxRuntimeExports.jsx(Anchor, { ...popperScope, ...anchorProps, ref: forwardedRef }); 8991 } 8992); 8993PopoverAnchor$1.displayName = ANCHOR_NAME; 8994var TRIGGER_NAME = "PopoverTrigger"; 8995var PopoverTrigger = reactExports.forwardRef( 8996 (props, forwardedRef) => { 8997 const { __scopePopover, ...triggerProps } = props; 8998 const context = usePopoverContext(TRIGGER_NAME, __scopePopover); 8999 const popperScope = usePopperScope(__scopePopover); 9000 const composedTriggerRef = useComposedRefs(forwardedRef, context.triggerRef); 9001 const trigger = /* @__PURE__ */ jsxRuntimeExports.jsx( 9002 Primitive.button, 9003 { 9004 type: "button", 9005 "aria-haspopup": "dialog", 9006 "aria-expanded": context.open, 9007 "aria-controls": context.contentId, 9008 "data-state": getState(context.open), 9009 ...triggerProps, 9010 ref: composedTriggerRef, 9011 onClick: composeEventHandlers(props.onClick, context.onOpenToggle) 9012 } 9013 ); 9014 return context.hasCustomAnchor ? trigger : /* @__PURE__ */ jsxRuntimeExports.jsx(Anchor, { asChild: true, ...popperScope, children: trigger }); 9015 } 9016); 9017PopoverTrigger.displayName = TRIGGER_NAME; 9018var PORTAL_NAME = "PopoverPortal"; 9019var [PortalProvider, usePortalContext] = createPopoverContext(PORTAL_NAME, { 9020 forceMount: void 0 9021}); 9022var PopoverPortal = (props) => { 9023 const { __scopePopover, forceMount, children, container } = props; 9024 const context = usePopoverContext(PORTAL_NAME, __scopePopover); 9025 return /* @__PURE__ */ jsxRuntimeExports.jsx(PortalProvider, { scope: __scopePopover, forceMount, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Presence, { present: forceMount || context.open, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Portal$1, { asChild: true, container, children }) }) }); 9026}; 9027PopoverPortal.displayName = PORTAL_NAME; 9028var CONTENT_NAME = "PopoverContent"; 9029var PopoverContent$1 = reactExports.forwardRef( 9030 (props, forwardedRef) => { 9031 const portalContext = usePortalContext(CONTENT_NAME, props.__scopePopover); 9032 const { forceMount = portalContext.forceMount, ...contentProps } = props; 9033 const context = usePopoverContext(CONTENT_NAME, props.__scopePopover); 9034 return /* @__PURE__ */ jsxRuntimeExports.jsx(Presence, { present: forceMount || context.open, children: context.modal ? /* @__P
9034URE__ */ jsxRuntimeExports.jsx(PopoverContentModal, { ...contentProps, ref: forwardedRef }) : /* @__PURE__ */ jsxRuntimeExports.jsx(PopoverContentNonModal, { ...contentProps, ref: forwardedRef }) }); 9035 } 9036); 9037PopoverContent$1.displayName = CONTENT_NAME; 9038var Slot = /* @__PURE__ */ createSlot("PopoverContent.RemoveScroll"); 9039var PopoverContentModal = reactExports.forwardRef( 9040 (props, forwardedRef) => { 9041 const context = usePopoverContext(CONTENT_NAME, props.__scopePopover); 9042 const contentRef = reactExports.useRef(null); 9043 const composedRefs = useComposedRefs(forwardedRef, contentRef); 9044 const isRightClickOutsideRef = reactExports.useRef(false); 9045 reactExports.useEffect(() => { 9046 const content = contentRef.current; 9047 if (content) return hideOthers(content); 9048 }, []); 9049 return /* @__PURE__ */ jsxRuntimeExports.jsx(ReactRemoveScroll, { as: Slot, allowPinchZoom: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx( 9050 PopoverContentImpl, 9051 { 9052 ...props, 9053 ref: composedRefs, 9054 trapFocus: context.open, 9055 disableOutsidePointerEvents: true, 9056 onCloseAutoFocus: composeEventHandlers(props.onCloseAutoFocus, (event) => { 9057 var _a; 9058 event.preventDefault(); 9059 if (!isRightClickOutsideRef.current) (_a = context.triggerRef.current) == null ? void 0 : _a.focus(); 9060 }), 9061 onPointerDownOutside: composeEventHandlers( 9062 props.onPointerDownOutside, 9063 (event) => { 9064 const originalEvent = event.detail.originalEvent; 9065 const ctrlLeftClick = originalEvent.button === 0 && originalEvent.ctrlKey === true; 9066 const isRightClick = originalEvent.button === 2 || ctrlLeftClick; 9067 isRightClickOutsideRef.current = isRightClick; 9068 }, 9069 { checkForDefaultPrevented: false } 9070 ), 9071 onFocusOutside: composeEventHandlers( 9072 props.onFocusOutside, 9073 (event) => event.preventDefault(), 9074 { checkForDefaultPrevented: false } 9075 ) 9076 } 9077 ) }); 9078 } 9079); 9080var PopoverContentNonModal = reactExports.forwardRef( 9081 (props, forwardedRef) => { 9082 const context = usePopoverContext(CONTENT_NAME, props.__scopePopover); 9083 const hasInteractedOutsideRef = reactExports.useRef(false); 9084 const hasPointerDownOutsideRef = reactExports.useRef(false); 9085 return /* @__PURE__ */ jsxRuntimeExports.jsx( 9086 PopoverContentImpl, 9087 { 9088 ...props, 9089 ref: forwardedRef, 9090 trapFocus: false, 9091 disableOutsidePointerEvents: false, 9092 onCloseAutoFocus: (event) => { 9093 var _a, _b; 9094 (_a = props.onCloseAutoFocus) == null ? void 0 : _a.call(props, event); 9095 if (!event.defaultPrevented) { 9096 if (!hasInteractedOutsideRef.current) (_b = context.triggerRef.current) == null ? void 0 : _b.focus(); 9097 event.preventDefault(); 9098 } 9099 hasInteractedOutsideRef.current = false; 9100 hasPointerDownOutsideRef.current = false; 9101 }, 9102 onInteractOutside: (event) => { 9103 var _a, _b; 9104 (_a = props.onInteractOutside) == null ? void 0 : _a.call(props, event); 9105 if (!event.defaultPrevented) { 9106 hasInteractedOutsideRef.current = true; 9107 if (event.detail.originalEvent.type === "pointerdown") { 9108 hasPointerDownOutsideRef.current = true; 9109 } 9110 } 9111 const target = event.target; 9112 const targetIsTrigger = (_b = context.triggerRef.current) == null ? void 0 : _b.contains(target); 9113 if (targetIsTrigger) event.preventDefault(); 9114 if (event.detail.originalEvent.type === "focusin" && hasPointerDownOutsideRef.current) { 9115 event.preventDefault(); 9116 } 9117 } 9118 } 9119 ); 9120 } 9121); 9122var PopoverContentImpl = reactExports.forwardRef( 9123 (props, forwardedRef) => { 9124 const { 9125 __scopePopover, 9126 trapFocus, 9127 onOpenAutoFocus, 9128 onCloseAutoFocus, 9129 disableOutsidePointerEvents, 9130 onEscapeKeyDown, 9131 onPointerDownOutside, 9132 onFocusOutside, 9133 onInteractOutside, 9134 ...contentProps 9135 } = props; 9136 const context = usePopoverContext(CONTENT_NAME, __scopePopover); 9137 const popperScope = usePopperScope(__scopePopover); 9138 useFocusGuards(); 9139 return /* @__PURE__ */ jsxRuntimeExports.jsx( 9140 FocusScope, 9141 { 9142 asChild: true, 9143 loop: true, 9144 trapped: trapFocus, 9145 onMountAutoFocus: onOpenAutoFocus, 9146 onUnmountAutoFocus: onCloseAutoFocus, 9147 children: /* @__PURE__ */ jsxRuntimeExports.jsx( 9148 DismissableLayer, 9149 { 9150 asChild: true, 9151 disableOutsidePointerEvents, 9152 onInteractOutside, 9153 onEscapeKeyDown, 9154 onPointerDownOutside, 9155 onFocusOutside, 9156 onDismiss: () => context.onOpenChange(false), 9157 children: /* @__PURE__ */ jsxRuntimeExports.jsx( 9158 Content, 9159 { 9160 "data-state": getState(context.open), 9161 role: "dialog", 9162 id: context.contentId, 9163 ...popperScope, 9164 ...contentProps, 9165 ref: forwardedRef, 9166 style: { 9167 ...contentProps.style, 9168 // re-namespace exposed content custom properties 9169 ...{ 9170 "--radix-popover-content-transform-origin": "var(--radix-popper-transform-origin)", 9171 "--radix-popover-content-available-width": "var(--radix-popper-available-width)", 9172 "--radix-popover-content-available-height": "var(--radix-popper-available-height)", 9173 "--radix-popover-trigger-width": "var(--radix-popper-anchor-width)", 9174 "--radix-popover-trigger-height": "var(--radix-popper-anchor-height)" 9175 } 9176 } 9177 } 9178 ) 9179 } 9180 ) 9181 } 9182 ); 9183 } 9184); 9185var CLOSE_NAME = "PopoverClose"; 9186var PopoverClose = reactExports.forwardRef( 9187 (props, forwardedRef) => { 9188 const { __scopePopover, ...closeProps } = props; 9189 const context = usePopoverContext(CLOSE_NAME, __scopePopover); 9190 return /* @__PURE__ */ jsxRuntimeExports.jsx( 9191 Primitive.button, 9192 { 9193 type: "button", 9194 ...closeProps, 9195 ref: forwardedRef, 9196 onClick: composeEventHandlers(props.onClick, () => context.onOpenChange(false)) 9197 } 9198 ); 9199 } 9200); 9201PopoverClose.displayName = CLOSE_NAME; 9202var ARROW_NAME = "PopoverArrow"; 9203var PopoverArrow = reactExports.forwardRef( 9204 (props, forwardedRef) => { 9205 const { __scopePopover, ...arrowProps } = props; 9206 const popperScope = usePopperScope(__scopePopover); 9207 return /* @__PURE__ */ jsxRuntimeExports.jsx(Arrow, { ...popperScope, ...arrowProps, ref: forwardedRef }); 9208 } 9209); 9210PopoverArrow.displayName = ARROW_NAME; 9211function getState(open) { 9212 return open ? "open" : "closed"; 9213} 9214var Root2 = Popover$1; 9215var Anchor2 = PopoverAnchor$1; 9216var Portal = PopoverPortal; 9217var Content2 = PopoverContent$1; 9218const Popover = Root2; 9219const PopoverAnchor = Anchor2; 9220const PopoverContent = reactExports.forwardRef(({ 9221 className, 9222 align = "center", 9223 sideOffset = 4, 9224 ...props 9225}, ref) => /* @__PURE__ */ jsxRuntimeExports.jsx(Portal, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(Content2, { ref, align, sideOffset, className: cn$1("z-50 w-72 origin-[--radix-popover-content-transform-origin] rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:sli
9225de-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", className), ...props }) })); 9226PopoverContent.displayName = Content2.displayName; 9227function pickerAllowedOrigin({ 9228 previewUrl, 9229 hoverPointer, 9230 previewBaseDomain 9231}) { 9232 if (!hoverPointer) { 9233 return { 9234 allowed: false, 9235 reason: "touch" 9236 }; 9237 } 9238 if (!previewUrl) { 9239 return { 9240 allowed: false, 9241 reason: "unknown" 9242 }; 9243 } 9244 let host; 9245 try { 9246 host = new URL(previewUrl).hostname.toLowerCase(); 9247 } catch { 9248 return { 9249 allowed: false, 9250 reason: "unknown" 9251 }; 9252 } 9253 if (host === "history.landingsite.dev" || host.endsWith(".history.landingsite.dev")) { 9254 return { 9255 allowed: false, 9256 reason: "history" 9257 }; 9258 } 9259 if (host === "agncy.dev" || host.endsWith(".agncy.dev")) { 9260 return { 9261 allowed: false, 9262 reason: "agncy" 9263 }; 9264 } 9265 if (host === "landingsite.dev" || host.endsWith(".landingsite.dev")) { 9266 return { 9267 allowed: true 9268 }; 9269 } 9270 if (host === "agcy.dev" || host.endsWith(".agcy.dev")) { 9271 return { 9272 allowed: true 9273 }; 9274 } 9275 if (host === "localhost" || host === "127.0.0.1") { 9276 try { 9277 const port = new URL(previewUrl).port; 9278 if (port === "8792") return { 9279 allowed: true 9280 }; 9281 } catch { 9282 } 9283 } 9284 return { 9285 allowed: false, 9286 reason: "unknown" 9287 }; 9288} 9289function validateTextEdits(raw, requireLoc) { 9290 if (!Array.isArray(raw)) return []; 9291 const enc = new TextEncoder(); 9292 const out = []; 9293 for (const e of raw.slice(0, 500)) { 9294 if (!e || typeof e !== "object") continue; 9295 const ed = e; 9296 if (typeof ed.sourceLoc !== "string" || typeof ed.oldText !== "string" || typeof ed.newText !== "string") { 9297 continue; 9298 } 9299 const hasValidLoc = /^.+:\d+:\d+$/.test(ed.sourceLoc) && enc.encode(ed.sourceLoc).length <= 512; 9300 if (requireLoc && !hasValidLoc) continue; 9301 if (enc.encode(ed.oldText).length > 64 * 1024) continue; 9302 if (enc.encode(ed.newText).length > 64 * 1024) continue; 9303 out.push({ 9304 sourceLoc: hasValidLoc ? ed.sourceLoc : "", 9305 oldText: ed.oldText, 9306 newText: ed.newText, 9307 tag: typeof ed.tag === "string" ? ed.tag.toLowerCase().slice(0, 64) : "", 9308 path: typeof ed.path === "string" ? ed.path.slice(0, 512) : "" 9309 }); 9310 } 9311 return out; 9312} 9313function parsePickerMessage(data) { 9314 if (!data || typeof data !== "object") return null; 9315 const raw = data; 9316 if (typeof raw.type !== "string") return null; 9317 switch (raw.type) { 9318 case "selector-probe": 9319 case "enter-select": 9320 case "cancel-select": 9321 case "selector-ready": 9322 case "enter-select-ack": 9323 case "enter-edit-text-ack": 9324 case "cancel-select-request": 9325 return { 9326 type: raw.type 9327 }; 9328 case "edit-queue-count": { 9329 const r = raw; 9330 const count2 = typeof r.count === "number" ? r.count : 0; 9331 const persistable = typeof r.persistable === "number" ? r.persistable : 0; 9332 return { 9333 type: "edit-queue-count", 9334 count: Math.max(0, Math.floor(count2)), 9335 persistable: Math.max(0, Math.floor(persistable)) 9336 }; 9337 } 9338 case "text-edit-batch": { 9339 const r = raw; 9340 if (!Array.isArray(r.edits)) return null; 9341 const forEditId = typeof r.forEditId === "number" && Number.isFinite(r.forEditId) && r.forEditId >= 0 ? Math.floor(r.forEditId) : void 0; 9342 return { 9343 type: "text-edit-batch", 9344 edits: validateTextEdits(r.edits, true), 9345 agentEdits: validateTextEdits(r.agentEdits, false), 9346 ...forEditId !== void 0 ? { 9347 forEditId 9348 } : {} 9349 }; 9350 } 9351 case "active-edit-element": { 9352 const r = raw; 9353 if (typeof r.editId !== "number" || !Number.isFinite(r.editId) || r.editId < 0 || !r.rect || typeof r.rect !== "object" || typeof r.sourceLoc !== "string" || typeof r.hasSourceLoc !== "boolean" || typeof r.isDirty !== "boolean") { 9354 return null; 9355 } 9356 const rectRaw = r.rect; 9357 if (typeof rectRaw.top !== "number" || typeof rectRaw.left !== "number" || typeof rectRaw.width !== "number" || typeof rectRaw.height !== "number" || !Number.isFinite(rectRaw.top) || !Number.isFinite(rectRaw.left) || !Number.isFinite(rectRaw.width) || !Number.isFinite(rectRaw.height)) { 9358 return null; 9359 } 9360 const encActive = new TextEncoder(); 9361 const validLoc = r.sourceLoc.length > 0 && encActive.encode(r.sourceLoc).length <= 512 && /^.+:\d+:\d+$/.test(r.sourceLoc); 9362 return { 9363 type: "active-edit-element", 9364 editId: Math.floor(r.editId), 9365 rect: { 9366 top: rectRaw.top, 9367 left: rectRaw.left, 9368 width: Math.max(0, rectRaw.width), 9369 height: Math.max(0, rectRaw.height) 9370 },
9371 sourceLoc: validLoc ? r.sourceLoc : "", 9372 hasSourceLoc: validLoc ? r.hasSourceLoc : false, 9373 isDirty: r.isDirty 9374 }; 9375 } 9376 case "request-focus-change": { 9377 const r = raw; 9378 if (typeof r.fromEditId !== "number" || !Number.isFinite(r.fromEditId) || r.fromEditId < 0 || typeof r.toEditId !== "number" || !Number.isFinite(r.toEditId) || r.toEditId < -1 || typeof r.dirtyPreview !== "string") { 9379 return null; 9380 } 9381 return { 9382 type: "request-focus-change", 9383 fromEditId: Math.floor(r.fromEditId), 9384 toEditId: Math.floor(r.toEditId), 9385 dirtyPreview: r.dirtyPreview.slice(0, 120) 9386 }; 9387 } 9388 case "selected": { 9389 const r = raw; 9390 if (typeof r.outerHTML !== "string" || typeof r.tag !== "string" || typeof r.textPreview !== "string" || typeof r.path !== "string") { 9391 return null; 9392 } 9393 if (!/^[a-zA-Z][a-zA-Z0-9-]{0,63}$/.test(r.tag)) return null; 9394 const enc = new TextEncoder(); 9395 if (enc.encode(r.outerHTML).length > 200 * 1024) return null; 9396 if (enc.encode(r.textPreview).length > 1024) return null; 9397 if (enc.encode(r.path).length > 512) return null; 9398 let sourceLoc; 9399 if (typeof r.sourceLoc === "string") { 9400 if (enc.encode(r.sourceLoc).length <= 512 && /^.+:\d+:\d+$/.test(r.sourceLoc)) { 9401 sourceLoc = r.sourceLoc; 9402 } 9403 } 9404 return { 9405 type: "selected", 9406 outerHTML: r.outerHTML, 9407 tag: r.tag, 9408 textPreview: r.textPreview, 9409 path: r.path, 9410 ...sourceLoc !== void 0 ? { 9411 sourceLoc 9412 } : {} 9413 }; 9414 } 9415 default: 9416 return null; 9417 } 9418} 9419const COUNTRY_FORMS = { 9420 GB: { 9421 city: "Town or city", 9422 state: "County (optional)", 9423 postal: "Postcode" 9424 }, 9425 US: { 9426 city: "City", 9427 state: "State", 9428 postal: "ZIP code" 9429 }, 9430 CA: { 9431 city: "City", 9432 state: "Province", 9433 postal: "Postal code" 9434 }, 9435 IE: { 9436 city: "Town or city", 9437 state: "County", 9438 postal: "Eircode (optional)" 9439 }, 9440 AU: { 9441 city: "Suburb", 9442 state: "State", 9443 postal: "Postcode" 9444 }, 9445 NZ: { 9446 city: "Town or city", 9447 state: null, 9448 postal: "Postcode" 9449 }, 9450 DE: { 9451 city: "City", 9452 state: null, 9453 postal: "Postal code" 9454 }, 9455 FR: { 9456 city: "City", 9457 state: null, 9458 postal: "Postal code" 9459 }, 9460 NL: { 9461 city: "City", 9462 state: null, 9463 postal: "Postal code" 9464 }, 9465 ES: { 9466 city: "City", 9467 state: "Province", 9468 postal: "Postal code" 9469 }, 9470 IT: { 9471 city: "City", 9472 state: "Province", 9473 postal: "Postal code" 9474 }, 9475 HK: { 9476 city: "District", 9477 state: "Region", 9478 postal: false 9479 }, 9480 AE: { 9481 city: "City", 9482 state: "Emirate", 9483 postal: false 9484 }, 9485 SG: { 9486 city: "City", 9487 state: null, 9488 postal: "Postal code" 9489 }, 9490 JP: { 9491 city: "City / ward", 9492 state: "Prefecture", 9493 postal: "Postal code" 9494 } 9495}; 9496const DEFAULT_FORM = { 9497 city: "Town or city", 9498 state: "State, province or region (optional)", 9499 postal: "Postal code" 9500}; 9501const COUNTRY_NAMES = { 9502 GB: "United Kingdom", 9503 US: "United States", 9504 CA: "Canada", 9505 IE: "Ireland", 9506 AU: "Australia", 9507 NZ: "New Zealand", 9508 DE: "Germany", 9509 FR: "France", 9510 NL: "Netherlands", 9511 ES: "Spain", 9512 IT: "Italy", 9513 HK: "Hong Kong", 9514 AE: "United Arab Emirates", 9515 SG: "Singapore", 9516 JP: "Japan" 9517}; 9518const CURRENCY_HOME = { 9519 gbp: "GB", 9520 usd: "US", 9521 cad: "CA", 9522 eur: "IE", 9523 aud: "AU", 9524 nzd: "NZ", 9525 sgd: "SG", 9526 hkd: "HK", 9527 aed: "AE", 9528 jpy: "JP" 9529}; 9530const money = (minor, currency) => { 9531 try { 9532 return new Intl.NumberFormat(void 0, { 9533 style: "currency", 9534 currency: currency.toUpperCase() 9535 }).format(minor / 100); 9536 } catch { 9537 return `${(minor / 100).toFixed(2)} ${currency.toUpperCase()}`; 9538 } 9539}; 9540const intervalPhrase = (plan) => plan.intervalCount === 1 ? plan.interval : `${plan.intervalCount} ${plan.interval}s`; 9541const TestCheckoutSheet = ({ 9542 open, 9543 storeName, 9544 ownerEmail, 9545 collectsShippingAddress, 9546 shippingCountries, 9547 currency, 9548 lines, 9549 subtotal, 9550 shippingRates, 9551 freeShippingOver, 9552 stripeTaxEnabled, 9553 taxBehavior, 9554 subscription, 9555 submitting, 9556 onSubmit, 9557 onCancel 9558}) => { 9559 const countries = reactExports.useMemo(() => { 9560 if (shippingCountries && shippingCountries.length > 0) { 9561 return shippingCountries.map((c) => c.toUpperCase()); 9562 } 9563 const home = CURRENCY_HOME[currency == null ? void 0 : currency.toLowerCase()] ?? "US"; 9564 const rest = Object.keys(COUNTRY_NAMES).filter((c) => c !== home); 9565 return [home, ...rest]; 9566 }, [shippingCountries, currency]); 9567 const [name, setName] = reactExports.useState(""); 9568 const [line1, setLine1] = reactExports.useState(""); 9569 const [line2, setLine2] = reactExports.useState(""); 9570 const [city, setCity] = reactExports.useState(""); 9571 const [stateRegion, setStateRegion] = reactExports.useState(""); 9572 const [postalCode, setPostalCode] = reactExports.useState(""); 9573 const [country, setCountry] = reactExports.useState(countries[0] ?? "US"); 9574 const [touched, setTouched] = reactExports.useState(false); 9575 const [shippingLabel, setShippingLabel] = reactExports.useState(null); 9576 reactExports.useEffect(() => { 9577 if (!open) return; 9578 setName("");
9579 setLine1(""); 9580 setLine2(""); 9581 setCity(""); 9582 setStateRegion(""); 9583 setPostalCode(""); 9584 setCountry(countries[0] ?? "US"); 9585 setTouched(false); 9586 setShippingLabel(null); 9587 }, [open, countries]); 9588 const form = COUNTRY_FORMS[country] ?? DEFAULT_FORM; 9589 const shippableSubtotal = reactExports.useMemo(() => lines.reduce((sum, l) => l.shippable ? sum + l.unitAmount * l.quantity : sum, 0), [lines]); 9590 const freeShippingApplies = freeShippingOver !== null && shippableSubtotal >= freeShippingOver; 9591 const rateOptions = reactExports.useMemo(() => { 9592 let cheapest = 0; 9593 for (let i = 1; i < shippingRates.length; i++) { 9594 if (shippingRates[i].amount < shippingRates[cheapest].amount) cheapest = i; 9595 } 9596 return shippingRates.map((rate, i) => ({ 9597 label: rate.label, 9598 amount: freeShippingApplies && i === cheapest ? 0 : rate.amount, 9599 // What it would have cost â shown struck through, so the merchant sees 9600 // the discount happen instead of a bare "Free" with no evidence. 9601 listAmount: rate.amount, 9602 waived: freeShippingApplies && i === cheapest && rate.amount > 0 9603 })); 9604 }, [shippingRates, freeShippingApplies]); 9605 const selectedRate = rateOptions.find((r) => r.label === shippingLabel) ?? rateOptions[0] ?? null; 9606 const deliveryAmount = collectsShippingAddress && !subscription ? (selectedRate == null ? void 0 : selectedRate.amount) ?? 0 : 0; 9607 const chargeSubtotal = subscription ? subscription.amount : subtotal; 9608 const total = chargeSubtotal + deliveryAmount; 9609 const addressOk = !collectsShippingAddress || line1.trim().length > 0; 9610 const canSubmit = addressOk && !submitting; 9611 const submit = () => { 9612 setTouched(true); 9613 if (!canSubmit) return; 9614 onSubmit({ 9615 shippingAddress: collectsShippingAddress ? { 9616 name: name.trim() || void 0, 9617 line1: line1.trim(), 9618 line2: line2.trim() || void 0, 9619 city: city.trim() || void 0, 9620 state: stateRegion.trim() || void 0, 9621 postalCode: postalCode.trim() || void 0, 9622 country 9623 } : null, 9624 shippingOption: collectsShippingAddress && !subscription && selectedRate ? selectedRate.label : null 9625 }); 9626 }; 9627 return /* @__PURE__ */ jsxRuntimeExports.jsx(Dialog, { open, onOpenChange: (next) => !next && onCancel(), children: /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogContent, { className: "max-h-[90vh] gap-0 overflow-y-auto p-0 sm:max-w-[760px]", children: [ 9628 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-b border-amber-200 bg-amber-50 px-5 py-3", children: [
9629 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[11px] font-bold uppercase tracking-wider text-amber-800", children: "Test checkout" }), 9630 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-0.5 text-[13px] leading-snug text-amber-800/90", children: "A rehearsal of your checkout. No payment is taken, and no card can be entered here." }) 9631 ] }), 9632 /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogHeader, { className: "space-y-1 px-5 pb-1 pt-5 text-left", children: [ 9633 /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogTitle, { className: "text-base font-semibold", children: [ 9634 "What ", 9635 storeName, 9636 " asks buyers for" 9637 ] }), 9638 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[13px] text-muted-foreground", children: "Stripe collects these on your real checkout page. Fill them in and they'll appear on your test order and its email, exactly as a real sale's would." }) 9639 ] }), 9640 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid gap-0 md:grid-cols-[minmax(0,260px)_minmax(0,1fr)]", children: [
9641 /* @__PURE__ */ jsxRuntimeExports.jsxs("aside", { className: "flex flex-col gap-4 border-b bg-muted/40 px-5 py-5 md:border-b-0 md:border-r", children: [ 9642 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 9643 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[12px] text-muted-foreground", children: subscription ? `Subscribe to ${storeName}` : `Pay ${storeName}` }), 9644 /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-2xl font-semibold tabular-nums", children: [ 9645 money(total, currency), 9646 subscription && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-sm font-normal text-muted-foreground", children: [ 9647 " ", 9648 "/ ", 9649 intervalPhrase(subscription) 9650 ] }) 9651 ] }) 9652 ] }), 9653 /* @__PURE__ */ jsxRuntimeExports.jsx("ul", { className: "flex list-none flex-col gap-3 p-0", children: lines.map((line, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { className: "flex items-start gap-3", children: [
9654 line.imageUrl ? /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: line.imageUrl, alt: "", className: "h-10 w-10 shrink-0 rounded border object-cover" }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "h-10 w-10 shrink-0 rounded border bg-background" }), 9655 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "min-w-0 flex-1", children: [ 9656 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "block truncate text-[13px] font-medium", children: line.name }), 9657 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "block text-[12px] text-muted-foreground", children: [ 9658 line.variantTitle ? `${line.variantTitle} · ` : "", 9659 "Qty ", 9660 line.quantity 9661 ] }) 9662 ] }), 9663 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "shrink-0 text-[13px] tabular-nums", children: money(line.unitAmount * line.quantity, currency) }) 9664 ] }, i)) }), 9665 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1.5 border-t pt-3 text-[13px]", children: [
9666 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between text-muted-foreground", children: [ 9667 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Subtotal" }), 9668 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "tabular-nums", children: money(chargeSubtotal, currency) }) 9669 ] }), 9670 collectsShippingAddress && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between text-muted-foreground", children: [ 9671 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Delivery" }), 9672 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "tabular-nums", children: subscription ? "Included" : (selectedRate == null ? void 0 : selectedRate.waived) ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 9673 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "mr-1.5 line-through opacity-60", children: money(selectedRate.listAmount, currency) }), 9674 "Free" 9675 ] }) : money(deliveryAmount, currency) }) 9676 ] }), 9677 stripeTaxEnabled && taxBehavior === "exclusive" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between text-muted-foreground", children: [
9678 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Tax" }), 9679 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Added at checkout" }) 9680 ] }), 9681 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between border-t pt-1.5 font-semibold text-foreground", children: [ 9682 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Total" }), 9683 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "tabular-nums", children: [ 9684 money(total, currency), 9685 subscription ? ` / ${intervalPhrase(subscription)}` : "" 9686 ] }) 9687 ] }) 9688 ] }) 9689 ] }), 9690 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-5 px-5 pb-5 pt-4", children: [ 9691 subscription && /* A signup is a different promise from a purchase, and the 9692 merchant needs to see WHICH they are rehearsing. Terms only â 9693 renewal, cancel and reactivate are deliberately not simulated, 9694 so this screen must not imply it exercised them. */
9695 /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "rounded-md border border-violet-200 bg-violet-50 px-3 py-2.5 text-[12.5px] leading-snug text-violet-900", children: [ 9696 /* @__PURE__ */ jsxRuntimeExports.jsxs("strong", { className: "font-semibold", children: [ 9697 subscription.title ?? "Subscription", 9698 " â recurring" 9699 ] }), 9700 " ", 9701 "Your buyer is charged ", 9702 money(subscription.amount, currency), 9703 " ", 9704 "every ", 9705 intervalPhrase(subscription), 9706 " until they cancel. This rehearsal creates the signup and its first order; it does not run a renewal." 9707 ] }), 9708 /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "flex flex-col gap-2", children: [ 9709 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-sm font-semibold", children: "Contact" }), 9710 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "overflow-hidden rounded-md border border-input", children: [
9711 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 px-3 py-2.5", children: [ 9712 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1 truncate text-sm", children: ownerEmail }), 9713 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "rounded-full bg-violet-100 px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide text-violet-700", children: "You" }) 9714 ] }), 9715 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "border-t border-dashed border-input px-3 py-2 text-[12px] leading-snug text-muted-foreground", children: "Stripe collects your buyer's email here on every order. This test uses yours, so the receipt reaches you." }) 9716 ] }) 9717 ] }), 9718 collectsShippingAddress && /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "flex flex-col gap-3", children: [ 9719 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 9720 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-sm font-semibold", children: "Delivery address" }),
9721 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[12px] text-muted-foreground", children: "Collected because this order contains something you deliver." }) 9722 ] }), 9723 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1.5", children: [ 9724 /* @__PURE__ */ jsxRuntimeExports.jsx(Label, { htmlFor: "tc-name", className: "text-xs", children: "Full name" }), 9725 /* @__PURE__ */ jsxRuntimeExports.jsx(Input, { id: "tc-name", autoComplete: "off", value: name, onChange: (e) => setName(e.target.value) }) 9726 ] }), 9727 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1.5", children: [ 9728 /* @__PURE__ */ jsxRuntimeExports.jsx(Label, { htmlFor: "tc-line1", className: "text-xs", children: "Address" }), 9729 /* @__PURE__ */ jsxRuntimeExports.jsx(Input, { id: "tc-line1", autoComplete: "off", value: line1, onChange: (e) => setLine1(e.target.value) }), 9730 touched && !addressOk && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[12px] text-destructive", children: "Enter a street address." }) 9731 ] }),
9732 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1.5", children: [ 9733 /* @__PURE__ */ jsxRuntimeExports.jsx(Label, { htmlFor: "tc-line2", className: "text-xs", children: "Apartment, suite, etc. (optional)" }), 9734 /* @__PURE__ */ jsxRuntimeExports.jsx(Input, { id: "tc-line2", autoComplete: "off", value: line2, onChange: (e) => setLine2(e.target.value) }) 9735 ] }), 9736 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-3", children: [ 9737 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1.5", children: [ 9738 /* @__PURE__ */ jsxRuntimeExports.jsx(Label, { htmlFor: "tc-city", className: "text-xs", children: form.city }), 9739 /* @__PURE__ */ jsxRuntimeExports.jsx(Input, { id: "tc-city", autoComplete: "off", value: city, onChange: (e) => setCity(e.target.value) }) 9740 ] }), 9741 form.postal !== false && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1.5", children: [
9742 /* @__PURE__ */ jsxRuntimeExports.jsx(Label, { htmlFor: "tc-postal", className: "text-xs", children: form.postal }), 9743 /* @__PURE__ */ jsxRuntimeExports.jsx(Input, { id: "tc-postal", autoComplete: "off", value: postalCode, onChange: (e) => setPostalCode(e.target.value) }) 9744 ] }) 9745 ] }), 9746 form.state !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1.5", children: [ 9747 /* @__PURE__ */ jsxRuntimeExports.jsx(Label, { htmlFor: "tc-state", className: "text-xs", children: form.state }), 9748 /* @__PURE__ */ jsxRuntimeExports.jsx(Input, { id: "tc-state", autoComplete: "off", value: stateRegion, onChange: (e) => setStateRegion(e.target.value) }) 9749 ] }), 9750 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1.5", children: [ 9751 /* @__PURE__ */ jsxRuntimeExports.jsx(Label, { htmlFor: "tc-country", className: "text-xs", children: "Country" }), 9752 /* @__PURE__ */ jsxRuntimeExports.jsx("select", { id: "tc-country", className: "h-9 rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", value: country, onChange: (e) => setCountry(e.target.value), children: countries.map((code) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: code, children: COUNTRY_NAMES[code] ?? code }, code)) }),
9753 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[12px] text-muted-foreground", children: shippingCountries && shippingCountries.length > 0 ? "Buyers outside this list cannot check out." : "Your store delivers anywhere Stripe serves." }) 9754 ] }) 9755 ] }), 9756 collectsShippingAddress && !subscription && /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "flex flex-col gap-2", children: [ 9757 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-sm font-semibold", children: "Delivery method" }), 9758 shippingRates.length === 0 ? ( 9759 /* 9760 * An empty rate list is the single most valuable thing this 9761 * screen can surface. Collecting a delivery address while 9762 * charging nothing for postage is the expensive mistake in 9763 * this feature â the merchant posts the order and eats the 9764 * cost â and it is invisible everywhere else until the first 9765 * real sale. 9766 */
9767 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-md border border-amber-200 bg-amber-50 px-3 py-2.5 text-[12.5px] leading-snug text-amber-900", children: [ 9768 /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "font-semibold", children: "Your store charges nothing for delivery." }), 9769 " ", 9770 "Buyers pay ", 9771 money(0, currency), 9772 " postage because no delivery rates are set. Ask the assistant to set them if you charge for delivery." 9773 ] }) 9774 ) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1.5", children: [ 9775 rateOptions.map((rate) => { 9776 const checked = (selectedRate == null ? void 0 : selectedRate.label) === rate.label; 9777 return /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: `flex cursor-pointer items-center justify-between rounded-md border px-3 py-2 text-[13px] ${checked ? "border-violet-400 bg-violet-50/60 ring-1 ring-violet-200" : "hover:bg-muted/50"}`, children: [
9778 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-2.5", children: [ 9779 /* @__PURE__ */ jsxRuntimeExports.jsx("input", { type: "radio", name: "test-checkout-delivery", className: "accent-violet-600", checked, onChange: () => setShippingLabel(rate.label) }), 9780 rate.label 9781 ] }), 9782 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "tabular-nums", children: rate.waived ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 9783 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "mr-1.5 line-through opacity-60", children: money(rate.listAmount, currency) }), 9784 "Free" 9785 ] }) : money(rate.amount, currency) }) 9786 ] }, rate.label); 9787 }), 9788 freeShippingOver !== null && !freeShippingApplies && /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-[12px] text-muted-foreground", children: [ 9789 "Cheapest option is free over", 9790 " ", 9791 money(freeShippingOver, currency), 9792 "." 9793 ] }), 9794 freeShippingApplies && /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-[12px] text-muted-foreground", children: [ 9795 "Free delivery applied â this basket is over", 9796 " ", 9797 money(freeShippingOver, currency), 9798 "." 9799 ] }) 9800 ] }) 9801 ] }),
9802 /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "flex flex-col gap-2", children: [ 9803 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-sm font-semibold", children: "Payment" }), 9804 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "overflow-hidden rounded-md border border-input", children: [ 9805 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 px-3 py-2.5", children: [ 9806 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "grid h-6 w-9 shrink-0 place-items-center rounded bg-slate-800 text-[8px] font-bold tracking-wide text-white", children: "TEST" }), 9807 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1 font-mono text-sm tracking-wide", children: "â¢â¢â¢â¢ â¢â¢â¢â¢ â¢â¢â¢â¢ 4242" }), 9808 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "rounded-full bg-violet-100 px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide text-violet-700", children: "Simulated" }) 9809 ] }),
9810 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "border-t border-dashed border-input px-3 py-2 text-[12px] leading-snug text-muted-foreground", children: "Your buyers enter their own card on Stripe's secure page â never on your site." }) 9811 ] }) 9812 ] }), 9813 /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "flex flex-col gap-2", children: [ 9814 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-sm font-semibold", children: "What happens on the real checkout" }), 9815 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1.5 rounded-md border border-dashed px-3 py-2.5", children: [ 9816 /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-[12.5px] leading-snug text-muted-foreground", children: [ 9817 /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "font-medium text-foreground", children: "Stripe hosts the real page" }), 9818 " ", 9819 "under your business name and branding, at checkout.stripe.com." 9820 ] }),
9821 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[12.5px] leading-snug text-muted-foreground", children: stripeTaxEnabled ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 9822 /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "font-medium text-foreground", children: "Stripe Tax is on." }), 9823 " ", 9824 taxBehavior === "inclusive" ? "Your prices include tax, and Stripe itemises it on the receipt." : "Stripe adds tax on its page once the buyer enters their address." 9825 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 9826 /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "font-medium text-foreground", children: "Tax is off for this store." }), 9827 " ", 9828 "Turn on Stripe Tax in your Stripe Dashboard and it applies here automatically." 9829 ] }) }), 9830 collectsShippingAddress && /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-[12.5px] leading-snug text-muted-foreground", children: [
9831 /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "font-medium text-foreground", children: shippingCountries && shippingCountries.length > 0 ? `You deliver to ${shippingCountries.length} ${shippingCountries.length === 1 ? "country" : "countries"}.` : "Buyers can order from anywhere Stripe serves." }), 9832 " ", 9833 shippingCountries && shippingCountries.length > 0 ? "Stripe refuses any other delivery address on its own page." : "You haven't limited delivery countries." 9834 ] }) 9835 ] }) 9836 ] }) 9837 ] }) 9838 ] }), 9839 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-end gap-2 border-t bg-muted/30 px-5 py-3", children: [ 9840 /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { variant: "ghost", onClick: onCancel, disabled: submitting, children: "Cancel" }), 9841 /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { onClick: submit, disabled: !canSubmit, children: submitting ? "Creatingâ¦" : subscription ? "Create test subscription" : "Place test order" }) 9842 ] }) 9843 ] }) }); 9844}; 9845const VITE_ERROR_CAPS = { 9846 message: 1024, 9847 file: 1024, 9848 frame: 4 * 1024, 9849 stack: 8 * 1024, 9850 path: 512 9851}; 9852function parseViteErrorMessage(data) { 9853 if (!data || typeof data !== "object") return null; 9854 const raw = data; 9855 if (raw.type === "vite-error-cleared") { 9856 return { 9857 type: "vite-error-cleared" 9858 }; 9859 } 9860 if (raw.type !== "vite-error") return null; 9861 const r = raw; 9862 if (typeof r.message !== "string" || typeof r.file !== "string" || typeof r.frame !== "string" || typeof r.stack !== "string" || typeof r.path !== "string") { 9863 return null; 9864 } 9865 const enc = new TextEncoder(); 9866 const cap = (s, max) => { 9867 const bytes = enc.encode(s); 9868 return bytes.length > max ? null : s; 9869 }; 9870 const message = cap(r.message, VITE_ERROR_CAPS.message); 9871 const file = cap(r.file, VITE_ERROR_CAPS.file); 9872 const frame = cap(r.frame, VITE_ERROR_CAPS.frame); 9873 const stack = cap(r.stack, VITE_ERROR_CAPS.stack); 9874 const path = cap(r.path, VITE_ERROR_CAPS.path); 9875 if (message === null || file === null || frame === null || stack === null || path === null) { 9876 return null; 9877 } 9878 if (!message && !file && !frame) return null; 9879 return { 9880 type: "vite-error", 9881 message, 9882 file, 9883 frame, 9884 stack, 9885 path 9886 }; 9887} 9888function fnv1a(str) { 9889 let hash = 2166136261; 9890 for (let i = 0; i < str.length; i++) { 9891 hash ^= str.charCodeAt(i); 9892 hash = Math.imul(hash, 16777619); 9893 } 9894 return (hash >>> 0).toString(16); 9895} 9896function viteErrorSignature(payload) { 9897 return fnv1a(`${payload.file}|${payload.message}`); 9898} 9899const DiscardEditsConfirmModal = ({ 9900 open, 9901 dirtyPreview = "", 9902 onConfirm, 9903 onCancel 9904}) => { 9905 const truncated = dirtyPreview.length > 80 ? `${dirtyPreview.slice(0, 80)}â¦` : dirtyPreview; 9906 return /* @__PURE__ */ jsxRuntimeExports.jsx(AlertDialog, { open, onOpenChange: (next) => { 9907 if (!next) onCancel(); 9908 }, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(AlertDialogContent, { className: "max-w-sm", children: [ 9909 /* @__PURE__ */ jsxRuntimeExports.jsxs(AlertDialogHeader, { children: [
9910 /* @__PURE__ */ jsxRuntimeExports.jsx(AlertDialogTitle, { children: "Discard your edits?" }), 9911 /* @__PURE__ */ jsxRuntimeExports.jsx(AlertDialogDescription, { children: truncated ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 9912 "You'll lose your changes to", 9913 " ", 9914 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-medium text-[#1B1A22]", children: [ 9915 "â", 9916 truncated, 9917 "â" 9918 ] }), 9919 ". This can't be undone." 9920 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: "You'll lose your unsaved changes. This can't be undone." }) }) 9921 ] }), 9922 /* @__PURE__ */ jsxRuntimeExports.jsxs(AlertDialogFooter, { children: [ 9923 /* @__PURE__ */ jsxRuntimeExports.jsx(AlertDialogCancel, { onClick: onCancel, children: "Keep editing" }), 9924 /* @__PURE__ */ jsxRuntimeExports.jsx(AlertDialogAction, { onClick: onConfirm, className: "bg-red-600 text-white hover:bg-red-700", children: "Discard" }) 9925 ] }) 9926 ] }) }); 9927}; 9928function byHomepageThenPath(a, b) { 9929 if (a.isHomepage !== b.isHomepage) return a.isHomepage ? -1 : 1; 9930 return a.path.localeCompare(b.path); 9931} 9932function filterPages(pages, query) { 9933 const normalized = query.trim().toLowerCase(); 9934 if (normalized === "") { 9935 return [...pages].sort(byHomepageThenPath); 9936 } 9937 const matches = pages.filter((page) => page.path.toLowerCase().includes(normalized) || page.name.toLowerCase().includes(normalized)); 9938 return matches.sort((a, b) => { 9939 const aExact = a.path.toLowerCase() === normalized; 9940 const bExact = b.path.toLowerCase() === normalized; 9941 if (aExact !== bExact) return aExact ? -1 : 1; 9942 return byHomepageThenPath(a, b); 9943 }); 9944} 9945function nextActiveIndex(current, length, direction) { 9946 if (length <= 0) return null; 9947 let safeCurrent; 9948 if (current === null || current < 0 || current >= length) { 9949 safeCurrent = direction === "up" ? length : -1; 9950 } else { 9951 safeCurrent = current; 9952 } 9953 switch (direction) { 9954 case "down": 9955 return (safeCurrent + 1) % length; 9956 case "up": 9957 return (safeCurrent - 1 + length) % length; 9958 case "home": 9959 return 0; 9960 case "end": 9961 return length - 1; 9962 } 9963} 9964const LISTBOX_ID = "pages-typeahead-listbox"; 9965const OPTION_ID_PREFIX = "pages-typeahead-option"; 9966function formatPageLabel(page) { 9967 var _a; 9968 const raw = ((_a = page.name) == null ? void 0 : _a.trim()) || page.path.replace(/^\/+/, "") || (page.isHomepage ? "home" : ""); 9969 if (!raw) return ""; 9970 return raw.charAt(0).toUpperCase() + raw.slice(1); 9971} 9972const PagesTypeahead = reactExports.forwardRef(function PagesTypeahead2(props, ref) { 9973 const { 9974 pages, 9975 query, 9976 currentPath, 9977 anchorRef, 9978 onSelect, 9979 onClose, 9980 onPointerDownInside, 9981 websiteId, 9982 hasCustomDomain = false, 9983 domainName 9984 } = props; 9985 const posthog = usePostHog(); 9986 const filtered = reactExports.useMemo(() => filterPages(pages, query), [pages, query]); 9987 const rowRefs = reactExports.useRef([]); 9988 const [active, setActive] = reactExports.useState(() => { 9989 if (filtered.length === 0) return { 9990 index: null, 9991 source: "init" 9992 }; 9993 const match = filtered.findIndex((p) => p.path === currentPath); 9994 return { 9995 index: match >= 0 ? match : 0, 9996 source: "init" 9997 }; 9998 }); 9999 const hasInitializedRef = reactExports.useRef(false); 10000 reactExports.useEffect(() => { 10001 if (!hasInitializedRef.current) { 10002 hasInitializedRef.current = true; 10003 return; 10004 } 10005 setActive((prev) => { 10006 if (filtered.length === 0) return { 10007 index: null, 10008 source: "init" 10009 }; 10010 if (prev.index !== null && prev.index < filtered.length) return prev; 10011 const match = filtered.findIndex((p) => p.path === currentPath); 10012 return { 10013 index: match >= 0 ? match : 0, 10014 source: "init" 10015 }; 10016 }); 10017 }, [filtered, currentPath]); 10018 reactExports.useEffect(() => { 10019 if (active.source !== "keyboard" || active.index === null) return; 10020 const el = rowRefs.current[active.index]; 10021 el == null ? void 0 : el.scrollIntoView({ 10022 block: "nearest", 10023 behavior: "smooth" 10024 }); 10025 }, [active]); 10026 reactExports.useImperativeHandle(ref, () => ({ 10027 moveActive: (direction) => {
10028 setActive((prev) => ({ 10029 index: nextActiveIndex(prev.index, filtered.length, direction), 10030 source: "keyboard" 10031 })); 10032 }, 10033 selectActive: () => { 10034 if (active.index === null) return null; 10035 const page = filtered[active.index]; 10036 return page ? page.path : null; 10037 } 10038 }), [filtered, active.index]); 10039 const liveRegionMessage = filtered.length === 0 ? "No matching pages" : `${filtered.length} page${filtered.length === 1 ? "" : "s"} available`; 10040 return /* @__PURE__ */ jsxRuntimeExports.jsxs( 10041 PopoverContent, 10042 { 10043 id: LISTBOX_ID, 10044 align: "start", 10045 side: "bottom", 10046 sideOffset: 8, 10047 style: { 10048 width: "var(--radix-popover-trigger-width)", 10049 // Round 5 â white body to match the URL bar in both states. 10050 background: "#FFFFFF", 10051 border: "1px solid rgba(27,26,34,0.06)", 10052 boxShadow: "0 1px 2px rgba(27,26,34,0.04), 0 16px 40px -12px rgba(27,26,34,0.16)", 10053 // Drives the scale direction. The panel scales out from its top 10054 // edge (the direction of the URL bar above) rather than from 10055 // center. 10056 transformOrigin: "top center" 10057 }, 10058 className: cn$1( 10059 "flex flex-col p-0", 10060 "max-h-[min(360px,var(--radix-popover-content-available-height,360px))] overflow-hidden", 10061 // Round 5 â separate rounded card, 16px on all corners. [&]: bumps 10062 // specificity to beat shadcn PopoverContent's default `rounded-md`. 10063 "[&]:rounded-[16px]", 10064 // Emil Kowalski-style layered animation: scaleX + scaleY + translateY 10065 // + opacity. Smooth confident settle on open (cubic-bezier(0.32,0.72,0,1)), 10066 // snappier asymmetric exit (cubic-bezier(0.4,0,1,1)). 10067 "motion-safe:data-[state=open]:animate-[pages-typeahead-rise_280ms_cubic-bezier(0.32,0.72,0,1)]", 10068 "motion-safe:data-[state=closed]:animate-[pages-typeahead-fall_160ms_cubic-bezier(0.4,0,1,1)]" 10069 ), 10070 onOpenAutoFocus: (event) => event.preventDefault(), 10071 onPointerDownOutside: (event) => { 10072 var _a; 10073 if ((_a = anchorRef.current) == null ? void 0 : _a.contains(event.target)) { 10074 event.preventDefault(); 10075 } 10076 }, 10077 onInteractOutside: (event) => { 10078 var _a; 10079 const target = event.target; 10080 if (target && ((_a = anchorRef.current) == null ? void 0 : _a.contains(target))) { 10081 event.preventDefault(); 10082 } 10083 }, 10084 onEscapeKeyDown: () => onClose(), 10085 onPointerDownCapture: () => onPointerDownInside == null ? void 0 : onPointerDownInside(), 10086 children: [
10087 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { role: "status", "aria-live": "polite", className: "sr-only", children: liveRegionMessage }), 10088 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn$1("min-h-0 flex-1 overflow-y-auto p-1", "[mask-image:linear-gradient(to_bottom,black_calc(100%-24px),transparent)]", "[scrollbar-width:none] [&::-webkit-scrollbar]:hidden"), children: filtered.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-3 py-2 text-sm text-muted-foreground", "data-testid": "pages-typeahead-empty", children: query.trim() === "" ? "No pages yet" : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 10089 "No matching pages â Enter navigates to", 10090 " ", 10091 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-medium text-[#1B1A22]", children: [ 10092 "/", 10093 query.trim().replace(/^\/+/, "") 10094 ] }) 10095 ] }) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("ul", { role: "listbox", "aria-label": "Site pages", className: "flex flex-col gap-0.5", children: filtered.map((page, index) => { 10096 const isActive = active.index === index; 10097 const isCurrent = page.path === currentPath; 10098 const Icon = page.isHomepage ? House : FileText; 10099 const optionId = `${OPTION_ID_PREFIX}-${page.id}`; 10100 return /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { ref: (el) => { 10101 rowRefs.current[index] = el; 10102 }, id: optionId, role: "option", "aria-selected": isActive, "aria-current": isCurrent ? "page" : void 0, className: cn$1( 10103 "relative flex cursor-pointer items-center gap-2.5 rounded-[8px] px-2.5 py-1.5 text-sm text-[#1B1A22]", 10104 "transition-colors duration-100 ease-out", 10105 // Hover (keyboard-active OR mouse-over) is a soft wash, 10106 // not a nested glass pill. Granola's hover is a brushed 10107 // full-width tint, no inset highlights, no inner light. 10108 isActive && "bg-[#1B1A22]/[0.035]" 10109 ), onMouseEnter: () => setActive({ 10110 index, 10111 source: "hover" 10112 }), onMouseDown: (event) => { 10113 event.preventDefault(); 10114 onSelect(page.path); 10115 }, children: [
10116 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { "aria-hidden": "true", className: cn$1("flex h-[22px] w-[22px] flex-shrink-0 items-center justify-center rounded-[8px]", "transition-colors duration-100 ease-out", isCurrent ? "bg-[#1B1A22] text-white" : "bg-[#1B1A22]/[0.06] text-[#1B1A22]/70"), children: /* @__PURE__ */ jsxRuntimeExports.jsx(Icon, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) }), 10117 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: cn$1("flex-1 truncate text-[13px]", isCurrent ? "font-semibold" : "font-medium"), children: formatPageLabel(page) }), 10118 (isActive || isCurrent) && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { "aria-hidden": "true", className: "flex-shrink-0 truncate font-mono text-[11.5px] text-[#1B1A22]/40", children: [ 10119 domainName || "yourdomain.com", 10120 page.path 10121 ] }), 10122 isCurrent && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { "aria-hidden": "true", className: "flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-full bg-[#1B1A22] text-white", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "h-3 w-3", strokeWidth: 3 }) }) 10123 ] }, page.id); 10124 }) }) }),
10125 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-shrink-0 items-center justify-between gap-2 border-t border-[#1B1A22]/[0.06] px-1 py-1.5 pr-3", children: [ 10126 !hasCustomDomain && websiteId ? /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { type: "button", onMouseDown: (event) => { 10127 event.preventDefault(); 10128 posthog == null ? void 0 : posthog.capture("Set Up Domain Clicked", { 10129 websiteId, 10130 source: "pages_typeahead", 10131 websiteType: "react" 10132 }); 10133 navigate(namedRoutes.reactWebsiteChatSettingsTab({ 10134 id: websiteId, 10135 tab: "domain" 10136 })); 10137 }, className: cn$1( 10138 // Pill shape (matches URL-bar globe button). Padding stays 10139 // horizontally aligned with the list rows so the globe sits 10140 // directly under the page icons above; vertical padding is 10141 // tight (py-1) for a slim pill. 10142 "group inline-flex items-center gap-2.5 rounded-full px-2.5 py-1 text-xs font-medium", 10143 "text-[#1B1A22]/40 hover:text-green-500 hover:bg-[#d6e9db]", 10144 "transition-colors duration-150 ease-out" 10145 ), children: [
10146 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "relative flex h-[22px] w-[22px] flex-none items-center justify-center", children: [ 10147 /* @__PURE__ */ jsxRuntimeExports.jsx(Globe, { className: "h-3.5 w-3.5 [filter:drop-shadow(0_0.5px_0_rgba(255,255,255,0.7))]", strokeWidth: 2.25 }), 10148 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { "aria-hidden": "true", className: cn$1("pointer-events-none absolute right-[2px] top-[2px] h-1.5 w-1.5 rounded-full", "bg-gradient-to-b from-[#FCD34D] to-[#EAB308]", "shadow-[inset_0_0.5px_0_rgba(255,255,255,0.55),0_0.5px_1px_rgba(146,64,14,0.18),0_0_0_2px_#FFFFFF]", "transition-opacity duration-150 ease-out", "group-hover:opacity-0 group-focus-visible:opacity-0") }) 10149 ] }), 10150 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "whitespace-nowrap leading-5", children: "Set up your domain" }) 10151 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", {}), 10152 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { "aria-hidden": "true", className: "flex items-center gap-2 font-mono text-[10.5px] text-[#1B1A22]/45", children: [
10153 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1", children: [ 10154 /* @__PURE__ */ jsxRuntimeExports.jsx("kbd", { className: "inline-flex h-4 items-center justify-center rounded-[4px] bg-[#1B1A22]/[0.06] px-1 text-[#1B1A22]/60", children: "âµ" }), 10155 "Go" 10156 ] }), 10157 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[#1B1A22]/25", children: "·" }), 10158 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1", children: [ 10159 /* @__PURE__ */ jsxRuntimeExports.jsx("kbd", { className: "inline-flex h-4 items-center justify-center rounded-[4px] bg-[#1B1A22]/[0.06] px-1 text-[#1B1A22]/60", children: "esc" }), 10160 "Cancel" 10161 ] }) 10162 ] }) 10163 ] }) 10164 ] 10165 } 10166 ); 10167}); 10168const VIEWPORT_ORDER = ["desktop", "tablet", "mobile"]; 10169const VIEWPORT_TILE_STEP = 28; 10170function viewportIndicatorOffset(size2) { 10171 const index = VIEWPORT_ORDER.indexOf(size2); 10172 return (index < 0 ? 0 : index) * VIEWPORT_TILE_STEP; 10173} 10174function bootFields(boot) { 10175 if (!boot) return {}; 10176 return { 10177 bootPath: boot.path, 10178 bootDurationMs: boot.durationMs, 10179 bootPhases: boot.phases, 10180 bootViteReady: boot.viteReady, 10181 bootCreatedAt: boot.createdAt 10182 }; 10183} 10184function newBootId() { 10185 return Math.random().toString(36).slice(2, 10); 10186} 10187const stripInternalParams = (path) => { 10188 if (!path.includes("?")) return path; 10189 try { 10190 const url = new URL(path, "http://dummy"); 10191 url.searchParams.delete("_r"); 10192 const search = url.searchParams.toString(); 10193 return url.pathname + (search ? `?${search}` : "") + url.hash; 10194 } catch { 10195 return path.replace(/[?&]_r=[^&]*/, "").replace(/\?$/, ""); 10196 } 10197}; 10198const buildPreviewUrl = (baseUrl, path = "/") => { 10199 const normalizedBase = baseUrl.replace(/\/$/, ""); 10200 const normalizedPath = path.startsWith("/") ? path : `/${path}`; 10201 return `${normalizedBase}${normalizedPath}`; 10202}; 10203const CREATE_TEST_ORDER_MUTATION = gql` 10204 mutation CreateTestOrderFromPreview( 10205 $websiteId: String! 10206 $productId: String 10207 $variantId: String 10208 $quantity: Int 10209 $amount: Int 10210 $items: [TestOrderItemInput!] 10211 $metadata: JSON 10212 $shippingAddress: TestShippingAddressInput 10213 $shippingOption: String 10214 ) { 10215 createTestOrder( 10216 websiteId: $websiteId 10217 productId: $productId 10218 variantId: $variantId 10219 quantity: $quantity 10220 amount: $amount 10221 items: $items 10222 metadata: $metadata 10223 shippingAddress: $shippingAddress 10224 shippingOption: $shippingOption 10225 ) { 10226 orderId 10227 sessionId 10228 } 10229 } 10230`; 10231const CREATE_TEST_SUBSCRIPTION_MUTATION = gql` 10232 mutation CreateTestSubscriptionFromPreview( 10233 $websiteId: String! 10234 $productId: String! 10235 $variantId: String 10236 $planId: String 10237 $metadata: JSON 10238 $shippingAddress: TestShippingAddressInput 10239 ) { 10240 createTestSubscription( 10241 websiteId: $websiteId 10242 productId: $productId 10243 variantId: $variantId 10244 planId: $planId 10245 metadata: $metadata 10246 shippingAddress: $shippingAddress 10247 ) { 10248 orderId 10249 sessionId 10250 subscriptionId 10251 } 10252 } 10253`; 10254const TEST_CHECKOUT_CONTEXT_QUERY = gql` 10255 query TestCheckoutContextForPreview( 10256 $websiteId: String! 10257 $productId: String 10258 $variantId: String 10259 $quantity: Int 10260 $amount: Int 10261 $items: [TestOrderItemInput!] 10262 $planId: String 10263 ) { 10264 testCheckoutContext( 10265 websiteId: $websiteId 10266 productId: $productId 10267 variantId: $variantId 10268 quantity: $quantity 10269 amount: $amount 10270 items: $items 10271 planId: $planId 10272 ) { 10273 subscription { 10274 planId 10275 title 10276 interval 10277 intervalCount 10278 amount 10279 } 10280 collectsShippingAddress 10281 shippingCountries 10282 currency 10283 subtotal 10284 freeShippingOver 10285 stripeTaxEnabled 10286 taxBehavior 10287 lines { 10288 name 10289 variantTitle 10290 quantity 10291 unitAmount 10292 imageUrl 10293 shippable 10294 } 10295 shippingRates { 10296 label 10297 amount 10298 } 10299 } 10300 } 10301`; 10302const TEST_ORDER_PAYMENTS_STATE_QUERY = gql` 10303 query ReactPreviewPanelTestOrderPaymentsState($websiteId: String!) { 10304 websiteCommerceIntent(websiteId: $websiteId) { 10305 paymentsActive 10306 } 10307 } 10308`; 10309const MINT_PREVIEW_SESSION_MUTATION = gql` 10310 mutation MintAifeaturesPreviewSessionFromPreview( 10311 $websiteId: String! 10312 $email: String! 10313 ) { 10314 mintAifeaturesPreviewSession(websiteId: $websiteId, email: $email) { 10315 token 10316 expiresAt 10317 } 10318 } 10319`; 10320const parseTestOrderItems = (raw) => { 10321 if (!Array.isArray(raw) || raw.length < 1 || raw.length > 20) return null; 10322 const items = []; 10323 for (const entry of raw) { 10324 if (typeof entry !== "object" || entry === null) return null; 10325 const { 10326 productId, 10327 variantId, 10328 quantity 10329 } = entry; 10330 if (typeof productId !== "string") return null; 10331 if (variantId !== void 0 && typeof variantId !== "string") return null; 10332 if (quantity !== void 0 && (typeof quantity !== "number" || !Number.isInteger(quantity) || quantity < 1)) return null; 10333 items.push({ 10334 productId, 10335 ...typeof variantId === "string" ? { 10336 variantId 10337 } : {},
10338 ...typeof quantity === "number" ? { 10339 quantity 10340 } : {} 10341 }); 10342 } 10343 return items; 10344}; 10345const parseTestOrderMetadata = (raw) => { 10346 if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return null; 10347 const out = {}; 10348 for (const [key, value] of Object.entries(raw)) { 10349 if (typeof value !== "string") return null; 10350 out[key] = value; 10351 } 10352 return Object.keys(out).length > 0 ? out : null; 10353}; 10354const GLASS_PILL = "bg-[linear-gradient(180deg,rgba(247,247,250,0.97)_0%,rgba(241,241,245,0.94)_100%)] shadow-[inset_0_1px_0_rgba(255,255,255,0.95),inset_0_2px_5px_-1px_rgba(255,255,255,0.55),inset_0_-2px_5px_-3px_rgba(27,26,34,0.08),inset_0_0_0_0.5px_rgba(255,255,255,0.45),inset_0_0_0_1px_rgba(27,26,34,0.07),0_0_4px_rgba(27,26,34,0.04),0_0_13px_-2px_rgba(27,26,34,0.045),0_0_20px_-5px_rgba(27,26,34,0.05)]"; 10355const TOOLBAR_CAPSULE = `flex items-center gap-0 rounded-full p-0.5 ${GLASS_PILL}`; 10356const TOOLBAR_BTN = "flex h-7 w-7 items-center justify-center rounded-full bg-transparent text-[#1B1A22]"; 10357const TOOLBAR_BTN_STYLE = { 10358 transition: "background-color 120ms ease-out, box-shadow 120ms ease-out, transform 90ms ease-out" 10359}; 10360const TOOLBAR_BTN_HAPTIC = "disabled:cursor-not-allowed disabled:opacity-50 enabled:hover:bg-[rgba(27,26,34,0.055)] enabled:hover:shadow-[inset_0_0.5px_0_rgba(255,255,255,0.7),inset_0_0_0_0.5px_rgba(27,26,34,0.04)] enabled:active:scale-[0.94] enabled:active:bg-[rgba(27,26,34,0.10)]"; 10361const composeAgentEditPrompt = (edits) => { 10362 const lines = edits.map((e, i) => { 10363 const where = e.tag ? `in the <${e.tag}>` : "on this page"; 10364 const loc = e.sourceLoc ? ` (source: ${e.sourceLoc})` : ""; 10365 return `${i + 1}. ${where}, change "${e.oldText.trim()}" to "${e.newText.trim()}"${loc}`; 10366 }); 10367 return "Please make these exact text changes on the current page and nothing else:\n" + lines.join("\n"); 10368}; 10369const ReactPreviewPanel = ({ 10370 previewUrl, 10371 containerStatus: initialContainerStatus, 10372 containerError: initialContainerError, 10373 pickModeActive = false, 10374 onPicked, 10375 onPickModeExit, 10376 onIframeReload, 10377 onSelectorReadyChange, 10378 onPickerAvailabilityChange, 10379 onCurrentPathChange, 10380 onViteError, 10381 onViteErrorCleared, 10382 editTextModeActive = false, 10383 onToggleEditTextMode, 10384 onEditTextModeExit, 10385 onApplyWithAI, 10386 isAgentResponding = false, 10387 onEditBusyChange, 10388 pages = [], 10389 websiteId, 10390 hasCustomDomain = false, 10391 domainName, 10392 refreshSignal 10393}) => { 10394 const { 10395 getToken, 10396 currentUser 10397 } = useAuth(); 10398 const posthog = usePostHog(); 10399 const iframeRef = reactExports.useRef(null); 10400 const containerRef = reactExports.useRef(null); 10401 const [isLoading, setIsLoading] = reactExports.useState(true); 10402 const [hasError, setHasError] = reactExports.useState(false); 10403 const [viewportSize, setViewportSize] = reactExports.useState("desktop"); 10404 const [currentPath, setCurrentPath] = reactExports.useState("/"); 10405 const [urlInputValue, setUrlInputValue] = reactExports.useState("/"); 10406 const [historyStack, setHistoryStack] = reactExports.useState(["/"]); 10407 const [historyIndex, setHistoryIndex] = reactExports.useState(0); 10408 const [isTypeaheadOpen, setIsTypeaheadOpen] = reactExports.useState(false); 10409 const [isDomainCtaHovered, setIsDomainCtaHovered] = reactExports.useState(false); 10410 const [isDesktop, setIsDesktop] = reactExports.useState(() => { 10411 if (typeof window === "undefined" || !window.matchMedia) return true; 10412 return window.matchMedia("(min-width: 1024px)").matches; 10413 }); 10414 const formRef = reactExports.useRef(null); 10415 const inputRef = reactExports.useRef(null); 10416 const typeaheadRef = reactExports.useRef(null); 10417 const blurTimerRef = reactExports.useRef(null); 10418 const [iframePages, setIframePages] = reactExports.useState([]); 10419 const [containerSize, setContainerSize] = reactExports.useState({ 10420 width: 0, 10421 height: 0 10422 }); 10423 const [editPersistableCount, setEditPersistableCount] = reactExports.useState(0); 10424 const [editNeedsAgentCount, setEditNeedsAgentCount] = reactExports.useState(0); 10425 const [isSavingEdits, setIsSavingEdits] = reactExports.useState(false); 10426 const [isApplyingWithAI, setIsApplyingWithAI] = reactExports.useState(false); 10427 const aiTurnStartedRef = reactExports.useRef(false); 10428 const pendingFlushResolveRef = reactExports.useRef(null); 10429 const [pendingExitConfirm, setPendingExitConfirm] = reactExports.useState(false); 10430 const [bannerRendered, setBannerRendered] = reactExports.useState(false); 10431 const [bannerExiting, setBannerExiting] = reactExports.useState(false); 10432 const [isNavigating, setIsNavigating] = reactExports.useState(false); 10433 const ignoreNextNavMessage = reactExports.useRef(false); 10434 const [liveContainerStatus, setLiveContainerStatus] = reactExports.useState(null); 10435 const [isContainerStarting, setIsContainerStarting] = reactExports.useState(false); 10436 const loadingStartTimeRef = reactExports.useRef(null); 10437 const [wasEverReady, setWasEverReady] = reactExports.useState(false); 10438 const isContainerStartingRef = reactExports.useRef(false); 10439 const wasEverReadyRef = reactExports.useRef(false); 10440 const containerTimingRef = reactExports.useRef({ 10441 bootId: newBootId(), 10442 pageLoadTime: Date.now(), 10443 iframeLoadStartTime: null, 10444 firstStatusCheckTime: null, 10445 containerReadyTime: null, 10446 iframeLoadCompleteTime: null 10447 }); 10448 const websiteIdRef = reactExports.useRef(websiteId); 10449 websiteIdRef.current = websiteId; 10450 const lastStatusCaptureRef = reactExports.useRef(null); 10451 const shouldCaptureStatus = reactExports.useCallback((outcome, hasBoot) => { 10452 const bootId = containerTimingRef.current.bootId; 10453 const last = lastStatusCaptureRef.current; 10454 const sameLoad = (last == null ? void 0 : last.bootId) === bootId; 10455 const capture = !sameLoad || last.outcome !== outcome || hasBoot && !last.hadB
10455oot; 10456 if (capture) { 10457 lastStatusCaptureRef.current = { 10458 bootId, 10459 outcome, 10460 hadBoot: hasBoot || sameLoad && last.hadBoot 10461 }; 10462 } 10463 return capture; 10464 }, []); 10465 const oneShotStepsRef = reactExports.useRef({ 10466 bootId: "", 10467 steps: /* @__PURE__ */ new Set() 10468 }); 10469 const logContainerTiming = reactExports.useCallback( 10470 (event, extra, opts) => { 10471 const now = Date.now(); 10472 const timing = containerTimingRef.current; 10473 const sincePageLoad = now - timing.pageLoadTime; 10474 const sinceIframeStart = timing.iframeLoadStartTime ? now - timing.iframeLoadStartTime : null; 10475 const payload = { 10476 timestamp: new Date(now).toISOString(), 10477 sincePageLoadMs: sincePageLoad, 10478 sinceIframeStartMs: sinceIframeStart, 10479 ...extra 10480 }; 10481 console.log(`[ContainerTiming] ${event}`, payload); 10482 if ((opts == null ? void 0 : opts.capture) === false) return; 10483 try { 10484 posthog == null ? void 0 : posthog.capture("preview_boot", { 10485 step: event, 10486 bootId: timing.bootId, 10487 websiteId: websiteIdRef.current, 10488 sinceNavigationMs: Math.round(performance.now()), 10489 ...payload 10490 }); 10491 } catch { 10492 } 10493 }, 10494 [posthog] 10495 // posthog is the provider singleton; everything else is a ref 10496 ); 10497 const logOncePerLoad = reactExports.useCallback((step, extra) => { 10498 const bootId = containerTimingRef.current.bootId; 10499 if (oneShotStepsRef.current.bootId !== bootId) { 10500 oneShotStepsRef.current = { 10501 bootId, 10502 steps: /* @__PURE__ */ new Set() 10503 }; 10504 } 10505 if (oneShotStepsRef.current.steps.has(step)) return; 10506 oneShotStepsRef.current.steps.add(step); 10507 logContainerTiming(step, extra); 10508 }, [logContainerTiming]); 10509 const containerStatus = liveContainerStatus || initialContainerStatus; 10510 const containerError = initialContainerError; 10511 const checkContainerStatus = reactExports.useCallback(async () => { 10512 if (!previewUrl) return null; 10513 const checkStartTime = Date.now(); 10514 const requestBootId = containerTimingRef.current.bootId; 10515 if (!containerTimingRef.current.firstStatusCheckTime) { 10516 containerTimingRef.current.firstStatusCheckTime = checkStartTime; 10517 logContainerTiming("first_status_check_start"); 10518 } 10519 try { 10520 const token = await getToken(); 10521 const statusUrl = `${previewUrl.replace(/\/$/, "")}/__container/status`; 10522 const headers = {}; 10523 if (token) { 10524 headers["Authorization"] = `Bearer ${token}`; 10525 } 10526 const response = await fetch(statusUrl, { 10527 headers 10528 }); 10529 if (!response.ok) { 10530 console.error("[ReactPreviewPanel] Status check failed:", response.status); 10531 logContainerTiming("status_check_failed", { 10532 statusCode: response.status, 10533 durationMs: Date.now() - checkStartTime 10534 }, { 10535 capture: shouldCaptureStatus(`failed_${response.status}`, false) 10536 }); 10537 return null; 10538 } 10539 const data = await response.json(); 10540 const capture = shouldCaptureStatus(`ok_${data.status}`, !!data.boot); 10541 logContainerTiming("status_check_complete", { 10542 status: data.status, 10543 durationMs: Date.now() - checkStartTime, 10544 ...bootFields(data.boot) 10545 }, { 10546 capture 10547 }); 10548 if (["running", "healthy", "warm"].includes(data.status) && containerTimingRef.current.bootId === requestBootId) { 10549 logOncePerLoad("container_first_ready", { 10550 status: data.status, 10551 ...bootFields(data.boot) 10552 }); 10553 } 10554 return data; 10555 } catch (error) { 10556 console.error("[ReactPreviewPanel] Status check error:", error); 10557 logContainerTiming("status_check_error", { 10558 error: error instanceof Error ? error.message : String(error), 10559 durationMs: Date.now() - checkStartTime 10560 }, { 10561 capture: shouldCaptureStatus("error", false) 10562 }); 10563 return null; 10564 } 10565 }, [previewUrl, getToken, logContainerTiming, shouldCaptureStatus, logOncePerLoad]); 10566 reactExports.useEffect(() => { 10567 if (!isLoading || !previewUrl) { 10568 loadingStartTimeRef.current = null; 10569 return; 10570 } 10571 if (!loadingStartTimeRef.current) { 10572 loadingStartTimeRef.current = Date.now(); 10573 } 10574 let pollInterval = null; 10575 let isCancelled = false;
10576 const pollStatus = async () => { 10577 const loadingDuration = Date.now() - (loadingStartTimeRef.current || 0); 10578 if (loadingDuration < 3e3) return; 10579 const status = await checkContainerStatus(); 10580 if (isCancelled) return; 10581 if (status) { 10582 setLiveContainerStatus(status.status); 10583 if (["running", "healthy", "warm"].includes(status.status)) { 10584 if (!wasEverReadyRef.current) { 10585 containerTimingRef.current.containerReadyTime = Date.now(); 10586 wasEverReadyRef.current = true; 10587 setWasEverReady(true); 10588 } 10589 } 10590 const isStarting = !["running", "healthy", "warm"].includes(status.status); 10591 const wasStarting = isContainerStartingRef.current; 10592 isContainerStartingRef.current = isStarting; 10593 setIsContainerStarting(isStarting); 10594 if (["running", "healthy", "warm"].includes(status.status) && wasStarting) { 10595 logContainerTiming("container_became_ready_reloading_iframe", { 10596 status: status.status 10597 }); 10598 isCancelled = true; 10599 if (pollInterval) clearInterval(pollInterval); 10600 if (iframeRef.current) { 10601 setTimeout(() => { 10602 if (iframeRef.current) { 10603 const currentSrc = iframeRef.current.src; 10604 const url = new URL(currentSrc); 10605 url.searchParams.set("_r", Date.now().toString()); 10606 iframeRef.current.src = url.toString(); 10607 logContainerTiming("iframe_reload_triggered"); 10608 } 10609 }, 500); 10610 } 10611 } 10612 } 10613 }; 10614 const initialTimeout = setTimeout(pollStatus, 3e3); 10615 pollInterval = setInterval(pollStatus, 2e3); 10616 return () => { 10617 isCancelled = true; 10618 clearTimeout(initialTimeout); 10619 if (pollInterval) clearInterval(pollInterval); 10620 }; 10621 }, [isLoading, previewUrl, checkContainerStatus, logContainerTiming]); 10622 const viewportConfig = { 10623 mobile: { 10624 width: 393, 10625 height: 852 10626 }, 10627 // iPhone 15/16 effective resolution 10628 tablet: { 10629 width: 768, 10630 height: 1024 10631 }, 10632 desktop: { 10633 width: 1512, 10634 height: 982 10635 } 10636 // MacBook Pro 14" effective resolution 10637 }; 10638 const config = viewportConfig[viewportSize]; 10639 reactExports.useEffect(() => { 10640 if (editTextModeActive) return; 10641 setPendingExitConfirm(false); 10642 }, [editTextModeActive]); 10643 reactExports.useEffect(() => { 10644 if (editTextModeActive) { 10645 setBannerRendered(true); 10646 setBannerExiting(false); 10647 return; 10648 } 10649 if (!bannerRendered) return; 10650 setBannerExiting(true); 10651 const t = window.setTimeout(() => { 10652 setBannerRendered(false); 10653 setBannerExiting(false); 10654 }, 320); 10655 return () => window.clearTimeout(t); 10656 }, [editTextModeActive, bannerRendered]); 10657 reactExports.useEffect(() => { 10658 const container = containerRef.current; 10659 if (!container) return; 10660 const resizeObserver = new ResizeObserver((entries) => { 10661 const entry = entries[0]; 10662 if (entry) { 10663 setContainerSize({ 10664 width: entry.contentRect.width, 10665 height: entry.contentRect.height 10666 }); 10667 } 10668 }); 10669 resizeObserver.observe(container); 10670 return () => resizeObserver.disconnect(); 10671 }, []); 10672 reactExports.useEffect(() => { 10673 if (typeof window === "undefined" || !window.matchMedia) return; 10674 const mql = window.matchMedia("(min-width: 1024px)"); 10675 const onChange = (event) => setIsDesktop(event.matches); 10676 setIsDesktop(mql.matches); 10677 if (mql.addEventListener) { 10678 mql.addEventListener("change", onChange); 10679 return () => mql.removeEventListener("change", onChange); 10680 } 10681 mql.addListener(onChange); 10682 return () => mql.removeListener(onChange); 10683 }, []); 10684 reactExports.useEffect(() => { 10685 const handleRoutesMessage = (event) => { 10686 var _a; 10687 if (((_a = event.data) == null ? void 0 : _a.type) !== "routes-manifest") return; 10688 const routes2 = event.data.routes; 10689 if (!Array.isArray(routes2)) return; 10690 const sanitized = routes2.filter((r) => !!r && typeof r === "object" && typeof r.id === "string" && typeof r.path === "string" && typeof r.name === "string" && typeof r.isHomepage === "boolean").map((r) => ({ 10691 id: r.id, 10692 name: r.name, 10693 path: r.path, 10694 isHomepage: r.isHomepage 10695 })); 10696 setIframePages(sanitized); 10697 }; 10698 window.addEventListener("message", handleRoutesMessage); 10699 const requestManifest = () => { 10700 const iframe = iframeRef.current; 10701 if (!(iframe == null ? void 0 : iframe.contentWindow)) return;
10702 try { 10703 iframe.contentWindow.postMessage({ 10704 type: "request-routes-manifest" 10705 }, "*"); 10706 } catch { 10707 } 10708 }; 10709 const t1 = window.setTimeout(requestManifest, 500); 10710 const t2 = window.setTimeout(requestManifest, 1500); 10711 const t3 = window.setTimeout(requestManifest, 3500); 10712 return () => { 10713 window.removeEventListener("message", handleRoutesMessage); 10714 window.clearTimeout(t1); 10715 window.clearTimeout(t2); 10716 window.clearTimeout(t3); 10717 }; 10718 }, [previewUrl]); 10719 const effectivePages = iframePages.length > 0 ? iframePages : pages; 10720 const isPreviewLoading = isLoading || isNavigating || effectivePages.length === 0; 10721 const getScaleAndDimensions = () => { 10722 const targetWidth = typeof config.width === "number" ? config.width : 0; 10723 const targetHeight = typeof config.height === "number" ? config.height : 0; 10724 if (containerSize.width > 0 && containerSize.height > 0) { 10725 if (viewportSize === "desktop") { 10726 const scale3 = containerSize.width / targetWidth; 10727 const scaledHeight = containerSize.height / scale3; 10728 return { 10729 width: targetWidth, 10730 // 1280px - content renders at desktop width 10731 height: scaledHeight, 10732 // calculated to fill container when scaled 10733 scale: scale3, 10734 needsScaling: true 10735 // always scale for desktop to ensure consistent rendering 10736 }; 10737 } 10738 const padding = 32; 10739 const availableWidth = containerSize.width - padding; 10740 const scale2 = Math.min(availableWidth / targetWidth, 1); 10741 return { 10742 width: targetWidth, 10743 height: targetHeight, 10744 scale: scale2, 10745 needsScaling: scale2 < 1 10746 }; 10747 } 10748 return { 10749 width: targetWidth, 10750 height: targetHeight, 10751 scale: 1, 10752 needsScaling: false 10753 }; 10754 }; 10755 const { 10756 width: iframeWidth, 10757 height: iframeHeight, 10758 scale, 10759 needsScaling 10760 } = getScaleAndDimensions(); 10761 reactExports.useEffect(() => { 10762 setUrlInputValue(currentPath); 10763 }, [currentPath]); 10764 reactExports.useEffect(() => { 10765 if (previewUrl && !containerTimingRef.current.iframeLoadStartTime) { 10766 containerTimingRef.current.iframeLoadStartTime = Date.now(); 10767 checkContainerStatus().then((statusResponse) => { 10768 if (statusResponse) { 10769 setLiveContainerStatus(statusResponse.status); 10770 } 10771 logContainerTiming("iframe_initial_load_start", { 10772 previewUrl: previewUrl.replace(/token=[^&]+/, "token=***"), 10773 initialStatus: (statusResponse == null ? void 0 : statusResponse.status) ?? "fetch_failed" 10774 }); 10775 }); 10776 } 10777 }, [previewUrl, logContainerTiming, checkContainerStatus]); 10778 reactExports.useEffect(() => { 10779 const handleMessage = (event) => { 10780 var _a, _b; 10781 if (!previewUrl) return; 10782 if (((_a = event.data) == null ? void 0 : _a.type) !== "navigation" || !((_b = event.data) == null ? void 0 : _b.path)) return; 10783 const newPath = stripInternalParams(event.data.path); 10784 const navState = event.data.state; 10785 const isInFlight = navState !== void 0 && navState !== "idle"; 10786 setIsNavigating(isInFlight); 10787 if (isInFlight) { 10788 setUrlInputValue(newPath); 10789 return; 10790 } 10791 if (ignoreNextNavMessage.current) { 10792 ignoreNextNavMessage.current = false; 10793 setCurrentPath(newPath); 10794 return; 10795 } 10796 if (newPath !== currentPath) { 10797 setCurrentPath(newPath); 10798 setHistoryStack((prev) => [...prev.slice(0, historyIndex + 1), newPath]); 10799 setHistoryIndex((prev) => prev + 1); 10800 } 10801 }; 10802 window.addEventListener("message", handleMessage); 10803 return () => window.removeEventListener("message", handleMessage); 10804 }, [previewUrl, currentPath, historyIndex]); 10805 const testOrderInFlight = reactExports.useRef(false); 10806 const [createTestOrderMutation] = useMutation$1(CREATE_TEST_ORDER_MUTATION); 10807 const [createTestSubscriptionMutation] = useMutation$1(CREATE_TEST_SUBSCRIPTION_MUTATION); 10808 const [loadTestCheckoutContext] = useLazyQuery(TEST_CHECKOUT_CONTEXT_QUERY, { 10809 fetchPolicy: "network-only" 10810 }); 10811 const [loadPaymentsState] = useLazyQuery(TEST_ORDER_PAYMENTS_STATE_QUERY, { 10812 // Payments can go live mid-session (the merchant finishes Stripe in
10813 // another tab) â a cached "not active" would pitch an upgrade to someone 10814 // who just did it. 10815 fetchPolicy: "network-only" 10816 }); 10817 const [testCheckout, setTestCheckout] = reactExports.useState(null); 10818 const [testCheckoutSubmitting, setTestCheckoutSubmitting] = reactExports.useState(false); 10819 const testCheckoutReplyRef = reactExports.useRef(null); 10820 const closeTestCheckout = reactExports.useCallback(() => { 10821 setTestCheckout(null); 10822 setTestCheckoutSubmitting(false); 10823 testCheckoutReplyRef.current = null; 10824 testOrderInFlight.current = false; 10825 }, []); 10826 const cancelTestCheckout = reactExports.useCallback(() => { 10827 var _a; 10828 (_a = testCheckoutReplyRef.current) == null ? void 0 : _a.call(testCheckoutReplyRef, "failed", "Test purchase cancelled"); 10829 closeTestCheckout(); 10830 }, [closeTestCheckout]); 10831 const submitTestCheckout = reactExports.useCallback(async (submission) => { 10832 var _a, _b, _c, _d; 10833 if (!testCheckout) return; 10834 setTestCheckoutSubmitting(true); 10835 const paymentsStatePromise = websiteId ? loadPaymentsState({ 10836 variables: { 10837 websiteId 10838 } 10839 }).then(({ 10840 data: stateData 10841 }) => { 10842 var _a2; 10843 return ((_a2 = stateData == null ? void 0 : stateData.websiteCommerceIntent) == null ? void 0 : _a2.paymentsActive) !== false; 10844 }).catch(() => true) : Promise.resolve(true); 10845 try { 10846 let sessionId; 10847 if (testCheckout.planId) { 10848 const { 10849 data: result 10850 } = await createTestSubscriptionMutation({ 10851 variables: { 10852 websiteId: testCheckout.variables.websiteId, 10853 productId: testCheckout.variables.productId, 10854 variantId: testCheckout.variables.variantId ?? null, 10855 planId: testCheckout.planId, 10856 metadata: testCheckout.metadata, 10857 shippingAddress: submission.shippingAddress 10858 } 10859 }); 10860 sessionId = (_a = result == null ? void 0 : result.createTestSubscription) == null ? void 0 : _a.sessionId; 10861 } else { 10862 const { 10863 data: result 10864 } = await createTestOrderMutation({ 10865 variables: { 10866 ...testCheckout.variables, 10867 metadata: testCheckout.metadata, 10868 shippingAddress: submission.shippingAddress, 10869 shippingOption: submission.shippingOption 10870 } 10871 }); 10872 sessionId = (_b = result == null ? void 0 : result.createTestOrder) == null ? void 0 : _b.sessionId; 10873 } 10874 if (sessionId && iframeRef.current && previewUrl) { 10875 const paymentsActive = await paymentsStatePromise; 10876 const testNoun = testCheckout.planId ? "signup" : "purchase"; 10877 if (paymentsActive) { 10878 _t.success(`Test ${testNoun} created â no payment was collected. Test orders only happen here in the editor; on your published site this is a real charge.`, { 10879 duration: 8e3 10880 }); 10881 } else { 10882 _t.success(`Test ${testNoun} created â no payment was collected. Test orders only happen here in the editor â on your published site the button reads "Payments not set up" until you upgrade to Pro and connect Stripe.`, { 10883 duration: 8e3 10884 }); 10885 } 10886 iframeRef.current.src = buildPreviewUrl(previewUrl, `/order/confirmation?session_id=${encodeURIComponent(sessionId)}`); 10887 closeTestCheckout(); 10888 } else { 10889 (_c = testCheckoutReplyRef.current) == null ? void 0 : _c.call(testCheckoutReplyRef, "failed", "Could not create the test purchase"); 10890 _t.error("Could not create the test purchase"); 10891 closeTestCheckout(); 10892 } 10893 } catch (err) { 10894 const message = err instanceof Error ? err.message : "Could not create the test purchase"; 10895 (_d = testCheckoutReplyRef.current) == null ? void 0 : _d.call(testCheckoutReplyRef, "failed", message); 10896 _t.error(message); 10897 closeTestCheckout(); 10898 } 10899 }, [testCheckout, createTestOrderMutation, createTestSubscriptionMutation, previewUrl, closeTestCheckout, websiteId, loadPaymentsState]); 10900 reactExports.useEffect(() => { 10901 const handleTestCheckout = async (event) => { 10902 var _a, _b; 10903 if (!iframeRef.current || event.source !== iframeRef.current.contentWindow) { 10904 return; 10905 } 10906 if (!previewUrl) return;
10907 try { 10908 if (event.origin !== new URL(previewUrl).origin) return; 10909 } catch { 10910 return; 10911 } 10912 const data = event.data; 10913 if ((data == null ? void 0 : data.type) !== "aifeatures:test-checkout") return; 10914 if (!websiteId) return; 10915 const cartItems = "items" in data ? parseTestOrderItems(data.items) : null; 10916 if ("items" in data && cartItems === null) return; 10917 if (cartItems === null && typeof data.productId !== "string") return; 10918 const previewOrigin = new URL(previewUrl).origin; 10919 const requestId = typeof data.requestId === "string" ? data.requestId : void 0; 10920 const reply = (status, error) => { 10921 var _a2; 10922 try { 10923 ; 10924 (_a2 = event.source) == null ? void 0 : _a2.postMessage({ 10925 type: "aifeatures:test-checkout-ack", 10926 status, 10927 error, 10928 ...requestId !== void 0 ? { 10929 requestId 10930 } : {} 10931 }, previewOrigin); 10932 } catch { 10933 } 10934 }; 10935 if (testOrderInFlight.current) { 10936 reply("accepted"); 10937 return; 10938 } 10939 testOrderInFlight.current = true; 10940 reply("accepted"); 10941 testCheckoutReplyRef.current = reply; 10942 const planId = !cartItems && typeof data.planId === "string" ? data.planId : null; 10943 const variables = cartItems ? { 10944 websiteId, 10945 items: cartItems 10946 } : { 10947 websiteId, 10948 productId: data.productId, 10949 variantId: typeof data.variantId === "string" ? data.variantId : null, 10950 quantity: typeof data.quantity === "number" ? data.quantity : null, 10951 amount: typeof data.amount === "number" ? data.amount : null 10952 }; 10953 let ctx = { 10954 collectsShippingAddress: false, 10955 shippingCountries: null, 10956 currency: "usd", 10957 lines: [], 10958 subtotal: 0, 10959 shippingRates: [], 10960 freeShippingOver: null, 10961 stripeTaxEnabled: false, 10962 taxBehavior: null, 10963 subscription: null 10964 }; 10965 try { 10966 const { 10967 data: ctxData 10968 } = await loadTestCheckoutContext({ 10969 variables: cartItems ? { 10970 websiteId, 10971 items: cartItems 10972 } : { 10973 websiteId, 10974 productId: data.productId, 10975 variantId: typeof data.variantId === "string" ? data.variantId : null, 10976 quantity: typeof data.quantity === "number" ? data.quantity : null, 10977 amount: typeof data.amount === "number" ? data.amount : null, 10978 planId 10979 } 10980 }); 10981 if (planId && ((_b = (_a = ctxData == null ? void 0 : ctxData.testCheckoutContext) == null ? void 0 : _a.subscription) == null ? void 0 : _b.planId) !== planId) { 10982 throw new Error("Could not load this subscription plan. Please try again."); 10983 } 10984 if (ctxData == null ? void 0 : ctxData.testCheckoutContext) ctx = ctxData.testCheckoutContext; 10985 } catch (err) { 10986 if (planId) { 10987 const message = err instanceof Error ? err.message : "Could not load this subscription plan. Please try again."; 10988 reply("failed", message); 10989 _t.error(message); 10990 closeTestCheckout(); 10991 return; 10992 } 10993 } 10994 setTestCheckout({ 10995 variables, 10996 metadata: parseTestOrderMetadata(data.metadata), 10997 context: ctx, 10998 planId 10999 }); 11000 }; 11001 window.addEventListener("message", handleTestCheckout); 11002 return () => window.removeEventListener("message", handleTestCheckout); 11003 }, [websiteId, previewUrl, loadTestCheckoutContext, closeTestCheckout]); 11004 const [mintPreviewSessionMutation] = useMutation$1(MINT_PREVIEW_SESSION_MUTATION); 11005 reactExports.useEffect(() => { 11006 const handlePreviewSession = async (event) => { 11007 var _a; 11008 if (!iframeRef.current || event.source !== iframeRef.current.contentWindow) { 11009 return; 11010 } 11011 if (!previewUrl) return; 11012 try { 11013 if (event.origin !== new URL(previewUrl).origin) return; 11014 } catch { 11015 return; 11016 } 11017 const data = event.data; 11018 if ((data == null ? void 0 : data.type) !== "aifeatures:preview-session") return; 11019 if (!websiteId) return; 11020 if (typeof data.requestId !== "string" || !data.requestId) return; 11021 if (typeof data.email !== "string" || !data.email) return; 11022 const previewOrigin = new URL(previewUrl).origin; 11023 const requestId = data.requestId; 11024 const reply = (payload) => { 11025 var _a2; 11026 try { 11027 ; 11028 (_a2 = event.source) == null ? void 0 : _a2.postMessage({ 11029 type: "aifeatures:preview-session-ack", 11030 requestId, 11031 ...payload 11032 }, previewOrigin); 11033 } catch { 11034 } 11035 };
11036 try { 11037 const { 11038 data: result 11039 } = await mintPreviewSessionMutation({ 11040 variables: { 11041 websiteId, 11042 email: data.email 11043 } 11044 }); 11045 const token = (_a = result == null ? void 0 : result.mintAifeaturesPreviewSession) == null ? void 0 : _a.token; 11046 if (typeof token === "string" && token) { 11047 reply({ 11048 token 11049 }); 11050 } else { 11051 reply({ 11052 error: "The preview sign-in could not be created." 11053 }); 11054 } 11055 } catch (err) { 11056 reply({ 11057 error: err instanceof Error && err.message ? err.message : "The preview sign-in could not be created." 11058 }); 11059 } 11060 }; 11061 window.addEventListener("message", handlePreviewSession); 11062 return () => window.removeEventListener("message", handlePreviewSession); 11063 }, [websiteId, previewUrl, mintPreviewSessionMutation]); 11064 const [isSelectorReady, setIsSelectorReady] = reactExports.useState(false); 11065 const onPickedRef = reactExports.useRef(onPicked); 11066 const onPickModeExitRef = reactExports.useRef(onPickModeExit); 11067 const onEditTextModeExitRef = reactExports.useRef(onEditTextModeExit); 11068 const onApplyWithAIRef = reactExports.useRef(onApplyWithAI); 11069 const onIframeReloadRef = reactExports.useRef(onIframeReload); 11070 const onSelectorReadyChangeRef = reactExports.useRef(onSelectorReadyChange); 11071 const onPickerAvailabilityChangeRef = reactExports.useRef(onPickerAvailabilityChange); 11072 const onCurrentPathChangeRef = reactExports.useRef(onCurrentPathChange); 11073 const pickModeActiveRef = reactExports.useRef(pickModeActive); 11074 reactExports.useEffect(() => { 11075 onPickedRef.current = onPicked; 11076 }, [onPicked]); 11077 reactExports.useEffect(() => { 11078 onPickModeExitRef.current = onPickModeExit; 11079 }, [onPickModeExit]); 11080 reactExports.useEffect(() => { 11081 onEditTextModeExitRef.current = onEditTextModeExit; 11082 }, [onEditTextModeExit]); 11083 reactExports.useEffect(() => { 11084 onApplyWithAIRef.current = onApplyWithAI; 11085 }, [onApplyWithAI]); 11086 reactExports.useEffect(() => { 11087 onIframeReloadRef.current = onIframeReload; 11088 }, [onIframeReload]); 11089 reactExports.useEffect(() => { 11090 onSelectorReadyChangeRef.current = onSelectorReadyChange; 11091 }, [onSelectorReadyChange]); 11092 reactExports.useEffect(() => { 11093 onPickerAvailabilityChangeRef.current = onPickerAvailabilityChange; 11094 }, [onPickerAvailabilityChange]); 11095 reactExports.useEffect(() => { 11096 onCurrentPathChangeRef.current = onCurrentPathChange; 11097 }, [onCurrentPathChange]); 11098 reactExports.useEffect(() => { 11099 pickModeActiveRef.current = pickModeActive; 11100 }, [pickModeActive]); 11101 reactExports.useEffect(() => { 11102 var _a; 11103 (_a = onCurrentPathChangeRef.current) == null ? void 0 : _a.call(onCurrentPathChangeRef, currentPath); 11104 }, [currentPath]); 11105 const iframeOrigin = React.useMemo(() => { 11106 if (!previewUrl) return null; 11107 try { 11108 return new URL(previewUrl).origin; 11109 } catch { 11110 return null; 11111 } 11112 }, [previewUrl]); 11113 const isPickerOriginAllowed = reactExports.useCallback((origin) => { 11114 let url; 11115 try { 11116 url = new URL(origin); 11117 } catch { 11118 return false; 11119 } 11120 const host = url.hostname.toLowerCase(); 11121 if (host === "landingsite.dev" || host.endsWith(".landingsite.dev")) { 11122 return true; 11123 } 11124 if (host === "agcy.dev" || host.endsWith(".agcy.dev")) { 11125 return true; 11126 } 11127 if ((host === "localhost" || host === "127.0.0.1") && url.port === "8792") { 11128 return true; 11129 } 11130 return false; 11131 }, []); 11132 const parsePickerMessage$1 = reactExports.useCallback((data) => parsePickerMessage(data), []); 11133 reactExports.useEffect(() => { 11134 var _a; 11135 (_a = onSelectorReadyChangeRef.current) == null ? void 0 : _a.call(onSelectorReadyChangeRef, isSelectorReady); 11136 }, [isSelectorReady]); 11137 reactExports.useEffect(() => { 11138 const handlePickerMessage = (event) => { 11139 var _a, _b, _c, _d, _e, _f; 11140 if (!isPickerOriginAllowed(event.origin)) return; 11141 if (!iframeRef.current || event.source !== iframeRef.current.contentWindow) { 11142 return; 11143 } 11144 const msg = parsePickerMessage$1(event.data); 11145 if (!msg) { 11146 const t = (_a = event.data) == null ? void 0 : _a.type; 11147 if (typeof t === "string" && ["selector-probe", "enter-select", "cancel-select", "selector-ready", "enter-select-ack", "enter-edit-text-ack", "selected", "cancel-select-request", "edit-queue-count", "text-edit-batch"].includes(t)) { 11148 console.warn("[ReactPreviewPanel] Rejected picker message: payload validation failed", { 11149 type: t 11150 }); 11151 } 11152 return; 11153 } 11154 switch (msg.type) { 11155 case "selector-ready": { 11156 setIsSelectorReady(true); 11157 if (selectorProbeTimerRef.current !== null) { 11158 clearTimeout(selectorProbeTimerRef.current); 11159 selectorProbeTimerRef.current = null; 11160 } 11161 break; 11162 } 11163 case "enter-select-ack": { 11164 if (enterSelectAckTimerRef.current !== null) { 11165 clearTimeout(enterSelectAckTimerRef.current); 11166 enterSelectAckTimerRef.current = null; 11167 } 11168 break; 11169 } 11170 case "enter-edit-text-ack": { 11171 if (enterEditTextAckTimerRef.current !== null) { 11172 clearTimeout(enterEditTextAckTimerRef.current); 11173 enterEditTextAckTimerRef.current = null; 11174 } 11175 break; 11176 } 11177 case "selected": { 11178 if (!pickModeActiveRef.current) { 11179 (_c = (_b = iframeRef.current) == null ? void 0 : _b.contentWindow) == null ? void 0 : _c.postMessage({ 11180 type: "cancel-select" 11181 }, event.origin); 11182 return; 11183 } 11184 if (enterSelectAckTimerRef.current !== null) { 11185 clearTimeout(enterSelectAckTimerRef.current); 11186 enterSelectAckTimerRef.current = null; 11187 } 11188 const { 11189 outerHTML, 11190 tag, 11191 textPreview, 11192 path, 11193 sourceLoc 11194 } = msg; 11195 (_d = onPickedRef.current) == null ? void 0 : _d.call(onPickedRef, { 11196 outerHTML, 11197 tag, 11198 textPreview, 11199 path, 11200 sourceLoc 11201 }); 11202 break; 11203 } 11204 case "cancel-select-request": { 11205 if (enterSelectAckTimerRef.current !== null) { 11206 clearTimeout(enterSelectAckTimerRef.current); 11207 enterSelectAckTimerRef.current = null; 11208 } 11209 (_e = onPickModeExitRef.current) == null ? void 0 : _e.call(onPickModeExitRef); 11210 break; 11211 } 11212 case "edit-queue-count": { 11213 setEditPersistableCount(msg.persistable); 11214 setEditNeedsAgentCount(Math.max(0, msg.count - msg.persistable)); 11215 break; 11216 } 11217 case "text-edit-batch": { 11218 (_f = pendingFlushResolveRef.current) == null ? void 0 : _f.call(pendingFlushResolveRef, { 11219 edits: msg.edits, 11220 agentEdits: msg.agentEdits 11221 }); 11222 pendingFlushResolveRef.current = null; 11223 break; 11224 } 11225 } 11226 }; 11227 window.addEventListener("message", handlePickerMessage); 11228 return () => window.removeEventListener("message", handlePickerMessage); 11229 }, [isPickerOriginAllowed, parsePickerMessage$1]); 11230 const onViteErrorRef = reactExports.useRef(onViteError); 11231 const onViteErrorClearedRef = reactExports.useRef(onViteErrorCleared); 11232 reactExports.useEffect(() => { 11233 onViteErrorRef.current = onViteError; 11234 }, [onViteError]); 11235 reactExports.useEffect(() => { 11236 onViteErrorClearedRef.current = onViteErrorCleared; 11237 }, [onViteErrorCleared]); 11238 reactExports.useEffect(() => { 11239 const handleViteErrorMessage = (event) => { 11240 var _a, _b, _c; 11241 if (!isPickerOriginAllowed(event.origin)) return; 11242 if (!iframeRef.current || event.source !== iframeRef.current.contentWindow) { 11243 return; 11244 } 11245 const msg = parseViteErrorMessage(event.data); 11246 if (!msg) { 11247 if (event.data && typeof event.data === "object" && (event.data.type === "vite-error" || event.data.type === "vite-error-cleared")) { 11248 console.log("[ls:preview] vite-error msg dropped by parser", { 11249 origin: event.origin, 11250 data: event.data 11251 }); 11252 } 11253 return; 11254 } 11255 if (msg.type === "vite-error") { 11256 const { 11257 type: _t2, 11258 ...payload 11259 } = msg; 11260 livePreviewErrorRef.current = true; 11261 setHasPreviewError(true); 11262 console.log("[ls:preview] vite-error received", { 11263 origin: event.origin, 11264 message: (_a = payload.message) == null ? void 0 : _a.slice(0, 80), 11265 file: payload.file 11266 }); 11267 (_b = onViteErrorRef.current) == null ? void 0 : _b.call(onViteErrorRef, payload); 11268 } else { 11269 livePreviewErrorRef.current = false;
11270 setHasPreviewError(false); 11271 console.log("[ls:preview] vite-error-cleared received", { 11272 origin: event.origin 11273 }); 11274 (_c = onViteErrorClearedRef.current) == null ? void 0 : _c.call(onViteErrorClearedRef); 11275 } 11276 }; 11277 window.addEventListener("message", handleViteErrorMessage); 11278 return () => window.removeEventListener("message", handleViteErrorMessage); 11279 }, [isPickerOriginAllowed]); 11280 reactExports.useEffect(() => { 11281 const handlePreviewDocMessage = (event) => { 11282 if (!iframeRef.current || event.source !== iframeRef.current.contentWindow) { 11283 return; 11284 } 11285 const data = event.data; 11286 if (!data || typeof data !== "object" || data.type !== "ls-preview-doc") { 11287 return; 11288 } 11289 logOncePerLoad(data.healthy === true ? "iframe_content_rendered" : "iframe_error_rendered"); 11290 }; 11291 const PERF_FIELDS = ["ttfbMs", "domContentLoadedMs", "loadMs", "fcpMs", "resourceCount", "scriptCount", "styleCount", "transferKB", "lastResourceEndMs", "slowestResourceMs"]; 11292 const handlePreviewPerfMessage = (event) => { 11293 if (!iframeRef.current || event.source !== iframeRef.current.contentWindow) { 11294 return; 11295 } 11296 const data = event.data; 11297 if (!data || typeof data !== "object" || data.type !== "ls-preview-perf") { 11298 return; 11299 } 11300 const props = {}; 11301 for (const field of PERF_FIELDS) { 11302 const value = data[field]; 11303 if (typeof value === "number" && Number.isFinite(value)) { 11304 props[field] = value; 11305 } 11306 } 11307 logOncePerLoad("iframe_page_timing", props); 11308 }; 11309 window.addEventListener("message", handlePreviewDocMessage); 11310 window.addEventListener("message", handlePreviewPerfMessage); 11311 return () => { 11312 window.removeEventListener("message", handlePreviewDocMessage); 11313 window.removeEventListener("message", handlePreviewPerfMessage); 11314 }; 11315 }, [logOncePerLoad]); 11316 const lastStartupErrorRef = reactExports.useRef(void 0); 11317 const hasStartupErrorRef = reactExports.useRef(false); 11318 const livePreviewErrorRef = reactExports.useRef(false); 11319 const [hasPreviewError, setHasPreviewError] = reactExports.useState(false); 11320 const currentPathRef = reactExports.useRef(currentPath); 11321 reactExports.useEffect(() => { 11322 currentPathRef.current = currentPath; 11323 }, [currentPath]); 11324 reactExports.useEffect(() => { 11325 if (!previewUrl) return; 11326 let cancelled = false; 11327 const tick = async () => { 11328 var _a, _b; 11329 if (typeof document !== "undefined" && document.visibilityState === "hidden") { 11330 return; 11331 } 11332 const status = await checkContainerStatus(); 11333 if (cancelled || !status) return; 11334 const next = (status.startupError ?? "").trim() || null; 11335 const prev = lastStartupErrorRef.current; 11336 if (next === prev) return; 11337 lastStartupErrorRef.current = next; 11338 hasStartupErrorRef.current = next !== null; 11339 if (next !== null) { 11340 const frame = next.slice(0, 4 * 1024); 11341 setHasPreviewError(true); 11342 (_a = onViteErrorRef.current) == null ? void 0 : _a.call(onViteErrorRef, { 11343 message: "Vite preview server failed to start", 11344 file: "", 11345 frame, 11346 stack: "", 11347 path: currentPathRef.current 11348 }); 11349 } else { 11350 if (typeof prev === "string" || !livePreviewErrorRef.current) { 11351 setHasPreviewError(false); 11352 (_b = onViteErrorClearedRef.current) == null ? void 0 : _b.call(onViteErrorClearedRef); 11353 } 11354 if (typeof prev === "string" && iframeRef.current) { 11355 const recoverUrl = new URL(buildPreviewUrl(previewUrl, currentPathRef.current)); 11356 recoverUrl.searchParams.set("_r", Date.now().toString()); 11357 iframeRef.current.src = recoverUrl.toString(); 11358 } 11359 } 11360 }; 11361 const initialTimeout = setTimeout(tick, 4e3); 11362 const interval = setInterval(tick, 15e3); 11363 const onVisible = () => { 11364 if (document.visibilityState === "visible") void tick(); 11365 }; 11366 document.addEventListener("visibilitychange", onVisible); 11367 return () => { 11368 cancelled = true; 11369 clearTimeout(initialTimeout); 11370 clearInterval(interval); 11371 document.removeEventListener("visibilitychange", onVisible); 11372 }; 11373 }, [previewUrl, checkContainerStatus]); 11374 const selectorProbeTimerRef = reactExports.useRef(null); 11375 const enterSelectAckTimerRef = reactExports.useRef(null); 11376 const enterEditTextAckTimerRef = reactExports.useRef(null); 11377 reactExports.useEffect(() => { 11378 var _a; 11379 const targetWindow = (_a = iframeRef.current) == null ? void 0 : _a.contentWindow; 11380 if (!targetWindow || !iframeOrigin) return; 11381 if (pickModeActive) { 11382 if (!isSelectorReady) { 11383 return; 11384 } 11385 targetWindow.postMessage({ 11386 type: "enter-select" 11387 }, iframeOrigin); 11388 if (enterSelectAckTimerRef.current !== null) { 11389 clearTimeout(enterSelectAckTimerRef.current); 11390 } 11391 enterSelectAckTimerRef.current = setTimeout(() => { 11392 var _a2; 11393 enterSelectAckTimerRef.current = null; 11394 (_a2 = onPickModeExitRef.current) == null ? void 0 : _a2.call(onPickModeExitRef); 11395 _t("Preview isn't ready yet"); 11396 }, 500); 11397 } else { 11398 targetWindow.postMessage({ 11399 type: "cancel-select" 11400 }, iframeOrigin); 11401 if (enterSelectAckTimerRef.current !== null) { 11402 clearTimeout(enterSelectAckTimerRef.current); 11403 enterSelectAckTimerRef.current = null; 11404 } 11405 } 11406 return () => { 11407 }; 11408 }, [pickModeActive, isSelectorReady, iframeOrigin]); 11409 reactExports.useEffect(() => { 11410 var _a; 11411 const targetWindow = (_a = iframeRef.current) == null ? void 0 : _a.contentWindow; 11412 if (!targetWindow || !iframeOrigin) return; 11413 if (editTextModeActive) { 11414 if (!isSelectorReady) return; 11415 targetWindow.postMessage({ 11416 type: "enter-edit-text" 11417 }, iframeOrigin); 11418 if (enterEditTextAckTimerRef.current !== null) { 11419 clearTimeout(enterEditTextAckTimerRef.current); 11420 } 11421 enterEditTextAckTimerRef.current = setTimeout(() => { 11422 var _a2; 11423 enterEditTextAckTimerRef.current = null; 11424 (_a2 = onEditTextModeExitRef.current) == null ? void 0 : _a2.call(onEditTextModeExitRef); 11425 _t("Preview isn't ready yet"); 11426 }, 500); 11427 } else { 11428 targetWindow.postMessage({ 11429 type: "exit-edit-text" 11430 }, iframeOrigin); 11431 if (enterEditTextAckTimerRef.current !== null) { 11432 clearTimeout(enterEditTextAckTimerRef.current); 11433 enterEditTextAckTimerRef.current = null; 11434 } 11435 setEditPersistableCount(0); 11436 setEditNeedsAgentCount(0); 11437 } 11438 }, [editTextModeActive, isSelectorReady, iframeOrigin]); 11439 reactExports.useEffect(() => { 11440 if (!isApplyingWithAI) { 11441 aiTurnStartedRef.current = false;
11442 return; 11443 } 11444 if (isAgentResponding) { 11445 aiTurnStartedRef.current = true; 11446 } else if (aiTurnStartedRef.current) { 11447 setIsApplyingWithAI(false); 11448 return; 11449 } 11450 const safety = setTimeout(() => setIsApplyingWithAI(false), 12e4); 11451 return () => clearTimeout(safety); 11452 }, [isApplyingWithAI, isAgentResponding]); 11453 reactExports.useEffect(() => { 11454 onEditBusyChange == null ? void 0 : onEditBusyChange(isSavingEdits || isApplyingWithAI); 11455 }, [isSavingEdits, isApplyingWithAI, onEditBusyChange]); 11456 const persistEditBatch = reactExports.useCallback(async (edits) => { 11457 if (!previewUrl) throw new Error("No preview URL"); 11458 const token = await getToken(); 11459 const url = `${previewUrl.replace(/\/$/, "")}/__agent/edit-batch`; 11460 const res = await fetch(url, { 11461 method: "POST", 11462 headers: { 11463 "Content-Type": "application/json", 11464 ...token ? { 11465 Authorization: `Bearer ${token}` 11466 } : {} 11467 }, 11468 body: JSON.stringify({ 11469 edits 11470 }) 11471 }); 11472 if (!res.ok) { 11473 throw new Error(`edit-batch failed: ${res.status}`); 11474 } 11475 return res.json().catch(() => ({})); 11476 }, [previewUrl, getToken]); 11477 const requestEditFlush = reactExports.useCallback(() => { 11478 const empty = { 11479 edits: [], 11480 agentEdits: [] 11481 }; 11482 return new Promise((resolve) => { 11483 var _a; 11484 const target = (_a = iframeRef.current) == null ? void 0 : _a.contentWindow; 11485 if (!target || !iframeOrigin) return resolve(empty); 11486 const timer = setTimeout(() => { 11487 if (pendingFlushResolveRef.current) { 11488 pendingFlushResolveRef.current = null; 11489 resolve(empty); 11490 } 11491 }, 3e3); 11492 pendingFlushResolveRef.current = (batch) => { 11493 clearTimeout(timer); 11494 resolve(batch); 11495 }; 11496 target.postMessage({ 11497 type: "flush-edits" 11498 }, iframeOrigin); 11499 }); 11500 }, [iframeOrigin]); 11501 const handleSaveEdits = reactExports.useCallback(async () => { 11502 var _a, _b; 11503 if (isSavingEdits) return; 11504 setIsSavingEdits(true); 11505 try { 11506 const { 11507 edits, 11508 agentEdits 11509 } = await requestEditFlush(); 11510 let appliedCount = 0; 11511 let skippedEdits = []; 11512 if (edits.length > 0) { 11513 const result = await persistEditBatch(edits); 11514 appliedCount = typeof (result == null ? void 0 : result.applied) === "
11514number" ? result.applied : edits.length; 11515 const skips = Array.isArray(result == null ? void 0 : result.skipped) ? result.skipped : []; 11516 const skippedLocs = new Set(skips.map((s) => s == null ? void 0 : s.sourceLoc).filter(Boolean)); 11517 skippedEdits = edits.filter((e) => skippedLocs.has(e.sourceLoc)); 11518 if (skips.length > 0) { 11519 const byLoc = new Map(edits.map((e) => [e.sourceLoc, e])); 11520 for (const s of skips) { 11521 const e = (s == null ? void 0 : s.sourceLoc) ? byLoc.get(s.sourceLoc) : void 0; 11522 console.warn(`[inline-edit] punted to AI â reason=${(s == null ? void 0 : s.reason) ?? "unknown"} loc=${(s == null ? void 0 : s.sourceLoc) ?? "?"} old=${JSON.stringify(((e == null ? void 0 : e.oldText) ?? "").slice(0, 60))} new=${JSON.stringify(((e == null ? void 0 : e.newText) ?? "").slice(0, 60))}`); 11523 } 11524 } 11525 } 11526 if (agentEdits.length > 0) { 11527 console.warn(`[inline-edit] ${agentEdits.length} edit(s) routed to AI by the iframe (structural change or no resolvable source location) â see the preview-frame console for per-edit detail.`); 11528 } 11529 (_a = onEditTextModeExitRef.current) == null ? void 0 : _a.call(onEditTextModeExitRef); 11530 const aiEdits = [...agentEdits, ...skippedEdits]; 11531 posthog == null ? void 0 : posthog.capture("inline_edit_saved", { 11532 direct_applied: appliedCount, 11533 ai_routed: aiEdits.length 11534 }); 11535 if (aiEdits.length > 0 && onApplyWithAIRef.current) { 11536 const target = (_b = iframeRef.current) == null ? void 0 : _b.contentWindow; 11537 if (target && iframeOrigin) { 11538 target.postMessage({ 11539 type: "discard-edits" 11540 }, iframeOrigin); 11541 } 11542 setIsApplyingWithAI(true); 11543 onApplyWithAIRef.current(composeAgentEditPrompt(aiEdits)); 11544 _t((appliedCount > 0 ? `Saved ${appliedCount} change${appliedCount > 1 ? "s" : ""}. ` : "") + `Applying ${aiEdits.length} change${aiEdits.length > 1 ? "s" : ""} with AIâ¦`, { 11545 duration: 6e3 11546 }); 11547 } else if (aiEdits.length > 0) { 11548 _t(`${aiEdits.length} edit${aiEdits.length > 1 ? "s" : ""} couldn't be applied automatically.`, { 11549 duration: 6e3 11550 }); 11551 } else { 11552 _t.success(appliedCount > 0 ? `Saved ${appliedCount} change${appliedCount > 1 ? "s" : ""}` : "No changes to save"); 11553 } 11554 } catch { 11555 _t.error("Couldn't save your edits â please try again."); 11556 } finally { 11557 setIsSavingEdits(false); 11558 } 11559 }, [isSavingEdits, requestEditFlush, persistEditBatch, iframeOrigin, posthog]); 11560 const handleCancelEdits = reactExports.useCallback(() => { 11561 var _a, _b; 11562 if (isSavingEdits) return; 11563 posthog == null ? void 0 : posthog.capture("inline_edit_cancelled"); 11564 const target = (_a = iframeRef.current) == null ? void 0 : _a.contentWindow; 11565 if (target && iframeOrigin) { 11566 target.postMessage({ 11567 type: "discard-edits" 11568 }, iframeOrigin); 11569 } 11570 (_b = onEditTextModeExitRef.current) == null ? void 0 : _b.call(onEditTextModeExitRef); 11571 }, [isSavingEdits, iframeOrigin, posthog]); 11572 const handleDiscardConfirm = reactExports.useCallback(() => { 11573 var _a, _b; 11574 setPendingExitConfirm(false); 11575 const target = (_a = iframeRef.current) == null ? void 0 : _a.contentWindow; 11576 if (target && iframeOrigin) { 11577 target.postMessage({ 11578 type: "discard-edits" 11579 }, iframeOrigin); 11580 } 11581 (_b = onEditTextModeExitRef.current) == null ? void 0 : _b.call(onEditTextModeExitRef); 11582 }, [iframeOrigin]); 11583 const handleDiscardCancel = reactExports.useCallback(() => { 11584 setPendingExitConfirm(false); 11585 }, []); 11586 const handleEditTextToggle = reactExports.useCallback(() => { 11587 posthog == null ? void 0 : posthog.capture("inline_edit_button_clicked", { 11588 inline_edit_active: !editTextModeActive, 11589 websiteId 11590 }); 11591 if (!editTextModeActive) { 11592 onToggleEditTextMode == null ? void 0 : onToggleEditTextMode(); 11593 return; 11594 } 11595 if (editPersistableCount + editNeedsAgentCount > 0) { 11596 setPendingExitConfirm(true); 11597 return; 11598 } 11599 onToggleEditTextMode == null ? void 0 : onToggleEditTextMode(); 11600 }, [editTextModeActive, onToggleEditTextMode, posthog, editPersistableCount, editNeedsAgentCount, websiteId]); 11601 const editSaving = isSavingEdits || isApplyingWithAI; 11602 const editTotalCount = editPersistableCount + editNeedsAgentCount; 11603 const editSaveLabel = editPersistableCount === 0 && editNeedsAgentCount > 0 ? "Apply with AI" : "Save"; 11604 reactExports.useEffect(() => { 11605 var _a; 11606 let hoverPointer = true; 11607 if (typeof window !== "undefined" && typeof window.matchMedia === "function") { 11608 hoverPointer = window.matchMedia("(hover: hover) and (pointer: fine)").matches; 11609 } 11610 const result = pickerAllowedOrigin({ 11611 previewUrl, 11612 hoverPointer, 11613 // Dev/staging preview base domain â passed so the helper can allow 11614 // hosts under e.g. `thoop.dev` without hardcoding dev domains. 11615 previewBaseDomain: void 0 11616 }); 11617 (_a = onPickerAvailabilityChangeRef.current) == null ? void 0 : _a.call(onPickerAvailabilityChangeRef, result.allowed); 11618 }, [previewUrl]); 11619 reactExports.useEffect(() => { 11620 return () => { 11621 var _a; 11622 if (selectorProbeTimerRef.current !== null) { 11623 clearTimeout(selectorProbeTimerRef.current); 11624 selectorProbeTimerRef.current = null; 11625 } 11626 if (enterSelectAckTimerRef.current !== null) { 11627 clearTimeout(enterSelectAckTimerRef.current); 11628 enterSelectAckTimerRef.current = null; 11629 } 11630 if (enterEditTextAckTimerRef.current !== null) { 11631 clearTimeout(enterEditTextAckTimerRef.current); 11632 enterEditTextAckTimerRef.current = null; 11633 } 11634 (_a = onPickModeExitRef.current) == null ? void 0 : _a.call(onPickModeExitRef); 11635 }; 11636 }, []); 11637 reactExports.useEffect(() => { 11638 const handleFlush = () => { 11639 var _a, _b; 11640 (_b = (_a = iframeRef.current) == null ? void 0 : _a.contentWindow) == null ? void 0 : _b.postMessage({ 11641 type: "flush-console-logs" 11642 }, "*"); 11643 }; 11644 window.addEventListener("flush-browser-logs", handleFlush); 11645 return () => window.removeEventListener("flush-browser-logs", handleFlush); 11646 }, []); 11647 const handleIframeLoad = () => { 11648 var _a, _b, _c; 11649 containerTimingRef.current.iframeLoadCompleteTime = Date.now(); 11650 logContainerTiming("iframe_load_complete", { 11651 totalLoadTimeMs: containerTimingRef.current.iframeLoadStartTime ? Date.now() - containerTimingRef.current.iframeLoadStartTime : null 11652 }); 11653 setIsLoading(false); 11654 setHasError(false); 11655 setIsSelectorReady(false); 11656 (_a = onIframeReloadRef.current) == null ? void 0 : _a.call(onIframeReloadRef); 11657 const willClear = !hasStartupErrorRef.current && !livePreviewErrorRef.current; 11658 console.log("[ls:preview] iframe-load", { 11659 hasStartupErrorRef: hasStartupErrorRef.current, 11660 livePreviewErrorRef: livePreviewErrorRef.current, 11661 willClearViteError: willClear 11662 }); 11663 if (willClear) { 11664 setHasPreviewError(false); 11665 (_b = onViteErrorClearedRef.current) == null ? void 0 : _b.call(onViteErrorClearedRef); 11666 } 11667 if (selectorProbeTimerRef.current !== null) { 11668 clearTimeout(selectorProbeTimerRef.current); 11669 selectorProbeTimerRef.current = null; 11670 } 11671 const targetWindow = (_c = iframeRef.current) == null ? void 0 : _c.contentWindow; 11672 if (targetWindow && iframeOrigin) { 11673 targetWindow.postMessage({ 11674 type: "selector-probe" 11675 }, iframeOrigin); 11676 selectorProbeTimerRef.current = setTimeout(() => { 11677 selectorProbeTimerRef.current = null; 11678 }, 5e3); 11679 } 11680 }; 11681 const handleIframeError = () => { 11682 logContainerTiming("iframe_load_error"); 11683 setIsLoading(false); 11684 setHasError(true); 11685 }; 11686 const handleRefresh = () => { 11687 if (iframeRef.current && previewUrl) { 11688 logContainerTiming("manual_refresh_triggered"); 11689 containerTimingRef.current.iframeLoadStartTime = Date.now(); 11690 setIsLoading(true); 11691 setHasError(false); 11692 const refreshUrl = buildPreviewUrl(previewUrl, currentPath); 11693 const url = new URL(refreshUrl); 11694 url.searchParams.set("_r", Date.now().toString()); 11695 iframeRef.current.src = url.toString(); 11696 } 11697 }; 11698 const lastRefreshSignalRef = reactExports.useRef(refreshSignal); 11699 reactExports.useEffect(() => { 11700 if (refreshSignal === void 0) return; 11701 if (lastRefreshSignalRef.current === refreshSignal) return; 11702 lastRefreshSignalRef.current = refreshSignal; 11703 handleRefresh(); 11704 }, [refreshSignal]); 11705 const handleWakeAndRefresh = () => { 11706 containerTimingRef.current.bootId = newBootId(); 11707 containerTimingRef.current.pageLoadTime = Date.now(); 11708 containerTimingRef.current.iframeLoadStartTime = Date.now(); 11709 containerTimingRef.current.firstStatusCheckTime = null; 11710 containerTimingRef.current.containerReadyTime = null; 11711 containerTimingRef.current.iframeLoadCompleteTime = null; 11712 logContainerTiming("wake_and_refresh_triggered"); 11713 setIsLoading(true); 11714 setHasError(false); 11715 setIsContainerStarting(true); 11716 isContainerStartingRef.current = true;
11717 setLiveContainerStatus(null); 11718 loadingStartTimeRef.current = Date.now(); 11719 if (iframeRef.current && previewUrl) { 11720 const refreshUrl = buildPreviewUrl(previewUrl, currentPath); 11721 const url = new URL(refreshUrl); 11722 url.searchParams.set("_r", Date.now().toString()); 11723 iframeRef.current.src = url.toString(); 11724 } 11725 }; 11726 const navigateToPath = reactExports.useCallback((path, addToHistory = true) => { 11727 if (!previewUrl) return; 11728 const normalizedPath = path.startsWith("/") ? path : `/${path}`; 11729 setCurrentPath(normalizedPath); 11730 setIsLoading(true); 11731 setHasError(false); 11732 if (addToHistory) { 11733 setHistoryStack((prev) => [...prev.slice(0, historyIndex + 1), normalizedPath]); 11734 setHistoryIndex((prev) => prev + 1); 11735 } 11736 if (iframeRef.current) { 11737 iframeRef.current.src = buildPreviewUrl(previewUrl, normalizedPath); 11738 } 11739 }, [previewUrl, historyIndex]); 11740 const handleBack = reactExports.useCallback(() => { 11741 var _a; 11742 if (historyIndex > 0 && ((_a = iframeRef.current) == null ? void 0 : _a.contentWindow)) { 11743 const newIndex = historyIndex - 1; 11744 const targetPath = historyStack[newIndex]; 11745 console.log("[ReactPreviewPanel] handleBack - sending navigate-to-path:", targetPath); 11746 ignoreNextNavMessage.current = true; 11747 iframeRef.current.contentWindow.postMessage({ 11748 type: "navigate-to-path", 11749 path: targetPath 11750 }, "*"); 11751 setHistoryIndex(newIndex); 11752 setCurrentPath(targetPath); 11753 } 11754 }, [historyIndex, historyStack]); 11755 const handleForward = reactExports.useCallback(() => { 11756 var _a; 11757 if (historyIndex < historyStack.length - 1 && ((_a = iframeRef.current) == null ? void 0 : _a.contentWindow)) { 11758 const newIndex = historyIndex + 1; 11759 const targetPath = historyStack[newIndex]; 11760 ignoreNextNavMessage.current = true; 11761 iframeRef.current.contentWindow.postMessage({ 11762 type: "navigate-to-path", 11763 path: targetPath 11764 }, "*"); 11765 setHistoryIndex(newIndex); 11766 setCurrentPath(targetPath); 11767 } 11768 }, [historyIndex, historyStack]); 11769 const handleUrlSubmit = (e) => { 11770 e.preventDefault(); 11771 if (urlInputValue.trim() === "") return; 11772 navigateToPath(urlInputValue); 11773 }; 11774 const handleUrlKeyDown = (e) => { 11775 var _a, _b, _c, _d, _e; 11776 if (!isTypeaheadOpen) { 11777 if (e.key === "ArrowDown" && isDesktop && effectivePages.length > 0) { 11778 e.preventDefault(); 11779 setIsTypeaheadOpen(true); 11780 return; 11781 } 11782 if (e.key === "Enter") { 11783 e.preventDefault(); 11784 if (urlInputValue.trim() !== "") navigateToPath(urlInputValue); 11785 } 11786 return; 11787 } 11788 switch (e.key) { 11789 case "ArrowDown": 11790 e.preventDefault(); 11791 (_a = typeaheadRef.current) == null ? void 0 : _a.moveActive("down"); 11792 break; 11793 case "ArrowUp": 11794 e.preventDefault(); 11795 (_b = typeaheadRef.current) == null ? void 0 : _b.moveActive("up"); 11796 break; 11797 case "Home": 11798 e.preventDefault(); 11799 (_c = typeaheadRef.current) == null ? void 0 : _c.moveActive("home"); 11800 break; 11801 case "End": 11802 e.preventDefault(); 11803 (_d = typeaheadRef.current) == null ? void 0 : _d.moveActive("end"); 11804 break; 11805 case "Enter": { 11806 const activePath = ((_e = typeaheadRef.current) == null ? void 0 : _e.selectActive()) ?? null; 11807 e.preventDefault(); 11808 if (activePath) { 11809 navigateToPath(activePath); 11810 setIsTypeaheadOpen(false); 11811 } else if (urlInputValue.trim() !== "") { 11812 navigateToPath(urlInputValue); 11813 setIsTypeaheadOpen(false); 11814 } else { 11815 setIsTypeaheadOpen(false); 11816 } 11817 break; 11818 } 11819 case "Escape": 11820 setIsTypeaheadOpen(false); 11821 break; 11822 } 11823 }; 11824 const handleUrlBlur = () => { 11825 if (blurTimerRef.current !== null) { 11826 window.clearTimeout(blurTimerRef.current); 11827 } 11828 blurTimerRef.current = window.setTimeout(() => { 11829 setIsTypeaheadOpen(false); 11830 blurTimerRef.current = null; 11831 }, 150); 11832 }; 11833 reactExports.useEffect(() => { 11834 return () => { 11835 if (blurTimerRef.current !== null) { 11836 window.clearTimeout(blurTimerRef.current); 11837 } 11838 }; 11839 }, []); 11840 const handleTypeaheadSelect = (path) => { 11841 var _a; 11842 navigateToPath(path); 11843 setIsTypeaheadOpen(false); 11844 if (blurTimerRef.current !== null) { 11845 window.clearTimeout(blurTimerRef.current); 11846 blurTimerRef.current = null; 11847 } 11848 (_a = inputRef.current) == null ? void 0 : _a.blur(); 11849 }; 11850 const handleTypeaheadPointerDownInside = () => { 11851 if (blurTimerRef.current !== null) { 11852 window.clearTimeout(blurTimerRef.current); 11853 blurTimerRef.current = null; 11854 } 11855 }; 11856 const canGoBack = historyIndex > 0; 11857 const canGoForward = historyIndex < historyStack.length - 1; 11858 const browserToolbar = /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex h-16 flex-shrink-0 items-center gap-2 border-b border-[#1B1A22]/[0.06] bg-white px-3.5", children: [
11859 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: TOOLBAR_CAPSULE, children: [ 11860 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: handleBack, disabled: !canGoBack, className: clsx(TOOLBAR_BTN, TOOLBAR_BTN_HAPTIC), style: TOOLBAR_BTN_STYLE, title: "Go back", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronLeft, { className: "h-4 w-4" }) }), 11861 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: handleForward, disabled: !canGoForward, className: clsx(TOOLBAR_BTN, TOOLBAR_BTN_HAPTIC), style: TOOLBAR_BTN_STYLE, title: "Go forward", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "h-4 w-4" }) }), 11862 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: handleRefresh, disabled: isLoading, className: clsx(TOOLBAR_BTN, TOOLBAR_BTN_HAPTIC), style: TOOLBAR_BTN_STYLE, title: "Refresh", children: /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: clsx("h-3.5 w-3.5", (isLoading || isNavigating) && "animate-spin") }) }) 11863 ] }), 11864 /* @__PURE__ */ jsxRuntimeExports.jsxs(Popover, { open: isTypeaheadOpen, onOpenChange: setIsTypeaheadOpen, children: [
11865 /* @__PURE__ */ jsxRuntimeExports.jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("form", { ref: formRef, onSubmit: (e) => { 11866 handleUrlSubmit(e); 11867 setIsTypeaheadOpen(false); 11868 }, className: "relative mx-auto flex-[0_1_360px] focus-within:basis-[480px]", style: { 11869 transition: "flex-basis 240ms cubic-bezier(0.4, 0, 0.2, 1)" 11870 }, children: [ 11871 /* @__PURE__ */ jsxRuntimeExports.jsx(Search, { "aria-hidden": "true", className: "text-[#1B1A22]/45 pointer-events-none absolute left-[19px] top-1/2 h-3.5 w-3.5 -translate-y-1/2", style: { 11872 opacity: isTypeaheadOpen ? 1 : 0, 11873 transition: "opacity 240ms ease-out" 11874 } }), 11875 /* @__PURE__ */ jsxRuntimeExports.jsx( 11876 "input", 11877 { 11878 ref: inputRef, 11879 type: "text", 11880 value: urlInputValue, 11881 onChange: (e) => setUrlInputValue(e.target.value), 11882 onKeyDown: handleUrlKeyDown, 11883 onClick: () => { 11884 if (!isDesktop || effectivePages.length === 0) return; 11885 setIsTypeaheadOpen(true); 11886 }, 11887 onFocus: () => { 11888 setUrlInputValue(""); 11889 }, 11890 onBlur: handleUrlBlur, 11891 role: "combobox", 11892 "aria-expanded": isTypeaheadOpen, 11893 "aria-controls": "pages-typeahead-listbox", 11894 className: clsx(!isTypeaheadOpen && GLASS_PILL, "h-7 w-full rounded-full border-0 pr-3 text-left text-sm font-medium leading-7 placeholder-gray-400 placeholder:font-normal focus:outline-none", isTypeaheadOpen ? "text-[#1B1A22]" : "text-transparent"), 11895 style: { 11896 transition: "box-shadow 240ms ease-out, background 240ms ease-out, padding-left 280ms cubic-bezier(0.32, 0.72, 0, 1)", 11897 paddingLeft: isTypeaheadOpen ? "47px" : "45%", 11898 ...isTypeaheadOpen ? { 11899 background: "#FFFFFF", 11900 boxShadow: "inset 0 1px 0 rgba(255,255,255,1), inset 0 -1px 0 rgba(27,26,34,0.04), 0 1px 3px rgba(27,26,34,0.06), 0 8px 24px -6px rgba(27,26,34,0.10)" 11901 } : {} 11902 }, 11903 placeholder: isTypeaheadOpen ? "Search pages or type a path" : "" 11904 } 11905 ), 11906 !isTypeaheadOpen && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 flex items-center justify-center gap-2 px-3", style: { 11907 WebkitMaskImage: isDomainCtaHovered ? "linear-gradient(to right, black 0%, black 25%, transparent 70%)" : "linear-gradient(to right, black 0%, black 100%, transparent 100%)", 11908 maskImage: isDomainCtaHovered ? "linear-gradient(to right, black 0%, black 25%, transparent 70%)" : "linear-gradient(to right, black 0%, black 100%, transparent 100%)", 11909 transition: "mask-image 240ms ease-out, -webkit-mask-image 240ms ease-out" 11910 }, children: [ 11911 isPreviewLoading && /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "h-3.5 w-3.5 flex-none animate-spin text-[#1B1A22]/40", strokeWidth: 2.25 }),
11912 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "truncate text-sm font-medium leading-7", children: [ 11913 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[#1B1A22]/40", children: [ 11914 domainName || "yourdomain.com", 11915 "/" 11916 ] }), 11917 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[#1B1A22]", children: currentPath.replace(/^\/+/, "") }) 11918 ] }) 11919 ] }), 11920 !isTypeaheadOpen && !hasCustomDomain && websiteId && /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { type: "button", onClick: () => { 11921 posthog == null ? void 0 : posthog.capture("Set Up Domain Clicked", { 11922 websiteId, 11923 source: "preview_url_bar", 11924 websiteType: "react" 11925 }); 11926 navigate(namedRoutes.reactWebsiteChatSettingsTab({ 11927 id: websiteId, 11928 tab: "domain" 11929 })); 11930 }, onMouseEnter: () => setIsDomainCtaHovered(true), onMouseLeave: () => setIsDomainCtaHovered(false), onFocus: () => setIsDomainCtaHovered(true), onBlur: () => setIsDomainCtaHovered(false), "aria-label": "Set up your domain", className: clsx("group absolute inset-y-[2px] right-[2px] z-10 flex items-center", "overflow-hidden rounded-full bg-transparent text-[#1B1A22]/40", "hover:bg-[#d6e9db] hover:text-green-500", "max-w-[24px] hover:max-w-[180px]", "duration-[200ms] ease-[cubic-bezier(0.4,0,1,1)] transition-[max-width,background-color,color]", "hover:duration-[280ms] hover:ease-[cubic-bezier(0.32,0.72,0,1)]"), children: [
11931 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "relative flex h-6 w-6 flex-none items-center justify-center", children: [ 11932 /* @__PURE__ */ jsxRuntimeExports.jsx(Globe, { className: "h-3.5 w-3.5 [filter:drop-shadow(0_0.5px_0_rgba(255,255,255,0.7))]", strokeWidth: 2.25 }), 11933 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { "aria-hidden": "true", className: clsx("pointer-events-none absolute right-[3px] top-[3px] h-1.5 w-1.5 rounded-full", "bg-gradient-to-b from-[#FCD34D] to-[#EAB308]", "shadow-[inset_0_0.5px_0_rgba(255,255,255,0.55),0_0.5px_1px_rgba(146,64,14,0.18),0_0_0_2px_#F4F4F7]", "transition-opacity duration-150 ease-out", "group-hover:opacity-0 group-focus-visible:opacity-0") }) 11934 ] }), 11935 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: clsx("whitespace-nowrap pr-3 text-xs font-medium leading-6", "opacity-0 transition-opacity duration-150 ease-out", "group-hover:opacity-100 group-hover:delay-100"), children: "Set up your domain" }) 11936 ] }) 11937 ] }) }),
11938 /* @__PURE__ */ jsxRuntimeExports.jsx(PagesTypeahead, { ref: typeaheadRef, pages: effectivePages, query: urlInputValue, currentPath, anchorRef: formRef, onSelect: handleTypeaheadSelect, onClose: () => setIsTypeaheadOpen(false), onPointerDownInside: handleTypeaheadPointerDownInside, websiteId, hasCustomDomain, domainName }) 11939 ] }), 11940 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: clsx(TOOLBAR_CAPSULE, "group relative"), children: [ 11941 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { "aria-hidden": true, className: "pointer-events-none absolute left-0.5 top-0.5 z-0 h-7 w-7 scale-100 rounded-full bg-[linear-gradient(180deg,#ffffff_0%,#f6f6f8_100%)] shadow-[inset_0_1px_0_rgba(255,255,255,1),inset_0_2px_4px_rgba(255,255,255,0.5),inset_0_-2px_4px_-2px_rgba(27,26,34,0.08),inset_0_0_0_0.5px_rgba(27,26,34,0.14),0_0_4px_rgba(27,26,34,0.055),0_0_13px_-2px_rgba(27,26,34,0.06),0_0_24px_-5px_rgba(27,26,34,0.07)] group-active:scale-[1.16]", style: { 11942 translate: `${viewportIndicatorOffset(viewportSize)}px 0`, 11943 transition: "translate 280ms cubic-bezier(0.4,0,0.2,1), transform 260ms cubic-bezier(0.34,1.56,0.64,1)" 11944 } }),
11945 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => setViewportSize("desktop"), className: clsx(TOOLBAR_BTN, "relative z-[1]", viewportSize !== "desktop" && TOOLBAR_BTN_HAPTIC), style: TOOLBAR_BTN_STYLE, title: "Desktop view", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Monitor, { className: "h-4 w-4" }) }), 11946 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => setViewportSize("tablet"), className: clsx(TOOLBAR_BTN, "relative z-[1]", viewportSize !== "tablet" && TOOLBAR_BTN_HAPTIC), style: TOOLBAR_BTN_STYLE, title: "Tablet view", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Tablet, { className: "h-4 w-4" }) }), 11947 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => setViewportSize("mobile"), className: clsx(TOOLBAR_BTN, "relative z-[1]", viewportSize !== "mobile" && TOOLBAR_BTN_HAPTIC), style: TOOLBAR_BTN_STYLE, title: "Mobile view", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Smartphone, { className: "h-4 w-4" }) }) 11948 ] }), 11949 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: TOOLBAR_CAPSULE, children: /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { delayDuration: 250, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
11950 /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: handleEditTextToggle, disabled: !isSelectorReady || hasPreviewError && !editTextModeActive, "aria-pressed": editTextModeActive, "aria-label": editTextModeActive ? "Exit Edit text mode" : "Edit text directly in the preview", className: clsx(editTextModeActive ? clsx( 11951 // Active glass-button shell: positioned + clipped to 11952 // round so the specular ::before stays inside the 11953 // circle. Class hook `ls-glass-active` lets the 11954 // reduced-motion media query in index.css cancel the 11955 // pulse + drift while leaving the static layering 11956 // untouched. 11957 "ls-glass-active relative flex h-7 w-7 items-center justify-center overflow-hidden rounded-full text-white", 11958 "active:scale-[0.94]", 11959 // Specular drift ::before â radial highlight whose 11960 // background-position transitions on hover so the 11961 // bright spot drifts diagonally. Constrained to the 11962 // button's circle by `overflow-hidden` above. 11963 "before:absolute before:inset-0 before:rounded-full before:content-['']", 11964 "before:bg-[radial-gradient(circle_at_30%_25%,rgba(255,255,255,0.55)_0%,transparent_45%)]", 11965 "before:duration-[280ms] before:transition-[background-position]", 11966 "before:ease-[cubic-bezier(0.32,0.72,0,1)]", 11967 "hover:before:[background-position:60%_50%]", 11968 // Keep the icon glyph above the specular layer. 11969 "[&>svg]:relative [&>svg]:z-[1]" 11970 ) : clsx(TOOLBAR_BTN, TOOLBAR_BTN_HAPTIC)), style: editTextModeActive ? { 11971 // Active state: flat indigo gradient. Formerly a 11972 // backdrop-filter chain (SVG displacement lens + blur 11973 // + saturate) with an infinite halo pulse â dropped 11974 // with the rest of the editor's backdrop filters 11975 // (HS #16453): the lens re-filtered the backdrop 11976 // every frame, the costliest effect per pixel in the 11977 // editor, for a button that reads nearly the same 11978 // painted. The specular ::before in the className 11979 // keeps the glassy highlight without filter cost. 11980 background: "linear-gradient(180deg, #818cf8 0%, #6366f1 50%, #4338ca 100%)", 11981 transition: "transform 110ms ease-out, background 240ms ease-out" 11982 } : TOOLBAR_BTN_STYLE, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Type, { className: "h-4 w-4" }) }) }),
11983 /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { sideOffset: 6, children: hasPreviewError && !editTextModeActive ? "Fix the preview error before editing text" : "Edit text" }) 11984 ] }) }) }) 11985 ] }); 11986 if (containerStatus === "starting") { 11987 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex h-full flex-col bg-white", children: [ 11988 browserToolbar, 11989 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-1 items-center justify-center bg-gray-100", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center", children: [ 11990 /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "mx-auto h-12 w-12 animate-spin text-blue-600" }), 11991 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "mt-4 text-lg font-medium text-gray-900", children: "Starting Preview Server..." }), 11992 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-2 text-sm text-gray-500", children: "This may take a few moments" }) 11993 ] }) }) 11994 ] }); 11995 } 11996 if (wasEverReady && (containerStatus === "stopped" || containerStatus === "stopped_with_code")) { 11997 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex h-full flex-col bg-white", children: [ 11998 browserToolbar,
11999 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-1 items-center justify-center bg-gray-100", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-w-md text-center", children: [ 12000 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-amber-100", children: /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { className: "h-8 w-8 text-amber-600" }) }), 12001 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-lg font-medium text-gray-900", children: "Preview Server Stopped" }), 12002 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-2 text-sm text-gray-500", children: "The preview server has stopped. Click below to restart it." }), 12003 /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { onClick: handleWakeAndRefresh, className: "mt-4 inline-flex items-center gap-2 rounded-md bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-blue-700", children: [
12004 /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "h-4 w-4" }), 12005 "Start Preview Server" 12006 ] }), 12007 /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "mt-4 text-xs text-gray-400", children: [ 12008 "If this keeps happening, contact", 12009 " ", 12010 /* @__PURE__ */ jsxRuntimeExports.jsx("a", { href: "mailto:[email protected]", className: "text-blue-600 hover:underline", children: "[email protected]" }) 12011 ] }) 12012 ] }) }) 12013 ] }); 12014 } 12015 if (containerStatus === "error" || containerError) { 12016 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex h-full flex-col bg-white", children: [ 12017 browserToolbar, 12018 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-1 items-center justify-center bg-gray-100", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-w-md text-center", children: [ 12019 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-red-100", children: /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { className: "h-8 w-8 text-red-600" }) }),
12020 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-lg font-medium text-gray-900", children: "Preview Unavailable" }), 12021 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-2 text-sm text-gray-500", children: containerError || "Unable to start the preview server. Please try again." }), 12022 /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { onClick: handleWakeAndRefresh, className: "mt-4 inline-flex items-center gap-2 rounded-md bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-blue-700", children: [ 12023 /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "h-4 w-4" }), 12024 "Retry" 12025 ] }), 12026 /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "mt-4 text-xs text-gray-400", children: [ 12027 "If this keeps happening, contact", 12028 " ", 12029 /* @__PURE__ */ jsxRuntimeExports.jsx("a", { href: "mailto:[email protected]", className: "text-blue-600 hover:underline", children: "[email protected]" }) 12030 ] }) 12031 ] }) }) 12032 ] }); 12033 } 12034 if (!previewUrl) { 12035 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex h-full flex-col bg-white", children: [ 12036 browserToolbar,
12037 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-1 items-center justify-center bg-gray-100", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-w-sm text-center", children: [ 12038 /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "mx-auto h-12 w-12 animate-spin text-blue-600" }), 12039 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "mt-4 text-lg font-medium text-gray-900", children: "Initializing Preview..." }), 12040 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-2 text-sm text-gray-500", children: "Setting up your development environment" }), 12041 /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "mt-4 text-xs text-gray-400", children: [ 12042 "Taking too long? Try refreshing the page or contact", 12043 " ", 12044 /* @__PURE__ */ jsxRuntimeExports.jsx("a", { href: "mailto:[email protected]", className: "text-blue-600 hover:underline", children: "[email protected]" }) 12045 ] }) 12046 ] }) }) 12047 ] }); 12048 } 12049 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex h-full flex-col bg-white", children: [
12050 bannerRendered && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { role: "status", "aria-live": "polite", className: "relative flex h-8 flex-shrink-0 items-center justify-center gap-2 overflow-hidden rounded-t-[16px] border-b border-[#1B1A22]/[0.08] px-4 text-xs font-medium text-indigo-950", style: { 12051 // Solid pale-indigo base + a faint 1px-on/9px-off diagonal stripe 12052 // overlay (so the stripe pattern reads as texture without 12053 // fighting the text for legibility). 12054 backgroundColor: "rgba(165, 180, 252, 0.32)", 12055 backgroundImage: "repeating-linear-gradient(45deg, rgba(67,56,202,0.06) 0px, rgba(67,56,202,0.06) 1px, transparent 1px, transparent 10px)", 12056 // Emerge / retreat keyframes drive max-height + opacity so the 12057 // banner unfurls downward from inside the EditorLayout's 12058 // rounded-[16px] top edge and tucks back up the same way on 12059 // exit. Asymmetric easing: slower confident settle on emerge, 12060 // snappier exit on retreat. See @keyframes in index.css. 12061 animation: bannerExiting ? "edit-text-banner-retreat 320ms cubic-bezier(0.4,0,1,1) both" : "edit-text-banner-emerge 320ms cubic-bezier(0.32,0.72,0,1) both" 12062 }, children: [
12063 /* @__PURE__ */ jsxRuntimeExports.jsx(Type, { className: "h-3.5 w-3.5 text-indigo-700" }), 12064 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: editTotalCount > 0 ? `Editing text · ${editTotalCount} change${editTotalCount === 1 ? "" : "s"}` : "Editing text" }), 12065 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute right-2 flex items-center gap-1", children: [ 12066 editTotalCount > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 12067 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onClick: handleCancelEdits, disabled: editSaving, className: "flex h-6 items-center rounded-[7px] px-2 text-[11px] font-medium text-indigo-900/70 transition-colors hover:bg-indigo-900/10 hover:text-indigo-900 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500/50 disabled:cursor-not-allowed disabled:opacity-50", children: "Discard" }), 12068 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onClick: handleSaveEdits, disabled: editSaving, className: "flex h-6 items-center whitespace-nowrap rounded-[7px] bg-indigo-600 px-2.5 text-[11px] font-semibold text-white transition-colors hover:bg-indigo-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-300 disabled:cursor-not-allowed disabled:opacity-70", children: editSaving ? "Savingâ¦" : editSaveLabel }) 12069 ] }),
12070 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onClick: handleEditTextToggle, disabled: editSaving, "aria-label": "Exit text editing", title: "Exit text editing", className: "flex h-5 w-5 items-center justify-center rounded-full text-indigo-700/80 transition-colors hover:bg-indigo-900/10 hover:text-indigo-900 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500/50 disabled:cursor-not-allowed disabled:opacity-50", children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "h-3.5 w-3.5" }) }) 12071 ] }) 12072 ] }), 12073 browserToolbar, 12074 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { ref: containerRef, className: "relative flex flex-1 items-center justify-center overflow-hidden rounded-b-[16px] bg-gray-100", children: [ 12075 (isSavingEdits || isApplyingWithAI) && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: clsx("absolute inset-0 z-20 flex items-center justify-center backdrop-blur-[1px]", isApplyingWithAI ? "bg-white/90" : "bg-white/60"), children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 rounded-md bg-white px-4 py-2 text-sm font-medium text-gray-700 shadow", children: [
12076 /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "h-4 w-4 animate-spin text-indigo-600" }), 12077 isApplyingWithAI ? "Applying with AIâ¦" : "Saving your editsâ¦" 12078 ] }) }), 12079 isLoading && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 z-10 flex items-center justify-center bg-gray-100", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center", children: [ 12080 /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "mx-auto h-8 w-8 animate-spin text-blue-600" }), 12081 isContainerStarting ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 12082 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "mt-4 text-lg font-medium text-gray-900", children: "Starting Preview Server..." }), 12083 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-2 text-sm text-gray-500", children: "The container is waking up. This may take 30-60 seconds." }), 12084 /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "mt-1 text-xs text-gray-400", children: [ 12085 "Status: ", 12086 liveContainerStatus || "checking..." 12087 ] }) 12088 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-2 text-sm text-gray-500", children: "Loading preview..." }) 12089 ] }) }), 12090 hasError && !isLoading && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 z-10 flex items-center justify-center bg-gray-100", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-w-sm text-center", children: [
12091 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-amber-100", children: /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { className: "h-8 w-8 text-amber-600" }) }), 12092 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-lg font-medium text-gray-900", children: "Preview Connection Lost" }), 12093 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-2 text-sm text-gray-500", children: "The preview server may have timed out or stopped." }), 12094 liveContainerStatus && /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "mt-1 text-xs text-gray-400", children: [ 12095 "Container status: ", 12096 liveContainerStatus 12097 ] }), 12098 /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { onClick: handleWakeAndRefresh, className: "mt-4 inline-flex items-center gap-2 rounded-md bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-blue-700", children: [ 12099 /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "h-4 w-4" }), 12100 "Restart Preview" 12101 ] }),
12102 /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "mt-4 text-xs text-gray-400", children: [ 12103 "If this keeps happening, contact", 12104 " ", 12105 /* @__PURE__ */ jsxRuntimeExports.jsx("a", { href: "mailto:[email protected]", className: "text-blue-600 hover:underline", children: "[email protected]" }) 12106 ] }) 12107 ] }) }), 12108 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: clsx( 12109 "relative overflow-hidden bg-white transition-all duration-300", 12110 // For mobile/tablet, always show device frame styling 12111 viewportSize !== "desktop" && "rounded-xl border border-gray-200 shadow-lg" 12112 ), style: { 12113 // Desktop: fill the container entirely 12114 // Mobile/Tablet: use fixed dimensions (scaled if needed), capped to container height 12115 width: viewportSize === "desktop" ? "100%" : needsScaling ? `${iframeWidth * scale}px` : `${iframeWidth}px`, 12116 height: viewportSize === "desktop" ? "100%" : `${Math.min(iframeHeight * scale, containerSize.height - 32)}px`, 12117 maxHeight: viewportSize !== "desktop" ? `${containerSize.height - 32}px` : void 0 12118 }, children: /* @__PURE__ */ jsxRuntimeExports.jsx( 12119 "iframe", 12120 { 12121 ref: iframeRef, 12122 src: buildPreviewUrl(previewUrl), 12123 className: "bg-white", 12124 style: { 12125 // The iframe is at target dimensions (e.g., 1512px for desktop) 12126 // This makes the content INSIDE the iframe render at desktop width 12127 width: `${iframeWidth}px`, 12128 height: `${iframeHeight}px`, 12129 // Scale down to fit the wrapper visually 12130 transform: `scale(${scale})`, 12131 transformOrigin: "top left" 12132 }, 12133 onLoad: handleIframeLoad, 12134 onError: handleIframeError, 12135 title: "Website Preview", 12136 sandbox: "allow-scripts allow-same-origin allow-forms allow-popups allow-downloads allow-popups-to-escape-sandbox" 12137 } 12138 ) }) 12139 ] }), 12140 /* @__PURE__ */ jsxRuntimeExports.jsx(DiscardEditsConfirmModal, { open: pendingExitConfirm, onConfirm: handleDiscardConfirm, onCancel: handleDiscardCancel }), 12141 testCheckout && /* @__PURE__ */ jsxRuntimeExports.jsx(TestCheckoutSheet, { open: true, storeName: domainName || "your store", ownerEmail: (currentUser == null ? void 0 : currentUser.email) ?? "you", collectsShippingAddress: testCheckout.context.collectsShippingAddress, shippingCountries: testCheckout.context.shippingCountries, currency: testCheckout.context.currency, lines: testCheckout.context.lines, subtotal: testCheckout.context.subtotal, shippingRates: testCheckout.context.shippingRates, freeShippingOver: testCheckout.context.freeShippingOver, stripeTaxEnabled: testCheckout.context.stripeTaxEnabled, taxBehavior: testCheckout.context.taxBehavior, subscription: testCheckout.context.subscription, submitting: testCheckoutSubmitting, onSubmit: submitTestCheckout, onCancel: cancelTestCheckout }) 12142 ] }); 12143}; 12144const ReactPreviewPanel$1 = React.memo(ReactPreviewPanel); 12145const STORAGE_KEY = "ls_hdyhau_done_v1"; 12146const THANKS_MS = 2e3; 12147const OPTIONS = [{ 12148 value: "tiktok", 12149 label: "TikTok" 12150}, { 12151 value: "instagram", 12152 label: "Instagram" 12153}, { 12154 value: "youtube", 12155 label: "YouTube" 12156}, { 12157 value: "facebook", 12158 label: "Facebook" 12159}, { 12160 value: "chatgpt", 12161 label: "ChatGPT" 12162}, { 12163 value: "google", 12164 label: "Google" 12165}, { 12166 value: "friend", 12167 label: "A friend" 12168}, { 12169 value: "other", 12170 label: "Other" 12171}]; 12172const HdyhauSurveyCard = () => { 12173 const { 12174 isAuthenticated 12175 } = useAuth(); 12176 const [recordAcquisition] = useMutation$1(RECORD_ACQUISITION); 12177 const [phase, setPhase] = reactExports.useState(() => { 12178 try { 12179 return localStorage.getItem(STORAGE_KEY) ? "hidden" : "ask"; 12180 } catch { 12181 return "ask"; 12182 } 12183 }); 12184 const [detail, setDetail] = reactExports.useState(""); 12185 const thanksTimer = reactExports.useRef(void 0); 12186 reactExports.useEffect(() => { 12187 return () => { 12188 if (thanksTimer.current) clearTimeout(thanksTimer.current); 12189 }; 12190 }, []); 12191 const setGuard = () => { 12192 try { 12193 localStorage.setItem(STORAGE_KEY, "1"); 12194 } catch { 12195 } 12196 }; 12197 const send = (source, detailText) => { 12198 if (isAuthenticated) { 12199 recordAcquisition({ 12200 variables: { 12201 surveySource: source, 12202 surveyDetail: detailText ?? null 12203 } 12204 }).catch(() => { 12205 }); 12206 } else { 12207 stashSurveyAnswer(source, detailText); 12208 } 12209 }; 12210 const showThanks = () => { 12211 setPhase("thanks"); 12212 thanksTimer.current = setTimeout(() => setPhase("hidden"), THANKS_MS); 12213 }; 12214 const onPick = (source) => { 12215 send(source); 12216 setGuard(); 12217 if (source === "other") { 12218 setPhase("detail"); 12219 } else { 12220 showThanks(); 12221 } 12222 }; 12223 const onDetailSubmit = () => { 12224 const trimmed = detail.trim(); 12225 if (trimmed) send("other", trimmed); 12226 showThanks(); 12227 }; 12228 const onSkip = () => { 12229 setGuard(); 12230 setPhase("hidden"); 12231 }; 12232 if (phase === "hidden") return null; 12233 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-10 w-full max-w-md rounded-lg border border-gray-200 bg-white p-4 text-left shadow-sm", children: phase === "thanks" ? /* @__P
12233URE__ */ jsxRuntimeExports.jsx("p", { className: "text-center text-sm text-gray-600", children: "Thanks â that helps us a lot! ð" }) : phase === "detail" ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 12234 /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "mb-2 text-sm font-medium text-gray-700", children: [ 12235 "Where was that? ", 12236 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-gray-400", children: "(optional)" }) 12237 ] }), 12238 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [ 12239 /* @__PURE__ */ jsxRuntimeExports.jsx("input", { type: "text", value: detail, onChange: (e) => setDetail(e.target.value), onKeyDown: (e) => e.key === "Enter" && onDetailSubmit(), maxLength: 500, placeholder: "e.g. a podcast, a friend's websiteâ¦", className: "min-w-0 flex-1 rounded-md border border-gray-300 px-3 py-1.5 text-sm focus:border-blue-500 focus:outline-none" }), 12240 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onClick: onDetailSubmit, className: "rounded-md bg-blue-600 px-3 py-1.5 text-sm font-medium text-white hover:bg-blue-700", children: "Send" }) 12241 ] }) 12242 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 12243 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-3 flex items-start justify-between gap-2", children: [
12244 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm font-medium text-gray-700", children: "While you wait â where did you hear about us?" }), 12245 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onClick: onSkip, className: "-mr-1 -mt-1 shrink-0 rounded px-1 text-xs text-gray-400 hover:text-gray-600", children: "Skip" }) 12246 ] }), 12247 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-wrap gap-2", children: OPTIONS.map((option) => /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onClick: () => onPick(option.value), className: "rounded-full border border-gray-300 bg-white px-3 py-1.5 text-sm text-gray-700 transition-colors hover:border-blue-500 hover:bg-blue-50 hover:text-blue-700", children: option.label }, option.value)) }) 12248 ] }) }); 12249}; 12250var module = {}; 12251(function main(global, module2, isWorker, workerSize) { 12252 var canUseWorker = !!(global.Worker && global.Blob && global.Promise && global.OffscreenCanvas && global.OffscreenCanvasRenderingContext2D && global.HTMLCanvasElement && global.HTMLCanvasElement.prototype.transferControlToOffscreen && global.URL && global.URL.createObjectURL); 12253 var canUsePaths = typeof Path2D === "function" && typeof DOMMatrix === "function"; 12254 var canDrawBitmap = function() { 12255 if (!global.OffscreenCanvas) { 12256 return false; 12257 } 12258 var canvas = new OffscreenCanvas(1, 1); 12259 var ctx = canvas.getContext("2d"); 12260 ctx.fillRect(0, 0, 1, 1); 12261 var bitmap = canvas.transferToImageBitmap(); 12262 try { 12263 ctx.createPattern(bitmap, "no-repeat"); 12264 } catch (e) { 12265 return false; 12266 } 12267 return true; 12268 }(); 12269 function noop() { 12270 } 12271 function promise(func) { 12272 var ModulePromise = module2.exports.Promise; 12273 var Prom = ModulePromise !== void 0 ? ModulePromise : global.Promise; 12274 if (typeof Prom === "function") { 12275 return new Prom(func); 12276 } 12277 func(noop, noop); 12278 return null; 12279 } 12280 var bitmapMapper = /* @__PURE__ */ function(skipTransform, map) { 12281 return { 12282 transform: function(bitmap) { 12283 if (skipTransform) { 12284 return bitmap; 12285 } 12286 if (map.has(bitmap)) { 12287 return map.get(bitmap); 12288 } 12289 var canvas = new OffscreenCanvas(bitmap.width, bitmap.height); 12290 var ctx = canvas.getContext("2d"); 12291 ctx.drawImage(bitmap, 0, 0); 12292 map.set(bitmap, canvas); 12293 return canvas; 12294 }, 12295 clear: function() { 12296 map.clear(); 12297 } 12298 }; 12299 }(canDrawBitmap, /* @__PURE__ */ new Map()); 12300 var raf = function() { 12301 var TIME = Math.floor(1e3 / 60); 12302 var frame, cancel; 12303 var frames = {}; 12304 var lastFrameTime = 0; 12305 if (typeof requestAnimationFrame === "function" && typeof cancelAnimationFrame === "function") { 12306 frame = function(cb) { 12307 var id = Math.random(); 12308 frames[id] = requestAnimationFrame(function onFrame(time) { 12309 if (lastFrameTime === time || lastFrameTime + TIME - 1 < time) { 12310 lastFrameTime = time; 12311 delete frames[id]; 12312 cb(); 12313 } else { 12314 frames[id] = requestAnimationFrame(onFrame); 12315 } 12316 }); 12317 return id; 12318 }; 12319 cancel = function(id) { 12320 if (frames[id]) { 12321 cancelAnimationFrame(frames[id]); 12322 } 12323 }; 12324 } else { 12325 frame = function(cb) { 12326 return setTimeout(cb, TIME); 12327 }; 12328 cancel = function(timer) { 12329 return clearTimeout(timer); 12330 }; 12331 } 12332 return { frame, cancel }; 12333 }(); 12334 var getWorker = /* @__PURE__ */ function() { 12335 var worker; 12336 var prom; 12337 var resolves = {}; 12338 function decorate(worker2) { 12339 function execute(options, callback) { 12340 worker2.postMessage({ options: options || {}, callback }); 12341 } 12342 worker2.init = function initWorker(canvas) { 12343 var offscreen = canvas.transferControlToOffscreen(); 12344 worker2.postMessage({ canvas: offscreen }, [offscreen]); 12345 }; 12346 worker2.fire = function fireWorker(options, size2, done) { 12347 if (prom) { 12348 execute(options, null); 12349 return prom; 12350 } 12351 var id = Math.random().toString(36).slice(2); 12352 prom = promise(function(resolve) { 12353 function workerDone(msg) { 12354 if (msg.data.callback !== id) { 12355 return; 12356 } 12357 delete resolves[id]; 12358 worker2.removeEventListener("message", workerDone); 12359 prom = null; 12360 bitmapMapper.clear(); 12361 done(); 12362 resolve(); 12363 } 12364 worker2.addEventListener("message", workerDone); 12365 execute(options, id); 12366 resolves[id] = workerDone.bind(null, { data: { callback: id } }); 12367 }); 12368 return prom; 12369 }; 12370 worker2.reset = function resetWorker() { 12371 worker2.postMessage({ reset: true }); 12372 for (var id in resolves) { 12373 resolves[id](); 12374 delete resolves[id]; 12375 } 12376 }; 12377 } 12378 return function() { 12379 if (worker) { 12380 return worker; 12381 } 12382 if (!isWorker && canUseWorker) { 12383 var code = [
12384 "var CONFETTI, SIZE = {}, module = {};", 12385 "(" + main.toString() + ")(this, module, true, SIZE);", 12386 "onmessage = function(msg) {", 12387 " if (msg.data.options) {", 12388 " CONFETTI(msg.data.options).then(function () {", 12389 " if (msg.data.callback) {", 12390 " postMessage({ callback: msg.data.callback });", 12391 " }", 12392 " });", 12393 " } else if (msg.data.reset) {", 12394 " CONFETTI && CONFETTI.reset();", 12395 " } else if (msg.data.resize) {", 12396 " SIZE.width = msg.data.resize.width;", 12397 " SIZE.height = msg.data.resize.height;", 12398 " } else if (msg.data.canvas) {", 12399 " SIZE.width = msg.data.canvas.width;", 12400 " SIZE.height = msg.data.canvas.height;", 12401 " CONFETTI = module.exports.create(msg.data.canvas);", 12402 " }", 12403 "}" 12404 ].join("\n"); 12405 try { 12406 worker = new Worker(URL.createObjectURL(new Blob([code]))); 12407 } catch (e) { 12408 typeof console !== void 0 && typeof console.warn === "function" ? console.warn("ð Could not load worker", e) : null; 12409 return null; 12410 } 12411 decorate(worker); 12412 } 12413 return worker; 12414 }; 12415 }(); 12416 var defaults = { 12417 particleCount: 50, 12418 angle: 90, 12419 spread: 45, 12420 startVelocity: 45, 12421 decay: 0.9, 12422 gravity: 1, 12423 drift: 0, 12424 ticks: 200, 12425 x: 0.5, 12426 y: 0.5, 12427 shapes: ["square", "circle"], 12428 zIndex: 100, 12429 colors: [ 12430 "#26ccff", 12431 "#a25afd", 12432 "#ff5e7e", 12433 "#88ff5a", 12434 "#fcff42", 12435 "#ffa62d", 12436 "#ff36ff" 12437 ], 12438 // probably should be true, but back-compat 12439 disableForReducedMotion: false, 12440 scalar: 1 12441 }; 12442 function convert(val, transform) {
12443 return transform ? transform(val) : val; 12444 } 12445 function isOk(val) { 12446 return !(val === null || val === void 0); 12447 } 12448 function prop(options, name, transform) { 12449 return convert( 12450 options && isOk(options[name]) ? options[name] : defaults[name], 12451 transform 12452 ); 12453 } 12454 function onlyPositiveInt(number) { 12455 return number < 0 ? 0 : Math.floor(number); 12456 } 12457 function randomInt(min, max) { 12458 return Math.floor(Math.random() * (max - min)) + min; 12459 } 12460 function toDecimal(str) { 12461 return parseInt(str, 16); 12462 } 12463 function colorsToRgb(colors) { 12464 return colors.map(hexToRgb); 12465 } 12466 function hexToRgb(str) { 12467 var val = String(str).replace(/[^0-9a-f]/gi, ""); 12468 if (val.length < 6) { 12469 val = val[0] + val[0] + val[1] + val[1] + val[2] + val[2]; 12470 } 12471 return { 12472 r: toDecimal(val.substring(0, 2)), 12473 g: toDecimal(val.substring(2, 4)), 12474 b: toDecimal(val.substring(4, 6)) 12475 }; 12476 } 12477 function getOrigin(options) { 12478 var origin = prop(options, "origin", Object); 12479 origin.x = prop(origin, "x", Number); 12480 origin.y = prop(origin, "y", Number); 12481 return origin; 12482 } 12483 function setCanvasWindowSize(canvas) { 12484 canvas.width = document.documentElement.clientWidth; 12485 canvas.height = document.documentElement.clientHeight; 12486 } 12487 function setCanvasRectSize(canvas) { 12488 var rect = canvas.getBoundingClientRect(); 12489 canvas.width = rect.width; 12490 canvas.height = rect.height; 12491 } 12492 function getCanvas(zIndex) { 12493 var canvas = document.createElement("canvas"); 12494 canvas.style.position = "fixed"; 12495 canvas.style.top = "0px"; 12496 canvas.style.left = "0px"; 12497 canvas.style.pointerEvents = "none"; 12498 canvas.style.zIndex = zIndex; 12499 return canvas; 12500 } 12501 function ellipse(context, x, y, radiusX, radiusY, rotation, startAngle, endAngle, antiClockwise) { 12502 context.save(); 12503 context.translate(x, y); 12504 context.rotate(rotation); 12505 context.scale(radiusX, radiusY); 12506 context.arc(0, 0, 1, startAngle, endAngle, antiClockwise); 12507 context.restore(); 12508 } 12509 function randomPhysics(opts) { 12510 var radAngle = opts.angle * (Math.PI / 180); 12511 var radSpread = opts.spread * (Math.PI / 180); 12512 return { 12513 x: opts.x, 12514 y: opts.y, 12515 wobble: Math.random() * 10, 12516 wobbleSpeed: Math.min(0.11, Math.random() * 0.1 + 0.05), 12517 velocity: opts.startVelocity * 0.5 + Math.random() * opts.startVelocity, 12518 angle2D: -radAngle + (0.5 * radSpread - Math.random() * radSpread), 12519 tiltAngle: (Math.random() * (0.75 - 0.25) + 0.25) * Math.PI, 12520 color: opts.color, 12521 shape: opts.shape, 12522 tick: 0, 12523 totalTicks: opts.ticks, 12524 decay: opts.decay, 12525 drift: opts.drift, 12526 random: Math.random() + 2, 12527 tiltSin: 0, 12528 tiltCos: 0, 12529 wobbleX: 0, 12530 wobbleY: 0, 12531 gravity: opts.gravity * 3, 12532 ovalScalar: 0.6, 12533 scalar: opts.scalar, 12534 flat: opts.flat 12535 }; 12536 } 12537 function updateFetti(context, fetti) { 12538 fetti.x += Math.cos(fetti.angle2D) * fetti.velocity + fetti.drift; 12539 fetti.y += Math.sin(fetti.angle2D) * fetti.velocity + fetti.gravity; 12540 fetti.velocity *= fetti.decay; 12541 if (fetti.flat) { 12542 fetti.wobble = 0; 12543 fetti.wobbleX = fetti.x + 10 * fetti.scalar; 12544 fetti.wobbleY = fetti.y + 10 * fetti.scalar; 12545 fetti.tiltSin = 0; 12546 fetti.tiltCos = 0; 12547 fetti.random = 1; 12548 } else { 12549 fetti.wobble += fetti.wobbleSpeed; 12550 fetti.wobbleX = fetti.x + 10 * fetti.scalar * Math.cos(fetti.wobble); 12551 fetti.wobbleY = fetti.y + 10 * fetti.scalar * Math.sin(fetti.wobble); 12552 fetti.tiltAngle += 0.1; 12553 fetti.tiltSin = Math.sin(fetti.tiltAngle); 12554 fetti.tiltCos = Math.cos(fetti.tiltAngle); 12555 fetti.random = Math.random() + 2; 12556 } 12557 var progress = fetti.tick++ / fetti.totalTicks; 12558 var x1 = fetti.x + fetti.random * fetti.tiltCos; 12559 var y1 = fetti.y + fetti.random * fetti.tiltSin; 12560 var x2 = fetti.wobbleX + fetti.random * fetti.tiltCos; 12561 var y2 = fetti.wobbleY + fetti.random * fetti.tiltSin; 12562 context.fillStyle = "rgba(" + fetti.color.r + ", " + fetti.color.g + ", " + fetti.color.b + ", " + (1 - progress) + ")"; 12563 context.beginPath(); 12564 if (canUsePaths && fetti.shape.type === "path" && typeof fetti.shape.path === "string" && Array.isArray(fetti.shape.matrix)) { 12565 context.fill(transformPath2D( 12566 fetti.shape.path, 12567 fetti.shape.matrix, 12568 fetti.x, 12569 fetti.y, 12570 Math.abs(x2 - x1) * 0.1, 12571 Math.abs(y2 - y1) * 0.1, 12572 Math.PI / 10 * fetti.wobble 12573 )); 12574 } else if (fetti.shape.type === "bitmap") { 12575 var rotation = Math.PI / 10 * fetti.wobble; 12576 var scaleX = Math.abs(x2 - x1) * 0.1; 12577 var scaleY = Math.abs(y2 - y1) * 0.1; 12578 var width = fetti.shape.bitmap.width * fetti.scalar; 12579 var height = fetti.shape.bitmap.height * fetti.scalar; 12580 var matrix = new DOMMatrix([
vendor: 8,449 bytes, lines 12581-12830
12581 Math.cos(rotation) * scaleX, 12582 Math.sin(rotation) * scaleX, 12583 -Math.sin(rotation) * scaleY, 12584 Math.cos(rotation) * scaleY, 12585 fetti.x, 12586 fetti.y 12587 ]); 12588 matrix.multiplySelf(new DOMMatrix(fetti.shape.matrix)); 12589 var pattern = context.createPattern(bitmapMapper.transform(fetti.shape.bitmap), "no-repeat"); 12590 pattern.setTransform(matrix); 12591 context.globalAlpha = 1 - progress; 12592 context.fillStyle = pattern; 12593 context.fillRect( 12594 fetti.x - width / 2, 12595 fetti.y - height / 2, 12596 width, 12597 height 12598 ); 12599 context.globalAlpha = 1; 12600 } else if (fetti.shape === "circle") { 12601 context.ellipse ? context.ellipse(fetti.x, fetti.y, Math.abs(x2 - x1) * fetti.ovalScalar, Math.abs(y2 - y1) * fetti.ovalScalar, Math.PI / 10 * fetti.wobble, 0, 2 * Math.PI) : ellipse(context, fetti.x, fetti.y, Math.abs(x2 - x1) * fetti.ovalScalar, Math.abs(y2 - y1) * fetti.ovalScalar, Math.PI / 10 * fetti.wobble, 0, 2 * Math.PI); 12602 } else if (fetti.shape === "star") { 12603 var rot = Math.PI / 2 * 3; 12604 var innerRadius = 4 * fetti.scalar; 12605 var outerRadius = 8 * fetti.scalar; 12606 var x = fetti.x; 12607 var y = fetti.y; 12608 var spikes = 5; 12609 var step = Math.PI / spikes; 12610 while (spikes--) { 12611 x = fetti.x + Math.cos(rot) * outerRadius; 12612 y = fetti.y + Math.sin(rot) * outerRadius; 12613 context.lineTo(x, y); 12614 rot += step; 12615 x = fetti.x + Math.cos(rot) * innerRadius; 12616 y = fetti.y + Math.sin(rot) * innerRadius; 12617 context.lineTo(x, y); 12618 rot += step; 12619 } 12620 } else { 12621 context.moveTo(Math.floor(fetti.x), Math.floor(fetti.y)); 12622 context.lineTo(Math.floor(fetti.wobbleX), Math.floor(y1)); 12623 context.lineTo(Math.floor(x2), Math.floor(y2)); 12624 context.lineTo(Math.floor(x1), Math.floor(fetti.wobbleY)); 12625 } 12626 context.closePath(); 12627 context.fill(); 12628 return fetti.tick < fetti.totalTicks; 12629 } 12630 function animate(canvas, fettis, resizer, size2, done) { 12631 var animatingFettis = fettis.slice(); 12632 var context = canvas.getContext("2d"); 12633 var animationFrame; 12634 var destroy; 12635 var prom = promise(function(resolve) { 12636 function onDone() { 12637 animationFrame = destroy = null; 12638 context.clearRect(0, 0, size2.width, size2.height); 12639 bitmapMapper.clear(); 12640 done(); 12641 resolve(); 12642 } 12643 function update() { 12644 if (isWorker && !(size2.width === workerSize.width && size2.height === workerSize.height)) { 12645 size2.width = canvas.width = workerSize.width; 12646 size2.height = canvas.height = workerSize.height; 12647 } 12648 if (!size2.width && !size2.height) { 12649 resizer(canvas); 12650 size2.width = canvas.width; 12651 size2.height = canvas.height; 12652 } 12653 context.clearRect(0, 0, size2.width, size2.height); 12654 animatingFettis = animatingFettis.filter(function(fetti) { 12655 return updateFetti(context, fetti); 12656 }); 12657 if (animatingFettis.length) { 12658 animationFrame = raf.frame(update); 12659 } else { 12660 onDone(); 12661 } 12662 } 12663 animationFrame = raf.frame(update); 12664 destroy = onDone; 12665 }); 12666 return { 12667 addFettis: function(fettis2) { 12668 animatingFettis = animatingFettis.concat(fettis2); 12669 return prom; 12670 }, 12671 canvas, 12672 promise: prom, 12673 reset: function() { 12674 if (animationFrame) { 12675 raf.cancel(animationFrame); 12676 } 12677 if (destroy) { 12678 destroy(); 12679 } 12680 } 12681 }; 12682 } 12683 function confettiCannon(canvas, globalOpts) { 12684 var isLibCanvas = !canvas; 12685 var allowResize = !!prop(globalOpts || {}, "resize"); 12686 var hasResizeEventRegistered = false; 12687 var globalDisableForReducedMotion = prop(globalOpts, "disableForReducedMotion", Boolean); 12688 var shouldUseWorker = canUseWorker && !!prop(globalOpts || {}, "useWorker"); 12689 var worker = shouldUseWorker ? getWorker() : null; 12690 var resizer = isLibCanvas ? setCanvasWindowSize : setCanvasRectSize; 12691 var initialized = canvas && worker ? !!canvas.__confetti_initialized : false; 12692 var preferLessMotion = typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion)").matches; 12693 var animationObj; 12694 function fireLocal(options, size2, done) { 12695 var particleCount = prop(options, "particleCount", onlyPositiveInt); 12696 var angle = prop(options, "angle", Number); 12697 var spread = prop(options, "spread", Number); 12698 var startVelocity = prop(options, "startVelocity", Number); 12699 var decay = prop(options, "decay", Number); 12700 var gravity = prop(options, "gravity", Number); 12701 var drift = prop(options, "drift", Number); 12702 var colors = prop(options, "colors", colorsToRgb); 12703 var ticks = prop(options, "ticks", Number); 12704 var shapes = prop(options, "shapes"); 12705 var scalar = prop(options, "scalar"); 12706 var flat = !!prop(options, "flat"); 12707 var origin = getOrigin(options); 12708 var temp = particleCount; 12709 var fettis = []; 12710 var startX = canvas.width * origin.x; 12711 var startY = canvas.height * origin.y; 12712 while (temp--) { 12713 fettis.push( 12714 randomPhysics({ 12715 x: startX, 12716 y: startY, 12717 angle, 12718 spread, 12719 startVelocity, 12720 color: colors[temp % colors.length], 12721 shape: shapes[randomInt(0, shapes.length)], 12722 ticks, 12723 decay, 12724 gravity, 12725 drift, 12726 scalar, 12727 flat 12728 }) 12729 ); 12730 } 12731 if (animationObj) { 12732 return animationObj.addFettis(fettis); 12733 } 12734 animationObj = animate(canvas, fettis, resizer, size2, done); 12735 return animationObj.promise; 12736 } 12737 function fire(options) { 12738 var disableForReducedMotion = globalDisableForReducedMotion || prop(options, "disableForReducedMotion", Boolean); 12739 var zIndex = prop(options, "zIndex", Number); 12740 if (disableForReducedMotion && preferLessMotion) { 12741 return promise(function(resolve) { 12742 resolve(); 12743 }); 12744 } 12745 if (isLibCanvas && animationObj) { 12746 canvas = animationObj.canvas; 12747 } else if (isLibCanvas && !canvas) { 12748 canvas = getCanvas(zIndex); 12749 document.body.appendChild(canvas); 12750 } 12751 if (allowResize && !initialized) { 12752 resizer(canvas); 12753 } 12754 var size2 = { 12755 width: canvas.width, 12756 height: canvas.height 12757 }; 12758 if (worker && !initialized) { 12759 worker.init(canvas); 12760 } 12761 initialized = true; 12762 if (worker) { 12763 canvas.__confetti_initialized = true; 12764 } 12765 function onResize() { 12766 if (worker) { 12767 var obj = { 12768 getBoundingClientRect: function() { 12769 if (!isLibCanvas) { 12770 return canvas.getBoundingClientRect(); 12771 } 12772 } 12773 }; 12774 resizer(obj); 12775 worker.postMessage({ 12776 resize: { 12777 width: obj.width, 12778 height: obj.height 12779 } 12780 }); 12781 return; 12782 } 12783 size2.width = size2.height = null; 12784 } 12785 function done() { 12786 animationObj = null; 12787 if (allowResize) { 12788 hasResizeEventRegistered = false; 12789 global.removeEventListener("resize", onResize); 12790 } 12791 if (isLibCanvas && canvas) { 12792 if (document.body.contains(canvas)) { 12793 document.body.removeChild(canvas); 12794 } 12795 canvas = null; 12796 initialized = false; 12797 } 12798 } 12799 if (allowResize && !hasResizeEventRegistered) { 12800 hasResizeEventRegistered = true; 12801 global.addEventListener("resize", onResize, false); 12802 } 12803 if (worker) { 12804 return worker.fire(options, size2, done); 12805 } 12806 return fireLocal(options, size2, done); 12807 } 12808 fire.reset = function() { 12809 if (worker) { 12810 worker.reset(); 12811 } 12812 if (animationObj) { 12813 animationObj.reset(); 12814 } 12815 }; 12816 return fire; 12817 } 12818 var defaultFire; 12819 function getDefaultFire() { 12820 if (!defaultFire) { 12821 defaultFire = confettiCannon(null, { useWorker: true, resize: true }); 12822 } 12823 return defaultFire; 12824 } 12825 function transformPath2D(pathString, pathMatrix, x, y, scaleX, scaleY, rotation) { 12826 var path2d = new Path2D(pathString); 12827 var t1 = new Path2D(); 12828 t1.addPath(path2d, new DOMMatrix(pathMatrix)); 12829 var t2 = new Path2D(); 12830 t2.addPath(t1, new DOMMatrix([
12831 Math.cos(rotation) * scaleX, 12832 Math.sin(rotation) * scaleX, 12833 -Math.sin(rotation) * scaleY, 12834 Math.cos(rotation) * scaleY, 12835 x, 12836 y 12837 ])); 12838 return t2; 12839 } 12840 function shapeFromPath(pathData) { 12841 if (!canUsePaths) { 12842 throw new Error("path confetti are not supported in this browser"); 12843 } 12844 var path, matrix; 12845 if (typeof pathData === "string") { 12846 path = pathData; 12847 } else { 12848 path = pathData.path; 12849 matrix = pathData.matrix; 12850 } 12851 var path2d = new Path2D(path); 12852 var tempCanvas = document.createElement("canvas"); 12853 var tempCtx = tempCanvas.getContext("2d"); 12854 if (!matrix) { 12855 var maxSize = 1e3; 12856 var minX = maxSize; 12857 var minY = maxSize; 12858 var maxX = 0; 12859 var maxY = 0; 12860 var width, height; 12861 for (var x = 0; x < maxSize; x += 2) { 12862 for (var y = 0; y < maxSize; y += 2) { 12863 if (tempCtx.isPointInPath(path2d, x, y, "nonzero")) { 12864 minX = Math.min(minX, x); 12865 minY = Math.min(minY, y); 12866 maxX = Math.max(maxX, x); 12867 maxY = Math.max(maxY, y); 12868 } 12869 } 12870 } 12871 width = maxX - minX; 12872 height = maxY - minY; 12873 var maxDesiredSize = 10; 12874 var scale = Math.min(maxDesiredSize / width, maxDesiredSize / height); 12875 matrix = [ 12876 scale, 12877 0, 12878 0, 12879 scale, 12880 -Math.round(width / 2 + minX) * scale, 12881 -Math.round(height / 2 + minY) * scale 12882 ]; 12883 } 12884 return { 12885 type: "path", 12886 path, 12887 matrix 12888 }; 12889 } 12890 function shapeFromText(textData) { 12891 var text, scalar = 1, color = "#000000", fontFamily = '"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji", "EmojiOne Color", "Android Emoji", "Twemoji Mozilla", "system emoji", sans-serif'; 12892 if (typeof textData === "string") { 12893 text = textData; 12894 } else { 12895 text = textData.text; 12896 scalar = "scalar" in textData ? textData.scalar : scalar; 12897 fontFamily = "fontFamily" in textData ? textData.fontFamily : fontFamily; 12898 color = "color" in textData ? textData.color : color; 12899 } 12900 var fontSize = 10 * scalar; 12901 var font = "" + fontSize + "px " + fontFamily; 12902 var canvas = new OffscreenCanvas(fontSize, fontSize); 12903 var ctx = canvas.getContext("2d"); 12904 ctx.font = font; 12905 var size2 = ctx.measureText(text); 12906 var width = Math.ceil(size2.actualBoundingBoxRight + size2.actualBoundingBoxLeft); 12907 var height = Math.ceil(size2.actualBoundingBoxAscent + size2.actualBoundingBoxDescent); 12908 var padding = 2; 12909 var x = size2.actualBoundingBoxLeft + padding; 12910 var y = size2.actualBoundingBoxAscent + padding; 12911 width += padding + padding; 12912 height += padding + padding; 12913 canvas = new OffscreenCanvas(width, height); 12914 ctx = canvas.getContext("2d"); 12915 ctx.font = font; 12916 ctx.fillStyle = color; 12917 ctx.fillText(text, x, y); 12918 var scale = 1 / scalar; 12919 return { 12920 type: "bitmap", 12921 // TODO these probably need to be transfered for workers 12922 bitmap: canvas.transferToImageBitmap(), 12923 matrix: [scale, 0, 0, scale, -width * scale / 2, -height * scale / 2] 12924 }; 12925 } 12926 module2.exports = function() { 12927 return getDefaultFire().apply(this, arguments); 12928 }; 12929 module2.exports.reset = function() { 12930 getDefaultFire().reset(); 12931 }; 12932 module2.exports.create = confettiCannon; 12933 module2.exports.shapeFromPath = shapeFromPath; 12934 module2.exports.shapeFromText = shapeFromText; 12935})(function() { 12936 if (typeof window !== "undefined") { 12937 return window; 12938 } 12939 if (typeof self !== "undefined") { 12940 return self; 12941 } 12942 return this || {}; 12943}(), module, false); 12944const confetti = module.exports; 12945module.exports.create; 12946const FIRST_PUBLISH_CELEBRATION_STATUS = gql` 12947 query FirstPublishCelebrationStatus { 12948 firstPublishCelebrationStatus { 12949 shouldShow 12950 websiteId 12951 liveUrl 12952 } 12953 } 12954`; 12955const MARK_TRUSTPILOT_REVIEW_PROMPT_SEEN = gql` 12956 mutation MarkTrustpilotReviewPromptSeen { 12957 markTrustpilotReviewPromptSeen 12958 } 12959`; 12960const TRUSTPILOT_REVIEW_URL = "https://www.trustpilot.com/evaluate/landingsite.ai"; 12961const ANALYTICS_PROPS = { 12962 trigger: "first_publish" 12963}; 12964const FirstPublishCelebration = ({ 12965 websiteId, 12966 publishCompletedSignal 12967}) => { 12968 const posthog = usePostHog(); 12969 const [open, setOpen] = reactExports.useState(false); 12970 const armedRef = reactExports.useRef(false); 12971 const clickedRef = reactExports.useRef(false); 12972 const dismissedRef = reactExports.useRef(false); 12973 const prevSignalRef = reactExports.useRef(publishCompletedSignal); 12974 const [markSeen] = useMutation$1(MARK_TRUSTPILOT_REVIEW_PROMPT_SEEN); 12975 const { 12976 data, 12977 refetch 12978 }
12978 = useQuery$1(FIRST_PUBLISH_CELEBRATION_STATUS, { 12979 fetchPolicy: "network-only" 12980 }); 12981 reactExports.useEffect(() => { 12982 if (publishCompletedSignal !== void 0 && publishCompletedSignal !== prevSignalRef.current) { 12983 prevSignalRef.current = publishCompletedSignal; 12984 refetch().catch(() => { 12985 }); 12986 } 12987 }, [publishCompletedSignal, refetch]); 12988 const status = data == null ? void 0 : data.firstPublishCelebrationStatus; 12989 reactExports.useEffect(() => { 12990 var _a; 12991 if ((status == null ? void 0 : status.shouldShow) !== true) return; 12992 if (status.websiteId !== websiteId) return; 12993 if (armedRef.current) return; 12994 armedRef.current = true; 12995 setOpen(true); 12996 try { 12997 const prefersReducedMotion = typeof window !== "undefined" && ((_a = window.matchMedia) == null ? void 0 : _a.call(window, "(prefers-reduced-motion: reduce)").matches); 12998 if (!prefersReducedMotion) { 12999 confetti({ 13000 particleCount: 150, 13001 spread: 90, 13002 origin: { 13003 y: 0.6 13004 }, 13005 zIndex: 100 13006 }); 13007 } 13008 } catch { 13009 } 13010 markSeen().then((result) => { 13011 var _a2; 13012 if (((_a2 = result == null ? void 0 : result.data) == null ? void 0 : _a2.markTrustpilotReviewPromptSeen) === true) { 13013 posthog == null ? void 0 : posthog.capture("Trustpilot Prompt Shown", ANALYTICS_PROPS); 13014 } 13015 }).catch(() => { 13016 }); 13017 }, [status, websiteId, markSeen, posthog]); 13018 const handleDismiss = () => { 13019 if (!clickedRef.current && !dismissedRef.current) { 13020 dismissedRef.current = true; 13021 posthog == null ? void 0 : posthog.capture("Trustpilot Prompt Dismissed", ANALYTICS_PROPS); 13022 } 13023 setOpen(false); 13024 }; 13025 const handleOpenChange = (nextOpen) => { 13026 if (!nextOpen) handleDismiss(); 13027 }; 13028 if (!open) return null; 13029 const liveUrl = (status == null ? void 0 : status.liveUrl) ?? null; 13030 return /* @__PURE__ */ jsxRuntimeExports.jsx(Dialog, { open, onOpenChange: handleOpenChange, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogContent, { className: "max-w-md", children: [ 13031 /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogHeader, { children: [
13032 /* @__PURE__ */ jsxRuntimeExports.jsx(DialogTitle, { className: "text-center text-2xl", children: "ð Your site is live!" }), 13033 liveUrl && /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "pt-2 text-center text-sm text-muted-foreground", children: [ 13034 "Check it out at", 13035 " ", 13036 /* @__PURE__ */ jsxRuntimeExports.jsx("a", { href: liveUrl, target: "_blank", rel: "noopener noreferrer", className: "font-medium text-primary underline underline-offset-4 hover:opacity-80", children: liveUrl.replace(/^https?:\/\//, "") }) 13037 ] }), 13038 /* @__PURE__ */ jsxRuntimeExports.jsx(DialogDescription, { className: "pt-2 text-center", children: "Did you have fun building it? We'd love it if you shared your experience on Trustpilot â it helps other small businesses find us." }) 13039 ] }), 13040 /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogFooter, { className: "gap-2 sm:justify-center", children: [ 13041 /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { variant: "outline", onClick: handleDismiss, children: "Not now" }),
13042 /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx("a", { href: TRUSTPILOT_REVIEW_URL, target: "_blank", rel: "noopener noreferrer", onClick: () => { 13043 if (!clickedRef.current) { 13044 clickedRef.current = true; 13045 posthog == null ? void 0 : posthog.capture("Trustpilot Prompt Clicked", ANALYTICS_PROPS); 13046 } 13047 }, children: "Leave a review" }) }) 13048 ] }) 13049 ] }) }); 13050}; 13051const buildPreWarmUrl = (baseUrl) => { 13052 return baseUrl.replace(/\/$/, ""); 13053}; 13054const BUILD_STEPS = [{ 13055 label: "Designing your logo", 13056 icon: Palette, 13057 startAt: 0 13058}, { 13059 label: "Choosing your colors & fonts", 13060 icon: Type, 13061 startAt: 8 13062}, { 13063 label: "Finding the perfect images", 13064 icon: Image, 13065 startAt: 18 13066}, { 13067 label: "Planning your pages", 13068 icon: LayoutTemplate, 13069 startAt: 30 13070}, { 13071 label: "Preparing your site", 13072 icon: Sparkles, 13073 startAt: 42 13074}]; 13075const SpinnerIcon = () => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative mx-auto h-20 w-20", children: [ 13076 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 rounded-full border-4 border-gray-200" }), 13077 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 animate-spin rounded-full border-4 border-transparent border-t-blue-500" }), 13078 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-2 animate-spin rounded-full border-4 border-transparent border-b-purple-500", style: { 13079 animationDirection: "reverse", 13080 animationDuration: "1.5s" 13081 } }), 13082 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { className: "h-8 w-8 text-blue-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" }) }) }) 13083] }); 13084const StepIndicator = ({ 13085 steps, 13086 activeIndex 13087}) => /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-10 w-full max-w-sm space-y-3", children: steps.map((step, i) => { 13088 const isCompleted = i < activeIndex; 13089 const isActive = i === activeIndex; 13090 const isPending = i > activeIndex; 13091 const Icon = step.icon; 13092 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `flex items-center gap-3 transition-all duration-500 ${isPending ? "opacity-40" : "opacity-100"}`, children: [ 13093 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full transition-all duration-500 ${isCompleted ? "bg-green-100 text-green-600" : isActive ? "bg-blue-100 text-blue-600" : "bg-gray-100 text-gray-400"}`, children: isCompleted ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "h-4 w-4" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Icon, { className: `h-4 w-4 ${isActive ? "animate-pulse" : ""}` }) }), 13094 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: `text-sm transition-all duration-500 ${isCompleted ? "text-green-700" : isActive ? "font-medium text-gray-900" : "text-gray-400"}`, children: [ 13095 step.label, 13096 isActive && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "ml-1 inline-flex", children: [
13097 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "animate-bounce text-gray-400", style: { 13098 animationDelay: "0ms" 13099 }, children: "." }), 13100 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "animate-bounce text-gray-400", style: { 13101 animationDelay: "150ms" 13102 }, children: "." }), 13103 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "animate-bounce text-gray-400", style: { 13104 animationDelay: "300ms" 13105 }, children: "." }) 13106 ] }) 13107 ] }) 13108 ] }, step.label); 13109}) }); 13110const ReactWebsiteLoadingScreen = ({ 13111 businessName, 13112 previewUrl, 13113 containerStatus, 13114 footer 13115}) => { 13116 const isWaitingForContainer = typeof containerStatus === "string" && containerStatus.length > 0; 13117 const [elapsedSeconds, setElapsedSeconds] = reactExports.useState(0); 13118 const preWarmAttemptedRef = reactExports.useRef(false); 13119 const mountTimeRef = reactExports.useRef(Date.now()); 13120 reactExports.useEffect(() => { 13121 if (!previewUrl || preWarmAttemptedRef.current) return; 13122 preWarmAttemptedRef.current = true; 13123 const preWarmUrl = buildPreWarmUrl(previewUrl); 13124 const MAX_RETRIES = 3; 13125 const RETRY_DELAY_MS = 2e3; 13126 let cancelled = false;
13127 const attempt = async (retryCount) => { 13128 const sinceMountMs = Date.now() - mountTimeRef.current; 13129 console.log("[PreWarm] Starting container pre-warm", { 13130 timestamp: (/* @__PURE__ */ new Date()).toISOString(), 13131 localTime: (/* @__PURE__ */ new Date()).toLocaleTimeString(), 13132 sinceMountMs, 13133 previewUrl, 13134 attempt: retryCount + 1 13135 }); 13136 try { 13137 const response = await fetch(preWarmUrl); 13138 const elapsed = Date.now() - mountTimeRef.current; 13139 console.log("[PreWarm] Container pre-warm response received", { 13140 timestamp: (/* @__PURE__ */ new Date()).toISOString(), 13141 localTime: (/* @__PURE__ */ new Date()).toLocaleTimeString(), 13142 sinceMountMs: elapsed, 13143 status: response.status, 13144 statusText: response.statusText, 13145 attempt: retryCount + 1 13146 }); 13147 if (response.status >= 500 && retryCount < MAX_RETRIES && !cancelled) { 13148 console.log(`[PreWarm] Got ${response.status}, retrying in ${RETRY_DELAY_MS}ms...`); 13149 await new Promise((r) => setTimeout(r, RETRY_DELAY_MS)); 13150 if (!cancelled) await attempt(retryCount + 1); 13151 } 13152 } catch (error) { 13153 const elapsed = Date.now() - mountTimeRef.current; 13154 console.log("[PreWarm] Container pre-warm request failed (may be CORS)", { 13155 timestamp: (/* @__PURE__ */ new Date()).toISOString(), 13156 localTime: (/* @__PURE__ */ new Date()).toLocaleTimeString(), 13157 sinceMountMs: elapsed, 13158 error: error instanceof Error ? error.message : String(error), 13159 attempt: retryCount + 1 13160 }); 13161 if (retryCount < MAX_RETRIES && !cancelled) { 13162 console.log(`[PreWarm] Retrying in ${RETRY_DELAY_MS}ms...`); 13163 await new Promise((r) => setTimeout(r, RETRY_DELAY_MS)); 13164 if (!cancelled) await attempt(retryCount + 1); 13165 } 13166 } 13167 }; 13168 attempt(0); 13169 return () => { 13170 cancelled = true; 13171 }; 13172 }, [previewUrl]); 13173 reactExports.useEffect(() => { 13174 const interval = setInterval(() => { 13175 setElapsedSeconds((prev) => prev + 1); 13176 }, 1e3); 13177 return () => clearInterval(interval); 13178 }, []); 13179 const activeStepIndex = BUILD_STEPS.reduce((idx, step, i) => { 13180 return elapsedSeconds >= step.startAt ? i : idx; 13181 }, 0); 13182 const displayStepIndex = isWaitingForContainer && activeStepIndex >= BUILD_STEPS.length - 1 ? BUILD_STEPS.length : activeStepIndex; 13183 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex min-h-screen w-full flex-col items-center justify-center bg-gray-50", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center px-6 text-center", children: [ 13184 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-8 flex items-center gap-2", children: [
13185 /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: "/logo-paper-plane.png", alt: "Landingsite", className: "h-5 w-auto" }), 13186 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xl font-semibold tracking-tight text-gray-800", children: "Landingsite" }) 13187 ] }), 13188 /* @__PURE__ */ jsxRuntimeExports.jsx(SpinnerIcon, {}), 13189 /* @__PURE__ */ jsxRuntimeExports.jsx("h1", { className: "mt-8 text-center text-2xl font-bold text-gray-900 sm:text-3xl", children: businessName ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 13190 "Building", 13191 " ", 13192 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-blue-600", children: businessName }) 13193 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: "Building Your Website" }) }), 13194 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mx-auto mt-3 max-w-md text-gray-500", children: isWaitingForContainer ? "Putting it all together..." : "Our AI is crafting your custom website. This will only take a moment." }), 13195 /* @__PURE__ */ jsxRuntimeExports.jsx(StepIndicator, { steps: BUILD_STEPS, activeIndex: displayStepIndex }), 13196 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-8 flex justify-center gap-2", children: [
13197 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "h-2 w-2 animate-bounce rounded-full bg-blue-500", style: { 13198 animationDelay: "0ms" 13199 } }), 13200 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "h-2 w-2 animate-bounce rounded-full bg-purple-500", style: { 13201 animationDelay: "150ms" 13202 } }), 13203 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "h-2 w-2 animate-bounce rounded-full bg-blue-500", style: { 13204 animationDelay: "300ms" 13205 } }) 13206 ] }), 13207 footer 13208 ] }) }); 13209}; 13210const LONG_FRAME_MIN_MS = 200; 13211const MAX_EVENTS_PER_PAGE = 20; 13212const MIN_GAP_MS = 3e3; 13213function summarizeLongFrame(entry, mountedAt) { 13214 const allScripts = entry.scripts || []; 13215 const scripts = allScripts.filter((s) => (s.duration ?? 0) >= 5).sort((a, b) => (b.duration ?? 0) - (a.duration ?? 0)).slice(0, 3); 13216 return { 13217 frame_ms: Math.round(entry.duration), 13218 blocking_ms: Math.round(entry.blockingDuration ?? 0), 13219 style_layout_ms: Math.round(entry.styleAndLayoutDuration ?? 0), 13220 // Aggregates over ALL browser-reported scripts (vs the top-3 list 13221 // below). Note the LoAF API itself only reports scripts >5ms â the spec 13222 // (§3.2.2) appends a script entry only "if the duration ... is greater 13223 // than 5 milliseconds" â so these aggregates distinguish frames with 13224 // many moderate (>5ms) scripts beyond the top-3 list from frames with 13225 // genuinely zero reported scripts (GPU/presentation wait). Limitation: 13226 // a frame full of sub-5ms callbacks (e.g. rrweb per-mutation work) is 13227 // indistinguishable from a script-free frame in LoAF data; classifying 13228 // those needs a different instrument (an in-page profiling probe or an 13229 // on/off experiment). 13230 script_count: allScripts.length, 13231 script_total_ms: Math.round(allScripts.reduce((sum, s) => sum + (s.duration ?? 0), 0)), 13232 editor_age_ms: Math.max(0, Math.round(entry.startTime - mountedAt)), 13233 scripts: scripts.map((s) => ({ 13234 src: (s.sourceURL || s.name || "unknown").slice(-120), 13235 fn: (s.sourceFunctionName || "").slice(0, 80), 13236 invoker: (s.invoker || "").slice(-120), 13237 invoker_type: s.invokerType || "", 13238 dur_ms: Math.round(s.duration ?? 0), 13239 forced_layout_ms: Math.round(s.forcedStyleAndLayoutDuration ?? 0) 13240 })) 13241 }; 13242} 13243function pageContext() { 13244 try { 13245 return { 13246 chat_bubbles: document.querySelectorAll(".msg-row-enter").length, 13247 dom_nodes: document.getElementsByTagName("*").length 13248 }; 13249 } catch { 13250 return {}; 13251 } 13252} 13253function startLongFrameTelemetry(capture, websiteId) { 13254 const mountedAt = performance.now(); 13255 let sent = 0; 13256 let lastSentAt = -Infinity; 13257 let observer = null; 13258 try { 13259 observer = new PerformanceObserver((list) => { 13260 const longEntries = list.getEntries().filter((e) => e.duration >= LONG_FRAME_MIN_MS); 13261 if (longEntries.length === 0) return; 13262 const now = performance.now(); 13263 if (sent >= MAX_EVENTS_PER_PAGE || now - lastSentAt < MIN_GAP_MS) { 13264 return; 13265 } 13266 const worst = longEntries.reduce((a, b) => b.duration > a.duration ? b : a); 13267 sent++; 13268 lastSentAt = now; 13269 capture("editor_long_frame", { 13270 websiteId, 13271 ...summarizeLongFrame(worst, mountedAt),
13272 ...pageContext() 13273 }); 13274 if (sent >= MAX_EVENTS_PER_PAGE) { 13275 observer == null ? void 0 : observer.disconnect(); 13276 observer = null; 13277 } 13278 }); 13279 observer.observe({ 13280 type: "long-animation-frame", 13281 buffered: true 13282 }); 13283 } catch { 13284 observer = null; 13285 } 13286 return () => observer == null ? void 0 : observer.disconnect(); 13287} 13288const DOCK_MIN = 320; 13289const DOCK_MAX = 720; 13290const DOCK_DEFAULT = 396; 13291const EditorLayout = ({ 13292 navbar, 13293 chat, 13294 preview, 13295 merchantView, 13296 viewSwitcher, 13297 banner, 13298 websiteName, 13299 chatExpanded = true 13300}) => { 13301 const isDesktop = useBreakpoint("lg"); 13302 const [dockWidth, setDockWidth] = reactExports.useState(DOCK_DEFAULT); 13303 const [dragging, setDragging] = reactExports.useState(false); 13304 const dragRef = reactExports.useRef(null); 13305 const handlePointerDown = (e) => { 13306 e.currentTarget.setPointerCapture(e.pointerId); 13307 dragRef.current = { 13308 startX: e.clientX, 13309 startWidth: dockWidth 13310 }; 13311 setDragging(true); 13312 }; 13313 const handlePointerMove = (e) => { 13314 const drag = dragRef.current; 13315 if (!drag) return; 13316 const dx = e.clientX - drag.startX; 13317 const next = drag.startWidth - dx; 13318 setDockWidth(Math.max(DOCK_MIN, Math.min(DOCK_MAX, next))); 13319 }; 13320 const handlePointerUp = (e) => { 13321 if (e.currentTarget.hasPointerCapture(e.pointerId)) { 13322 e.currentTarget.releasePointerCapture(e.pointerId); 13323 } 13324 dragRef.current = null; 13325 setDragging(false); 13326 }; 13327 const handleDoubleClick = () => setDockWidth(DOCK_DEFAULT); 13328 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex h-screen flex-col text-[#1B1A22]", style: { 13329 background: "radial-gradient(120% 90% at 82% -8%, #F3F1FB 0%, transparent 46%), radial-gradient(110% 80% at 6% 110%, #FDF1EC 0%, transparent 44%), #FBFAF8" 13330 }, children: [ 13331 banner, 13332 /* @__PURE__ */ jsxRuntimeExports.jsx("header", { className: "z-20 px-4 py-1.5", children: navbar }), 13333 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex min-h-0 flex-1 flex-row-reverse items-stretch", children: [
13334 /* @__PURE__ */ jsxRuntimeExports.jsx("aside", { className: isDesktop ? "relative z-10 flex h-full flex-shrink-0 pb-3 pr-3" : `absolute inset-x-0 bottom-0 z-20 flex px-3 pb-3 ${chatExpanded ? "h-[75svh]" : "h-auto"}`, style: isDesktop ? { 13335 width: dockWidth 13336 } : void 0, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `flex h-full w-full flex-col overflow-hidden rounded-[16px] animate-in fade-in-0 duration-500 ease-out fill-mode-both ${isDesktop ? "border border-[#1B1A22]/[0.08] bg-white shadow-[0_28px_70px_-40px_rgba(27,26,34,0.40)] slide-in-from-right-4" : chatExpanded ? "border border-[#1B1A22]/[0.08] bg-white shadow-[0_-18px_50px_-30px_rgba(27,26,34,0.45)] slide-in-from-bottom-4" : "slide-in-from-bottom-4"}`, children: [ 13337 isDesktop && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 border-b border-[#1B1A22]/[0.06] px-5 py-4", children: [ 13338 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "grid h-8 w-8 flex-shrink-0 place-items-center rounded-[12px] bg-[linear-gradient(135deg,#F4674E,#6E56CF)] text-white shadow-[0_6px_16px_-6px_rgba(110,86,207,0.6)]", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 15, strokeWidth: 2.25 }) }),
13339 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 flex-1 leading-tight", children: [ 13340 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "truncate text-[13.5px] font-semibold tracking-[-0.012em]", children: "Build with AI" }), 13341 /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "truncate text-[11.5px] text-[#6B6878]", children: [ 13342 "Editing ", 13343 websiteName 13344 ] }) 13345 ] }), 13346 viewSwitcher ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-shrink-0", children: viewSwitcher }) : null 13347 ] }), 13348 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "min-h-0 flex-1", children: chat }) 13349 ] }) }), 13350 isDesktop && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "group flex w-3 flex-shrink-0 cursor-col-resize touch-none select-none items-center justify-center pb-3", role: "separator", "aria-orientation": "vertical", "aria-label": "Resize chat dock", "aria-valuenow": Math.round(dockWidth), "aria-valuemin": DOCK_MIN, "aria-valuemax": DOCK_MAX, onPointerDown: handlePointerDown, onPointerMove: handlePointerMove, onPointerUp: handlePointerUp, onPointerCancel: handlePointerUp, onDoubleClick: handleDoubleClick, children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `h-10 w-1 rounded-full transition-colors ${dragging ? "bg-[#1B1A22]/60" : "bg-[#1B1A22]/30 group-hover:bg-[#1B1A22]/50"}` }) }),
13351 /* @__PURE__ */ jsxRuntimeExports.jsxs("main", { className: `relative flex h-full min-w-0 flex-1 items-stretch justify-start ${isDesktop ? "pb-3 pl-3" : "px-3 pb-3"}`, children: [ 13352 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { "aria-hidden": true, className: "pointer-events-none absolute inset-x-28 top-2 h-48 rounded-[100%] blur-[64px]", style: { 13353 background: "radial-gradient(closest-side, rgba(110,86,207,0.22), transparent)" 13354 } }), 13355 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex h-full w-full flex-col overflow-hidden rounded-[16px] border border-[#1B1A22]/[0.08] bg-white shadow-[0_40px_100px_-48px_rgba(27,26,34,0.46)] animate-in fade-in-0 zoom-in-95 duration-700 ease-out fill-mode-both", children: [ 13356 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "min-h-0 flex-1", style: merchantView ? { 13357 visibility: "hidden", 13358 pointerEvents: "none" 13359 } : void 0, children: preview }), 13360 merchantView && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 overflow-y-auto bg-white", children: merchantView }) 13361 ] }) 13362 ] }) 13363 ] }) 13364 ] }); 13365}; 13366const GET_REACT_WEBSITE = { 13367 "kind": "Document", 13368 "definitions": [{ 13369 "kind": "OperationDefinition", 13370 "operation": "query", 13371 "name": { 13372 "kind": "Name", 13373 "value": "GetReactWebsite" 13374 }, 13375 "variableDefinitions": [{ 13376 "kind": "VariableDefinition", 13377 "variable": { 13378 "kind": "Variable", 13379 "name": { 13380 "kind": "Name", 13381 "value": "id" 13382 } 13383 }, 13384 "type": { 13385 "kind": "NonNullType", 13386 "type": { 13387 "kind": "NamedType", 13388 "name": { 13389 "kind": "Name", 13390 "value": "String" 13391 } 13392 } 13393 }, 13394 "directives": [] 13395 }], 13396 "directives": [], 13397 "selectionSet": { 13398 "kind": "SelectionSet", 13399 "selections": [{ 13400 "kind": "Field", 13401 "name": { 13402 "kind": "Name", 13403 "value": "website" 13404 }, 13405 "arguments": [{ 13406 "kind": "Argument", 13407 "name": { 13408 "kind": "Name", 13409 "value": "id" 13410 }, 13411 "value": { 13412 "kind": "Variable", 13413 "name": { 13414 "kind": "Name", 13415 "value": "id" 13416 } 13417 } 13418 }], 13419 "directives": [], 13420 "selectionSet": { 13421 "kind": "SelectionSet", 13422 "selections": [{ 13423 "kind": "Field", 13424 "name": { 13425 "kind": "Name", 13426 "value": "__typename" 13427 }, 13428 "arguments": [], 13429 "directives": [] 13430 }, { 13431 "kind": "Field", 13432 "name": { 13433 "kind": "Name", 13434 "value": "id" 13435 }, 13436 "arguments": [], 13437 "directives": [] 13438 }, { 13439 "kind": "Field", 13440 "name": { 13441 "kind": "Name", 13442 "value": "name" 13443 }, 13444 "arguments": [], 13445 "directives": [] 13446 }, { 13447 "kind": "Field", 13448 "name": { 13449 "kind": "Name", 13450 "value": "url" 13451 }, 13452 "arguments": [], 13453 "directives": [] 13454 }, { 13455 "kind": "Field", 13456 "name": { 13457 "kind": "Name", 13458 "value": "publicId" 13459 }, 13460 "arguments": [], 13461 "directives": [] 13462 }, { 13463 "kind": "Field", 13464 "name": { 13465 "kind": "Name", 13466 "value": "businessName" 13467 }, 13468 "arguments": [], 13469 "directives": [] 13470 }, { 13471 "kind": "Field", 13472 "name": { 13473 "kind": "Name", 13474 "value": "businessAbout" 13475 }, 13476 "arguments": [], 13477 "directives": [] 13478 }, { 13479 "kind": "Field", 13480 "name": { 13481 "kind": "Name", 13482 "value": "websiteType" 13483 }, 13484 "arguments": [], 13485 "directives": [] 13486 }, { 13487 "kind": "Field", 13488 "name": { 13489 "kind": "Name", 13490 "value": "isFirstWebsite" 13491 }, 13492 "arguments": [], 13493 "directives": [] 13494 }, { 13495 "kind": "Field", 13496 "name": { 13497 "kind": "Name", 13498 "value": "createdAt" 13499 }, 13500 "arguments": [], 13501 "directives": [] 13502 }, { 13503 "kind": "Field", 13504 "name": { 13505 "kind": "Name", 13506 "value": "stripePlan" 13507 }, 13508 "arguments": [], 13509 "directives": [] 13510 }, { 13511 "kind": "Field", 13512 "name": { 13513 "kind": "Name", 13514 "value": "stripeIsAnnualPlan" 13515 }, 13516 "arguments": [], 13517 "directives": [] 13518 }, { 13519 "kind": "Field", 13520 "name": { 13521 "kind": "Name", 13522 "value": "paymentPastDue" 13523 }, 13524 "arguments": [], 13525 "directives": [] 13526 }, { 13527 "kind": "Field", 13528 "name": { 13529 "kind": "Name", 13530 "value": "pastDueInvoiceUrl" 13531 }, 13532 "arguments": [], 13533 "directives": [] 13534 }, { 13535 "kind": "Field", 13536 "name": { 13537 "kind": "Name", 13538 "value": "underReview" 13539 }, 13540 "arguments": [], 13541 "directives": [] 13542 }, { 13543 "kind": "Field", 13544 "name": { 13545 "kind": "Name", 13546 "value": "siteCaseNotice" 13547 }, 13548 "arguments": [], 13549 "directives": [], 13550 "selectionSet": { 13551 "kind": "SelectionSet", 13552 "selections": [{ 13553 "kind": "Field", 13554 "name": { 13555 "kind": "Name", 13556 "value": "type" 13557 }, 13558 "arguments": [], 13559 "directives": [] 13560 }, { 13561 "kind": "Field", 13562 "name": { 13563 "kind": "Name", 13564 "value": "brand" 13565 }, 13566 "arguments": [], 13567 "directives": [] 13568 }, { 13569 "kind": "Field", 13570 "name": { 13571 "kind": "Name", 13572 "value": "deadlineAt" 13573 }, 13574 "arguments": [], 13575 "directives": [] 13576 }, { 13577 "kind": "Field", 13578 "name": { 13579 "kind": "Name", 13580 "value": "paused" 13581 }, 13582 "arguments": [], 13583 "directives": [] 13584 }] 13585 } 13586 }, { 13587 "kind": "Field", 13588 "name": { 13589 "kind": "Name", 13590 "value": "analyticsId" 13591 }, 13592 "arguments": [], 13593 "directives": [] 13594 }, { 13595 "kind": "Field", 13596 "name": { 13597 "kind": "Name", 13598 "value": "updatedAt" 13599 }, 13600 "arguments": [], 13601 "directives": [] 13602 }, { 13603 "kind": "Field", 13604 "name": { 13605 "kind": "Name", 13606 "value": "publishingStatus" 13607 }, 13608 "arguments": [], 13609 "directives": [] 13610 }, { 13611 "kind": "Field", 13612 "name": { 13613 "kind": "Name", 13614 "value": "publishingStep" 13615 }, 13616 "arguments": [], 13617 "directives": [] 13618 }, { 13619 "kind": "Field", 13620 "name": { 13621 "kind": "Name", 13622 "value": "publishingProgress" 13623 }, 13624 "arguments": [], 13625 "directives": [] 13626 }, { 13627 "kind": "Field", 13628 "name": { 13629 "kind": "Name", 13630 "value": "startedPublishingAt" 13631 }, 13632 "arguments": [], 13633 "directives": [] 13634 }, { 13635 "kind": "Field", 13636 "name": { 13637 "kind": "Name", 13638 "value": "finishedPublishingAt" 13639 }, 13640 "arguments": [], 13641 "directives": [] 13642 }, { 13643 "kind": "Field", 13644 "name": { 13645 "kind": "Name", 13646 "value": "proPublishedAt" 13647 }, 13648 "arguments": [], 13649 "directives": [] 13650 }, { 13651 "kind": "Field", 13652 "name": { 13653 "kind": "Name", 13654 "value": "lastPublishedCheckpoint" 13655 }, 13656 "arguments": [], 13657 "directives": [] 13658 }, { 13659 "kind": "Field", 13660 "name": { 13661 "kind": "Name", 13662 "value": "lastPublishedCommitHash" 13663 }, 13664 "arguments": [], 13665 "directives": [] 13666 }, { 13667 "kind": "Field", 13668 "name": { 13669 "kind": "Name",
13670 "value": "aiChatMessagesUsed" 13671 }, 13672 "arguments": [], 13673 "directives": [] 13674 }, { 13675 "kind": "Field", 13676 "name": { 13677 "kind": "Name", 13678 "value": "chatMonthlyAllotment" 13679 }, 13680 "arguments": [], 13681 "directives": [] 13682 }, { 13683 "kind": "Field", 13684 "name": { 13685 "kind": "Name", 13686 "value": "chatMonthlyUsed" 13687 }, 13688 "arguments": [], 13689 "directives": [] 13690 }, { 13691 "kind": "Field", 13692 "name": { 13693 "kind": "Name", 13694 "value": "chatPurchasedCredits" 13695 }, 13696 "arguments": [], 13697 "directives": [] 13698 }, { 13699 "kind": "Field", 13700 "name": { 13701 "kind": "Name", 13702 "value": "chatQuotaCurrentPeriodStartAt" 13703 }, 13704 "arguments": [], 13705 "directives": [] 13706 }, { 13707 "kind": "Field", 13708 "name": { 13709 "kind": "Name", 13710 "value": "chatQuotaCurrentPeriodEndAt" 13711 }, 13712 "arguments": [], 13713 "directives": [] 13714 }, { 13715 "kind": "Field", 13716 "name": { 13717 "kind": "Name", 13718 "value": "relaceRepoId" 13719 }, 13720 "arguments": [], 13721 "directives": [] 13722 }, { 13723 "kind": "Field", 13724 "name": { 13725 "kind": "Name", 13726 "value": "cloudflareContainerUrl" 13727 }, 13728 "arguments": [], 13729 "directives": [] 13730 }, { 13731 "kind": "Field", 13732 "name": { 13733 "kind": "Name", 13734 "value": "aiHomepageCreationPrompt" 13735 }, 13736 "arguments": [], 13737 "directives": [] 13738 }, { 13739 "kind": "Field", 13740 "name": { 13741 "kind": "Name", 13742 "value": "hasSubmittedHomepageCreationMessage" 13743 }, 13744 "arguments": [], 13745 "directives": [] 13746 }, { 13747 "kind": "Field", 13748 "name": { 13749 "kind": "Name", 13750 "value": "startedGeneratingAt" 13751 }, 13752 "arguments": [], 13753 "directives": [] 13754 }, { 13755 "kind": "Field", 13756 "name": { 13757 "kind": "Name", 13758 "value": "finishedGeneratingAt" 13759 }, 13760 "arguments": [], 13761 "directives": [] 13762 }, { 13763 "kind": "Field", 13764 "name": { 13765 "kind": "Name", 13766 "value": "freePublishedAt" 13767 }, 13768 "arguments": [], 13769 "directives": [] 13770 }, { 13771 "kind": "Field", 13772 "name": { 13773 "kind": "Name", 13774 "value": "cloneStatus" 13775 }, 13776 "arguments": [], 13777 "directives": [] 13778 }, { 13779 "kind": "Field", 13780 "name": { 13781 "kind": "Name", 13782 "value": "homepageGeneratedAt" 13783 }, 13784 "arguments": [], 13785 "directives": [] 13786 }, { 13787 "kind": "Field", 13788 "name": { 13789 "kind": "Name", 13790 "value": "aifeaturesSiteToken" 13791 }, 13792 "arguments": [], 13793 "directives": [] 13794 }, { 13795 "kind": "Field", 13796 "name": { 13797 "kind": "Name", 13798 "value": "user" 13799 }, 13800 "arguments": [], 13801 "directives": [], 13802 "selectionSet": { 13803 "kind": "SelectionSet", 13804 "selections": [{ 13805 "kind": "Field", 13806 "name": { 13807 "kind": "Name", 13808 "value": "id" 13809 }, 13810 "arguments": [], 13811 "directives": [] 13812 }, { 13813 "kind": "Field", 13814 "name": { 13815 "kind": "Name", 13816 "value": "email" 13817 }, 13818 "arguments": [], 13819 "directives": [] 13820 }, { 13821 "kind": "Field", 13822 "name": { 13823 "kind": "Name", 13824 "value": "isAgencyPlan" 13825 }, 13826 "arguments": [], 13827 "directives": [] 13828 }, { 13829 "kind": "Field", 13830 "name": { 13831 "kind": "Name", 13832 "value": "agencyMonthlyCredits" 13833 }, 13834 "arguments": [], 13835 "directives": [] 13836 }, { 13837 "kind": "Field", 13838 "name": { 13839 "kind": "Name", 13840 "value": "agencyPaidCredits" 13841 }, 13842 "arguments": [], 13843 "directives": [] 13844 }, { 13845 "kind": "Field", 13846 "name": { 13847 "kind": "Name", 13848 "value": "agencyTotalCredits" 13849 }, 13850 "arguments": [], 13851 "directives": [] 13852 }, { 13853 "kind": "Field", 13854 "name": { 13855 "kind": "Name", 13856 "value": "merchantPaymentsBetaEnabled" 13857 }, 13858 "arguments": [], 13859 "directives": [] 13860 }] 13861 } 13862 }, { 13863 "kind": "Field", 13864 "name": { 13865 "kind": "Name", 13866 "value": "Domain" 13867 }, 13868 "arguments": [], 13869 "directives": [], 13870 "selectionSet": { 13871 "kind": "SelectionSet", 13872 "selections": [{ 13873 "kind": "Field", 13874 "name": { 13875 "kind": "Name", 13876 "value": "id" 13877 }, 13878 "arguments": [], 13879 "directives": [] 13880 }, { 13881 "kind": "Field", 13882 "name": { 13883 "kind": "Name", 13884 "value": "domainName" 13885 }, 13886 "arguments": [], 13887 "directives": [] 13888 }, { 13889 "kind": "Field", 13890 "name": { 13891 "kind": "Name", 13892 "value": "nameserverStatus" 13893 }, 13894 "arguments": [], 13895 "directives": [] 13896 }, { 13897 "kind": "Field", 13898 "name": { 13899 "kind": "Name", 13900 "value": "verificationStatus" 13901 }, 13902 "arguments": [], 13903 "directives": [] 13904 }, { 13905 "kind": "Field", 13906 "name": { 13907 "kind": "Name", 13908 "value": "configurationMethod" 13909 }, 13910 "arguments": [], 13911 "directives": [] 13912 }, { 13913 "kind": "Field", 13914 "name": { 13915 "kind": "Name", 13916 "value": "purchaseType" 13917 }, 13918 "arguments": [], 13919 "directives": [] 13920 }] 13921 } 13922 }, { 13923 "kind": "Field", 13924 "name": { 13925 "kind": "Name", 13926 "value": "reactWebsiteConfig" 13927 }, 13928 "arguments": [], 13929 "directives": [], 13930 "selectionSet": { 13931 "kind": "SelectionSet", 13932 "selections": [{ 13933 "kind": "Field", 13934 "name": { 13935 "kind": "Name", 13936 "value": "containerStatus" 13937 }, 13938 "arguments": [], 13939 "directives": [] 13940 }, { 13941 "kind": "Field", 13942 "name": { 13943 "kind": "Name", 13944 "value": "containerLastActive" 13945 }, 13946 "arguments": [], 13947 "directives": [] 13948 }, { 13949 "kind": "Field", 13950 "name": { 13951 "kind": "Name", 13952 "value": "containerErrorMessage" 13953 }, 13954 "arguments": [], 13955 "directives": [] 13956 }] 13957 } 13958 }] 13959 } 13960 }, { 13961 "kind": "Field", 13962 "name": { 13963 "kind": "Name", 13964 "value": "myWebsiteAccess" 13965 }, 13966 "arguments": [{ 13967 "kind": "Argument", 13968 "name": { 13969 "kind": "Name", 13970 "value": "websiteId" 13971 }, 13972 "value": { 13973 "kind": "Variable", 13974 "name": { 13975 "kind": "Name", 13976 "value": "id" 13977 } 13978 } 13979 }], 13980 "directives": [], 13981 "selectionSet": { 13982 "kind": "SelectionSet", 13983 "selections": [{ 13984 "kind": "Field", 13985 "name": { 13986 "kind": "Name", 13987 "value": "role" 13988 }, 13989 "arguments": [], 13990 "directives": [] 13991 }, { 13992 "kind": "Field", 13993 "name": { 13994 "kind": "Name", 13995 "value": "canEdit" 13996 }, 13997 "arguments": [], 13998 "directives": [] 13999 }] 14000 } 14001 }, { 14002 "kind": "Field", 14003 "alias": { 14004 "kind": "Name", 14005 "value": "pagesForToolbar" 14006 }, 14007 "name": { 14008 "kind": "Name", 14009 "value": "pages" 14010 }, 14011 "arguments": [{ 14012 "kind": "Argument", 14013 "name": { 14014 "kind": "Name", 14015 "value": "websiteId" 14016 }, 14017 "value": { 14018 "kind": "Variable", 14019 "name": { 14020 "kind": "Name", 14021 "value": "id" 14022 } 14023 } 14024 }], 14025 "directives": [], 14026 "selectionSet": { 14027 "kind": "SelectionSet", 14028 "selections": [{ 14029 "kind": "Field", 14030 "name": { 14031 "kind": "Name", 14032 "value": "id" 14033 }, 14034 "arguments": [], 14035 "directives": [] 14036 }, { 14037 "kind": "Field", 14038 "name": { 14039 "kind": "Name", 14040 "value": "name" 14041 }, 14042 "arguments": [], 14043 "directives": [] 14044 }, { 14045 "kind": "Field", 14046 "name": { 14047 "kind": "Name", 14048 "value": "path" 14049 }, 14050 "arguments": [], 14051 "directives": [] 14052 }, { 14053 "kind": "Field", 14054 "name": { 14055 "kind": "Name", 14056 "value": "isHomepage" 14057 }, 14058 "arguments": [], 14059 "directives": [] 14060 }] 14061 } 14062 }] 14063 } 14064 }], 14065 "loc": { 14066 "start": 0, 14067 "end": 2339,
14068 "source": { 14069 "body": ` 14070 query GetReactWebsite($id: String!) { 14071 website(id: $id) { 14072 __typename 14073 id 14074 name 14075 url 14076 publicId 14077 businessName 14078 businessAbout 14079 websiteType 14080 isFirstWebsite 14081 createdAt 14082 stripePlan 14083 stripeIsAnnualPlan 14084 paymentPastDue 14085 pastDueInvoiceUrl 14086 underReview 14087 siteCaseNotice { 14088 type 14089 brand 14090 deadlineAt 14091 paused 14092 } 14093 analyticsId 14094 updatedAt 14095 publishingStatus 14096 publishingStep 14097 publishingProgress 14098 startedPublishingAt 14099 finishedPublishingAt 14100 proPublishedAt 14101 lastPublishedCheckpoint 14102 lastPublishedCommitHash 14103 aiChatMessagesUsed 14104 chatMonthlyAllotment 14105 chatMonthlyUsed 14106 chatPurchasedCredits 14107 chatQuotaCurrentPeriodStartAt 14108 chatQuotaCurrentPeriodEndAt 14109 relaceRepoId 14110 cloudflareContainerUrl 14111 aiHomepageCreationPrompt 14112 hasSubmittedHomepageCreationMessage 14113 startedGeneratingAt 14114 finishedGeneratingAt 14115 # U3/R6. Read by the header's site picker, which falls back to this row 14116 # whenever the landingSites query does not contain this site (a support 14117 # session, or a deep link past the row cap). Without them the picker 14118 # reads a legacy free-published site as a Draft, and a clone in flight 14119 # as "Setting up" instead of "Copying". 14120 freePublishedAt 14121 cloneStatus 14122 homepageGeneratedAt 14123 aifeaturesSiteToken 14124 user { 14125 id 14126 email 14127 isAgencyPlan 14128 agencyMonthlyCredits 14129 agencyPaidCredits 14130 agencyTotalCredits 14131 # The website OWNER's beta flag â merchant views gate on the 14132 # owner's enrollment regardless of who is editing (the same fact 14133 # the sandbox preflight's businessToolsEnabled evaluates, so tool 14134 # gate and view gate stay coupled). 14135 merchantPaymentsBetaEnabled 14136 } 14137 Domain { 14138 id 14139 domainName 14140 nameserverStatus 14141 verificationStatus 14142 configurationMethod 14143 purchaseType 14144 } 14145 reactWebsiteConfig { 14146 containerStatus 14147 containerLastActive 14148 containerErrorMessage 14149 } 14150 } 14151 myWebsiteAccess(websiteId: $id) { 14152 role 14153 canEdit 14154 } 14155 pagesForToolbar: pages(websiteId: $id) { 14156 id 14157 name 14158 path 14159 isHomepage 14160 } 14161 } 14162`, 14163 "name": "GraphQL request", 14164 "locationOffset": { 14165 "line": 1, 14166 "column": 1 14167 } 14168 } 14169 } 14170}; 14171const CLAIM_WEBSITE = { 14172 "kind": "Document", 14173 "definitions": [{ 14174 "kind": "OperationDefinition", 14175 "operation": "mutation", 14176 "name": { 14177 "kind": "Name", 14178 "value": "ReactWebsiteChatPageClaimWebsiteMutation" 14179 }, 14180 "variableDefinitions": [{ 14181 "kind": "VariableDefinition", 14182 "variable": { 14183 "kind": "Variable", 14184 "name": { 14185 "kind": "Name", 14186 "value": "id" 14187 } 14188 }, 14189 "type": { 14190 "kind": "NonNullType", 14191 "type": { 14192 "kind": "NamedType", 14193 "name": { 14194 "kind": "Name", 14195 "value": "String" 14196 } 14197 } 14198 }, 14199 "directives": [] 14200 }], 14201 "directives": [], 14202 "selectionSet": { 14203 "kind": "SelectionSet", 14204 "selections": [{ 14205 "kind": "Field", 14206 "name": { 14207 "kind": "Name", 14208 "value": "claimWebsite" 14209 }, 14210 "arguments": [{ 14211 "kind": "Argument", 14212 "name": { 14213 "kind": "Name", 14214 "value": "id" 14215 }, 14216 "value": { 14217 "kind": "Variable", 14218 "name": { 14219 "kind": "Name", 14220 "value": "id" 14221 } 14222 } 14223 }], 14224 "directives": [], 14225 "selectionSet": { 14226 "kind": "SelectionSet", 14227 "selections": [{ 14228 "kind": "Field", 14229 "name": { 14230 "kind": "Name", 14231 "value": "id" 14232 }, 14233 "arguments": [], 14234 "directives": [] 14235 }] 14236 } 14237 }] 14238 } 14239 }], 14240 "loc": { 14241 "start": 0, 14242 "end": 116, 14243 "source": { 14244 "body": "\n mutation ReactWebsiteChatPageClaimWebsiteMutation($id: String!) {\n claimWebsite(id: $id) {\n id\n }\n }\n", 14245 "name": "GraphQL request", 14246 "locationOffset": { 14247 "line": 1, 14248 "column": 1 14249 } 14250 } 14251 } 14252}; 14253const ENSURE_HOMEPAGE_GENERATION = { 14254 "kind": "Document", 14255 "definitions": [{ 14256 "kind": "OperationDefinition", 14257 "operation": "mutation", 14258 "name": { 14259 "kind": "Name", 14260 "value": "EnsureHomepageGenerationMutation" 14261 }, 14262 "variableDefinitions": [{ 14263 "kind": "VariableDefinition", 14264 "variable": { 14265 "kind": "Variable", 14266 "name": { 14267 "kind": "Name", 14268 "value": "id" 14269 } 14270 }, 14271 "type": { 14272 "kind": "NonNullType", 14273 "type": { 14274 "kind": "NamedType", 14275 "name": { 14276 "kind": "Name", 14277 "value": "String" 14278 } 14279 } 14280 }, 14281 "directives": [] 14282 }], 14283 "directives": [], 14284 "selectionSet": { 14285 "kind": "SelectionSet", 14286 "selections": [{ 14287 "kind": "Field", 14288 "name": { 14289 "kind": "Name", 14290 "value": "ensureHomepageGeneration" 14291 }, 14292 "arguments": [{ 14293 "kind": "Argument", 14294 "name": { 14295 "kind": "Name", 14296 "value": "id" 14297 }, 14298 "value": { 14299 "kind": "Variable", 14300 "name": { 14301 "kind": "Name", 14302 "value": "id" 14303 } 14304 } 14305 }], 14306 "directives": [], 14307 "selectionSet": { 14308 "kind": "SelectionSet", 14309 "selections": [{ 14310 "kind": "Field", 14311 "name": { 14312 "kind": "Name", 14313 "value": "id" 14314 }, 14315 "arguments": [], 14316 "directives": [] 14317 }, { 14318 "kind": "Field", 14319 "name": { 14320 "kind": "Name", 14321 "value": "startedGeneratingAt" 14322 }, 14323 "arguments": [], 14324 "directives": [] 14325 }, { 14326 "kind": "Field", 14327 "name": { 14328 "kind": "Name", 14329 "value": "finishedGeneratingAt" 14330 }, 14331 "arguments": [], 14332 "directives": [] 14333 }, { 14334 "kind": "Field", 14335 "name": { 14336 "kind": "Name", 14337 "value": "hasSubmittedHomepageCreationMessage" 14338 }, 14339 "arguments": [], 14340 "directives": [] 14341 }, { 14342 "kind": "Field", 14343 "name": { 14344 "kind": "Name", 14345 "value": "homepageGeneratedAt" 14346 }, 14347 "arguments": [], 14348 "directives": [] 14349 }] 14350 } 14351 }] 14352 } 14353 }], 14354 "loc": { 14355 "start": 0, 14356 "end": 241, 14357 "source": { 14358 "body": "\n mutation EnsureHomepageGenerationMutation($id: String!) {\n ensureHomepageGeneration(id: $id) {\n id\n startedGeneratingAt\n finishedGeneratingAt\n hasSubmittedHomepageCreationMessage\n homepageGeneratedAt\n }\n }\n", 14359 "name": "GraphQL request", 14360 "locationOffset": { 14361 "line": 1, 14362 "column": 1 14363 } 14364 } 14365 } 14366}; 14367const PUBLISH_REACT_WEBSITE = { 14368 "kind": "Document", 14369 "definitions": [{ 14370 "kind": "OperationDefinition", 14371 "operation": "mutation", 14372 "name": { 14373 "kind": "Name", 14374 "value": "PublishReactWebsiteMutation" 14375 }, 14376 "variableDefinitions": [{ 14377 "kind": "VariableDefinition", 14378 "variable": { 14379 "kind": "Variable", 14380 "name": { 14381 "kind": "Name", 14382 "value": "id" 14383 } 14384 }, 14385 "type": { 14386 "kind": "NonNullType", 14387 "type": { 14388 "kind": "NamedType", 14389 "name": { 14390 "kind": "Name", 14391 "value": "String" 14392 } 14393 } 14394 }, 14395 "directives": [] 14396 }], 14397 "directives": [], 14398 "selectionSet": { 14399 "kind": "SelectionSet", 14400 "selections": [{ 14401 "kind": "Field", 14402 "name": { 14403 "kind": "Name", 14404 "value": "publishReactWebsite" 14405 }, 14406 "arguments": [{ 14407 "kind": "Argument", 14408 "name": { 14409 "kind": "Name", 14410 "value": "id" 14411 }, 14412 "value": { 14413 "kind": "Variable", 14414 "name": { 14415 "kind": "Name", 14416 "value": "id" 14417 } 14418 } 14419 }], 14420 "directives": [], 14421 "selectionSet": { 14422 "kind": "SelectionSet", 14423 "selections": [{ 14424 "kind": "Field", 14425 "name": { 14426 "kind": "Name", 14427 "value": "success" 14428 }, 14429 "arguments": [], 14430 "directives": [] 14431 }, { 14432 "kind": "Field", 14433 "name": { 14434 "kind": "Name", 14435 "value": "error" 14436 }, 14437 "arguments": [], 14438 "directives": [] 14439 }, { 14440 "kind": "Field", 14441 "name": { 14442 "kind": "Name", 14443 "value": "errorCode" 14444 }, 14445 "arguments": [], 14446 "directives": [] 14447 }, { 14448 "kind": "Field", 14449 "name": { 14450 "kind": "Name", 14451 "value": "blockers" 14452 }, 14453 "arguments": [], 14454 "directives": [], 14455 "selectionSet": { 14456 "kind": "SelectionSet", 14457 "selections": [{ 14458 "kind": "Field", 14459 "name": { 14460 "kind": "Name", 14461 "value": "code" 14462 }, 14463 "arguments": [], 14464 "directives": [] 14465 }, { 14466 "kind": "Field", 14467 "name": { 14468 "kind": "Name", 14469 "value": "message" 14470 }, 14471 "arguments": [], 14472 "directives": [] 14473 }] 14474 } 14475 }, { 14476 "kind": "Field", 14477 "name": { 14478 "kind": "Name", 14479 "value": "website" 14480 }, 14481 "arguments": [], 14482 "directives": [], 14483 "selectionSet": { 14484 "kind": "SelectionSet", 14485 "selections": [{ 14486 "kind": "Field", 14487 "name": { 14488 "kind": "Name", 14489 "value": "__typename" 14490 }, 14491 "arguments": [], 14492 "directives": [] 14493 }, { 14494 "kind": "Field", 14495 "name": { 14496 "kind": "Name", 14497 "value": "id" 14498 }, 14499 "arguments": [], 14500 "directives": [] 14501 }, { 14502 "kind": "Field", 14503 "name": { 14504 "kind": "Name", 14505 "value": "publishingStatus" 14506 }, 14507 "arguments": [], 14508 "directives": [] 14509 }, { 14510 "kind": "Field", 14511 "name": { 14512 "kind": "Name", 14513 "value": "finishedPublishingAt" 14514 }, 14515 "arguments": [], 14516 "directives": [] 14517 }, { 14518 "kind": "Field", 14519 "name": { 14520 "kind": "Name", 14521 "value": "proPublishedAt" 14522 }, 14523 "arguments": [], 14524 "directives": [] 14525 }, { 14526 "kind": "Field", 14527 "name": { 14528 "kind": "Name", 14529 "value": "lastPublishedCheckpoint" 14530 }, 14531 "arguments": [], 14532 "directives": [] 14533 }] 14534 } 14535 }] 14536 } 14537 }] 14538 } 14539 }], 14540 "loc": { 14541 "start": 0, 14542 "end": 360, 14543 "source": { 14544 "body": "\n mutation PublishReactWebsiteMutation($id: String!) {\n publishReactWebsite(id: $id) {\n success\n error\n errorCode\n blockers {\n code\n message\n }\n website {\n __typename\n id\n publishingStatus\n finishedPublishingAt\n proPublishedAt\n lastPublishedCheckpoint\n }\n }\n }\n", 14545 "name": "GraphQL request", 14546 "locationOffset": { 14547 "line": 1, 14548 "column": 1 14549 } 14550 } 14551 } 14552}; 14553const GET_LATEST_REACT_VERSION = { 14554 "kind": "Document", 14555 "definitions": [{ 14556 "kind": "OperationDefinition", 14557 "operation": "query", 14558 "name": { 14559 "kind": "Name", 14560 "value": "GetLatestReactVersion" 14561 }, 14562 "variableDefinitions": [{ 14563 "kind": "VariableDefinition", 14564 "variable": { 14565 "kind": "Variable", 14566 "name": { 14567 "kind": "Name", 14568 "value": "websiteId" 14569 } 14570 }, 14571 "type": { 14572 "kind": "NonNullType", 14573 "type": { 14574 "kind": "NamedType", 14575 "name": { 14576 "kind": "Name", 14577 "value": "String" 14578 } 14579 } 14580 }, 14581 "directives": [] 14582 }], 14583 "directives": [], 14584 "selectionSet": { 14585 "kind": "SelectionSet", 14586 "selections": [{ 14587 "kind": "Field", 14588 "name": { 14589 "kind": "Name", 14590 "value": "reactWebsiteVersions" 14591 }, 14592 "arguments": [{ 14593 "kind": "Argument", 14594 "name": { 14595 "kind": "Name", 14596 "value": "websiteId" 14597 }, 14598 "value": { 14599 "kind": "Variable", 14600 "name": { 14601 "kind": "Name", 14602 "value": "websiteId" 14603 } 14604 } 14605 }, { 14606 "kind": "Argument", 14607 "name": { 14608 "kind": "Name", 14609 "value": "take" 14610 }, 14611 "value": { 14612 "kind": "IntValue", 14613 "value": "1" 14614 } 14615 }], 14616 "directives": [], 14617 "selectionSet": { 14618 "kind": "SelectionSet", 14619 "selections": [{ 14620 "kind": "Field", 14621 "name": { 14622 "kind": "Name", 14623 "value": "commitHash" 14624 }, 14625 "arguments": [], 14626 "directives": [] 14627 }] 14628 } 14629 }] 14630 } 14631 }], 14632 "loc": { 14633 "start": 0, 14634 "end": 140, 14635 "source": { 14636 "body": "\n query GetLatestReactVersion($websiteId: String!) {\n reactWebsiteVersions(websiteId: $websiteId, take: 1) {\n commitHash\n }\n }\n", 14637 "name": "GraphQL request", 14638 "locationOffset": { 14639 "line": 1, 14640 "column": 1 14641 } 14642 } 14643 } 14644}; 14645const REFUND_ORDER = { 14646 "kind": "Document", 14647 "definitions": [{ 14648 "kind": "OperationDefinition", 14649 "operation": "mutation", 14650 "name": { 14651 "kind": "Name", 14652 "value": "ReactWebsiteChatPageRefundOrderMutation" 14653 }, 14654 "variableDefinitions": [{ 14655 "kind": "VariableDefinition", 14656 "variable": { 14657 "kind": "Variable", 14658 "name": { 14659 "kind": "Name", 14660 "value": "websiteId" 14661 } 14662 }, 14663 "type": { 14664 "kind": "NonNullType", 14665 "type": { 14666 "kind": "NamedType", 14667 "name": { 14668 "kind": "Name", 14669 "value": "String" 14670 } 14671 } 14672 }, 14673 "directives": [] 14674 }, {
14675 "kind": "VariableDefinition", 14676 "variable": { 14677 "kind": "Variable", 14678 "name": { 14679 "kind": "Name", 14680 "value": "orderId" 14681 } 14682 }, 14683 "type": { 14684 "kind": "NonNullType", 14685 "type": { 14686 "kind": "NamedType", 14687 "name": { 14688 "kind": "Name", 14689 "value": "String" 14690 } 14691 } 14692 }, 14693 "directives": [] 14694 }, { 14695 "kind": "VariableDefinition", 14696 "variable": { 14697 "kind": "Variable", 14698 "name": { 14699 "kind": "Name", 14700 "value": "amount" 14701 } 14702 }, 14703 "type": { 14704 "kind": "NamedType", 14705 "name": { 14706 "kind": "Name", 14707 "value": "Int" 14708 } 14709 }, 14710 "directives": [] 14711 }], 14712 "directives": [], 14713 "selectionSet": { 14714 "kind": "SelectionSet", 14715 "selections": [{ 14716 "kind": "Field", 14717 "name": { 14718 "kind": "Name", 14719 "value": "refundOrder" 14720 }, 14721 "arguments": [{ 14722 "kind": "Argument", 14723 "name": { 14724 "kind": "Name", 14725 "value": "websiteId" 14726 }, 14727 "value": { 14728 "kind": "Variable", 14729 "name": { 14730 "kind": "Name", 14731 "value": "websiteId" 14732 } 14733 } 14734 }, { 14735 "kind": "Argument", 14736 "name": { 14737 "kind": "Name", 14738 "value": "orderId" 14739 }, 14740 "value": { 14741 "kind": "Variable", 14742 "name": { 14743 "kind": "Name", 14744 "value": "orderId" 14745 } 14746 } 14747 }, { 14748 "kind": "Argument", 14749 "name": { 14750 "kind": "Name", 14751 "value": "amount" 14752 }, 14753 "value": { 14754 "kind": "Variable", 14755 "name": { 14756 "kind": "Name", 14757 "value": "amount" 14758 } 14759 } 14760 }], 14761 "directives": [], 14762 "selectionSet": { 14763 "kind": "SelectionSet", 14764 "selections": [{ 14765 "kind": "Field", 14766 "name": { 14767 "kind": "Name", 14768 "value": "status" 14769 }, 14770 "arguments": [], 14771 "directives": [] 14772 }, { 14773 "kind": "Field", 14774 "name": { 14775 "kind": "Name", 14776 "value": "amountRefunded" 14777 }, 14778 "arguments": [], 14779 "directives": [] 14780 }] 14781 } 14782 }] 14783 } 14784 }], 14785 "loc": { 14786 "start": 0, 14787 "end": 243, 14788 "source": { 14789 "body": "\n mutation ReactWebsiteChatPageRefundOrderMutation(\n $websiteId: String!\n $orderId: String!\n $amount: Int\n ) {\n refundOrder(websiteId: $websiteId, orderId: $orderId, amount: $amount) {\n status\n amountRefunded\n }\n }\n", 14790 "name": "GraphQL request", 14791 "locationOffset": { 14792 "line": 1, 14793 "column": 1 14794 } 14795 } 14796 } 14797}; 14798const SiteViews = reactExports.lazy(() => __vitePreload(() => import("./SiteViews-DrsHYaEK.js"), true ? __vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12]) : void 0)); 14799const LoadingSpinner = () => /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex h-screen items-center justify-center bg-white", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-8 w-8 animate-spin rounded-full border-2 border-gray-300 border-t-black" }) }); 14800const ErrorContent = ({ 14801 error 14802}) => { 14803 var _a; 14804 const isNotFound = (error == null ? void 0 : error.message) === "user does not have access to this website" || ((_a = error == null ? void 0 : error.message) == null ? void 0 : _a.includes("not found")); 14805 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-1 items-center justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center", children: [ 14806 /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-xl font-semibold text-gray-900", children: isNotFound ? "Website not found" : "Unable to load website" }), 14807 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-2 text-gray-600", children: isNotFound ? "This website doesn't exist or you don't have access to it." : (error == null ? void 0 : error.message) || "Please try again later." }),
14808 /* @__PURE__ */ jsxRuntimeExports.jsx("a", { href: "/sites", className: "mt-4 inline-block text-sm text-blue-600 hover:text-blue-800", children: "Go back to dashboard" }) 14809 ] }) }); 14810}; 14811const ReactWebsiteChatEditor = ({ 14812 id 14813}) => { 14814 var _a, _b, _c, _d, _e; 14815 const { 14816 currentUser, 14817 isAuthenticated, 14818 loading: authLoading, 14819 getToken 14820 } = useAuth(); 14821 useRecordLastViewedWebsite(id); 14822 const { 14823 search 14824 } = useLocation(); 14825 const chatPanelRef = reactExports.useRef(null); 14826 const [isChatPanelExpanded, setIsChatPanelExpanded] = reactExports.useState(true); 14827 const [previewKey, setPreviewKey] = reactExports.useState(0); 14828 const [isClaiming, setIsClaiming] = reactExports.useState(false); 14829 const [isPublishing, setIsPublishing] = reactExports.useState(false); 14830 const [publishCompletedSignal, setPublishCompletedSignal] = reactExports.useState(0); 14831 const prevPublishingStatusRef = reactExports.useRef(void 0); 14832 const [publishBlockers, setPublishBlockers] = reactExports.useState([]); 14833 const [showUpgradeModal, setShowUpgradeModal] = reactExports.useState(false); 14834 const [upgradeError, _setUpgradeError] = reactExports.useState(null); 14835 const [isPreparationComplete, setIsPreparationComplete] = reactExports.useState(false); 14836 const [isContainerReady, setIsContainerReady] = reactExports.useState(false); 14837 const [containerStatus, setContainerStatus] = reactExports.useState(null); 14838 const [localAgencyCreditsDelta, setLocalAgencyCreditsDelta] = reactExports.useState(0); 14839 const [localAiChatMessagesDelta, setLocalAiChatMessagesDelta] = reactExports.useState(0); 14840 const [pickModeActive, setPickModeActive] = reactExports.useState(false); 14841 const [selectedElement, setSelectedElement] = reactExports.useState(null); 14842 const [isSelectorReady, setIsSelectorReady] = reactExports.useState(false); 14843 const [isPickerAvailable, setIsPickerAvailable] = reactExports.useState(false); 14844 const [currentPath, setCurrentPath] = reactExports.useState("/"); 14845 const [editTextModeActive, setEditTextModeActive] = reactExports.useState(false); 14846 const [isAgentResponding, setIsAgentResponding] = reactExports.useState(false); 14847 const handleAgentBusyChange = reactExports.useCallback((busy) => { 14848 setIsAgentResponding(busy); 14849 }, []); 14850 const [showPublishWhileBusy, setShowPublishWhileBusy] = reactExports.useState(false); 14851 const [previewRefreshSignal, setPreviewRefreshSignal] = reactExports.useState(0); 14852 const handlePaymentsDataChanged = reactExports.useCallback(() => { 14853 setPreviewRefreshSignal((n) => n + 1); 14854 }, []); 14855 const [activeView, setActiveView] = reactExports.useState("website"); 14856 const [showViewNonce, setShowViewNonce] = reactExports.useState(0); 14857 const [viewRefreshSignal, setViewRefreshSignal] = reactExports.useState(0); 14858 const [orderDetailId, setOrderDetailId] = reactExports.useState(void 0); 14859 const [invoiceDetailId, setInvoiceDetailId] = reactExports.useState(void 0); 14860 const isDesktop = useBreakpoint("lg"); 14861 const handleBusinessDataChanged = reactExports.useCallback(() => {
14862 setViewRefreshSignal((n) => n + 1); 14863 }, []); 14864 const handleBackToWebsite = reactExports.useCallback(() => { 14865 setActiveView("website"); 14866 setOrderDetailId(void 0); 14867 setInvoiceDetailId(void 0); 14868 }, []); 14869 reactExports.useEffect(() => { 14870 if (activeView !== "website") { 14871 __vitePreload(() => Promise.resolve({}), true ? __vite__mapDeps([13]) : void 0); 14872 } 14873 }, [activeView]); 14874 const [isEditBusy, setIsEditBusy] = reactExports.useState(false); 14875 const handleEditBusyChange = reactExports.useCallback((busy) => { 14876 setIsEditBusy(busy); 14877 }, []); 14878 const [viteError, setViteError] = reactExports.useState(null); 14879 const viteErrorCaptureTimer = reactExports.useRef(null); 14880 const currentViteErrorSig = reactExports.useRef(null); 14881 const viteErrorClearedSinceSeen = reactExports.useRef(false); 14882 const handleViteErrorCleared = reactExports.useCallback(() => { 14883 setViteError(null); 14884 if (viteErrorCaptureTimer.current) { 14885 clearTimeout(viteErrorCaptureTimer.current); 14886 viteErrorCaptureTimer.current = null; 14887 } 14888 viteErrorClearedSinceSeen.current = true; 14889 }, []); 14890 const handleTogglePickMode = reactExports.useCallback(() => { 14891 setPickModeActive((prev) => { 14892 const next = !prev; 14893 if (next) setEditTextModeActive(false); 14894 return next; 14895 }); 14896 }, []); 14897 const handleToggleEditTextMode = reactExports.useCallback(() => { 14898 setEditTextModeActive((prev) => { 14899 const next = !prev; 14900 if (next) setPickModeActive(false); 14901 return next; 14902 }); 14903 }, []); 14904 const handleEditTextModeExit = reactExports.useCallback(() => { 14905 setEditTextModeActive(false); 14906 }, []); 14907 const handleApplyEditsWithAI = reactExports.useCallback((prompt) => { 14908 var _a2; 14909 (_a2 = chatPanelRef.current) == null ? void 0 : _a2.sendUserMessage(prompt); 14910 }, []); 14911 const handleClearSelection = reactExports.useCallback(() => { 14912 setSelectedElement(null); 14913 }, []); 14914 const handlePicked = reactExports.useCallback((payload) => { 14915 setSelectedElement(payload); 14916 setPickModeActive(false); 14917 }, []); 14918 const handlePickModeExit = reactExports.useCallback(() => { 14919 setPickModeActive(false); 14920 }, []); 14921 const handleIframeReload = reactExports.useCallback(() => { 14922 setPickModeActive(false); 14923 setSelectedElement(null); 14924 setEditTextModeActive(false); 14925 }, []); 14926 const handleSelectorReadyChange = reactExports.useCallback((ready) => { 14927 setIsSelectorReady(ready); 14928 }, []); 14929 const handlePickerAvailabilityChange = reactExports.useCallback((available) => { 14930 setIsPickerAvailable(available); 14931 }, []); 14932 const handleCurrentPathChange = reactExports.useCallback((path) => { 14933 setCurrentPath(path); 14934 }, []); 14935 reactExports.useEffect(() => { 14936 if (!pickModeActive) return; 14937 const handleKeyDown = (e) => { 14938 if (e.key === "Escape") { 14939 e.preventDefault(); 14940 handlePickModeExit(); 14941 } 14942 }; 14943 window.addEventListener("keydown", handleKeyDown); 14944 return () => { 14945 window.removeEventListener("keydown", handleKeyDown); 14946 }; 14947 }, [pickModeActive, handlePickModeExit]); 14948 const lastKnownCommitHashRef = reactExports.useRef(null); 14949 const isAIRespondingRef = reactExports.useRef(false); 14950 const { 14951 data, 14952 loading, 14953 error, 14954 refetch, 14955 startPolling, 14956 stopPolling 14957 } = useQuery$1(GET_REACT_WEBSITE, { 14958 variables: { 14959 id 14960 }, 14961 skip: !isAuthenticated, 14962 // Don't query until authenticated 14963 fetchPolicy: "network-only", 14964 // Always fetch fresh data (credits, publishing status, etc.) 14965 // Don't poll while the tab is in the background â the user can't see 14966 // the result, and the visibility-change handler below does a fresh 14967 // refetch the moment the tab is visible again. 14968 skipPollAttempt: () => document.hidden 14969 }); 14970 reactExports.useEffect(() => { 14971 if (!authLoading && !isAuthenticated) { 14972 const urlParams = new URLSearchParams(search); 14973 const screenHint = urlParams.get("screen_hint"); 14974 urlParams.delete("screen_hint"); 14975 const cleanSearch = urlParams.toString(); 14976 const cleanPath = window.location.pathname + (cleanSearch ? `?${cleanSearch}` : ""); 14977 const encodedRedirect = encodeURIComponent(cleanPath); 14978 if (screenHint === "signup") { 14979 navigate(namedRoutes.signup({ 14980 redirectTo: encodedRedirect 14981 })); 14982 } else { 14983 navigate(namedRoutes.login({ 14984 redirectTo: encodedRedirect 14985 })); 14986 } 14987 } 14988 if (isAuthenticated) {
14989 const urlParams = new URLSearchParams(search); 14990 const hasScreenHint = urlParams.has("screen_hint"); 14991 if (hasScreenHint) { 14992 urlParams.delete("screen_hint"); 14993 const newSearch = urlParams.toString(); 14994 const newPath = window.location.pathname + (newSearch ? `?${newSearch}` : ""); 14995 navigate(newPath, { 14996 replace: true 14997 }); 14998 } 14999 } 15000 }, [authLoading, isAuthenticated, search]); 15001 const [publishReactWebsite] = useMutation$1(PUBLISH_REACT_WEBSITE); 15002 const [claimWebsite] = useMutation$1(CLAIM_WEBSITE); 15003 const [ensureHomepageGeneration] = useMutation$1(ENSURE_HOMEPAGE_GENERATION); 15004 const [refundOrder] = useMutation$1(REFUND_ORDER); 15005 const refundInFlight = reactExports.useRef(false); 15006 const posthog = usePostHog(); 15007 const hasFiredFirstViewRef = reactExports.useRef(false); 15008 const hasAttemptedClaimRef = reactExports.useRef(false); 15009 const hasAttemptedEnsureRef = reactExports.useRef(false); 15010 reactExports.useEffect(() => { 15011 if (!posthog) return; 15012 return posthog.onFeatureFlags(() => { 15013 if (posthog.isFeatureEnabled("disable-editor-session-recording")) { 15014 posthog.stopSessionRecording(); 15015 return; 15016 } 15017 posthog.startSessionRecording(); 15018 }); 15019 }, [posthog]); 15020 reactExports.useEffect(() => { 15021 if (!posthog) return; 15022 return startLongFrameTelemetry((event, properties) => posthog.capture(event, properties), id); 15023 }, [posthog, id]); 15024 const handleViteError = reactExports.useCallback((payload) => { 15025 setViteError(payload); 15026 try { 15027 const signatureHash = viteErrorSignature(payload); 15028 const dedupKey = `ls_react_err_${id}_${signatureHash}`; 15029 if (localStorage.getItem(dedupKey)) return; 15030 const capture = () => { 15031 var _a2; 15032 viteErrorCaptureTimer.current = null; 15033 if (localStorage.getItem(dedupKey)) return; 15034 localStorage.setItem(dedupKey, "1"); 15035 posthog == null ? void 0 : posthog.capture("react_website_error_detected", { 15036 websiteId: id, 15037 source: "frontend_overlay", 15038 message: (_a2 = payload.message) == null ? void 0 : _a2.slice(0, 500), 15039 file: payload.file, 15040 path: payload.path, 15041 signatureHash, 15042 websiteType: "react" 15043 }); 15044 }; 15045 if (signatureHash === currentViteErrorSig.current && viteErrorClearedSinceSeen.current) { 15046 if (viteErrorCaptureTimer.current) { 15047 clearTimeout(viteErrorCaptureTimer.current); 15048 } 15049 capture(); 15050 return; 15051 } 15052 if (signatureHash !== currentViteErrorSig.current) { 15053 currentViteErrorSig.current = signatureHash; 15054 viteErrorClearedSinceSeen.current = false; 15055 if (viteErrorCaptureTimer.current) { 15056 clearTimeout(viteErrorCaptureTimer.current); 15057 } 15058 viteErrorCaptureTimer.current = setTimeout(capture, 1500); 15059 } 15060 } catch { 15061 } 15062 }, [id, posthog]); 15063 const website = data == null ? void 0 : data.website; 15064 const myWebsiteAccess = data == null ? void 0 : data.myWebsiteAccess; 15065 const hasSeededFirstViewRef = reactExports.useRef(false); 15066 const prevHomepageGeneratedAtRef = reactExports.useRef(null); 15067 reactExports.useEffect(() => { 15068 var _a2; 15069 if (!website) return; 15070 const prev = prevHomepageGeneratedAtRef.current; 15071 prevHomepageGeneratedAtRef.current = website.homepageGeneratedAt ?? null; 15072 if (!hasSeededFirstViewRef.current) { 15073 hasSeededFirstViewRef.current = true; 15074 return; 15075 } 15076 if (prev !== null || !website.homepageGeneratedAt) return; 15077 if (!(currentUser == null ? void 0 : currentUser.id) || ((_a2 = website.user) == null ? void 0 : _a2.id) !== currentUser.id) return; 15078 if (hasFiredFirstViewRef.current) return; 15079 hasFiredFirstViewRef.current = true; 15080 posthog == null ? void 0 : posthog.capture("Website First View After Build"); 15081 }, [website, currentUser == null ? void 0 : currentUser.id, posthog]); 15082 const hasFiredViewedRef = reactExports.useRef(false); 15083 const generatedAtMountRef = reactExports.useRef(null); 15084 reactExports.useEffect(() => { 15085 var _a2; 15086 if (!website) return; 15087 if (generatedAtMountRef.current === null) { 15088 generatedAtMountRef.current = !!website.homepageGeneratedAt; 15089 } 15090 if (hasFiredViewedRef.current) return; 15091 if (!website.homepageGeneratedAt) return; 15092 if (!(currentUser == null ? void 0 : currentUser.id) || ((_a2 = website.user) == null ? void 0 : _a2.id) !== currentUser.id) return; 15093 hasFiredViewedRef.current = true; 15094 posthog == null ? void 0 : posthog.capture("Generated Website Viewed", { 15095 websiteId: website.id, 15096 viewMode: generatedAtMountRef.current ? "returned" : "live" 15097 }); 15098 }, [website, currentUser == null ? void 0 : currentUser.id, posthog]); 15099 reactExports.useEffect(() => { 15100 var _a2; 15101 if (website && !website.user && isAuthenticated && !hasAttemptedClaimRef.current) { 15102 const isAdmin = (_a2 = currentUser == null ? void 0 : currentUser.roles) == null ? void 0 : _a2.includes("support"); 15103 if (!isAdmin) { 15104 hasAttemptedClaimRef.current = true; 15105 setIsClaiming(true); 15106 claimWebsite({ 15107 variables: { 15108 id: website.id 15109 } 15110 }).then(() => refetch()).then(() => setIsClaiming(false)).catch(async (error2) => { 15111 var _a3, _b2, _c2; 15112 const message = (error2 == null ? void 0 : error2.message) || "Failed to claim this website."; 15113 if (message.includes("already claimed")) { 15114 try { 15115 const result = await refetch(); 15116 if (((_c2 = (_b2 = (_a3 = result == null ? void 0 : result.data) == null ? void 0 : _a3.website) == null ? void 0 : _b2.user) == null ? void 0 : _c2.id) === (currentUser == null ? void 0 : currentUser.id)) { 15117 setIsClaiming(false); 15118 return; 15119 } 15120 } catch { 15121 } 15122 } 15123 console.error("Failed to claim website:", error2); 15124 navigate(namedRoutes.sites() + `?error=${encodeURIComponent(message)}`); 15125 }); 15126 } 15127 } 15128 }, [website, isAuthenticated, currentUser]); 15129 reactExports.useEffect(() => { 15130 var _a2; 15131 if (hasAttemptedEnsureRef.current) return; 15132 if (!website || website.websiteType !== "react") return; 15133 if (!(currentUser == null ? void 0 : currentUser.id) || ((_a2 = website.user) == null ? void 0 : _a2.id) !== currentUser.id) return; 15134 if (website.homepageGeneratedAt) return; 15135 const prepStalled = website.startedGeneratingAt && !website.finishedGeneratingAt; 15136 const turnUndispatched = website.finishedGeneratingAt && !website.hasSubmittedHomepageCreationMessage; 15137 if (!prepStalled && !turnUndispatched) return; 15138 hasAttemptedEnsureRef.current = true; 15139 ensureHomepageGeneration({ 15140 variables: { 15141 id: website.id 15142 } 15143 }).catch((err) => { 15144 console.error("Failed to ensure homepage generation:", err); 15145 }); 15146 }, [website, currentUser == null ? void 0 : currentUser.id, ensureHomepageGeneration]); 15147 const isBeingPrepared = (website == null ? void 0 : website.websiteType) === "react" && !(website == null ? void 0 : website.finishedGeneratingAt); 15148 reactExports.useEffect(() => { 15149 if (!isBeingPrepared && (website == null ? void 0 : website.finishedGeneratingAt) && !isPreparationComplete) { 15150 setIsPreparationComplete(true); 15151 } 15152 }, [isBeingPrepared, isPreparationComplete, website == null ? void 0 : website.finishedGeneratingAt]); 15153 reactExports.useEffect(() => { 15154 setIsPublishing((website == null ? void 0 : website.publishingStatus) === "In Progress"); 15155 }, [website == null ? void 0 : website.publishingStatus]); 15156 reactExports.useEffect(() => { 15157 const current = website == null ? void 0 : website.publishingStatus; 15158 if (current === void 0) return; 15159 const previous = prevPublishingStatusRef.current; 15160 prevPublishingStatusRef.current = current; 15161 if (previous !== void 0 && previous !== "Completed" && current === "Completed") { 15162 setPublishCompletedSignal((n) => n + 1); 15163 } 15164 }, [website == null ? void 0 : website.publishingStatus]); 15165 reactExports.useEffect(() => {
15166 setLocalAiChatMessagesDelta(0); 15167 setLocalAgencyCreditsDelta(0); 15168 }, [website == null ? void 0 : website.aiChatMessagesUsed, (_a = website == null ? void 0 : website.user) == null ? void 0 : _a.agencyTotalCredits]); 15169 const idlePollInterval = isBeingPrepared && !isPreparationComplete ? 2e3 : 15e3; 15170 reactExports.useEffect(() => { 15171 startPolling((website == null ? void 0 : website.publishingStatus) === "In Progress" ? 2e3 : idlePollInterval); 15172 return () => stopPolling(); 15173 }, [idlePollInterval, website == null ? void 0 : website.publishingStatus, startPolling, stopPolling]); 15174 reactExports.useEffect(() => { 15175 if ((website == null ? void 0 : website.websiteType) === "html" || (website == null ? void 0 : website.websiteType) === null) { 15176 navigate(namedRoutes.websiteChat({ 15177 id 15178 })); 15179 } 15180 }, [website == null ? void 0 : website.websiteType, id]); 15181 const previewBaseDomain = "landingsite.dev"; 15182 const previewUrl = (website == null ? void 0 : website.relaceRepoId) && (website == null ? void 0 : website.publicId) ? `https://${website.publicId}.${previewBaseDomain}` : null; 15183 reactExports.useEffect(() => { 15184 if ((website == null ? void 0 : website.hasSubmittedHomepageCreationMessage) && !isContainerReady) { 15185 setIsContainerReady(true); 15186 return; 15187 } 15188 if (!(website == null ? void 0 : website.finishedGeneratingAt) || !previewUrl || isContainerReady) { 15189 return; 15190 } 15191 let isCancelled = false; 15192 let pollInterval = null; 15193 const checkStatus = async () => { 15194 try { 15195 const token = await getToken(); 15196 const statusUrl = `${previewUrl}/__container/status`; 15197 const headers = {}; 15198 if (token) { 15199 headers["Authorization"] = `Bearer ${token}`; 15200 } 15201 const response = await fetch(statusUrl, { 15202 headers 15203 }); 15204 if (!response.ok || isCancelled) return; 15205 const data2 = await response.json(); 15206 setContainerStatus(data2.status); 15207 if (data2.ready === true) { 15208 setIsContainerReady(true); 15209 if (pollInterval) clearInterval(pollInterval); 15210 } 15211 } catch { 15212 } 15213 }; 15214 checkStatus(); 15215 pollInterval = setInterval(checkStatus, 2e3); 15216 return () => { 15217 isCancelled = true; 15218 if (pollInterval) clearInterval(pollInterval); 15219 }; 15220 }, [website == null ? void 0 : website.finishedGeneratingAt, website == null ? void 0 : website.hasSubmittedHomepageCreationMessage, previewUrl, isContainerReady, getToken]); 15221 const apolloClient = useApolloClient(); 15222 const handleCommitHashChange = reactExports.useCallback((hash) => { 15223 const prevHash = lastKnownCommitHashRef.current; 15224 lastKnownCommitHashRef.current = hash; 15225 const shouldBump = !!(prevHash && prevHash !== hash && viteError); 15226 console.log("[ls:preview] commit-hash-change", { 15227 prevHash: (prevHash == null ? void 0 : prevHash.slice(0, 7)) ?? null, 15228 newHash: hash.slice(0, 7), 15229 hashChanged: prevHash !== hash, 15230 hasViteError: !!viteError, 15231 viteErrorMessage: (viteError == null ? void 0 : viteError.message) ?? null, 15232 bumpingPreviewKey: shouldBump 15233 }); 15234 if (shouldBump) { 15235 setPreviewKey((prev) => prev + 1); 15236 } 15237 }, [viteError]); 15238 reactExports.useEffect(() => { 15239 if (!(website == null ? void 0 : website.id) || !isContainerReady) return; 15240 const handleVisibilityChange = async () => { 15241 var _a2, _b2, _c2, _d2, _e2; 15242 if (document.visibilityState !== "visible") return; 15243 if (isAIRespondingRef.current) return; 15244 refetch(); 15245 setLocalAgencyCreditsDelta(0); 15246 setLocalAiChatMessagesDelta(0); 15247 try { 15248 const result = await apolloClient.query({ 15249 query: GET_LATEST_REACT_VERSION, 15250 variables: { 15251 websiteId: website.id 15252 }, 15253 fetchPolicy: "network-only" 15254 }); 15255 const latestHash = ((_c2 = (_b2 = (_a2 = result.data) == null ? void 0 : _a2.reactWebsiteVersions) == null ? void 0 : _b2[0]) == null ? void 0 : _c2.commitHash) ?? null; 15256 if (latestHash && lastKnownCommitHashRef.current && latestHash !== lastKnownCommitHashRef.current) { 15257 console.log("[ReactWebsiteChatPage] External changes detected, refreshing preview and chat", { 15258 previous: (_d2 = lastKnownCommitHashRef.current) == null ? void 0 : _d2.substring(0, 7), 15259 latest: latestHash.substring(0, 7) 15260 }); 15261 lastKnownCommitHashRef.current = latestHash; 15262 setPreviewKey((prev) => prev + 1); 15263 (_e2 = chatPanelRef.current) == null ? void 0 : _e2.reloadMessages(); 15264 } else if (latestHash) { 15265 lastKnownCommitHashRef.current = latestHash; 15266 } 15267 } catch { 15268 } 15269 }; 15270 document.addEventListener("visibilitychange", handleVisibilityChange); 15271 return () => { 15272 document.removeEventListener("visibilitychange", handleVisibilityChange); 15273 }; 15274 }, [website == null ? void 0 : website.id, isContainerReady, apolloClient, refetch]); 15275 const handleRefreshPreview = reactExports.useCallback(() => { 15276 setPreviewKey((prev) => prev + 1); 15277 }, []); 15278 const handleRefetchWebsite = reactExports.useCallback(() => { 15279 refetch();
15280 setLocalAgencyCreditsDelta(0); 15281 setLocalAiChatMessagesDelta(0); 15282 }, [refetch]); 15283 const handleAgencyCreditUsed = reactExports.useCallback(() => { 15284 setLocalAgencyCreditsDelta((prev) => prev - 1); 15285 }, []); 15286 const handleAiChatMessageUsed = reactExports.useCallback(() => { 15287 setLocalAiChatMessagesDelta((prev) => prev + 1); 15288 }, []); 15289 const toolbarPages = reactExports.useMemo(() => (data == null ? void 0 : data.pagesForToolbar) ?? [], [data == null ? void 0 : data.pagesForToolbar]); 15290 const chatWebsite = reactExports.useMemo(() => website ? { 15291 id: website.id, 15292 publicId: website.publicId, 15293 analyticsId: website.analyticsId, 15294 name: website.name, 15295 businessName: website.businessName, 15296 websiteType: website.websiteType, 15297 homepageGeneratedAt: website.homepageGeneratedAt, 15298 stripePlan: website.stripePlan, 15299 aiChatMessagesUsed: website.aiChatMessagesUsed, 15300 chatMonthlyAllotment: website.chatMonthlyAllotment, 15301 chatMonthlyUsed: website.chatMonthlyUsed, 15302 chatPurchasedCredits: website.chatPurchasedCredits, 15303 user: website.user, 15304 Domain: website.Domain 15305 } : void 0, [website == null ? void 0 : website.id, website == null ? void 0 : website.publicId, website == null ? void 0 : website.analyticsId, website == null ? void 0 : website.name, website == null ? void 0 : website.businessName, website == null ? void 0 : website.websiteType, website == null ? void 0 : website.homepageGeneratedAt, website == null ? void 0 : website.stripePlan, website == null ? void 0 : website.aiChatMessagesUsed, website == null ? void 0 : website.chatMonthlyAllotment, website == null ? void 0 : website.chatMonthlyUsed, website == null ? void 0 : website.chatPurchasedCredits, website == null ? void 0 : website.user, website == null ? void 0 : website.Domain]); 15306 const handleAIRespondingChange = reactExports.useCallback((responding) => { 15307 isAIRespondingRef.current = responding; 15308 }, []); 15309 const canEditWebsite = !!(myWebsiteAccess == null ? void 0 : myWebsiteAccess.canEdit); 15310 const accessRole = myWebsiteAccess == null ? void 0 : myWebsiteAccess.role; 15311 const aifeaturesSiteToken = (website == null ? void 0 : website.aifeaturesSiteToken) ?? null; 15312 const ownerCommerceEnabled = !!((_b = website == null ? void 0 : website.user) == null ? void 0 : _b.merchantPaymentsBetaEnabled); 15313 const isSitePublished = Boolean(website == null ? void 0 : website.proPublishedAt); 15314 const surfaceFacts = reactExports.useMemo(() => ({ 15315 isPublished: isSitePublished, 15316 /** 15317 * NOT QUERYABLE HERE YET. Nothing on the editor's website query answers 15318 * "does this site sell anything" â no product count, no connected 15319 * Stripe account. No surface rule reads this fact today, and 15320 * `siteSurfaces.test.ts` fails the moment one starts to, which is the 15321 * signal that this call site needs a real source before it can. 15322 */ 15323 commerceRelevant: false, 15324 role: accessRole, 15325 canEdit: canEditWebsite, 15326 ownerCommerceEnabled 15327 }), [isSitePublished, accessRole, canEditWebsite, ownerCommerceEnabled]); 15328 const canOpenView = reactExports.useCallback((view) => canOpenSiteSurface(view, surfaceFacts, !!aifeaturesSiteToken), [surfaceFacts, aifeaturesSiteToken]); 15329 const handleShowView = reactExports.useCallback( 15330 (view, params, source = "agent") => { 15331 if (view === "website") { 15332 setActiveView("website"); 15333 setOrderDetailId(void 0); 15334 setInvoiceDetailId(void 0); 15335 return; 15336 } 15337 if (!canOpenView(view)) return; 15338 posthog == null ? void 0 : posthog.capture("merchant_view_opened", { 15339 view, 15340 deep_open: Boolean((params == null ? void 0 : params.orderId) || (params == null ? void 0 : params.invoiceId)), 15341 source, 15342 websiteId: id 15343 }); 15344 setActiveView(view); 15345 setOrderDetailId(view === "orders" ? params == null ? void 0 : params.orderId : void 0); 15346 setInvoiceDetailId(view === "invoices" ? params == null ? void 0 : params.invoiceId : void 0); 15347 setShowViewNonce((n) => n + 1); 15348 if (!isDesktop) { 15349 setIsChatPanelExpanded(false); 15350 } 15351 }, 15352 // The gate rules live in `canOpenView`; this only needs the callback plus 15353 // what it uses for instrumentation and layout. 15354 [canOpenView, isDesktop, posthog, id] 15355 ); 15356 const handleMerchantViewRefund = reactExports.useCallback(async (order, amount) => { 15357 if (refundInFlight.current) { 15358 throw new Error("A refund is already in progress."); 15359 } 15360 refundInFlight.current = true; 15361 try { 15362 await refundOrder({ 15363 variables: { 15364 websiteId: id, 15365 orderId: order.id, 15366 amount 15367 } 15368 }); 15369 _t.success("Refund issued");
15370 setViewRefreshSignal((n) => n + 1); 15371 } finally { 15372 refundInFlight.current = false; 15373 } 15374 }, [refundOrder, id]); 15375 const isOwnerSide = accessRole === "OWNER" || accessRole === "ADMIN"; 15376 const merchantViewAllowed = activeView !== "website" && canOpenView(activeView); 15377 const merchantViewEl = reactExports.useMemo(() => { 15378 if (!merchantViewAllowed || !aifeaturesSiteToken) return null; 15379 return ( 15380 // NO FALLBACK UI. The chunk is fetched on the same interaction that 15381 // opens the view, and a spinner that flashes for one frame on a warm 15382 // cache reads as jank rather than as progress. The dock already shows 15383 // the switch happening. 15384 /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: null, children: /* @__PURE__ */ jsxRuntimeExports.jsx( 15385 SiteViews, 15386 { 15387 view: activeView, 15388 viewKey: `${activeView}:${showViewNonce}`, 15389 siteToken: aifeaturesSiteToken, 15390 apiUrl: void 0, 15391 refreshKey: viewRefreshSignal, 15392 initialOrderId: orderDetailId, 15393 initialInvoiceId: invoiceDetailId, 15394 isOwner: isOwnerSide, 15395 needsPaidPlan: (website == null ? void 0 : website.stripePlan) !== "Pro", 15396 published: Boolean(website == null ? void 0 : website.proPublishedAt), 15397 lastPublishedAt: (website == null ? void 0 : website.proPublishedAt) ?? null, 15398 onRefund: handleMerchantViewRefund, 15399 onBackToWebsite: handleBackToWebsite, 15400 onUpgradePlan: () => navigate(namedRoutes.reactWebsiteChatSettingsTab({ 15401 id, 15402 tab: "plan" 15403 })), 15404 onConnectStripe: () => navigate(namedRoutes.settingsTab({ 15405 tab: "payments" 15406 })) 15407 } 15408 ) }) 15409 ); 15410 }, [merchantViewAllowed, activeView, showViewNonce, viewRefreshSignal, orderDetailId, invoiceDetailId, aifeaturesSiteToken, isOwnerSide, handleMerchantViewRefund, handleBackToWebsite, website == null ? void 0 : website.stripePlan, website == null ? void 0 : website.proPublishedAt, id]); 15411 reactExports.useEffect(() => { 15412 if (activeView !== "website" && !merchantViewAllowed) { 15413 setActiveView("website"); 15414 setOrderDetailId(void 0); 15415 setInvoiceDetailId(void 0); 15416 } 15417 }, [activeView, merchantViewAllowed]); 15418 const viewSwitcherEl = reactExports.useMemo(() => { 15419 const options = editorSiteSurfaces(surfaceFacts, !!aifeaturesSiteToken); 15420 if (options.length < 2) return null; 15421 const active = options.find((o) => o.surface === activeView) ?? options[0]; 15422 const ActiveIcon = active.icon; 15423 return /* @__PURE__ */ jsxRuntimeExports.jsxs(Root2$2, { children: [
15424 /* @__PURE__ */ jsxRuntimeExports.jsx(Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { type: "button", "aria-label": `Current view: ${active.label}. Switch view`, className: "group flex items-center gap-1.5 rounded-[9px] border border-[#1B1A22]/[0.08] bg-white px-2 py-1.5 text-[11.5px] font-medium text-[#1B1A22] shadow-[0_1px_2px_rgba(27,26,34,0.05)] transition-colors hover:border-[#1B1A22]/[0.16] data-[state=open]:border-[#1B1A22]/[0.16] data-[state=open]:bg-[#1B1A22]/[0.03]", children: [ 15425 /* @__PURE__ */ jsxRuntimeExports.jsx(ActiveIcon, { size: 13, strokeWidth: 2, className: "flex-shrink-0 text-[#6B6878]" }), 15426 active.label, 15427 /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { size: 12, strokeWidth: 2.5, className: "flex-shrink-0 text-[#9995A6] transition-transform duration-150 group-data-[state=open]:rotate-180" }) 15428 ] }) }), 15429 /* @__PURE__ */ jsxRuntimeExports.jsx(Portal2, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(Content2$1, { align: "end", sideOffset: 6, className: "z-50 min-w-[212px] rounded-[12px] border border-[#1B1A22]/[0.08] bg-white p-1 shadow-[0_20px_48px_-18px_rgba(27,26,34,0.32)] data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95", children: options.map((option) => { 15430 const isActive = option.surface === activeView; 15431 const OptionIcon = option.icon; 15432 return /* @__PURE__ */ jsxRuntimeExports.jsxs(Item2, { onSelect: () => handleShowView(option.surface, void 0, "user"), className: "flex cursor-pointer select-none items-center gap-2.5 rounded-[8px] px-2 py-1.5 outline-none data-[highlighted]:bg-[#1B1A22]/[0.045]", children: [
15433 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `grid h-6 w-6 flex-shrink-0 place-items-center rounded-[7px] ${isActive ? "bg-[#6E56CF]/[0.10] text-[#6E56CF]" : "bg-[#1B1A22]/[0.045] text-[#6B6878]"}`, children: /* @__PURE__ */ jsxRuntimeExports.jsx(OptionIcon, { size: 13, strokeWidth: 2 }) }), 15434 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "min-w-0 leading-tight", children: [ 15435 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "block text-[12.5px] font-medium text-[#1B1A22]", children: option.label }), 15436 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "block truncate text-[10.5px] text-[#9995A6]", children: option.hint }) 15437 ] }), 15438 isActive && /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { size: 13, strokeWidth: 2.75, className: "ml-auto flex-shrink-0 text-[#6E56CF]" }) 15439 ] }, option.surface); 15440 }) }) }) 15441 ] }); 15442 }, [surfaceFacts, aifeaturesSiteToken, activeView, handleShowView]); 15443 const handlePublish = reactExports.useCallback(async () => { 15444 var _a2, _b2, _c2; 15445 if (!website) return; 15446 setPublishBlockers([]); 15447 setIsPublishing(true); 15448 startPolling(1e3); 15449 try { 15450 const { 15451 data: publishData 15452 } = await publishReactWebsite({ 15453 variables: { 15454 id: website.id 15455 } 15456 }); 15457 const result = publishData == null ? void 0 : publishData.publishReactWebsite; 15458 if (result && !result.success) { 15459 startPolling(idlePollInterval); 15460 setIsPublishing(false); 15461 setPublishBlockers(((_a2 = result.blockers) == null ? void 0 : _a2.length) ? result.blockers : result.error ? [{ 15462 code: result.errorCode ?? "publish_failed", 15463 message: result.error 15464 }] : []); 15465 } 15466 } catch (err) { 15467 console.error("Publish mutation failed:", err); 15468 let claimed = false; 15469 let reachable = true; 15470 let freshStatus; 15471 try { 15472 const fresh = (_c2 = (_b2 = await refetch()) == null ? void 0 : _b2.data) == null ? void 0 : _c2.website; 15473 freshStatus = fresh == null ? void 0 : fresh.publishingStatus; 15474 claimed = freshStatus === "In Progress" || (fresh == null ? void 0 : fresh.startedPublishingAt) != null && fresh.startedPublishingAt !== website.startedPublishingAt; 15475 } catch { 15476 reachable = false; 15477 } 15478 if (claimed) { 15479 if (freshStatus !== "In Progress") { 15480 startPolling(idlePollInterval); 15481 setIsPublishing(false); 15482 } 15483 return; 15484 } 15485 startPolling(idlePollInterval); 15486 setIsPublishing(false); 15487 _t.error(reachable ? "Publishing failed to start. Please try again." : "Could not reach the server to start publishing. Check your connection and try again; if it did start, the status will update shortly."); 15488 } 15489 }, [website, refetch, idlePollInterval, startPolling, publishReactWebsite]); 15490 const handlePublishClicked = reactExports.useCallback(() => { 15491 if (isAgentResponding) { 15492 setShowPublishWhileBusy(true); 15493 return; 15494 } 15495 handlePublish(); 15496 }, [isAgentResponding, handlePublish]); 15497 if (authLoading || loading && !website) return /* @__PURE__ */ jsxRuntimeExports.jsx(LoadingSpinner, {}); 15498 if (error || !website) return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex h-screen flex-col bg-gray-50", children: [ 15499 /* @__PURE__ */ jsxRuntimeExports.jsx("header", { className: "px-4 py-1.5", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ReactNavbar, {}) }), 15500 /* @__PURE__ */ jsxRuntimeExports.jsx(ErrorContent, { error }) 15501 ] }); 15502 if (website.websiteType === "html" || website.websiteType === null) { 15503 return /* @__PURE__ */ jsxRuntimeExports.jsx(LoadingSpinner, {}); 15504 } 15505 if (isBeingPrepared) { 15506 return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
15507 /* @__PURE__ */ jsxRuntimeExports.jsx(Metadata, { title: `Preparing ${website.name || "Website"}` }), 15508 /* @__PURE__ */ jsxRuntimeExports.jsx(ReactWebsiteLoadingScreen, { businessName: website.businessName || website.name, previewUrl, containerStatus, footer: ( 15509 // Only ask "where did you hear about us?" while the user's first 15510 // website generates. Fail open: show unless we definitively know 15511 // this isn't their first (isFirstWebsite === false). 15512 website.isFirstWebsite === false ? void 0 : /* @__PURE__ */ jsxRuntimeExports.jsx(HdyhauSurveyCard, {}) 15513 ) }) 15514 ] }); 15515 } 15516 const navbarEl = /* @__PURE__ */ jsxRuntimeExports.jsx(ReactNavbar, { website, onPublishClicked: handlePublishClicked, isPublishing, publishingProgress: website.publishingProgress, publishingStep: website.publishingStep, publishingStatus: website.publishingStatus }); 15517 const chatEl = /* @__PURE__ */ jsxRuntimeExports.jsx(ChatErrorBoundary, { websiteId: id, resetUrl: previewUrl ? `${previewUrl}/__agent/reset` : void 0, children: /* @__PURE__ */ jsxRuntimeExports.jsx(ReactChatPanel$1, { ref: chatPanelRef, websiteId: id, isContainerReady, website: chatWebsite, expanded: isChatPanelExpanded, onToggleChatPanel: setIsChatPanelExpanded, onRefetchWebsite: handleRefetchWebsite, onRefreshPreview: handleRefreshPreview, myWebsiteAccess, onAIRespondingChange: handleAIRespondingChange, onCommitHashChange: handleCommitHashChange, localAgencyCreditsDelta, onAgencyCreditUsed: handleAgencyCreditUsed, localAiChatMessagesDelta, onAiChatMessageUsed: handleAiChatMessageUsed, onAgentBusyChange: handleAgentBusyChange, onPaymentsDataChanged: handlePaymentsDataChanged, onBusinessDataChanged: handleBusinessDataChanged, onShowView: handleShowView, inlineEditInProgress: editTextModeActive || isEditBusy, pickModeActive, selectedElement, currentPath, onTogglePickMode: handleTogglePickMode, onClearSelection: handleClearSelection, onRestoreSelection: setSelectedElement, isPickerAvailable, isSelectorReady, viteError }) }); 15518 const previewEl = /* @__PURE__ */ jsxRuntimeExports.jsx(ReactPreviewPanel$1, { previewUrl, containerStatus: (_c = website.reactWebsiteConfig) == null ? void 0 : _c.containerStatus, containerError: (_d = website.reactWebsiteConfig) == null ? void 0 : _d.containerErrorMessage, pages: toolbarPages, websiteId: website.id, hasCustomDomain: Boolean(website.Domain), domainName: ((_e = website.Domain) == null ? void 0 : _e.domainName) ?? void 0, pickModeActive, onPicked: handlePicked, onPickModeExit: handlePickModeExit, onIframeReload: handleIframeReload, onSelectorReadyChange: handleSelectorReadyChange, onPickerAvailabilityChange: handlePickerAvailabilityChange, onCurrentPathChange: handleCurrentPathChange, onViteError: handleViteError, onViteErrorCleared: handleViteErrorCleared, editTextModeActive, onToggleEditTextMode: handleToggleEditTextMode, onEditTextModeExit: handleEditTextModeExit, onApplyWithAI: handleApplyEditsWithAI, isAgentResponding, onEditBusyChange: handleEditBusyChange, refreshSignal: previewRefreshSignal }, previewKey); 15519 return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 15520 /* @__PURE__ */ jsxRuntimeExports.jsx(Metadata, { title: website.name }), 15521 isClaiming && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-white", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center gap-3", children: [
15522 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-8 w-8 animate-spin rounded-full border-2 border-gray-300 border-t-black" }), 15523 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-700", children: "Setting up your website..." }) 15524 ] }) }), 15525 /* @__PURE__ */ jsxRuntimeExports.jsx(EditorLayout, { navbar: navbarEl, chat: chatEl, preview: previewEl, merchantView: merchantViewEl, viewSwitcher: viewSwitcherEl, banner: ( 15526 // Owner-only: the past-due banner carries the owner's Stripe pay 15527 // link, and the under-review notice is for the owner to act on, so 15528 // neither shows to shared-access collaborators. Under-review first: 15529 // a hidden site is the more urgent of the two. 15530 (myWebsiteAccess == null ? void 0 : myWebsiteAccess.role) === "OWNER" ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 15531 /* @__PURE__ */ jsxRuntimeExports.jsx(ImpersonationReviewBanner, { underReview: website.underReview }), 15532 !website.underReview && /* @__PURE__ */ jsxRuntimeExports.jsx(SiteCaseNoticeBanner, { notice: website.siteCaseNotice }),
15533 /* @__PURE__ */ jsxRuntimeExports.jsx(PaymentPastDueBanner, { paymentPastDue: website.paymentPastDue, pastDueInvoiceUrl: website.pastDueInvoiceUrl, fallbackHref: namedRoutes.reactWebsiteChatSettingsTab({ 15534 id: website.id, 15535 tab: "plan" 15536 }) }) 15537 ] }) : void 0 15538 ), websiteName: website.name ?? "", chatExpanded: isChatPanelExpanded }), 15539 /* @__PURE__ */ jsxRuntimeExports.jsx(FirstPublishCelebration, { websiteId: id, publishCompletedSignal }), 15540 /* @__PURE__ */ jsxRuntimeExports.jsx(PublishWhileAgentBusyModal, { open: showPublishWhileBusy, onPublish: () => { 15541 setShowPublishWhileBusy(false); 15542 handlePublish(); 15543 }, onWait: () => setShowPublishWhileBusy(false) }), 15544 /* @__PURE__ */ jsxRuntimeExports.jsx(PublishBlockedModal, { blockers: publishBlockers, websiteId: website.id, onClose: () => setPublishBlockers([]) }), 15545 showUpgradeModal && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mx-4 max-w-md rounded-2xl bg-white p-8 shadow-2xl", children: [
15546 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-6 text-center", children: [ 15547 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-gradient-to-br from-amber-400 to-orange-500 shadow-lg", children: /* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: "fa-solid fa-rocket text-2xl text-white" }) }), 15548 /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "mb-2 text-2xl font-bold text-gray-900", children: "Upgrade to Pro" }), 15549 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "leading-relaxed text-gray-600", children: upgradeError || "Your website has multiple pages. Upgrade to Pro to publish multi-page websites." }) 15550 ] }), 15551 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-3", children: [ 15552 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => { 15553 setShowUpgradeModal(false); 15554 navigate(namedRoutes.reactWebsiteChatSettingsTab({ 15555 id: website.id, 15556 tab: "plan" 15557 })); 15558 }, className: "w-full rounded-lg bg-gradient-to-r from-blue-600 to-indigo-600 px-6 py-3 font-semibold text-white shadow-md transition-all hover:from-blue-700 hover:to-indigo-700 hover:shadow-lg", children: "View Upgrade Options" }),
15559 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => setShowUpgradeModal(false), className: "w-full rounded-lg border border-gray-300 px-6 py-3 font-medium text-gray-700 transition-colors hover:bg-gray-50", children: "Maybe Later" }) 15560 ] }) 15561 ] }) }) 15562 ] }); 15563}; 15564const ReactWebsiteChatPage = (props) => /* @__PURE__ */ jsxRuntimeExports.jsx(ReactWebsiteChatEditor, { ...props }, props.id); 15565export { 15566 ReactWebsiteChatPage as default 15567}; 15568//# sourceMappingURL=ReactWebsiteChatPage-DmjgzGss.js.map 15569 15570//# debugId=ea5d659a-f734-5a99-962b-fd02b29dfbea
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.