1const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/D2a8Cx-C.js","assets/BkXa9QNX.js","assets/BmDlrdls.js","assets/GKSP12iv.js","assets/B-mNdDrH.js","assets/CMChhlKQ.js","assets/DH44VkHe.js","assets/CDsG-L8b.js","assets/CC1DGAqk.js","assets/BbS5JWlq.js","assets/CduXoNkY.js","assets/BanWWZt2.js","assets/CrLHLS9U.js","assets/2INXKiLw.js","assets/CsaeKhul.js","assets/DqQDN-_H.css","assets/pLQ8JASp.js","assets/uPi-84l2.js","assets/FndHqydU.js","assets/CPxQk51H.js","assets/BGGUBa6A.js","assets/ji1faxdD.js"])))=>i.map(i=>d[i]); 2import { r as reactExports, V as Viewport, R as Root2, A as Action, C as Close, T as Title, D as Description, j as jsxRuntimeExports, X, P as Provider, z, a as Toaster$2, b as Content2, d as Portal, e as Root3, f as Trigger, h as Provider$1, B as BrowserRouter, i as Routes, k as Route, N as Navigate, S as Slot, O as Overlay, l as Content, m as Title$1, n as Description$1, o as Portal$1, p as Close$1, q as Root, s as reatomComponent, Z as ZoomIn, I as Image, t as CornerDownLeft, u as useNavigate, M as Minimize2, v as Maximize2, w as Scissors, x as Sparkles, y as Brain, E as Zap, F as clientExports, G as reatomContext } from "./BkXa9QNX.js"; 3import { t as twMerge, c as clsx, a as cva } from "./CMChhlKQ.js"; 4import "./CsaeKhul.js"; 5import { a as atom, f as withAssign, e as action, t as toAbortError, r as reatomAsync, n as noop, g as reatomBoolean, h as reatomNumber, j as withLocalStorage, k as batch, l as createCtx } from "./BmDlrdls.js"; 6import { aj as uuidv7obj, ak as uuidv4, al as imageCompression, am as pDefer, an as UAParser } from "./GKSP12iv.js"; 7(function polyfill() { 8 const relList = document.createElement("link").relList; 9 if (relList && relList.supports && relList.supports("modulepreload")) { 10 return; 11 } 12 for (const link of document.querySelectorAll('link[rel="modulepreload"]')) { 13 processPreload(link); 14 } 15 new MutationObserver((mutations) => { 16 for (const mutation of mutations) { 17 if (mutation.type !== "childList") { 18 continue; 19 } 20 for (const node of mutation.addedNodes) { 21 if (node.tagName === "LINK" && node.rel === "modulepreload") 22 processPreload(node); 23 } 24 } 25 }).observe(document, { childList: true, subtree: true }); 26 function getFetchOpts(link) { 27 const fetchOpts = {}; 28 if (link.integrity) fetchOpts.integrity = link.integrity; 29 if (link.referrerPolicy) fetchOpts.referrerPolicy = link.referrerPolicy; 30 if (link.crossOrigin === "use-credentials") 31 fetchOpts.credentials = "include"; 32 else if (link.crossOrigin === "anonymous") fetchOpts.credentials = "omit"; 33 else fetchOpts.credentials = "same-origin"; 34 return fetchOpts; 35 } 36 function processPreload(link) { 37 if (link.ep) 38 return; 39 link.ep = true; 40 const fetchOpts = getFetchOpts(link); 41 fetch(link.href, fetchOpts); 42 } 43})(); 44const scriptRel = "modulepreload"; 45const assetsURL = function(dep) { 46 return "/" + dep; 47}; 48const seen = {}; 49const __vitePreload = function preload(baseModule, deps, importerUrl) { 50 let promise = Promise.resolve(); 51 if (deps && deps.length > 0) { 52 let allSettled2 = function(promises) { 53 return Promise.all( 54 promises.map( 55 (p) => Promise.resolve(p).then( 56 (value) => ({ status: "fulfilled", value }), 57 (reason) => ({ status: "rejected", reason }) 58 ) 59 ) 60 ); 61 }; 62 document.getElementsByTagName("link"); 63 const cspNonceMeta = document.querySelector( 64 "meta[property=csp-nonce]" 65 ); 66 const cspNonce = cspNonceMeta?.nonce || cspNonceMeta?.getAttribute("nonce"); 67 promise = allSettled2( 68 deps.map((dep) => { 69 dep = assetsURL(dep); 70 if (dep in seen) return; 71 seen[dep] = true; 72 const isCss = dep.endsWith(".css"); 73 const cssSelector = isCss ? '[rel="stylesheet"]' : ""; 74 if (document.querySelector(`link[href="${dep}"]${cssSelector}`)) { 75 return; 76 } 77 const link = document.createElement("link"); 78 link.rel = isCss ? "stylesheet" : scriptRel; 79 if (!isCss) { 80 link.as = "script"; 81 } 82 link.crossOrigin = ""; 83 link.href = dep; 84 if (cspNonce) { 85 link.setAttribute("nonce", cspNonce); 86 } 87 document.head.appendChild(link); 88 if (isCss) { 89 return new Promise((res, rej) => { 90 link.addEventListener("load", res); 91 link.addEventListener( 92 "error", 93 () => rej(new Error(`Unable to preload CSS for ${dep}`)) 94 ); 95 }); 96 } 97 }) 98 ); 99 } 100 function handlePreloadError(err) { 101 const e = new Event("vite:preloadError", { 102 cancelable: true 103 }); 104 e.payload = err; 105 window.dispatchEvent(e); 106 if (!e.defaultPrevented) { 107 throw err; 108 } 109 } 110 return promise.then((res) => { 111 for (const item of res || []) { 112 if (item.status !== "rejected") continue; 113 handlePreloadError(item.reason); 114 } 115 return baseModule().catch(handlePreloadError); 116 }); 117}; 118const TOAST_LIMIT = 1; 119const TOAST_REMOVE_DELAY = 1e6; 120let count = 0; 121function genId() { 122 count = (count + 1) % Number.MAX_SAFE_INTEGER; 123 return count.toString(); 124} 125const toastTimeouts = /* @__PURE__ */ new Map(); 126const addToRemoveQueue = (toastId) => { 127 if (toastTimeouts.has(toastId)) { 128 return; 129 } 130 const timeout = setTimeout(() => { 131 toastTimeouts.delete(toastId); 132 dispatch({ 133 type: "REMOVE_TOAST", 134 toastId 135 }); 136 }, TOAST_REMOVE_DELAY); 137 toastTimeouts.set(toastId, timeout); 138}; 139const reducer = (state, action2) => { 140 switch (action2.type) { 141 case "ADD_TOAST": 142 return { 143 ...state, 144 toasts: [ 145 action2.toast, 146 ...state.toasts 147 ].slice(0, TOAST_LIMIT) 148 }; 149 case "UPDATE_TOAST": 150 return { 151 ...state, 152 toasts: state.toasts.map((t) => t.id === action2.toast.id ? { 153 ...t, 154 ...action2.toast 155 } : t) 156 }; 157 case "DISMISS_TOAST": { 158 const { toastId } = action2; 159 if (toastId) { 160 addToRemoveQueue(toastId); 161 } else {
162 state.toasts.forEach((toast2) => { 163 addToRemoveQueue(toast2.id); 164 }); 165 } 166 return { 167 ...state, 168 toasts: state.toasts.map((t) => t.id === toastId || toastId === void 0 ? { 169 ...t, 170 open: false 171 } : t) 172 }; 173 } 174 case "REMOVE_TOAST": 175 if (action2.toastId === void 0) { 176 return { 177 ...state, 178 toasts: [] 179 }; 180 } 181 return { 182 ...state, 183 toasts: state.toasts.filter((t) => t.id !== action2.toastId) 184 }; 185 } 186}; 187const listeners = []; 188let memoryState = { 189 toasts: [] 190}; 191function dispatch(action2) { 192 memoryState = reducer(memoryState, action2); 193 listeners.forEach((listener) => { 194 listener(memoryState); 195 }); 196} 197function toast({ ...props }) { 198 const id = genId(); 199 const update = (props2) => dispatch({ 200 type: "UPDATE_TOAST", 201 toast: { 202 ...props2, 203 id 204 } 205 }); 206 const dismiss = () => dispatch({ 207 type: "DISMISS_TOAST", 208 toastId: id 209 }); 210 dispatch({ 211 type: "ADD_TOAST", 212 toast: { 213 ...props, 214 id, 215 open: true, 216 onOpenChange: (open) => { 217 if (!open) dismiss(); 218 } 219 } 220 }); 221 return { 222 id, 223 dismiss, 224 update 225 }; 226} 227function useToast() { 228 const [state, setState] = reactExports.useState(memoryState); 229 reactExports.useEffect(() => { 230 listeners.push(setState); 231 return () => { 232 const index = listeners.indexOf(setState); 233 if (index > -1) { 234 listeners.splice(index, 1); 235 } 236 }; 237 }, [ 238 state 239 ]); 240 return { 241 ...state, 242 toast, 243 dismiss: (toastId) => dispatch({ 244 type: "DISMISS_TOAST", 245 toastId 246 }) 247 }; 248} 249function cn(...inputs) { 250 return twMerge(clsx(inputs)); 251} 252const ToastProvider = Provider; 253const ToastViewport = /* @__PURE__ */ reactExports.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsx(Viewport, { 254 ref, 255 className: cn("fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]", className), 256 ...props 257})); 258ToastViewport.displayName = Viewport.displayName; 259const toastVariants = cva("group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full", { 260 variants: { 261 variant: { 262 default: "border bg-background text-foreground", 263 destructive: "destructive group border-destructive bg-destructive text-destructive-foreground" 264 } 265 }, 266 defaultVariants: { 267 variant: "default" 268 } 269}); 270const Toast = /* @__PURE__ */ reactExports.forwardRef(({ className, variant, ...props }, ref) => { 271 return /* @__PURE__ */ jsxRuntimeExports.jsx(Root2, { 272 ref, 273 className: cn(toastVariants({ 274 variant 275 }), className), 276 ...props 277 }); 278}); 279Toast.displayName = Root2.displayName; 280const ToastAction = /* @__PURE__ */ reactExports.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsx(Action, { 281 ref, 282 className: cn("ring-offset-background hover:bg-secondary focus:ring-ring group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", className), 283 ...props 284})); 285ToastAction.displayName = Action.displayName; 286const ToastClose = /* @__PURE__ */ reactExports.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsx(Close, { 287 ref,
288 className: cn("text-foreground/50 hover:text-foreground absolute right-2 top-2 rounded-md p-1 opacity-0 transition-opacity focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600", className), 289 "toast-close": "", 290 ...props, 291 children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { 292 className: "h-4 w-4" 293 }) 294})); 295ToastClose.displayName = Close.displayName; 296const ToastTitle = /* @__PURE__ */ reactExports.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsx(Title, { 297 ref, 298 className: cn("text-sm font-semibold", className), 299 ...props 300})); 301ToastTitle.displayName = Title.displayName; 302const ToastDescription = /* @__PURE__ */ reactExports.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsx(Description, { 303 ref, 304 className: cn("text-sm opacity-90", className), 305 ...props 306})); 307ToastDescription.displayName = Description.displayName; 308function Toaster$1() { 309 const { toasts } = useToast(); 310 return /* @__PURE__ */ jsxRuntimeExports.jsxs(ToastProvider, { 311 children: [ 312 toasts.map(function({ id, title, description, action: action2, ...props }) { 313 return /* @__PURE__ */ jsxRuntimeExports.jsxs(Toast, { 314 ...props, 315 children: [
316 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { 317 className: "grid gap-1", 318 children: [ 319 title && /* @__PURE__ */ jsxRuntimeExports.jsx(ToastTitle, { 320 children: title 321 }), 322 description && /* @__PURE__ */ jsxRuntimeExports.jsx(ToastDescription, { 323 children: description 324 }) 325 ] 326 }), 327 action2, 328 /* @__PURE__ */ jsxRuntimeExports.jsx(ToastClose, {}) 329 ] 330 }, id); 331 }), 332 /* @__PURE__ */ jsxRuntimeExports.jsx(ToastViewport, {}) 333 ] 334 }); 335} 336const Toaster = ({ ...props }) => { 337 const { theme = "system" } = z(); 338 return /* @__PURE__ */ jsxRuntimeExports.jsx(Toaster$2, { 339 theme, 340 className: "toaster group", 341 toastOptions: { 342 classNames: { 343 toast: "group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg", 344 description: "group-[.toast]:text-muted-foreground", 345 actionButton: "group-[.toast]:bg-primary group-[.toast]:text-primary-foreground", 346 cancelButton: "group-[.toast]:bg-muted group-[.toast]:text-muted-foreground" 347 } 348 }, 349 ...props 350 }); 351}; 352const TooltipProvider = Provider$1; 353const Tooltip = Root3; 354const TooltipContent = /* @__PURE__ */ reactExports.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsx(Portal, { 355 children: /* @__PURE__ */ jsxRuntimeExports.jsx(Content2, { 356 ref, 357 sideOffset, 358 className: cn("z-50 overflow-hidden rounded-md border bg-white/60 px-3 py-1.5 text-sm backdrop-blur-lg", "text-popover-foreground animate-in fade-in-0 zoom-in-95 shadow-md", "data-[state=closed]:animate-out data-[state=closed]:fade-out-0", "data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2", "data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2", "data-[side=top]:slide-in-from-bottom-2", className), 359 ...props 360 }) 361})); 362TooltipContent.displayName = Content2.displayName; 363function SimpleTooltip({ children, content, open, defaultOpen, onOpenChange, sideOffset = 4, ...props }) { 364 return /* @__PURE__ */ jsxRuntimeExports.jsxs(Root3, { 365 open, 366 defaultOpen, 367 onOpenChange, 368 children: [
369 /* @__PURE__ */ jsxRuntimeExports.jsx(Trigger, { 370 asChild: true, 371 children 372 }), 373 /* @__PURE__ */ jsxRuntimeExports.jsx(Portal, { 374 className: "max-w-56", 375 children: /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { 376 sideOffset, 377 ...props, 378 children: typeof content === "string" ? /* @__PURE__ */ jsxRuntimeExports.jsx("p", { 379 className: "max-w-56", 380 children: content 381 }) : content 382 }) 383 }) 384 ] 385 }); 386} 387const AppIndex = /* @__PURE__ */ reactExports.lazy(() => __vitePreload(() => import("./D2a8Cx-C.js"), true ? __vite__mapDeps([0,1,2,3,4,5,6,7,8,9]) : void 0)); 388const Chats = /* @__PURE__ */ reactExports.lazy(() => __vitePreload(() => import("./CduXoNkY.js"), true ? __vite__mapDeps([10,1,2,3,4,6,5,7,11,8,12,13,14,15]) : void 0)); 389const CurrentChat = /* @__PURE__ */ reactExports.lazy(() => __vitePreload(() => import("./pLQ8JASp.js"), true ? __vite__mapDeps([16,1,2,3,4,6,5,7,8,13,14,15,11,12]) : void 0)); 390const Landing = /* @__PURE__ */ reactExports.lazy(() => __vitePreload(() => import("./uPi-84l2.js"), true ? __vite__mapDeps([17,1,2,3,4,9,7,11]) : void 0)); 391const Welcome = /* @__PURE__ */ reactExports.lazy(() => __vitePreload(() => import("./FndHqydU.js"), true ? __vite__mapDeps([18,1,2,3,4]) : void 0)); 392const PrivacyPolicy = /* @__PURE__ */ reactExports.lazy(() => __vitePreload(() => import("./CPxQk51H.js"), true ? __vite__mapDeps([19,1,2,3,4]) : void 0)); 393const TermsOfService = /* @__PURE__ */ reactExports.lazy(() => __vitePreload(() => import("./BGGUBa6A.js"), true ? __vite__mapDeps([20,1,2,3,4]) : void 0)); 394const Uninstall = /* @__PURE__ */ reactExports.lazy(() => __vitePreload(() => import("./ji1faxdD.js"), true ? __vite__mapDeps([21,1,2,3,4,6,5,7,12]) : void 0)); 395const PageLoader = () => /* @__PURE__ */ jsxRuntimeExports.jsx("div", { 396 className: "flex h-screen w-full items-center justify-center", 397 children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { 398 className: "h-32 w-32 animate-spin rounded-full border-b-2 border-gray-900" 399 }) 400}); 401if (typeof document !== "undefined") { 402 document.head.querySelectorAll("title").forEach((el) => el.remove()); 403} 404const App = () => { 405 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { 406 className: "min-w-[300px]", 407 children: /* @__PURE__ */ jsxRuntimeExports.jsxs(TooltipProvider, { 408 children: [
409 /* @__PURE__ */ jsxRuntimeExports.jsx(Toaster$1, {}), 410 /* @__PURE__ */ jsxRuntimeExports.jsx(Toaster, {}), 411 /* @__PURE__ */ jsxRuntimeExports.jsx(BrowserRouter, { 412 children: /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { 413 fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(PageLoader, {}), 414 children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Routes, { 415 children: [ 416 /* @__PURE__ */ jsxRuntimeExports.jsx(Route, { 417 index: true, 418 element: /* @__PURE__ */ jsxRuntimeExports.jsx(Landing, {}) 419 }), 420 /* @__PURE__ */ jsxRuntimeExports.jsx(Route, { 421 path: "app", 422 element: /* @__PURE__ */ jsxRuntimeExports.jsx(AppIndex, {}) 423 }), 424 /* @__PURE__ */ jsxRuntimeExports.jsx(Route, { 425 path: "history", 426 element: /* @__PURE__ */ jsxRuntimeExports.jsx(Chats, {}) 427 }), 428 /* @__P
428URE__ */ jsxRuntimeExports.jsx(Route, { 429 path: "c/:chatId?/:slug?", 430 element: /* @__PURE__ */ jsxRuntimeExports.jsx(CurrentChat, {}) 431 }), 432 /* @__PURE__ */ jsxRuntimeExports.jsx(Route, { 433 path: "welcome", 434 element: /* @__PURE__ */ jsxRuntimeExports.jsx(Welcome, {}) 435 }), 436 /* @__PURE__ */ jsxRuntimeExports.jsx(Route, { 437 path: "privacy-policy", 438 element: /* @__PURE__ */ jsxRuntimeExports.jsx(PrivacyPolicy, {}) 439 }), 440 /* @__PURE__ */ jsxRuntimeExports.jsx(Route, { 441 path: "terms-of-service", 442 element: /* @__PURE__ */ jsxRuntimeExports.jsx(TermsOfService, {}) 443 }), 444 /* @__PURE__ */ jsxRuntimeExports.jsx(Route, { 445 path: "uninstall", 446 element: /* @__PURE__ */ jsxRuntimeExports.jsx(Uninstall, {}) 447 }), 448 /* @__PURE__ */ jsxRuntimeExports.jsx(Route, { 449 path: "*", 450 element: /* @__PURE__ */ jsxRuntimeExports.jsx(Navigate, { 451 to: "/", 452 replace: true 453 }) 454 }) 455 ] 456 }) 457 }) 458 }) 459 ] 460 }) 461 }); 462}; 463const isNodeEnvironment = () => typeof process !== "undefined" && process.versions?.node != null; 464const encodeBase64Ascii = (bytes) => { 465 try { 466 if (isNodeEnvironment()) { 467 return Buffer.from(bytes).toString("base64"); 468 } 469 } catch (e) { 470 console.error("Failed to encode Base64 using Buffer", e); 471 } 472 return btoa(String.fromCharCode(...bytes)); 473}; 474const encodeBase64 = (bytes) => { 475 let string = encodeBase64Ascii(bytes); 476 string = string.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); 477 return string; 478}; 479const isValidUuidv7b64 = (id) => !!id && /^[a-zA-Z0-9_-]{20,22}$/.test(id); 480const uuidv7b64 = () => encodeBase64(uuidv7obj().bytes); 481const randomId = () => uuidv4(); 482const u$1 = void 0; 483const BG_COMM_ID = "bg"; 484const COMM_ANSWER_TIMEOUT = 96; 485const WEB_APP_ID_PREFIX = "wa-", CS_COMM_ID_PREFIX = "cs-"; 486const isCsCommId = (id) => !!id && id.startsWith(CS_COMM_ID_PREFIX); 487var CommMsgType; 488(function(CommMsgType2) { 489 CommMsgType2[CommMsgType2["ping"] = 1] = "ping"; 490 CommMsgType2[CommMsgType2["ga"] = 2] = "ga"; 491 CommMsgType2[CommMsgType2["extReady"] = 3] = "extReady"; 492 CommMsgType2[CommMsgType2["ok"] = 4] = "ok"; 493 CommMsgType2[CommMsgType2["startSolvingCmd"] = 10] = "startSolvingCmd"; 494 CommMsgType2[CommMsgType2["startImageCaptureCmd"] = 11] = "startImageCaptureCmd"; 495 CommMsgType2[CommMsgType2["getTabState"] = 12] = "getTabState"; 496 CommMsgType2[CommMsgType2["isPopupOpen"] = 30] = "isPopupOpen"; 497 CommMsgType2[CommMsgType2["onPopup"] = 31] = "onPopup"; 498 CommMsgType2[CommMsgType2["openSidePanelOrPopup"] = 32] = "openSidePanelOrPopup"; 499 CommMsgType2[CommMsgType2["onTabUnload"] = 90] = "onTabUnload"; 500})(CommMsgType || (CommMsgType = {})); 501var MsgFrom; 502(function(MsgFrom2) { 503 MsgFrom2[MsgFrom2["ContextMenu"] = 1] = "ContextMenu"; 504 MsgFrom2[MsgFrom2["Popup"] = 2] = "Popup"; 505 MsgFrom2[MsgFrom2["Options"] = 3] = "Options"; 506 MsgFrom2[MsgFrom2["SidePanel"] = 4] = "SidePanel"; 507 MsgFrom2[MsgFrom2["Cs"] = 5] = "Cs"; 508 MsgFrom2[MsgFrom2["WebApp"] = 6] = "WebApp"; 509 MsgFrom2[MsgFrom2["Command"] = 7] = "Command";
510 MsgFrom2[MsgFrom2["Bg"] = 9] = "Bg"; 511})(MsgFrom || (MsgFrom = {})); 512var ExtensionState; 513(function(ExtensionState2) { 514 ExtensionState2[ExtensionState2["unknown"] = 1] = "unknown"; 515 ExtensionState2[ExtensionState2["unsupported"] = 99] = "unsupported"; 516})(ExtensionState || (ExtensionState = {})); 517const fromLabel = (from) => from ? MsgFrom[from] || `${from}` : u$1; 518const msgTypeLabel = (t) => t ? CommMsgType[t] || t : u$1; 519const msgDebug = (msg) => { 520 return isCommMsg(msg) ? removeUndefinedOrNullValues({ 521 ...msg, 522 from: msg ? fromLabel(msg.from) : u$1, 523 t: msg ? msgTypeLabel(msg.t) : u$1 524 }) : msg; 525}; 526const removeUndefinedOrNullValues = (obj) => Object.fromEntries(Object.entries(obj).filter(([_, value]) => value !== void 0 && value !== null)); 527const isCommMsg = (msg) => msg && msg.t && typeof msg.t === "number"; 528const PingMsg = { t: CommMsgType.ping }; 529const StartImageCaptureMsg = (from, tabId) => removeUndefinedOrNullValues({ 530 t: CommMsgType.startImageCaptureCmd, 531 tabId, 532 from, 533 for: CS_COMM_ID_PREFIX 534}); 535const isExtensionOrigin = (origin) => { 536 return /^chrome-extension:\/\/[a-zA-Z0-9-]+$/.test(origin); 537}; 538const screenCaptureAtom = atom(false, "screenCaptureAtom").pipe(withAssign((isCapturing, name) => { 539 const start = action((ctx, source = "button") => { 540 isCapturing(ctx, true); 541 return ctx.schedule(async () => await ExtBridge.getInstance(ctx).sendMessage(StartImageCaptureMsg(MsgFrom.WebApp))); 542 }, name + ".start"); 543 const stop = action((ctx) => { 544 isCapturing(ctx, false); 545 }, name + ".stop"); 546 return { 547 start, 548 stop 549 }; 550})); 551const EXTENSION_ID_PARAM = "eid"; 552const USER_ID_PARAM = "uid"; 553const ABORTED = "aborted"; 554const createAbortError = (reason = ABORTED) => toAbortError(reason); 555const compressImageAsync = async (image, signal) => { 556 return imageCompression(image, { 557 maxSizeMB: 1, 558 maxWidthOrHeight: 2048, 559 useWebWorker: true, 560 preserveExif: false, 561 libURL: void 0, 562 signal 563 }); 564}; 565function getFilefromDataUrl(dataUrl, filename, lastModified) { 566 return imageCompression.getFilefromDataUrl(dataUrl, filename, lastModified); 567} 568const USER_ID_KEY = "muid"; 569const TEST_KEY = "test"; 570const COOKIE_EXPIRY_DAYS = 365; 571const w$2 = window; 572const LS = (() => { 573 let ls; 574 try { 575 ls = localStorage; 576 ls.setItem(TEST_KEY, TEST_KEY); 577 ls.removeItem(TEST_KEY); 578 } catch (error) { 579 console.warn("localStorage not available:", error); 580 } 581 return ls; 582})(); 583const setCookie = (name, value, days) => { 584 try { 585 const expires = /* @__PURE__ */ new Date(); 586 expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1e3); 587 const hostname = location.hostname; 588 const isLocalhost = hostname === "localhost"; 589 const isIp = /^(\d{1,3}\.){3}\d{1,3}$/.test(hostname) || /^[a-fA-F0-9:]+$/.test(hostname); 590 const domain = isLocalhost || isIp ? hostname : "." + hostname.split(".").slice(-2).join("."); 591 document.cookie = `${name}=${value};expires=${expires.toUTCString()};path=/;domain=${domain}`; 592 } catch (error) { 593 console.error("Failed to set cookie:", error); 594 } 595}; 596const getCookie = (name) => { 597 try { 598 const match = document.cookie.match(new RegExp(`(^| )${name}=([^;]+)`)); 599 return match ? match[2] : null; 600 } catch (error) { 601 console.error("Failed to get cookie:", error); 602 return null; 603 } 604}; 605let UID = null; 606const storeUserId = (uid, ls) => { 607 try { 608 UID = uid; 609 GA.setUserId(uid);
610 setCookie(USER_ID_KEY, uid, COOKIE_EXPIRY_DAYS); 611 ls?.setItem(USER_ID_KEY, uid); 612 } catch (error) { 613 console.error(`Failed to store ${USER_ID_KEY}:`, error); 614 } finally { 615 } 616}; 617const getUserId = (ctx = reatomCtx, readonly) => { 618 if (UID) { 619 return UID; 620 } 621 const location1 = w$2.location; 622 const urlParams = new URLSearchParams(location1.search); 623 const uidFromUrl = urlParams.get("uid")?.trim(); 624 if (uidFromUrl) { 625 urlParams.delete(USER_ID_PARAM); 626 const urlParamsString = urlParams.toString(); 627 const newUrl = `${location1.pathname}${urlParamsString ? `?${urlParamsString}` : ""}${location1.hash}`; 628 w$2.history.replaceState({}, "", newUrl); 629 if (isValidUuidv7b64(uidFromUrl)) { 630 if (!readonly) storeUserId(uidFromUrl, LS); 631 return uidFromUrl; 632 } 633 } 634 const uidFromExt = ctx.get(extensionCtxAtom.userId); 635 if (uidFromExt && isValidUuidv7b64(uidFromExt)) { 636 if (!readonly) storeUserId(uidFromExt, LS); 637 return uidFromExt; 638 } 639 const cookieId = getCookie(USER_ID_KEY)?.trim(); 640 if (cookieId && isValidUuidv7b64(cookieId)) { 641 if (!readonly) storeUserId(cookieId, LS); 642 return cookieId; 643 } 644 const storedId = LS?.getItem(USER_ID_KEY)?.trim(); 645 if (storedId && isValidUuidv7b64(storedId)) { 646 if (!readonly) storeUserId(storedId, LS); 647 return storedId; 648 } 649 const newId = readonly ? "" : uuidv7b64(); 650 if (!readonly) storeUserId(newId, LS); 651 return newId; 652}; 653const w$1 = window; 654let userIdSet = false; 655const GA = { 656 setUserId: (userId) => { 657 userIdSet = true; 658 w$1.gtag?.("set", { 659 user_id: userId 660 }); 661 }, 662 sendEvent: (event, params = {}) => { 663 if (!params.user_id) { 664 try { 665 const userId = getUserId(void 0, true); 666 params.user_id = userId; 667 if (!userIdSet) { 668 GA.setUserId(userId); 669 } 670 } catch (error) { 671 console.error("Failed to get UID:", error); 672 } 673 } 674 w$1.gtag?.("event", event, params); 675 } 676}; 677const MAX_IMAGE_SIZE = 5 * 1024 * 1024; 678const IMAGE_MIME = "image/"; 679const ALLOWED_IMAGE_TYPES = ["image/jpeg", "image/png", "image/gif"]; 680const formatFileSize = (bytes) => { 681 if (bytes === 0) 682 return "0 B"; 683 const k = 1024; 684 const sizes = ["B", "KB", "MB", "GB"]; 685 const i = Math.floor(Math.log(bytes) / Math.log(k)); 686 return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`; 687}; 688const MAX_SIZE_STRING = formatFileSize(MAX_IMAGE_SIZE); 689const fileToBase64 = async (file, signal) => new Promise((resolve, reject) => { 690 const reader = new FileReader(); 691 reader.onload = (ev) => { 692 const url = reader.result; 693 if (url) 694 resolve(url); 695 else 696 reject(ev || url); 697 }; 698 reader.onerror = reject; 699 reader.onabort = () => reject(createAbortError()); 700 signal?.addEventListener("abort", (reason) => { 701 reject(reason || createAbortError()); 702 reader.abort(); 703 }); 704 reader.readAsDataURL(file); 705}); 706const DEBUG = false; 707const ALLOWED_HINT = `JPEG, PNG, or GIF; ${MAX_SIZE_STRING} max`; 708const attachedImageAtom = atom(null, "attachedImage").pipe(withAssign((attachedImage, name) => { 709 const previewUrlAtom = atom(null, name + ".previewUrl"); 710 const details = atom(null, name + ".details"); 711 const reset = action((ctx) => { 712 details(ctx, null); 713 previewUrlAtom(ctx, null); 714 attachedImage(ctx, null); 715 }, name + ".reset"); 716 const compressImage = reatomAsync((ctx, image2) => { 717 return compressImageAsync(image2, ctx.controller.signal); 718 }); 719 compressImage.onReject.onCall((_, error) => { 720 if (error !== ABORTED || DEBUG) console.error("Failed to compress image", error); 721 }); 722 const getBase64FromImage = reatomAsync((ctx, image2) => { 723 return fileToBase64(image2, ctx.controller.signal); 724 }); 725 getBase64FromImage.onReject.onCall((_, error) => { 726 if (error !== ABORTED || DEBUG) console.error("Failed to process image", error); 727 }); 728 const abortAtom = atom(null, name + ".abort"); 729 const onAttachedImageChange = reatomAsync(async (ctx, image2) => { 730 const startTime = Date.now(); 731 ctx.get(abortAtom)?.abort(ABORTED); 732 const ac = ctx.controller; 733 const signal = ac.signal; 734 abortAtom(ctx, ac); 735 if (!image2) { 736 previewUrlAtom(ctx, null); 737 details(ctx, null); 738 return; 739 } 740 const format = image2.type.split("/")[1].toUpperCase(); 741 const fileSize = image2.size; 742 const sizeFormatted = formatFileSize(fileSize); 743 details(ctx, { 744 size: sizeFormatted, 745 format 746 }); 747 let hasCompressed = false;
748 const simple = getBase64FromImage(ctx, image2); 749 simple.then((previewUrl) => { 750 if (!hasCompressed && !signal.aborted) { 751 previewUrlAtom(ctx, previewUrl); 752 } 753 return previewUrl; 754 }); 755 const compressed = compressImage(ctx, image2).then((compressedImage) => getBase64FromImage(ctx, compressedImage)).then((previewUrl) => { 756 simple.controller.abort(ABORTED); 757 if (!signal.aborted) { 758 hasCompressed = true; 759 previewUrlAtom(ctx, previewUrl); 760 } 761 return previewUrl; 762 }); 763 simple.catch(() => compressed).then((previewUrl) => { 764 const img = document.createElement("img"); 765 img.onload = () => { 766 if (!signal.aborted) details(ctx, { 767 size: sizeFormatted, 768 dimensions: `${img.width}Ã${img.height}`, 769 format 770 }); 771 }; 772 img.src = previewUrl; 773 }); 774 await compressed.catch(() => simple); 775 GA.sendEvent("imageAttached", { 776 format, 777 fileSize, 778 processingMs: Date.now() - startTime, 779 dimensions: ctx.get(details)?.dimensions ?? 0 780 }); 781 }, name + ".onChange"); 782 onAttachedImageChange.onReject.onCall((ctx, error) => { 783 if (error !== ABORTED) { 784 console.error("Failed to process image", error); 785 reset(ctx); 786 inputErrorAtom(ctx, "Failed to process image"); 787 } 788 }); 789 attachedImage.onChange(onAttachedImageChange); 790 const image = atom((ctx) => ctx.spy(previewUrlAtom) || ctx.spy(attachedImage), name + ".image"); 791 return { 792 previewUrl: previewUrlAtom, 793 image, 794 details, 795 hasImage: atom((ctx) => !!ctx.spy(image), name + ".hasImage"), 796 reset 797 }; 798})); 799const inputErrorAtom = atom("", "inputError").pipe(withAssign((target, name) => ({ 800 show: action((ctx, description, title = "Error") => { 801 target(ctx, description); 802 ctx.schedule(() => { 803 toast({ 804 title, 805 description, 806 variant: "destructive" 807 }); 808 GA.sendEvent("inputError", { 809 title, 810 description 811 }); 812 }).catch(console.error); 813 }, name + ".show") 814}))); 815const validateAndProcessImage = action((ctx, file) => { 816 if (!ALLOWED_IMAGE_TYPES.includes(file.type)) { 817 inputErrorAtom.show(ctx, `Please select a valid image file (${ALLOWED_HINT})`, "Invalid file type"); 818 return false; 819 } 820 if (file.size > MAX_IMAGE_SIZE) { 821 inputErrorAtom.show(ctx, "Image size should be less than" + MAX_SIZE_STRING, "File too large"); 822 return false; 823 } 824 attachedImageAtom(ctx, file); 825 return true; 826}, "validateAndProcessImage"); 827const safeErrorCause = (error, cause) => { 828 const e = error instanceof Error ? error : error === ABORTED ? toAbortError(error) : new Error(error); 829 try { 830 if (error !== cause && cause) { 831 e.cause = cause; 832 } else if (e !== error) { 833 e.cause = error; 834 } 835 } catch (failure) { 836 console.error("Failed to set error cause", failure, e, cause); 837 } 838 return e; 839}; 840var __defProp$1 = Object.defineProperty; 841var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; 842var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value); 843const r = chrome.runtime; 844const KEEP_CONNECTION_INTERVAL = 7 * 1e3; 845const DEFAULT_RETRY_DELAY = 1e3; 846const MAX_RETRY_DELAY = 3e4; 847const MAX_RETRIES = 5; 848const u = void 0; 849class WebAppCommunicator { 850 constructor(targetExtensionId, onMessageCb = u, id = newWebAppCommId(), from = MsgFrom.WebApp, title = id) { 851 __publicField$1(this, "port"); 852 __publicField$1(this, "id"); 853 __publicField$1(this, "targetExtensionId"); 854 __publicField$1(this, "title"); 855 __publicField$1(this, "onMessageCb"); 856 __publicField$1(this, "from"); 857 __publicField$1(this, "myTabId"); 858 __publicField$1(this, "responseQueue", /* @__PURE__ */ new Map()); 859 __publicField$1(this, "lastDisconnectTime", 0); 860 __publicField$1(this, "fastDisconnects", 0); 861 __publicField$1(this, "onMessage", (msg, sender) => { 862 const api2 = this; 863 const { title: title2, id: id2 } = api2; 864 const myTabId = api2.myTabId; 865 const type = msg.t; 866 const for_ = msg.for; 867 const msgTabId = msg.tabId; 868 if (msg.isAnswer) { 869 onAnswerReceived(api2.responseQueue, msg, title2, sender); 870 return 0; 871 } 872 let answer = 0; 873 if (!type) { 874 if (msg && !Number.isInteger(msg)) { 875 { 876 let error = `${title2} Unexpected from ${senderLabel(sender, msg)}`; 877 try { 878 error += `: ${JSON.stringify(msgDebug(msg))}`; 879 } catch (_) { 880 error += `: ${msgDebug(msg)}`; 881 } 882 } 883 } 884 } else if (for_ && for_ !== id2 && !id2.startsWith(for_)) ; 885 else if (msgTabId && myTabId && msgTabId !== myTabId) ; 886 else if (!sender || false) { 887 switch (type) { 888 case CommMsgType.ping: 889 break; 890 default: 891 answer = api2.onMessageCb?.(msg, sender) || 0; 892 } 893 } 894 return prepareAnswer(msg, answer, title2, api2.from); 895 }); 896 __publicField$1(this, "portMsgListener", (msg, p) => { 897 const api2 = this, port = p || api2.port; 898 if (msg && port) { 899 const answer = api2.onMessage(msg, port.sender); 900 postMessageSafe(port, answer); 901 } 902 }); 903 __publicField$1(this, "openPort", (retry = 0) => { 904 const api2 = this; 905 const id2 = api2.id, port = r.connect(api2.targetExtensionId, { 906 name: id2 907 }), portError = r.lastError; 908 if (portError || !port) { 909 const r2 = retry + 1; 910 const msg = portError?.message || "unknown"; 911 const nonRecoverable = !portError || r2 >= MAX_RETRIES || msg.toLowerCase().includes("receiving end"); 912 if (!nonRecoverable) { 913 const timeoutMs = Math.min(Math.pow(r2, 2) * DEFAULT_RETRY_DELAY, MAX_RETRY_DELAY); 914 setTimeout(() => api2.openPort(r2), timeoutMs); 915 } 916 if (port) runCatching(() => port.disconnect()); 917 return; 918 } 919 api2.port = port;
920 port.onMessage.addListener(api2.portMsgListener); 921 let intervalId; 922 { 923 intervalId = setInterval(api2.sendPing, KEEP_CONNECTION_INTERVAL); 924 } 925 port.onDisconnect.addListener((p) => { 926 clearInterval(intervalId); 927 const _port = p || port; 928 if (api2.port === _port) api2.port = u; 929 const currentTime = Date.now(); 930 const timeFromLastDisconnect = currentTime - api2.lastDisconnectTime; 931 api2.lastDisconnectTime = currentTime; 932 { 933 if (timeFromLastDisconnect < 900) { 934 api2.fastDisconnects++; 935 const fastDisconnects = api2.fastDisconnects; 936 if (fastDisconnects > 3) { 937 return; 938 } 939 } else api2.fastDisconnects = 0; 940 api2.openPort(); 941 } 942 }); 943 return port; 944 }); 945 __publicField$1(this, "sendMsg", async (msg, callback, from2 = this.from, to = BG_COMM_ID) => { 946 const api2 = this, isUnloadMsg = msg.t === CommMsgType.onTabUnload, doAnswer = !isUnloadMsg && callback; 947 let cb = callback !== true ? callback : u; 948 let timeout; 949 try { 950 to ? msg.for = to : to = msg.for; 951 if (from2) msg.from = from2; 952 if (doAnswer && !msg.id) msg.id = randomId(); 953 const port = isUnloadMsg ? u : api2.port || api2.openPort(); 954 if (port && (!doAnswer || msg.id)) { 955 const answer = await postMessageSafeWithAnswer(port, msg, api2.responseQueue); 956 if (answer != false) { 957 cb?.(answer); 958 return answer; 959 } 960 } 961 const smDeferred = pDefer(); 962 const t = COMM_ANSWER_TIMEOUT; 963 timeout = isUnloadMsg ? u : setTimeout(() => smDeferred.reject(`timeout ${t}ms`), t); 964 sendMessageSafe(() => r.sendMessage(api2.targetExtensionId, msg), msg).catch(smDeferred.reject).then((response) => { 965 smDeferred.resolve(response); 966 cb?.(response); 967 cb = u; 968 }); 969 return await smDeferred.promise; 970 } catch (e) { 971 cb?.(u, e); 972 } finally { 973 clearTimeout(timeout); 974 } 975 }); 976 __publicField$1(this, "sendPing", () => { 977 const api2 = this; 978 if (api2.myTabId || !isCsCommId(api2.id)) { 979 api2.sendMsg(PingMsg).catch(noop); 980 } else { 981 api2.sendMsg({ 982 t: CommMsgType.ping, 983 id: randomId() 984 }, (msg, error) => { 985 if (msg && !error) { 986 const myTabId = msg.v || msg.d; 987 api2.myTabId = myTabId; 988 } 989 }).catch(noop); 990 } 991 }); 992 this.id = id; 993 this.targetExtensionId = targetExtensionId; 994 this.title = title; 995 this.onMessageCb = onMessageCb; 996 this.from = from; 997 const api = this; 998 const rMsgListener = (msg, sender, sendResponse) => { 999 if (msg) sendResponse(api.onMessage(msg, sender)); 1000 }; 1001 r.onMessage?.addListener(rMsgListener); 1002 api.openPort(); 1003 } 1004} 1005const newWebAppCommId = () => `${WEB_APP_ID_PREFIX}${uuidv7b64()}`; 1006const runCatching = (fn, silent) => { 1007 try { 1008 return fn(); 1009 } catch (e) { 1010 } 1011}; 1012const senderLabel = (sender, msg) => { 1013 const url = sender?.url || sender?.tab?.url || sender?.origin || sender?.nativeApplication || sender?.id; 1014 return url || msg && fromLabel(msg.from) || u; 1015}; 1016const onAnswerReceived = (responseQueue, msg, title, sender) => { 1017 const msgId = msg.id; 1018 const deferred = msgId ? responseQueue.get(msgId) : u; 1019 if (!deferred) { 1020 senderLabel(sender, msg); 1021 } else { 1022 deferred.resolve(msg); 1023 responseQueue.delete(msgId); 1024 } 1025}; 1026const prepareAnswer = (msg, answer, title, from) => { 1027 if (msg.id && !msg.isAnswer) { 1028 if (!isCommMsg(answer)) { 1029 answer = { 1030 t: CommMsgType.ping, 1031 v: answer 1032 }; 1033 } 1034 answer.id = msg.id; 1035 answer.isAnswer = true; 1036 from ? answer.from = from : delete answer.from; 1037 } 1038 return answer; 1039}; 1040const postMessageSafe = (port, msg) => { 1041 try { 1042 port.postMessage(msg); 1043 const lastError = r.lastError; 1044 if (!lastError) { 1045 return true; 1046 } 1047 } catch (e) { 1048 } 1049}; 1050const postMessageSafeWithAnswer = (port, msg, responseQueue, commTitle, timeoutMs = COMM_ANSWER_TIMEOUT) => new Promise(async (resolve) => { 1051 let timeout; 1052 const msgId = msg && msg.id; 1053 try { 1054 let portDeferred; 1055 if (msgId) { 1056 const d = pDefer(); 1057 portDeferred = d; 1058 d.promise.then(resolve); 1059 responseQueue.set(msgId, d); 1060 timeout = setTimeout(() => { 1061 d.resolve(false); 1062 }, timeoutMs); 1063 } 1064 if (postMessageSafe(port, msg)) { 1065 resolve(portDeferred ? await portDeferred.promise : u); 1066 } 1067 } catch (e) { 1068 } finally { 1069 clearTimeout(timeout); 1070 if (msgId) responseQueue.delete(msgId); 1071 } 1072 resolve(false); 1073}); 1074const sendMessageSafe = async (sendMessage, ...debugArgs) => { 1075 let e; 1076 let lastError; 1077 try { 1078 const response = await sendMessage(); 1079 lastError = r.lastError; 1080 if (!lastError) return response; 1081 } catch (error) { 1082 e = error; 1083 lastError = r.lastError; 1084 } 1085 if (e || lastError) { 1086 throw e || lastError?.message || lastError; 1087 } 1088 return false;
1089}; 1090const buttonVariants = cva("inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", { 1091 variants: { 1092 variant: { 1093 default: "bg-primary text-primary-foreground hover:bg-primary/90", 1094 destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90", 1095 outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground", 1096 secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80", 1097 ghost: "hover:bg-accent hover:text-accent-foreground", 1098 link: "text-primary underline-offset-4 hover:underline" 1099 }, 1100 size: { 1101 default: "h-10 px-4 py-2", 1102 sm: "h-9 rounded-md px-3", 1103 lg: "h-11 rounded-md px-8", 1104 icon: "h-10 w-10" 1105 } 1106 }, 1107 defaultVariants: { 1108 variant: "default", 1109 size: "default" 1110 } 1111}); 1112const Button = /* @__PURE__ */ reactExports.forwardRef(({ className, variant, size, asChild = false, ...props }, ref) => { 1113 const Comp = asChild ? Slot : "button"; 1114 return /* @__PURE__ */ jsxRuntimeExports.jsx(Comp, { 1115 className: cn(buttonVariants({ 1116 variant, 1117 size, 1118 className 1119 })), 1120 ref, 1121 ...props 1122 }); 1123}); 1124Button.displayName = "Button"; 1125const isMac = () => { 1126 if (typeof navigator === "undefined") { 1127 return false; 1128 } 1129 const n = navigator; 1130 const macRegex = /Mac/i; 1131 return macRegex.test(n.userAgentData?.platform || "") || macRegex.test(n.platform || "") || macRegex.test(n.userAgent || ""); 1132}; 1133const IS_MAC = isMac(); 1134const SolutionTypeModes = [ 1135 { 1136 id: "answer", 1137 label: "Quick Answer", 1138 description: "direct, concise solution" 1139 }, 1140 { 1141 id: "tutor", 1142 label: "AI Tutor", 1143 description: "detailed guidance with best learning experience" 1144 }, 1145 { 1146 id: "stepby", 1147 label: "Step-by-Step", 1148 description: "step-by-step guidance tailored to your needs" 1149 } 1150]; 1151const findSolutionTypeMode = (value) => value ? SolutionTypeModes.find((mode) => mode.id === value) : null; 1152const MODE_KEY = "mode"; 1153const solutionModeAtom = atom(findSolutionTypeMode(LS?.getItem(MODE_KEY)) || SolutionTypeModes[0], "solutionModeAtom"); 1154const isDraggingAtom = reatomBoolean(false, "isDraggingAtom").pipe(withAssign((isDragging, name) => { 1155 const error = reatomBoolean(false, name + ".error"); 1156 const reset = action((ctx) => { 1157 error.setFalse(ctx); 1158 isDragging.setFalse(ctx); 1159 dragCounter.reset(ctx); 1160 }); 1161 let dragCounter = reatomNumber(0, name + ".counter"); 1162 return { 1163 error, 1164 reset, 1165 onEnter: action((ctx, e) => { 1166 e.preventDefault(); 1167 e.stopPropagation(); 1168 if (!ctx.get(isDragging)) isDragging.setTrue(ctx); 1169 if (dragCounter.increment(ctx) >= 1) return; 1170 const items = e.dataTransfer?.items || []; 1171 const hasImageFile = Array.from(items).some((item) => item.type.startsWith(IMAGE_MIME)); 1172 error(ctx, !hasImageFile); 1173 }, "onEnter"), 1174 onLeave: action((ctx, e) => { 1175 e.preventDefault(); 1176 e.stopPropagation(); 1177 if (dragCounter.decrement(ctx) > 0) return; 1178 reset(ctx); 1179 }, "onLeave"), 1180 onDrop: action((ctx, e) => { 1181 e.preventDefault(); 1182 e.stopPropagation(); 1183 reset(ctx); 1184 handleImageFiles(ctx, e.dataTransfer?.files); 1185 }, "onDrop") 1186 }; 1187})); 1188const handleImageFiles = action((ctx, files) => { 1189 if (!files || files.length <= 0) return false; 1190 for (const file of files) { 1191 if (file && file.type.startsWith(IMAGE_MIME)) { 1192 return validateAndProcessImage(ctx, file); 1193 } 1194 } 1195 inputErrorAtom.show(ctx, "Please select a valid image file (JPEG, PNG, or GIF; 5MB max)"); 1196 return false; 1197}, "handleImageFiles"); 1198const handlePaste = action((ctx, e) => { 1199 const items = e.clipboardData?.items; 1200 if (!items || items.length <= 0) return; 1201 const nonTextItems = Array.from(items).filter((item) => !item.type.startsWith("text")).map((item) => item.getAsFile()); 1202 if (nonTextItems.length > 0) { 1203 e.preventDefault(); 1204 e.stopPropagation(); 1205 handleImageFiles(ctx, nonTextItems); 1206 } 1207}, "handlePaste"); 1208const Dialog = Root; 1209const DialogPortal = Portal$1; 1210const DialogOverlay = /* @__PURE__ */ reactExports.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsx(Overlay, { 1211 ref, 1212 className: cn("data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80", className), 1213 ...props 1214})); 1215DialogOverlay.displayName = Overlay.displayName; 1216const DialogContent = /* @__PURE__ */ reactExports.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogPortal, { 1217 children: [
1218 /* @__PURE__ */ jsxRuntimeExports.jsx(DialogOverlay, {}), 1219 /* @__PURE__ */ jsxRuntimeExports.jsxs(Content, { 1220 ref, 1221 className: cn("bg-background 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-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg duration-200 sm:rounded-lg", className), 1222 ...props, 1223 children: [ 1224 children, 1225 /* @__PURE__ */ jsxRuntimeExports.jsxs(Close$1, { 1226 className: "ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:pointer-events-none", 1227 children: [
1228 /* @__PURE__ */ jsxRuntimeExports.jsx(X, { 1229 className: "h-4 w-4" 1230 }), 1231 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { 1232 className: "sr-only", 1233 children: "Close" 1234 }) 1235 ] 1236 }) 1237 ] 1238 }) 1239 ] 1240})); 1241DialogContent.displayName = Content.displayName; 1242const DialogTitle = /* @__PURE__ */ reactExports.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsx(Title$1, { 1243 ref, 1244 className: cn("text-lg font-semibold leading-none tracking-tight", className), 1245 ...props 1246})); 1247DialogTitle.displayName = Title$1.displayName; 1248const DialogDescription = /* @__PURE__ */ reactExports.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsx(Description$1, { 1249 ref, 1250 className: cn("text-muted-foreground text-sm", className), 1251 ...props 1252})); 1253DialogDescription.displayName = Description$1.displayName; 1254const ImageAttachment = reatomComponent(({ ctx, onRemove, onUpload, attachShortcut: attachShortcut2, error }) => { 1255 const [isPreviewOpen, setIsPreviewOpen] = reactExports.useState(false); 1256 const imagePreviewUrl = ctx.spy(attachedImageAtom.previewUrl); 1257 const imageDetails = ctx.spy(attachedImageAtom.details); 1258 if (ctx.spy(attachedImageAtom.hasImage)) { 1259 return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { 1260 children: [
1261 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { 1262 className: "flex items-center gap-2", 1263 children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { 1264 className: cn("group relative flex items-center gap-2", "rounded-lg bg-gray-100/80", "border border-gray-200/50 shadow-sm", "transition-all duration-200 hover:shadow-md"), 1265 onClick: (e) => e.stopPropagation(), 1266 children: [ 1267 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { 1268 type: "button", 1269 onClick: () => setIsPreviewOpen(true), 1270 className: cn("relative ml-[1px] h-8 w-8 overflow-hidden rounded-md border border-gray-200/50", "group-hover:ring-primary/20 transition-all duration-200 group-hover:ring-2", "focus-visible:ring-primary focus-visible:outline-none focus-visible:ring-2"), 1271 "aria-label": "Preview image", 1272 children: imagePreviewUrl ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { 1273 children: [
1274 /* @__PURE__ */ jsxRuntimeExports.jsx("img", { 1275 src: imagePreviewUrl, 1276 alt: "Problem preview", 1277 className: "h-full w-full object-cover" 1278 }), 1279 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { 1280 className: cn("absolute inset-0 bg-black/0 group-hover:bg-black/30", "flex items-center justify-center transition-all duration-200", "opacity-0 group-hover:opacity-100"), 1281 children: /* @__PURE__ */ jsxRuntimeExports.jsx(ZoomIn, { 1282 className: "h-4 w-4 text-white" 1283 }) 1284 }) 1285 ] 1286 }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { 1287 className: "h-full w-full animate-spin rounded-full border-b-2 border-gray-900 object-cover" 1288 }) 1289 }), 1290 !ctx.spy(inputErrorAtom) && imageDetails && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { 1291 className: "flex flex-col text-xs text-gray-600", 1292 onClick: () => setIsPreviewOpen(true), 1293 children: [ 1294 imageDetails.format, 1295 " ", 1296 imageDetails.dimensions || "", 1297 " ⢠", 1298 imageDetails.size 1299 ] 1300 }),
1301 /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { 1302 onClick: onRemove, 1303 variant: "ghost", 1304 size: "icon", 1305 type: "button", 1306 className: cn("ml-1 mr-1 h-6 w-6 rounded-full", "hover:bg-destructive hover:text-destructive-foreground", "transition-colors duration-200"), 1307 "aria-label": "Remove image", 1308 children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { 1309 className: "h-4 w-4" 1310 }) 1311 }) 1312 ] 1313 }) 1314 }), 1315 /* @__PURE__ */ jsxRuntimeExports.jsx(Dialog, { 1316 open: isPreviewOpen, 1317 onOpenChange: setIsPreviewOpen, 1318 children: /* @__PURE__ */ jsxRuntimeExports.jsx(DialogContent, { 1319 className: "max-w-4xl overflow-hidden bg-black/95 p-0", 1320 children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { 1321 className: "relative", 1322 children: [ 1323 /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { 1324 onClick: () => setIsPreviewOpen(false), 1325 variant: "ghost", 1326 size: "icon", 1327 className: cn("absolute right-2 top-2 z-10", "h-8 w-8 rounded-full", "bg-black/50 hover:bg-black/70", "text-white/70 hover:text-white"), 1328 "aria-label": "Close preview", 1329 children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { 1330 className: "h-4 w-4" 1331 }) 1332 }), 1333 imagePreviewUrl && /* @__PURE__ */ jsxRuntimeExports.jsx("img", { 1334 src: imagePreviewUrl, 1335 alt: "Problem preview", 1336 className: "h-full max-h-[80vh] w-full object-contain" 1337 }) 1338 ] 1339 }) 1340 }) 1341 }) 1342 ] 1343 }); 1344 } 1345 return /* @__PURE__ */ jsxRuntimeExports.jsx(SimpleTooltip, { 1346 content: `Attach image (${attachShortcut2}), drag & drop or paste`, 1347 children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { 1348 type: "button", 1349 variant: "outline", 1350 size: "sm", 1351 onClick: onUpload, 1352 className: cn("transition-colors duration-200 hover:bg-gray-100", "border-gray-200/50"), 1353 "aria-label": "Attach image", 1354 children: [
1355 /* @__PURE__ */ jsxRuntimeExports.jsx(Image, { 1356 className: "mr-2 h-4 w-4 text-gray-600" 1357 }), 1358 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { 1359 className: "text-gray-600", 1360 children: [ 1361 "Attach", 1362 !error && /* @__PURE__ */ jsxRuntimeExports.jsxs("small", { 1363 className: "xs:inline hidden", 1364 children: [ 1365 " (", 1366 attachShortcut2, 1367 ")" 1368 ] 1369 }) 1370 ] 1371 }) 1372 ] 1373 }) 1374 }); 1375}, "ImageAttachment"); 1376const ShortcutDisplay = ({ shortcut }) => { 1377 return /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { 1378 children: shortcut.split(" + ").map((part, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { 1379 children: [ 1380 i > 0 && " + ", 1381 part === "Enter" ? /* @__PURE__ */ jsxRuntimeExports.jsx(CornerDownLeft, { 1382 className: "relative top-[-0.25px] inline size-[0.9rem]", 1383 "aria-label": part 1384 }) : part 1385 ] 1386 }, part)) 1387 }); 1388}; 1389let browserInfo; 1390try { 1391 browserInfo = UAParser(navigator?.userAgent || void 0); 1392} catch (e) { 1393 console.error("UAParser error:", e); 1394} 1395const bInfo = browserInfo; 1396const getScreenshotHotkeys = () => { 1397 const bi = bInfo; 1398 const type = bi?.device?.type; 1399 if (type === "mobile" || type === "tablet") { 1400 return null; 1401 } 1402 const osName = bi?.os?.name?.toLowerCase(); 1403 if (!osName) { 1404 return null; 1405 } 1406 switch (true) { 1407 case (osName.includes("mac") || IS_MAC): 1408 return [ 1409 "â + Shift + 4" 1410 ]; 1411 case osName.includes("win"): 1412 return [ 1413 "Win + Shift + S" 1414 ]; 1415 case osName.includes("linux"): 1416 return [ 1417 "Shift + Print Screen (GNOME-based)", 1418 "Meta + Shift + S (KDE Plasma with Spectacle)" 1419 ]; 1420 case osName.includes("chrome"): 1421 return [ 1422 "Ctrl + Shift + Show Windows (â³ key)" 1423 ]; 1424 default: 1425 return null; 1426 } 1427}; 1428const MESSAGE_KEY = "problem"; 1429const messageAtom = atom("", "messageAtom").pipe(withLocalStorage(MESSAGE_KEY)); 1430const LEVEL_KEY = "level"; 1431const levelAtom = atom(void 0, "levelAtom").pipe(withLocalStorage(LEVEL_KEY)); 1432const EXPANDED_KEY = "expanded"; 1433const isExpandedAtom = reatomBoolean(false, "isExpandedAtom").pipe(withLocalStorage(EXPANDED_KEY)); 1434const showExpandButtonAtom = atom((ctx) => { 1435 if (ctx.spy(isExpandedAtom)) return true; 1436 const input = ctx.spy(messageAtom); 1437 return input.length > 60 || input.includes("\n"); 1438}); 1439const submitProblemForSolving = reatomAsync(async (ctx, navigate) => { 1440 const content = ctx.get(messageAtom)?.trim() || void 0; 1441 const image = ctx.get(attachedImageAtom.image) || void 0; 1442 GA.sendEvent("submit_problem", { 1443 content, 1444 image: !!image 1445 }); 1446 navigate("/c", { 1447 state: { 1448 content, 1449 image, 1450 lvl: ctx.get(levelAtom), 1451 type: ctx.get(solutionModeAtom).id 1452 } 1453 }); 1454 batch(ctx, () => { 1455 messageAtom(ctx, ""); 1456 attachedImageAtom.reset(ctx); 1457 ctx.schedule(() => { 1458 LS?.removeItem(MESSAGE_KEY); 1459 }); 1460 }); 1461}, "submitProblemForSolving"); 1462const ctrlMod = IS_MAC ? "â" : "Ctrl"; 1463const submitShortcut = `${ctrlMod} + Enter`; 1464const expandShortcut = `${ctrlMod} + E`; 1465const attachShortcut = `${ctrlMod} + U`; 1466const captureShortcut = `${ctrlMod} + Shift + S`; 1467const getInvalidUrlTooltip = () => { 1468 const hotkeys = getScreenshotHotkeys(); 1469 const msg = "Screen capture is not available on this page. Please open a regular web page or use your OS screenshot functionality"; 1470 if (!hotkeys) { 1471 return msg + "."; 1472 } 1473 const hk = hotkeys.map((t) => t.replace(/ /g, "Â ")).join(" or "); 1474 return `${msg} (${hk}).`; 1475}; 1476const hasInput = atom((ctx) => { 1477 return !!ctx.spy(messageAtom).trim() || ctx.spy(attachedImageAtom.hasImage); 1478}, "hasInput"); 1479const MathInput = reatomComponent(({ ctx }) => { 1480 const navigate = useNavigate(); 1481 const { dismiss: dismissToast } = useToast(); 1482 const textareaRef = reactExports.useRef(null); 1483 const fileInputRef = reactExports.useRef(null); 1484 const [isFocused, setIsFocused] = reactExports.useState(false); 1485 const hideError = reactExports.useCallback(() => { 1486 inputErrorAtom(ctx, ""); 1487 dismissToast(); 1488 }, [ 1489 ctx, 1490 dismissToast 1491 ]); 1492 const input = ctx.get(messageAtom); 1493 const handleSubmit = reactExports.useCallback((e) => { 1494 e?.preventDefault(); 1495 if (!ctx.get(messageAtom).trim() && !ctx.get(attachedImageAtom.hasImage)) { 1496 inputErrorAtom.show(ctx, "Please enter a math problem or upload an image"); 1497 return; 1498 } 1499 hideError(); 1500 submitProblemForSolving(ctx, navigate); 1501 isExpandedAtom(ctx, false); 1502 }, [ 1503 ctx, 1504 navigate, 1505 hideError 1506 ]); 1507 const handleKeyDown = reactExports.useCallback((e) => { 1508 if (!e.metaKey && !e.ctrlKey) return;
1509 const key = e.key.toLowerCase(); 1510 if (key === "enter") { 1511 handleSubmit(e); 1512 } else if (key === "e") { 1513 e.preventDefault(); 1514 isExpandedAtom.toggle(ctx); 1515 } else if (key === "u") { 1516 e.preventDefault(); 1517 fileInputRef.current?.click(); 1518 } else if (key === "s" && e.shiftKey) { 1519 e.preventDefault(); 1520 screenCaptureAtom.start(ctx, "shortcut"); 1521 } 1522 }, [ 1523 handleSubmit, 1524 ctx 1525 ]); 1526 reactExports.useEffect(() => { 1527 document.addEventListener("keydown", handleKeyDown); 1528 return () => document.removeEventListener("keydown", handleKeyDown); 1529 }, [ 1530 ctx, 1531 handleKeyDown 1532 ]); 1533 const removeImage = (e) => { 1534 e?.preventDefault(); 1535 attachedImageAtom.reset(ctx); 1536 const fileInput = fileInputRef.current; 1537 if (fileInput) { 1538 fileInput.value = ""; 1539 } 1540 }; 1541 const error = ctx.spy(inputErrorAtom); 1542 const isExpanded = ctx.spy(isExpandedAtom); 1543 const showExpandButton = ctx.spy(showExpandButtonAtom); 1544 const solutionType = ctx.spy(solutionModeAtom).id; 1545 const TheSolveIcon = solutionType === "stepby" ? Sparkles : solutionType === "tutor" ? Brain : Zap; 1546 const solveButtonAction = solutionType === "tutor" ? "Explain" : "Solve"; 1547 return /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { 1548 children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { 1549 className: "mx-auto w-full max-w-2xl", 1550 onClick: () => { 1551 textareaRef.current?.focus(); 1552 hideError(); 1553 }, 1554 children: /* @__PURE__ */ jsxRuntimeExports.jsxs("form", { 1555 className: cn("relative overflow-hidden rounded-xl transition-all duration-200", "border bg-white/80 shadow-sm backdrop-blur-lg", "focus-within:ring-primary/20 focus-within:shadow-md focus-within:ring-2 hover:shadow-md", error && "ring-destructive ring-2" || isFocused && "ring-primary/20 ring-2"), 1556 onSubmit: handleSubmit, 1557 "aria-label": "Math problem input form", 1558 role: "form", 1559 children: [
1560 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { 1561 className: "relative", 1562 children: [ 1563 /* @__PURE__ */ jsxRuntimeExports.jsx("textarea", { 1564 value: input, 1565 autoFocus: true, 1566 ref: textareaRef, 1567 onChange: (e) => { 1568 messageAtom(ctx, e.target.value); 1569 hideError(); 1570 }, 1571 onFocus: () => setIsFocused(true), 1572 onBlur: () => setIsFocused(false), 1573 onPaste: (e) => handlePaste(ctx, e), 1574 placeholder: !ctx.spy(attachedImageAtom.hasImage) ? "Type your math question or attach an image..." : "Type your math question or comment for the attached image...", 1575 className: cn("min-h-20 w-full bg-transparent outline-none", "p-4 text-gray-800 placeholder:text-gray-400", isExpanded ? "h-48 resize-y" : "h-20 resize-none transition-all duration-75"), 1576 "aria-label": "Math problem input", 1577 "aria-invalid": !!error, 1578 "aria-describedby": error ? "input-error" : void 0 1579 }),
1580 showExpandButton && /* @__PURE__ */ jsxRuntimeExports.jsx(SimpleTooltip, { 1581 content: (isExpanded ? "Collapse" : "Expand") + ` (${expandShortcut})`, 1582 children: /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { 1583 type: "button", 1584 variant: "ghost", 1585 size: "icon", 1586 onClick: () => isExpandedAtom.toggle(ctx), 1587 className: "absolute right-2 top-2 h-6 w-6 rounded-lg transition-colors duration-200 hover:bg-gray-100/50", 1588 "aria-label": isExpanded ? "Collapse input" : "Expand input", 1589 children: isExpanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(Minimize2, { 1590 className: "h-4 w-4 text-gray-400" 1591 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Maximize2, { 1592 className: "h-4 w-4 text-gray-400" 1593 }) 1594 }) 1595 }) 1596 ] 1597 }), 1598 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { 1599 className: "flex flex-row justify-between gap-1 border-t bg-gray-50/50 p-2 sm:gap-2", 1600 children: [
1601 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { 1602 className: "flex flex-row items-start gap-1 sm:gap-2", 1603 children: [ 1604 ctx.spy(hasExtensionAtom) && !ctx.spy(attachedImageAtom.hasImage) && /* @__PURE__ */ jsxRuntimeExports.jsx(SimpleTooltip, { 1605 content: ctx.spy(extensionCtxAtom.isValidUrl) ? `Capture area (${captureShortcut})` : getInvalidUrlTooltip(), 1606 children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { 1607 children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { 1608 type: "button", 1609 variant: "outline", 1610 size: "sm", 1611 onClick: () => screenCaptureAtom.start(ctx, "button"), 1612 className: cn("transition-colors duration-200 hover:bg-gray-100", "border-gray-200/50", !ctx.spy(extensionCtxAtom.isValidUrl) && "cursor-not-allowed opacity-50 hover:bg-transparent"), 1613 "aria-label": "Capture area", 1614 disabled: !ctx.spy(extensionCtxAtom.isValidUrl), 1615 children: [
1616 /* @__PURE__ */ jsxRuntimeExports.jsx(Scissors, { 1617 className: "mr-2 h-4 w-4 text-gray-600" 1618 }), 1619 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { 1620 className: "text-gray-600", 1621 children: [ 1622 "Capture", 1623 !error && ctx.spy(extensionCtxAtom.isValidUrl) && /* @__PURE__ */ jsxRuntimeExports.jsxs("small", { 1624 className: "xs:inline hidden", 1625 children: [ 1626 " (", 1627 captureShortcut, 1628 ")" 1629 ] 1630 }) 1631 ] 1632 }) 1633 ] 1634 }) 1635 }) 1636 }), 1637 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { 1638 className: "flex items-center gap-2", 1639 children: /* @__PURE__ */ jsxRuntimeExports.jsx(ImageAttachment, { 1640 onRemove: removeImage, 1641 onUpload: () => fileInputRef.current?.click(), 1642 attachShortcut 1643 }) 1644 }), 1645 error && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { 1646 id: "input-error", 1647 className: "text-destructive max-w-full break-words text-sm", 1648 role: "alert", 1649 children: error 1650 }) 1651 ] 1652 }),
1653 /* @__PURE__ */ jsxRuntimeExports.jsx(SimpleTooltip, { 1654 content: `${solveButtonAction} (${submitShortcut})`, 1655 children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { 1656 type: "submit", 1657 size: "sm", 1658 className: cn("bg-primary text-white transition-all duration-200", !ctx.spy(hasInput) ? "cursor-not-allowed opacity-50" : "hover:bg-primary/90 hover:scale-[1.02] hover:shadow-md"), 1659 "aria-label": "Solve math problem", 1660 children: [ 1661 /* @__PURE__ */ jsxRuntimeExports.jsx(TheSolveIcon, { 1662 className: "mr-2 h-4 w-4" 1663 }), 1664 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { 1665 className: "flex items-center gap-1.5", 1666 children: [ 1667 solveButtonAction, 1668 !error && /* @__PURE__ */ jsxRuntimeExports.jsxs("small", { 1669 className: "xs:inline hidden", 1670 children: [ 1671 " ", 1672 "(",
1673 /* @__PURE__ */ jsxRuntimeExports.jsx(ShortcutDisplay, { 1674 shortcut: submitShortcut 1675 }), 1676 ")" 1677 ] 1678 }) 1679 ] 1680 }) 1681 ] 1682 }) 1683 }) 1684 ] 1685 }), 1686 /* @__PURE__ */ jsxRuntimeExports.jsx("input", { 1687 type: "file", 1688 accept: ALLOWED_IMAGE_TYPES.join(","), 1689 ref: fileInputRef, 1690 className: "hidden", 1691 onChange: (e) => { 1692 if (!handleImageFiles(ctx, e.target.files)) { 1693 e.target.value = ""; 1694 } 1695 }, 1696 "aria-label": "Upload image" 1697 }) 1698 ] 1699 }) 1700 }) 1701 }); 1702}); 1703const EXTENSION_ID = "bjilalbcjkmilhhkfagmblkoekicmcnf"; 1704var __defProp = Object.defineProperty; 1705var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; 1706var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); 1707const extensionCtxAtom = atom(null, "extensionContext").pipe(withAssign((ext, name) => { 1708 return { 1709 id: atom((ctx) => ctx.spy(ext)?.extensionId, name + ".id"), 1710 userId: atom((ctx) => ctx.spy(ext)?.userId, name + ".userId"), 1711 tabId: atom(0, name + ".tabId"), 1712 url: atom("", name + ".url"), 1713 isValidUrl: atom(true, name + ".isValidUrl") 1714 }; 1715})); 1716const hasExtensionAtom = atom((ctx) => !!ctx.spy(extensionCtxAtom.id)); 1717const hasWindow = typeof window !== "undefined"; 1718const w = hasWindow ? window : {}; 1719const parentWindow = hasWindow && w.parent !== w && w.parent; 1720const chromRuntime = hasWindow && "chrome" in window && chrome.runtime; 1721const _ExtBridge = class _ExtBridge2 { 1722 constructor(ctx) { 1723 __publicField(this, "messageHandlers", /* @__PURE__ */ new Map()); 1724 __publicField(this, "initialized", false); 1725 __publicField(this, "extensionId", null); 1726 __publicField(this, "comm", null); 1727 __publicField(this, "handleRuntimeMessage", (msg, sender) => { 1728 if (!msg) return; 1729 if (!isCommMsg(msg)) { 1730 return; 1731 } 1732 this.processMessage(msg, "runtime"); 1733 }); 1734 __publicField(this, "handleMessage", (event) => { 1735 const { data, origin } = event; 1736 if (!isExtensionOrigin(origin) || !isCommMsg(data) && data) { 1737 return; 1738 } 1739 this.processMessage(data, "iframe"); 1740 }); 1741 __publicField(this, "processMessage", (msg, via) => { 1742 if (msg.eid) { 1743 this.initialized = true; 1744 this.extensionId = msg.eid; 1745 } 1746 this.messageHandlers.get(msg.t)?.forEach((handler) => handler(msg)); 1747 }); 1748 if (hasWindow) { 1749 const w2 = window; 1750 w2.addEventListener("message", this.handleMessage); 1751 const urlParams = new URLSearchParams(w2.location.search); 1752 const extensionId = urlParams.get(EXTENSION_ID_PARAM) || EXTENSION_ID; 1753 this.extensionId = extensionId; 1754 if (parentWindow) { 1755 const userId = urlParams.get(USER_ID_PARAM); 1756 if (userId) { 1757 getUserId(ctx); 1758 } 1759 extensionCtxAtom(ctx, { 1760 isExtension: true, 1761 userId, 1762 extensionId 1763 }); 1764 if (chromRuntime) { 1765 this.comm = new WebAppCommunicator(extensionId, this.handleRuntimeMessage); 1766 } 1767 } 1768 this.onMessage(CommMsgType.startSolvingCmd, async (msg) => { 1769 try { 1770 const d = msg.d; 1771 const text = d?.text?.trim(); 1772 if (text) { 1773 messageAtom(ctx, (prev) => { 1774 prev = prev.trim(); 1775 return prev ? prev + "\n" + text : text; 1776 }); 1777 } 1778 let image = d?.image; 1779 if (image) { 1780 GA.sendEvent("ext_screenshot", { 1781 imageChars: typeof image === "string" ? image.length : 0 1782 }); 1783 if (!(image instanceof File)) { 1784 try { 1785 image = await getFilefromDataUrl(image, "screenshot.png"); 1786 } catch (error) { 1787 attachedImageAtom.previewUrl(ctx, image); 1788 return; 1789 } 1790 } 1791 validateAndProcessImage(ctx, image); 1792 } 1793 } finally { 1794 screenCaptureAtom.stop(ctx); 1795 } 1796 }); 1797 this.onMessage(CommMsgType.getTabState, (msg) => { 1798 batch(ctx, () => { 1799 const tabId = msg.tabId || 0; 1800 const url = String(msg.v || ""); 1801 const state = msg.d; 1802 extensionCtxAtom.tabId(ctx, tabId); 1803 extensionCtxAtom.url(ctx, url); 1804 extensionCtxAtom.isValidUrl(ctx, state !== ExtensionState.unsupported); 1805 }); 1806 }); 1807 this.sendMessage(PingMsg).catch((err) => { 1808 }); 1809 } 1810 } 1811 static getInstance(ctx) { 1812 if (!_ExtBridge2.instance) { 1813 if (!ctx) throw new Error("Context required for ExtBridge"); 1814 _ExtBridge2.instance = new _ExtBridge2(ctx); 1815 } 1816 return _ExtBridge2.instance; 1817 } 1818 async sendMessage(msg) { 1819 return new Promise(async (resolve, reject) => { 1820 if (!hasWindow) return resolve(); 1821 msg.from = MsgFrom.WebApp; 1822 let err; 1823 let couldSend = false;
1824 const comm = this.comm; 1825 if (chromRuntime && comm) { 1826 couldSend = true; 1827 try { 1828 await comm.sendMsg(msg); 1829 return resolve(true); 1830 } catch (error) { 1831 err = safeErrorCause(error); 1832 console.error("Failed to send runtime message:", err); 1833 } 1834 } 1835 if (parentWindow) { 1836 couldSend = true; 1837 try { 1838 parentWindow.postMessage(msg, "*"); 1839 return resolve(true); 1840 } catch (error) { 1841 err = safeErrorCause(error, err); 1842 console.error("Failed to send iframe message:", err); 1843 } 1844 } 1845 if (!err && !couldSend) { 1846 return resolve(false); 1847 } 1848 reject(err || new Error("Could not send message")); 1849 }); 1850 } 1851 onMessage(type, handler) { 1852 if (!this.messageHandlers.has(type)) { 1853 this.messageHandlers.set(type, /* @__PURE__ */ new Set()); 1854 } 1855 this.messageHandlers.get(type).add(handler); 1856 return () => { 1857 const handlers = this.messageHandlers.get(type); 1858 if (handlers) { 1859 handlers.delete(handler); 1860 if (handlers.size === 0) { 1861 this.messageHandlers.delete(type); 1862 } 1863 } 1864 }; 1865 } 1866}; 1867__publicField(_ExtBridge, "instance"); 1868let ExtBridge = _ExtBridge; 1869const reatomCtx = createCtx(); 1870const CreateAppInternal = () => /* @__PURE__ */ jsxRuntimeExports.jsx(reatomContext.Provider, { 1871 value: reatomCtx, 1872 children: /* @__PURE__ */ jsxRuntimeExports.jsx(App, {}) 1873}); 1874const createApp = () => { 1875 return /* @__PURE__ */ jsxRuntimeExports.jsx(CreateAppInternal, {}); 1876}; 1877const mount = () => { 1878 const rootNode = document.getElementById("app"); 1879 if (!rootNode) { 1880 throw new Error("#app element not found"); 1881 } 1882 const root = clientExports.createRoot(rootNode); 1883 root.render(createApp()); 1884}; 1885if (typeof window !== "undefined") { 1886 ExtBridge.getInstance(reatomCtx); 1887 getUserId(reatomCtx); 1888 mount(); 1889} 1890export { 1891 ALLOWED_HINT as A, 1892 Button as B, 1893 EXTENSION_ID as E, 1894 GA as G, 1895 IS_MAC as I, 1896 MathInput as M, 1897 SolutionTypeModes as S, 1898 Tooltip as T, 1899 isDraggingAtom as a, 1900 getUserId as b, 1901 cn as c, 1902 hasExtensionAtom as d, 1903 ShortcutDisplay as e, 1904 findSolutionTypeMode as f, 1905 getScreenshotHotkeys as g, 1906 handlePaste as h, 1907 inputErrorAtom as i, 1908 ABORTED as j, 1909 safeErrorCause as k, 1910 uuidv7b64 as l, 1911 fileToBase64 as m, 1912 reatomCtx as r, 1913 solutionModeAtom as s, 1914 useToast as u 1915};
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.