Scrollmark

本地優先的 X/Twitter 研究歸檔、搜尋、書籤擷取與可攜式 Bundle 匯出工具。

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name               Scrollmark
// @name:zh-CN         Scrollmark
// @name:zh-TW         Scrollmark
// @name:ja            Scrollmark
// @namespace          https://github.com/kmccleary3301/scrollmark
// @version            1.0.0
// @author             Kyle McCleary
// @description        Local-first X/Twitter research archive, search, bookmark capture, and portable bundle export by Kyle McCleary.
// @description:zh-CN  本地优先的 X/Twitter 研究归档、搜索、书签采集与可移植 Bundle 导出工具。
// @description:zh-TW  本地優先的 X/Twitter 研究歸檔、搜尋、書籤擷取與可攜式 Bundle 匯出工具。
// @description:ja     ローカルファーストの X/Twitter 研究アーカイブ、検索、ブックマーク取得、ポータブル Bundle 出力ツール。
// @license            MIT
// @icon               data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABmklEQVR4Ae3XA4wcARSA4dq2bUQ1g9pRbVtBzai2otpug9pxUttn2753/3m9Ozq/5NsdvvfGM6VKoshE8/ORFbAMbxCGWHzDHjS2sXxPlM0eKYclGoq3w1eIHVGYikaYg6e4ZppgAgQrVBSvDw+IEylIhSAATUyTHIYgFdsUNnAGosAfDMccLMtOchli4g7quFC8FhIhCsRD8Bk1sxMdgVjwxRyUdtDABIgKH9DQNNEkiB1fMB9VbDSwEKLQJ1S1TFQRXhAHYnADy9ETdTEeotAze7tzNJIhCiRBFLpnq/hmzMR65UkVO2WrgaOQPLLW3u6XPDLAVgOl8R5isEhUtHcSdkEoxEBXnN3ZuuMbxCDDnTVQF52xBcEQHX1BaWcNtDLwMpzg6tNtN0RnD5U8XsviGkQnYWih9CWjNBbDHaJBMsZqec8rjV54B1EoFXO0Fh+DrxCFEjBTTdFy6IvNGu4Hf9FXSdGheAUvjZdgLPajqtp3+jl4jVSIAgHYjRZ6fWC0wSpcwScEQZCMUPzEfezEYJQrVRKFOdIAZGq1QBG8EiYAAAAASUVORK5CYII=
// @homepage           https://github.com/kmccleary3301/scrollmark
// @homepageURL        https://github.com/kmccleary3301/scrollmark
// @supportURL         https://github.com/kmccleary3301/scrollmark/issues
// @match              *://twitter.com/*
// @match              *://x.com/*
// @match              *://mobile.x.com/*
// @require            https://cdn.jsdelivr.net/npm/[email protected]/dayjs.min.js
// @require            https://cdn.jsdelivr.net/npm/[email protected]/dist/dexie.min.js
// @require            https://cdn.jsdelivr.net/npm/[email protected]/dist/dexie-export-import.js
// @require            https://cdn.jsdelivr.net/npm/[email protected]/dist/FileSaver.min.js
// @require            https://cdn.jsdelivr.net/npm/[email protected]/i18next.min.js
// @require            https://cdn.jsdelivr.net/npm/[email protected]/dist/preact.min.js
// @require            https://cdn.jsdelivr.net/npm/[email protected]/hooks/dist/hooks.umd.js
// @require            https://cdn.jsdelivr.net/npm/@preact/[email protected]/dist/signals-core.min.js
// @require            https://cdn.jsdelivr.net/npm/@preact/[email protected]/dist/signals.min.js
// @require            https://cdn.jsdelivr.net/npm/@tanstack/[email protected]/build/umd/index.production.js
// @connect            cdn.syndication.twimg.com
// @grant              GM_addStyle
// @grant              GM_registerMenuCommand
// @grant              GM_xmlhttpRequest
// @grant              unsafeWindow
// @inject-into        page
// @run-at             document-start
// ==/UserScript==

(t=>{if(typeof GM_addStyle=="function"){GM_addStyle(t);return}const o=document.createElement("style");o.textContent=t,document.head.append(o)})(` #twe-root *,#twe-root :before,#twe-root :after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }#twe-root ::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }#twe-root *,#twe-root :before,#twe-root :after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}#twe-root :before,#twe-root :after{--tw-content: ""}#twe-root,#twe-root :host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}#twe-root{margin:0;line-height:inherit}#twe-root hr{height:0;color:inherit;border-top-width:1px}#twe-root abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}#twe-root h1,#twe-root h2,#twe-root h3,#twe-root h4,#twe-root h5,#twe-root h6{font-size:inherit;font-weight:inherit}#twe-root a{color:inherit;text-decoration:inherit}#twe-root b,#twe-root strong{font-weight:bolder}#twe-root code,#twe-root kbd,#twe-root samp,#twe-root pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}#twe-root small{font-size:80%}#twe-root sub,#twe-root sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}#twe-root sub{bottom:-.25em}#twe-root sup{top:-.5em}#twe-root table{text-indent:0;border-color:inherit;border-collapse:collapse}#twe-root button,#twe-root input,#twe-root optgroup,#twe-root select,#twe-root textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}#twe-root button,#twe-root select{text-transform:none}#twe-root button,#twe-root input:where([type=button]),#twe-root input:where([type=reset]),#twe-root input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}#twe-root :-moz-focusring{outline:auto}#twe-root :-moz-ui-invalid{box-shadow:none}#twe-root progress{vertical-align:baseline}#twe-root ::-webkit-inner-spin-button,#twe-root ::-webkit-outer-spin-button{height:auto}#twe-root [type=search]{-webkit-appearance:textfield;outline-offset:-2px}#twe-root ::-webkit-search-decoration{-webkit-appearance:none}#twe-root ::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}#twe-root summary{display:list-item}#twe-root blockquote,#twe-root dl,#twe-root dd,#twe-root h1,#twe-root h2,#twe-root h3,#twe-root h4,#twe-root h5,#twe-root h6,#twe-root hr,#twe-root figure,#twe-root p,#twe-root pre{margin:0}#twe-root fieldset{margin:0;padding:0}#twe-root legend{padding:0}#twe-root ol,#twe-root ul,#twe-root menu{list-style:none;margin:0;padding:0}#twe-root dialog{padding:0}#twe-root textarea{resize:vertical}#twe-root input::-moz-placeholder,#twe-root textarea::-moz-placeholder{opacity:1;color:#9ca3af}#twe-root input::placeholder,#twe-root textarea::placeholder{opacity:1;color:#9ca3af}#twe-root button,#twe-root [role=button]{cursor:pointer}#twe-root :disabled{cursor:default}#twe-root img,#twe-root svg,#twe-root video,#twe-root canvas,#twe-root audio,#twe-root iframe,#twe-root embed,#twe-root object{display:block;vertical-align:middle}#twe-root img,#twe-root video{max-width:100%;height:auto}#twe-root [hidden]:where(:not([hidden=until-found])){display:none}#twe-root,#twe-root [data-theme]{background-color:var(--fallback-b1,oklch(var(--b1)/1));color:var(--fallback-bc,oklch(var(--bc)/1))}@supports not (color: oklch(0% 0 0)){#twe-root{color-scheme:light;--fallback-p: #491eff;--fallback-pc: #d4dbff;--fallback-s: #ff41c7;--fallback-sc: #fff9fc;--fallback-a: #00cfbd;--fallback-ac: #00100d;--fallback-n: #2b3440;--fallback-nc: #d7dde4;--fallback-b1: #ffffff;--fallback-b2: #e5e6e6;--fallback-b3: #e5e6e6;--fallback-bc: #1f2937;--fallback-in: #00b3f0;--fallback-inc: #000000;--fallback-su: #00ca92;--fallback-suc: #000000;--fallback-wa: #ffc22d;--fallback-wac: #000000;--fallback-er: #ff6f70;--fallback-erc: #000000}@media(prefers-color-scheme:dark){#twe-root{color-scheme:dark;--fallback-p: #7582ff;--fallback-pc: #050617;--fallback-s: #ff71cf;--fallback-sc: #190211;--fallback-a: #00c7b5;--fallback-ac: #000e0c;--fallback-n: #2a323c;--fallback-nc: #a6adbb;--fallback-b1: #1d232a;--fallback-b2: #191e24;--fallback-b3: #15191e;--fallback-bc: #a6adbb;--fallback-in: #00b3f0;--fallback-inc: #000000;--fallback-su: #00ca92;--fallback-suc: #000000;--fallback-wa: #ffc22d;--fallback-wac: #000000;--fallback-er: #ff6f70;--fallback-erc: #000000}}}#twe-root{-webkit-tap-highlight-color:transparent}#twe-root *{scrollbar-color:color-mix(in oklch,currentColor 35%,transparent) transparent}#twe-root *:hover{scrollbar-color:color-mix(in oklch,currentColor 60%,transparent) transparent}#twe-root{color-scheme:light;--in: 72.06% .191 231.6;--su: 64.8% .15 160;--wa: 84.71% .199 83.87;--er: 71.76% .221 22.18;--pc: 15.2344% .017892 200.026556;--sc: 15.787% .020249 356.29965;--ac: 15.8762% .029206 78.618794;--nc: 84.7148% .013247 313.189598;--inc: 0% 0 0;--suc: 0% 0 0;--wac: 0% 0 0;--erc: 0% 0 0;--rounded-box: 16px;--rounded-badge: 30.4px;--animation-btn: .25s;--animation-input: .2s;--btn-focus-scale: .95;--border-btn: 1px;--p: 76.172% .089459 200.026556;--s: 78.9351% .101246 356.29965;--a: 79.3811% .146032 78.618794;--n: 23.5742% .066235 313.189598;--b1: 97.7882% .00418 56.375637;--b2: 93.9822% .007638 61.449292;--b3: 91.5861% .006811 53.440502;--bc: 23.5742% .066235 313.189598;--rounded-btn: 30.4px;--tab-border: 2px;--tab-radius: 11.2px}@media(prefers-color-scheme:dark){#twe-root{color-scheme:dark;--b2: 26.8053% .020556 277.508664;--b3: 24.7877% .019009 277.508664;--pc: 15.0922% .036614 346.812432;--sc: 14.8405% .029709 301.883095;--ac: 16.6785% .024826 66.558491;--nc: 87.8891% .006515 275.524078;--inc: 17.6526% .018676 212.846491;--suc: 17.4199% .043903 148.024881;--wac: 19.1068% .026849 112.757109;--erc: 13.6441% .041266 24.430965;--rounded-box: 16px;--rounded-btn: 8px;--rounded-badge: 30.4px;--animation-btn: .25s;--animation-input: .2s;--btn-focus-scale: .95;--border-btn: 1px;--tab-border: 1px;--tab-radius: 8px;--p: 75.4611% .18307 346.812432;--s: 74.2023% .148546 301.883095;--a: 83.3927% .124132 66.558491;--n: 39.4456% .032576 275.524078;--b1: 28.8229% .022103 277.508664;--bc: 97.7477% .007913 106.545019;--in: 88.263% .09338 212.846491;--su: 87.0995% .219516 148.024881;--wa: 95.5338% .134246 112.757109;--er: 68.2204% .206328 24.430965}}#twe-root [data-theme=cupcake]{color-scheme:light;--in: 72.06% .191 231.6;--su: 64.8% .15 160;--wa: 84.71% .199 83.87;--er: 71.76% .221 22.18;--pc: 15.2344% .017892 200.026556;--sc: 15.787% .020249 356.29965;--ac: 15.8762% .029206 78.618794;--nc: 84.7148% .013247 313.189598;--inc: 0% 0 0;--suc: 0% 0 0;--wac: 0% 0 0;--erc: 0% 0 0;--rounded-box: 16px;--rounded-badge: 30.4px;--animation-btn: .25s;--animation-input: .2s;--btn-focus-scale: .95;--border-btn: 1px;--p: 76.172% .089459 200.026556;--s: 78.9351% .101246 356.29965;--a: 79.3811% .146032 78.618794;--n: 23.5742% .066235 313.189598;--b1: 97.7882% .00418 56.375637;--b2: 93.9822% .007638 61.449292;--b3: 91.5861% .006811 53.440502;--bc: 23.5742% .066235 313.189598;--rounded-btn: 30.4px;--tab-border: 2px;--tab-radius: 11.2px}#twe-root [data-theme=dark]{color-scheme:dark;--in: 72.06% .191 231.6;--su: 64.8% .15 160;--wa: 84.71% .199 83.87;--er: 71.76% .221 22.18;--pc: 13.138% .0392 275.75;--sc: 14.96% .052 342.55;--ac: 14.902% .0334 183.61;--inc: 0% 0 0;--suc: 0% 0 0;--wac: 0% 0 0;--erc: 0% 0 0;--rounded-box: 16px;--rounded-btn: 8px;--rounded-badge: 30.4px;--animation-btn: .25s;--animation-input: .2s;--btn-focus-scale: .95;--border-btn: 1px;--tab-border: 1px;--tab-radius: 8px;--p: 65.69% .196 275.75;--s: 74.8% .26 342.55;--a: 74.51% .167 183.61;--n: 31.3815% .021108 254.139175;--nc: 74.6477% .0216 264.435964;--b1: 25.3267% .015896 252.417568;--b2: 23.2607% .013807 253.100675;--b3: 21.1484% .01165 254.087939;--bc: 74.6477% .0216 264.435964}#twe-root [data-theme=emerald]{color-scheme:light;--b2: 93% 0 0;--b3: 86% 0 0;--in: 72.06% .191 231.6;--su: 64.8% .15 160;--wa: 84.71% .199 83.87;--er: 71.76% .221 22.18;--inc: 0% 0 0;--suc: 0% 0 0;--wac: 0% 0 0;--erc: 0% 0 0;--rounded-box: 16px;--rounded-btn: 8px;--rounded-badge: 30.4px;--border-btn: 1px;--tab-border: 1px;--tab-radius: 8px;--p: 76.6626% .135433 153.450024;--pc: 33.3872% .040618 162.240129;--s: 61.3028% .202368 261.294233;--sc: 100% 0 0;--a: 72.7725% .149783 33.200363;--ac: 0% 0 0;--n: 35.5192% .032071 262.988584;--nc: 98.4625% .001706 247.838921;--b1: 100% 0 0;--bc: 35.5192% .032071 262.988584;--animation-btn: 0;--animation-input: 0;--btn-focus-scale: 1}#twe-root [data-theme=cyberpunk]{color-scheme:light;--b2: 87.8943% .16647 104.32;--b3: 81.2786% .15394 104.32;--in: 72.06% .191 231.6;--su: 64.8% .15 160;--wa: 84.71% .199 83.87;--er: 71.76% .221 22.18;--bc: 18.902% .0358 104.32;--pc: 14.844% .0418 6.35;--sc: 16.666% .0368 204.72;--ac: 14.372% .04352 310.43;--inc: 0% 0 0;--suc: 0% 0 0;--wac: 0% 0 0;--erc: 0% 0 0;--animation-btn: .25s;--animation-input: .2s;--btn-focus-scale: .95;--border-btn: 1px;--tab-border: 1px;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;--p: 74.22% .209 6.35;--s: 83.33% .184 204.72;--a: 71.86% .2176 310.43;--n: 23.04% .065 269.31;--nc: 94.51% .179 104.32;--b1: 94.51% .179 104.32;--rounded-box: 0;--rounded-btn: 0;--rounded-badge: 0;--tab-radius: 0}#twe-root [data-theme=valentine]{color-scheme:light;--b2: 88.0567% .024834 337.06289;--b3: 81.4288% .022964 337.06289;--pc: 13.7239% .030755 15.066527;--sc: 14.3942% .029258 293.189609;--ac: 14.2537% .014961 197.828857;--inc: 90.923% .043042 262.880917;--suc: 12.541% .033982 149.213788;--wac: 13.3168% .031484 58.31834;--erc: 14.614% .0414 27.33;--rounded-box: 16px;--rounded-badge: 30.4px;--animation-btn: .25s;--animation-input: .2s;--btn-focus-scale: .95;--border-btn: 1px;--tab-border: 1px;--p: 68.6197% .153774 15.066527;--s: 71.971% .14629 293.189609;--a: 71.2685% .074804 197.828857;--n: 54.6053% .143342 358.004839;--nc: 90.2701% .037202 336.955191;--b1: 94.6846% .026703 337.06289;--bc: 37.3085% .081131 4.606426;--in: 54.615% .215208 262.880917;--su: 62.7052% .169912 149.213788;--wa: 66.584% .157422 58.31834;--er: 73.07% .207 27.33;--rounded-btn: 30.4px;--tab-radius: 11.2px}#twe-root [data-theme=lofi]{color-scheme:light;--inc: 15.908% .0206 205.9;--suc: 18.026% .0306 164.14;--wac: 17.674% .027 79.94;--erc: 15.732% .03 28.47;--border-btn: 1px;--tab-border: 1px;--p: 15.9066% 0 0;--pc: 100% 0 0;--s: 21.455% .001566 17.278957;--sc: 100% 0 0;--a: 26.8618% 0 0;--ac: 100% 0 0;--n: 0% 0 0;--nc: 100% 0 0;--b1: 100% 0 0;--b2: 96.1151% 0 0;--b3: 92.268% .001082 17.17934;--bc: 0% 0 0;--in: 79.54% .103 205.9;--su: 90.13% .153 164.14;--wa: 88.37% .135 79.94;--er: 78.66% .15 28.47;--rounded-box: 4px;--rounded-btn: 2px;--rounded-badge: 2px;--tab-radius: 2px;--animation-btn: 0;--animation-input: 0;--btn-focus-scale: 1}#twe-root [data-theme=dracula]{color-scheme:dark;--b2: 26.8053% .020556 277.508664;--b3: 24.7877% .019009 277.508664;--pc: 15.0922% .036614 346.812432;--sc: 14.8405% .029709 301.883095;--ac: 16.6785% .024826 66.558491;--nc: 87.8891% .006515 275.524078;--inc: 17.6526% .018676 212.846491;--suc: 17.4199% .043903 148.024881;--wac: 19.1068% .026849 112.757109;--erc: 13.6441% .041266 24.430965;--rounded-box: 16px;--rounded-btn: 8px;--rounded-badge: 30.4px;--animation-btn: .25s;--animation-input: .2s;--btn-focus-scale: .95;--border-btn: 1px;--tab-border: 1px;--tab-radius: 8px;--p: 75.4611% .18307 346.812432;--s: 74.2023% .148546 301.883095;--a: 83.3927% .124132 66.558491;--n: 39.4456% .032576 275.524078;--b1: 28.8229% .022103 277.508664;--bc: 97.7477% .007913 106.545019;--in: 88.263% .09338 212.846491;--su: 87.0995% .219516 148.024881;--wa: 95.5338% .134246 112.757109;--er: 68.2204% .206328 24.430965}#twe-root [data-theme=cmyk]{color-scheme:light;--b2: 93% 0 0;--b3: 86% 0 0;--bc: 20% 0 0;--pc: 14.3544% .02666 239.443325;--sc: 12.8953% .040552 359.339283;--ac: 18.8458% .037948 105.306968;--nc: 84.3557% 0 0;--inc: 13.6952% .0189 217.284104;--suc: 89.3898% .032505 321.406278;--wac: 14.2473% .031969 52.023412;--erc: 12.4027% .041677 28.717543;--rounded-box: 16px;--rounded-btn: 8px;--rounded-badge: 30.4px;--animation-btn: .25s;--animation-input: .2s;--btn-focus-scale: .95;--border-btn: 1px;--tab-border: 1px;--tab-radius: 8px;--p: 71.7722% .133298 239.443325;--s: 64.4766% .202758 359.339283;--a: 94.2289% .189741 105.306968;--n: 21.7787% 0 0;--b1: 100% 0 0;--in: 68.4759% .094499 217.284104;--su: 46.949% .162524 321.406278;--wa: 71.2364% .159843 52.023412;--er: 62.0133% .208385 28.717543}#twe-root [data-theme=business]{color-scheme:dark;--b2: 22.6487% 0 0;--b3: 20.944% 0 0;--bc: 84.8707% 0 0;--pc: 88.3407% .019811 251.473931;--sc: 12.8185% .005481 229.389418;--ac: 13.4542% .033545 35.791525;--nc: 85.4882% .00265 253.041249;--inc: 12.5233% .028702 240.033697;--suc: 14.0454% .018919 156.59611;--wac: 15.4965% .023141 81.519177;--erc: 90.3221% .029356 29.674507;--animation-btn: .25s;--animation-input: .2s;--btn-focus-scale: .95;--border-btn: 1px;--tab-border: 1px;--tab-radius: 8px;--p: 41.7036% .099057 251.473931;--s: 64.0924% .027405 229.389418;--a: 67.271% .167726 35.791525;--n: 27.441% .01325 253.041249;--b1: 24.3535% 0 0;--in: 62.6163% .143511 240.033697;--su: 70.2268% .094594 156.59611;--wa: 77.4824% .115704 81.519177;--er: 51.6105% .14678 29.674507;--rounded-box: 4px;--rounded-btn: 2px;--rounded-badge: 2px}#twe-root [data-theme=winter]{color-scheme:light;--pc: 91.372% .051 257.57;--sc: 88.5103% .03222 282.339433;--ac: 11.988% .038303 335.171434;--nc: 83.9233% .012704 257.651965;--inc: 17.6255% .017178 214.515264;--suc: 16.0988% .015404 197.823719;--wac: 17.8345% .009167 71.47031;--erc: 14.6185% .022037 20.076293;--rounded-box: 16px;--rounded-btn: 8px;--rounded-badge: 30.4px;--animation-btn: .25s;--animation-input: .2s;--btn-focus-scale: .95;--border-btn: 1px;--tab-border: 1px;--tab-radius: 8px;--p: 56.86% .255 257.57;--s: 42.5516% .161098 282.339433;--a: 59.9398% .191515 335.171434;--n: 19.6166% .063518 257.651965;--b1: 100% 0 0;--b2: 97.4663% .011947 259.822565;--b3: 93.2686% .016223 262.751375;--bc: 41.8869% .053885 255.824911;--in: 88.1275% .085888 214.515264;--su: 80.4941% .077019 197.823719;--wa: 89.1725% .045833 71.47031;--er: 73.0926% .110185 20.076293}#twe-root .alert{display:grid;width:100%;grid-auto-flow:row;align-content:flex-start;align-items:center;justify-items:center;gap:16px;text-align:center;border-radius:var(--rounded-box, 16px);border-width:1px;--tw-border-opacity: 1;border-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)));padding:16px;--tw-text-opacity: 1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));--alert-bg: var(--fallback-b2,oklch(var(--b2)/1));--alert-bg-mix: var(--fallback-b1,oklch(var(--b1)/1));background-color:var(--alert-bg)}@media(min-width:640px){#twe-root .alert{grid-auto-flow:column;grid-template-columns:auto minmax(auto,1fr);justify-items:start;text-align:start}}#twe-root .avatar{position:relative;display:inline-flex}#twe-root .avatar>div{display:block;aspect-ratio:1 / 1;overflow:hidden}#twe-root .avatar img{height:100%;width:100%;-o-object-fit:cover;object-fit:cover}#twe-root .avatar.placeholder>div{display:flex;align-items:center;justify-content:center}#twe-root .badge{display:inline-flex;align-items:center;justify-content:center;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(0,0,.2,1);transition-duration:.2s;height:20px;font-size:14px;line-height:20px;width:-moz-fit-content;width:fit-content;padding-left:9.008px;padding-right:9.008px;border-radius:var(--rounded-badge, 30.4px);border-width:1px;--tw-border-opacity: 1;border-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)));--tw-bg-opacity: 1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));--tw-text-opacity: 1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)))}@media(hover:hover){#twe-root .checkbox-accent:hover{--tw-border-opacity: 1;border-color:var(--fallback-a,oklch(var(--a)/var(--tw-border-opacity)))}#twe-root .label a:hover{--tw-text-opacity: 1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)))}#twe-root .menu li>*:not(ul,.menu-title,details,.btn):active,#twe-root .menu li>*:not(ul,.menu-title,details,.btn).active,#twe-root .menu li>details>summary:active{--tw-bg-opacity: 1;background-color:var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));--tw-text-opacity: 1;color:var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity)))}#twe-root .tab:hover{--tw-text-opacity: 1}#twe-root .table tr.hover:hover,#twe-root .table tr.hover:nth-child(2n):hover{--tw-bg-opacity: 1;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)))}#twe-root .\\!table tr.hover:hover,#twe-root .\\!table tr.hover:nth-child(2n):hover{--tw-bg-opacity: 1 !important;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)))!important}#twe-root .table-zebra tr.hover:hover,#twe-root .table-zebra tr.hover:nth-child(2n):hover{--tw-bg-opacity: 1;background-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity)))}}#twe-root .btn{display:inline-flex;height:48px;min-height:48px;flex-shrink:0;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;flex-wrap:wrap;align-items:center;justify-content:center;border-radius:var(--rounded-btn, 8px);border-color:transparent;border-color:oklch(var(--btn-color, var(--b2)) / var(--tw-border-opacity));padding-left:16px;padding-right:16px;text-align:center;font-size:14px;line-height:1em;gap:8px;font-weight:600;text-decoration-line:none;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1);border-width:var(--border-btn, 1px);transition-property:color,background-color,border-color,opacity,box-shadow,transform;--tw-text-opacity: 1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);outline-color:var(--fallback-bc,oklch(var(--bc)/1));background-color:oklch(var(--btn-color, var(--b2)) / var(--tw-bg-opacity));--tw-bg-opacity: 1;--tw-border-opacity: 1}#twe-root .btn-disabled,#twe-root .btn[disabled],#twe-root .btn:disabled{pointer-events:none}#twe-root :where(.btn:is(input[type=checkbox])),#twe-root :where(.btn:is(input[type=radio])){width:auto;-webkit-appearance:none;-moz-appearance:none;appearance:none}#twe-root .btn:is(input[type=checkbox]):after,#twe-root .btn:is(input[type=radio]):after{--tw-content: attr(aria-label);content:var(--tw-content)}#twe-root .card{position:relative;display:flex;flex-direction:column;border-radius:var(--rounded-box, 16px)}#twe-root .card:focus{outline:2px solid transparent;outline-offset:2px}#twe-root .card figure{display:flex;align-items:center;justify-content:center}#twe-root .card.image-full{display:grid}#twe-root .card.image-full:before{position:relative;content:"";z-index:10;border-radius:var(--rounded-box, 16px);--tw-bg-opacity: 1;background-color:var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));opacity:.75}#twe-root .card.image-full:before,#twe-root .card.image-full>*{grid-column-start:1;grid-row-start:1}#twe-root .card.image-full>figure img{height:100%;-o-object-fit:cover;object-fit:cover}#twe-root .card.image-full>.card-body{position:relative;z-index:20;--tw-text-opacity: 1;color:var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity)))}#twe-root .checkbox{flex-shrink:0;--chkbg: var(--fallback-bc,oklch(var(--bc)/1));--chkfg: var(--fallback-b1,oklch(var(--b1)/1));height:24px;width:24px;cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:var(--rounded-btn, 8px);border-width:1px;border-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));--tw-border-opacity: .2}#twe-root .divider{display:flex;flex-direction:row;align-items:center;align-self:stretch;margin-top:16px;margin-bottom:16px;height:16px;white-space:nowrap}#twe-root .divider:before,#twe-root .divider:after{height:2px;width:100%;flex-grow:1;--tw-content: "";content:var(--tw-content);background-color:var(--fallback-bc,oklch(var(--bc)/.1))}#twe-root .dropdown{position:relative;display:inline-block}#twe-root .dropdown>*:not(summary):focus{outline:2px solid transparent;outline-offset:2px}#twe-root .dropdown .dropdown-content{position:absolute}#twe-root .dropdown:is(:not(details)) .dropdown-content{visibility:hidden;opacity:0;transform-origin:top;--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(0,0,.2,1);transition-duration:.2s}#twe-root .dropdown-end .dropdown-content{inset-inline-end:0px}#twe-root .dropdown-left .dropdown-content{bottom:auto;inset-inline-end:100%;top:0;transform-origin:right}#twe-root .dropdown-right .dropdown-content{bottom:auto;inset-inline-start:100%;top:0;transform-origin:left}#twe-root .dropdown-bottom .dropdown-content{bottom:auto;top:100%;transform-origin:top}#twe-root .dropdown-top .dropdown-content{bottom:100%;top:auto;transform-origin:bottom}#twe-root .dropdown-end.dropdown-right .dropdown-content,#twe-root .dropdown-end.dropdown-left .dropdown-content{bottom:0;top:auto}#twe-root .dropdown.dropdown-open .dropdown-content,#twe-root .dropdown:not(.dropdown-hover):focus .dropdown-content,#twe-root .dropdown:focus-within .dropdown-content{visibility:visible;opacity:1}@media(hover:hover){#twe-root .dropdown.dropdown-hover:hover .dropdown-content{visibility:visible;opacity:1}#twe-root .btm-nav>*.disabled:hover,#twe-root .btm-nav>*[disabled]:hover{pointer-events:none;--tw-border-opacity: 0;background-color:var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));--tw-bg-opacity: .1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));--tw-text-opacity: .2}#twe-root .btn:hover{--tw-border-opacity: 1;border-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-border-opacity)));--tw-bg-opacity: 1;background-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity)))}@supports (color: color-mix(in oklab,black,black)){#twe-root .btn:hover{background-color:color-mix(in oklab,oklch(var(--btn-color, var(--b2)) / var(--tw-bg-opacity, 1)) 90%,black);border-color:color-mix(in oklab,oklch(var(--btn-color, var(--b2)) / var(--tw-border-opacity, 1)) 90%,black)}}@supports not (color: oklch(0% 0 0)){#twe-root .btn:hover{background-color:var(--btn-color, var(--fallback-b2));border-color:var(--btn-color, var(--fallback-b2))}}#twe-root .btn.glass:hover{--glass-opacity: 25%;--glass-border-opacity: 15%}#twe-root .btn-ghost:hover{border-color:transparent}@supports (color: oklch(0% 0 0)){#twe-root .btn-ghost:hover{background-color:var(--fallback-bc,oklch(var(--bc)/.2))}}#twe-root .btn-outline:hover{--tw-border-opacity: 1;border-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));--tw-bg-opacity: 1;background-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)));--tw-text-opacity: 1;color:var(--fallback-b1,oklch(var(--b1)/var(--tw-text-opacity)))}#twe-root .btn-outline.btn-primary:hover{--tw-text-opacity: 1;color:var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)))}@supports (color: color-mix(in oklab,black,black)){#twe-root .btn-outline.btn-primary:hover{background-color:color-mix(in oklab,var(--fallback-p,oklch(var(--p)/1)) 90%,black);border-color:color-mix(in oklab,var(--fallback-p,oklch(var(--p)/1)) 90%,black)}}#twe-root .btn-outline.btn-secondary:hover{--tw-text-opacity: 1;color:var(--fallback-sc,oklch(var(--sc)/var(--tw-text-opacity)))}@supports (color: color-mix(in oklab,black,black)){#twe-root .btn-outline.btn-secondary:hover{background-color:color-mix(in oklab,var(--fallback-s,oklch(var(--s)/1)) 90%,black);border-color:color-mix(in oklab,var(--fallback-s,oklch(var(--s)/1)) 90%,black)}}#twe-root .btn-outline.btn-accent:hover{--tw-text-opacity: 1;color:var(--fallback-ac,oklch(var(--ac)/var(--tw-text-opacity)))}@supports (color: color-mix(in oklab,black,black)){#twe-root .btn-outline.btn-accent:hover{background-color:color-mix(in oklab,var(--fallback-a,oklch(var(--a)/1)) 90%,black);border-color:color-mix(in oklab,var(--fallback-a,oklch(var(--a)/1)) 90%,black)}}#twe-root .btn-outline.btn-success:hover{--tw-text-opacity: 1;color:var(--fallback-suc,oklch(var(--suc)/var(--tw-text-opacity)))}@supports (color: color-mix(in oklab,black,black)){#twe-root .btn-outline.btn-success:hover{background-color:color-mix(in oklab,var(--fallback-su,oklch(var(--su)/1)) 90%,black);border-color:color-mix(in oklab,var(--fallback-su,oklch(var(--su)/1)) 90%,black)}}#twe-root .btn-outline.btn-info:hover{--tw-text-opacity: 1;color:var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity)))}@supports (color: color-mix(in oklab,black,black)){#twe-root .btn-outline.btn-info:hover{background-color:color-mix(in oklab,var(--fallback-in,oklch(var(--in)/1)) 90%,black);border-color:color-mix(in oklab,var(--fallback-in,oklch(var(--in)/1)) 90%,black)}}#twe-root .btn-outline.btn-warning:hover{--tw-text-opacity: 1;color:var(--fallback-wac,oklch(var(--wac)/var(--tw-text-opacity)))}@supports (color: color-mix(in oklab,black,black)){#twe-root .btn-outline.btn-warning:hover{background-color:color-mix(in oklab,var(--fallback-wa,oklch(var(--wa)/1)) 90%,black);border-color:color-mix(in oklab,var(--fallback-wa,oklch(var(--wa)/1)) 90%,black)}}#twe-root .btn-outline.btn-error:hover{--tw-text-opacity: 1;color:var(--fallback-erc,oklch(var(--erc)/var(--tw-text-opacity)))}@supports (color: color-mix(in oklab,black,black)){#twe-root .btn-outline.btn-error:hover{background-color:color-mix(in oklab,var(--fallback-er,oklch(var(--er)/1)) 90%,black);border-color:color-mix(in oklab,var(--fallback-er,oklch(var(--er)/1)) 90%,black)}}#twe-root .btn-disabled:hover,#twe-root .btn[disabled]:hover,#twe-root .btn:disabled:hover{--tw-border-opacity: 0;background-color:var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));--tw-bg-opacity: .2;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));--tw-text-opacity: .2}@supports (color: color-mix(in oklab,black,black)){#twe-root .btn:is(input[type=checkbox]:checked):hover,#twe-root .btn:is(input[type=radio]:checked):hover{background-color:color-mix(in oklab,var(--fallback-p,oklch(var(--p)/1)) 90%,black);border-color:color-mix(in oklab,var(--fallback-p,oklch(var(--p)/1)) 90%,black)}}#twe-root .dropdown.dropdown-hover:hover .dropdown-content{--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}#twe-root :where(.menu li:not(.menu-title,.disabled)>*:not(ul,details,.menu-title)):not(.active,.btn):hover,#twe-root :where(.menu li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.active,.btn):hover{cursor:pointer;outline:2px solid transparent;outline-offset:2px}@supports (color: oklch(0% 0 0)){#twe-root :where(.menu li:not(.menu-title,.disabled)>*:not(ul,details,.menu-title)):not(.active,.btn):hover,#twe-root :where(.menu li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.active,.btn):hover{background-color:var(--fallback-bc,oklch(var(--bc)/.1))}}#twe-root .tab[disabled],#twe-root .tab[disabled]:hover{cursor:not-allowed;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));--tw-text-opacity: .2}}#twe-root .dropdown:is(details) summary::-webkit-details-marker{display:none}#twe-root .file-input{height:48px;flex-shrink:1;padding-inline-end:16px;font-size:16px;line-height:2;line-height:24px;overflow:hidden;border-radius:var(--rounded-btn, 8px);border-width:1px;border-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));--tw-border-opacity: 0;--tw-bg-opacity: 1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)))}#twe-root .file-input::file-selector-button{margin-inline-end:16px;display:inline-flex;height:100%;flex-shrink:0;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;flex-wrap:wrap;align-items:center;justify-content:center;padding-left:16px;padding-right:16px;text-align:center;font-size:14px;line-height:20px;line-height:1em;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(0,0,.2,1);transition-duration:.2s;border-style:solid;--tw-border-opacity: 1;border-color:var(--fallback-n,oklch(var(--n)/var(--tw-border-opacity)));--tw-bg-opacity: 1;background-color:var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));font-weight:600;text-transform:uppercase;--tw-text-opacity: 1;color:var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity)));text-decoration-line:none;border-width:var(--border-btn, 1px);animation:button-pop var(--animation-btn, .25s) ease-out}#twe-root .footer{display:grid;width:100%;grid-auto-flow:row;place-items:start;-moz-column-gap:16px;column-gap:16px;row-gap:40px;font-size:14px;line-height:20px}#twe-root .footer>*{display:grid;place-items:start;gap:8px}@media(min-width:48rem){#twe-root .footer{grid-auto-flow:column}#twe-root .footer-center{grid-auto-flow:row dense}}#twe-root .form-control{display:flex;flex-direction:column}#twe-root .label{display:flex;-webkit-user-select:none;-moz-user-select:none;user-select:none;align-items:center;justify-content:space-between;padding:8px 4px}#twe-root .input{flex-shrink:1;-webkit-appearance:none;-moz-appearance:none;appearance:none;height:48px;padding-left:16px;padding-right:16px;font-size:16px;line-height:2;line-height:24px;border-radius:var(--rounded-btn, 8px);border-width:1px;border-color:transparent;--tw-bg-opacity: 1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)))}#twe-root .input[type=number]::-webkit-inner-spin-button,#twe-root .input-md[type=number]::-webkit-inner-spin-button{margin-top:-16px;margin-bottom:-16px;margin-inline-end:-16px}#twe-root .input-xs[type=number]::-webkit-inner-spin-button{margin-top:-4px;margin-bottom:-4px;margin-inline-end:-0px}#twe-root .input-sm[type=number]::-webkit-inner-spin-button{margin-top:0;margin-bottom:0;margin-inline-end:-0px}#twe-root .join{display:inline-flex;align-items:stretch;border-radius:var(--rounded-btn, 8px)}#twe-root .join :where(.join-item){border-start-end-radius:0;border-end-end-radius:0;border-end-start-radius:0;border-start-start-radius:0}#twe-root .join .join-item:not(:first-child):not(:last-child),#twe-root .join *:not(:first-child):not(:last-child) .join-item{border-start-end-radius:0;border-end-end-radius:0;border-end-start-radius:0;border-start-start-radius:0}#twe-root .join .join-item:first-child:not(:last-child),#twe-root .join *:first-child:not(:last-child) .join-item{border-start-end-radius:0;border-end-end-radius:0}#twe-root .join .dropdown .join-item:first-child:not(:last-child),#twe-root .join *:first-child:not(:last-child) .dropdown .join-item{border-start-end-radius:inherit;border-end-end-radius:inherit}#twe-root .join :where(.join-item:first-child:not(:last-child)),#twe-root .join :where(*:first-child:not(:last-child) .join-item){border-end-start-radius:inherit;border-start-start-radius:inherit}#twe-root .join .join-item:last-child:not(:first-child),#twe-root .join *:last-child:not(:first-child) .join-item{border-end-start-radius:0;border-start-start-radius:0}#twe-root .join :where(.join-item:last-child:not(:first-child)),#twe-root .join :where(*:last-child:not(:first-child) .join-item){border-start-end-radius:inherit;border-end-end-radius:inherit}@supports not selector(:has(*)){#twe-root :where(.join *){border-radius:inherit}}@supports selector(:has(*)){#twe-root :where(.join *:has(.join-item)){border-radius:inherit}}#twe-root .link{cursor:pointer;text-decoration-line:underline}#twe-root .menu{display:flex;flex-direction:column;flex-wrap:wrap;font-size:14px;line-height:20px;padding:8px}#twe-root .menu :where(li ul){position:relative;white-space:nowrap;margin-inline-start:16px;padding-inline-start:8px}#twe-root .menu :where(li:not(.menu-title)>*:not(ul,details,.menu-title,.btn)),#twe-root .menu :where(li:not(.menu-title)>details>summary:not(.menu-title)){display:grid;grid-auto-flow:column;align-content:flex-start;align-items:center;gap:8px;grid-auto-columns:minmax(auto,max-content) auto max-content;-webkit-user-select:none;-moz-user-select:none;user-select:none}#twe-root .menu li.disabled{cursor:not-allowed;-webkit-user-select:none;-moz-user-select:none;user-select:none;color:var(--fallback-bc,oklch(var(--bc)/.3))}#twe-root .menu :where(li>.menu-dropdown:not(.menu-dropdown-show)){display:none}#twe-root :where(.menu li){position:relative;display:flex;flex-shrink:0;flex-direction:column;flex-wrap:wrap;align-items:stretch}#twe-root :where(.menu li) .badge{justify-self:end}#twe-root .modal{pointer-events:none;position:fixed;top:0;right:0;bottom:0;left:0;margin:0;display:grid;height:100%;max-height:none;width:100%;max-width:none;justify-items:center;padding:0;opacity:0;overscroll-behavior:contain;z-index:999;background-color:transparent;color:inherit;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1);transition-property:transform,opacity,visibility;overflow-y:hidden}#twe-root :where(.modal){align-items:center}#twe-root .modal-box{max-height:calc(100vh - 5em);grid-column-start:1;grid-row-start:1;width:91.666667%;max-width:512px;--tw-scale-x: .9;--tw-scale-y: .9;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));border-bottom-right-radius:var(--rounded-box, 16px);border-bottom-left-radius:var(--rounded-box, 16px);border-top-left-radius:var(--rounded-box, 16px);border-top-right-radius:var(--rounded-box, 16px);--tw-bg-opacity: 1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));padding:24px;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(0,0,.2,1);transition-duration:.2s;box-shadow:#00000040 0 25px 50px -12px;overflow-y:auto;overscroll-behavior:contain}#twe-root .modal-open,#twe-root .modal:target,#twe-root .modal-toggle:checked+.modal,#twe-root .modal[open]{pointer-events:auto;visibility:visible;opacity:1}#twe-root:has(:is(.modal-open,.modal:target,.modal-toggle:checked+.modal,.modal[open])){overflow:hidden;scrollbar-gutter:stable}#twe-root .progress{position:relative;width:100%;-webkit-appearance:none;-moz-appearance:none;appearance:none;overflow:hidden;height:8px;border-radius:var(--rounded-box, 16px);background-color:var(--fallback-bc,oklch(var(--bc)/.2))}#twe-root .radio{flex-shrink:0;--chkbg: var(--bc);height:24px;width:24px;cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:9999px;border-width:1px;border-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));--tw-border-opacity: .2}#twe-root .range{height:24px;width:100%;cursor:pointer;-moz-appearance:none;appearance:none;-webkit-appearance:none;--range-shdw: var(--fallback-bc,oklch(var(--bc)/1));overflow:hidden;border-radius:var(--rounded-box, 16px);background-color:transparent}#twe-root .range:focus{outline:none}#twe-root .select{display:inline-flex;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;-webkit-appearance:none;-moz-appearance:none;appearance:none;height:48px;min-height:48px;padding-inline-start:16px;padding-inline-end:40px;font-size:14px;line-height:20px;line-height:2;border-radius:var(--rounded-btn, 8px);border-width:1px;border-color:transparent;--tw-bg-opacity: 1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));background-image:linear-gradient(45deg,transparent 50%,currentColor 50%),linear-gradient(135deg,currentColor 50%,transparent 50%);background-position:calc(100% - 20px) calc(1px + 50%),calc(100% - 16.1px) calc(1px + 50%);background-size:4px 4px,4px 4px;background-repeat:no-repeat}#twe-root .select[multiple]{height:auto}#twe-root .stack{display:inline-grid;place-items:center;align-items:flex-end}#twe-root .stack>*{grid-column-start:1;grid-row-start:1;transform:translateY(10%) scale(.9);z-index:1;width:100%;opacity:.6}#twe-root .stack>*:nth-child(2){transform:translateY(5%) scale(.95);z-index:2;opacity:.8}#twe-root .stack>*:nth-child(1){transform:translateY(0) scale(1);z-index:3;opacity:1}#twe-root .stats{display:inline-grid;border-radius:var(--rounded-box, 16px);--tw-bg-opacity: 1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));--tw-text-opacity: 1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)))}#twe-root :where(.stats){grid-auto-flow:column;overflow-x:auto}#twe-root .tabs{display:grid;align-items:flex-end}#twe-root .tabs-lifted:has(.tab-content[class^=rounded-]) .tab:first-child:not(:is(.tab-active,[aria-selected=true])),#twe-root .tabs-lifted:has(.tab-content[class*=" rounded-"]) .tab:first-child:not(:is(.tab-active,[aria-selected=true])){border-bottom-color:transparent}#twe-root .tab{position:relative;grid-row-start:1;display:inline-flex;height:32px;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;-webkit-appearance:none;-moz-appearance:none;appearance:none;flex-wrap:wrap;align-items:center;justify-content:center;text-align:center;font-size:14px;line-height:20px;line-height:2;--tab-padding: 16px;--tw-text-opacity: .5;--tab-color: var(--fallback-bc,oklch(var(--bc)/1));--tab-bg: var(--fallback-b1,oklch(var(--b1)/1));--tab-border-color: var(--fallback-b3,oklch(var(--b3)/1));color:var(--tab-color);padding-inline-start:var(--tab-padding, 16px);padding-inline-end:var(--tab-padding, 16px)}#twe-root .tab:is(input[type=radio]){width:auto;border-bottom-right-radius:0;border-bottom-left-radius:0}#twe-root .tab:is(input[type=radio]):after{--tw-content: attr(aria-label);content:var(--tw-content)}#twe-root .tab:not(input):empty{cursor:default;grid-column-start:span 9999}#twe-root input.tab:checked+.tab-content,#twe-root :is(.tab-active,[aria-selected=true])+.tab-content{display:block}#twe-root .\\!table{position:relative!important;width:100%!important;border-radius:var(--rounded-box, 16px)!important;text-align:left!important;font-size:14px!important;line-height:20px!important}#twe-root .table{position:relative;width:100%;border-radius:var(--rounded-box, 16px);text-align:left;font-size:14px;line-height:20px}#twe-root .\\!table :where(.table-pin-rows thead tr){position:sticky!important;top:0!important;z-index:1!important;--tw-bg-opacity: 1 !important;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)))!important}#twe-root .table :where(.table-pin-rows thead tr){position:sticky;top:0;z-index:1;--tw-bg-opacity: 1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)))}#twe-root .\\!table :where(.table-pin-rows tfoot tr){position:sticky!important;bottom:0!important;z-index:1!important;--tw-bg-opacity: 1 !important;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)))!important}#twe-root .table :where(.table-pin-rows tfoot tr){position:sticky;bottom:0;z-index:1;--tw-bg-opacity: 1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)))}#twe-root .\\!table :where(.table-pin-cols tr th){position:sticky!important;left:0!important;right:0!important;--tw-bg-opacity: 1 !important;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)))!important}#twe-root .table :where(.table-pin-cols tr th){position:sticky;left:0;right:0;--tw-bg-opacity: 1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)))}#twe-root .table-zebra tbody tr:nth-child(2n) :where(.table-pin-cols tr th){--tw-bg-opacity: 1;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)))}#twe-root .textarea{min-height:48px;flex-shrink:1;padding:8px 16px;font-size:14px;line-height:20px;line-height:2;border-radius:var(--rounded-btn, 8px);border-width:1px;border-color:transparent;--tw-bg-opacity: 1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)))}#twe-root .\\!timeline{position:relative!important;display:flex!important}#twe-root .timeline{position:relative;display:flex}#twe-root :where(.\\!timeline>li){position:relative!important;display:grid!important;flex-shrink:0!important;align-items:center!important;grid-template-rows:var(--timeline-row-start, minmax(0, 1fr)) auto var( --timeline-row-end, minmax(0, 1fr) )!important;grid-template-columns:var(--timeline-col-start, minmax(0, 1fr)) auto var( --timeline-col-end, minmax(0, 1fr) )!important}#twe-root :where(.timeline>li){position:relative;display:grid;flex-shrink:0;align-items:center;grid-template-rows:var(--timeline-row-start, minmax(0, 1fr)) auto var( --timeline-row-end, minmax(0, 1fr) );grid-template-columns:var(--timeline-col-start, minmax(0, 1fr)) auto var( --timeline-col-end, minmax(0, 1fr) )}#twe-root .\\!timeline>li>hr{width:100%!important;border-width:0px!important}#twe-root .timeline>li>hr{width:100%;border-width:0px}#twe-root :where(.\\!timeline>li>hr):first-child{grid-column-start:1!important;grid-row-start:2!important}#twe-root :where(.timeline>li>hr):first-child{grid-column-start:1;grid-row-start:2}#twe-root :where(.\\!timeline>li>hr):last-child{grid-column-start:3!important;grid-column-end:none!important;grid-row-start:2!important;grid-row-end:auto!important}#twe-root :where(.timeline>li>hr):last-child{grid-column-start:3;grid-column-end:none;grid-row-start:2;grid-row-end:auto}#twe-root .toggle{flex-shrink:0;--tglbg: var(--fallback-b1,oklch(var(--b1)/1));--handleoffset: 24px;--handleoffsetcalculator: calc(var(--handleoffset) * -1);--togglehandleborder: 0 0;height:24px;width:48px;cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:var(--rounded-badge, 30.4px);border-width:1px;border-color:currentColor;background-color:currentColor;color:var(--fallback-bc,oklch(var(--bc)/.5));transition:background,box-shadow var(--animation-input, .2s) ease-out;box-shadow:var(--handleoffsetcalculator) 0 0 2px var(--tglbg) inset,0 0 0 2px var(--tglbg) inset,var(--togglehandleborder)}#twe-root .alert-error{border-color:var(--fallback-er,oklch(var(--er)/.2));--tw-text-opacity: 1;color:var(--fallback-erc,oklch(var(--erc)/var(--tw-text-opacity)));--alert-bg: var(--fallback-er,oklch(var(--er)/1));--alert-bg-mix: var(--fallback-b1,oklch(var(--b1)/1))}#twe-root .avatar-group :where(.avatar){overflow:hidden;border-radius:9999px;border-width:4px;--tw-border-opacity: 1;border-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-border-opacity)))}#twe-root .badge-outline{border-color:currentColor;--tw-border-opacity: .5;background-color:transparent;color:currentColor}#twe-root .badge-outline.badge-neutral{--tw-text-opacity: 1;color:var(--fallback-n,oklch(var(--n)/var(--tw-text-opacity)))}#twe-root .badge-outline.badge-primary{--tw-text-opacity: 1;color:var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity)))}#twe-root .badge-outline.badge-secondary{--tw-text-opacity: 1;color:var(--fallback-s,oklch(var(--s)/var(--tw-text-opacity)))}#twe-root .badge-outline.badge-accent{--tw-text-opacity: 1;color:var(--fallback-a,oklch(var(--a)/var(--tw-text-opacity)))}#twe-root .badge-outline.badge-info{--tw-text-opacity: 1;color:var(--fallback-in,oklch(var(--in)/var(--tw-text-opacity)))}#twe-root .badge-outline.badge-success{--tw-text-opacity: 1;color:var(--fallback-su,oklch(var(--su)/var(--tw-text-opacity)))}#twe-root .badge-outline.badge-warning{--tw-text-opacity: 1;color:var(--fallback-wa,oklch(var(--wa)/var(--tw-text-opacity)))}#twe-root .badge-outline.badge-error{--tw-text-opacity: 1;color:var(--fallback-er,oklch(var(--er)/var(--tw-text-opacity)))}#twe-root .btm-nav>*:where(.active){border-top-width:2px;--tw-bg-opacity: 1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)))}#twe-root .btm-nav>*.disabled,#twe-root .btm-nav>*[disabled]{pointer-events:none;--tw-border-opacity: 0;background-color:var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));--tw-bg-opacity: .1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));--tw-text-opacity: .2}#twe-root .btm-nav>* .label{font-size:16px;line-height:24px}@media(prefers-reduced-motion:no-preference){#twe-root .btn{animation:button-pop var(--animation-btn, .25s) ease-out}}#twe-root .btn:active:hover,#twe-root .btn:active:focus{animation:button-pop 0s ease-out;transform:scale(var(--btn-focus-scale, .97))}@supports not (color: oklch(0% 0 0)){#twe-root .btn{background-color:var(--btn-color, var(--fallback-b2));border-color:var(--btn-color, var(--fallback-b2))}#twe-root .btn-primary{--btn-color: var(--fallback-p)}#twe-root .btn-secondary{--btn-color: var(--fallback-s)}#twe-root .btn-accent{--btn-color: var(--fallback-a)}#twe-root .btn-neutral{--btn-color: var(--fallback-n)}#twe-root .btn-info{--btn-color: var(--fallback-in)}#twe-root .btn-warning{--btn-color: var(--fallback-wa)}#twe-root .btn-error{--btn-color: var(--fallback-er)}}@supports (color: color-mix(in oklab,black,black)){#twe-root .btn-outline.btn-primary.btn-active{background-color:color-mix(in oklab,var(--fallback-p,oklch(var(--p)/1)) 90%,black);border-color:color-mix(in oklab,var(--fallback-p,oklch(var(--p)/1)) 90%,black)}#twe-root .btn-outline.btn-secondary.btn-active{background-color:color-mix(in oklab,var(--fallback-s,oklch(var(--s)/1)) 90%,black);border-color:color-mix(in oklab,var(--fallback-s,oklch(var(--s)/1)) 90%,black)}#twe-root .btn-outline.btn-accent.btn-active{background-color:color-mix(in oklab,var(--fallback-a,oklch(var(--a)/1)) 90%,black);border-color:color-mix(in oklab,var(--fallback-a,oklch(var(--a)/1)) 90%,black)}#twe-root .btn-outline.btn-success.btn-active{background-color:color-mix(in oklab,var(--fallback-su,oklch(var(--su)/1)) 90%,black);border-color:color-mix(in oklab,var(--fallback-su,oklch(var(--su)/1)) 90%,black)}#twe-root .btn-outline.btn-info.btn-active{background-color:color-mix(in oklab,var(--fallback-in,oklch(var(--in)/1)) 90%,black);border-color:color-mix(in oklab,var(--fallback-in,oklch(var(--in)/1)) 90%,black)}#twe-root .btn-outline.btn-warning.btn-active{background-color:color-mix(in oklab,var(--fallback-wa,oklch(var(--wa)/1)) 90%,black);border-color:color-mix(in oklab,var(--fallback-wa,oklch(var(--wa)/1)) 90%,black)}#twe-root .btn-outline.btn-error.btn-active{background-color:color-mix(in oklab,var(--fallback-er,oklch(var(--er)/1)) 90%,black);border-color:color-mix(in oklab,var(--fallback-er,oklch(var(--er)/1)) 90%,black)}}#twe-root .btn:focus-visible{outline-style:solid;outline-width:2px;outline-offset:2px}#twe-root .btn-primary{--tw-text-opacity: 1;color:var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)));outline-color:var(--fallback-p,oklch(var(--p)/1))}@supports (color: oklch(0% 0 0)){#twe-root .btn-primary{--btn-color: var(--p)}#twe-root .btn-secondary{--btn-color: var(--s)}#twe-root .btn-accent{--btn-color: var(--a)}#twe-root .btn-neutral{--btn-color: var(--n)}#twe-root .btn-info{--btn-color: var(--in)}#twe-root .btn-warning{--btn-color: var(--wa)}#twe-root .btn-error{--btn-color: var(--er)}}#twe-root .btn-secondary{--tw-text-opacity: 1;color:var(--fallback-sc,oklch(var(--sc)/var(--tw-text-opacity)));outline-color:var(--fallback-s,oklch(var(--s)/1))}#twe-root .btn-accent{--tw-text-opacity: 1;color:var(--fallback-ac,oklch(var(--ac)/var(--tw-text-opacity)));outline-color:var(--fallback-a,oklch(var(--a)/1))}#twe-root .btn-neutral{--tw-text-opacity: 1;color:var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity)));outline-color:var(--fallback-n,oklch(var(--n)/1))}#twe-root .btn-info{--tw-text-opacity: 1;color:var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity)));outline-color:var(--fallback-in,oklch(var(--in)/1))}#twe-root .btn-warning{--tw-text-opacity: 1;color:var(--fallback-wac,oklch(var(--wac)/var(--tw-text-opacity)));outline-color:var(--fallback-wa,oklch(var(--wa)/1))}#twe-root .btn-error{--tw-text-opacity: 1;color:var(--fallback-erc,oklch(var(--erc)/var(--tw-text-opacity)));outline-color:var(--fallback-er,oklch(var(--er)/1))}#twe-root .btn.glass{--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);outline-color:currentColor}#twe-root .btn.glass.btn-active{--glass-opacity: 25%;--glass-border-opacity: 15%}#twe-root .btn-ghost{border-width:1px;border-color:transparent;background-color:transparent;color:currentColor;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);outline-color:currentColor}#twe-root .btn-ghost.btn-active{border-color:transparent;background-color:var(--fallback-bc,oklch(var(--bc)/.2))}#twe-root .btn-outline{border-color:currentColor;background-color:transparent;--tw-text-opacity: 1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}#twe-root .btn-outline.btn-active{--tw-border-opacity: 1;border-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));--tw-bg-opacity: 1;background-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)));--tw-text-opacity: 1;color:var(--fallback-b1,oklch(var(--b1)/var(--tw-text-opacity)))}#twe-root .btn-outline.btn-primary{--tw-text-opacity: 1;color:var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity)))}#twe-root .btn-outline.btn-primary.btn-active{--tw-text-opacity: 1;color:var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)))}#twe-root .btn-outline.btn-secondary{--tw-text-opacity: 1;color:var(--fallback-s,oklch(var(--s)/var(--tw-text-opacity)))}#twe-root .btn-outline.btn-secondary.btn-active{--tw-text-opacity: 1;color:var(--fallback-sc,oklch(var(--sc)/var(--tw-text-opacity)))}#twe-root .btn-outline.btn-accent{--tw-text-opacity: 1;color:var(--fallback-a,oklch(var(--a)/var(--tw-text-opacity)))}#twe-root .btn-outline.btn-accent.btn-active{--tw-text-opacity: 1;color:var(--fallback-ac,oklch(var(--ac)/var(--tw-text-opacity)))}#twe-root .btn-outline.btn-success{--tw-text-opacity: 1;color:var(--fallback-su,oklch(var(--su)/var(--tw-text-opacity)))}#twe-root .btn-outline.btn-success.btn-active{--tw-text-opacity: 1;color:var(--fallback-suc,oklch(var(--suc)/var(--tw-text-opacity)))}#twe-root .btn-outline.btn-info{--tw-text-opacity: 1;color:var(--fallback-in,oklch(var(--in)/var(--tw-text-opacity)))}#twe-root .btn-outline.btn-info.btn-active{--tw-text-opacity: 1;color:var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity)))}#twe-root .btn-outline.btn-warning{--tw-text-opacity: 1;color:var(--fallback-wa,oklch(var(--wa)/var(--tw-text-opacity)))}#twe-root .btn-outline.btn-warning.btn-active{--tw-text-opacity: 1;color:var(--fallback-wac,oklch(var(--wac)/var(--tw-text-opacity)))}#twe-root .btn-outline.btn-error{--tw-text-opacity: 1;color:var(--fallback-er,oklch(var(--er)/var(--tw-text-opacity)))}#twe-root .btn-outline.btn-error.btn-active{--tw-text-opacity: 1;color:var(--fallback-erc,oklch(var(--erc)/var(--tw-text-opacity)))}#twe-root .btn.btn-disabled,#twe-root .btn[disabled],#twe-root .btn:disabled{--tw-border-opacity: 0;background-color:var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));--tw-bg-opacity: .2;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));--tw-text-opacity: .2}#twe-root .btn:is(input[type=checkbox]:checked),#twe-root .btn:is(input[type=radio]:checked){--tw-border-opacity: 1;border-color:var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity)));--tw-bg-opacity: 1;background-color:var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity)));--tw-text-opacity: 1;color:var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)))}#twe-root .btn:is(input[type=checkbox]:checked):focus-visible,#twe-root .btn:is(input[type=radio]:checked):focus-visible{outline-color:var(--fallback-p,oklch(var(--p)/1))}@keyframes button-pop{0%{transform:scale(var(--btn-focus-scale, .98))}40%{transform:scale(1.02)}to{transform:scale(1)}}#twe-root .card :where(figure:first-child){overflow:hidden;border-start-start-radius:inherit;border-start-end-radius:inherit;border-end-start-radius:unset;border-end-end-radius:unset}#twe-root .card :where(figure:last-child){overflow:hidden;border-start-start-radius:unset;border-start-end-radius:unset;border-end-start-radius:inherit;border-end-end-radius:inherit}#twe-root .card:focus-visible{outline:2px solid currentColor;outline-offset:2px}#twe-root .card.bordered{border-width:1px;--tw-border-opacity: 1;border-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)))}#twe-root .card.compact .card-body{padding:16px;font-size:14px;line-height:20px}#twe-root .card.image-full :where(figure){overflow:hidden;border-radius:inherit}#twe-root .checkbox:focus{box-shadow:none}#twe-root .checkbox:focus-visible{outline-style:solid;outline-width:2px;outline-offset:2px;outline-color:var(--fallback-bc,oklch(var(--bc)/1))}#twe-root .checkbox:disabled{border-width:0px;cursor:not-allowed;border-color:transparent;--tw-bg-opacity: 1;background-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)));opacity:.2}#twe-root .checkbox:checked,#twe-root .checkbox[aria-checked=true]{background-repeat:no-repeat;animation:checkmark var(--animation-input, .2s) ease-out;background-color:var(--chkbg);background-image:linear-gradient(-45deg,transparent 65%,var(--chkbg) 65.99%),linear-gradient(45deg,transparent 75%,var(--chkbg) 75.99%),linear-gradient(-45deg,var(--chkbg) 40%,transparent 40.99%),linear-gradient(45deg,var(--chkbg) 30%,var(--chkfg) 30.99%,var(--chkfg) 40%,transparent 40.99%),linear-gradient(-45deg,var(--chkfg) 50%,var(--chkbg) 50.99%)}#twe-root .checkbox:indeterminate{--tw-bg-opacity: 1;background-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)));background-repeat:no-repeat;animation:checkmark var(--animation-input, .2s) ease-out;background-image:linear-gradient(90deg,transparent 80%,var(--chkbg) 80%),linear-gradient(-90deg,transparent 80%,var(--chkbg) 80%),linear-gradient(0deg,var(--chkbg) 43%,var(--chkfg) 43%,var(--chkfg) 57%,var(--chkbg) 57%)}#twe-root .checkbox-accent{--chkbg: var(--fallback-a,oklch(var(--a)/1));--chkfg: var(--fallback-ac,oklch(var(--ac)/1));--tw-border-opacity: 1;border-color:var(--fallback-a,oklch(var(--a)/var(--tw-border-opacity)))}#twe-root .checkbox-accent:focus-visible{outline-color:var(--fallback-a,oklch(var(--a)/1))}#twe-root .checkbox-accent:checked,#twe-root .checkbox-accent[aria-checked=true]{--tw-border-opacity: 1;border-color:var(--fallback-a,oklch(var(--a)/var(--tw-border-opacity)));--tw-bg-opacity: 1;background-color:var(--fallback-a,oklch(var(--a)/var(--tw-bg-opacity)));--tw-text-opacity: 1;color:var(--fallback-ac,oklch(var(--ac)/var(--tw-text-opacity)))}@keyframes checkmark{0%{background-position-y:5px}50%{background-position-y:-2px}to{background-position-y:0}}#twe-root .divider:not(:empty){gap:16px}#twe-root .dropdown.dropdown-open .dropdown-content,#twe-root .dropdown:focus .dropdown-content,#twe-root .dropdown:focus-within .dropdown-content{--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}#twe-root .file-input-bordered{--tw-border-opacity: .2}#twe-root .file-input:focus{outline-style:solid;outline-width:2px;outline-offset:2px;outline-color:var(--fallback-bc,oklch(var(--bc)/.2))}#twe-root .file-input-disabled,#twe-root .file-input[disabled]{cursor:not-allowed;--tw-border-opacity: 1;border-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)));--tw-bg-opacity: 1;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)));--tw-text-opacity: .2}#twe-root .file-input-disabled::-moz-placeholder,#twe-root .file-input[disabled]::-moz-placeholder{color:var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity)));--tw-placeholder-opacity: .2}#twe-root .file-input-disabled::placeholder,#twe-root .file-input[disabled]::placeholder{color:var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity)));--tw-placeholder-opacity: .2}#twe-root .file-input-disabled::file-selector-button,#twe-root .file-input[disabled]::file-selector-button{--tw-border-opacity: 0;background-color:var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));--tw-bg-opacity: .2;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));--tw-text-opacity: .2}#twe-root .label-text{font-size:14px;line-height:20px;--tw-text-opacity: 1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)))}#twe-root .input input{--tw-bg-opacity: 1;background-color:var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity)));background-color:transparent}#twe-root .input input:focus{outline:2px solid transparent;outline-offset:2px}#twe-root .input[list]::-webkit-calendar-picker-indicator{line-height:1em}#twe-root .input-bordered{border-color:var(--fallback-bc,oklch(var(--bc)/.2))}#twe-root .input:focus,#twe-root .input:focus-within{box-shadow:none;border-color:var(--fallback-bc,oklch(var(--bc)/.2));outline-style:solid;outline-width:2px;outline-offset:2px;outline-color:var(--fallback-bc,oklch(var(--bc)/.2))}#twe-root .input:has(>input[disabled]),#twe-root .input-disabled,#twe-root .input:disabled,#twe-root .input[disabled]{cursor:not-allowed;--tw-border-opacity: 1;border-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)));--tw-bg-opacity: 1;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)));color:var(--fallback-bc,oklch(var(--bc)/.4))}#twe-root .input:has(>input[disabled])::-moz-placeholder,#twe-root .input-disabled::-moz-placeholder,#twe-root .input:disabled::-moz-placeholder,#twe-root .input[disabled]::-moz-placeholder{color:var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity)));--tw-placeholder-opacity: .2}#twe-root .input:has(>input[disabled])::placeholder,#twe-root .input-disabled::placeholder,#twe-root .input:disabled::placeholder,#twe-root .input[disabled]::placeholder{color:var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity)));--tw-placeholder-opacity: .2}#twe-root .input:has(>input[disabled])>input[disabled]{cursor:not-allowed}#twe-root .input::-webkit-date-and-time-value{text-align:inherit}#twe-root .join>:where(*:not(:first-child)){margin-top:0;margin-bottom:0;margin-inline-start:-1px}#twe-root .join>:where(*:not(:first-child)):is(.btn){margin-inline-start:calc(var(--border-btn) * -1)}#twe-root .join-item:focus{isolation:isolate}#twe-root .link-primary{--tw-text-opacity: 1;color:var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity)))}@supports (color:color-mix(in oklab,black,black)){@media(hover:hover){#twe-root .link-primary:hover{color:color-mix(in oklab,var(--fallback-p,oklch(var(--p)/1)) 80%,black)}}}#twe-root .link:focus{outline:2px solid transparent;outline-offset:2px}#twe-root .link:focus-visible{outline:2px solid currentColor;outline-offset:2px}#twe-root .loading{pointer-events:none;display:inline-block;aspect-ratio:1 / 1;width:24px;background-color:currentColor;-webkit-mask-size:100%;mask-size:100%;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-position:center;mask-position:center;-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E")}#twe-root .loading-spinner{-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E")}#twe-root .loading-xs{width:16px}#twe-root :where(.menu li:empty){--tw-bg-opacity: 1;background-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)));opacity:.1;margin:8px 16px;height:1px}#twe-root .menu :where(li ul):before{position:absolute;bottom:12px;inset-inline-start:0px;top:12px;width:1px;--tw-bg-opacity: 1;background-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)));opacity:.1;content:""}#twe-root .menu :where(li:not(.menu-title)>*:not(ul,details,.menu-title,.btn)),#twe-root .menu :where(li:not(.menu-title)>details>summary:not(.menu-title)){border-radius:var(--rounded-btn, 8px);padding:8px 16px;text-align:start;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(0,0,.2,1);transition-duration:.2s;text-wrap:balance}#twe-root :where(.menu li:not(.menu-title,.disabled)>*:not(ul,details,.menu-title)):not(summary,.active,.btn).focus,#twe-root :where(.menu li:not(.menu-title,.disabled)>*:not(ul,details,.menu-title)):not(summary,.active,.btn):focus,#twe-root :where(.menu li:not(.menu-title,.disabled)>*:not(ul,details,.menu-title)):is(summary):not(.active,.btn):focus-visible,#twe-root :where(.menu li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(summary,.active,.btn).focus,#twe-root :where(.menu li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(summary,.active,.btn):focus,#twe-root :where(.menu li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):is(summary):not(.active,.btn):focus-visible{cursor:pointer;background-color:var(--fallback-bc,oklch(var(--bc)/.1));--tw-text-opacity: 1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));outline:2px solid transparent;outline-offset:2px}#twe-root .menu li>*:not(ul,.menu-title,details,.btn):active,#twe-root .menu li>*:not(ul,.menu-title,details,.btn).active,#twe-root .menu li>details>summary:active{--tw-bg-opacity: 1;background-color:var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));--tw-text-opacity: 1;color:var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity)))}#twe-root .menu :where(li>details>summary)::-webkit-details-marker{display:none}#twe-root .menu :where(li>details>summary):after,#twe-root .menu :where(li>.menu-dropdown-toggle):after{justify-self:end;display:block;margin-top:-8px;height:8px;width:8px;transform:rotate(45deg);transition-property:transform,margin-top;transition-duration:.3s;transition-timing-function:cubic-bezier(.4,0,.2,1);content:"";transform-origin:75% 75%;box-shadow:2px 2px;pointer-events:none}#twe-root .menu :where(li>details[open]>summary):after,#twe-root .menu :where(li>.menu-dropdown-toggle.menu-dropdown-show):after{transform:rotate(225deg);margin-top:0}#twe-root .mockup-phone .display{overflow:hidden;border-radius:40px;margin-top:-25px}#twe-root .mockup-browser .mockup-browser-toolbar .input{position:relative;margin-left:auto;margin-right:auto;display:block;height:28px;width:384px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;--tw-bg-opacity: 1;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)));padding-left:32px;direction:ltr}#twe-root .mockup-browser .mockup-browser-toolbar .input:before{content:"";position:absolute;left:8px;top:50%;aspect-ratio:1 / 1;height:12px;--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));border-radius:9999px;border-width:2px;border-color:currentColor;opacity:.6}#twe-root .mockup-browser .mockup-browser-toolbar .input:after{content:"";position:absolute;left:20px;top:50%;height:8px;--tw-translate-y: 25%;--tw-rotate: -45deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));border-radius:9999px;border-width:1px;border-color:currentColor;opacity:.6}#twe-root .modal:not(dialog:not(.modal-open)),#twe-root .modal::backdrop{background-color:#0006;animation:modal-pop .2s ease-out}#twe-root .modal-backdrop{z-index:-1;grid-column-start:1;grid-row-start:1;display:grid;align-self:stretch;justify-self:stretch;color:transparent}#twe-root .modal-open .modal-box,#twe-root .modal-toggle:checked+.modal .modal-box,#twe-root .modal:target .modal-box,#twe-root .modal[open] .modal-box{--tw-translate-y: 0px;--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes modal-pop{0%{opacity:0}}#twe-root .progress::-moz-progress-bar{border-radius:var(--rounded-box, 16px);background-color:currentColor}#twe-root .progress-primary::-moz-progress-bar{border-radius:var(--rounded-box, 16px);--tw-bg-opacity: 1;background-color:var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity)))}#twe-root .progress-secondary::-moz-progress-bar{border-radius:var(--rounded-box, 16px);--tw-bg-opacity: 1;background-color:var(--fallback-s,oklch(var(--s)/var(--tw-bg-opacity)))}#twe-root .progress:indeterminate{--progress-color: var(--fallback-bc,oklch(var(--bc)/1));background-image:repeating-linear-gradient(90deg,var(--progress-color) -1%,var(--progress-color) 10%,transparent 10%,transparent 90%);background-size:200%;background-position-x:15%;animation:progress-loading 5s ease-in-out infinite}#twe-root .progress-primary:indeterminate{--progress-color: var(--fallback-p,oklch(var(--p)/1))}#twe-root .progress-secondary:indeterminate{--progress-color: var(--fallback-s,oklch(var(--s)/1))}#twe-root .progress::-webkit-progress-bar{border-radius:var(--rounded-box, 16px);background-color:transparent}#twe-root .progress::-webkit-progress-value{border-radius:var(--rounded-box, 16px);background-color:currentColor}#twe-root .progress-primary::-webkit-progress-value{--tw-bg-opacity: 1;background-color:var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity)))}#twe-root .progress-secondary::-webkit-progress-value{--tw-bg-opacity: 1;background-color:var(--fallback-s,oklch(var(--s)/var(--tw-bg-opacity)))}#twe-root .progress:indeterminate::-moz-progress-bar{background-color:transparent;background-image:repeating-linear-gradient(90deg,var(--progress-color) -1%,var(--progress-color) 10%,transparent 10%,transparent 90%);background-size:200%;background-position-x:15%;animation:progress-loading 5s ease-in-out infinite}@keyframes progress-loading{50%{background-position-x:-115%}}#twe-root .radio:focus{box-shadow:none}#twe-root .radio:focus-visible{outline-style:solid;outline-width:2px;outline-offset:2px;outline-color:var(--fallback-bc,oklch(var(--bc)/1))}#twe-root .radio:checked,#twe-root .radio[aria-checked=true]{--tw-bg-opacity: 1;background-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)));background-image:none;animation:radiomark var(--animation-input, .2s) ease-out;box-shadow:0 0 0 4px var(--fallback-b1,oklch(var(--b1)/1)) inset,0 0 0 4px var(--fallback-b1,oklch(var(--b1)/1)) inset}#twe-root .radio:disabled{cursor:not-allowed;opacity:.2}@keyframes radiomark{0%{box-shadow:0 0 0 12px var(--fallback-b1,oklch(var(--b1)/1)) inset,0 0 0 12px var(--fallback-b1,oklch(var(--b1)/1)) inset}50%{box-shadow:0 0 0 3px var(--fallback-b1,oklch(var(--b1)/1)) inset,0 0 0 3px var(--fallback-b1,oklch(var(--b1)/1)) inset}to{box-shadow:0 0 0 4px var(--fallback-b1,oklch(var(--b1)/1)) inset,0 0 0 4px var(--fallback-b1,oklch(var(--b1)/1)) inset}}#twe-root .range:focus-visible::-webkit-slider-thumb{--focus-shadow: 0 0 0 6px var(--fallback-b1,oklch(var(--b1)/1)) inset, 0 0 0 32px var(--range-shdw) inset}#twe-root .range:focus-visible::-moz-range-thumb{--focus-shadow: 0 0 0 6px var(--fallback-b1,oklch(var(--b1)/1)) inset, 0 0 0 32px var(--range-shdw) inset}#twe-root .range::-webkit-slider-runnable-track{height:8px;width:100%;border-radius:var(--rounded-box, 16px);background-color:var(--fallback-bc,oklch(var(--bc)/.1))}#twe-root .range::-moz-range-track{height:8px;width:100%;border-radius:var(--rounded-box, 16px);background-color:var(--fallback-bc,oklch(var(--bc)/.1))}#twe-root .range::-webkit-slider-thumb{position:relative;height:24px;width:24px;border-radius:var(--rounded-box, 16px);border-style:none;--tw-bg-opacity: 1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));-moz-appearance:none;appearance:none;-webkit-appearance:none;top:50%;color:var(--range-shdw);transform:translateY(-50%);--filler-size: 1600px;--filler-offset: 9.6px;box-shadow:0 0 0 3px var(--range-shdw) inset,var(--focus-shadow, 0 0),calc(var(--filler-size) * -1 - var(--filler-offset)) 0 0 var(--filler-size)}#twe-root .range::-moz-range-thumb{position:relative;height:24px;width:24px;border-radius:var(--rounded-box, 16px);border-style:none;--tw-bg-opacity: 1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));top:50%;color:var(--range-shdw);--filler-size: 1600px;--filler-offset: 8px;box-shadow:0 0 0 3px var(--range-shdw) inset,var(--focus-shadow, 0 0),calc(var(--filler-size) * -1 - var(--filler-offset)) 0 0 var(--filler-size)}@keyframes rating-pop{0%{transform:translateY(-.125em)}40%{transform:translateY(-.125em)}to{transform:translateY(0)}}#twe-root .select-bordered{border-color:var(--fallback-bc,oklch(var(--bc)/.2))}#twe-root .select:focus{box-shadow:none;border-color:var(--fallback-bc,oklch(var(--bc)/.2));outline-style:solid;outline-width:2px;outline-offset:2px;outline-color:var(--fallback-bc,oklch(var(--bc)/.2))}#twe-root .select-disabled,#twe-root .select:disabled,#twe-root .select[disabled]{cursor:not-allowed;--tw-border-opacity: 1;border-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)));--tw-bg-opacity: 1;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)));color:var(--fallback-bc,oklch(var(--bc)/.4))}#twe-root .select-disabled::-moz-placeholder,#twe-root .select:disabled::-moz-placeholder,#twe-root .select[disabled]::-moz-placeholder{color:var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity)));--tw-placeholder-opacity: .2}#twe-root .select-disabled::placeholder,#twe-root .select:disabled::placeholder,#twe-root .select[disabled]::placeholder{color:var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity)));--tw-placeholder-opacity: .2}#twe-root .select-multiple,#twe-root .select[multiple],#twe-root .select[size].select:not([size="1"]){background-image:none;padding-right:16px}#twe-root [dir=rtl] .select{background-position:calc(0% + 12px) calc(1px + 50%),calc(0% + 16px) calc(1px + 50%)}@keyframes skeleton{0%{background-position:150%}to{background-position:-50%}}#twe-root :where(.stats)>:not([hidden])~:not([hidden]){--tw-divide-x-reverse: 0;border-right-width:calc(1px * var(--tw-divide-x-reverse));border-left-width:calc(1px * calc(1 - var(--tw-divide-x-reverse)));--tw-divide-y-reverse: 0;border-top-width:calc(0px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(0px * var(--tw-divide-y-reverse))}#twe-root [dir=rtl] .stats>*:not([hidden])~*:not([hidden]){--tw-divide-x-reverse: 1}#twe-root .tabs-lifted>.tab:focus-visible{border-end-end-radius:0;border-end-start-radius:0}#twe-root .tab:is(.tab-active,[aria-selected=true]):not(.tab-disabled):not([disabled]),#twe-root .tab:is(input:checked){border-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));--tw-border-opacity: 1;--tw-text-opacity: 1}#twe-root .tab:focus{outline:2px solid transparent;outline-offset:2px}#twe-root .tab:focus-visible{outline:2px solid currentColor;outline-offset:-5px}#twe-root .tab-disabled,#twe-root .tab[disabled]{cursor:not-allowed;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));--tw-text-opacity: .2}#twe-root .tabs-bordered>.tab{border-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));--tw-border-opacity: .2;border-style:solid;border-bottom-width:calc(var(--tab-border, 1px) + 1px)}#twe-root .tabs-lifted>.tab{border:var(--tab-border, 1px) solid transparent;border-width:0 0 var(--tab-border, 1px) 0;border-start-start-radius:var(--tab-radius, 8px);border-start-end-radius:var(--tab-radius, 8px);border-bottom-color:var(--tab-border-color);padding-inline-start:var(--tab-padding, 16px);padding-inline-end:var(--tab-padding, 16px);padding-top:var(--tab-border, 1px)}#twe-root .tabs-lifted>.tab:is(.tab-active,[aria-selected=true]):not(.tab-disabled):not([disabled]),#twe-root .tabs-lifted>.tab:is(input:checked){background-color:var(--tab-bg);border-width:var(--tab-border, 1px) var(--tab-border, 1px) 0 var(--tab-border, 1px);border-inline-start-color:var(--tab-border-color);border-inline-end-color:var(--tab-border-color);border-top-color:var(--tab-border-color);padding-inline-start:calc(var(--tab-padding, 16px) - var(--tab-border, 1px));padding-inline-end:calc(var(--tab-padding, 16px) - var(--tab-border, 1px));padding-bottom:var(--tab-border, 1px);padding-top:0}#twe-root .tabs-lifted>.tab:is(.tab-active,[aria-selected=true]):not(.tab-disabled):not([disabled]):before,#twe-root .tabs-lifted>.tab:is(input:checked):before{z-index:1;content:"";display:block;position:absolute;width:calc(100% + var(--tab-radius, 8px) * 2);height:var(--tab-radius, 8px);bottom:0;background-size:var(--tab-radius, 8px);background-position:top left,top right;background-repeat:no-repeat;--tab-grad: calc(69% - var(--tab-border, 1px));--radius-start: radial-gradient( circle at top left, transparent var(--tab-grad), var(--tab-border-color) calc(var(--tab-grad) + .25px), var(--tab-border-color) calc(var(--tab-grad) + var(--tab-border, 1px)), var(--tab-bg) calc(var(--tab-grad) + var(--tab-border, 1px) + .25px) );--radius-end: radial-gradient( circle at top right, transparent var(--tab-grad), var(--tab-border-color) calc(var(--tab-grad) + .25px), var(--tab-border-color) calc(var(--tab-grad) + var(--tab-border, 1px)), var(--tab-bg) calc(var(--tab-grad) + var(--tab-border, 1px) + .25px) );background-image:var(--radius-start),var(--radius-end)}#twe-root .tabs-lifted>.tab:is(.tab-active,[aria-selected=true]):not(.tab-disabled):not([disabled]):first-child:before,#twe-root .tabs-lifted>.tab:is(input:checked):first-child:before{background-image:var(--radius-end);background-position:top right}#twe-root [dir=rtl] .tabs-lifted>.tab:is(.tab-active,[aria-selected=true]):not(.tab-disabled):not([disabled]):first-child:before,#twe-root [dir=rtl] .tabs-lifted>.tab:is(input:checked):first-child:before{background-image:var(--radius-start);background-position:top left}#twe-root .tabs-lifted>.tab:is(.tab-active,[aria-selected=true]):not(.tab-disabled):not([disabled]):last-child:before,#twe-root .tabs-lifted>.tab:is(input:checked):last-child:before{background-image:var(--radius-start);background-position:top left}#twe-root [dir=rtl] .tabs-lifted>.tab:is(.tab-active,[aria-selected=true]):not(.tab-disabled):not([disabled]):last-child:before,#twe-root [dir=rtl] .tabs-lifted>.tab:is(input:checked):last-child:before{background-image:var(--radius-end);background-position:top right}#twe-root .tabs-lifted>:is(.tab-active,[aria-selected=true]):not(.tab-disabled):not([disabled])+.tabs-lifted :is(.tab-active,[aria-selected=true]):not(.tab-disabled):not([disabled]):before,#twe-root .tabs-lifted>.tab:is(input:checked)+.tabs-lifted .tab:is(input:checked):before{background-image:var(--radius-end);background-position:top right}#twe-root .tabs-boxed .tab{border-radius:var(--rounded-btn, 8px)}#twe-root .\\!table:where([dir=rtl],[dir=rtl] *){text-align:right!important}#twe-root .table:where([dir=rtl],[dir=rtl] *){text-align:right}#twe-root .\\!table :where(th,td){padding:12px 16px!important;vertical-align:middle!important}#twe-root .table :where(th,td){padding:12px 16px;vertical-align:middle}#twe-root .table tr.active,#twe-root .table tr.active:nth-child(2n),#twe-root .table-zebra tbody tr:nth-child(2n){--tw-bg-opacity: 1;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)))}#twe-root .\\!table tr.active,#twe-root .\\!table tr.active:nth-child(2n){--tw-bg-opacity: 1 !important;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)))!important}#twe-root .table-zebra tr.active,#twe-root .table-zebra tr.active:nth-child(2n),#twe-root .table-zebra-zebra tbody tr:nth-child(2n){--tw-bg-opacity: 1;background-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity)))}#twe-root .\\!table :where(thead tr,tbody tr:not(:last-child),tbody tr:first-child:last-child){border-bottom-width:1px!important;--tw-border-opacity: 1 !important;border-bottom-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)))!important}#twe-root .table :where(thead tr,tbody tr:not(:last-child),tbody tr:first-child:last-child){border-bottom-width:1px;--tw-border-opacity: 1;border-bottom-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)))}#twe-root .\\!table :where(thead,tfoot){white-space:nowrap!important;font-size:12px!important;line-height:16px!important;font-weight:700!important;color:var(--fallback-bc,oklch(var(--bc)/.6))!important}#twe-root .table :where(thead,tfoot){white-space:nowrap;font-size:12px;line-height:16px;font-weight:700;color:var(--fallback-bc,oklch(var(--bc)/.6))}#twe-root .\\!table :where(tfoot){border-top-width:1px!important;--tw-border-opacity: 1 !important;border-top-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)))!important}#twe-root .table :where(tfoot){border-top-width:1px;--tw-border-opacity: 1;border-top-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)))}#twe-root .textarea-bordered{border-color:var(--fallback-bc,oklch(var(--bc)/.2))}#twe-root .textarea:focus{box-shadow:none;border-color:var(--fallback-bc,oklch(var(--bc)/.2));outline-style:solid;outline-width:2px;outline-offset:2px;outline-color:var(--fallback-bc,oklch(var(--bc)/.2))}#twe-root .textarea-disabled,#twe-root .textarea:disabled,#twe-root .textarea[disabled]{cursor:not-allowed;--tw-border-opacity: 1;border-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)));--tw-bg-opacity: 1;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)));color:var(--fallback-bc,oklch(var(--bc)/.4))}#twe-root .textarea-disabled::-moz-placeholder,#twe-root .textarea:disabled::-moz-placeholder,#twe-root .textarea[disabled]::-moz-placeholder{color:var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity)));--tw-placeholder-opacity: .2}#twe-root .textarea-disabled::placeholder,#twe-root .textarea:disabled::placeholder,#twe-root .textarea[disabled]::placeholder{color:var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity)));--tw-placeholder-opacity: .2}#twe-root .\\!timeline hr{height:4px!important}#twe-root .timeline hr{height:4px}#twe-root :where(.\\!timeline hr){--tw-bg-opacity: 1 !important;background-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity)))!important}#twe-root :where(.timeline hr){--tw-bg-opacity: 1;background-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity)))}#twe-root :where(.\\!timeline:has(.timeline-middle) hr):first-child{border-start-end-radius:var(--rounded-badge, 30.4px)!important;border-end-end-radius:var(--rounded-badge, 30.4px)!important;border-start-start-radius:0px!important;border-end-start-radius:0px!important}#twe-root :where(.timeline:has(.timeline-middle) hr):first-child{border-start-end-radius:var(--rounded-badge, 30.4px);border-end-end-radius:var(--rounded-badge, 30.4px);border-start-start-radius:0px;border-end-start-radius:0px}#twe-root :where(.\\!timeline:has(.timeline-middle) hr):last-child{border-start-start-radius:var(--rounded-badge, 30.4px)!important;border-end-start-radius:var(--rounded-badge, 30.4px)!important;border-start-end-radius:0px!important;border-end-end-radius:0px!important}#twe-root :where(.timeline:has(.timeline-middle) hr):last-child{border-start-start-radius:var(--rounded-badge, 30.4px);border-end-start-radius:var(--rounded-badge, 30.4px);border-start-end-radius:0px;border-end-end-radius:0px}#twe-root :where(.\\!timeline:not(:has(.timeline-middle)) :first-child hr:last-child){border-start-start-radius:var(--rounded-badge, 30.4px)!important;border-end-start-radius:var(--rounded-badge, 30.4px)!important;border-start-end-radius:0px!important;border-end-end-radius:0px!important}#twe-root :where(.timeline:not(:has(.timeline-middle)) :first-child hr:last-child){border-start-start-radius:var(--rounded-badge, 30.4px);border-end-start-radius:var(--rounded-badge, 30.4px);border-start-end-radius:0px;border-end-end-radius:0px}#twe-root :where(.\\!timeline:not(:has(.timeline-middle)) :last-child hr:first-child){border-start-end-radius:var(--rounded-badge, 30.4px)!important;border-end-end-radius:var(--rounded-badge, 30.4px)!important;border-start-start-radius:0px!important;border-end-start-radius:0px!important}#twe-root :where(.timeline:not(:has(.timeline-middle)) :last-child hr:first-child){border-start-end-radius:var(--rounded-badge, 30.4px);border-end-end-radius:var(--rounded-badge, 30.4px);border-start-start-radius:0px;border-end-start-radius:0px}@keyframes toast-pop{0%{transform:scale(.9);opacity:0}to{transform:scale(1);opacity:1}}#twe-root [dir=rtl] .toggle{--handleoffsetcalculator: calc(var(--handleoffset) * 1)}#twe-root .toggle:focus-visible{outline-style:solid;outline-width:2px;outline-offset:2px;outline-color:var(--fallback-bc,oklch(var(--bc)/.2))}#twe-root .toggle:hover{background-color:currentColor}#twe-root .toggle:checked,#twe-root .toggle[aria-checked=true]{background-image:none;--handleoffsetcalculator: var(--handleoffset);--tw-text-opacity: 1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)))}#twe-root [dir=rtl] .toggle:checked,#twe-root [dir=rtl] .toggle[aria-checked=true]{--handleoffsetcalculator: calc(var(--handleoffset) * -1)}#twe-root .toggle:indeterminate{--tw-text-opacity: 1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));box-shadow:calc(var(--handleoffset) / 2) 0 0 2px var(--tglbg) inset,calc(var(--handleoffset) / -2) 0 0 2px var(--tglbg) inset,0 0 0 2px var(--tglbg) inset}#twe-root [dir=rtl] .toggle:indeterminate{box-shadow:calc(var(--handleoffset) / 2) 0 0 2px var(--tglbg) inset,calc(var(--handleoffset) / -2) 0 0 2px var(--tglbg) inset,0 0 0 2px var(--tglbg) inset}#twe-root .toggle-primary:focus-visible{outline-color:var(--fallback-p,oklch(var(--p)/1))}#twe-root .toggle-primary:checked,#twe-root .toggle-primary[aria-checked=true]{border-color:var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity)));--tw-border-opacity: .1;--tw-bg-opacity: 1;background-color:var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity)));--tw-text-opacity: 1;color:var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)))}#twe-root .toggle-secondary:focus-visible{outline-color:var(--fallback-s,oklch(var(--s)/1))}#twe-root .toggle-secondary:checked,#twe-root .toggle-secondary[aria-checked=true]{border-color:var(--fallback-s,oklch(var(--s)/var(--tw-border-opacity)));--tw-border-opacity: .1;--tw-bg-opacity: 1;background-color:var(--fallback-s,oklch(var(--s)/var(--tw-bg-opacity)));--tw-text-opacity: 1;color:var(--fallback-sc,oklch(var(--sc)/var(--tw-text-opacity)))}#twe-root .toggle-warning:focus-visible{outline-color:var(--fallback-wa,oklch(var(--wa)/1))}#twe-root .toggle-warning:checked,#twe-root .toggle-warning[aria-checked=true]{border-color:var(--fallback-wa,oklch(var(--wa)/var(--tw-border-opacity)));--tw-border-opacity: .1;--tw-bg-opacity: 1;background-color:var(--fallback-wa,oklch(var(--wa)/var(--tw-bg-opacity)));--tw-text-opacity: 1;color:var(--fallback-wac,oklch(var(--wac)/var(--tw-text-opacity)))}#twe-root .toggle:disabled{cursor:not-allowed;--tw-border-opacity: 1;border-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));background-color:transparent;opacity:.3;--togglehandleborder: 0 0 0 3px var(--fallback-bc,oklch(var(--bc)/1)) inset, var(--handleoffsetcalculator) 0 0 3px var(--fallback-bc,oklch(var(--bc)/1)) inset}#twe-root .badge-xs{height:12px;font-size:12px;line-height:12px;padding-left:5.008px;padding-right:5.008px}#twe-root .badge-sm{height:16px;font-size:12px;line-height:16px;padding-left:7.008px;padding-right:7.008px}#twe-root .btm-nav-xs>*:where(.active){border-top-width:1px}#twe-root .btm-nav-sm>*:where(.active){border-top-width:2px}#twe-root .btm-nav-md>*:where(.active){border-top-width:2px}#twe-root .btm-nav-lg>*:where(.active){border-top-width:4px}#twe-root .btn-xs{height:24px;min-height:24px;padding-left:8px;padding-right:8px;font-size:12px}#twe-root .btn-sm{height:32px;min-height:32px;padding-left:12px;padding-right:12px;font-size:14px}#twe-root .btn-square:where(.btn-xs){height:24px;width:24px;padding:0}#twe-root .btn-square:where(.btn-sm){height:32px;width:32px;padding:0}#twe-root .btn-circle:where(.btn-xs){height:24px;width:24px;border-radius:9999px;padding:0}#twe-root .btn-circle:where(.btn-sm){height:32px;width:32px;border-radius:9999px;padding:0}#twe-root [type=checkbox].checkbox-xs{height:16px;width:16px}#twe-root [type=checkbox].checkbox-sm{height:20px;width:20px}#twe-root .file-input-sm{height:32px;padding-inline-end:12px;font-size:14px;line-height:20px;line-height:2}#twe-root .file-input-sm::file-selector-button{margin-right:12px;font-size:14px}#twe-root .input-xs{height:24px;padding-left:8px;padding-right:8px;font-size:12px;line-height:16px;line-height:1.625}#twe-root .input-sm{height:32px;padding-left:12px;padding-right:12px;font-size:14px;line-height:32px}#twe-root .join.join-vertical{flex-direction:column}#twe-root .join.join-vertical .join-item:first-child:not(:last-child),#twe-root .join.join-vertical *:first-child:not(:last-child) .join-item{border-end-start-radius:0;border-end-end-radius:0;border-start-start-radius:inherit;border-start-end-radius:inherit}#twe-root .join.join-vertical .join-item:last-child:not(:first-child),#twe-root .join.join-vertical *:last-child:not(:first-child) .join-item{border-start-start-radius:0;border-start-end-radius:0;border-end-start-radius:inherit;border-end-end-radius:inherit}#twe-root .join.join-horizontal{flex-direction:row}#twe-root .join.join-horizontal .join-item:first-child:not(:last-child),#twe-root .join.join-horizontal *:first-child:not(:last-child) .join-item{border-end-end-radius:0;border-start-end-radius:0;border-end-start-radius:inherit;border-start-start-radius:inherit}#twe-root .join.join-horizontal .join-item:last-child:not(:first-child),#twe-root .join.join-horizontal *:last-child:not(:first-child) .join-item{border-end-start-radius:0;border-start-start-radius:0;border-end-end-radius:inherit;border-start-end-radius:inherit}#twe-root [type=radio].radio-sm{height:20px;width:20px}#twe-root .select-sm{height:32px;min-height:32px;padding-left:12px;padding-right:32px;font-size:14px;line-height:32px}#twe-root [dir=rtl] .select-sm{padding-left:32px;padding-right:12px}#twe-root .select-xs{height:24px;min-height:24px;padding-left:8px;padding-right:32px;font-size:12px;line-height:16px;line-height:1.625}#twe-root [dir=rtl] .select-xs{padding-left:32px;padding-right:8px}#twe-root .tabs-md :where(.tab){height:32px;font-size:14px;line-height:20px;line-height:2;--tab-padding: 16px}#twe-root .tabs-lg :where(.tab){height:48px;font-size:18px;line-height:28px;line-height:2;--tab-padding: 20px}#twe-root .tabs-sm :where(.tab){height:24px;font-size:14px;line-height:12px;--tab-padding: 12px}#twe-root .tabs-xs :where(.tab){height:20px;font-size:12px;line-height:12px;--tab-padding: 8px}#twe-root [type=checkbox].toggle-xs{--handleoffset: 8px;height:16px;width:24px}#twe-root [type=checkbox].toggle-sm{--handleoffset: 12px;height:20px;width:32px}#twe-root .tooltip{position:relative;display:inline-block;--tooltip-offset: calc(100% + 1px + var(--tooltip-tail, 0px))}#twe-root .tooltip:before{position:absolute;pointer-events:none;z-index:1;content:var(--tw-content);--tw-content: attr(data-tip)}#twe-root .tooltip:before,#twe-root .tooltip-top:before{transform:translate(-50%);top:auto;left:50%;right:auto;bottom:var(--tooltip-offset)}#twe-root .tooltip-bottom:before{transform:translate(-50%);top:var(--tooltip-offset);left:50%;right:auto;bottom:auto}#twe-root .avatar.online:before{content:"";position:absolute;z-index:10;display:block;border-radius:9999px;--tw-bg-opacity: 1;background-color:var(--fallback-su,oklch(var(--su)/var(--tw-bg-opacity)));outline-style:solid;outline-width:2px;outline-color:var(--fallback-b1,oklch(var(--b1)/1));width:15%;height:15%;top:7%;right:7%}#twe-root .avatar.offline:before{content:"";position:absolute;z-index:10;display:block;border-radius:9999px;--tw-bg-opacity: 1;background-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity)));outline-style:solid;outline-width:2px;outline-color:var(--fallback-b1,oklch(var(--b1)/1));width:15%;height:15%;top:7%;right:7%}#twe-root .card-compact .card-body{padding:16px;font-size:14px;line-height:20px}#twe-root .card-compact .card-title{margin-bottom:4px}#twe-root .join.join-vertical>:where(*:not(:first-child)){margin-left:0;margin-right:0;margin-top:-1px}#twe-root .join.join-vertical>:where(*:not(:first-child)):is(.btn){margin-top:calc(var(--border-btn) * -1)}#twe-root .join.join-horizontal>:where(*:not(:first-child)){margin-top:0;margin-bottom:0;margin-inline-start:-1px}#twe-root .join.join-horizontal>:where(*:not(:first-child)):is(.btn){margin-inline-start:calc(var(--border-btn) * -1);margin-top:0}#twe-root .menu-sm :where(li:not(.menu-title)>*:not(ul,details,.menu-title)),#twe-root .menu-sm :where(li:not(.menu-title)>details>summary:not(.menu-title)){border-radius:var(--rounded-btn, 8px);padding:4px 12px;font-size:14px;line-height:20px}#twe-root .menu-sm .menu-title{padding:8px 12px}#twe-root .modal-top :where(.modal-box){width:100%;max-width:none;--tw-translate-y: -40px;--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));border-bottom-right-radius:var(--rounded-box, 16px);border-bottom-left-radius:var(--rounded-box, 16px);border-top-left-radius:0;border-top-right-radius:0}#twe-root .modal-middle :where(.modal-box){width:91.666667%;max-width:512px;--tw-translate-y: 0px;--tw-scale-x: .9;--tw-scale-y: .9;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));border-top-left-radius:var(--rounded-box, 16px);border-top-right-radius:var(--rounded-box, 16px);border-bottom-right-radius:var(--rounded-box, 16px);border-bottom-left-radius:var(--rounded-box, 16px)}#twe-root .modal-bottom :where(.modal-box){width:100%;max-width:none;--tw-translate-y: 40px;--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));border-top-left-radius:var(--rounded-box, 16px);border-top-right-radius:var(--rounded-box, 16px);border-bottom-right-radius:0;border-bottom-left-radius:0}#twe-root .table-xs :not(thead):not(tfoot) tr{font-size:12px;line-height:16px}#twe-root .table-xs :where(th,td){padding:4px 8px}#twe-root .table-sm :not(thead):not(tfoot) tr{font-size:14px;line-height:20px}#twe-root .table-sm :where(th,td){padding:8px 12px}#twe-root .tooltip{position:relative;display:inline-block;text-align:center;--tooltip-tail: 3px;--tooltip-color: var(--fallback-n,oklch(var(--n)/1));--tooltip-text-color: var(--fallback-nc,oklch(var(--nc)/1));--tooltip-tail-offset: calc(100% + 1px - var(--tooltip-tail))}#twe-root .tooltip:before,#twe-root .tooltip:after{opacity:0;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-delay:.1s;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1)}#twe-root .tooltip:after{position:absolute;content:"";border-style:solid;border-width:var(--tooltip-tail, 0);width:0;height:0;display:block}#twe-root .tooltip:before{max-width:320px;white-space:normal;border-radius:4px;padding:4px 8px;font-size:14px;line-height:20px;background-color:var(--tooltip-color);color:var(--tooltip-text-color);width:-moz-max-content;width:max-content}#twe-root .tooltip.tooltip-open:before{opacity:1;transition-delay:75ms}#twe-root .tooltip.tooltip-open:after{opacity:1;transition-delay:75ms}#twe-root .tooltip:hover:before{opacity:1;transition-delay:75ms}#twe-root .tooltip:hover:after{opacity:1;transition-delay:75ms}#twe-root .tooltip:has(:focus-visible):after,#twe-root .tooltip:has(:focus-visible):before{opacity:1;transition-delay:75ms}#twe-root .tooltip:not([data-tip]):hover:before,#twe-root .tooltip:not([data-tip]):hover:after{visibility:hidden;opacity:0}#twe-root .tooltip:after,#twe-root .tooltip-top:after{transform:translate(-50%);border-color:var(--tooltip-color) transparent transparent transparent;top:auto;left:50%;right:auto;bottom:var(--tooltip-tail-offset)}#twe-root .tooltip-bottom:after{transform:translate(-50%);border-color:transparent transparent var(--tooltip-color) transparent;top:var(--tooltip-tail-offset);left:50%;right:auto;bottom:auto}#twe-root .pointer-events-none{pointer-events:none}#twe-root .static{position:static}#twe-root .fixed{position:fixed}#twe-root .absolute{position:absolute}#twe-root .relative{position:relative}#twe-root .sticky{position:sticky}#twe-root .inset-x-0{left:0;right:0}#twe-root .bottom-0{bottom:0}#twe-root .bottom-0\\.5{bottom:2px}#twe-root .bottom-14{bottom:56px}#twe-root .left-0\\.5{left:2px}#twe-root .left-8{left:32px}#twe-root .left-\\[-20px\\]{left:-20px}#twe-root .right-2{right:8px}#twe-root .right-3{right:12px}#twe-root .top-3{top:12px}#twe-root .top-8{top:32px}#twe-root .top-\\[60\\%\\]{top:60%}#twe-root .z-10{z-index:10}#twe-root .z-\\[2\\]{z-index:2}#twe-root .z-\\[6000\\]{z-index:6000}#twe-root .col-span-1{grid-column:span 1 / span 1}#twe-root .col-span-2{grid-column:span 2 / span 2}#twe-root .col-span-3{grid-column:span 3 / span 3}#twe-root .m-0{margin:0}#twe-root .mx-4{margin-left:16px;margin-right:16px}#twe-root .my-1{margin-top:4px;margin-bottom:4px}#twe-root .my-3{margin-top:12px;margin-bottom:12px}#twe-root .my-\\[2px\\]{margin-top:2px;margin-bottom:2px}#twe-root .-ml-2{margin-left:-8px}#twe-root .mb-0{margin-bottom:0}#twe-root .mb-0\\.5{margin-bottom:2px}#twe-root .mb-1{margin-bottom:4px}#twe-root .mb-1\\.5{margin-bottom:6px}#twe-root .mb-2{margin-bottom:8px}#twe-root .mb-3{margin-bottom:12px}#twe-root .mb-4{margin-bottom:16px}#twe-root .ml-0\\.5{margin-left:2px}#twe-root .ml-1{margin-left:4px}#twe-root .ml-2{margin-left:8px}#twe-root .ml-4{margin-left:16px}#twe-root .mr-1{margin-right:4px}#twe-root .mr-2{margin-right:8px}#twe-root .mr-3{margin-right:12px}#twe-root .mt-0{margin-top:0}#twe-root .mt-0\\.5{margin-top:2px}#twe-root .mt-1{margin-top:4px}#twe-root .mt-1\\.5{margin-top:6px}#twe-root .mt-2{margin-top:8px}#twe-root .mt-3{margin-top:12px}#twe-root .mt-6{margin-top:24px}#twe-root .line-clamp-3{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3}#twe-root .line-clamp-4{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:4}#twe-root .block{display:block}#twe-root .inline-block{display:inline-block}#twe-root .inline{display:inline}#twe-root .flex{display:flex}#twe-root .inline-flex{display:inline-flex}#twe-root .\\!table{display:table!important}#twe-root .table{display:table}#twe-root .grid{display:grid}#twe-root .contents{display:contents}#twe-root .hidden{display:none}#twe-root .h-11{height:44px}#twe-root .h-12{height:48px}#twe-root .h-20{height:80px}#twe-root .h-28{height:112px}#twe-root .h-36{height:144px}#twe-root .h-4{height:16px}#twe-root .h-5{height:20px}#twe-root .h-6{height:24px}#twe-root .h-8{height:32px}#twe-root .h-9{height:36px}#twe-root .h-\\[320px\\]{height:320px}#twe-root .h-\\[82vh\\]{height:82vh}#twe-root .h-auto{height:auto}#twe-root .h-full{height:100%}#twe-root .h-screen{height:100vh}#twe-root .max-h-44{max-height:176px}#twe-root .max-h-48{max-height:192px}#twe-root .max-h-52{max-height:208px}#twe-root .max-h-56{max-height:224px}#twe-root .max-h-72{max-height:288px}#twe-root .max-h-80{max-height:320px}#twe-root .max-h-\\[400px\\]{max-height:400px}#twe-root .max-h-\\[460px\\]{max-height:460px}#twe-root .max-h-\\[500px\\]{max-height:500px}#twe-root .max-h-\\[calc\\(100vh-4rem\\)\\]{max-height:calc(100vh - 64px)}#twe-root .max-h-full{max-height:100%}#twe-root .max-h-screen{max-height:100vh}#twe-root .min-h-0{min-height:0px}#twe-root .min-h-16{min-height:64px}#twe-root .min-h-6{min-height:24px}#twe-root .min-h-9{min-height:36px}#twe-root .min-h-\\[512px\\]{min-height:512px}#twe-root .min-h-\\[560px\\]{min-height:560px}#twe-root .w-12{width:48px}#twe-root .w-20{width:80px}#twe-root .w-24{width:96px}#twe-root .w-28{width:112px}#twe-root .w-32{width:128px}#twe-root .w-36{width:144px}#twe-root .w-4{width:16px}#twe-root .w-40{width:160px}#twe-root .w-44{width:176px}#twe-root .w-48{width:192px}#twe-root .w-52{width:208px}#twe-root .w-56{width:224px}#twe-root .w-60{width:240px}#twe-root .w-8{width:32px}#twe-root .w-80{width:320px}#twe-root .w-9{width:36px}#twe-root .w-auto{width:auto}#twe-root .w-full{width:100%}#twe-root .w-max{width:-moz-max-content;width:max-content}#twe-root .w-screen{width:100vw}#twe-root .min-w-0{min-width:0px}#twe-root .min-w-44{min-width:176px}#twe-root .min-w-56{min-width:224px}#twe-root .max-w-2xl{max-width:672px}#twe-root .max-w-3xl{max-width:768px}#twe-root .max-w-4xl{max-width:896px}#twe-root .max-w-\\[50\\%\\]{max-width:50%}#twe-root .max-w-\\[calc\\(100\\%-46px\\)\\]{max-width:calc(100% - 46px)}#twe-root .max-w-full{max-width:100%}#twe-root .max-w-lg{max-width:512px}#twe-root .max-w-none{max-width:none}#twe-root .max-w-sm{max-width:384px}#twe-root .max-w-xl{max-width:576px}#twe-root .max-w-xs{max-width:320px}#twe-root .flex-1{flex:1 1 0%}#twe-root .flex-shrink-0,#twe-root .shrink-0{flex-shrink:0}#twe-root .flex-grow,#twe-root .grow{flex-grow:1}#twe-root .origin-\\[bottom_center\\]{transform-origin:bottom center}#twe-root .translate-x-0{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}#twe-root .translate-x-\\[-500px\\]{--tw-translate-x: -500px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}#twe-root .transform-none{transform:none}@keyframes ping{75%,to{transform:scale(2);opacity:0}}#twe-root .animate-ping{animation:ping 1s cubic-bezier(0,0,.2,1) infinite}#twe-root .cursor-pointer{cursor:pointer}#twe-root .select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}#twe-root .resize{resize:both}#twe-root .list-decimal{list-style-type:decimal}#twe-root .grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}#twe-root .grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}#twe-root .grid-cols-\\[auto_minmax\\(auto\\,1fr\\)\\]{grid-template-columns:auto minmax(auto,1fr)}#twe-root .flex-row{flex-direction:row}#twe-root .flex-col{flex-direction:column}#twe-root .flex-wrap{flex-wrap:wrap}#twe-root .items-start{align-items:flex-start}#twe-root .items-end{align-items:flex-end}#twe-root .items-center{align-items:center}#twe-root .justify-start{justify-content:flex-start}#twe-root .justify-end{justify-content:flex-end}#twe-root .justify-center{justify-content:center}#twe-root .justify-between{justify-content:space-between}#twe-root .gap-1{gap:4px}#twe-root .gap-2{gap:8px}#twe-root .gap-3{gap:12px}#twe-root .gap-4{gap:16px}#twe-root .space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(4px * var(--tw-space-x-reverse));margin-left:calc(4px * calc(1 - var(--tw-space-x-reverse)))}#twe-root .space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(8px * var(--tw-space-x-reverse));margin-left:calc(8px * calc(1 - var(--tw-space-x-reverse)))}#twe-root .space-y-0\\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(2px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2px * var(--tw-space-y-reverse))}#twe-root .space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(4px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(4px * var(--tw-space-y-reverse))}#twe-root .space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(8px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(8px * var(--tw-space-y-reverse))}#twe-root .overflow-auto{overflow:auto}#twe-root .overflow-hidden{overflow:hidden}#twe-root .overflow-visible{overflow:visible}#twe-root .overflow-scroll{overflow:scroll}#twe-root .overflow-x-auto{overflow-x:auto}#twe-root .overflow-y-auto{overflow-y:auto}#twe-root .overflow-x-scroll{overflow-x:scroll}#twe-root .overflow-y-scroll{overflow-y:scroll}#twe-root .overscroll-contain{overscroll-behavior:contain}#twe-root .overscroll-none{overscroll-behavior:none}#twe-root .scroll-smooth{scroll-behavior:smooth}#twe-root .truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}#twe-root .whitespace-normal{white-space:normal}#twe-root .whitespace-nowrap{white-space:nowrap}#twe-root .whitespace-pre{white-space:pre}#twe-root .whitespace-pre-line{white-space:pre-line}#twe-root .whitespace-pre-wrap{white-space:pre-wrap}#twe-root .break-words{overflow-wrap:break-word}#twe-root .break-all{word-break:break-all}#twe-root .rounded{border-radius:4px}#twe-root .rounded-\\[20px\\]{border-radius:20px}#twe-root .rounded-\\[2px\\]{border-radius:2px}#twe-root .rounded-box{border-radius:var(--rounded-box, 16px)}#twe-root .rounded-full{border-radius:9999px}#twe-root .rounded-md{border-radius:6px}#twe-root .rounded-none{border-radius:0}#twe-root .border{border-width:1px}#twe-root .border-0{border-width:0px}#twe-root .border-b{border-bottom-width:1px}#twe-root .border-l-2{border-left-width:2px}#twe-root .border-t{border-top-width:1px}#twe-root .border-solid{border-style:solid}#twe-root .border-accent\\/40{border-color:var(--fallback-a,oklch(var(--a)/.4))}#twe-root .border-accent\\/50{border-color:var(--fallback-a,oklch(var(--a)/.5))}#twe-root .border-base-300{--tw-border-opacity: 1;border-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-border-opacity, 1)))}#twe-root .border-info\\/50{border-color:var(--fallback-in,oklch(var(--in)/.5))}#twe-root .border-neutral-content{--tw-border-opacity: 1;border-color:var(--fallback-nc,oklch(var(--nc)/var(--tw-border-opacity, 1)))}#twe-root .border-success\\/50{border-color:var(--fallback-su,oklch(var(--su)/.5))}#twe-root .border-warning\\/40{border-color:var(--fallback-wa,oklch(var(--wa)/.4))}#twe-root .border-warning\\/60{border-color:var(--fallback-wa,oklch(var(--wa)/.6))}#twe-root .border-opacity-50{--tw-border-opacity: .5}#twe-root .bg-accent{--tw-bg-opacity: 1;background-color:var(--fallback-a,oklch(var(--a)/var(--tw-bg-opacity, 1)))}#twe-root .bg-accent\\/10{background-color:var(--fallback-a,oklch(var(--a)/.1))}#twe-root .bg-base-100{--tw-bg-opacity: 1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity, 1)))}#twe-root .bg-base-200{--tw-bg-opacity: 1;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity, 1)))}#twe-root .bg-base-200\\/50{background-color:var(--fallback-b2,oklch(var(--b2)/.5))}#twe-root .bg-base-200\\/60{background-color:var(--fallback-b2,oklch(var(--b2)/.6))}#twe-root .bg-base-200\\/70{background-color:var(--fallback-b2,oklch(var(--b2)/.7))}#twe-root .bg-base-300{--tw-bg-opacity: 1;background-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity, 1)))}#twe-root .bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}#twe-root .bg-black\\/40{background-color:#0006}#twe-root .bg-info{--tw-bg-opacity: 1;background-color:var(--fallback-in,oklch(var(--in)/var(--tw-bg-opacity, 1)))}#twe-root .bg-neutral{--tw-bg-opacity: 1;background-color:var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity, 1)))}#twe-root .bg-primary{--tw-bg-opacity: 1;background-color:var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity, 1)))}#twe-root .bg-secondary{--tw-bg-opacity: 1;background-color:var(--fallback-s,oklch(var(--s)/var(--tw-bg-opacity, 1)))}#twe-root .bg-success{--tw-bg-opacity: 1;background-color:var(--fallback-su,oklch(var(--su)/var(--tw-bg-opacity, 1)))}#twe-root .bg-transparent{background-color:transparent}#twe-root .bg-warning{--tw-bg-opacity: 1;background-color:var(--fallback-wa,oklch(var(--wa)/var(--tw-bg-opacity, 1)))}#twe-root .bg-warning\\/10{background-color:var(--fallback-wa,oklch(var(--wa)/.1))}#twe-root .bg-warning\\/30{background-color:var(--fallback-wa,oklch(var(--wa)/.3))}#twe-root .bg-opacity-30{--tw-bg-opacity: .3}#twe-root .bg-gradient-to-b{background-image:linear-gradient(to bottom,var(--tw-gradient-stops))}#twe-root .bg-gradient-to-t{background-image:linear-gradient(to top,var(--tw-gradient-stops))}#twe-root .from-base-100{--tw-gradient-from: var(--fallback-b1,oklch(var(--b1)/1)) var(--tw-gradient-from-position);--tw-gradient-to: var(--fallback-b1,oklch(var(--b1)/0)) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}#twe-root .from-black\\/70{--tw-gradient-from: rgb(0 0 0 / .7) var(--tw-gradient-from-position);--tw-gradient-to: rgb(0 0 0 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}#twe-root .via-black\\/20{--tw-gradient-to: rgb(0 0 0 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), rgb(0 0 0 / .2) var(--tw-gradient-via-position), var(--tw-gradient-to)}#twe-root .to-base-200\\/80{--tw-gradient-to: var(--fallback-b2,oklch(var(--b2)/.8)) var(--tw-gradient-to-position)}#twe-root .to-transparent{--tw-gradient-to: transparent var(--tw-gradient-to-position)}#twe-root .object-contain{-o-object-fit:contain;object-fit:contain}#twe-root .object-cover{-o-object-fit:cover;object-fit:cover}#twe-root .\\!p-0{padding:0!important}#twe-root .p-0{padding:0}#twe-root .p-2{padding:8px}#twe-root .p-3{padding:12px}#twe-root .p-6{padding:24px}#twe-root .px-0\\.5{padding-left:2px;padding-right:2px}#twe-root .px-1{padding-left:4px;padding-right:4px}#twe-root .px-1\\.5{padding-left:6px;padding-right:6px}#twe-root .px-2{padding-left:8px;padding-right:8px}#twe-root .px-3{padding-left:12px;padding-right:12px}#twe-root .px-4{padding-left:16px;padding-right:16px}#twe-root .px-\\[1px\\]{padding-left:1px;padding-right:1px}#twe-root .py-0{padding-top:0;padding-bottom:0}#twe-root .py-1{padding-top:4px;padding-bottom:4px}#twe-root .py-1\\.5{padding-top:6px;padding-bottom:6px}#twe-root .py-2{padding-top:8px;padding-bottom:8px}#twe-root .py-2\\.5{padding-top:10px;padding-bottom:10px}#twe-root .py-3{padding-top:12px;padding-bottom:12px}#twe-root .pb-3{padding-bottom:12px}#twe-root .pl-2{padding-left:8px}#twe-root .pl-4{padding-left:16px}#twe-root .pl-5{padding-left:20px}#twe-root .pr-2{padding-right:8px}#twe-root .pt-1{padding-top:4px}#twe-root .pt-8{padding-top:32px}#twe-root .text-left{text-align:left}#twe-root .text-center{text-align:center}#twe-root .align-top{vertical-align:top}#twe-root .align-middle{vertical-align:middle}#twe-root .font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}#twe-root .text-\\[10px\\]{font-size:10px}#twe-root .text-\\[11px\\]{font-size:11px}#twe-root .text-base{font-size:16px;line-height:24px}#twe-root .text-lg{font-size:18px;line-height:28px}#twe-root .text-sm{font-size:14px;line-height:20px}#twe-root .text-xl{font-size:20px;line-height:28px}#twe-root .text-xs{font-size:12px;line-height:16px}#twe-root .font-bold{font-weight:700}#twe-root .font-medium{font-weight:500}#twe-root .font-semibold{font-weight:600}#twe-root .uppercase{text-transform:uppercase}#twe-root .leading-4{line-height:16px}#twe-root .leading-5{line-height:20px}#twe-root .leading-6{line-height:24px}#twe-root .leading-8{line-height:32px}#twe-root .leading-\\[48px\\]{line-height:48px}#twe-root .leading-loose{line-height:2}#twe-root .leading-none{line-height:1}#twe-root .leading-normal{line-height:1.5}#twe-root .leading-tight{line-height:1.25}#twe-root .tracking-\\[0\\.01em\\]{letter-spacing:.01em}#twe-root .tracking-\\[0\\.08em\\]{letter-spacing:.08em}#twe-root .text-base-content{--tw-text-opacity: 1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity, 1)))}#twe-root .text-base-content\\/50{color:var(--fallback-bc,oklch(var(--bc)/.5))}#twe-root .text-base-content\\/70{color:var(--fallback-bc,oklch(var(--bc)/.7))}#twe-root .text-error{--tw-text-opacity: 1;color:var(--fallback-er,oklch(var(--er)/var(--tw-text-opacity, 1)))}#twe-root .text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}#twe-root .text-info{--tw-text-opacity: 1;color:var(--fallback-in,oklch(var(--in)/var(--tw-text-opacity, 1)))}#twe-root .text-success{--tw-text-opacity: 1;color:var(--fallback-su,oklch(var(--su)/var(--tw-text-opacity, 1)))}#twe-root .text-warning{--tw-text-opacity: 1;color:var(--fallback-wa,oklch(var(--wa)/var(--tw-text-opacity, 1)))}#twe-root .text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}#twe-root .text-opacity-50{--tw-text-opacity: .5}#twe-root .text-opacity-60{--tw-text-opacity: .6}#twe-root .text-opacity-70{--tw-text-opacity: .7}#twe-root .opacity-50{opacity:.5}#twe-root .opacity-60{opacity:.6}#twe-root .opacity-65{opacity:.65}#twe-root .opacity-70{opacity:.7}#twe-root .opacity-75{opacity:.75}#twe-root .opacity-80{opacity:.8}#twe-root .opacity-90{opacity:.9}#twe-root .shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}#twe-root .shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}#twe-root .shadow-md{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}#twe-root .shadow-none{--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}#twe-root .shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}#twe-root .shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}#twe-root .\\!filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)!important}#twe-root .filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}#twe-root .transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}#twe-root .transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}#twe-root .transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}#twe-root .transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}#twe-root .duration-150{transition-duration:.15s}#twe-root .duration-200{transition-duration:.2s}#twe-root .duration-300{transition-duration:.3s}#twe-root .duration-500{transition-duration:.5s}#twe-root .rounded-box-half{border-radius:calc(var(--rounded-box) / 2)}#twe-root .table-border-bc :where(thead,tbody) :where(tr:not(:last-child)),#twe-root .table-border-bc :where(thead,tbody) :where(tr:first-child:last-child){--tw-border-opacity: 20%;border-bottom-width:1px;border-bottom-color:var(--fallback-bc, oklch(var(--bc) / var(--tw-border-opacity)))}#twe-root .table-padding-sm :where(th,td){padding:8px 12px}#twe-root .no-scrollbar::-webkit-scrollbar{display:none}#twe-root .no-scrollbar{-ms-overflow-style:none;scrollbar-width:none}#twe-root .before\\:max-w-40:before{content:var(--tw-content);max-width:160px}#twe-root .before\\:max-w-max:before{content:var(--tw-content);max-width:-moz-max-content;max-width:max-content}#twe-root .before\\:whitespace-pre-line:before{content:var(--tw-content);white-space:pre-line}#twe-root .hover\\:bg-base-200:hover{--tw-bg-opacity: 1;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity, 1)))}#twe-root .group:hover .group-hover\\:translate-x-\\[5px\\]{--tw-translate-x: 5px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}#twe-root .group:hover .group-hover\\:rotate-\\[20deg\\]{--tw-rotate: 20deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}#twe-root .group:hover .group-hover\\:scale-\\[1\\.02\\]{--tw-scale-x: 1.02;--tw-scale-y: 1.02;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}#twe-root .group:hover .group-hover\\:opacity-90{opacity:.9}@media(min-width:640px){#twe-root .sm\\:grid{display:grid}#twe-root .sm\\:h-9{height:36px}#twe-root .sm\\:max-w-screen-sm{max-width:640px}#twe-root .sm\\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}#twe-root .sm\\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}#twe-root .sm\\:gap-2{gap:8px}#twe-root .sm\\:pl-2{padding-left:8px}}@media(min-width:768px){#twe-root .md\\:max-w-screen-md{max-width:768px}#twe-root .md\\:max-w-screen-sm{max-width:640px}#twe-root .md\\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}#twe-root .\\[\\&\\>path\\]\\:stroke-0>path{stroke-width:0} `);

(function (preact, hooks, i18next, signals, dayjs, fileSaverEs, Dexie, dexieExportImport, tableCore) {
  'use strict';

  var __defProp = Object.defineProperty;
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
  var f = 0;
  function u(e, t, n, o, i, u2) {
    t || (t = {});
    var a, c, p = t;
    if ("ref" in p) for (c in p = {}, t) "ref" == c ? a = t[c] : p[c] = t[c];
    var l = { type: e, props: p, key: n, ref: a, __k: null, __: null, __b: 0, __e: null, __c: null, constructor: void 0, __v: --f, __i: -1, __u: 0, __source: i, __self: u2 };
    if ("function" == typeof e && (a = e.defaultProps)) for (c in a) void 0 === p[c] && (p[c] = a[c]);
    return preact.options.vnode && preact.options.vnode(l), l;
  }
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var defaultAttributes = {
    outline: {
      xmlns: "http://www.w3.org/2000/svg",
      width: 24,
      height: 24,
      viewBox: "0 0 24 24",
      fill: "none",
      stroke: "currentColor",
      "stroke-width": 2,
      "stroke-linecap": "round",
      "stroke-linejoin": "round"
    },
    filled: {
      xmlns: "http://www.w3.org/2000/svg",
      width: 24,
      height: 24,
      viewBox: "0 0 24 24",
      fill: "currentColor",
      stroke: "none"
    }
  };
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  const createPreactComponent = (type2, iconName, iconNamePascal, iconNode) => {
    const Component2 = ({
      color = "currentColor",
      size = 24,
      stroke = 2,
      title,
      children,
      className = "",
      class: classes = "",
      style,
      ...rest
    }) => preact.h(
      "svg",
      {
        ...defaultAttributes[type2],
        width: String(size),
        height: String(size),
        class: [`tabler-icon`, `tabler-icon-${iconName}`, classes, className].join(" "),
        ...type2 === "filled" ? {
          fill: color
        } : {
          "stroke-width": stroke,
          stroke: color
        },
        style,
        ...rest
      },
      [
        title && preact.h("title", {}, title),
        ...iconNode.map(([tag, attrs]) => preact.h(tag, attrs)),
        ...preact.toChildArray(children)
      ]
    );
    Component2.displayName = `${iconNamePascal}`;
    return Component2;
  };
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconArrowUpRight = createPreactComponent("outline", "arrow-up-right", "IconArrowUpRight", [["path", { "d": "M17 7l-10 10", "key": "svg-0" }], ["path", { "d": "M8 7l9 0l0 9", "key": "svg-1" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconArrowsMaximize = createPreactComponent("outline", "arrows-maximize", "IconArrowsMaximize", [["path", { "d": "M16 4l4 0l0 4", "key": "svg-0" }], ["path", { "d": "M14 10l6 -6", "key": "svg-1" }], ["path", { "d": "M8 20l-4 0l0 -4", "key": "svg-2" }], ["path", { "d": "M4 20l6 -6", "key": "svg-3" }], ["path", { "d": "M16 20l4 0l0 -4", "key": "svg-4" }], ["path", { "d": "M14 14l6 6", "key": "svg-5" }], ["path", { "d": "M8 4l-4 0l0 4", "key": "svg-6" }], ["path", { "d": "M4 4l6 6", "key": "svg-7" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconArrowsMinimize = createPreactComponent("outline", "arrows-minimize", "IconArrowsMinimize", [["path", { "d": "M5 9l4 0l0 -4", "key": "svg-0" }], ["path", { "d": "M3 3l6 6", "key": "svg-1" }], ["path", { "d": "M5 15l4 0l0 4", "key": "svg-2" }], ["path", { "d": "M3 21l6 -6", "key": "svg-3" }], ["path", { "d": "M19 9l-4 0l0 -4", "key": "svg-4" }], ["path", { "d": "M15 9l6 -6", "key": "svg-5" }], ["path", { "d": "M19 15l-4 0l0 4", "key": "svg-6" }], ["path", { "d": "M15 15l6 6", "key": "svg-7" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconBookmark = createPreactComponent("outline", "bookmark", "IconBookmark", [["path", { "d": "M18 7v14l-6 -4l-6 4v-14a4 4 0 0 1 4 -4h4a4 4 0 0 1 4 4z", "key": "svg-0" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconCircleCheck = createPreactComponent("outline", "circle-check", "IconCircleCheck", [["path", { "d": "M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0", "key": "svg-0" }], ["path", { "d": "M9 12l2 2l4 -4", "key": "svg-1" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconCircleDashed = createPreactComponent("outline", "circle-dashed", "IconCircleDashed", [["path", { "d": "M8.56 3.69a9 9 0 0 0 -2.92 1.95", "key": "svg-0" }], ["path", { "d": "M3.69 8.56a9 9 0 0 0 -.69 3.44", "key": "svg-1" }], ["path", { "d": "M3.69 15.44a9 9 0 0 0 1.95 2.92", "key": "svg-2" }], ["path", { "d": "M8.56 20.31a9 9 0 0 0 3.44 .69", "key": "svg-3" }], ["path", { "d": "M15.44 20.31a9 9 0 0 0 2.92 -1.95", "key": "svg-4" }], ["path", { "d": "M20.31 15.44a9 9 0 0 0 .69 -3.44", "key": "svg-5" }], ["path", { "d": "M20.31 8.56a9 9 0 0 0 -1.95 -2.92", "key": "svg-6" }], ["path", { "d": "M15.44 3.69a9 9 0 0 0 -3.44 -.69", "key": "svg-7" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconDatabaseExport = createPreactComponent("outline", "database-export", "IconDatabaseExport", [["path", { "d": "M4 6c0 1.657 3.582 3 8 3s8 -1.343 8 -3s-3.582 -3 -8 -3s-8 1.343 -8 3", "key": "svg-0" }], ["path", { "d": "M4 6v6c0 1.657 3.582 3 8 3c1.118 0 2.183 -.086 3.15 -.241", "key": "svg-1" }], ["path", { "d": "M20 12v-6", "key": "svg-2" }], ["path", { "d": "M4 12v6c0 1.657 3.582 3 8 3c.157 0 .312 -.002 .466 -.005", "key": "svg-3" }], ["path", { "d": "M16 19h6", "key": "svg-4" }], ["path", { "d": "M19 16l3 3l-3 3", "key": "svg-5" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconDatabaseImport = createPreactComponent("outline", "database-import", "IconDatabaseImport", [["path", { "d": "M4 6c0 1.657 3.582 3 8 3s8 -1.343 8 -3s-3.582 -3 -8 -3s-8 1.343 -8 3", "key": "svg-0" }], ["path", { "d": "M4 6v6c0 1.657 3.582 3 8 3c.856 0 1.68 -.05 2.454 -.144m5.546 -2.856v-6", "key": "svg-1" }], ["path", { "d": "M4 12v6c0 1.657 3.582 3 8 3c.171 0 .341 -.002 .51 -.006", "key": "svg-2" }], ["path", { "d": "M19 22v-6", "key": "svg-3" }], ["path", { "d": "M22 19l-3 -3l-3 3", "key": "svg-4" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconExclamationCircle = createPreactComponent("outline", "exclamation-circle", "IconExclamationCircle", [["path", { "d": "M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0", "key": "svg-0" }], ["path", { "d": "M12 9v4", "key": "svg-1" }], ["path", { "d": "M12 16v.01", "key": "svg-2" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconExternalLink = createPreactComponent("outline", "external-link", "IconExternalLink", [["path", { "d": "M12 6h-6a2 2 0 0 0 -2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-6", "key": "svg-0" }], ["path", { "d": "M11 13l9 -9", "key": "svg-1" }], ["path", { "d": "M15 4h5v5", "key": "svg-2" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconFileDownload = createPreactComponent("outline", "file-download", "IconFileDownload", [["path", { "d": "M14 3v4a1 1 0 0 0 1 1h4", "key": "svg-0" }], ["path", { "d": "M17 21h-10a2 2 0 0 1 -2 -2v-14a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2z", "key": "svg-1" }], ["path", { "d": "M12 17v-6", "key": "svg-2" }], ["path", { "d": "M9.5 14.5l2.5 2.5l2.5 -2.5", "key": "svg-3" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconHeart = createPreactComponent("outline", "heart", "IconHeart", [["path", { "d": "M19.5 12.572l-7.5 7.428l-7.5 -7.428a5 5 0 1 1 7.5 -6.566a5 5 0 1 1 7.5 6.572", "key": "svg-0" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconHelp = createPreactComponent("outline", "help", "IconHelp", [["path", { "d": "M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0", "key": "svg-0" }], ["path", { "d": "M12 17l0 .01", "key": "svg-1" }], ["path", { "d": "M12 13.5a1.5 1.5 0 0 1 1 -1.5a2.6 2.6 0 1 0 -3 -4", "key": "svg-2" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconInfoCircle = createPreactComponent("outline", "info-circle", "IconInfoCircle", [["path", { "d": "M3 12a9 9 0 1 0 18 0a9 9 0 0 0 -18 0", "key": "svg-0" }], ["path", { "d": "M12 9h.01", "key": "svg-1" }], ["path", { "d": "M11 12h1v4h1", "key": "svg-2" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconLayoutColumns = createPreactComponent("outline", "layout-columns", "IconLayoutColumns", [["path", { "d": "M4 4m0 2a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2z", "key": "svg-0" }], ["path", { "d": "M12 4l0 16", "key": "svg-1" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconLayoutGrid = createPreactComponent("outline", "layout-grid", "IconLayoutGrid", [["path", { "d": "M4 4m0 1a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-4a1 1 0 0 1 -1 -1z", "key": "svg-0" }], ["path", { "d": "M14 4m0 1a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-4a1 1 0 0 1 -1 -1z", "key": "svg-1" }], ["path", { "d": "M4 14m0 1a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-4a1 1 0 0 1 -1 -1z", "key": "svg-2" }], ["path", { "d": "M14 14m0 1a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-4a1 1 0 0 1 -1 -1z", "key": "svg-3" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconLink = createPreactComponent("outline", "link", "IconLink", [["path", { "d": "M9 15l6 -6", "key": "svg-0" }], ["path", { "d": "M11 6l.463 -.536a5 5 0 0 1 7.071 7.072l-.534 .464", "key": "svg-1" }], ["path", { "d": "M13 18l-.397 .534a5.068 5.068 0 0 1 -7.127 0a4.972 4.972 0 0 1 0 -7.071l.524 -.463", "key": "svg-2" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconPhoto = createPreactComponent("outline", "photo", "IconPhoto", [["path", { "d": "M15 8h.01", "key": "svg-0" }], ["path", { "d": "M3 6a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v12a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3v-12z", "key": "svg-1" }], ["path", { "d": "M3 16l5 -5c.928 -.893 2.072 -.893 3 0l5 5", "key": "svg-2" }], ["path", { "d": "M14 14l1 -1c.928 -.893 2.072 -.893 3 0l3 3", "key": "svg-3" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconRefresh = createPreactComponent("outline", "refresh", "IconRefresh", [["path", { "d": "M20 11a8.1 8.1 0 0 0 -15.5 -2m-.5 -4v4h4", "key": "svg-0" }], ["path", { "d": "M4 13a8.1 8.1 0 0 0 15.5 2m.5 4v-4h-4", "key": "svg-1" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconRepeat = createPreactComponent("outline", "repeat", "IconRepeat", [["path", { "d": "M4 12v-3a3 3 0 0 1 3 -3h13m-3 -3l3 3l-3 3", "key": "svg-0" }], ["path", { "d": "M20 12v3a3 3 0 0 1 -3 3h-13m3 3l-3 -3l3 -3", "key": "svg-1" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconReportAnalytics = createPreactComponent("outline", "report-analytics", "IconReportAnalytics", [["path", { "d": "M9 5h-2a2 2 0 0 0 -2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-12a2 2 0 0 0 -2 -2h-2", "key": "svg-0" }], ["path", { "d": "M9 3m0 2a2 2 0 0 1 2 -2h2a2 2 0 0 1 2 2v0a2 2 0 0 1 -2 2h-2a2 2 0 0 1 -2 -2z", "key": "svg-1" }], ["path", { "d": "M9 17v-5", "key": "svg-2" }], ["path", { "d": "M12 17v-1", "key": "svg-3" }], ["path", { "d": "M15 17v-3", "key": "svg-4" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconSearch = createPreactComponent("outline", "search", "IconSearch", [["path", { "d": "M10 10m-7 0a7 7 0 1 0 14 0a7 7 0 1 0 -14 0", "key": "svg-0" }], ["path", { "d": "M21 21l-6 -6", "key": "svg-1" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconSettings = createPreactComponent("outline", "settings", "IconSettings", [["path", { "d": "M10.325 4.317c.426 -1.756 2.924 -1.756 3.35 0a1.724 1.724 0 0 0 2.573 1.066c1.543 -.94 3.31 .826 2.37 2.37a1.724 1.724 0 0 0 1.065 2.572c1.756 .426 1.756 2.924 0 3.35a1.724 1.724 0 0 0 -1.066 2.573c.94 1.543 -.826 3.31 -2.37 2.37a1.724 1.724 0 0 0 -2.572 1.065c-.426 1.756 -2.924 1.756 -3.35 0a1.724 1.724 0 0 0 -2.573 -1.066c-1.543 .94 -3.31 -.826 -2.37 -2.37a1.724 1.724 0 0 0 -1.065 -2.572c-1.756 -.426 -1.756 -2.924 0 -3.35a1.724 1.724 0 0 0 1.066 -2.573c-.94 -1.543 .826 -3.31 2.37 -2.37c1 .608 2.296 .07 2.572 -1.065z", "key": "svg-0" }], ["path", { "d": "M9 12a3 3 0 1 0 6 0a3 3 0 0 0 -6 0", "key": "svg-1" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconSortAscending = createPreactComponent("outline", "sort-ascending", "IconSortAscending", [["path", { "d": "M4 6l7 0", "key": "svg-0" }], ["path", { "d": "M4 12l7 0", "key": "svg-1" }], ["path", { "d": "M4 18l9 0", "key": "svg-2" }], ["path", { "d": "M15 9l3 -3l3 3", "key": "svg-3" }], ["path", { "d": "M18 6l0 12", "key": "svg-4" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconSortDescending = createPreactComponent("outline", "sort-descending", "IconSortDescending", [["path", { "d": "M4 6l9 0", "key": "svg-0" }], ["path", { "d": "M4 12l7 0", "key": "svg-1" }], ["path", { "d": "M4 18l7 0", "key": "svg-2" }], ["path", { "d": "M15 15l3 3l3 -3", "key": "svg-3" }], ["path", { "d": "M18 6l0 12", "key": "svg-4" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconTable = createPreactComponent("outline", "table", "IconTable", [["path", { "d": "M3 5a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v14a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2v-14z", "key": "svg-0" }], ["path", { "d": "M3 10h18", "key": "svg-1" }], ["path", { "d": "M10 3v18", "key": "svg-2" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconTrashX = createPreactComponent("outline", "trash-x", "IconTrashX", [["path", { "d": "M4 7h16", "key": "svg-0" }], ["path", { "d": "M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2l1 -12", "key": "svg-1" }], ["path", { "d": "M9 7v-3a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v3", "key": "svg-2" }], ["path", { "d": "M10 12l4 4m0 -4l-4 4", "key": "svg-3" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconTrash = createPreactComponent("outline", "trash", "IconTrash", [["path", { "d": "M4 7l16 0", "key": "svg-0" }], ["path", { "d": "M10 11l0 6", "key": "svg-1" }], ["path", { "d": "M14 11l0 6", "key": "svg-2" }], ["path", { "d": "M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2l1 -12", "key": "svg-3" }], ["path", { "d": "M9 7v-3a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v3", "key": "svg-4" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconX = createPreactComponent("outline", "x", "IconX", [["path", { "d": "M18 6l-12 12", "key": "svg-0" }], ["path", { "d": "M6 6l12 12", "key": "svg-1" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconBrandGithubFilled = createPreactComponent("filled", "brand-github-filled", "IconBrandGithubFilled", [["path", { "d": "M5.315 2.1c.791 -.113 1.9 .145 3.333 .966l.272 .161l.16 .1l.397 -.083a13.3 13.3 0 0 1 4.59 -.08l.456 .08l.396 .083l.161 -.1c1.385 -.84 2.487 -1.17 3.322 -1.148l.164 .008l.147 .017l.076 .014l.05 .011l.144 .047a1 1 0 0 1 .53 .514a5.2 5.2 0 0 1 .397 2.91l-.047 .267l-.046 .196l.123 .163c.574 .795 .93 1.728 1.03 2.707l.023 .295l.007 .272c0 3.855 -1.659 5.883 -4.644 6.68l-.245 .061l-.132 .029l.014 .161l.008 .157l.004 .365l-.002 .213l-.003 3.834a1 1 0 0 1 -.883 .993l-.117 .007h-6a1 1 0 0 1 -.993 -.883l-.007 -.117v-.734c-1.818 .26 -3.03 -.424 -4.11 -1.878l-.535 -.766c-.28 -.396 -.455 -.579 -.589 -.644l-.048 -.019a1 1 0 0 1 .564 -1.918c.642 .188 1.074 .568 1.57 1.239l.538 .769c.76 1.079 1.36 1.459 2.609 1.191l.001 -.678l-.018 -.168a5.03 5.03 0 0 1 -.021 -.824l.017 -.185l.019 -.12l-.108 -.024c-2.976 -.71 -4.703 -2.573 -4.875 -6.139l-.01 -.31l-.004 -.292a5.6 5.6 0 0 1 .908 -3.051l.152 -.222l.122 -.163l-.045 -.196a5.2 5.2 0 0 1 .145 -2.642l.1 -.282l.106 -.253a1 1 0 0 1 .529 -.514l.144 -.047l.154 -.03z", "key": "svg-0" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconBrandTwitterFilled = createPreactComponent("filled", "brand-twitter-filled", "IconBrandTwitterFilled", [["path", { "d": "M14.058 3.41c-1.807 .767 -2.995 2.453 -3.056 4.38l-.002 .182l-.243 -.023c-2.392 -.269 -4.498 -1.512 -5.944 -3.531a1 1 0 0 0 -1.685 .092l-.097 .186l-.049 .099c-.719 1.485 -1.19 3.29 -1.017 5.203l.03 .273c.283 2.263 1.5 4.215 3.779 5.679l.173 .107l-.081 .043c-1.315 .663 -2.518 .952 -3.827 .9c-1.056 -.04 -1.446 1.372 -.518 1.878c3.598 1.961 7.461 2.566 10.792 1.6c4.06 -1.18 7.152 -4.223 8.335 -8.433l.127 -.495c.238 -.993 .372 -2.006 .401 -3.024l.003 -.332l.393 -.779l.44 -.862l.214 -.434l.118 -.247c.265 -.565 .456 -1.033 .574 -1.43l.014 -.056l.008 -.018c.22 -.593 -.166 -1.358 -.941 -1.358l-.122 .007a.997 .997 0 0 0 -.231 .057l-.086 .038a7.46 7.46 0 0 1 -.88 .36l-.356 .115l-.271 .08l-.772 .214c-1.336 -1.118 -3.144 -1.254 -5.012 -.554l-.211 .084z", "key": "svg-0" }]]);
  /**
   * @license @tabler/icons-preact v3.31.0 - MIT
   *
   * This source code is licensed under the MIT license.
   * See the LICENSE file in the root directory of this source tree.
   */
  var IconPlayerPlayFilled = createPreactComponent("filled", "player-play-filled", "IconPlayerPlayFilled", [["path", { "d": "M6 4v16a1 1 0 0 0 1.524 .852l13 -8a1 1 0 0 0 0 -1.704l-13 -8a1 1 0 0 0 -1.524 .852z", "key": "svg-0" }]]);
  const ar = { "exporter": { "ID": "ID", "Date": "التاريخ", "Content": "المحتوى", "Show Full Text": "عرض النص الكامل", "Media": "الوسائط", "Screen Name": "اسم المستخدم", "Profile Name": "اسم الملف الشخصي", "Profile Image": "صورة الملف الشخصي", "User ID": "معرّف المستخدم", "Replying To": "ردًا على", "RT Source": "مصدر إعادة التغريد", "Quote Source": "مصدر الاقتباس", "Media Tags": "وسوم الوسائط", "Favorites": "المفضلات", "Retweets": "إعادات التغريد", "Bookmarks": "العلامات المرجعية", "Quotes": "الاقتباسات", "Replies": "الردود", "Views": "المشاهدات", "Favorited": "مضاف إلى المفضلة", "Retweeted": "أُعيد تغريده", "Bookmarked": "مضاف إلى العلامات المرجعية", "Bookmark Folder": "مجلد العلامات المرجعية", "URL": "URL", "Actions": "الإجراءات", "Details": "التفاصيل", "Description": "الوصف", "Profile Banner": "لافتة الملف الشخصي", "Followers": "المتابعون", "FollowingCount": "عدد المتابَعين", "Statuses": "المنشورات", "Favourites": "المفضلات", "Listed": "مدرج في القوائم", "Location": "الموقع", "Website": "الموقع الإلكتروني", "Birthdate": "تاريخ الميلاد", "Verified Type": "نوع التوثيق", "Blue Verified": "موثّق Blue", "Following": "يتابع", "Follows You": "يتابعك", "Can DM": "يمكن مراسلته خاصًا", "Protected": "محمي", "Created At": "تاريخ الإنشاء", "Sender": "المرسل", "Recipient": "المستلم", "Conversation ID": "معرّف المحادثة", "Conversation Type": "نوع المحادثة", "Data": "البيانات", "Export captured data as JSON/HTML/CSV file. This may take a while depending on the amount of data. The exported file does not include media files such as images and videos but only the URLs.": "صدّر البيانات الملتقطة كملف JSON/HTML/CSV. قد يستغرق ذلك وقتًا حسب حجم البيانات. لا يتضمن الملف المصدّر ملفات وسائط مثل الصور والفيديو، بل يتضمن عناوين URL فقط.", "Data length:": "طول البيانات:", "Export scope:": "نطاق التصدير:", "All current results": "كل النتائج الحالية", "Selected rows": "الصفوف المحددة", "Pinned result set": "مجموعة نتائج مثبتة", "Query": "الاستعلام", "Sort": "الترتيب", "Include all metadata:": "تضمين كل البيانات الوصفية:", "Export as:": "تصدير كـ:", "No data selected.": "لم يتم تحديد بيانات.", "Cancel": "إلغاء", "Start Export": "بدء التصدير", "Download and save media files from captured data. This may take a while depending on the amount of data. Media that will be downloaded includes: profile images, profile banners (for users), images, videos (for tweets).": "تنزيل وحفظ ملفات الوسائط من البيانات الملتقطة. قد يستغرق ذلك وقتًا حسب حجم البيانات. تشمل الوسائط التي سيتم تنزيلها صور الملفات الشخصية ولافتاتها (للمستخدمين) والصور والفيديوهات (للتغريدات).", "For more than 100 media or large files, it is recommended to copy the URLs and download them with an external download manager such as aria2.": "لأكثر من 100 عنصر وسائط أو للملفات الكبيرة، يُوصى بنسخ عناوين URL وتنزيلها بمدير تنزيل خارجي مثل aria2.", "Browser ZIP export now uses bounded parallel downloads. For very large video-heavy jobs, URL or aria2 export is still the safest low-memory path.": "يستخدم تصدير ZIP في المتصفح الآن تنزيلات متوازية محدودة. للأعمال الكبيرة جدًا وكثيرة الفيديو، يبقى تصدير URL أو aria2 المسار الأكثر أمانًا والأقل استهلاكًا للذاكرة.", "Filename template:": "قالب اسم الملف:", "Download scheduler": "مجدول التنزيل", "Faster defaults are intended for bulk CDN transfer.": "القيم الافتراضية الأسرع مخصصة للنقل الجماعي من CDN.", "Start delay (ms)": "تأخير البدء (ms)", "Global parallel": "التوازي العام", "Per host": "لكل مضيف", "Videos": "الفيديوهات", "Retries": "إعادة المحاولة", "Use aria2 format:": "استخدام تنسيق aria2:", "Click for more information. Each URL will be on a new line, with its filename on the next line. This format is compatible with aria2.": "انقر لمزيد من المعلومات. سيكون كل URL في سطر جديد، واسم ملفه في السطر التالي. هذا التنسيق متوافق مع aria2.", "Rate limit (ms):": "حد المعدّل (ms):", "Media Filter:": "مرشح الوسائط:", "File Name": "اسم الملف", "Media Type": "نوع الوسائط", "Download URL": "رابط التنزيل", "No media selected.": "لم يتم تحديد وسائط.", "Preview limited to first 250 media items.": "المعاينة محدودة بأول 250 عنصر وسائط.", "Zipping": "جارٍ الضغط", "Copied!": "تم النسخ!", "Copy URLs": "نسخ الروابط", "The tweet ID": "معرّف التغريدة", "The username of tweet author": "اسم مستخدم كاتب التغريدة", "The profile name of tweet author": "اسم ملف كاتب التغريدة الشخصي", "The media index in tweet (start from 0)": "فهرس الوسيط في التغريدة (يبدأ من 0)", "The order of media in tweet (1/2/3/4)": "ترتيب الوسيط في التغريدة (1/2/3/4)", "The post date in YYYYMMDD format": "تاريخ النشر بتنسيق YYYYMMDD", "The post time in HHmmss format": "وقت النشر بتنسيق HHmmss", "The media type (photo/video/animated_gif)": "نوع الوسيط (photo/video/animated_gif)", "The file extension of media (jpg/png/mp4)": "امتداد ملف الوسيط (jpg/png/mp4)", "Failed to export media. Open DevTools for more details.": "فشل تصدير الوسائط. افتح DevTools لمزيد من التفاصيل.", "Failed to copy media URLs. Open DevTools for more details.": "فشل نسخ روابط الوسائط. افتح DevTools لمزيد من التفاصيل.", "filter.photo": "صورة", "filter.video": "فيديو", "filter.animated_gif": "GIF", "filter.retweet": "تضمين إعادة التغريد" }, "common": { "Open Control Panel": "فتح لوحة التحكم", "Browse around to capture more data.": "تصفّح لالتقاط مزيد من البيانات.", "Settings": "الإعدادات", "General": "عام", "Theme": "السمة", "Language": "اللغة", "Debug": "تصحيح الأخطاء", "Date Time Format": "تنسيق التاريخ والوقت", "Click for more information. This will take effect on both previewer and exported files.": "انقر لمزيد من المعلومات. سيؤثر هذا في المعاينة والملفات المصدّرة.", "Use dedicated DB for accounts": "استخدام قاعدة بيانات مخصصة لكل حساب", "This will create separate database for each Twitter account, which can help reduce the chance of data mixing when you use multiple accounts.": "سينشئ هذا قاعدة بيانات منفصلة لكل حساب Twitter، مما يساعد على تقليل احتمال اختلاط البيانات عند استخدام عدة حسابات.", "Local Database": "قاعدة البيانات المحلية", "Analyze DB": "تحليل", "Export DB": "تصدير", "Clear DB": "مسح", "Bundle Export": "تصدير حزمة", "Bundle Library": "مكتبة الحزم", "QC Session": "جلسة فحص الجودة", "Diagnostic capture": "التقاط تشخيصي", "Diagnostic buffers cleared.": "تم مسح مخازن التشخيص المؤقتة.", "Clear Buffers": "مسح المخازن", "Preparing...": "جارٍ التحضير...", "Preparing diagnostics...": "جارٍ تحضير التشخيصات...", "Export QC Diagnostics": "تصدير تشخيصات فحص الجودة", "Export Diagnostics Bundle": "تصدير حزمة التشخيص", "QC idle.": "فحص الجودة خامل.", "Are you sure to clear all data in the database?": "هل أنت متأكد من مسح كل البيانات في قاعدة البيانات؟", "Database cleared.": "تم مسح قاعدة البيانات.", "Module": "وحدة", "Modules (Scroll to see more)": "الوحدات (مرّر لرؤية المزيد)", "About": "حول", "Version": "الإصدار", "Search...": "بحث...", "Something went wrong.": "حدث خطأ ما.", "Error:": "خطأ:", "Captured:": "تم الالتقاط:", "Rows per page:": "الصفوف لكل صفحة:", "Relation Types": "أنواع العلاقات", "Subject Accounts": "الحسابات محلّ العلاقة", "Subject User IDs": "معرّفات المستخدمين محلّ العلاقة", "Last Observed At": "آخر رصد", "A - B of N items": "{{from}} - {{to}} من {{total}} عنصرًا", "No data available.": "لا توجد بيانات متاحة.", "No media available.": "لا توجد وسائط متاحة.", "Clear": "مسح", "Bookmark folders": "مجلدات العلامات المرجعية", "1 folder selected": "تم اختيار مجلد واحد", "{{count}} folders selected": "تم اختيار {{count}} مجلدات", "Export Media": "تصدير الوسائط", "Export Data": "تصدير البيانات", "Export Search History": "تصدير سجل البحث", "JSON View": "عرض JSON", "Media View": "عرض الوسائط", "Bookmarks": "العلامات المرجعية", "Tweets": "التغريدات", "Users": "المستخدمون", "User Tweets": "تغريدات المستخدم", "User Media": "وسائط المستخدم", "Tweet Details": "تفاصيل التغريدة", "Search Timeline": "خطّ البحث الزمني", "Home Timeline": "خطّ الصفحة الرئيسية الزمني", "List Timeline": "خطّ القائمة الزمني", "Community Timeline": "خطّ المجتمع الزمني", "Community Members": "أعضاء المجتمع", "List Members": "أعضاء القائمة", "List Subscribers": "مشتركو القائمة", "Direct Messages": "الرسائل الخاصة", "Interaction Events": "أحداث التفاعل", "Local Search": "بحث محلي", "Runtime Logs": "سجلات التشغيل", "Bundle Viewer": "عارض الحزم", "{{count}} imported bundles": "{{count}} حزم مستوردة", "Search indexed tweets with Twitter-style operators": "ابحث في التغريدات المفهرسة باستخدام معاملات شبيهة بـ Twitter", "Local Recorder Search": "بحث المسجّل المحلي", "Quotes": "الاقتباسات", "CommunityMembers": "أعضاء المجتمع", "CommunityTimeline": "خطّ المجتمع الزمني", "DirectMessages": "الرسائل الخاصة", "Followers": "المتابعون", "Following": "يتابع", "HomeTimeline": "خطّ الصفحة الرئيسية", "Likes": "الإعجابات", "ListMembers": "أعضاء القائمة", "ListSubscribers": "مشتركو القائمة", "ListTimeline": "خطّ القائمة", "InteractionEvents": "أحداث التفاعل", "LocalSearch": "بحث محلي", "RawCapture": "التقاط خام", "Retweeters": "معيدو التغريد", "RetweetersModule": "معيدو التغريد", "RuntimeLogs": "سجلات التشغيل", "SearchTimeline": "خطّ البحث", "TweetIndex": "فهرس التغريدات", "TweetDetail": "تفاصيل التغريدة", "UserDetail": "تفاصيل المستخدم", "UserMedia": "وسائط المستخدم", "UserTweets": "تغريدات المستخدم" } };
  const bn = { "exporter": { "ID": "ID", "Date": "তারিখ", "Content": "বিষয়বস্তু", "Show Full Text": "সম্পূর্ণ লেখা দেখান", "Media": "মিডিয়া", "Screen Name": "ব্যবহারকারীর নাম", "Profile Name": "প্রোফাইল নাম", "Profile Image": "প্রোফাইল ছবি", "User ID": "ব্যবহারকারী ID", "Replying To": "যার উত্তরে", "RT Source": "RT উৎস", "Quote Source": "কোট উৎস", "Media Tags": "মিডিয়া ট্যাগ", "Favorites": "প্রিয়", "Retweets": "রিটুইট", "Bookmarks": "বুকমার্ক", "Quotes": "কোট", "Replies": "উত্তর", "Views": "দেখা", "Favorited": "প্রিয় করা হয়েছে", "Retweeted": "রিটুইট করা হয়েছে", "Bookmarked": "বুকমার্ক করা হয়েছে", "Bookmark Folder": "বুকমার্ক ফোল্ডার", "URL": "URL", "Actions": "ক্রিয়া", "Details": "বিস্তারিত", "Description": "বিবরণ", "Profile Banner": "প্রোফাইল ব্যানার", "Followers": "ফলোয়ার", "FollowingCount": "ফলো করা সংখ্যা", "Statuses": "পোস্ট", "Favourites": "প্রিয়", "Listed": "লিস্টে আছে", "Location": "অবস্থান", "Website": "ওয়েবসাইট", "Birthdate": "জন্মতারিখ", "Verified Type": "যাচাইকরণ ধরন", "Blue Verified": "Blue যাচাইকৃত", "Following": "ফলো করছে", "Follows You": "আপনাকে ফলো করে", "Can DM": "DM করা যায়", "Protected": "সুরক্ষিত", "Created At": "তৈরি হয়েছে", "Sender": "প্রেরক", "Recipient": "প্রাপক", "Conversation ID": "কথোপকথন ID", "Conversation Type": "কথোপকথনের ধরন", "Data": "ডেটা", "Export captured data as JSON/HTML/CSV file. This may take a while depending on the amount of data. The exported file does not include media files such as images and videos but only the URLs.": "ক্যাপচার করা ডেটা JSON/HTML/CSV ফাইল হিসেবে এক্সপোর্ট করুন। ডেটার পরিমাণ অনুযায়ী সময় লাগতে পারে। এক্সপোর্ট করা ফাইলে ছবি বা ভিডিওর মতো মিডিয়া ফাইল থাকবে না, শুধু URL থাকবে।", "Data length:": "ডেটার দৈর্ঘ্য:", "Export scope:": "এক্সপোর্ট পরিসর:", "All current results": "বর্তমান সব ফলাফল", "Selected rows": "নির্বাচিত সারি", "Pinned result set": "পিন করা ফলাফল সেট", "Query": "কোয়েরি", "Sort": "সাজানো", "Include all metadata:": "সব মেটাডেটা অন্তর্ভুক্ত করুন:", "Export as:": "যে রূপে এক্সপোর্ট:", "No data selected.": "কোনো ডেটা নির্বাচিত নয়।", "Cancel": "বাতিল", "Start Export": "এক্সপোর্ট শুরু করুন", "Download and save media files from captured data. This may take a while depending on the amount of data. Media that will be downloaded includes: profile images, profile banners (for users), images, videos (for tweets).": "ক্যাপচার করা ডেটা থেকে মিডিয়া ফাইল ডাউনলোড ও সংরক্ষণ করুন। ডেটার পরিমাণ অনুযায়ী সময় লাগতে পারে। ডাউনলোড হবে: প্রোফাইল ছবি, প্রোফাইল ব্যানার (ব্যবহারকারীর জন্য), ছবি ও ভিডিও (টুইটের জন্য)।", "For more than 100 media or large files, it is recommended to copy the URLs and download them with an external download manager such as aria2.": "১০০টির বেশি মিডিয়া বা বড় ফাইলের জন্য URL কপি করে aria2-এর মতো বাহ্যিক ডাউনলোড ম্যানেজার দিয়ে ডাউনলোড করার পরামর্শ দেওয়া হয়।", "Browser ZIP export now uses bounded parallel downloads. For very large video-heavy jobs, URL or aria2 export is still the safest low-memory path.": "ব্রাউজার ZIP এক্সপোর্ট এখন সীমিত সমান্তরাল ডাউনলোড ব্যবহার করে। খুব বড় ভিডিও-ভিত্তিক কাজের জন্য URL বা aria2 এক্সপোর্ট এখনও কম-মেমরির সবচেয়ে নিরাপদ পথ।", "Filename template:": "ফাইলনাম টেমপ্লেট:", "Download scheduler": "ডাউনলোড শিডিউলার", "Faster defaults are intended for bulk CDN transfer.": "দ্রুততর ডিফল্টগুলো বড় CDN ট্রান্সফারের জন্য।", "Start delay (ms)": "শুরুর বিলম্ব (ms)", "Global parallel": "গ্লোবাল সমান্তরাল", "Per host": "প্রতি হোস্ট", "Videos": "ভিডিও", "Retries": "পুনঃচেষ্টা", "Use aria2 format:": "aria2 ফরম্যাট ব্যবহার করুন:", "Click for more information. Each URL will be on a new line, with its filename on the next line. This format is compatible with aria2.": "আরও তথ্যের জন্য ক্লিক করুন। প্রতিটি URL নতুন লাইনে থাকবে, আর পরের লাইনে তার ফাইলনাম থাকবে। এই ফরম্যাট aria2-এর সঙ্গে সামঞ্জস্যপূর্ণ।", "Rate limit (ms):": "রেট সীমা (ms):", "Media Filter:": "মিডিয়া ফিল্টার:", "File Name": "ফাইলনাম", "Media Type": "মিডিয়ার ধরন", "Download URL": "ডাউনলোড URL", "No media selected.": "কোনো মিডিয়া নির্বাচিত নয়।", "Preview limited to first 250 media items.": "প্রিভিউ প্রথম ২৫০টি মিডিয়া আইটেমে সীমিত।", "Zipping": "ZIP করা হচ্ছে", "Copied!": "কপি হয়েছে!", "Copy URLs": "URL কপি করুন", "The tweet ID": "টুইট ID", "The username of tweet author": "টুইট লেখকের ব্যবহারকারীর নাম", "The profile name of tweet author": "টুইট লেখকের প্রোফাইল নাম", "The media index in tweet (start from 0)": "টুইটে মিডিয়ার ইনডেক্স (০ থেকে শুরু)", "The order of media in tweet (1/2/3/4)": "টুইটে মিডিয়ার ক্রম (1/2/3/4)", "The post date in YYYYMMDD format": "YYYYMMDD ফরম্যাটে পোস্টের তারিখ", "The post time in HHmmss format": "HHmmss ফরম্যাটে পোস্টের সময়", "The media type (photo/video/animated_gif)": "মিডিয়ার ধরন (photo/video/animated_gif)", "The file extension of media (jpg/png/mp4)": "মিডিয়া ফাইল এক্সটেনশন (jpg/png/mp4)", "Failed to export media. Open DevTools for more details.": "মিডিয়া এক্সপোর্ট ব্যর্থ। আরও জানতে DevTools খুলুন।", "Failed to copy media URLs. Open DevTools for more details.": "মিডিয়া URL কপি ব্যর্থ। আরও জানতে DevTools খুলুন।", "filter.photo": "ছবি", "filter.video": "ভিডিও", "filter.animated_gif": "GIF", "filter.retweet": "রিটুইট অন্তর্ভুক্ত করুন" }, "common": { "Open Control Panel": "কন্ট্রোল প্যানেল খুলুন", "Browse around to capture more data.": "আরও ডেটা ক্যাপচার করতে ব্রাউজ করুন।", "Settings": "সেটিংস", "General": "সাধারণ", "Theme": "থিম", "Language": "ভাষা", "Debug": "ডিবাগ", "Date Time Format": "তারিখ-সময় বিন্যাস", "Click for more information. This will take effect on both previewer and exported files.": "আরও তথ্যের জন্য ক্লিক করুন। এটি প্রিভিউয়ার এবং এক্সপোর্ট করা ফাইল—দুটিতেই প্রযোজ্য হবে।", "Use dedicated DB for accounts": "প্রতিটি অ্যাকাউন্টের জন্য আলাদা DB ব্যবহার করুন", "This will create separate database for each Twitter account, which can help reduce the chance of data mixing when you use multiple accounts.": "এটি প্রতিটি Twitter অ্যাকাউন্টের জন্য আলাদা ডেটাবেস তৈরি করবে, ফলে একাধিক অ্যাকাউন্ট ব্যবহার করার সময় ডেটা মিশে যাওয়ার ঝুঁকি কমে।", "Local Database": "স্থানীয় ডেটাবেস", "Analyze DB": "বিশ্লেষণ", "Export DB": "এক্সপোর্ট", "Clear DB": "মুছুন", "Bundle Export": "বান্ডল এক্সপোর্ট", "Bundle Library": "বান্ডল লাইব্রেরি", "QC Session": "QC সেশন", "Diagnostic capture": "ডায়াগনস্টিক ক্যাপচার", "Diagnostic buffers cleared.": "ডায়াগনস্টিক বাফার মুছে ফেলা হয়েছে।", "Clear Buffers": "বাফার মুছুন", "Preparing...": "প্রস্তুত হচ্ছে...", "Preparing diagnostics...": "ডায়াগনস্টিক প্রস্তুত হচ্ছে...", "Export QC Diagnostics": "QC ডায়াগনস্টিক এক্সপোর্ট", "Export Diagnostics Bundle": "ডায়াগনস্টিক বান্ডল এক্সপোর্ট", "QC idle.": "QC নিষ্ক্রিয়।", "Are you sure to clear all data in the database?": "আপনি কি নিশ্চিত যে ডেটাবেসের সব ডেটা মুছে ফেলতে চান?", "Database cleared.": "ডেটাবেস মুছে ফেলা হয়েছে।", "Module": "মডিউল", "Modules (Scroll to see more)": "মডিউল (আরও দেখতে স্ক্রল করুন)", "About": "সম্পর্কে", "Version": "সংস্করণ", "Search...": "খুঁজুন...", "Something went wrong.": "কিছু ভুল হয়েছে।", "Error:": "ত্রুটি:", "Captured:": "ক্যাপচার করা হয়েছে:", "Rows per page:": "প্রতি পৃষ্ঠায় সারি:", "Relation Types": "সম্পর্কের ধরন", "Subject Accounts": "বিষয় অ্যাকাউন্ট", "Subject User IDs": "বিষয় ব্যবহারকারী ID", "Last Observed At": "শেষ পর্যবেক্ষণ", "A - B of N items": "{{total}}টির মধ্যে {{from}} - {{to}}", "No data available.": "কোনো ডেটা নেই।", "No media available.": "কোনো মিডিয়া নেই।", "Clear": "সাফ করুন", "Bookmark folders": "বুকমার্ক ফোল্ডার", "1 folder selected": "১টি ফোল্ডার নির্বাচিত", "{{count}} folders selected": "{{count}}টি ফোল্ডার নির্বাচিত", "Export Media": "মিডিয়া এক্সপোর্ট", "Export Data": "ডেটা এক্সপোর্ট", "Export Search History": "সার্চ ইতিহাস এক্সপোর্ট", "JSON View": "JSON ভিউ", "Media View": "মিডিয়া ভিউ", "Bookmarks": "বুকমার্ক", "Tweets": "টুইট", "Users": "ব্যবহারকারী", "User Tweets": "ব্যবহারকারীর টুইট", "User Media": "ব্যবহারকারীর মিডিয়া", "Tweet Details": "টুইটের বিবরণ", "Search Timeline": "সার্চ টাইমলাইন", "Home Timeline": "হোম টাইমলাইন", "List Timeline": "লিস্ট টাইমলাইন", "Community Timeline": "কমিউনিটি টাইমলাইন", "Community Members": "কমিউনিটি সদস্য", "List Members": "লিস্ট সদস্য", "List Subscribers": "লিস্ট সাবস্ক্রাইবার", "Direct Messages": "ডাইরেক্ট মেসেজ", "Interaction Events": "ইন্টারঅ্যাকশন ইভেন্ট", "Local Search": "স্থানীয় সার্চ", "Runtime Logs": "রানটাইম লগ", "Bundle Viewer": "বান্ডল ভিউয়ার", "{{count}} imported bundles": "{{count}}টি আমদানি করা বান্ডল", "Search indexed tweets with Twitter-style operators": "Twitter-ধাঁচের অপারেটর দিয়ে ইনডেক্স করা টুইট খুঁজুন", "Local Recorder Search": "লোকাল রেকর্ডার সার্চ", "Quotes": "কোট", "CommunityMembers": "কমিউনিটি সদস্য", "CommunityTimeline": "কমিউনিটি টাইমলাইন", "DirectMessages": "ডাইরেক্ট মেসেজ", "Followers": "ফলোয়ার", "Following": "ফলো করছে", "HomeTimeline": "হোম টাইমলাইন", "Likes": "লাইক", "ListMembers": "লিস্ট সদস্য", "ListSubscribers": "লিস্ট সাবস্ক্রাইবার", "ListTimeline": "লিস্ট টাইমলাইন", "InteractionEvents": "ইন্টারঅ্যাকশন ইভেন্ট", "LocalSearch": "স্থানীয় সার্চ", "RawCapture": "কাঁচা ক্যাপচার", "Retweeters": "রিটুইটকারী", "RetweetersModule": "রিটুইটকারী", "RuntimeLogs": "রানটাইম লগ", "SearchTimeline": "সার্চ টাইমলাইন", "TweetIndex": "টুইট ইনডেক্স", "TweetDetail": "টুইট বিবরণ", "UserDetail": "ব্যবহারকারী বিবরণ", "UserMedia": "ব্যবহারকারীর মিডিয়া", "UserTweets": "ব্যবহারকারীর টুইট" } };
  const en = { "exporter": { "ID": "ID", "Date": "Date", "Content": "Content", "Show Full Text": "Show Full Text", "Media": "Media", "Screen Name": "Screen Name", "Profile Name": "Profile Name", "Profile Image": "Profile Image", "User ID": "User ID", "Replying To": "Replying To", "RT Source": "RT Source", "Quote Source": "Quote Source", "Media Tags": "Media Tags", "Favorites": "Favorites", "Retweets": "Retweets", "Bookmarks": "Bookmarks", "Quotes": "Quotes", "Replies": "Replies", "Views": "Views", "Favorited": "Favorited", "Retweeted": "Retweeted", "Bookmarked": "Bookmarked", "Bookmark Folder": "Bookmark Folder", "URL": "URL", "Actions": "Actions", "Details": "Details", "Description": "Description", "Profile Banner": "Profile Banner", "Followers": "Followers", "FollowingCount": "Following Count", "Statuses": "Statuses", "Favourites": "Favourites", "Listed": "Listed", "Location": "Location", "Website": "Website", "Birthdate": "Birthdate", "Verified Type": "Verified Type", "Blue Verified": "Blue Verified", "Following": "Following", "Follows You": "Follows You", "Can DM": "Can DM", "Protected": "Protected", "Created At": "Created At", "Sender": "Sender", "Recipient": "Recipient", "Conversation ID": "Conversation ID", "Conversation Type": "Conversation Type", "Data": "Data", "Export captured data as JSON/HTML/CSV file. This may take a while depending on the amount of data. The exported file does not include media files such as images and videos but only the URLs.": "Export captured data as JSON/HTML/CSV file. This may take a while depending on the amount of data. The exported file does not include media files such as images and videos but only the URLs.", "Data length:": "Data length:", "Export scope:": "Export scope:", "All current results": "All current results", "Selected rows": "Selected rows", "Pinned result set": "Pinned result set", "Query": "Query", "Sort": "Sort", "Include all metadata:": "Include all metadata:", "Export as:": "Export as:", "No data selected.": "No data selected.", "Cancel": "Cancel", "Start Export": "Start Export", "Download and save media files from captured data. This may take a while depending on the amount of data. Media that will be downloaded includes: profile images, profile banners (for users), images, videos (for tweets).": "Download and save media files from captured data. This may take a while depending on the amount of data. Media that will be downloaded includes: profile images, profile banners (for users), images, videos (for tweets).", "For more than 100 media or large files, it is recommended to copy the URLs and download them with an external download manager such as aria2.": "For more than 100 media or large files, it is recommended to copy the URLs and download them with an external download manager such as aria2.", "Browser ZIP export now uses bounded parallel downloads. For very large video-heavy jobs, URL or aria2 export is still the safest low-memory path.": "Browser ZIP export now uses bounded parallel downloads. For very large video-heavy jobs, URL or aria2 export is still the safest low-memory path.", "Filename template:": "Filename template:", "Download scheduler": "Download scheduler", "Faster defaults are intended for bulk CDN transfer.": "Faster defaults are intended for bulk CDN transfer.", "Start delay (ms)": "Start delay (ms)", "Global parallel": "Global parallel", "Per host": "Per host", "Videos": "Videos", "Retries": "Retries", "Use aria2 format:": "Use aria2 format:", "Click for more information. Each URL will be on a new line, with its filename on the next line. This format is compatible with aria2.": "Click for more information. Each URL will be on a new line, with its filename on the next line. This format is compatible with aria2.", "Rate limit (ms):": "Rate limit (ms):", "Media Filter:": "Media Filter:", "File Name": "File Name", "Media Type": "Media Type", "Download URL": "Download URL", "No media selected.": "No media selected.", "Preview limited to first 250 media items.": "Preview limited to first 250 media items.", "Zipping": "Zipping", "Copied!": "Copied!", "Copy URLs": "Copy URLs", "The tweet ID": "The tweet ID", "The username of tweet author": "The username of tweet author", "The profile name of tweet author": "The profile name of tweet author", "The media index in tweet (start from 0)": "The media index in tweet (start from 0)", "The order of media in tweet (1/2/3/4)": "The order of media in tweet (1/2/3/4)", "The post date in YYYYMMDD format": "The post date in YYYYMMDD format", "The post time in HHmmss format": "The post time in HHmmss format", "The media type (photo/video/animated_gif)": "The media type (photo/video/animated_gif)", "The file extension of media (jpg/png/mp4)": "The file extension of media (jpg/png/mp4)", "Failed to export media. Open DevTools for more details.": "Failed to export media. Open DevTools for more details.", "Failed to copy media URLs. Open DevTools for more details.": "Failed to copy media URLs. Open DevTools for more details.", "filter.photo": "Photo", "filter.video": "Video", "filter.animated_gif": "GIF", "filter.retweet": "Include retweets" }, "common": { "Open Control Panel": "Open Control Panel", "Browse around to capture more data.": "Browse around to capture more data.", "Settings": "Settings", "General": "General", "Theme": "Theme", "Language": "Language", "Debug": "Debug", "Date Time Format": "Date Time Format", "Click for more information. This will take effect on both previewer and exported files.": "Click for more information. This will take effect on both previewer and exported files.", "Use dedicated DB for accounts": "Use dedicated DB for accounts", "This will create separate database for each Twitter account, which can help reduce the chance of data mixing when you use multiple accounts.": "This will create separate database for each Twitter account, which can help reduce the chance of data mixing when you use multiple accounts.", "Local Database": "Local Database", "Analyze DB": "Analyze", "Export DB": "Export", "Clear DB": "Clear", "Bundle Export": "Bundle Export", "Bundle Library": "Bundle Library", "QC Session": "QC Session", "Diagnostic capture": "Diagnostic capture", "Diagnostic buffers cleared.": "Diagnostic buffers cleared.", "Clear Buffers": "Clear Buffers", "Preparing...": "Preparing...", "Preparing diagnostics...": "Preparing diagnostics...", "Export QC Diagnostics": "Export QC Diagnostics", "Export Diagnostics Bundle": "Export Diagnostics Bundle", "QC idle.": "QC idle.", "Are you sure to clear all data in the database?": "Are you sure to clear all data in the database?", "Database cleared.": "Database cleared.", "Module": "Module", "Modules (Scroll to see more)": "Modules (Scroll to see more)", "About": "About", "Version": "Version", "Search...": "Search...", "Something went wrong.": "Something went wrong.", "Error:": "Error:", "Captured:": "Captured:", "Rows per page:": "Rows per page:", "Relation Types": "Relation Types", "Subject Accounts": "Subject Accounts", "Subject User IDs": "Subject User IDs", "Last Observed At": "Last Observed At", "A - B of N items": "{{from}} - {{to}} of {{total}} items", "No data available.": "No data available.", "No media available.": "No media available.", "Clear": "Clear", "Bookmark folders": "Bookmark folders", "1 folder selected": "1 folder selected", "{{count}} folders selected": "{{count}} folders selected", "Export Media": "Export Media", "Export Data": "Export Data", "Export Search History": "Export Search History", "JSON View": "JSON View", "Media View": "Media View", "Bookmarks": "Bookmarks", "Tweets": "Tweets", "Users": "Users", "User Tweets": "User Tweets", "User Media": "User Media", "Tweet Details": "Tweet Details", "Search Timeline": "Search Timeline", "Home Timeline": "Home Timeline", "List Timeline": "List Timeline", "Community Timeline": "Community Timeline", "Community Members": "Community Members", "List Members": "List Members", "List Subscribers": "List Subscribers", "Direct Messages": "Direct Messages", "Interaction Events": "Interaction Events", "Local Search": "Local Search", "Runtime Logs": "Runtime Logs", "Bundle Viewer": "Bundle Viewer", "{{count}} imported bundles": "{{count}} imported bundles", "Search indexed tweets with Twitter-style operators": "Search indexed tweets with Twitter-style operators", "Local Recorder Search": "Local Recorder Search", "Quotes": "Quotes", "CommunityMembers": "CommunityMembers", "CommunityTimeline": "CommunityTimeline", "DirectMessages": "DirectMessages", "Followers": "Followers", "Following": "Following", "HomeTimeline": "HomeTimeline", "Likes": "Likes", "ListMembers": "ListMembers", "ListSubscribers": "ListSubscribers", "ListTimeline": "ListTimeline", "InteractionEvents": "InteractionEvents", "LocalSearch": "LocalSearch", "RawCapture": "RawCapture", "Retweeters": "Retweeters", "RetweetersModule": "RetweetersModule", "RuntimeLogs": "RuntimeLogs", "SearchTimeline": "SearchTimeline", "TweetIndex": "TweetIndex", "TweetDetail": "TweetDetail", "UserDetail": "UserDetail", "UserMedia": "UserMedia", "UserTweets": "UserTweets" } };
  const es = { "exporter": { "ID": "ID", "Date": "Fecha", "Content": "Contenido", "Show Full Text": "Mostrar texto completo", "Media": "Medios", "Screen Name": "Nombre de usuario", "Profile Name": "Nombre del perfil", "Profile Image": "Imagen de perfil", "User ID": "ID de usuario", "Replying To": "Responde a", "RT Source": "Fuente del RT", "Quote Source": "Fuente de la cita", "Media Tags": "Etiquetas de medios", "Favorites": "Favoritos", "Retweets": "Retweets", "Bookmarks": "Marcadores", "Quotes": "Citas", "Replies": "Respuestas", "Views": "Visualizaciones", "Favorited": "Marcado como favorito", "Retweeted": "Retuiteado", "Bookmarked": "Guardado en marcadores", "Bookmark Folder": "Carpeta de marcadores", "URL": "URL", "Actions": "Acciones", "Details": "Detalles", "Description": "Descripción", "Profile Banner": "Banner del perfil", "Followers": "Seguidores", "FollowingCount": "Número de seguidos", "Statuses": "Publicaciones", "Favourites": "Favoritos", "Listed": "En listas", "Location": "Ubicación", "Website": "Sitio web", "Birthdate": "Fecha de nacimiento", "Verified Type": "Tipo de verificación", "Blue Verified": "Verificación Blue", "Following": "Siguiendo", "Follows You": "Te sigue", "Can DM": "Puede recibir DM", "Protected": "Protegido", "Created At": "Creado el", "Sender": "Remitente", "Recipient": "Destinatario", "Conversation ID": "ID de conversación", "Conversation Type": "Tipo de conversación", "Data": "Datos", "Export captured data as JSON/HTML/CSV file. This may take a while depending on the amount of data. The exported file does not include media files such as images and videos but only the URLs.": "Exporta los datos capturados como archivo JSON/HTML/CSV. Puede tardar según la cantidad de datos. El archivo exportado no incluye archivos multimedia como imágenes o videos, solo sus URL.", "Data length:": "Longitud de datos:", "Export scope:": "Alcance de exportación:", "All current results": "Todos los resultados actuales", "Selected rows": "Filas seleccionadas", "Pinned result set": "Conjunto de resultados fijado", "Query": "Consulta", "Sort": "Orden", "Include all metadata:": "Incluir todos los metadatos:", "Export as:": "Exportar como:", "No data selected.": "No hay datos seleccionados.", "Cancel": "Cancelar", "Start Export": "Iniciar exportación", "Download and save media files from captured data. This may take a while depending on the amount of data. Media that will be downloaded includes: profile images, profile banners (for users), images, videos (for tweets).": "Descarga y guarda archivos multimedia desde los datos capturados. Puede tardar según la cantidad de datos. Se descargarán imágenes de perfil, banners de perfil (para usuarios), imágenes y videos (para tweets).", "For more than 100 media or large files, it is recommended to copy the URLs and download them with an external download manager such as aria2.": "Para más de 100 medios o archivos grandes, se recomienda copiar las URL y descargarlas con un gestor externo como aria2.", "Browser ZIP export now uses bounded parallel downloads. For very large video-heavy jobs, URL or aria2 export is still the safest low-memory path.": "La exportación ZIP del navegador ahora usa descargas paralelas limitadas. Para trabajos muy grandes con muchos videos, exportar URL o aria2 sigue siendo la ruta más segura y de baja memoria.", "Filename template:": "Plantilla de nombre de archivo:", "Download scheduler": "Planificador de descargas", "Faster defaults are intended for bulk CDN transfer.": "Los valores predeterminados más rápidos están pensados para transferencias masivas desde CDN.", "Start delay (ms)": "Retraso inicial (ms)", "Global parallel": "Paralelismo global", "Per host": "Por host", "Videos": "Videos", "Retries": "Reintentos", "Use aria2 format:": "Usar formato aria2:", "Click for more information. Each URL will be on a new line, with its filename on the next line. This format is compatible with aria2.": "Haz clic para más información. Cada URL irá en una línea nueva, con su nombre de archivo en la línea siguiente. Este formato es compatible con aria2.", "Rate limit (ms):": "Límite de tasa (ms):", "Media Filter:": "Filtro de medios:", "File Name": "Nombre de archivo", "Media Type": "Tipo de medio", "Download URL": "URL de descarga", "No media selected.": "No hay medios seleccionados.", "Preview limited to first 250 media items.": "La vista previa se limita a los primeros 250 elementos multimedia.", "Zipping": "Comprimiendo", "Copied!": "Copiado", "Copy URLs": "Copiar URL", "The tweet ID": "El ID del tweet", "The username of tweet author": "El nombre de usuario del autor del tweet", "The profile name of tweet author": "El nombre de perfil del autor del tweet", "The media index in tweet (start from 0)": "El índice del medio en el tweet (empieza en 0)", "The order of media in tweet (1/2/3/4)": "El orden del medio en el tweet (1/2/3/4)", "The post date in YYYYMMDD format": "La fecha de publicación en formato YYYYMMDD", "The post time in HHmmss format": "La hora de publicación en formato HHmmss", "The media type (photo/video/animated_gif)": "El tipo de medio (photo/video/animated_gif)", "The file extension of media (jpg/png/mp4)": "La extensión del archivo multimedia (jpg/png/mp4)", "Failed to export media. Open DevTools for more details.": "No se pudieron exportar los medios. Abre DevTools para más detalles.", "Failed to copy media URLs. Open DevTools for more details.": "No se pudieron copiar las URL de medios. Abre DevTools para más detalles.", "filter.photo": "Foto", "filter.video": "Video", "filter.animated_gif": "GIF", "filter.retweet": "Incluir retweets" }, "common": { "Open Control Panel": "Abrir panel de control", "Browse around to capture more data.": "Navega para capturar más datos.", "Settings": "Configuración", "General": "General", "Theme": "Tema", "Language": "Idioma", "Debug": "Depuración", "Date Time Format": "Formato de fecha y hora", "Click for more information. This will take effect on both previewer and exported files.": "Haz clic para ver más información. Esto se aplicará tanto al visor previo como a los archivos exportados.", "Use dedicated DB for accounts": "Usar una base de datos dedicada por cuenta", "This will create separate database for each Twitter account, which can help reduce the chance of data mixing when you use multiple accounts.": "Esto creará una base de datos separada para cada cuenta de Twitter, lo que ayuda a reducir la mezcla de datos al usar varias cuentas.", "Local Database": "Base de datos local", "Analyze DB": "Analizar", "Export DB": "Exportar", "Clear DB": "Borrar", "Bundle Export": "Exportar paquete", "Bundle Library": "Biblioteca de paquetes", "QC Session": "Sesión de QC", "Diagnostic capture": "Captura diagnóstica", "Diagnostic buffers cleared.": "Búferes de diagnóstico borrados.", "Clear Buffers": "Borrar búferes", "Preparing...": "Preparando...", "Preparing diagnostics...": "Preparando diagnósticos...", "Export QC Diagnostics": "Exportar diagnósticos de QC", "Export Diagnostics Bundle": "Exportar paquete de diagnósticos", "QC idle.": "QC inactivo.", "Are you sure to clear all data in the database?": "¿Seguro que quieres borrar todos los datos de la base de datos?", "Database cleared.": "Base de datos borrada.", "Module": "Módulo", "Modules (Scroll to see more)": "Módulos (desplázate para ver más)", "About": "Acerca de", "Version": "Versión", "Search...": "Buscar...", "Something went wrong.": "Algo salió mal.", "Error:": "Error:", "Captured:": "Capturado:", "Rows per page:": "Filas por página:", "Relation Types": "Tipos de relación", "Subject Accounts": "Cuentas sujeto", "Subject User IDs": "ID de usuario sujeto", "Last Observed At": "Última observación", "A - B of N items": "{{from}} - {{to}} de {{total}} elementos", "No data available.": "No hay datos disponibles.", "No media available.": "No hay medios disponibles.", "Clear": "Limpiar", "Bookmark folders": "Carpetas de marcadores", "1 folder selected": "1 carpeta seleccionada", "{{count}} folders selected": "{{count}} carpetas seleccionadas", "Export Media": "Exportar medios", "Export Data": "Exportar datos", "Export Search History": "Exportar historial de búsqueda", "JSON View": "Vista JSON", "Media View": "Vista de medios", "Bookmarks": "Marcadores", "Tweets": "Tweets", "Users": "Usuarios", "User Tweets": "Tweets de usuario", "User Media": "Medios de usuario", "Tweet Details": "Detalles del tweet", "Search Timeline": "Cronología de búsqueda", "Home Timeline": "Cronología de inicio", "List Timeline": "Cronología de lista", "Community Timeline": "Cronología de comunidad", "Community Members": "Miembros de comunidad", "List Members": "Miembros de lista", "List Subscribers": "Suscriptores de lista", "Direct Messages": "Mensajes directos", "Interaction Events": "Eventos de interacción", "Local Search": "Búsqueda local", "Runtime Logs": "Registros de ejecución", "Bundle Viewer": "Visor de paquetes", "{{count}} imported bundles": "{{count}} paquetes importados", "Search indexed tweets with Twitter-style operators": "Busca tweets indexados con operadores estilo Twitter", "Local Recorder Search": "Búsqueda del registrador local", "Quotes": "Citas", "CommunityMembers": "Miembros de comunidad", "CommunityTimeline": "Cronología de comunidad", "DirectMessages": "Mensajes directos", "Followers": "Seguidores", "Following": "Siguiendo", "HomeTimeline": "Cronología de inicio", "Likes": "Me gusta", "ListMembers": "Miembros de lista", "ListSubscribers": "Suscriptores de lista", "ListTimeline": "Cronología de lista", "InteractionEvents": "Eventos de interacción", "LocalSearch": "Búsqueda local", "RawCapture": "Captura sin procesar", "Retweeters": "Retuiteadores", "RetweetersModule": "Retuiteadores", "RuntimeLogs": "Registros de ejecución", "SearchTimeline": "Búsqueda", "TweetIndex": "Índice de tweets", "TweetDetail": "Detalle del tweet", "UserDetail": "Detalle de usuario", "UserMedia": "Medios de usuario", "UserTweets": "Tweets de usuario" } };
  const fr = { "exporter": { "ID": "ID", "Date": "Date", "Content": "Contenu", "Show Full Text": "Afficher le texte complet", "Media": "Médias", "Screen Name": "Nom d’utilisateur", "Profile Name": "Nom du profil", "Profile Image": "Image de profil", "User ID": "ID utilisateur", "Replying To": "Répond à", "RT Source": "Source du RT", "Quote Source": "Source de la citation", "Media Tags": "Tags média", "Favorites": "Favoris", "Retweets": "Retweets", "Bookmarks": "Signets", "Quotes": "Citations", "Replies": "Réponses", "Views": "Vues", "Favorited": "Ajouté aux favoris", "Retweeted": "Retweeté", "Bookmarked": "Ajouté aux signets", "Bookmark Folder": "Dossier de signets", "URL": "URL", "Actions": "Actions", "Details": "Détails", "Description": "Description", "Profile Banner": "Bannière du profil", "Followers": "Abonnés", "FollowingCount": "Nombre d’abonnements", "Statuses": "Publications", "Favourites": "Favoris", "Listed": "Listé", "Location": "Lieu", "Website": "Site web", "Birthdate": "Date de naissance", "Verified Type": "Type de vérification", "Blue Verified": "Vérifié Blue", "Following": "Abonnements", "Follows You": "Vous suit", "Can DM": "DM possible", "Protected": "Protégé", "Created At": "Créé le", "Sender": "Expéditeur", "Recipient": "Destinataire", "Conversation ID": "ID de conversation", "Conversation Type": "Type de conversation", "Data": "Données", "Export captured data as JSON/HTML/CSV file. This may take a while depending on the amount of data. The exported file does not include media files such as images and videos but only the URLs.": "Exportez les données capturées en fichier JSON/HTML/CSV. Cela peut prendre du temps selon la quantité de données. Le fichier exporté n’inclut pas les médias comme les images et vidéos, seulement leurs URL.", "Data length:": "Longueur des données :", "Export scope:": "Portée de l’export :", "All current results": "Tous les résultats actuels", "Selected rows": "Lignes sélectionnées", "Pinned result set": "Ensemble de résultats épinglé", "Query": "Requête", "Sort": "Tri", "Include all metadata:": "Inclure toutes les métadonnées :", "Export as:": "Exporter en :", "No data selected.": "Aucune donnée sélectionnée.", "Cancel": "Annuler", "Start Export": "Démarrer l’export", "Download and save media files from captured data. This may take a while depending on the amount of data. Media that will be downloaded includes: profile images, profile banners (for users), images, videos (for tweets).": "Télécharge et enregistre les médias à partir des données capturées. Cela peut prendre du temps selon la quantité de données. Les médias téléchargés incluent les images de profil, les bannières de profil (pour les utilisateurs), les images et les vidéos (pour les tweets).", "For more than 100 media or large files, it is recommended to copy the URLs and download them with an external download manager such as aria2.": "Pour plus de 100 médias ou de gros fichiers, il est recommandé de copier les URL et de les télécharger avec un gestionnaire externe comme aria2.", "Browser ZIP export now uses bounded parallel downloads. For very large video-heavy jobs, URL or aria2 export is still the safest low-memory path.": "L’export ZIP du navigateur utilise maintenant des téléchargements parallèles bornés. Pour les très gros travaux riches en vidéos, l’export URL ou aria2 reste la voie la plus sûre et la moins gourmande en mémoire.", "Filename template:": "Modèle de nom de fichier :", "Download scheduler": "Planificateur de téléchargement", "Faster defaults are intended for bulk CDN transfer.": "Les valeurs par défaut plus rapides sont prévues pour les transferts CDN en masse.", "Start delay (ms)": "Délai de départ (ms)", "Global parallel": "Parallélisme global", "Per host": "Par hôte", "Videos": "Vidéos", "Retries": "Tentatives", "Use aria2 format:": "Utiliser le format aria2 :", "Click for more information. Each URL will be on a new line, with its filename on the next line. This format is compatible with aria2.": "Cliquez pour plus d’informations. Chaque URL sera sur une nouvelle ligne, avec le nom de fichier sur la ligne suivante. Ce format est compatible avec aria2.", "Rate limit (ms):": "Limite de débit (ms) :", "Media Filter:": "Filtre média :", "File Name": "Nom de fichier", "Media Type": "Type de média", "Download URL": "URL de téléchargement", "No media selected.": "Aucun média sélectionné.", "Preview limited to first 250 media items.": "Aperçu limité aux 250 premiers médias.", "Zipping": "Compression ZIP", "Copied!": "Copié !", "Copy URLs": "Copier les URL", "The tweet ID": "L’ID du tweet", "The username of tweet author": "Le nom d’utilisateur de l’auteur du tweet", "The profile name of tweet author": "Le nom de profil de l’auteur du tweet", "The media index in tweet (start from 0)": "L’index du média dans le tweet (commence à 0)", "The order of media in tweet (1/2/3/4)": "L’ordre du média dans le tweet (1/2/3/4)", "The post date in YYYYMMDD format": "La date de publication au format YYYYMMDD", "The post time in HHmmss format": "L’heure de publication au format HHmmss", "The media type (photo/video/animated_gif)": "Le type de média (photo/video/animated_gif)", "The file extension of media (jpg/png/mp4)": "L’extension du fichier média (jpg/png/mp4)", "Failed to export media. Open DevTools for more details.": "Échec de l’export des médias. Ouvrez DevTools pour plus de détails.", "Failed to copy media URLs. Open DevTools for more details.": "Échec de la copie des URL média. Ouvrez DevTools pour plus de détails.", "filter.photo": "Photo", "filter.video": "Vidéo", "filter.animated_gif": "GIF", "filter.retweet": "Inclure les retweets" }, "common": { "Open Control Panel": "Ouvrir le panneau de contrôle", "Browse around to capture more data.": "Parcourez les pages pour capturer plus de données.", "Settings": "Paramètres", "General": "Général", "Theme": "Thème", "Language": "Langue", "Debug": "Débogage", "Date Time Format": "Format de date et d’heure", "Click for more information. This will take effect on both previewer and exported files.": "Cliquez pour plus d’informations. Cela s’appliquera à la prévisualisation et aux fichiers exportés.", "Use dedicated DB for accounts": "Utiliser une base dédiée par compte", "This will create separate database for each Twitter account, which can help reduce the chance of data mixing when you use multiple accounts.": "Cela créera une base de données séparée pour chaque compte Twitter, ce qui réduit le risque de mélange des données lorsque vous utilisez plusieurs comptes.", "Local Database": "Base de données locale", "Analyze DB": "Analyser", "Export DB": "Exporter", "Clear DB": "Effacer", "Bundle Export": "Exporter un paquet", "Bundle Library": "Bibliothèque de paquets", "QC Session": "Session QC", "Diagnostic capture": "Capture diagnostique", "Diagnostic buffers cleared.": "Tampons de diagnostic effacés.", "Clear Buffers": "Effacer les tampons", "Preparing...": "Préparation...", "Preparing diagnostics...": "Préparation des diagnostics...", "Export QC Diagnostics": "Exporter les diagnostics QC", "Export Diagnostics Bundle": "Exporter le paquet de diagnostics", "QC idle.": "QC inactif.", "Are you sure to clear all data in the database?": "Voulez-vous vraiment effacer toutes les données de la base ?", "Database cleared.": "Base de données effacée.", "Module": "Module", "Modules (Scroll to see more)": "Modules (faites défiler pour en voir plus)", "About": "À propos", "Version": "Version", "Search...": "Rechercher...", "Something went wrong.": "Une erreur est survenue.", "Error:": "Erreur :", "Captured:": "Capturé :", "Rows per page:": "Lignes par page :", "Relation Types": "Types de relation", "Subject Accounts": "Comptes sujets", "Subject User IDs": "ID utilisateur sujets", "Last Observed At": "Dernière observation", "A - B of N items": "{{from}} - {{to}} sur {{total}} éléments", "No data available.": "Aucune donnée disponible.", "No media available.": "Aucun média disponible.", "Clear": "Effacer", "Bookmark folders": "Dossiers de signets", "1 folder selected": "1 dossier sélectionné", "{{count}} folders selected": "{{count}} dossiers sélectionnés", "Export Media": "Exporter les médias", "Export Data": "Exporter les données", "Export Search History": "Exporter l’historique de recherche", "JSON View": "Vue JSON", "Media View": "Vue média", "Bookmarks": "Signets", "Tweets": "Tweets", "Users": "Utilisateurs", "User Tweets": "Tweets utilisateur", "User Media": "Médias utilisateur", "Tweet Details": "Détails du tweet", "Search Timeline": "Fil de recherche", "Home Timeline": "Fil d’accueil", "List Timeline": "Fil de liste", "Community Timeline": "Fil de communauté", "Community Members": "Membres de communauté", "List Members": "Membres de liste", "List Subscribers": "Abonnés de liste", "Direct Messages": "Messages directs", "Interaction Events": "Événements d’interaction", "Local Search": "Recherche locale", "Runtime Logs": "Journaux d’exécution", "Bundle Viewer": "Visionneuse de paquets", "{{count}} imported bundles": "{{count}} paquets importés", "Search indexed tweets with Twitter-style operators": "Rechercher des tweets indexés avec des opérateurs de type Twitter", "Local Recorder Search": "Recherche de l’enregistreur local", "Quotes": "Citations", "CommunityMembers": "Membres de communauté", "CommunityTimeline": "Fil de communauté", "DirectMessages": "Messages directs", "Followers": "Abonnés", "Following": "Abonnements", "HomeTimeline": "Fil d’accueil", "Likes": "J’aime", "ListMembers": "Membres de liste", "ListSubscribers": "Abonnés de liste", "ListTimeline": "Fil de liste", "InteractionEvents": "Événements d’interaction", "LocalSearch": "Recherche locale", "RawCapture": "Capture brute", "Retweeters": "Retweeteurs", "RetweetersModule": "Retweeteurs", "RuntimeLogs": "Journaux d’exécution", "SearchTimeline": "Fil de recherche", "TweetIndex": "Index des tweets", "TweetDetail": "Détail du tweet", "UserDetail": "Détail utilisateur", "UserMedia": "Médias utilisateur", "UserTweets": "Tweets utilisateur" } };
  const hi = { "exporter": { "ID": "ID", "Date": "दिनांक", "Content": "सामग्री", "Show Full Text": "पूरा पाठ दिखाएँ", "Media": "मीडिया", "Screen Name": "यूज़रनेम", "Profile Name": "प्रोफ़ाइल नाम", "Profile Image": "प्रोफ़ाइल छवि", "User ID": "उपयोगकर्ता ID", "Replying To": "जिसे उत्तर दे रहा है", "RT Source": "RT स्रोत", "Quote Source": "कोट स्रोत", "Media Tags": "मीडिया टैग", "Favorites": "पसंदीदा", "Retweets": "रीट्वीट", "Bookmarks": "बुकमार्क", "Quotes": "कोट्स", "Replies": "उत्तर", "Views": "दृश्य", "Favorited": "पसंदीदा किया गया", "Retweeted": "रीट्वीट किया गया", "Bookmarked": "बुकमार्क किया गया", "Bookmark Folder": "बुकमार्क फ़ोल्डर", "URL": "URL", "Actions": "क्रियाएँ", "Details": "विवरण", "Description": "विवरण", "Profile Banner": "प्रोफ़ाइल बैनर", "Followers": "फ़ॉलोअर", "FollowingCount": "फ़ॉलोइंग संख्या", "Statuses": "पोस्ट", "Favourites": "पसंदीदा", "Listed": "सूचियों में", "Location": "स्थान", "Website": "वेबसाइट", "Birthdate": "जन्म तिथि", "Verified Type": "सत्यापन प्रकार", "Blue Verified": "Blue सत्यापित", "Following": "फ़ॉलो कर रहे हैं", "Follows You": "आपको फ़ॉलो करता है", "Can DM": "DM कर सकते हैं", "Protected": "संरक्षित", "Created At": "बनाया गया", "Sender": "प्रेषक", "Recipient": "प्राप्तकर्ता", "Conversation ID": "वार्तालाप ID", "Conversation Type": "वार्तालाप प्रकार", "Data": "डेटा", "Export captured data as JSON/HTML/CSV file. This may take a while depending on the amount of data. The exported file does not include media files such as images and videos but only the URLs.": "कैप्चर किए गए डेटा को JSON/HTML/CSV फ़ाइल के रूप में निर्यात करें। डेटा की मात्रा के अनुसार इसमें समय लग सकता है। निर्यात फ़ाइल में चित्र और वीडियो जैसी मीडिया फ़ाइलें शामिल नहीं होंगी, केवल उनके URL होंगे।", "Data length:": "डेटा लंबाई:", "Export scope:": "निर्यात दायरा:", "All current results": "सभी मौजूदा परिणाम", "Selected rows": "चयनित पंक्तियाँ", "Pinned result set": "पिन किया गया परिणाम सेट", "Query": "क्वेरी", "Sort": "क्रम", "Include all metadata:": "सारा मेटाडेटा शामिल करें:", "Export as:": "इस रूप में निर्यात करें:", "No data selected.": "कोई डेटा चयनित नहीं है।", "Cancel": "रद्द करें", "Start Export": "निर्यात शुरू करें", "Download and save media files from captured data. This may take a while depending on the amount of data. Media that will be downloaded includes: profile images, profile banners (for users), images, videos (for tweets).": "कैप्चर किए गए डेटा से मीडिया फ़ाइलें डाउनलोड और सेव करें। डेटा की मात्रा के अनुसार इसमें समय लग सकता है। डाउनलोड में प्रोफ़ाइल छवियाँ, प्रोफ़ाइल बैनर (उपयोगकर्ताओं के लिए), छवियाँ और वीडियो (ट्वीट के लिए) शामिल होंगे।", "For more than 100 media or large files, it is recommended to copy the URLs and download them with an external download manager such as aria2.": "100 से अधिक मीडिया या बड़ी फ़ाइलों के लिए URL कॉपी करके aria2 जैसे बाहरी डाउनलोड मैनेजर से डाउनलोड करना बेहतर है।", "Browser ZIP export now uses bounded parallel downloads. For very large video-heavy jobs, URL or aria2 export is still the safest low-memory path.": "ब्राउज़र ZIP निर्यात अब सीमित समानांतर डाउनलोड का उपयोग करता है। बहुत बड़े, वीडियो-भारी कार्यों के लिए URL या aria2 निर्यात अभी भी कम-मेमोरी वाला सबसे सुरक्षित रास्ता है।", "Filename template:": "फ़ाइल नाम टेम्पलेट:", "Download scheduler": "डाउनलोड शेड्यूलर", "Faster defaults are intended for bulk CDN transfer.": "तेज़ डिफ़ॉल्ट बड़े CDN ट्रांसफ़र के लिए हैं।", "Start delay (ms)": "आरंभ विलंब (ms)", "Global parallel": "वैश्विक समानांतर", "Per host": "प्रति होस्ट", "Videos": "वीडियो", "Retries": "पुनः प्रयास", "Use aria2 format:": "aria2 प्रारूप इस्तेमाल करें:", "Click for more information. Each URL will be on a new line, with its filename on the next line. This format is compatible with aria2.": "अधिक जानकारी के लिए क्लिक करें। हर URL नई पंक्ति में होगा, और अगली पंक्ति में उसका फ़ाइल नाम होगा। यह प्रारूप aria2 के साथ संगत है।", "Rate limit (ms):": "दर सीमा (ms):", "Media Filter:": "मीडिया फ़िल्टर:", "File Name": "फ़ाइल नाम", "Media Type": "मीडिया प्रकार", "Download URL": "डाउनलोड URL", "No media selected.": "कोई मीडिया चयनित नहीं है।", "Preview limited to first 250 media items.": "पूर्वावलोकन पहले 250 मीडिया आइटम तक सीमित है।", "Zipping": "ZIP बनाया जा रहा है", "Copied!": "कॉपी किया गया!", "Copy URLs": "URL कॉपी करें", "The tweet ID": "ट्वीट ID", "The username of tweet author": "ट्वीट लेखक का यूज़रनेम", "The profile name of tweet author": "ट्वीट लेखक का प्रोफ़ाइल नाम", "The media index in tweet (start from 0)": "ट्वीट में मीडिया इंडेक्स (0 से शुरू)", "The order of media in tweet (1/2/3/4)": "ट्वीट में मीडिया का क्रम (1/2/3/4)", "The post date in YYYYMMDD format": "YYYYMMDD प्रारूप में पोस्ट दिनांक", "The post time in HHmmss format": "HHmmss प्रारूप में पोस्ट समय", "The media type (photo/video/animated_gif)": "मीडिया प्रकार (photo/video/animated_gif)", "The file extension of media (jpg/png/mp4)": "मीडिया फ़ाइल एक्सटेंशन (jpg/png/mp4)", "Failed to export media. Open DevTools for more details.": "मीडिया निर्यात विफल। अधिक जानकारी के लिए DevTools खोलें।", "Failed to copy media URLs. Open DevTools for more details.": "मीडिया URL कॉपी करने में विफल। अधिक जानकारी के लिए DevTools खोलें।", "filter.photo": "फ़ोटो", "filter.video": "वीडियो", "filter.animated_gif": "GIF", "filter.retweet": "रीट्वीट शामिल करें" }, "common": { "Open Control Panel": "कंट्रोल पैनल खोलें", "Browse around to capture more data.": "अधिक डेटा कैप्चर करने के लिए ब्राउज़ करें।", "Settings": "सेटिंग्स", "General": "सामान्य", "Theme": "थीम", "Language": "भाषा", "Debug": "डीबग", "Date Time Format": "दिनांक और समय प्रारूप", "Click for more information. This will take effect on both previewer and exported files.": "अधिक जानकारी के लिए क्लिक करें। यह प्रीव्यूअर और निर्यात की गई फ़ाइलों दोनों पर लागू होगा।", "Use dedicated DB for accounts": "खातों के लिए अलग डेटाबेस इस्तेमाल करें", "This will create separate database for each Twitter account, which can help reduce the chance of data mixing when you use multiple accounts.": "यह हर Twitter खाते के लिए अलग डेटाबेस बनाएगा, जिससे कई खाते इस्तेमाल करते समय डेटा मिल जाने की संभावना घटती है।", "Local Database": "स्थानीय डेटाबेस", "Analyze DB": "विश्लेषण", "Export DB": "निर्यात", "Clear DB": "साफ़ करें", "Bundle Export": "बंडल निर्यात", "Bundle Library": "बंडल लाइब्रेरी", "QC Session": "QC सत्र", "Diagnostic capture": "नैदानिक कैप्चर", "Diagnostic buffers cleared.": "नैदानिक बफ़र साफ़ किए गए।", "Clear Buffers": "बफ़र साफ़ करें", "Preparing...": "तैयार हो रहा है...", "Preparing diagnostics...": "डायग्नॉस्टिक्स तैयार हो रहे हैं...", "Export QC Diagnostics": "QC डायग्नॉस्टिक्स निर्यात करें", "Export Diagnostics Bundle": "डायग्नॉस्टिक्स बंडल निर्यात करें", "QC idle.": "QC निष्क्रिय है।", "Are you sure to clear all data in the database?": "क्या आप डेटाबेस का सारा डेटा साफ़ करना चाहते हैं?", "Database cleared.": "डेटाबेस साफ़ कर दिया गया।", "Module": "मॉड्यूल", "Modules (Scroll to see more)": "मॉड्यूल (और देखने के लिए स्क्रॉल करें)", "About": "परिचय", "Version": "संस्करण", "Search...": "खोजें...", "Something went wrong.": "कुछ गलत हो गया।", "Error:": "त्रुटि:", "Captured:": "कैप्चर किया गया:", "Rows per page:": "प्रति पृष्ठ पंक्तियाँ:", "Relation Types": "संबंध प्रकार", "Subject Accounts": "विषय खाते", "Subject User IDs": "विषय उपयोगकर्ता ID", "Last Observed At": "अंतिम बार देखा गया", "A - B of N items": "{{total}} में से {{from}} - {{to}} आइटम", "No data available.": "कोई डेटा उपलब्ध नहीं है।", "No media available.": "कोई मीडिया उपलब्ध नहीं है।", "Clear": "साफ़ करें", "Bookmark folders": "बुकमार्क फ़ोल्डर", "1 folder selected": "1 फ़ोल्डर चयनित", "{{count}} folders selected": "{{count}} फ़ोल्डर चयनित", "Export Media": "मीडिया निर्यात करें", "Export Data": "डेटा निर्यात करें", "Export Search History": "खोज इतिहास निर्यात करें", "JSON View": "JSON दृश्य", "Media View": "मीडिया दृश्य", "Bookmarks": "बुकमार्क", "Tweets": "ट्वीट", "Users": "उपयोगकर्ता", "User Tweets": "उपयोगकर्ता ट्वीट", "User Media": "उपयोगकर्ता मीडिया", "Tweet Details": "ट्वीट विवरण", "Search Timeline": "खोज टाइमलाइन", "Home Timeline": "होम टाइमलाइन", "List Timeline": "सूची टाइमलाइन", "Community Timeline": "समुदाय टाइमलाइन", "Community Members": "समुदाय सदस्य", "List Members": "सूची सदस्य", "List Subscribers": "सूची सदस्यता लेने वाले", "Direct Messages": "डायरेक्ट संदेश", "Interaction Events": "इंटरैक्शन घटनाएँ", "Local Search": "स्थानीय खोज", "Runtime Logs": "रनटाइम लॉग", "Bundle Viewer": "बंडल दर्शक", "{{count}} imported bundles": "{{count}} आयातित बंडल", "Search indexed tweets with Twitter-style operators": "Twitter-शैली ऑपरेटरों से इंडेक्स किए गए ट्वीट खोजें", "Local Recorder Search": "स्थानीय रिकॉर्डर खोज", "Quotes": "कोट्स", "CommunityMembers": "समुदाय सदस्य", "CommunityTimeline": "समुदाय टाइमलाइन", "DirectMessages": "डायरेक्ट संदेश", "Followers": "फ़ॉलोअर", "Following": "फ़ॉलो कर रहे हैं", "HomeTimeline": "होम टाइमलाइन", "Likes": "लाइक", "ListMembers": "सूची सदस्य", "ListSubscribers": "सूची सदस्यता लेने वाले", "ListTimeline": "सूची टाइमलाइन", "InteractionEvents": "इंटरैक्शन घटनाएँ", "LocalSearch": "स्थानीय खोज", "RawCapture": "कच्चा कैप्चर", "Retweeters": "रीट्वीट करने वाले", "RetweetersModule": "रीट्वीट करने वाले", "RuntimeLogs": "रनटाइम लॉग", "SearchTimeline": "खोज टाइमलाइन", "TweetIndex": "ट्वीट इंडेक्स", "TweetDetail": "ट्वीट विवरण", "UserDetail": "उपयोगकर्ता विवरण", "UserMedia": "उपयोगकर्ता मीडिया", "UserTweets": "उपयोगकर्ता ट्वीट" } };
  const id = { "exporter": { "ID": "ID", "Date": "Tanggal", "Content": "Konten", "Show Full Text": "Tampilkan teks lengkap", "Media": "Media", "Screen Name": "Nama pengguna", "Profile Name": "Nama profil", "Profile Image": "Gambar profil", "User ID": "ID pengguna", "Replying To": "Membalas ke", "RT Source": "Sumber RT", "Quote Source": "Sumber kutipan", "Media Tags": "Tag media", "Favorites": "Favorit", "Retweets": "Retweet", "Bookmarks": "Bookmark", "Quotes": "Kutipan", "Replies": "Balasan", "Views": "Tayangan", "Favorited": "Difavoritkan", "Retweeted": "Di-retweet", "Bookmarked": "Di-bookmark", "Bookmark Folder": "Folder bookmark", "URL": "URL", "Actions": "Tindakan", "Details": "Detail", "Description": "Deskripsi", "Profile Banner": "Banner profil", "Followers": "Pengikut", "FollowingCount": "Jumlah mengikuti", "Statuses": "Postingan", "Favourites": "Favorit", "Listed": "Masuk daftar", "Location": "Lokasi", "Website": "Situs web", "Birthdate": "Tanggal lahir", "Verified Type": "Jenis verifikasi", "Blue Verified": "Terverifikasi Blue", "Following": "Mengikuti", "Follows You": "Mengikuti Anda", "Can DM": "Bisa DM", "Protected": "Dilindungi", "Created At": "Dibuat pada", "Sender": "Pengirim", "Recipient": "Penerima", "Conversation ID": "ID percakapan", "Conversation Type": "Jenis percakapan", "Data": "Data", "Export captured data as JSON/HTML/CSV file. This may take a while depending on the amount of data. The exported file does not include media files such as images and videos but only the URLs.": "Ekspor data yang ditangkap sebagai file JSON/HTML/CSV. Ini mungkin memerlukan waktu tergantung jumlah data. File ekspor tidak menyertakan file media seperti gambar dan video, hanya URL-nya.", "Data length:": "Panjang data:", "Export scope:": "Cakupan ekspor:", "All current results": "Semua hasil saat ini", "Selected rows": "Baris dipilih", "Pinned result set": "Set hasil yang dipin", "Query": "Kueri", "Sort": "Urutkan", "Include all metadata:": "Sertakan semua metadata:", "Export as:": "Ekspor sebagai:", "No data selected.": "Tidak ada data dipilih.", "Cancel": "Batal", "Start Export": "Mulai ekspor", "Download and save media files from captured data. This may take a while depending on the amount of data. Media that will be downloaded includes: profile images, profile banners (for users), images, videos (for tweets).": "Unduh dan simpan file media dari data yang ditangkap. Ini mungkin memerlukan waktu tergantung jumlah data. Media yang diunduh mencakup gambar profil, banner profil (untuk pengguna), gambar, dan video (untuk tweet).", "For more than 100 media or large files, it is recommended to copy the URLs and download them with an external download manager such as aria2.": "Untuk lebih dari 100 media atau file besar, disarankan menyalin URL dan mengunduhnya dengan pengelola unduhan eksternal seperti aria2.", "Browser ZIP export now uses bounded parallel downloads. For very large video-heavy jobs, URL or aria2 export is still the safest low-memory path.": "Ekspor ZIP browser sekarang menggunakan unduhan paralel terbatas. Untuk pekerjaan sangat besar dan banyak video, ekspor URL atau aria2 tetap jalur paling aman dengan memori rendah.", "Filename template:": "Templat nama file:", "Download scheduler": "Penjadwal unduhan", "Faster defaults are intended for bulk CDN transfer.": "Default yang lebih cepat ditujukan untuk transfer massal CDN.", "Start delay (ms)": "Jeda awal (ms)", "Global parallel": "Paralel global", "Per host": "Per host", "Videos": "Video", "Retries": "Coba lagi", "Use aria2 format:": "Gunakan format aria2:", "Click for more information. Each URL will be on a new line, with its filename on the next line. This format is compatible with aria2.": "Klik untuk informasi lebih lanjut. Setiap URL akan berada di baris baru, dengan nama file di baris berikutnya. Format ini kompatibel dengan aria2.", "Rate limit (ms):": "Batas laju (ms):", "Media Filter:": "Filter media:", "File Name": "Nama file", "Media Type": "Jenis media", "Download URL": "URL unduhan", "No media selected.": "Tidak ada media dipilih.", "Preview limited to first 250 media items.": "Pratinjau dibatasi pada 250 item media pertama.", "Zipping": "Membuat ZIP", "Copied!": "Disalin!", "Copy URLs": "Salin URL", "The tweet ID": "ID tweet", "The username of tweet author": "Nama pengguna penulis tweet", "The profile name of tweet author": "Nama profil penulis tweet", "The media index in tweet (start from 0)": "Indeks media dalam tweet (mulai dari 0)", "The order of media in tweet (1/2/3/4)": "Urutan media dalam tweet (1/2/3/4)", "The post date in YYYYMMDD format": "Tanggal posting dalam format YYYYMMDD", "The post time in HHmmss format": "Waktu posting dalam format HHmmss", "The media type (photo/video/animated_gif)": "Jenis media (photo/video/animated_gif)", "The file extension of media (jpg/png/mp4)": "Ekstensi file media (jpg/png/mp4)", "Failed to export media. Open DevTools for more details.": "Gagal mengekspor media. Buka DevTools untuk detail.", "Failed to copy media URLs. Open DevTools for more details.": "Gagal menyalin URL media. Buka DevTools untuk detail.", "filter.photo": "Foto", "filter.video": "Video", "filter.animated_gif": "GIF", "filter.retweet": "Sertakan retweet" }, "common": { "Open Control Panel": "Buka Panel Kontrol", "Browse around to capture more data.": "Jelajahi halaman untuk menangkap lebih banyak data.", "Settings": "Pengaturan", "General": "Umum", "Theme": "Tema", "Language": "Bahasa", "Debug": "Debug", "Date Time Format": "Format Tanggal dan Waktu", "Click for more information. This will take effect on both previewer and exported files.": "Klik untuk informasi lebih lanjut. Ini akan berlaku pada pratinjau dan file yang diekspor.", "Use dedicated DB for accounts": "Gunakan DB khusus untuk akun", "This will create separate database for each Twitter account, which can help reduce the chance of data mixing when you use multiple accounts.": "Ini akan membuat database terpisah untuk setiap akun Twitter, sehingga mengurangi kemungkinan data tercampur saat memakai beberapa akun.", "Local Database": "Database lokal", "Analyze DB": "Analisis", "Export DB": "Ekspor", "Clear DB": "Hapus", "Bundle Export": "Ekspor bundel", "Bundle Library": "Pustaka bundel", "QC Session": "Sesi QC", "Diagnostic capture": "Tangkapan diagnostik", "Diagnostic buffers cleared.": "Buffer diagnostik dihapus.", "Clear Buffers": "Hapus buffer", "Preparing...": "Menyiapkan...", "Preparing diagnostics...": "Menyiapkan diagnostik...", "Export QC Diagnostics": "Ekspor diagnostik QC", "Export Diagnostics Bundle": "Ekspor bundel diagnostik", "QC idle.": "QC diam.", "Are you sure to clear all data in the database?": "Yakin ingin menghapus semua data di database?", "Database cleared.": "Database dihapus.", "Module": "Modul", "Modules (Scroll to see more)": "Modul (gulir untuk melihat lainnya)", "About": "Tentang", "Version": "Versi", "Search...": "Cari...", "Something went wrong.": "Terjadi kesalahan.", "Error:": "Kesalahan:", "Captured:": "Ditangkap:", "Rows per page:": "Baris per halaman:", "Relation Types": "Jenis relasi", "Subject Accounts": "Akun subjek", "Subject User IDs": "ID pengguna subjek", "Last Observed At": "Terakhir diamati", "A - B of N items": "{{from}} - {{to}} dari {{total}} item", "No data available.": "Tidak ada data.", "No media available.": "Tidak ada media.", "Clear": "Bersihkan", "Bookmark folders": "Folder bookmark", "1 folder selected": "1 folder dipilih", "{{count}} folders selected": "{{count}} folder dipilih", "Export Media": "Ekspor media", "Export Data": "Ekspor data", "Export Search History": "Ekspor riwayat pencarian", "JSON View": "Tampilan JSON", "Media View": "Tampilan media", "Bookmarks": "Bookmark", "Tweets": "Tweet", "Users": "Pengguna", "User Tweets": "Tweet pengguna", "User Media": "Media pengguna", "Tweet Details": "Detail tweet", "Search Timeline": "Linimasa pencarian", "Home Timeline": "Linimasa beranda", "List Timeline": "Linimasa daftar", "Community Timeline": "Linimasa komunitas", "Community Members": "Anggota komunitas", "List Members": "Anggota daftar", "List Subscribers": "Pelanggan daftar", "Direct Messages": "Pesan langsung", "Interaction Events": "Peristiwa interaksi", "Local Search": "Pencarian lokal", "Runtime Logs": "Log runtime", "Bundle Viewer": "Penampil bundel", "{{count}} imported bundles": "{{count}} bundel diimpor", "Search indexed tweets with Twitter-style operators": "Cari tweet terindeks dengan operator gaya Twitter", "Local Recorder Search": "Pencarian perekam lokal", "Quotes": "Kutipan", "CommunityMembers": "Anggota komunitas", "CommunityTimeline": "Linimasa komunitas", "DirectMessages": "Pesan langsung", "Followers": "Pengikut", "Following": "Mengikuti", "HomeTimeline": "Linimasa beranda", "Likes": "Suka", "ListMembers": "Anggota daftar", "ListSubscribers": "Pelanggan daftar", "ListTimeline": "Linimasa daftar", "InteractionEvents": "Peristiwa interaksi", "LocalSearch": "Pencarian lokal", "RawCapture": "Tangkapan mentah", "Retweeters": "Pengguna yang me-retweet", "RetweetersModule": "Pengguna yang me-retweet", "RuntimeLogs": "Log runtime", "SearchTimeline": "Pencarian", "TweetIndex": "Indeks tweet", "TweetDetail": "Detail tweet", "UserDetail": "Detail pengguna", "UserMedia": "Media pengguna", "UserTweets": "Tweet pengguna" } };
  const ja = { "exporter": { "ID": "ID", "Date": "日付", "Content": "内容", "Show Full Text": "全文を表示", "Media": "メディア", "Screen Name": "ユーザー名", "Profile Name": "プロフィール名", "Profile Image": "プロフィール画像", "User ID": "ユーザーID", "Replying To": "返信先", "RT Source": "RT元", "Quote Source": "引用元", "Media Tags": "メディアタグ", "Favorites": "お気に入り", "Retweets": "リツイート", "Bookmarks": "ブックマーク", "Quotes": "引用", "Replies": "返信", "Views": "表示回数", "Favorited": "お気に入り済み", "Retweeted": "リツイート済み", "Bookmarked": "ブックマーク済み", "Bookmark Folder": "ブックマークフォルダ", "URL": "URL", "Actions": "操作", "Details": "詳細", "Description": "説明", "Profile Banner": "プロフィールバナー", "Followers": "フォロワー", "FollowingCount": "フォロー数", "Statuses": "投稿数", "Favourites": "お気に入り", "Listed": "リスト登録数", "Location": "場所", "Website": "Webサイト", "Birthdate": "生年月日", "Verified Type": "認証タイプ", "Blue Verified": "Blue認証", "Following": "フォロー中", "Follows You": "あなたをフォロー", "Can DM": "DM可能", "Protected": "非公開", "Created At": "作成日時", "Sender": "送信者", "Recipient": "受信者", "Conversation ID": "会話ID", "Conversation Type": "会話タイプ", "Data": "データ", "Export captured data as JSON/HTML/CSV file. This may take a while depending on the amount of data. The exported file does not include media files such as images and videos but only the URLs.": "取得したデータをJSON/HTML/CSVファイルとしてエクスポートします。データ量によって時間がかかる場合があります。エクスポートファイルには画像や動画などのメディアファイルは含まれず、URLのみが含まれます。", "Data length:": "データ件数:", "Export scope:": "エクスポート範囲:", "All current results": "現在の全結果", "Selected rows": "選択した行", "Pinned result set": "固定された結果セット", "Query": "クエリ", "Sort": "並び順", "Include all metadata:": "すべてのメタデータを含める:", "Export as:": "形式:", "No data selected.": "データが選択されていません。", "Cancel": "キャンセル", "Start Export": "エクスポート開始", "Download and save media files from captured data. This may take a while depending on the amount of data. Media that will be downloaded includes: profile images, profile banners (for users), images, videos (for tweets).": "取得データからメディアファイルをダウンロードして保存します。データ量によって時間がかかる場合があります。ダウンロード対象にはプロフィール画像、プロフィールバナー(ユーザー)、画像、動画(ツイート)が含まれます。", "For more than 100 media or large files, it is recommended to copy the URLs and download them with an external download manager such as aria2.": "100件を超えるメディアや大きなファイルでは、URLをコピーし、aria2などの外部ダウンロードマネージャーでダウンロードすることを推奨します。", "Browser ZIP export now uses bounded parallel downloads. For very large video-heavy jobs, URL or aria2 export is still the safest low-memory path.": "ブラウザのZIPエクスポートは、制限付き並列ダウンロードを使用するようになりました。非常に大きく動画の多い処理では、URLまたはaria2エクスポートが引き続き最も安全で低メモリな方法です。", "Filename template:": "ファイル名テンプレート:", "Download scheduler": "ダウンロードスケジューラ", "Faster defaults are intended for bulk CDN transfer.": "高速な初期値はCDNからの一括転送向けです。", "Start delay (ms)": "開始遅延 (ms)", "Global parallel": "全体並列数", "Per host": "ホストごと", "Videos": "動画", "Retries": "再試行", "Use aria2 format:": "aria2形式を使用:", "Click for more information. Each URL will be on a new line, with its filename on the next line. This format is compatible with aria2.": "詳細を確認するにはクリックしてください。各URLは新しい行に、その次の行にファイル名が出力されます。この形式はaria2と互換性があります。", "Rate limit (ms):": "レート制限 (ms):", "Media Filter:": "メディアフィルター:", "File Name": "ファイル名", "Media Type": "メディアタイプ", "Download URL": "ダウンロードURL", "No media selected.": "メディアが選択されていません。", "Preview limited to first 250 media items.": "プレビューは最初の250件のメディアに制限されています。", "Zipping": "ZIP作成中", "Copied!": "コピーしました!", "Copy URLs": "URLをコピー", "The tweet ID": "ツイートID", "The username of tweet author": "ツイート投稿者のユーザー名", "The profile name of tweet author": "ツイート投稿者のプロフィール名", "The media index in tweet (start from 0)": "ツイート内のメディアインデックス(0から開始)", "The order of media in tweet (1/2/3/4)": "ツイート内のメディア順序(1/2/3/4)", "The post date in YYYYMMDD format": "YYYYMMDD形式の投稿日", "The post time in HHmmss format": "HHmmss形式の投稿時刻", "The media type (photo/video/animated_gif)": "メディアタイプ(photo/video/animated_gif)", "The file extension of media (jpg/png/mp4)": "メディアファイル拡張子(jpg/png/mp4)", "Failed to export media. Open DevTools for more details.": "メディアのエクスポートに失敗しました。詳細はDevToolsを開いてください。", "Failed to copy media URLs. Open DevTools for more details.": "メディアURLのコピーに失敗しました。詳細はDevToolsを開いてください。", "filter.photo": "写真", "filter.video": "動画", "filter.animated_gif": "GIF", "filter.retweet": "リツイートを含める" }, "common": { "Open Control Panel": "コントロールパネルを開く", "Browse around to capture more data.": "ページを閲覧して、さらにデータを取得します。", "Settings": "設定", "General": "一般", "Theme": "テーマ", "Language": "言語", "Debug": "デバッグ", "Date Time Format": "日時形式", "Click for more information. This will take effect on both previewer and exported files.": "詳細を確認するにはクリックしてください。これはプレビューとエクスポートファイルの両方に適用されます。", "Use dedicated DB for accounts": "アカウントごとに専用DBを使用", "This will create separate database for each Twitter account, which can help reduce the chance of data mixing when you use multiple accounts.": "Twitterアカウントごとに別々のデータベースを作成します。複数アカウント使用時のデータ混在を減らせます。", "Local Database": "ローカルデータベース", "Analyze DB": "分析", "Export DB": "エクスポート", "Clear DB": "削除", "Bundle Export": "バンドルをエクスポート", "Bundle Library": "バンドルライブラリ", "QC Session": "QCセッション", "Diagnostic capture": "診断キャプチャ", "Diagnostic buffers cleared.": "診断バッファをクリアしました。", "Clear Buffers": "バッファをクリア", "Preparing...": "準備中...", "Preparing diagnostics...": "診断を準備中...", "Export QC Diagnostics": "QC診断をエクスポート", "Export Diagnostics Bundle": "診断バンドルをエクスポート", "QC idle.": "QCは待機中です。", "Are you sure to clear all data in the database?": "データベース内のすべてのデータを削除しますか?", "Database cleared.": "データベースを削除しました。", "Module": "モジュール", "Modules (Scroll to see more)": "モジュール(スクロールしてさらに表示)", "About": "情報", "Version": "バージョン", "Search...": "検索...", "Something went wrong.": "問題が発生しました。", "Error:": "エラー:", "Captured:": "取得済み:", "Rows per page:": "ページあたりの行数:", "Relation Types": "関係タイプ", "Subject Accounts": "対象アカウント", "Subject User IDs": "対象ユーザーID", "Last Observed At": "最終観測日時", "A - B of N items": "{{total}}件中 {{from}} - {{to}} 件", "No data available.": "データがありません。", "No media available.": "メディアがありません。", "Clear": "クリア", "Bookmark folders": "ブックマークフォルダ", "1 folder selected": "1個のフォルダを選択中", "{{count}} folders selected": "{{count}}個のフォルダを選択中", "Export Media": "メディアをエクスポート", "Export Data": "データをエクスポート", "Export Search History": "検索履歴をエクスポート", "JSON View": "JSON表示", "Media View": "メディア表示", "Bookmarks": "ブックマーク", "Tweets": "ツイート", "Users": "ユーザー", "User Tweets": "ユーザーのツイート", "User Media": "ユーザーメディア", "Tweet Details": "ツイート詳細", "Search Timeline": "検索タイムライン", "Home Timeline": "ホームタイムライン", "List Timeline": "リストタイムライン", "Community Timeline": "コミュニティタイムライン", "Community Members": "コミュニティメンバー", "List Members": "リストメンバー", "List Subscribers": "リスト購読者", "Direct Messages": "ダイレクトメッセージ", "Interaction Events": "インタラクションイベント", "Local Search": "ローカル検索", "Runtime Logs": "ランタイムログ", "Bundle Viewer": "バンドルビューア", "{{count}} imported bundles": "{{count}}個のバンドルをインポート済み", "Search indexed tweets with Twitter-style operators": "Twitter風の演算子でインデックス済みツイートを検索", "Local Recorder Search": "ローカルレコーダー検索", "Quotes": "引用", "CommunityMembers": "コミュニティメンバー", "CommunityTimeline": "コミュニティタイムライン", "DirectMessages": "ダイレクトメッセージ", "Followers": "フォロワー", "Following": "フォロー中", "HomeTimeline": "ホームタイムライン", "Likes": "いいね", "ListMembers": "リストメンバー", "ListSubscribers": "リスト購読者", "ListTimeline": "リストタイムライン", "InteractionEvents": "インタラクションイベント", "LocalSearch": "ローカル検索", "RawCapture": "生キャプチャ", "Retweeters": "リツイートしたユーザー", "RetweetersModule": "リツイートしたユーザー", "RuntimeLogs": "ランタイムログ", "SearchTimeline": "検索", "TweetIndex": "ツイートインデックス", "TweetDetail": "ツイート詳細", "UserDetail": "ユーザー詳細", "UserMedia": "ユーザーメディア", "UserTweets": "ユーザーのツイート" } };
  const pt_BR = { "exporter": { "ID": "ID", "Date": "Data", "Content": "Conteúdo", "Show Full Text": "Mostrar texto completo", "Media": "Mídia", "Screen Name": "Nome de usuário", "Profile Name": "Nome do perfil", "Profile Image": "Imagem do perfil", "User ID": "ID do usuário", "Replying To": "Respondendo a", "RT Source": "Fonte do RT", "Quote Source": "Fonte da citação", "Media Tags": "Tags de mídia", "Favorites": "Favoritos", "Retweets": "Retweets", "Bookmarks": "Favoritos", "Quotes": "Citações", "Replies": "Respostas", "Views": "Visualizações", "Favorited": "Favoritado", "Retweeted": "Retweetado", "Bookmarked": "Salvo nos favoritos", "Bookmark Folder": "Pasta de favoritos", "URL": "URL", "Actions": "Ações", "Details": "Detalhes", "Description": "Descrição", "Profile Banner": "Banner do perfil", "Followers": "Seguidores", "FollowingCount": "Contagem de seguindo", "Statuses": "Publicações", "Favourites": "Favoritos", "Listed": "Em listas", "Location": "Localização", "Website": "Site", "Birthdate": "Data de nascimento", "Verified Type": "Tipo de verificação", "Blue Verified": "Verificado Blue", "Following": "Seguindo", "Follows You": "Segue você", "Can DM": "Pode receber DM", "Protected": "Protegido", "Created At": "Criado em", "Sender": "Remetente", "Recipient": "Destinatário", "Conversation ID": "ID da conversa", "Conversation Type": "Tipo de conversa", "Data": "Dados", "Export captured data as JSON/HTML/CSV file. This may take a while depending on the amount of data. The exported file does not include media files such as images and videos but only the URLs.": "Exporte os dados capturados como arquivo JSON/HTML/CSV. Isso pode demorar conforme a quantidade de dados. O arquivo exportado não inclui arquivos de mídia, como imagens e vídeos, apenas as URLs.", "Data length:": "Tamanho dos dados:", "Export scope:": "Escopo da exportação:", "All current results": "Todos os resultados atuais", "Selected rows": "Linhas selecionadas", "Pinned result set": "Conjunto de resultados fixado", "Query": "Consulta", "Sort": "Ordenação", "Include all metadata:": "Incluir todos os metadados:", "Export as:": "Exportar como:", "No data selected.": "Nenhum dado selecionado.", "Cancel": "Cancelar", "Start Export": "Iniciar exportação", "Download and save media files from captured data. This may take a while depending on the amount of data. Media that will be downloaded includes: profile images, profile banners (for users), images, videos (for tweets).": "Baixe e salve arquivos de mídia dos dados capturados. Isso pode demorar conforme a quantidade de dados. A mídia baixada inclui imagens de perfil, banners de perfil (para usuários), imagens e vídeos (para tweets).", "For more than 100 media or large files, it is recommended to copy the URLs and download them with an external download manager such as aria2.": "Para mais de 100 mídias ou arquivos grandes, recomenda-se copiar as URLs e baixar com um gerenciador externo como aria2.", "Browser ZIP export now uses bounded parallel downloads. For very large video-heavy jobs, URL or aria2 export is still the safest low-memory path.": "A exportação ZIP no navegador agora usa downloads paralelos limitados. Para tarefas muito grandes com muitos vídeos, exportar URLs ou usar aria2 ainda é o caminho mais seguro e com menor uso de memória.", "Filename template:": "Modelo de nome de arquivo:", "Download scheduler": "Agendador de downloads", "Faster defaults are intended for bulk CDN transfer.": "Os padrões mais rápidos são pensados para transferência em massa via CDN.", "Start delay (ms)": "Atraso inicial (ms)", "Global parallel": "Paralelo global", "Per host": "Por host", "Videos": "Vídeos", "Retries": "Tentativas", "Use aria2 format:": "Usar formato aria2:", "Click for more information. Each URL will be on a new line, with its filename on the next line. This format is compatible with aria2.": "Clique para mais informações. Cada URL ficará em uma nova linha, com o nome do arquivo na linha seguinte. Esse formato é compatível com aria2.", "Rate limit (ms):": "Limite de taxa (ms):", "Media Filter:": "Filtro de mídia:", "File Name": "Nome do arquivo", "Media Type": "Tipo de mídia", "Download URL": "URL de download", "No media selected.": "Nenhuma mídia selecionada.", "Preview limited to first 250 media items.": "Prévia limitada aos primeiros 250 itens de mídia.", "Zipping": "Compactando", "Copied!": "Copiado!", "Copy URLs": "Copiar URLs", "The tweet ID": "O ID do tweet", "The username of tweet author": "O nome de usuário do autor do tweet", "The profile name of tweet author": "O nome de perfil do autor do tweet", "The media index in tweet (start from 0)": "O índice da mídia no tweet (começa em 0)", "The order of media in tweet (1/2/3/4)": "A ordem da mídia no tweet (1/2/3/4)", "The post date in YYYYMMDD format": "A data da publicação no formato YYYYMMDD", "The post time in HHmmss format": "A hora da publicação no formato HHmmss", "The media type (photo/video/animated_gif)": "O tipo de mídia (photo/video/animated_gif)", "The file extension of media (jpg/png/mp4)": "A extensão do arquivo de mídia (jpg/png/mp4)", "Failed to export media. Open DevTools for more details.": "Falha ao exportar mídia. Abra o DevTools para mais detalhes.", "Failed to copy media URLs. Open DevTools for more details.": "Falha ao copiar URLs de mídia. Abra o DevTools para mais detalhes.", "filter.photo": "Foto", "filter.video": "Vídeo", "filter.animated_gif": "GIF", "filter.retweet": "Incluir retweets" }, "common": { "Open Control Panel": "Abrir painel de controle", "Browse around to capture more data.": "Navegue para capturar mais dados.", "Settings": "Configurações", "General": "Geral", "Theme": "Tema", "Language": "Idioma", "Debug": "Depuração", "Date Time Format": "Formato de data e hora", "Click for more information. This will take effect on both previewer and exported files.": "Clique para mais informações. Isso terá efeito no visualizador e nos arquivos exportados.", "Use dedicated DB for accounts": "Usar DB dedicado para contas", "This will create separate database for each Twitter account, which can help reduce the chance of data mixing when you use multiple accounts.": "Isso criará um banco de dados separado para cada conta do Twitter, ajudando a reduzir a chance de mistura de dados ao usar várias contas.", "Local Database": "Banco de dados local", "Analyze DB": "Analisar", "Export DB": "Exportar", "Clear DB": "Limpar", "Bundle Export": "Exportar pacote", "Bundle Library": "Biblioteca de pacotes", "QC Session": "Sessão de QC", "Diagnostic capture": "Captura diagnóstica", "Diagnostic buffers cleared.": "Buffers de diagnóstico limpos.", "Clear Buffers": "Limpar buffers", "Preparing...": "Preparando...", "Preparing diagnostics...": "Preparando diagnósticos...", "Export QC Diagnostics": "Exportar diagnósticos de QC", "Export Diagnostics Bundle": "Exportar pacote de diagnósticos", "QC idle.": "QC ocioso.", "Are you sure to clear all data in the database?": "Tem certeza de que deseja limpar todos os dados do banco?", "Database cleared.": "Banco de dados limpo.", "Module": "Módulo", "Modules (Scroll to see more)": "Módulos (role para ver mais)", "About": "Sobre", "Version": "Versão", "Search...": "Pesquisar...", "Something went wrong.": "Algo deu errado.", "Error:": "Erro:", "Captured:": "Capturado:", "Rows per page:": "Linhas por página:", "Relation Types": "Tipos de relação", "Subject Accounts": "Contas sujeitas", "Subject User IDs": "IDs de usuário sujeitos", "Last Observed At": "Última observação", "A - B of N items": "{{from}} - {{to}} de {{total}} itens", "No data available.": "Nenhum dado disponível.", "No media available.": "Nenhuma mídia disponível.", "Clear": "Limpar", "Bookmark folders": "Pastas de favoritos", "1 folder selected": "1 pasta selecionada", "{{count}} folders selected": "{{count}} pastas selecionadas", "Export Media": "Exportar mídia", "Export Data": "Exportar dados", "Export Search History": "Exportar histórico de pesquisa", "JSON View": "Visualização JSON", "Media View": "Visualização de mídia", "Bookmarks": "Favoritos", "Tweets": "Tweets", "Users": "Usuários", "User Tweets": "Tweets do usuário", "User Media": "Mídia do usuário", "Tweet Details": "Detalhes do tweet", "Search Timeline": "Linha do tempo de pesquisa", "Home Timeline": "Linha do tempo inicial", "List Timeline": "Linha do tempo da lista", "Community Timeline": "Linha do tempo da comunidade", "Community Members": "Membros da comunidade", "List Members": "Membros da lista", "List Subscribers": "Assinantes da lista", "Direct Messages": "Mensagens diretas", "Interaction Events": "Eventos de interação", "Local Search": "Pesquisa local", "Runtime Logs": "Logs de execução", "Bundle Viewer": "Visualizador de pacotes", "{{count}} imported bundles": "{{count}} pacotes importados", "Search indexed tweets with Twitter-style operators": "Pesquise tweets indexados com operadores no estilo Twitter", "Local Recorder Search": "Pesquisa do gravador local", "Quotes": "Citações", "CommunityMembers": "Membros da comunidade", "CommunityTimeline": "Linha do tempo da comunidade", "DirectMessages": "Mensagens diretas", "Followers": "Seguidores", "Following": "Seguindo", "HomeTimeline": "Linha do tempo inicial", "Likes": "Curtidas", "ListMembers": "Membros da lista", "ListSubscribers": "Assinantes da lista", "ListTimeline": "Linha do tempo da lista", "InteractionEvents": "Eventos de interação", "LocalSearch": "Pesquisa local", "RawCapture": "Captura bruta", "Retweeters": "Retuitadores", "RetweetersModule": "Retuitadores", "RuntimeLogs": "Logs de execução", "SearchTimeline": "Pesquisa", "TweetIndex": "Índice de tweets", "TweetDetail": "Detalhe do tweet", "UserDetail": "Detalhe do usuário", "UserMedia": "Mídia do usuário", "UserTweets": "Tweets do usuário" } };
  const ru = { "exporter": { "ID": "ID", "Date": "Дата", "Content": "Содержимое", "Show Full Text": "Показать полный текст", "Media": "Медиа", "Screen Name": "Имя пользователя", "Profile Name": "Имя профиля", "Profile Image": "Изображение профиля", "User ID": "ID пользователя", "Replying To": "Ответ на", "RT Source": "Источник RT", "Quote Source": "Источник цитаты", "Media Tags": "Медиа-теги", "Favorites": "Избранное", "Retweets": "Ретвиты", "Bookmarks": "Закладки", "Quotes": "Цитаты", "Replies": "Ответы", "Views": "Просмотры", "Favorited": "В избранном", "Retweeted": "Ретвитнуто", "Bookmarked": "В закладках", "Bookmark Folder": "Папка закладок", "URL": "URL", "Actions": "Действия", "Details": "Детали", "Description": "Описание", "Profile Banner": "Баннер профиля", "Followers": "Подписчики", "FollowingCount": "Количество подписок", "Statuses": "Публикации", "Favourites": "Избранное", "Listed": "В списках", "Location": "Местоположение", "Website": "Сайт", "Birthdate": "Дата рождения", "Verified Type": "Тип верификации", "Blue Verified": "Blue верифицирован", "Following": "Подписки", "Follows You": "Подписан на вас", "Can DM": "Можно написать в DM", "Protected": "Защищён", "Created At": "Создано", "Sender": "Отправитель", "Recipient": "Получатель", "Conversation ID": "ID беседы", "Conversation Type": "Тип беседы", "Data": "Данные", "Export captured data as JSON/HTML/CSV file. This may take a while depending on the amount of data. The exported file does not include media files such as images and videos but only the URLs.": "Экспортируйте захваченные данные в JSON/HTML/CSV. Это может занять время в зависимости от объёма данных. Экспортируемый файл не содержит медиафайлы, такие как изображения и видео, а только URL.", "Data length:": "Длина данных:", "Export scope:": "Область экспорта:", "All current results": "Все текущие результаты", "Selected rows": "Выбранные строки", "Pinned result set": "Закреплённый набор результатов", "Query": "Запрос", "Sort": "Сортировка", "Include all metadata:": "Включить все метаданные:", "Export as:": "Экспортировать как:", "No data selected.": "Данные не выбраны.", "Cancel": "Отмена", "Start Export": "Начать экспорт", "Download and save media files from captured data. This may take a while depending on the amount of data. Media that will be downloaded includes: profile images, profile banners (for users), images, videos (for tweets).": "Скачать и сохранить медиафайлы из захваченных данных. Это может занять время в зависимости от объёма данных. Будут скачаны изображения профиля, баннеры профиля (для пользователей), изображения и видео (для твитов).", "For more than 100 media or large files, it is recommended to copy the URLs and download them with an external download manager such as aria2.": "Для более чем 100 медиа или крупных файлов рекомендуется скопировать URL и скачать их внешним менеджером, например aria2.", "Browser ZIP export now uses bounded parallel downloads. For very large video-heavy jobs, URL or aria2 export is still the safest low-memory path.": "ZIP-экспорт в браузере теперь использует ограниченные параллельные загрузки. Для очень больших задач с множеством видео экспорт URL или aria2 остаётся самым безопасным вариантом с низким потреблением памяти.", "Filename template:": "Шаблон имени файла:", "Download scheduler": "Планировщик загрузок", "Faster defaults are intended for bulk CDN transfer.": "Более быстрые значения по умолчанию предназначены для массовой передачи через CDN.", "Start delay (ms)": "Начальная задержка (мс)", "Global parallel": "Общий параллелизм", "Per host": "На хост", "Videos": "Видео", "Retries": "Повторы", "Use aria2 format:": "Использовать формат aria2:", "Click for more information. Each URL will be on a new line, with its filename on the next line. This format is compatible with aria2.": "Нажмите для подробностей. Каждый URL будет на новой строке, а имя файла — на следующей. Формат совместим с aria2.", "Rate limit (ms):": "Ограничение скорости (мс):", "Media Filter:": "Фильтр медиа:", "File Name": "Имя файла", "Media Type": "Тип медиа", "Download URL": "URL загрузки", "No media selected.": "Медиа не выбраны.", "Preview limited to first 250 media items.": "Предпросмотр ограничен первыми 250 медиаэлементами.", "Zipping": "Упаковка ZIP", "Copied!": "Скопировано!", "Copy URLs": "Копировать URL", "The tweet ID": "ID твита", "The username of tweet author": "Имя пользователя автора твита", "The profile name of tweet author": "Имя профиля автора твита", "The media index in tweet (start from 0)": "Индекс медиа в твите (начинается с 0)", "The order of media in tweet (1/2/3/4)": "Порядок медиа в твите (1/2/3/4)", "The post date in YYYYMMDD format": "Дата публикации в формате YYYYMMDD", "The post time in HHmmss format": "Время публикации в формате HHmmss", "The media type (photo/video/animated_gif)": "Тип медиа (photo/video/animated_gif)", "The file extension of media (jpg/png/mp4)": "Расширение медиафайла (jpg/png/mp4)", "Failed to export media. Open DevTools for more details.": "Не удалось экспортировать медиа. Откройте DevTools для подробностей.", "Failed to copy media URLs. Open DevTools for more details.": "Не удалось скопировать URL медиа. Откройте DevTools для подробностей.", "filter.photo": "Фото", "filter.video": "Видео", "filter.animated_gif": "GIF", "filter.retweet": "Включить ретвиты" }, "common": { "Open Control Panel": "Открыть панель управления", "Browse around to capture more data.": "Просматривайте страницы, чтобы захватывать больше данных.", "Settings": "Настройки", "General": "Общие", "Theme": "Тема", "Language": "Язык", "Debug": "Отладка", "Date Time Format": "Формат даты и времени", "Click for more information. This will take effect on both previewer and exported files.": "Нажмите, чтобы узнать подробнее. Это повлияет и на предпросмотр, и на экспортируемые файлы.", "Use dedicated DB for accounts": "Использовать отдельную БД для аккаунтов", "This will create separate database for each Twitter account, which can help reduce the chance of data mixing when you use multiple accounts.": "Это создаст отдельную базу данных для каждого аккаунта Twitter и снизит риск смешивания данных при использовании нескольких аккаунтов.", "Local Database": "Локальная база данных", "Analyze DB": "Анализ", "Export DB": "Экспорт", "Clear DB": "Очистить", "Bundle Export": "Экспорт пакета", "Bundle Library": "Библиотека пакетов", "QC Session": "Сессия QC", "Diagnostic capture": "Диагностический захват", "Diagnostic buffers cleared.": "Диагностические буферы очищены.", "Clear Buffers": "Очистить буферы", "Preparing...": "Подготовка...", "Preparing diagnostics...": "Подготовка диагностики...", "Export QC Diagnostics": "Экспорт диагностики QC", "Export Diagnostics Bundle": "Экспорт диагностического пакета", "QC idle.": "QC бездействует.", "Are you sure to clear all data in the database?": "Вы уверены, что хотите удалить все данные из базы?", "Database cleared.": "База данных очищена.", "Module": "Модуль", "Modules (Scroll to see more)": "Модули (прокрутите, чтобы увидеть больше)", "About": "О программе", "Version": "Версия", "Search...": "Поиск...", "Something went wrong.": "Что-то пошло не так.", "Error:": "Ошибка:", "Captured:": "Захвачено:", "Rows per page:": "Строк на странице:", "Relation Types": "Типы связей", "Subject Accounts": "Целевые аккаунты", "Subject User IDs": "ID целевых пользователей", "Last Observed At": "Последнее наблюдение", "A - B of N items": "{{from}} - {{to}} из {{total}} элементов", "No data available.": "Нет доступных данных.", "No media available.": "Нет доступных медиа.", "Clear": "Очистить", "Bookmark folders": "Папки закладок", "1 folder selected": "Выбрана 1 папка", "{{count}} folders selected": "Выбрано папок: {{count}}", "Export Media": "Экспорт медиа", "Export Data": "Экспорт данных", "Export Search History": "Экспорт истории поиска", "JSON View": "JSON-представление", "Media View": "Медиа-представление", "Bookmarks": "Закладки", "Tweets": "Твиты", "Users": "Пользователи", "User Tweets": "Твиты пользователя", "User Media": "Медиа пользователя", "Tweet Details": "Детали твита", "Search Timeline": "Лента поиска", "Home Timeline": "Домашняя лента", "List Timeline": "Лента списка", "Community Timeline": "Лента сообщества", "Community Members": "Участники сообщества", "List Members": "Участники списка", "List Subscribers": "Подписчики списка", "Direct Messages": "Личные сообщения", "Interaction Events": "События взаимодействия", "Local Search": "Локальный поиск", "Runtime Logs": "Журналы выполнения", "Bundle Viewer": "Просмотр пакетов", "{{count}} imported bundles": "Импортировано пакетов: {{count}}", "Search indexed tweets with Twitter-style operators": "Поиск индексированных твитов с операторами в стиле Twitter", "Local Recorder Search": "Поиск локального регистратора", "Quotes": "Цитаты", "CommunityMembers": "Участники сообщества", "CommunityTimeline": "Лента сообщества", "DirectMessages": "Личные сообщения", "Followers": "Подписчики", "Following": "Подписки", "HomeTimeline": "Домашняя лента", "Likes": "Лайки", "ListMembers": "Участники списка", "ListSubscribers": "Подписчики списка", "ListTimeline": "Лента списка", "InteractionEvents": "События взаимодействия", "LocalSearch": "Локальный поиск", "RawCapture": "Сырой захват", "Retweeters": "Ретвитнувшие", "RetweetersModule": "Ретвитнувшие", "RuntimeLogs": "Журналы выполнения", "SearchTimeline": "Поиск", "TweetIndex": "Индекс твитов", "TweetDetail": "Детали твита", "UserDetail": "Детали пользователя", "UserMedia": "Медиа пользователя", "UserTweets": "Твиты пользователя" } };
  const ur = { "exporter": { "ID": "ID", "Date": "تاریخ", "Content": "مواد", "Show Full Text": "مکمل متن دکھائیں", "Media": "میڈیا", "Screen Name": "صارف نام", "Profile Name": "پروفائل نام", "Profile Image": "پروفائل تصویر", "User ID": "صارف ID", "Replying To": "جواب برائے", "RT Source": "RT ماخذ", "Quote Source": "اقتباس ماخذ", "Media Tags": "میڈیا ٹیگز", "Favorites": "پسندیدہ", "Retweets": "ری ٹویٹس", "Bookmarks": "بک مارکس", "Quotes": "اقتباسات", "Replies": "جوابات", "Views": "دیکھے جانے کی تعداد", "Favorited": "پسندیدہ کیا گیا", "Retweeted": "ری ٹویٹ کیا گیا", "Bookmarked": "بک مارک کیا گیا", "Bookmark Folder": "بک مارک فولڈر", "URL": "URL", "Actions": "اعمال", "Details": "تفصیلات", "Description": "تفصیل", "Profile Banner": "پروفائل بینر", "Followers": "فالوورز", "FollowingCount": "فالوونگ تعداد", "Statuses": "پوسٹس", "Favourites": "پسندیدہ", "Listed": "لسٹ میں شامل", "Location": "مقام", "Website": "ویب سائٹ", "Birthdate": "تاریخ پیدائش", "Verified Type": "تصدیق کی قسم", "Blue Verified": "Blue تصدیق شدہ", "Following": "فالو کر رہے ہیں", "Follows You": "آپ کو فالو کرتا ہے", "Can DM": "DM ہو سکتا ہے", "Protected": "محفوظ", "Created At": "بنایا گیا", "Sender": "بھیجنے والا", "Recipient": "وصول کنندہ", "Conversation ID": "گفتگو ID", "Conversation Type": "گفتگو کی قسم", "Data": "ڈیٹا", "Export captured data as JSON/HTML/CSV file. This may take a while depending on the amount of data. The exported file does not include media files such as images and videos but only the URLs.": "محفوظ شدہ ڈیٹا کو JSON/HTML/CSV فائل کے طور پر برآمد کریں۔ ڈیٹا کی مقدار کے مطابق وقت لگ سکتا ہے۔ برآمد شدہ فائل میں تصاویر اور ویڈیوز جیسے میڈیا فائلز شامل نہیں ہوں گے، صرف URL ہوں گے۔", "Data length:": "ڈیٹا لمبائی:", "Export scope:": "برآمد کا دائرہ:", "All current results": "تمام موجودہ نتائج", "Selected rows": "منتخب قطاریں", "Pinned result set": "پن کیا ہوا نتیجہ سیٹ", "Query": "Query", "Sort": "ترتیب", "Include all metadata:": "تمام میٹا ڈیٹا شامل کریں:", "Export as:": "اس طور پر برآمد کریں:", "No data selected.": "کوئی ڈیٹا منتخب نہیں۔", "Cancel": "منسوخ", "Start Export": "برآمد شروع کریں", "Download and save media files from captured data. This may take a while depending on the amount of data. Media that will be downloaded includes: profile images, profile banners (for users), images, videos (for tweets).": "محفوظ شدہ ڈیٹا سے میڈیا فائلیں ڈاؤن لوڈ اور محفوظ کریں۔ ڈیٹا کی مقدار کے مطابق وقت لگ سکتا ہے۔ ڈاؤن لوڈ ہونے والے میڈیا میں پروفائل تصاویر، پروفائل بینرز (صارفین کے لیے)، تصاویر اور ویڈیوز (ٹویٹس کے لیے) شامل ہیں۔", "For more than 100 media or large files, it is recommended to copy the URLs and download them with an external download manager such as aria2.": "100 سے زیادہ میڈیا یا بڑی فائلوں کے لیے URL کاپی کر کے aria2 جیسے بیرونی ڈاؤن لوڈ مینیجر سے ڈاؤن لوڈ کرنا بہتر ہے۔", "Browser ZIP export now uses bounded parallel downloads. For very large video-heavy jobs, URL or aria2 export is still the safest low-memory path.": "براؤزر ZIP برآمد اب محدود متوازی ڈاؤن لوڈز استعمال کرتی ہے۔ بہت بڑے ویڈیو والے کاموں کے لیے URL یا aria2 برآمد اب بھی کم میموری والا محفوظ ترین راستہ ہے۔", "Filename template:": "فائل نام ٹیمپلیٹ:", "Download scheduler": "ڈاؤن لوڈ شیڈولر", "Faster defaults are intended for bulk CDN transfer.": "تیز ڈیفالٹس بڑے CDN ٹرانسفر کے لیے ہیں۔", "Start delay (ms)": "آغاز تاخیر (ms)", "Global parallel": "عالمی متوازی", "Per host": "فی ہوسٹ", "Videos": "ویڈیوز", "Retries": "دوبارہ کوششیں", "Use aria2 format:": "aria2 فارمیٹ استعمال کریں:", "Click for more information. Each URL will be on a new line, with its filename on the next line. This format is compatible with aria2.": "مزید معلومات کے لیے کلک کریں۔ ہر URL نئی لائن میں ہوگا، اور اس کا فائل نام اگلی لائن میں ہوگا۔ یہ فارمیٹ aria2 سے مطابقت رکھتا ہے۔", "Rate limit (ms):": "رفتار حد (ms):", "Media Filter:": "میڈیا فلٹر:", "File Name": "فائل نام", "Media Type": "میڈیا قسم", "Download URL": "ڈاؤن لوڈ URL", "No media selected.": "کوئی میڈیا منتخب نہیں۔", "Preview limited to first 250 media items.": "پیش نظارہ پہلے 250 میڈیا آئٹمز تک محدود ہے۔", "Zipping": "ZIP بن رہی ہے", "Copied!": "کاپی ہو گیا!", "Copy URLs": "URL کاپی کریں", "The tweet ID": "ٹویٹ ID", "The username of tweet author": "ٹویٹ مصنف کا صارف نام", "The profile name of tweet author": "ٹویٹ مصنف کا پروفائل نام", "The media index in tweet (start from 0)": "ٹویٹ میں میڈیا انڈیکس (0 سے شروع)", "The order of media in tweet (1/2/3/4)": "ٹویٹ میں میڈیا ترتیب (1/2/3/4)", "The post date in YYYYMMDD format": "YYYYMMDD فارمیٹ میں پوسٹ تاریخ", "The post time in HHmmss format": "HHmmss فارمیٹ میں پوسٹ وقت", "The media type (photo/video/animated_gif)": "میڈیا قسم (photo/video/animated_gif)", "The file extension of media (jpg/png/mp4)": "میڈیا فائل ایکسٹینشن (jpg/png/mp4)", "Failed to export media. Open DevTools for more details.": "میڈیا برآمد ناکام۔ مزید تفصیلات کے لیے DevTools کھولیں۔", "Failed to copy media URLs. Open DevTools for more details.": "میڈیا URL کاپی ناکام۔ مزید تفصیلات کے لیے DevTools کھولیں۔", "filter.photo": "تصویر", "filter.video": "ویڈیو", "filter.animated_gif": "GIF", "filter.retweet": "ری ٹویٹس شامل کریں" }, "common": { "Open Control Panel": "کنٹرول پینل کھولیں", "Browse around to capture more data.": "مزید ڈیٹا محفوظ کرنے کے لیے براؤز کریں۔", "Settings": "ترتیبات", "General": "عمومی", "Theme": "تھیم", "Language": "زبان", "Debug": "ڈیبگ", "Date Time Format": "تاریخ اور وقت کا فارمیٹ", "Click for more information. This will take effect on both previewer and exported files.": "مزید معلومات کے لیے کلک کریں۔ یہ پیش نظارہ اور برآمد شدہ فائلوں دونوں پر لاگو ہوگا۔", "Use dedicated DB for accounts": "اکاؤنٹس کے لیے الگ DB استعمال کریں", "This will create separate database for each Twitter account, which can help reduce the chance of data mixing when you use multiple accounts.": "یہ ہر Twitter اکاؤنٹ کے لیے الگ ڈیٹابیس بنائے گا، جس سے متعدد اکاؤنٹس استعمال کرتے وقت ڈیٹا کے مل جانے کا امکان کم ہوتا ہے۔", "Local Database": "مقامی ڈیٹابیس", "Analyze DB": "تجزیہ", "Export DB": "برآمد", "Clear DB": "صاف کریں", "Bundle Export": "بنڈل برآمد", "Bundle Library": "بنڈل لائبریری", "QC Session": "QC سیشن", "Diagnostic capture": "تشخیصی کیپچر", "Diagnostic buffers cleared.": "تشخیصی بفر صاف کر دیے گئے۔", "Clear Buffers": "بفر صاف کریں", "Preparing...": "تیاری ہو رہی ہے...", "Preparing diagnostics...": "تشخیص تیار ہو رہی ہے...", "Export QC Diagnostics": "QC تشخیص برآمد کریں", "Export Diagnostics Bundle": "تشخیصی بنڈل برآمد کریں", "QC idle.": "QC غیر فعال ہے۔", "Are you sure to clear all data in the database?": "کیا آپ واقعی ڈیٹابیس کا تمام ڈیٹا صاف کرنا چاہتے ہیں؟", "Database cleared.": "ڈیٹابیس صاف کر دی گئی۔", "Module": "ماڈیول", "Modules (Scroll to see more)": "ماڈیولز (مزید دیکھنے کے لیے اسکرول کریں)", "About": "متعلق", "Version": "ورژن", "Search...": "تلاش...", "Something went wrong.": "کچھ غلط ہو گیا۔", "Error:": "خرابی:", "Captured:": "محفوظ شدہ:", "Rows per page:": "فی صفحہ قطاریں:", "Relation Types": "رشتہ کی اقسام", "Subject Accounts": "موضوع اکاؤنٹس", "Subject User IDs": "موضوع صارف ID", "Last Observed At": "آخری مشاہدہ", "A - B of N items": "{{total}} میں سے {{from}} - {{to}} آئٹمز", "No data available.": "کوئی ڈیٹا دستیاب نہیں۔", "No media available.": "کوئی میڈیا دستیاب نہیں۔", "Clear": "صاف کریں", "Bookmark folders": "بک مارک فولڈرز", "1 folder selected": "1 فولڈر منتخب", "{{count}} folders selected": "{{count}} فولڈرز منتخب", "Export Media": "میڈیا برآمد کریں", "Export Data": "ڈیٹا برآمد کریں", "Export Search History": "تلاش کی تاریخ برآمد کریں", "JSON View": "JSON منظر", "Media View": "میڈیا منظر", "Bookmarks": "بک مارکس", "Tweets": "ٹویٹس", "Users": "صارفین", "User Tweets": "صارف کی ٹویٹس", "User Media": "صارف کا میڈیا", "Tweet Details": "ٹویٹ کی تفصیلات", "Search Timeline": "تلاش ٹائم لائن", "Home Timeline": "ہوم ٹائم لائن", "List Timeline": "لسٹ ٹائم لائن", "Community Timeline": "کمیونٹی ٹائم لائن", "Community Members": "کمیونٹی اراکین", "List Members": "لسٹ اراکین", "List Subscribers": "لسٹ سبسکرائبرز", "Direct Messages": "براہ راست پیغامات", "Interaction Events": "تعامل کے واقعات", "Local Search": "مقامی تلاش", "Runtime Logs": "رن ٹائم لاگز", "Bundle Viewer": "بنڈل ویور", "{{count}} imported bundles": "{{count}} درآمد شدہ بنڈلز", "Search indexed tweets with Twitter-style operators": "Twitter طرز کے آپریٹرز سے انڈیکس شدہ ٹویٹس تلاش کریں", "Local Recorder Search": "مقامی ریکارڈر تلاش", "Quotes": "اقتباسات", "CommunityMembers": "کمیونٹی اراکین", "CommunityTimeline": "کمیونٹی ٹائم لائن", "DirectMessages": "براہ راست پیغامات", "Followers": "فالوورز", "Following": "فالو کر رہے ہیں", "HomeTimeline": "ہوم ٹائم لائن", "Likes": "لائکس", "ListMembers": "لسٹ اراکین", "ListSubscribers": "لسٹ سبسکرائبرز", "ListTimeline": "لسٹ ٹائم لائن", "InteractionEvents": "تعامل کے واقعات", "LocalSearch": "مقامی تلاش", "RawCapture": "خام کیپچر", "Retweeters": "ری ٹویٹ کرنے والے", "RetweetersModule": "ری ٹویٹ کرنے والے", "RuntimeLogs": "رن ٹائم لاگز", "SearchTimeline": "تلاش", "TweetIndex": "ٹویٹ انڈیکس", "TweetDetail": "ٹویٹ تفصیل", "UserDetail": "صارف تفصیل", "UserMedia": "صارف میڈیا", "UserTweets": "صارف ٹویٹس" } };
  const zh_Hans = { "exporter": { "ID": "ID", "Date": "日期", "Content": "内容", "Show Full Text": "显示全文", "Media": "媒体", "Screen Name": "用户名", "Profile Name": "用户昵称", "Profile Image": "用户头像", "User ID": "用户 ID", "Replying To": "回复推文", "RT Source": "转推来源", "Quote Source": "引用来源", "Media Tags": "圈人", "Favorites": "喜欢数量", "Retweets": "转推数量", "Bookmarks": "书签数量", "Quotes": "引用数量", "Replies": "回复数量", "Views": "查看次数", "Favorited": "已喜欢", "Retweeted": "已转推", "Bookmarked": "已加书签", "Bookmark Folder": "书签文件夹", "URL": "URL", "Actions": "操作", "Details": "查看详情", "Description": "简介", "Profile Banner": "个人资料头图", "Followers": "关注者数量", "FollowingCount": "正在关注数量", "Statuses": "推文数量", "Favourites": "喜欢数量", "Listed": "被加入列表数", "Location": "位置", "Website": "网站", "Birthdate": "出生日期", "Verified Type": "认证类型", "Blue Verified": "蓝标认证", "Following": "正在关注", "Follows You": "关注你", "Can DM": "可私信", "Protected": "受保护", "Created At": "创建时间", "Sender": "发送者", "Recipient": "接收者", "Conversation ID": "对话 ID", "Conversation Type": "对话类型", "Data": "数据", "Export captured data as JSON/HTML/CSV file. This may take a while depending on the amount of data. The exported file does not include media files such as images and videos but only the URLs.": "将捕获的数据导出为 JSON/HTML/CSV 文件。这可能需要一些时间,具体取决于数据量。导出的文件不包括图片和视频等媒体文件,只包括它们的 URL。", "Data length:": "数据长度:", "Export scope:": "导出范围:", "All current results": "当前全部结果", "Selected rows": "已选择行", "Pinned result set": "已固定结果集", "Query": "查询", "Sort": "排序", "Include all metadata:": "包括所有元数据:", "Export as:": "导出为:", "No data selected.": "未选择数据。", "Cancel": "取消", "Start Export": "开始导出", "Download and save media files from captured data. This may take a while depending on the amount of data. Media that will be downloaded includes: profile images, profile banners (for users), images, videos (for tweets).": "从捕获的数据中下载并保存媒体文件。这可能需要一些时间,具体取决于数据量。将下载的媒体包括:用户的个人资料图片、个人资料头图、图片、推文中的视频。", "For more than 100 media or large files, it is recommended to copy the URLs and download them with an external download manager such as aria2.": "对于超过 100 个媒体或大文件,建议复制 URL 并使用外部下载管理器(如 aria2)下载。", "Browser ZIP export now uses bounded parallel downloads. For very large video-heavy jobs, URL or aria2 export is still the safest low-memory path.": "浏览器 ZIP 导出现在使用有界并行下载。对于非常大的视频密集任务,URL 或 aria2 导出仍是内存占用最低的安全路径。", "Filename template:": "文件名模板:", "Download scheduler": "下载调度器", "Faster defaults are intended for bulk CDN transfer.": "更快的默认值用于批量 CDN 传输。", "Start delay (ms)": "启动间隔 (ms)", "Global parallel": "全局并行", "Per host": "每主机", "Videos": "视频", "Retries": "重试", "Use aria2 format:": "使用 aria2 格式:", "Click for more information. Each URL will be on a new line, with its filename on the next line. This format is compatible with aria2.": "点击获取更多信息。每个 URL 将在新行中显示,其文件名在下一行。此格式与 aria2 兼容。", "Rate limit (ms):": "速率限制(毫秒):", "Media Filter:": "媒体过滤器:", "File Name": "文件名", "Media Type": "媒体类型", "Download URL": "下载地址", "No media selected.": "未选择媒体。", "Preview limited to first 250 media items.": "预览限制为前 250 个媒体项。", "Zipping": "正在打包", "Copied!": "已复制!", "Copy URLs": "复制 URL", "The tweet ID": "推文 ID", "The username of tweet author": "推文作者的用户名", "The profile name of tweet author": "推文作者的用户昵称", "The media index in tweet (start from 0)": "推文中的媒体索引(从 0 开始)", "The order of media in tweet (1/2/3/4)": "推文中的媒体顺序(1/2/3/4)", "The post date in YYYYMMDD format": "发布日期(YYYYMMDD 格式)", "The post time in HHmmss format": "发布时间(HHmmss 格式)", "The media type (photo/video/animated_gif)": "媒体类型(photo/video/animated_gif)", "The file extension of media (jpg/png/mp4)": "媒体文件扩展名(jpg/png/mp4)", "Failed to export media. Open DevTools for more details.": "导出媒体失败。打开 DevTools 以获取更多详细信息。", "Failed to copy media URLs. Open DevTools for more details.": "复制媒体 URL 失败。打开 DevTools 以获取更多详细信息。", "filter.photo": "图片", "filter.video": "视频", "filter.animated_gif": "GIF", "filter.retweet": "包括转推" }, "common": { "Open Control Panel": "打开控制面板", "Browse around to capture more data.": "浏览页面以捕获更多数据。", "Settings": "设置", "General": "通用", "Theme": "主题", "Language": "语言", "Debug": "调试开关", "Date Time Format": "日期时间格式", "Click for more information. This will take effect on both previewer and exported files.": "点击查看详细信息。此选项会影响预览和导出的文件。", "Use dedicated DB for accounts": "为每个账户使用专用数据库", "This will create separate database for each Twitter account, which can help reduce the chance of data mixing when you use multiple accounts.": "这将为每个 Twitter 账户创建单独的数据库,这有助于避免使用多个账户时数据混淆。", "Local Database": "本地数据库", "Analyze DB": "统计", "Export DB": "导出", "Clear DB": "清空", "Bundle Export": "导出捆绑包", "Bundle Library": "捆绑包库", "QC Session": "质检会话", "Diagnostic capture": "诊断捕获", "Diagnostic buffers cleared.": "诊断缓冲区已清除。", "Clear Buffers": "清除缓冲区", "Preparing...": "准备中...", "Preparing diagnostics...": "正在准备诊断...", "Export QC Diagnostics": "导出质检诊断", "Export Diagnostics Bundle": "导出诊断捆绑包", "QC idle.": "质检空闲。", "Are you sure to clear all data in the database?": "确定要清空数据库中的所有数据吗?", "Database cleared.": "数据库已清空。", "Module": "模块", "Modules (Scroll to see more)": "模块列表(滑动以查看完整列表)", "About": "关于", "Version": "版本", "Search...": "搜索...", "Something went wrong.": "出错了。", "Error:": "错误:", "Captured:": "已捕获:", "Rows per page:": "每页显示行数:", "Relation Types": "关系类型", "Subject Accounts": "主体账户", "Subject User IDs": "主体用户 ID", "Last Observed At": "最后观察时间", "A - B of N items": "第 {{from}} - {{to}} 项,共 {{total}} 项", "No data available.": "没有数据。", "No media available.": "没有可用媒体。", "Clear": "清除", "Bookmark folders": "书签文件夹", "1 folder selected": "已选择 1 个文件夹", "{{count}} folders selected": "已选择 {{count}} 个文件夹", "Export Media": "导出媒体文件", "Export Data": "导出数据", "Export Search History": "导出搜索历史", "JSON View": "JSON 数据预览", "Media View": "媒体预览", "Bookmarks": "书签", "Tweets": "推文", "Users": "用户", "User Tweets": "用户推文", "User Media": "用户媒体", "Tweet Details": "推文详情", "Search Timeline": "搜索时间线", "Home Timeline": "主页时间线", "List Timeline": "列表时间线", "Community Timeline": "社群时间线", "Community Members": "社群成员", "List Members": "列表成员", "List Subscribers": "列表关注者", "Direct Messages": "私信", "Interaction Events": "交互事件", "Local Search": "本地搜索", "Runtime Logs": "运行时日志", "Bundle Viewer": "捆绑包查看器", "{{count}} imported bundles": "已导入 {{count}} 个捆绑包", "Search indexed tweets with Twitter-style operators": "用 Twitter 风格运算符搜索已索引推文", "Local Recorder Search": "本地记录器搜索", "Quotes": "引用推文", "CommunityMembers": "社群成员", "CommunityTimeline": "社群时间线", "DirectMessages": "私信", "Followers": "关注者", "Following": "正在关注", "HomeTimeline": "主页时间线", "Likes": "喜欢", "ListMembers": "列表成员", "ListSubscribers": "列表关注者", "ListTimeline": "列表时间线", "InteractionEvents": "交互事件", "LocalSearch": "本地搜索", "RawCapture": "原始捕获", "Retweeters": "转推者", "RetweetersModule": "转推者", "RuntimeLogs": "运行时日志", "SearchTimeline": "搜索结果", "TweetIndex": "推文索引", "TweetDetail": "推文详情", "UserDetail": "用户详情", "UserMedia": "用户媒体", "UserTweets": "用户推文" } };
  const resources = {
    "ar": ar,
    "bn": bn,
    "en": en,
    "es": es,
    "fr": fr,
    "hi": hi,
    "id": id,
    "ja": ja,
    "pt-BR": pt_BR,
    "ru": ru,
    "ur": ur,
    "zh-Hans": zh_Hans
  };
  const DIAGNOSTIC_CAPTURE_ENABLED_STORAGE_KEY = "twe_diagnostic_capture_enabled_v1";
  const DIAGNOSTIC_PARSER_BUFFER_KEY = "__twe_diagnostic_parser_events_v1";
  const DIAGNOSTIC_DB_BUFFER_KEY = "__twe_diagnostic_db_events_v1";
  const DIAGNOSTIC_INTERACTION_BUFFER_KEY = "__twe_diagnostic_interaction_events_v1";
  const DIAGNOSTIC_EVENT_NAME = "twe:diagnostic-event-v1";
  const DIAGNOSTIC_BUFFER_LIMIT = 500;
  function getWindowRecord$1() {
    return globalThis;
  }
  function readStorageFlag(key, fallback = false) {
    try {
      if (typeof localStorage === "undefined") return fallback;
      const raw = localStorage.getItem(key);
      if (raw === null) return fallback;
      return raw === "1" || raw === "true";
    } catch {
      return fallback;
    }
  }
  function writeStorageFlag(key, value) {
    try {
      if (typeof localStorage === "undefined") return;
      localStorage.setItem(key, value ? "1" : "0");
    } catch {
    }
  }
  function pushBufferedEvent(key, event) {
    const g = getWindowRecord$1();
    const current = Array.isArray(g[key]) ? g[key] : [];
    current.push(event);
    if (current.length > DIAGNOSTIC_BUFFER_LIMIT) {
      current.splice(0, current.length - DIAGNOSTIC_BUFFER_LIMIT);
    }
    g[key] = current;
    if (typeof window !== "undefined" && typeof window.dispatchEvent === "function") {
      try {
        window.dispatchEvent(new CustomEvent(DIAGNOSTIC_EVENT_NAME, { detail: { key } }));
      } catch {
      }
    }
  }
  function isDiagnosticCaptureEnabled() {
    const g = getWindowRecord$1();
    const globalFlag = g.__twe_diagnostic_capture_enabled_v1;
    if (typeof globalFlag === "boolean") {
      return globalFlag;
    }
    const enabled = readStorageFlag(DIAGNOSTIC_CAPTURE_ENABLED_STORAGE_KEY, false);
    g.__twe_diagnostic_capture_enabled_v1 = enabled;
    return enabled;
  }
  function setDiagnosticCaptureEnabled(value) {
    const g = getWindowRecord$1();
    g.__twe_diagnostic_capture_enabled_v1 = value;
    writeStorageFlag(DIAGNOSTIC_CAPTURE_ENABLED_STORAGE_KEY, value);
    if (typeof window !== "undefined" && typeof window.dispatchEvent === "function") {
      try {
        window.dispatchEvent(
          new CustomEvent(DIAGNOSTIC_EVENT_NAME, {
            detail: { key: DIAGNOSTIC_CAPTURE_ENABLED_STORAGE_KEY, enabled: value }
          })
        );
      } catch {
      }
    }
  }
  function recordDiagnosticParserEvent(event) {
    if (!isDiagnosticCaptureEnabled()) return;
    pushBufferedEvent(DIAGNOSTIC_PARSER_BUFFER_KEY, event);
  }
  function recordDiagnosticDbEvent(event) {
    if (!isDiagnosticCaptureEnabled()) return;
    pushBufferedEvent(DIAGNOSTIC_DB_BUFFER_KEY, event);
  }
  function recordDiagnosticInteractionEvent(event) {
    if (!isDiagnosticCaptureEnabled()) return;
    pushBufferedEvent(DIAGNOSTIC_INTERACTION_BUFFER_KEY, event);
  }
  function readDiagnosticBuffers() {
    const g = getWindowRecord$1();
    return {
      parser: Array.isArray(g[DIAGNOSTIC_PARSER_BUFFER_KEY]) ? [
        ...g[DIAGNOSTIC_PARSER_BUFFER_KEY]
      ] : [],
      db: Array.isArray(g[DIAGNOSTIC_DB_BUFFER_KEY]) ? [...g[DIAGNOSTIC_DB_BUFFER_KEY]] : [],
      interaction: Array.isArray(g[DIAGNOSTIC_INTERACTION_BUFFER_KEY]) ? [
        ...g[DIAGNOSTIC_INTERACTION_BUFFER_KEY]
      ] : []
    };
  }
  function clearDiagnosticBuffers() {
    const g = getWindowRecord$1();
    g[DIAGNOSTIC_PARSER_BUFFER_KEY] = [];
    g[DIAGNOSTIC_DB_BUFFER_KEY] = [];
    g[DIAGNOSTIC_INTERACTION_BUFFER_KEY] = [];
  }
  const diagnosticKeys = {
    eventName: DIAGNOSTIC_EVENT_NAME
  };
  const logLinesSignal = signals.signal([]);
  const MAX_LOG_LINES_DEFAULT = 200;
  const MAX_LOG_LINES_DIAGNOSTIC = 400;
  const CONSOLE_INFO_STORAGE_KEY = "twe_console_info_v1";
  const CONSOLE_VERBOSE_STORAGE_KEY = "twe_console_verbose_v1";
  function isTruthy(value) {
    if (!value) return false;
    const normalized = value.trim().toLowerCase();
    return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on";
  }
  function shouldPrintInfoToConsole() {
    try {
      if (isTruthy(localStorage.getItem(CONSOLE_VERBOSE_STORAGE_KEY))) {
        return true;
      }
    } catch {
    }
    try {
      if (isTruthy(localStorage.getItem(CONSOLE_INFO_STORAGE_KEY))) {
        return true;
      }
    } catch {
    }
    return false;
  }
  function shouldPrintDebugToConsole() {
    try {
      return isTruthy(localStorage.getItem(CONSOLE_VERBOSE_STORAGE_KEY));
    } catch {
      return false;
    }
  }
  function getMaxLogLines() {
    if (isDiagnosticCaptureEnabled() || shouldPrintDebugToConsole()) {
      return MAX_LOG_LINES_DIAGNOSTIC;
    }
    return MAX_LOG_LINES_DEFAULT;
  }
  class Logger {
    constructor() {
      __publicField(this, "index", 0);
      __publicField(this, "buffer", []);
      __publicField(this, "bufferTimer", null);
    }
    info(line, ...args) {
      if (shouldPrintInfoToConsole()) {
        console.info("[twitter-web-exporter]", line, ...args);
      }
      this.writeBuffer({ type: "info", line, index: this.index++ });
    }
    warn(line, ...args) {
      console.warn("[twitter-web-exporter]", line, ...args);
      this.writeBuffer({ type: "warn", line, index: this.index++ });
    }
    error(line, ...args) {
      console.error("[twitter-web-exporter]", line, ...args);
      this.writeBuffer({ type: "error", line, index: this.index++ });
    }
    errorWithBanner(msg, err2, ...args) {
      this.error(
        `${msg} (Message: ${(err2 == null ? void 0 : err2.message) ?? "none"})
  This may be a problem caused by Twitter updates.
  Please file an issue on GitHub:
  https://github.com/kmccleary3301/scrollmark/issues`,
        ...args
      );
    }
    debug(...args) {
      if (shouldPrintDebugToConsole()) {
        console.debug("[twitter-web-exporter]", ...args);
      }
    }
    /**
     * Buffer log lines to reduce the number of signal and DOM updates.
     */
    writeBuffer(log) {
      this.buffer.push(log);
      if (this.bufferTimer) {
        clearTimeout(this.bufferTimer);
      }
      this.bufferTimer = window.setTimeout(() => {
        this.bufferTimer = null;
        this.flushBuffer();
      }, 0);
    }
    /**
     * Flush buffered log lines and update the UI.
     */
    flushBuffer() {
      const next = [...logLinesSignal.value, ...this.buffer];
      const limit = getMaxLogLines();
      logLinesSignal.value = next.length > limit ? next.slice(next.length - limit) : next;
      this.buffer = [];
    }
  }
  const logger = new Logger();
  function safeJSONParse(text) {
    try {
      return JSON.parse(text);
    } catch (e) {
      logger.error(e.message);
      return null;
    }
  }
  function useSignalState(value) {
    const signal2 = signals.useSignal(value);
    const updateSignal = (newValue) => {
      signal2.value = newValue;
    };
    return [signal2.value, updateSignal, signal2];
  }
  function useToggle(defaultValue = false) {
    const signal2 = signals.useSignal(defaultValue);
    const toggle = () => {
      signal2.value = !signal2.value;
    };
    return [signal2.value, toggle, signal2];
  }
  function cx(...classNames) {
    return classNames.filter(Boolean).join(" ");
  }
  function isEqual(obj1, obj2) {
    return JSON.stringify(obj1) === JSON.stringify(obj2);
  }
  function capitalizeFirstLetter(str) {
    return str.charAt(0).toUpperCase() + str.slice(1);
  }
  function escapeHTML(str) {
    return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
  }
  function normalizeSafeLinkURL(value) {
    if (!value) {
      return null;
    }
    try {
      const parsed = new URL(value);
      if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
        return null;
      }
      return parsed.href;
    } catch {
      return null;
    }
  }
  function strEntitiesToHTML(str, urls) {
    if (!(urls == null ? void 0 : urls.length)) {
      return escapeHTML(str);
    }
    let cursor = 0;
    let html = "";
    const entities = [...urls].filter((entity) => Array.isArray(entity.indices) && entity.indices.length >= 2).map((entity) => ({
      ...entity,
      start: Math.max(0, Number(entity.indices[0]) || 0),
      end: Math.max(0, Number(entity.indices[1]) || 0)
    })).filter((entity) => entity.end > entity.start && entity.start < str.length).sort((a, b) => a.start - b.start);
    for (const entity of entities) {
      const start = Math.max(cursor, entity.start);
      const end = Math.min(str.length, entity.end);
      if (start > cursor) {
        html += escapeHTML(str.slice(cursor, start));
      }
      const originalText = str.slice(start, end);
      const href = normalizeSafeLinkURL(entity.expanded_url ?? entity.url);
      if (href) {
        html += `<a class="link" target="_blank" rel="noopener noreferrer" href="${escapeHTML(
        href
      )}">${escapeHTML(entity.display_url ?? originalText)}</a>`;
      } else {
        html += escapeHTML(originalText);
      }
      cursor = end;
    }
    if (cursor < str.length) {
      html += escapeHTML(str.slice(cursor));
    }
    return html;
  }
  function parseTwitterDateTime(str) {
    if (!str) {
      return dayjs(0);
    }
    const trimmed = str.replace(/^\w+ (.*)$/, "$1");
    return dayjs(trimmed, "MMM DD HH:mm:ss ZZ YYYY", "en");
  }
  function formatDateTime(date, format) {
    if (typeof date === "number" || typeof date === "string") {
      date = dayjs(date);
    }
    return date.format(format);
  }
  function formatTwitterBirthdate(arg) {
    if (!arg) {
      return null;
    }
    const { day, month, year } = arg;
    const date = dayjs().set("year", year ?? 0).set("month", month - 1).set("date", day);
    return year ? date.format("MMM DD, YYYY") : date.format("MMM DD");
  }
  function formatVideoDuration(durationInMs) {
    if (typeof durationInMs !== "number" || Number.isNaN(durationInMs)) {
      return "N/A";
    }
    const durationInSeconds = Math.floor(durationInMs / 1e3);
    const minutes = Math.floor(durationInSeconds / 60);
    const seconds = durationInSeconds % 60;
    return `${minutes}:${seconds.toString().padStart(2, "0")}`;
  }
  const name = "scrollmark";
  const description = "Scrollmark: local-first X/Twitter research archive, search, and portable bundle export.";
  const version = "1.0.0";
  const author = "Kyle McCleary";
  const license = "MIT";
  const homepage = "https://github.com/kmccleary3301/scrollmark";
  const bugs = "https://github.com/kmccleary3301/scrollmark/issues";
  const type = "module";
  const scripts = { "clean": "rm -rf dist", "dev": "vite", "build": "tsc && vite build", "build:store": "tsc && SCROLLMARK_BUILD_TARGET=store vite build", "build:e2e": "tsc && TWE_BUILD_VARIANT=e2e vite build", "build:chrome-e2e": "tsc && TWE_BUILD_VARIANT=chrome-e2e vite build", "sync:store-artifact": "mkdir -p store && cp dist/scrollmark.store.user.js store/scrollmark.user.js", "build:all": "npm run clean && npm run build && npm run build:store && npm run sync:store-artifact && npm run build:e2e && npm run build:chrome-e2e", "prepare": "husky", "lint": "eslint .", "check:metadata": "node scripts/check-userscript-metadata.mjs", "commitlint": "commitlint --edit", "changelog": "git-cliff -o CHANGELOG.md", "preview": "vite preview" };
  const dependencies = { "@preact/signals": "2.0.0", "@preact/signals-core": "1.8.0", "@tabler/icons-preact": "3.31.0", "@tanstack/table-core": "8.21.2", "dayjs": "1.11.13", "dexie": "4.0.11", "dexie-export-import": "4.1.4", "fflate": "^0.8.2", "file-saver-es": "2.0.5", "i18next": "24.2.3", "preact": "10.26.4" };
  const devDependencies = { "@commitlint/cli": "^19.8.0", "@commitlint/config-conventional": "^19.8.0", "@eslint/js": "^9.23.0", "@preact/preset-vite": "^2.10.1", "@types/file-saver-es": "^2.0.3", "@types/node": "^22.14.0", "autoprefixer": "^10.4.21", "daisyui": "^4", "eslint": "^9.23.0", "eslint-config-prettier": "^10.1.1", "eslint-plugin-prettier": "^5.2.6", "git-cliff": "^2.8.0", "husky": "^9.1.7", "playwright": "^1.60.0", "postcss": "^8.5.3", "postcss-prefix-selector": "^2.1.1", "postcss-rem-to-pixel-next": "^5.0.3", "tailwindcss": "^3", "tsx": "^4.22.2", "typescript": "^5.8.2", "typescript-eslint": "^8.29.0", "vite": "^6.2.5", "vite-plugin-i18next-loader": "^3.1.2", "vite-plugin-monkey": "^5.0.8" };
  const packageJson = {
    name,
    description,
    version,
    author,
    license,
    homepage,
    bugs,
    "private": true,
    type,
    scripts,
    dependencies,
    devDependencies
  };
  const DEFAULT_APP_OPTIONS = {
    theme: "system",
    debug: false,
    showControlPanel: true,
    disabledExtensions: ["HomeTimelineModule"],
    dateTimeFormat: "YYYY-MM-DD HH:mm:ss Z",
    filenamePattern: "{screen_name}_{id}_{type}_{num}_{date}.{ext}",
    language: "",
    dedicatedDbForAccounts: false,
    safeMode: false,
    hookMode: "both",
    repairMode: "watchdog",
    directMessagesCaptureEnabled: false,
    rawCaptureEncryptedStorageReady: false,
    rawCapturePolicyPublicEnabled: true,
    rawCapturePolicySensitiveEnabled: true,
    rawCapturePolicyDmEnabled: true,
    rawCaptureEnabled: true,
    rawCaptureStreamEnabled: false,
    rawCaptureDaemonUrl: "http://127.0.0.1:8754",
    version: packageJson.version
  };
  const THEMES = [
    "system",
    "cupcake",
    "dark",
    "emerald",
    "cyberpunk",
    "valentine",
    "lofi",
    "dracula",
    "cmyk",
    "business",
    "winter"
  ];
  const LOCAL_STORAGE_KEY = packageJson.name;
  const DISABLED_EXTENSIONS_NO_LONGER_DEFAULT = /* @__PURE__ */ new Set([
    "RetweetersModule",
    "ListTimelineModule",
    "ListSubscribersModule",
    "ListMembersModule",
    "CommunityMembersModule",
    "CommunityTimelineModule"
  ]);
  function normalizeDisabledExtensions(value) {
    const current = Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
    const next = current.filter((item) => !DISABLED_EXTENSIONS_NO_LONGER_DEFAULT.has(item));
    return Array.from(new Set(next));
  }
  class AppOptionsManager {
    constructor() {
      __publicField(this, "appOptions", { ...DEFAULT_APP_OPTIONS });
      __publicField(this, "previous", { ...DEFAULT_APP_OPTIONS });
      /**
       * Signal for subscribing to option changes.
       */
      __publicField(this, "signal", new signals.Signal(0));
      this.loadAppOptions();
    }
    get(key, defaultValue) {
      return this.appOptions[key] ?? defaultValue;
    }
    set(key, value) {
      this.appOptions[key] = value;
      this.saveAppOptions();
    }
    /**
     * Read app options from local storage.
     */
    loadAppOptions() {
      this.appOptions = {
        ...this.appOptions,
        ...safeJSONParse(localStorage.getItem(LOCAL_STORAGE_KEY) || "{}")
      };
      this.appOptions.disabledExtensions = normalizeDisabledExtensions(
        this.appOptions.disabledExtensions
      );
      const oldVersion = this.appOptions.version ?? "";
      const newVersion = DEFAULT_APP_OPTIONS.version;
      if (newVersion.startsWith("1.1") && oldVersion.startsWith("1.0")) {
        this.appOptions.disabledExtensions = [
          ...normalizeDisabledExtensions(this.appOptions.disabledExtensions),
          "HomeTimelineModule",
          "ListTimelineModule"
        ];
        logger.info(`App options migrated from v${oldVersion} to v${newVersion}`);
        setTimeout(() => this.saveAppOptions(), 0);
      }
      this.previous = { ...this.appOptions };
      logger.info("App options loaded", this.appOptions);
      this.signal.value++;
    }
    /**
     * Write app options to local storage.
     */
    saveAppOptions() {
      const oldValue = this.previous;
      const newValue = {
        ...this.appOptions,
        version: packageJson.version
      };
      if (isEqual(oldValue, newValue)) {
        return;
      }
      this.appOptions = newValue;
      localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(this.appOptions));
      this.previous = { ...this.appOptions };
      logger.debug("App options saved", this.appOptions);
      this.signal.value++;
    }
  }
  const appOptionsManager = new AppOptionsManager();
  const LANGUAGES_CONFIG = {
    en: {
      name: "English",
      nameEn: "English",
      test: (code) => /^en/.test(code)
    },
    "zh-Hans": {
      name: "简体中文",
      nameEn: "Simplified Chinese",
      test: (code) => /^zh/.test(code)
    },
    es: {
      name: "Español",
      nameEn: "Spanish",
      test: (code) => /^es/.test(code)
    },
    hi: {
      name: "हिन्दी",
      nameEn: "Hindi",
      test: (code) => /^hi/.test(code)
    },
    fr: {
      name: "Français",
      nameEn: "French",
      test: (code) => /^fr/.test(code)
    },
    ar: {
      name: "العربية",
      nameEn: "Arabic",
      test: (code) => /^ar/.test(code)
    },
    bn: {
      name: "বাংলা",
      nameEn: "Bengali",
      test: (code) => /^bn/.test(code)
    },
    "pt-BR": {
      name: "Português (Brasil)",
      nameEn: "Portuguese",
      test: (code) => /^pt/.test(code)
    },
    ru: {
      name: "Русский",
      nameEn: "Russian",
      test: (code) => /^ru/.test(code)
    },
    ur: {
      name: "اردو",
      nameEn: "Urdu",
      test: (code) => /^ur/.test(code)
    },
    id: {
      name: "Bahasa Indonesia",
      nameEn: "Indonesian",
      test: (code) => /^id/.test(code)
    },
    ja: {
      name: "日本語",
      nameEn: "Japanese",
      test: (code) => /^ja/.test(code)
    }
  };
  function detectBrowserLanguage() {
    const language = window.navigator.language || "en";
    for (const [langTag, langConf] of Object.entries(LANGUAGES_CONFIG)) {
      if (langConf.test(language)) {
        return langTag;
      }
    }
    return language;
  }
  const languageDetector = {
    type: "languageDetector",
    detect: function() {
      return appOptionsManager.get("language") || detectBrowserLanguage();
    }
  };
  const I18N_DEBUG_STORAGE_KEY = "twe_i18n_debug_v1";
  function isTruthyStorageValue$1(value) {
    if (!value) return false;
    const normalized = value.trim().toLowerCase();
    return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on";
  }
  function shouldEnableI18nDebug() {
    try {
      return isTruthyStorageValue$1(localStorage.getItem(I18N_DEBUG_STORAGE_KEY));
    } catch {
      return false;
    }
  }
  function initI18n() {
    if (i18next.isInitialized) {
      return i18next;
    }
    i18next.on("languageChanged", (lng) => {
      if (!appOptionsManager.get("language")) {
        appOptionsManager.set("language", lng);
      }
    });
    i18next.use(languageDetector).init({
      initImmediate: true,
      defaultNS: "common",
      fallbackLng: "en",
      nsSeparator: "::",
      // Keep console quiet by default; opt-in via localStorage key.
      debug: shouldEnableI18nDebug(),
      resources
    });
    return i18next;
  }
  function useTranslation(ns) {
    const i18n = initI18n();
    const [t, setT] = hooks.useState(() => i18n.getFixedT(null, ns ?? null));
    const isMountedRef = hooks.useRef(true);
    const previousNamespaceRef = hooks.useRef(ns);
    hooks.useEffect(() => {
      isMountedRef.current = true;
      if (previousNamespaceRef.current !== ns) {
        previousNamespaceRef.current = ns;
        setT(() => i18n.getFixedT(null, ns ?? null));
      }
      function boundReset() {
        if (isMountedRef.current) {
          setT(() => i18n.getFixedT(null, ns ?? null));
        }
      }
      i18n.on("languageChanged", boundReset);
      return () => {
        isMountedRef.current = false;
        i18n.off("languageChanged", boundReset);
      };
    }, [ns]);
    return { t, i18n };
  }
  function Trans({ i18nKey, ns = "exporter" }) {
    const { t } = useTranslation(ns);
    return /* @__PURE__ */ u("span", { children: t(i18nKey) });
  }
  class ErrorBoundary extends preact.Component {
    constructor() {
      super(...arguments);
      __publicField(this, "state", { error: null });
    }
    static getDerivedStateFromError(err2) {
      return { error: err2.message };
    }
    componentDidCatch(err2) {
      logger.error(err2.message, err2);
      this.setState({ error: err2.message });
    }
    render() {
      if (this.state.error) {
        return /* @__PURE__ */ u("div", { class: "alert alert-error p-2", children: [
          /* @__PURE__ */ u(IconExclamationCircle, {}),
          /* @__PURE__ */ u("div", { children: [
            /* @__PURE__ */ u("h3", { class: "font-bold leading-normal", children: /* @__PURE__ */ u(Trans, { ns: "common", i18nKey: "Something went wrong." }) }),
            /* @__PURE__ */ u("p", { class: "text-xs", children: [
              /* @__PURE__ */ u(Trans, { ns: "common", i18nKey: "Error:" }),
              " ",
              this.state.error
            ] })
          ] })
        ] });
      }
      return this.props.children;
    }
  }
  var _GM_registerMenuCommand = /* @__PURE__ */ (() => typeof GM_registerMenuCommand != "undefined" ? GM_registerMenuCommand : void 0)();
  var _unsafeWindow = /* @__PURE__ */ (() => typeof unsafeWindow != "undefined" ? unsafeWindow : void 0)();
  class ExtensionInterceptorDispatcher {
    dispatch(extensions, req, res) {
      extensions.filter((ext) => ext.enabled).forEach((ext) => {
        try {
          const func = ext.intercept();
          if (!func) {
            return;
          }
          const startedAt = Date.now();
          recordDiagnosticParserEvent({
            ts: startedAt,
            extension: ext.name,
            phase: "claimed",
            request_id: req.requestId,
            method: req.method,
            url: req.url,
            status: res.status
          });
          func(req, res, ext);
          recordDiagnosticParserEvent({
            ts: Date.now(),
            extension: ext.name,
            phase: "completed",
            request_id: req.requestId,
            method: req.method,
            url: req.url,
            status: res.status,
            duration_ms: Math.max(0, Date.now() - startedAt)
          });
        } catch (err2) {
          recordDiagnosticParserEvent({
            ts: Date.now(),
            extension: ext.name,
            phase: "error",
            request_id: req.requestId,
            method: req.method,
            url: req.url,
            status: res.status,
            error: err2 instanceof Error ? err2.message : String(err2)
          });
          logger.error(`Interceptor error (${ext.name}):`, err2);
        }
      });
    }
  }
  const LOCAL_STORAGE_SAFE_MODE_KEY = "twe_safe_mode_v1";
  const LOCAL_STORAGE_HOOK_MODE_KEY = "twe_hook_mode_v1";
  const LOCAL_STORAGE_REPAIR_MODE_KEY = "twe_repair_mode_v1";
  const DEBUG_DISABLE_XHR_OPEN_WRAP_KEY = "twe_debug_disable_xhr_open_wrap_v1";
  const DEBUG_DISABLE_XHR_SEND_WRAP_KEY = "twe_debug_disable_xhr_send_wrap_v1";
  const DEBUG_DISABLE_XHR_LOAD_LISTENER_KEY = "twe_debug_disable_xhr_load_listener_v1";
  const DEBUG_DISABLE_FETCH_WRAP_KEY = "twe_debug_disable_fetch_wrap_v1";
  const DEBUG_DISABLE_EXPANDO_META_KEY = "twe_debug_disable_expando_meta_v1";
  const DEBUG_FORCE_CALL_NOT_APPLY_KEY = "twe_debug_force_call_not_apply_v1";
  const DEBUG_HOOK_DIAG_KEY = "twe_debug_hook_diag_v1";
  const DEBUG_CONSOLE_VERBOSE_KEY = "twe_console_verbose_v1";
  const DEBUG_HOOK_DIAG_MAX_PER_PHASE = 6;
  const HOOK_REPAIR_INTERVAL_MS = 1100;
  const HOOK_REPAIR_BACKOFF_MAX_MS = 6e4;
  const HOOK_REPAIR_FAILURE_LIMIT = 5;
  function normalizeHookMode(value) {
    return value === "xhr" || value === "fetch" || value === "off" ? value : "both";
  }
  function normalizeRepairMode(value) {
    return value === "off" ? "off" : "watchdog";
  }
  function readLocalStorageValue(key) {
    try {
      if (typeof localStorage === "undefined") return null;
      return localStorage.getItem(key);
    } catch {
      return null;
    }
  }
  function writeLocalStorageValue(key, value) {
    try {
      if (typeof localStorage === "undefined") return;
      localStorage.setItem(key, value);
    } catch {
    }
  }
  function isTruthyStorageValue(value) {
    if (!value) return false;
    const normalized = value.trim().toLowerCase();
    return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on";
  }
  function resolveHookDebugConfig() {
    const hookDiagRequested = isTruthyStorageValue(readLocalStorageValue(DEBUG_HOOK_DIAG_KEY));
    const consoleVerbose = isTruthyStorageValue(readLocalStorageValue(DEBUG_CONSOLE_VERBOSE_KEY));
    return {
      disableXhrOpenWrap: isTruthyStorageValue(
        readLocalStorageValue(DEBUG_DISABLE_XHR_OPEN_WRAP_KEY)
      ),
      disableXhrSendWrap: isTruthyStorageValue(
        readLocalStorageValue(DEBUG_DISABLE_XHR_SEND_WRAP_KEY)
      ),
      disableXhrLoadListener: isTruthyStorageValue(
        readLocalStorageValue(DEBUG_DISABLE_XHR_LOAD_LISTENER_KEY)
      ),
      disableFetchWrap: isTruthyStorageValue(readLocalStorageValue(DEBUG_DISABLE_FETCH_WRAP_KEY)),
      disableExpandoMeta: isTruthyStorageValue(readLocalStorageValue(DEBUG_DISABLE_EXPANDO_META_KEY)),
      forceCallNotApply: isTruthyStorageValue(readLocalStorageValue(DEBUG_FORCE_CALL_NOT_APPLY_KEY)),
      hookDiag: hookDiagRequested && consoleVerbose
    };
  }
  function resolveRuntimeModes() {
    const optionSafeMode = !!appOptionsManager.get("safeMode", false);
    const optionHookMode = normalizeHookMode(appOptionsManager.get("hookMode", "both"));
    const optionRepairMode = normalizeRepairMode(appOptionsManager.get("repairMode", "watchdog"));
    return {
      safeMode: optionSafeMode,
      hookMode: optionHookMode,
      repairMode: optionRepairMode
    };
  }
  class RuntimeControlPlane {
    constructor(host) {
      __publicField(this, "runtimeModes", resolveRuntimeModes());
      __publicField(this, "hookDebugConfig", resolveHookDebugConfig());
      __publicField(this, "hookDiagCounters", /* @__PURE__ */ Object.create(null));
      __publicField(this, "runtimeModeReason", "");
      __publicField(this, "hookRepairInterval", null);
      __publicField(this, "hookBootProbeTimeout", null);
      __publicField(this, "hookRepairBackoffMs", HOOK_REPAIR_INTERVAL_MS);
      __publicField(this, "hookRepairFailures", 0);
      this.host = host;
    }
    initialize() {
      this.refreshRuntimeModes();
      if (this.runtimeModes.hookMode !== "off") {
        try {
          this.host.installPageMessageBridge();
        } catch (err2) {
          this.enableSafeMode("install-page-message-bridge-failed", err2);
        }
      }
      if (!this.runtimeModes.safeMode && this.runtimeModes.hookMode !== "off") {
        try {
          this.host.installBookmarkContextTracking();
        } catch (err2) {
          const details = err2 instanceof Error ? `${err2.name}: ${err2.message}` : `unknown error: ${String(err2)}`;
          logger.warn(
            `Bookmark context tracking install failed; continuing without tracker (${details})`
          );
        }
      }
      try {
        if (this.isHookModeEnabled("xhr")) {
          this.host.installHttpHooks();
        }
        if (this.isHookModeEnabled("fetch")) {
          this.host.installFetchHooks();
        }
        const hookSelfTest = this.host.runHookSelfTest();
        if (!hookSelfTest.ok) {
          this.enableSafeMode("hook-self-test-failed", hookSelfTest.error);
        } else if (!this.runtimeModes.safeMode && this.runtimeModes.repairMode !== "off") {
          this.startHookRepairLoop();
        }
        if (!this.runtimeModes.safeMode) {
          this.startFetchHookBootProbe();
        }
      } catch (err2) {
        this.enableSafeMode("hook-install-failed", err2);
      }
    }
    dispose() {
      this.clearHookRepairLoop();
      this.clearFetchHookBootProbe();
      this.host.uninstallHooks();
    }
    applyRuntimeModesFromOptions() {
      const previous = { ...this.runtimeModes };
      this.refreshRuntimeModes();
      const next = this.runtimeModes;
      if (previous.safeMode === next.safeMode && previous.hookMode === next.hookMode && previous.repairMode === next.repairMode) {
        return;
      }
      if (!next.safeMode) {
        this.runtimeModeReason = "";
      }
      if (next.hookMode === "off") {
        this.host.uninstallHooks();
        this.clearHookRepairLoop();
        this.clearFetchHookBootProbe();
        return;
      }
      if (next.safeMode) {
        this.host.uninstallHooks();
        this.clearHookRepairLoop();
        this.clearFetchHookBootProbe();
        try {
          this.host.installPageMessageBridge();
          if (this.isHookModeEnabled("xhr")) {
            this.host.installHttpHooks(true);
          }
          if (this.isHookModeEnabled("fetch")) {
            this.host.installFetchHooks(true);
          }
        } catch (err2) {
          logger.warn("Failed to reconfigure lightweight safe mode hooks", err2 ?? "");
        }
        return;
      }
      try {
        this.host.installPageMessageBridge();
        this.host.installBookmarkContextTracking();
        if (this.isHookModeEnabled("xhr")) {
          this.host.installHttpHooks(true);
        }
        if (this.isHookModeEnabled("fetch")) {
          this.host.installFetchHooks(true);
        }
        const hookSelfTest = this.host.runHookSelfTest();
        if (!hookSelfTest.ok) {
          this.runtimeModeReason = "hook-self-test-failed";
          logger.warn("Hook self-test failed after runtime reconfigure", hookSelfTest.error ?? "");
          this.publishRuntimeModes({
            enabledAt: Date.now(),
            error: hookSelfTest.error ? String(hookSelfTest.error) : void 0
          });
          return;
        }
        if (this.runtimeModes.repairMode !== "off") {
          this.startHookRepairLoop();
        } else {
          this.clearHookRepairLoop();
        }
        this.startFetchHookBootProbe();
      } catch (err2) {
        this.enableSafeMode("hook-reconfigure-failed", err2);
      }
    }
    getRuntimeModesSnapshot() {
      return {
        safeMode: this.runtimeModes.safeMode,
        hookMode: this.runtimeModes.hookMode,
        repairMode: this.runtimeModes.repairMode,
        reason: this.runtimeModeReason || void 0
      };
    }
    getHookStatsSnapshot() {
      return this.host.readHookStatsSnapshot();
    }
    getRuntimeModes() {
      return this.runtimeModes;
    }
    getHookDebugConfig() {
      return this.hookDebugConfig;
    }
    refreshHookDebugConfig() {
      this.hookDebugConfig = resolveHookDebugConfig();
    }
    isHookModeEnabled(target) {
      if (this.runtimeModes.hookMode === "off") {
        return false;
      }
      if (this.runtimeModes.hookMode === "both") {
        return true;
      }
      return this.runtimeModes.hookMode === target;
    }
    emitHookDiag(phase, payload, options2) {
      const force = !!(options2 == null ? void 0 : options2.force);
      if (!this.hookDebugConfig.hookDiag) return;
      const count = (this.hookDiagCounters[phase] ?? 0) + 1;
      this.hookDiagCounters[phase] = count;
      if (!force && count > DEBUG_HOOK_DIAG_MAX_PER_PHASE) {
        return;
      }
      logger.info(`TWE_DIAG ${phase}`, {
        ...payload,
        count,
        safeMode: this.runtimeModes.safeMode,
        hookMode: this.runtimeModes.hookMode,
        repairMode: this.runtimeModes.repairMode,
        forceCallNotApply: this.hookDebugConfig.forceCallNotApply,
        disableExpandoMeta: this.hookDebugConfig.disableExpandoMeta,
        ts: Date.now()
      });
    }
    enableSafeMode(reason, error) {
      this.clearFetchHookBootProbe();
      this.runtimeModes.safeMode = true;
      this.runtimeModeReason = reason;
      this.persistRuntimeModes();
      this.publishRuntimeModes({
        enabledAt: Date.now(),
        error: error ? String(error) : void 0
      });
      this.host.uninstallHooks();
      this.clearHookRepairLoop();
      logger.error(`Hook safe mode enabled (${reason})`, error ?? "");
    }
    startFetchHookBootProbe(delayMs = 1200) {
      if (this.runtimeModes.safeMode || !this.isHookModeEnabled("fetch")) {
        return;
      }
      this.clearFetchHookBootProbe();
      this.hookBootProbeTimeout = setTimeout(() => {
        this.hookBootProbeTimeout = null;
        if (this.runtimeModes.safeMode || !this.isHookModeEnabled("fetch")) {
          return;
        }
        this.host.runFetchHookBootProbePass();
      }, delayMs);
    }
    startHookRepairLoop() {
      if (this.runtimeModes.safeMode || this.runtimeModes.repairMode === "off") {
        return;
      }
      if (this.hookRepairInterval !== null) {
        return;
      }
      const schedule = (delayMs) => {
        this.hookRepairInterval = setTimeout(() => {
          this.hookRepairInterval = null;
          repair();
        }, delayMs);
      };
      const repair = () => {
        if (this.runtimeModes.safeMode || this.runtimeModes.repairMode === "off") {
          return;
        }
        try {
          const result = this.host.runHookRepairPass();
          if (!result.ok) {
            this.enableSafeMode("hook-repair-self-test-failed", result.error);
            return;
          }
          this.hookRepairFailures = 0;
          this.hookRepairBackoffMs = HOOK_REPAIR_INTERVAL_MS;
        } catch (err2) {
          this.hookRepairFailures += 1;
          this.hookRepairBackoffMs = Math.min(
            this.hookRepairBackoffMs * 2,
            HOOK_REPAIR_BACKOFF_MAX_MS
          );
          logger.warn(
            `Hook repair failed (${this.hookRepairFailures}/${HOOK_REPAIR_FAILURE_LIMIT})`,
            err2
          );
          if (this.hookRepairFailures >= HOOK_REPAIR_FAILURE_LIMIT) {
            this.enableSafeMode("hook-repair-failure-limit", err2);
            return;
          }
        }
        schedule(this.hookRepairBackoffMs);
      };
      schedule(0);
    }
    clearHookRepairLoop() {
      if (this.hookRepairInterval !== null) {
        clearTimeout(this.hookRepairInterval);
        this.hookRepairInterval = null;
      }
    }
    clearFetchHookBootProbe() {
      if (this.hookBootProbeTimeout !== null) {
        clearTimeout(this.hookBootProbeTimeout);
        this.hookBootProbeTimeout = null;
      }
    }
    persistRuntimeModes() {
      writeLocalStorageValue(LOCAL_STORAGE_SAFE_MODE_KEY, this.runtimeModes.safeMode ? "1" : "0");
      writeLocalStorageValue(LOCAL_STORAGE_HOOK_MODE_KEY, this.runtimeModes.hookMode);
      writeLocalStorageValue(LOCAL_STORAGE_REPAIR_MODE_KEY, this.runtimeModes.repairMode);
      try {
        appOptionsManager.set("safeMode", this.runtimeModes.safeMode);
        appOptionsManager.set("hookMode", this.runtimeModes.hookMode);
        appOptionsManager.set("repairMode", this.runtimeModes.repairMode);
      } catch {
      }
    }
    publishRuntimeModes(extra) {
    }
    refreshRuntimeModes() {
      this.runtimeModes = resolveRuntimeModes();
      this.refreshHookDebugConfig();
      this.persistRuntimeModes();
      this.publishRuntimeModes();
    }
  }
  function getUnsafeWindowCandidate() {
    try {
      return _unsafeWindow ?? null;
    } catch {
      return null;
    }
  }
  function getWrappedPageWindowCandidate(unsafeCandidate) {
    try {
      const unsafeObject = unsafeCandidate;
      if (!(unsafeObject == null ? void 0 : unsafeObject.wrappedJSObject) || typeof unsafeObject.wrappedJSObject !== "object") {
        return null;
      }
      return unsafeObject.wrappedJSObject;
    } catch {
      return null;
    }
  }
  let cachedHookGlobalObject = null;
  function getHookGlobalObject() {
    if (cachedHookGlobalObject) {
      return cachedHookGlobalObject;
    }
    const unsafeCandidate = getUnsafeWindowCandidate();
    const wrappedCandidate = getWrappedPageWindowCandidate(unsafeCandidate);
    const windowCandidate = typeof window !== "undefined" ? window : null;
    const globalCandidate = globalThis;
    cachedHookGlobalObject = wrappedCandidate ?? unsafeCandidate ?? windowCandidate ?? globalCandidate;
    return cachedHookGlobalObject;
  }
  const hookGlobalObject = new Proxy({}, {
    get(_target, prop) {
      const globalObject = getHookGlobalObject();
      return Reflect.get(globalObject, prop, globalObject);
    },
    set(_target, prop, value) {
      const globalObject = getHookGlobalObject();
      return Reflect.set(globalObject, prop, value, globalObject);
    }
  });
  function getExportFunctionMaybe() {
    try {
      return globalThis.exportFunction;
    } catch {
      return void 0;
    }
  }
  const HOOK_MESSAGE_FLAG = "__twe_mcp_hook_v1";
  const ORIG_XHR_OPEN_KEY = "__twe_orig_xhr_open_v1";
  const ORIG_XHR_SEND_KEY = "__twe_orig_xhr_send_v1";
  const ORIG_FETCH_KEY = "__twe_orig_fetch_v1";
  const HOOK_BOOTSTRAP_ERROR_KEY = "__twe_bootstrap_error_v1";
  const BOOKMARK_CONTEXT_BOOKMARKS_ONLY_STALE_MS = 45e3;
  const BOOKMARK_CONTEXT_LOCK_TTL_MS = 18e4;
  const BOOKMARK_CONTEXT_SCAN_DEPTH = 6;
  const BOOKMARK_CONTEXT_MIN_CONFIDENCE = 12;
  const BOOKMARK_CONTEXT_DUMP_LIMIT = 200;
  const HOOK_REVISION = 3;
  const EXTENSION_MANAGER_SIGNATURE = "twitter-web-exporter-extension-manager-v1";
  const EXTENSION_MANAGER_REVISION = 3;
  const RESPONSE_DEDUPE_WINDOW_MS = 2600;
  const RESPONSE_DEDUPE_MAX_ENTRIES = 500;
  const RESPONSE_DEDUPE_CLEANUP_COUNT = 120;
  function getRuntimeCapabilities() {
    const unsafeCandidate = getUnsafeWindowCandidate();
    return {
      hasUnsafeWindow: !!unsafeCandidate,
      hasWrappedJSObject: !!getWrappedPageWindowCandidate(unsafeCandidate),
      hasExportFunction: !!getExportFunctionMaybe()
    };
  }
  function hashText$1(text) {
    let hash = 2166136261;
    for (let i = 0; i < text.length; i++) {
      hash ^= text.charCodeAt(i);
      hash = Math.imul(hash, 16777619);
    }
    return (hash >>> 0).toString(16);
  }
  function createRequestSignature(method, url, status, responseText) {
    return `${method.toUpperCase()} ${url} ${status} ${hashText$1(responseText)}`;
  }
  function cleanupSignatureCache(map) {
    const now = Date.now();
    if (map.size <= RESPONSE_DEDUPE_MAX_ENTRIES) return;
    const entries = [...map.entries()].filter(([, value]) => now - value.at > RESPONSE_DEDUPE_WINDOW_MS).sort((a, b) => a[1].at - b[1].at);
    for (const [key] of entries.slice(0, RESPONSE_DEDUPE_CLEANUP_COUNT)) {
      map.delete(key);
    }
  }
  function createHookStats(instanceId) {
    return {
      xhrMessages: 0,
      fetchMessages: 0,
      lastUrl: "",
      lastAt: 0,
      loggedUrls: 0,
      messagesTotal: 0,
      messagesLegacyShape: 0,
      messagesMissingContext: 0,
      messagesRepairedAtBridge: 0,
      messagesMissingBody: 0,
      responsesProcessed: 0,
      responsesSkippedDuplicate: 0,
      lastMessageAt: 0,
      activeInstanceId: instanceId,
      rev: HOOK_REVISION,
      repairCount: 0,
      endpointStats: /* @__PURE__ */ Object.create(null)
    };
  }
  let activeBookmarkContext = {
    folderId: null,
    pageUrl: "",
    source: "startup",
    capturedAt: 0
  };
  let bookmarkContextLock = null;
  let bookmarkContextDumpState = [];
  const xhrRequestMetaMap = /* @__PURE__ */ new WeakMap();
  function createDefaultXhrRequestMeta() {
    return {
      method: "GET",
      url: "",
      body: "",
      requestId: "",
      bookmarkContext: null,
      hooked: false
    };
  }
  function ensureXhrRequestMeta(xhr) {
    const existing = xhrRequestMetaMap.get(xhr);
    if (existing) return existing;
    const next = createDefaultXhrRequestMeta();
    xhrRequestMetaMap.set(xhr, next);
    return next;
  }
  function loadXhrRequestMeta(xhr, debugConfig) {
    if (debugConfig.disableExpandoMeta) {
      return ensureXhrRequestMeta(xhr);
    }
    const method = typeof xhr.__twe_req_method_v1 === "string" ? xhr.__twe_req_method_v1 : "GET";
    const url = typeof xhr.__twe_req_url_v1 === "string" ? xhr.__twe_req_url_v1 : "";
    const body = typeof xhr.__twe_req_body_v1 === "string" ? xhr.__twe_req_body_v1 : "";
    const requestId = typeof xhr.__twe_req_id_v1 === "string" ? xhr.__twe_req_id_v1 : "";
    const bookmarkContext = xhr.__twe_req_bookmark_context_v1 && typeof xhr.__twe_req_bookmark_context_v1 === "object" ? xhr.__twe_req_bookmark_context_v1 : null;
    const hooked = !!xhr.__twe_hooked_v1;
    return {
      method,
      url,
      body,
      requestId,
      bookmarkContext,
      hooked
    };
  }
  function storeXhrRequestMeta(xhr, debugConfig, meta) {
    if (debugConfig.disableExpandoMeta) {
      xhrRequestMetaMap.set(xhr, meta);
      return;
    }
    xhr.__twe_req_method_v1 = meta.method;
    xhr.__twe_req_url_v1 = meta.url;
    xhr.__twe_req_body_v1 = meta.body;
    xhr.__twe_req_id_v1 = meta.requestId;
    xhr.__twe_req_bookmark_context_v1 = meta.bookmarkContext;
    xhr.__twe_hooked_v1 = meta.hooked;
  }
  function callFunctionWithArgs(fn, thisArg, args) {
    switch (args.length) {
      case 0:
        return fn.call(thisArg);
      case 1:
        return fn.call(thisArg, args[0]);
      case 2:
        return fn.call(thisArg, args[0], args[1]);
      case 3:
        return fn.call(thisArg, args[0], args[1], args[2]);
      case 4:
        return fn.call(thisArg, args[0], args[1], args[2], args[3]);
      default:
        return fn.call(thisArg, args[0], args[1], args[2], args[3], args[4]);
    }
  }
  function recordBootstrapError(phase, instanceId, error) {
    const message = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
    const stack = error instanceof Error ? error.stack : void 0;
    const report = {
      message,
      stack,
      phase,
      instanceId,
      at: Date.now()
    };
    return report;
  }
  function clearBootstrapErrorMarker() {
    try {
      const deleted = (obj, key) => {
        if (obj && typeof obj === "object") {
          delete obj[key];
        }
      };
      deleted(globalThis, HOOK_BOOTSTRAP_ERROR_KEY);
    } catch {
    }
  }
  function normalizeRuntimeForRead(value, fallback) {
    if (!value || typeof value !== "object") {
      return fallback;
    }
    const candidate = value;
    return {
      ...candidate,
      xhrMessages: Number(candidate.xhrMessages) || 0,
      fetchMessages: Number(candidate.fetchMessages) || 0,
      lastUrl: typeof candidate.lastUrl === "string" ? candidate.lastUrl : "",
      lastAt: Number(candidate.lastAt) || 0,
      loggedUrls: Number(candidate.loggedUrls) || 0,
      messagesTotal: Number(candidate.messagesTotal) || 0,
      messagesLegacyShape: Number(candidate.messagesLegacyShape) || 0,
      messagesMissingContext: Number(candidate.messagesMissingContext) || 0,
      messagesRepairedAtBridge: Number(candidate.messagesRepairedAtBridge) || 0,
      messagesMissingBody: Number(candidate.messagesMissingBody) || 0,
      responsesProcessed: Number(candidate.responsesProcessed) || 0,
      responsesSkippedDuplicate: Number(candidate.responsesSkippedDuplicate) || 0,
      lastMessageAt: Number(candidate.lastMessageAt) || 0,
      activeInstanceId: typeof candidate.activeInstanceId === "string" ? candidate.activeInstanceId : fallback.activeInstanceId,
      rev: Number(candidate.rev) || HOOK_REVISION,
      repairCount: Number(candidate.repairCount) || 0,
      endpointStats: candidate.endpointStats || /* @__PURE__ */ Object.create(null)
    };
  }
  function toPlainFunctionSource(value) {
    if (typeof value !== "function") return "";
    try {
      return Function.prototype.toString.call(value);
    } catch {
      return "";
    }
  }
  function hasHookVersion(target, marker) {
    if (!target || typeof target !== "function") return false;
    const candidate = target;
    const markerMap = candidate;
    if (!markerMap[marker]) return false;
    return candidate.__twe_is_hook_revision_v1 === HOOK_REVISION;
  }
  function hasHookShape(target, marker) {
    if (!target || typeof target !== "function") return false;
    const candidate = target;
    if (candidate.__twe_is_hook_open_v1 || candidate.__twe_is_hook_send_v1 || candidate.__twe_is_hook_fetch_v1 || candidate.__twe_orig_xhr_open_v1 || candidate.__twe_orig_xhr_send_v1 || candidate.__twe_orig_fetch_v1) {
      return true;
    }
    if (marker && candidate[marker]) {
      return true;
    }
    const source = toPlainFunctionSource(target);
    if (!source) return false;
    return source.includes("__twe_mcp_hook_v1") || source.includes("__twe_is_hook_open_v1") || source.includes("__twe_is_hook_send_v1") || source.includes("__twe_is_hook_fetch_v1") || source.includes("__twe_is_hook_revision_v1") || source.includes("__twe_orig_xhr_open_v1") || source.includes("__twe_orig_xhr_send_v1") || source.includes("__twe_orig_fetch_v1") || source.includes("__twe_req_body_v1") || source.includes("__twe_req_url_v1") || source.includes("__twe_req_bookmark_context_v1");
  }
  const BOOKMARK_CONTEXT_REQUEST_ID_PREFIX = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
  let bookmarkRequestCounter = 0;
  function nextBookmarkRequestId() {
    bookmarkRequestCounter += 1;
    return `${BOOKMARK_CONTEXT_REQUEST_ID_PREFIX}-${bookmarkRequestCounter}`;
  }
  function normalizeContextDumpList(current) {
    var _a2, _b2, _c, _d, _e, _f, _g, _h;
    if (!Array.isArray(current)) return [];
    const out = [];
    for (const item of current) {
      const candidate = item;
      if (candidate && typeof candidate.requestId === "string" && typeof candidate.ts === "number" && typeof candidate.url === "string") {
        out.push({
          requestId: candidate.requestId,
          ts: candidate.ts,
          method: typeof candidate.method === "string" ? candidate.method : "GET",
          url: candidate.url,
          hasBody: !!candidate.hasBody,
          confidenceSource: typeof candidate.confidenceSource === "string" && candidate.confidenceSource ? candidate.confidenceSource : typeof ((_a2 = candidate.context) == null ? void 0 : _a2.source) === "string" ? candidate.context.source : "unknown",
          context: {
            folderId: typeof ((_b2 = candidate.context) == null ? void 0 : _b2.folderId) === "string" ? candidate.context.folderId : null,
            pageUrl: typeof ((_c = candidate.context) == null ? void 0 : _c.pageUrl) === "string" ? candidate.context.pageUrl : "",
            source: typeof ((_d = candidate.context) == null ? void 0 : _d.source) === "string" ? candidate.context.source : "unknown",
            capturedAt: typeof ((_e = candidate.context) == null ? void 0 : _e.capturedAt) === "number" ? candidate.context.capturedAt : Date.now(),
            requestId: typeof ((_f = candidate.context) == null ? void 0 : _f.requestId) === "string" ? candidate.context.requestId : void 0,
            routeSource: typeof ((_g = candidate.context) == null ? void 0 : _g.routeSource) === "string" ? candidate.context.routeSource : void 0,
            pageRouteUrl: typeof ((_h = candidate.context) == null ? void 0 : _h.pageRouteUrl) === "string" ? candidate.context.pageRouteUrl : void 0
          },
          normalizedRoute: typeof candidate.normalizedRoute === "string" ? candidate.normalizedRoute : ""
        });
      }
    }
    return out.sort((a, b) => b.ts - a.ts).slice(0, BOOKMARK_CONTEXT_DUMP_LIMIT);
  }
  function appendBookmarkContextDump(entry) {
    var _a2, _b2, _c, _d, _e, _f, _g, _h;
    const now = Date.now();
    const safeEntry = {
      requestId: entry.requestId || `${BOOKMARK_CONTEXT_REQUEST_ID_PREFIX}-${now}`,
      ts: Number.isFinite(entry.ts) ? entry.ts : now,
      method: entry.method || "GET",
      url: entry.url || "",
      hasBody: !!entry.hasBody,
      confidenceSource: typeof entry.confidenceSource === "string" && entry.confidenceSource ? entry.confidenceSource : ((_a2 = entry.context) == null ? void 0 : _a2.source) || "unknown",
      context: {
        folderId: ((_b2 = entry.context) == null ? void 0 : _b2.folderId) ?? null,
        pageUrl: ((_c = entry.context) == null ? void 0 : _c.pageUrl) || "",
        source: ((_d = entry.context) == null ? void 0 : _d.source) || "unknown",
        capturedAt: Number.isFinite((_e = entry.context) == null ? void 0 : _e.capturedAt) ? entry.context.capturedAt : now,
        requestId: (_f = entry.context) == null ? void 0 : _f.requestId,
        routeSource: (_g = entry.context) == null ? void 0 : _g.routeSource,
        pageRouteUrl: (_h = entry.context) == null ? void 0 : _h.pageRouteUrl
      },
      normalizedRoute: entry.normalizedRoute || ""
    };
    const current = normalizeContextDumpList(bookmarkContextDumpState);
    current.unshift(safeEntry);
    const deduped = /* @__PURE__ */ new Map();
    for (const candidate of current) {
      if (!deduped.has(candidate.requestId)) {
        deduped.set(candidate.requestId, candidate);
      }
    }
    const next = Array.from(deduped.values()).slice(0, BOOKMARK_CONTEXT_DUMP_LIMIT).sort((a, b) => b.ts - a.ts);
    const staleCutoff = Date.now() - BOOKMARK_CONTEXT_BOOKMARKS_ONLY_STALE_MS * 6;
    bookmarkContextDumpState = next.filter(
      (candidate) => !staleCutoff || candidate.ts >= staleCutoff
    );
  }
  function setBookmarkContextLock(value) {
    const payload = {
      folderId: value.folderId,
      pageUrl: value.pageUrl || "",
      source: value.source || "lock",
      capturedAt: value.capturedAt || Date.now(),
      requestId: value.requestId,
      routeSource: value.routeSource,
      pageRouteUrl: value.pageRouteUrl
    };
    if (!payload.folderId) {
      return;
    }
    bookmarkContextLock = payload;
  }
  function getBookmarkContextLock(now = Date.now()) {
    const candidates = [bookmarkContextLock];
    for (const raw of candidates) {
      if (!raw || typeof raw !== "object") continue;
      const candidate = raw;
      if (typeof candidate.folderId !== "string" || !candidate.folderId) continue;
      const capturedAt = typeof candidate.capturedAt === "number" && Number.isFinite(candidate.capturedAt) ? candidate.capturedAt : 0;
      if (!capturedAt || now - capturedAt > BOOKMARK_CONTEXT_LOCK_TTL_MS) {
        continue;
      }
      return {
        folderId: candidate.folderId,
        pageUrl: typeof candidate.pageUrl === "string" ? candidate.pageUrl : "",
        source: typeof candidate.source === "string" ? candidate.source : "lock",
        capturedAt,
        requestId: typeof candidate.requestId === "string" ? candidate.requestId : void 0,
        routeSource: typeof candidate.routeSource === "string" ? candidate.routeSource : void 0,
        pageRouteUrl: typeof candidate.pageRouteUrl === "string" ? candidate.pageRouteUrl : void 0
      };
    }
    return null;
  }
  function clearBookmarkContextLock() {
    bookmarkContextLock = null;
  }
  function resolveCanonicalRouteFromUrl(url) {
    return captureBookmarkRouteFromUrl(url) || { folderId: null, pageUrl: url };
  }
  function markHookFunction(fn, marker) {
    if (typeof fn !== "function") return;
    try {
      const hookFn = fn;
      hookFn[marker] = true;
      hookFn.__twe_is_hook_revision_v1 = HOOK_REVISION;
    } catch {
    }
  }
  function normalizeHookPayloadRequest(rawReq) {
    const req = rawReq;
    const normalized = {
      method: typeof req.method === "string" ? req.method : "GET",
      url: typeof req.url === "string" ? req.url : ""
    };
    if (typeof req.body === "string") {
      normalized.body = req.body;
    }
    if (req.bookmarkContext !== void 0) {
      normalized.bookmarkContext = req.bookmarkContext;
    }
    if (normalized.bookmarkContext === void 0 && req.requestContext !== void 0) {
      normalized.bookmarkContext = req.requestContext;
    }
    if (typeof req.requestId === "string" && req.requestId) {
      normalized.requestId = req.requestId;
    }
    if (typeof req.__twe_hook_revision_v1 === "number" && Number.isFinite(req.__twe_hook_revision_v1)) {
      normalized.hookRevision = req.__twe_hook_revision_v1;
    }
    return normalized;
  }
  function buildNormalizedHookMessageRequest(rawReq) {
    const req = normalizeHookPayloadRequest(rawReq);
    const requestId = typeof req.requestId === "string" && req.requestId.trim().length > 0 ? req.requestId : nextBookmarkRequestId();
    const method = typeof req.method === "string" && req.method ? req.method : "GET";
    const url = typeof req.url === "string" ? req.url : "";
    const body = typeof req.body === "string" ? req.body : "";
    const bookmarkContext = normalizeBookmarkContextValue(req.bookmarkContext, {
      requestId,
      hasBody: body.length > 0
    });
    return {
      method,
      url,
      body,
      bookmarkContext,
      requestId,
      hookRevision: HOOK_REVISION,
      __twe_hook_revision_v1: HOOK_REVISION
    };
  }
  function pickBridgeRequest(rawMessage) {
    if (!rawMessage || typeof rawMessage !== "object") return null;
    const envelope = rawMessage;
    if (envelope.req && typeof envelope.req === "object") return envelope.req;
    return envelope;
  }
  function getBridgeMessageRevision(rawMessage) {
    if (!rawMessage || typeof rawMessage !== "object") return null;
    const envelope = rawMessage;
    const req = envelope.req;
    if (typeof envelope.__twe_msg_revision_v1 === "number") {
      return Number(envelope.__twe_msg_revision_v1);
    }
    if (req && typeof req === "object" && typeof req.__twe_hook_revision_v1 === "number") {
      return Number(req.__twe_hook_revision_v1);
    }
    return null;
  }
  function createEndpointMetrics() {
    return {
      received: 0,
      processed: 0,
      skippedDuplicate: 0,
      newUniqueTweets: 0,
      legacyShape: 0,
      missingContext: 0,
      lastAt: 0,
      lastStatus: 0,
      lastUrl: ""
    };
  }
  function extractBookmarksEndpoint(url) {
    try {
      const parsed = new URL(url, "https://x.com");
      const path = parsed.pathname.toLowerCase();
      const graphqlMatch = path.match(/\/graphql\/[^/]+\/([^/?#]+)/);
      if (graphqlMatch == null ? void 0 : graphqlMatch[1]) {
        return `graphql:${graphqlMatch[1]}`;
      }
      const apiMatch = path.match(/\/i\/api\/1\.1\/([^/?#]+)/);
      if (apiMatch == null ? void 0 : apiMatch[1]) {
        return `api:${apiMatch[1]}`;
      }
    } catch {
    }
    return "other";
  }
  function countUniqueTweetIds(responseText) {
    const found = /* @__PURE__ */ new Set();
    const regex = /"rest_id"\s*:\s*"(\d{10,})"/g;
    for (; ; ) {
      const match = regex.exec(responseText);
      if (!match) break;
      if (match[1]) {
        found.add(match[1]);
      }
    }
    return found.size;
  }
  function serializeRequestBodyText(body) {
    try {
      if (!body) return void 0;
      if (typeof body === "string") {
        return body;
      }
      if (typeof URLSearchParams !== "undefined" && body instanceof URLSearchParams) {
        return body.toString();
      }
      if (typeof FormData !== "undefined" && body instanceof FormData) {
        try {
          const entries = [...body.entries()].map(([name2, value]) => `${name2}=${String(value).slice(0, 200)}`).join("&");
          return entries;
        } catch {
          return void 0;
        }
      }
      if (typeof Blob !== "undefined" && body instanceof Blob) {
        return `blob:${body.type || "application/octet-stream"}:${body.size}`;
      }
      return void 0;
    } catch {
      return void 0;
    }
  }
  function captureBookmarkRouteFromUrl(url) {
    try {
      const u2 = new URL(url, "https://x.com");
      const match = u2.pathname.match(/\/bookmarks\/(\d+)/);
      return { folderId: (match == null ? void 0 : match[1]) ?? null, pageUrl: u2.href };
    } catch {
      return null;
    }
  }
  function coerceBookmarkFolderId(value) {
    if (typeof value === "number" && Number.isFinite(value)) {
      const normalized = String(Math.trunc(value));
      return /^\d+$/.test(normalized) ? normalized : null;
    }
    if (typeof value !== "string") return null;
    const trimmed = value.trim();
    return /^\d+$/.test(trimmed) ? trimmed : null;
  }
  function isLikelyBookmarkFolderKey(key) {
    const normalized = key.toLowerCase().replace(/[^a-z0-9]/g, "");
    return /^(bookmarkcollectionid|bookmarkfolderid|bookmarkcollection|folderid|collectionid|bookmarkfolder|bookmarkcollectionid)/.test(
      normalized
    );
  }
  function findFolderIdInUnknownValue(value, depth = 0, seen2 = /* @__PURE__ */ new Set()) {
    if (!value || depth > BOOKMARK_CONTEXT_SCAN_DEPTH) return null;
    if (typeof value === "string") {
      const fromUrl = captureBookmarkRouteFromUrl(value);
      if (fromUrl == null ? void 0 : fromUrl.folderId) return { folderId: fromUrl.folderId, pageUrl: fromUrl.pageUrl };
      const fallback = value.match(
        /(?:bookmark|folder|collection)[^\w]{0,20}(?:id|_id|Id|_Id)\W*[:=]\W*["']?(\d{5,})["']?/i
      );
      if (fallback == null ? void 0 : fallback[1]) return { folderId: fallback[1] };
      return null;
    }
    if (typeof value !== "object") return null;
    if (Array.isArray(value)) {
      for (const item of value) {
        const found = findFolderIdInUnknownValue(item, depth + 1, seen2);
        if (found == null ? void 0 : found.folderId) return found;
      }
      return null;
    }
    const obj = value;
    if (seen2.has(obj)) return null;
    seen2.add(obj);
    for (const [key, nested] of Object.entries(obj)) {
      const fromKey = isLikelyBookmarkFolderKey(key) ? coerceBookmarkFolderId(nested) : null;
      if (fromKey) {
        const asText = typeof nested === "string" ? nested : "";
        const fromText = asText ? captureBookmarkRouteFromUrl(asText) : null;
        if (fromText == null ? void 0 : fromText.folderId) return { folderId: fromText.folderId, pageUrl: fromText.pageUrl };
        return { folderId: fromKey };
      }
      const found = findFolderIdInUnknownValue(nested, depth + 1, seen2);
      if (found == null ? void 0 : found.folderId) return found;
    }
    return null;
  }
  const BOOKMARK_QUERY_FOLDER_KEYS = [
    "bookmark_collection_id",
    "bookmarkcollectionid",
    "bookmarkCollectionId",
    "folder_id",
    "folderid",
    "folderId",
    "collection_id",
    "collectionid",
    "collectionId"
  ];
  function extractFolderIdFromRequestVariables(raw) {
    if (!raw) return null;
    let parsed;
    try {
      parsed = JSON.parse(decodeURIComponent(raw));
    } catch {
      try {
        parsed = JSON.parse(raw);
      } catch {
        return null;
      }
    }
    const found = findFolderIdInUnknownValue(parsed);
    if (found == null ? void 0 : found.folderId) return found.folderId;
    if (parsed && typeof parsed === "object") {
      const obj = parsed;
      for (const key of BOOKMARK_QUERY_FOLDER_KEYS) {
        const value = obj[key];
        const folderId = coerceBookmarkFolderId(value);
        if (folderId) return folderId;
      }
    }
    return null;
  }
  function extractFolderIdFromBookmarkRequestUrl(url) {
    try {
      const u2 = new URL(url, "https://x.com");
      const directQueryId = BOOKMARK_QUERY_FOLDER_KEYS.map((key) => u2.searchParams.get(key)).find(
        (value) => !!value && /^\d+$/.test(value)
      );
      if (directQueryId) {
        return directQueryId;
      }
      const fromVariables = extractFolderIdFromRequestVariables(u2.searchParams.get("variables"));
      if (fromVariables) {
        return fromVariables;
      }
      return null;
    } catch {
      return null;
    }
  }
  function extractFolderIdFromBookmarkRequestBody(body) {
    if (!body) return null;
    let parsed;
    try {
      parsed = JSON.parse(body);
    } catch {
      try {
        const form = new URLSearchParams(body);
        const formVariables = form.get("variables");
        if (formVariables) {
          parsed = JSON.parse(formVariables);
        } else {
          return null;
        }
      } catch {
        return null;
      }
    }
    const found = findFolderIdInUnknownValue(parsed);
    if (found == null ? void 0 : found.folderId) return found.folderId;
    return null;
  }
  function extractBookmarkFolderIdFromPath(pathOrUrl) {
    try {
      const path = new URL(pathOrUrl, location.href).pathname;
      const match = path.match(/\/bookmarks\/(\d+)/);
      return (match == null ? void 0 : match[1]) ?? null;
    } catch {
      return null;
    }
  }
  function normalizeBookmarkTabCandidateSource(value) {
    if (!value) return "bookmark-tab";
    return value;
  }
  function captureBookmarkRouteFromPerformanceNavigation() {
    try {
      if (typeof performance === "undefined" || typeof performance.getEntriesByType !== "function") {
        return null;
      }
      const entries = performance.getEntriesByType("navigation");
      const candidateEntry = entries[entries.length - 1];
      if (!candidateEntry || !candidateEntry.name) return null;
      const parsed = captureBookmarkRouteFromUrl(candidateEntry.name);
      if (!(parsed == null ? void 0 : parsed.folderId)) return null;
      return {
        folderId: parsed.folderId,
        pageUrl: parsed.pageUrl,
        source: "performance",
        confidence: 88
      };
    } catch {
      return null;
    }
  }
  function captureBookmarkRouteFromBookmarkTabs() {
    if (typeof document === "undefined") return null;
    const selectors = [
      '[role="tab"] a[href*="/i/bookmarks/"]',
      'a[role="tab"][href*="/i/bookmarks/"]',
      '[role="tablist"] a[href*="/i/bookmarks/"]',
      'a[href*="/i/bookmarks/"]',
      // Additional selectors for X's current DOM structure
      'nav a[href*="/bookmarks/"]',
      '[data-testid="primaryColumn"] a[href*="/bookmarks/"]',
      'a[href*="/bookmarks/"]'
    ];
    const activeTabs = [];
    const allTabs = [];
    const seen2 = /* @__PURE__ */ new Set();
    for (const selector of selectors) {
      const nodes = Array.from(document.querySelectorAll(selector));
      for (const node of nodes) {
        if (!(node instanceof HTMLAnchorElement)) continue;
        const rawHref = node.getAttribute("href");
        if (!rawHref) continue;
        const folderId = extractBookmarkFolderIdFromPath(rawHref);
        if (!folderId) {
          continue;
        }
        const safeFolderId = folderId;
        if (seen2.has(safeFolderId)) continue;
        seen2.add(safeFolderId);
        const hrefUrl = (() => {
          try {
            return new URL(rawHref, location.href).href;
          } catch {
            return rawHref;
          }
        })();
        const anchorOrFallback = node.closest("a[href]") || node;
        const attr = (el, name2) => el == null ? void 0 : el.getAttribute(name2);
        const attrBool = (el, name2) => attr(el, name2) === "true";
        const isActive = attrBool(anchorOrFallback, "aria-selected") || attrBool(anchorOrFallback, "aria-current") || attrBool(anchorOrFallback, "data-selected") || attrBool(anchorOrFallback, "data-state");
        const isInsideTabList = !!anchorOrFallback.closest('[role="tablist"]');
        const isAnchorTab = anchorOrFallback instanceof HTMLAnchorElement && (anchorOrFallback.role === "tab" || !!anchorOrFallback.closest('[role="tab"]'));
        let style = null;
        try {
          style = window.getComputedStyle(anchorOrFallback);
        } catch {
        }
        const isVisible = style ? style.display !== "none" && style.visibility !== "hidden" && style.opacity !== "0" : true;
        let score = 0;
        if (isActive) score += 20;
        if (isInsideTabList) score += 5;
        if (isAnchorTab) score += 3;
        if (isVisible) score += 2;
        const tabInfo = {
          folderId: safeFolderId,
          pageUrl: hrefUrl,
          source: normalizeBookmarkTabCandidateSource(
            anchorOrFallback.getAttribute("data-testid") ?? (isAnchorTab ? "bookmark-tab" : null)
          ),
          score
        };
        allTabs.push(tabInfo);
        if (isActive) {
          activeTabs.push(tabInfo);
        }
      }
    }
    const picked = activeTabs.length > 0 ? activeTabs : allTabs;
    if (!picked.length) return null;
    picked.sort((a, b) => {
      if (a.score !== b.score) return b.score - a.score;
      return a.folderId.localeCompare(b.folderId);
    });
    const top = picked[0];
    if (!top) return null;
    return {
      folderId: top.folderId,
      pageUrl: top.pageUrl,
      source: top.source,
      confidence: Math.max(
        activeTabs.length > 0 ? 45 : 25,
        top.score + (activeTabs.length > 0 ? 45 : 25)
      )
    };
  }
  function captureBookmarkRouteFromHistoryState() {
    var _a2;
    try {
      const state = (_a2 = hookGlobalObject.history) == null ? void 0 : _a2.state;
      if (!state) return null;
      const found = findFolderIdInUnknownValue(state);
      if (!(found == null ? void 0 : found.folderId)) return null;
      const pageUrl = found.pageUrl || (typeof location !== "undefined" ? location.href : "");
      return { folderId: found.folderId, pageUrl, source: "history-state", confidence: 86 };
    } catch {
      return null;
    }
  }
  function captureBookmarkRouteFromGlobalState() {
    const stateSources = [
      "__INITIAL_STATE__",
      "__NEXT_DATA__",
      "__INITIAL_PROPS__",
      "__NEXT_REDUX_STATE__",
      "__META_DATA__"
    ];
    const candidates = [];
    const globalObj = hookGlobalObject;
    for (const key of stateSources) {
      const value = globalObj[key];
      if (!value) continue;
      candidates.push({ source: key, value });
    }
    for (const { source, value } of candidates) {
      const found = findFolderIdInUnknownValue(value);
      if (!(found == null ? void 0 : found.folderId)) continue;
      return {
        folderId: found.folderId,
        pageUrl: found.pageUrl || (typeof location !== "undefined" ? location.href : ""),
        source,
        confidence: 82
      };
    }
    return null;
  }
  function captureBookmarkRouteFromEventTarget(target) {
    var _a2;
    if (!target || typeof target !== "object") return null;
    const candidates = [];
    const seen2 = /* @__PURE__ */ new Set();
    const push = (candidate) => {
      if (candidate instanceof Element && !seen2.has(candidate)) {
        seen2.add(candidate);
        candidates.push(candidate);
      }
    };
    push(target);
    const targetWithPath = target;
    if (typeof targetWithPath.composedPath === "function") {
      const path = targetWithPath.composedPath() || [];
      path.forEach((item) => {
        push(item);
      });
    }
    const directTarget = target;
    push(directTarget.target);
    push(directTarget.currentTarget);
    for (const node of candidates) {
      const anchor = (_a2 = node.closest) == null ? void 0 : _a2.call(node, "a[href]");
      const targetNode = node instanceof HTMLAnchorElement ? node : anchor;
      if (!targetNode) continue;
      const href = targetNode.getAttribute("href");
      if (!href || !href.includes("/i/bookmarks/")) {
        continue;
      }
      const parsed = captureBookmarkRouteFromUrl(href);
      if (!(parsed == null ? void 0 : parsed.folderId)) continue;
      return {
        folderId: parsed.folderId,
        pageUrl: parsed.pageUrl,
        source: "bookmark-click",
        confidence: 92
      };
    }
    return null;
  }
  function isBookmarksApiRequest(url) {
    try {
      const path = new URL(url, "https://x.com").pathname.toLowerCase();
      return /(bookmarks|bookmarkfolderslice|bookmarkfoldertimeline|bookmarkcollectiontimeline|bookmarkcollectionstimeline)/.test(
        path
      );
    } catch {
      return false;
    }
  }
  function resolveRequestBookmarkContext(url, bodyText, request) {
    const now = Date.now();
    const pageUrl = typeof location !== "undefined" ? location.href : "";
    const pageCandidate = resolveCanonicalRouteFromUrl(pageUrl);
    if (isBookmarksApiRequest(url)) {
      const fromRequest = extractFolderIdFromBookmarkRequestUrl(url);
      if (fromRequest) {
        return {
          folderId: fromRequest,
          pageUrl,
          source: "request-url",
          capturedAt: now,
          requestId: request == null ? void 0 : request.requestId,
          routeSource: "request-url",
          pageRouteUrl: pageCandidate == null ? void 0 : pageCandidate.pageUrl
        };
      }
      const fromBody = extractFolderIdFromBookmarkRequestBody(bodyText);
      if (fromBody) {
        return {
          folderId: fromBody,
          pageUrl,
          source: "request-body",
          capturedAt: now,
          requestId: request == null ? void 0 : request.requestId,
          routeSource: "request-body",
          pageRouteUrl: pageCandidate == null ? void 0 : pageCandidate.pageUrl
        };
      }
      if (pageCandidate == null ? void 0 : pageCandidate.folderId) {
        return {
          folderId: pageCandidate.folderId,
          pageUrl: pageCandidate.pageUrl || pageUrl,
          source: "page-route",
          capturedAt: now,
          requestId: request == null ? void 0 : request.requestId,
          routeSource: "location",
          pageRouteUrl: pageCandidate.pageUrl || pageUrl
        };
      }
      const lock = getBookmarkContextLock(now);
      if ((lock == null ? void 0 : lock.folderId) && isBookmarksRoute(pageUrl)) {
        return {
          folderId: lock.folderId,
          pageUrl: lock.pageUrl || pageUrl,
          source: lock.source || "active-context-lock",
          capturedAt: now,
          requestId: request == null ? void 0 : request.requestId,
          routeSource: "active-context-lock",
          pageRouteUrl: lock.pageUrl || pageUrl
        };
      }
      const pageUrlFresh = typeof location !== "undefined" ? location.href : pageUrl;
      if ((activeBookmarkContext == null ? void 0 : activeBookmarkContext.folderId) && now - activeBookmarkContext.capturedAt <= BOOKMARK_CONTEXT_BOOKMARKS_ONLY_STALE_MS) {
        return {
          folderId: activeBookmarkContext.folderId,
          pageUrl: activeBookmarkContext.pageUrl || pageUrlFresh,
          source: activeBookmarkContext.source || "active-context",
          capturedAt: now,
          requestId: request == null ? void 0 : request.requestId,
          routeSource: "active-context",
          pageRouteUrl: activeBookmarkContext.pageUrl || pageUrlFresh
        };
      }
      const fromPage = captureBookmarkRouteFromPage();
      if (fromPage == null ? void 0 : fromPage.folderId) {
        return {
          folderId: fromPage.folderId,
          pageUrl: fromPage.pageUrl,
          source: fromPage.source,
          capturedAt: now,
          requestId: request == null ? void 0 : request.requestId,
          routeSource: fromPage.source,
          pageRouteUrl: fromPage.pageUrl
        };
      }
    }
    return normalizeBookmarkContextValue(activeBookmarkContext, {
      method: (request == null ? void 0 : request.method) || "GET",
      requestId: request == null ? void 0 : request.requestId,
      hasBody: !!(request == null ? void 0 : request.body)
    });
  }
  function normalizeBookmarkContextValue(raw, request) {
    const fallbackUrl = typeof location !== "undefined" ? location.href : typeof document !== "undefined" ? document.URL : "";
    const fallback = {
      folderId: null,
      pageUrl: fallbackUrl,
      source: "fallback",
      capturedAt: Date.now(),
      requestId: request == null ? void 0 : request.requestId
    };
    if (!raw) {
      const found = captureBookmarkRouteFromPage();
      return {
        folderId: (found == null ? void 0 : found.folderId) ?? null,
        pageUrl: (found == null ? void 0 : found.pageUrl) ?? fallback.pageUrl,
        source: (found == null ? void 0 : found.source) ?? fallback.source,
        requestId: request == null ? void 0 : request.requestId,
        routeSource: found == null ? void 0 : found.source,
        pageRouteUrl: found == null ? void 0 : found.pageUrl,
        capturedAt: Date.now()
      };
    }
    if (typeof raw === "string") {
      const trimmed = raw.trim();
      if (!trimmed) return fallback;
      if (/^\d+$/.test(trimmed)) {
        return {
          folderId: trimmed,
          pageUrl: fallback.pageUrl,
          source: "string-id",
          capturedAt: Date.now(),
          requestId: request == null ? void 0 : request.requestId
        };
      }
      const fromRaw = captureBookmarkRouteFromUrl(trimmed);
      return {
        folderId: (fromRaw == null ? void 0 : fromRaw.folderId) ?? null,
        pageUrl: (fromRaw == null ? void 0 : fromRaw.pageUrl) ?? fallback.pageUrl,
        source: "raw-string",
        capturedAt: Date.now(),
        requestId: request == null ? void 0 : request.requestId,
        routeSource: (fromRaw == null ? void 0 : fromRaw.folderId) ? "string-id" : "fallback",
        pageRouteUrl: fromRaw == null ? void 0 : fromRaw.pageUrl
      };
    }
    if (typeof raw === "object") {
      const asObj = raw;
      const candidates = [
        asObj.folderUrl,
        asObj.pageUrl,
        asObj.url,
        asObj.location,
        asObj.currentUrl,
        asObj.pageUrlBase64
      ].map((value) => value).filter((value) => typeof value === "string" && value.length > 0);
      const now = Date.now();
      const candidate = candidates.map((value) => captureBookmarkRouteFromUrl(value)).find((value) => !!(value == null ? void 0 : value.folderId));
      const directFolderId = typeof asObj.folderId === "string" || typeof asObj.folderId === "number" ? String(asObj.folderId) : null;
      const pageUrl = typeof asObj.pageUrl === "string" && asObj.pageUrl.length > 0 ? asObj.pageUrl : typeof asObj.url === "string" && asObj.url.length > 0 ? asObj.url : fallback.pageUrl;
      if (candidate == null ? void 0 : candidate.folderId) {
        return {
          folderId: candidate.folderId,
          pageUrl: candidate.pageUrl,
          source: typeof asObj.source === "string" && asObj.source.length > 0 ? asObj.source : "object",
          capturedAt: typeof asObj.capturedAt === "number" ? asObj.capturedAt : now,
          requestId: request == null ? void 0 : request.requestId,
          routeSource: typeof asObj.source === "string" && asObj.source.length > 0 ? asObj.source : "object",
          pageRouteUrl: pageUrl
        };
      }
      if (directFolderId && /^\d+$/.test(directFolderId)) {
        return {
          folderId: directFolderId,
          pageUrl,
          source: typeof asObj.source === "string" && asObj.source.length > 0 ? asObj.source : "object",
          capturedAt: typeof asObj.capturedAt === "number" ? asObj.capturedAt : now,
          requestId: request == null ? void 0 : request.requestId,
          routeSource: typeof asObj.source === "string" && asObj.source.length > 0 ? asObj.source : "object",
          pageRouteUrl: pageUrl
        };
      }
    }
    const fromLocation = captureBookmarkRouteFromPage();
    return {
      folderId: (fromLocation == null ? void 0 : fromLocation.folderId) ?? null,
      pageUrl: (fromLocation == null ? void 0 : fromLocation.pageUrl) ?? fallback.pageUrl,
      source: "fallback",
      capturedAt: Date.now(),
      requestId: request == null ? void 0 : request.requestId,
      routeSource: fromLocation == null ? void 0 : fromLocation.source,
      pageRouteUrl: fromLocation == null ? void 0 : fromLocation.pageUrl
    };
  }
  function captureBookmarkRouteFromPage() {
    const candidates = [];
    const tabCandidate = captureBookmarkRouteFromBookmarkTabs();
    if (tabCandidate) candidates.push(tabCandidate);
    const historyCandidate = captureBookmarkRouteFromHistoryState();
    if (historyCandidate) candidates.push(historyCandidate);
    const navigationCandidate = captureBookmarkRouteFromPerformanceNavigation();
    if (navigationCandidate) candidates.push(navigationCandidate);
    const globalCandidate = captureBookmarkRouteFromGlobalState();
    if (globalCandidate) candidates.push(globalCandidate);
    const bestFolderCandidate = candidates.filter((candidate) => !!candidate.folderId).sort((a, b) => b.confidence - a.confidence)[0];
    if (bestFolderCandidate) {
      return {
        folderId: bestFolderCandidate.folderId,
        pageUrl: bestFolderCandidate.pageUrl,
        source: bestFolderCandidate.source,
        confidence: bestFolderCandidate.confidence
      };
    }
    const urlCandidates = [];
    const locationUrl = typeof location !== "undefined" ? location.href : "";
    const documentUrl = typeof document !== "undefined" ? document.URL : "";
    const canonical = typeof document !== "undefined" ? document.querySelector('link[rel="canonical"]') : null;
    const og = typeof document !== "undefined" ? document.querySelector('meta[property="og:url"]') : null;
    if (locationUrl) urlCandidates.push({ source: "location", url: locationUrl });
    if (documentUrl && documentUrl !== locationUrl)
      urlCandidates.push({ source: "document", url: documentUrl });
    if (canonical == null ? void 0 : canonical.href) urlCandidates.push({ source: "canonical", url: canonical.href });
    if (og == null ? void 0 : og.content) urlCandidates.push({ source: "og", url: og.content });
    for (const candidate of urlCandidates) {
      const parsed = captureBookmarkRouteFromUrl(candidate.url);
      if (parsed == null ? void 0 : parsed.folderId) {
        return { ...parsed, source: candidate.source, confidence: 30 };
      }
    }
    const firstCandidate = urlCandidates[0];
    if (!firstCandidate) return null;
    const firstUrl = firstCandidate.url;
    if (!firstUrl) return null;
    return {
      folderId: null,
      pageUrl: firstUrl,
      source: firstCandidate.source,
      confidence: 14
    };
  }
  function isBookmarksRoute(url) {
    try {
      return /\/i\/bookmarks(?:\/|$)/.test(new URL(url, "https://x.com").pathname);
    } catch {
      return false;
    }
  }
  function setBookmarkContext(value) {
    const payload = normalizeBookmarkContextValue(value);
    activeBookmarkContext = payload;
    if (payload.folderId) {
      setBookmarkContextLock(payload);
    } else if (!isBookmarksRoute(payload.pageUrl)) {
      clearBookmarkContextLock();
    }
  }
  function defineOn(target, name2, fn) {
    if (typeof fn !== "function") {
      return false;
    }
    const hookFn = fn;
    const exportFunctionMaybe = getExportFunctionMaybe();
    if (exportFunctionMaybe) {
      try {
        exportFunctionMaybe(hookFn, target, { defineAs: name2 });
        return true;
      } catch (err2) {
        logger.error(`Failed to define ${name2} hook`, err2);
        return false;
      }
    }
    if (target !== globalThis) {
      logger.error(
        `Failed to define ${name2} hook`,
        new Error("exportFunction unavailable for cross-realm target")
      );
      return false;
    }
    try {
      target[name2] = hookFn;
      return true;
    } catch (err2) {
      logger.error(`Failed to define ${name2} hook`, err2);
      return false;
    }
  }
  function getFunctionFromHookState(candidate, marker, originalKey) {
    if (!candidate || typeof candidate !== "function") {
      return void 0;
    }
    let current = candidate;
    if (!hasHookShape(candidate, marker)) {
      return candidate;
    }
    for (let i = 0; i < 10; i++) {
      const nested = current[originalKey];
      if (!nested || typeof nested !== "function") {
        return void 0;
      }
      if (!hasHookShape(nested, marker)) {
        return nested;
      }
      current = nested;
    }
    return void 0;
  }
  function isUsableFetchBaseCandidate(candidate) {
    if (typeof candidate !== "function") return false;
    if (hasHookShape(candidate, "__twe_is_hook_fetch_v1")) return false;
    return true;
  }
  function getObjectStringProperty(value, key) {
    if (!value || typeof value !== "object" && typeof value !== "function") {
      return null;
    }
    try {
      const candidate = value[key];
      return typeof candidate === "string" ? candidate : null;
    } catch {
      return null;
    }
  }
  function extractFetchLikeMethod(input, init) {
    const fromInit = typeof (init == null ? void 0 : init.method) === "string" && init.method ? init.method : null;
    if (fromInit) return fromInit;
    try {
      if (typeof Request !== "undefined" && input instanceof Request) {
        return input.method || "GET";
      }
    } catch {
    }
    const fromInput = getObjectStringProperty(input, "method");
    if (fromInput && fromInput.length > 0) return fromInput;
    return "GET";
  }
  function extractFetchLikeUrl(input) {
    try {
      if (typeof input === "string") return input;
      if (typeof URL !== "undefined" && input instanceof URL) return input.toString();
      if (typeof Request !== "undefined" && input instanceof Request) return input.url || "";
    } catch {
    }
    const fromInput = getObjectStringProperty(input, "url");
    if (fromInput && fromInput.length > 0) return fromInput;
    try {
      return String(input ?? "");
    } catch {
      return "";
    }
  }
  function isCaptureCandidateApiUrl(url) {
    if (!url) return false;
    return /\/graphql\/|\/i\/api\/|\/api\/1\.1\/|\/api\/2\//.test(url);
  }
  function getSafeErrorInfo(error) {
    let name2 = typeof error;
    let message = "";
    if (error === null) {
      return { name: "null", message: "null", summary: "null: null" };
    }
    if (typeof error === "undefined") {
      return { name: "undefined", message: "undefined", summary: "undefined: undefined" };
    }
    if (typeof error === "string") {
      return { name: "Error", message: error, summary: `Error: ${error}` };
    }
    try {
      const candidate = error;
      if (typeof candidate.name === "string" && candidate.name.length > 0) {
        name2 = candidate.name;
      }
      if (typeof candidate.message === "string" && candidate.message.length > 0) {
        message = candidate.message;
      }
    } catch {
    }
    if (!message) {
      try {
        message = String(error);
      } catch {
        message = "[inaccessible error object]";
      }
    }
    return {
      name: name2,
      message,
      summary: `${name2}: ${message}`
    };
  }
  function isLikelyCrossRealmPermissionError(error) {
    const info = getSafeErrorInfo(error);
    return /permission denied to access (property|object|then|apply)/i.test(info.summary);
  }
  function postHookMessage(payload) {
    const messageTargetOrigin = "*";
    try {
      if (typeof payload !== "object" || payload === null || payload.__twe_mcp_hook_v1 !== true) {
        return;
      }
      const data = payload;
      if (data.req && typeof data.req === "object") {
        data.req = buildNormalizedHookMessageRequest(data.req);
      }
      payload = {
        ...payload,
        __twe_msg_revision_v1: HOOK_REVISION
      };
      const postMessageOnHookGlobal = hookGlobalObject.postMessage;
      postMessageOnHookGlobal == null ? void 0 : postMessageOnHookGlobal(payload, messageTargetOrigin);
      return;
    } catch {
    }
    try {
      const postMessageOnGlobalThis = globalThis.postMessage;
      postMessageOnGlobalThis == null ? void 0 : postMessageOnGlobalThis(payload, messageTargetOrigin);
    } catch {
    }
  }
  function addEventListenerSafe(target, type2, listener, options2) {
    if (!target || typeof target !== "object" && typeof target !== "function") {
      return false;
    }
    const addEventListener = target.addEventListener;
    if (typeof addEventListener !== "function") {
      return false;
    }
    try {
      addEventListener.call(target, type2, listener, options2);
      return true;
    } catch {
      return false;
    }
  }
  class ExtensionManager {
    constructor() {
      __publicField(this, "extensions", /* @__PURE__ */ new Map());
      __publicField(this, "disabledExtensions", /* @__PURE__ */ new Set());
      __publicField(this, "debugEnabled", false);
      __publicField(this, "hookStats", null);
      __publicField(this, "hookRuntime", null);
      __publicField(this, "recentResponseSigs", /* @__PURE__ */ new Map());
      __publicField(this, "lastStickyBookmarkContext", null);
      __publicField(this, "runtimeControlPlane");
      __publicField(this, "interceptorDispatcher", new ExtensionInterceptorDispatcher());
      __publicField(this, "pageMessageHandler", null);
      __publicField(this, "instanceId", `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`);
      __publicField(this, "__twe_extension_manager_signature_v1", EXTENSION_MANAGER_SIGNATURE);
      __publicField(this, "__twe_extension_manager_revision_v1", EXTENSION_MANAGER_REVISION);
      __publicField(this, "__twe_extension_manager_started_at_v1", Date.now());
      __publicField(this, "disposed", false);
      __publicField(this, "endpointMetricLimit", 40);
      /**
       * Signal for subscribing to extension changes.
       */
      __publicField(this, "signal", new signals.Signal(1));
      this.runtimeControlPlane = new RuntimeControlPlane({
        installPageMessageBridge: () => this.installPageMessageBridge(),
        installBookmarkContextTracking: () => this.installBookmarkContextTracking(),
        installHttpHooks: (force) => this.installHttpHooks(force),
        installFetchHooks: (force) => this.installFetchHooks(force),
        uninstallHooks: () => this.uninstallHooks(),
        runHookSelfTest: () => this.runHookSelfTest(),
        runFetchHookBootProbePass: () => this.runFetchHookBootProbePass(),
        runHookRepairPass: () => this.runHookRepairPass(),
        readHookStatsSnapshot: () => this.readHookStatsSnapshot()
      });
      this.disabledExtensions = new Set(appOptionsManager.get("disabledExtensions", []));
      if (appOptionsManager.get("debug")) {
        this.debugEnabled = true;
        logger.info("Debug mode enabled");
      }
      clearBootstrapErrorMarker();
      let constructorError = null;
      try {
        this.hookStats = createHookStats(this.instanceId);
        this.hookRuntime = this.hookStats;
        this.syncRuntimeStats();
      } catch (error) {
        constructorError = recordBootstrapError(
          "ExtensionManager.constructor",
          this.instanceId,
          error
        );
        logger.error("ExtensionManager constructor bootstrap error", error);
      }
      try {
        const fallbackStats = normalizeRuntimeForRead(
          this.hookStats,
          createHookStats(this.instanceId)
        );
        if (!this.hookStats || this.hookStats.activeInstanceId !== this.instanceId) {
          this.hookStats = fallbackStats;
        }
        this.hookStats.activeInstanceId = this.instanceId;
        this.hookStats.rev = HOOK_REVISION;
        if (constructorError) {
          this.hookStats.repairCount = (this.hookStats.repairCount || 0) + 1;
        }
        this.hookRuntime = this.hookStats;
        this.syncRuntimeStats();
      } catch {
      }
      if (!this.hookRuntime || !this.hookStats) {
        this.hookStats = createHookStats(this.instanceId);
        this.hookRuntime = this.hookStats;
        this.syncRuntimeStats();
      }
      this.runtimeControlPlane.initialize();
    }
    get runtimeModes() {
      return this.runtimeControlPlane.getRuntimeModes();
    }
    get hookDebugConfig() {
      return this.runtimeControlPlane.getHookDebugConfig();
    }
    isHookModeEnabled(target) {
      return this.runtimeControlPlane.isHookModeEnabled(target);
    }
    refreshHookDebugConfig() {
      this.runtimeControlPlane.refreshHookDebugConfig();
    }
    emitHookDiag(phase, payload, options2) {
      this.runtimeControlPlane.emitHookDiag(phase, payload, options2);
    }
    enableSafeMode(reason, error) {
      this.runtimeControlPlane.enableSafeMode(reason, error);
    }
    applyRuntimeModesFromOptions() {
      this.runtimeControlPlane.applyRuntimeModesFromOptions();
    }
    getRuntimeModesSnapshot() {
      return this.runtimeControlPlane.getRuntimeModesSnapshot();
    }
    getHookStatsSnapshot() {
      return this.runtimeControlPlane.getHookStatsSnapshot();
    }
    dispose() {
      if (this.disposed) return;
      this.disposed = true;
      try {
        this.getExtensions().forEach((ext) => {
          try {
            if (ext.enabled) {
              ext.enabled = false;
              ext.dispose();
            }
          } catch {
          }
        });
      } catch {
      }
      if (this.pageMessageHandler) {
        try {
          window.removeEventListener("message", this.pageMessageHandler, false);
        } catch {
        }
        this.pageMessageHandler = null;
      }
      this.runtimeControlPlane.dispose();
    }
    runHookSelfTest() {
      try {
        if (this.runtimeModes.safeMode || this.runtimeModes.hookMode === "off") {
          return { ok: true };
        }
        const hookTarget = getHookGlobalObject();
        if (this.isHookModeEnabled("xhr")) {
          const xhrCtor = hookTarget.XMLHttpRequest;
          if (!(xhrCtor == null ? void 0 : xhrCtor.prototype)) {
            return { ok: false, error: "XMLHttpRequest.prototype unavailable" };
          }
          if (typeof xhrCtor.prototype.open !== "function") {
            return { ok: false, error: "XMLHttpRequest.prototype.open unavailable" };
          }
          if (typeof xhrCtor.prototype.send !== "function") {
            return { ok: false, error: "XMLHttpRequest.prototype.send unavailable" };
          }
        }
        if (this.isHookModeEnabled("fetch")) {
          const fetchCandidate = hookTarget.fetch;
          if (typeof fetchCandidate !== "function") {
            return { ok: false, error: "fetch unavailable" };
          }
        }
        return { ok: true };
      } catch (err2) {
        return { ok: false, error: String(err2) };
      }
    }
    syncRuntimeStats() {
      if (!this.hookStats || !this.hookRuntime) return;
      this.hookRuntime.messagesTotal = this.hookStats.messagesTotal;
      this.hookRuntime.messagesLegacyShape = this.hookStats.messagesLegacyShape;
      this.hookRuntime.messagesMissingContext = this.hookStats.messagesMissingContext;
      this.hookRuntime.messagesRepairedAtBridge = this.hookStats.messagesRepairedAtBridge;
      this.hookRuntime.messagesMissingBody = this.hookStats.messagesMissingBody;
      this.hookRuntime.responsesProcessed = this.hookStats.responsesProcessed;
      this.hookRuntime.responsesSkippedDuplicate = this.hookStats.responsesSkippedDuplicate;
      this.hookRuntime.lastMessageAt = this.hookStats.lastMessageAt;
      this.hookRuntime.activeInstanceId = this.hookStats.activeInstanceId;
      this.hookRuntime.rev = this.hookStats.rev;
      this.hookRuntime.endpointStats = this.hookStats.endpointStats;
    }
    readHookStatsSnapshot() {
      if (!this.hookStats) return null;
      return {
        xhrMessages: Number(this.hookStats.xhrMessages || 0),
        fetchMessages: Number(this.hookStats.fetchMessages || 0),
        lastUrl: typeof this.hookStats.lastUrl === "string" ? this.hookStats.lastUrl : "",
        lastAt: Number(this.hookStats.lastAt || 0)
      };
    }
    getEndpointStats(key) {
      if (!this.hookStats) return createEndpointMetrics();
      const existing = this.hookStats.endpointStats[key];
      if (existing) return existing;
      if (Object.keys(this.hookStats.endpointStats).length >= this.endpointMetricLimit) {
        const keys = Object.keys(this.hookStats.endpointStats);
        const oldest = keys[0];
        if (oldest) {
          delete this.hookStats.endpointStats[oldest];
        }
      }
      const next = createEndpointMetrics();
      this.hookStats.endpointStats[key] = next;
      return next;
    }
    uninstallHooks() {
      try {
        const g = hookGlobalObject;
        const xhrCtor = g.XMLHttpRequest;
        const proto = xhrCtor == null ? void 0 : xhrCtor.prototype;
        if (proto) {
          if (typeof proto[ORIG_XHR_OPEN_KEY] === "function") {
            proto.open = proto[ORIG_XHR_OPEN_KEY];
          }
          if (typeof proto[ORIG_XHR_SEND_KEY] === "function") {
            proto.send = proto[ORIG_XHR_SEND_KEY];
          }
          delete proto.__twe_is_hook_open_v1;
          delete proto.__twe_is_hook_send_v1;
          delete proto.__twe_is_hook_revision_v1;
        }
        if (typeof g[ORIG_FETCH_KEY] === "function") {
          g.fetch = g[ORIG_FETCH_KEY];
        }
        if (typeof g.fetch === "object" && g.fetch !== null) {
          const fetchFn = g.fetch;
          delete fetchFn.__twe_is_hook_fetch_v1;
          delete fetchFn.__twe_is_hook_revision_v1;
        }
      } catch {
      }
    }
    applyBookmarkRouteCandidate(candidate) {
      var _a2;
      const now = Date.now();
      const currentRoute = typeof location !== "undefined" ? location.href : "";
      const routeIsBookmarks = isBookmarksRoute(currentRoute);
      const candidateIsBookmarks = (candidate == null ? void 0 : candidate.pageUrl) ? isBookmarksRoute(candidate.pageUrl) : routeIsBookmarks;
      if (!candidate) {
        if (routeIsBookmarks && ((_a2 = this.lastStickyBookmarkContext) == null ? void 0 : _a2.folderId)) {
          const keep = this.lastStickyBookmarkContext;
          if (now - keep.capturedAt <= BOOKMARK_CONTEXT_BOOKMARKS_ONLY_STALE_MS) {
            setBookmarkContext({
              folderId: keep.folderId,
              pageUrl: keep.pageUrl || currentRoute,
              source: keep.source,
              capturedAt: now
            });
            return;
          }
        }
        this.lastStickyBookmarkContext = null;
        setBookmarkContext({
          folderId: null,
          pageUrl: currentRoute,
          source: "refresh",
          capturedAt: now
        });
        return;
      }
      if (!candidateIsBookmarks) {
        this.lastStickyBookmarkContext = null;
        setBookmarkContext({
          folderId: null,
          pageUrl: candidate.pageUrl,
          source: candidate.source,
          capturedAt: now
        });
        return;
      }
      if (candidate.folderId) {
        const sticky = {
          folderId: candidate.folderId,
          pageUrl: candidate.pageUrl,
          source: candidate.source,
          capturedAt: now
        };
        this.lastStickyBookmarkContext = sticky;
        setBookmarkContext(sticky);
        return;
      }
      const lastSticky = this.lastStickyBookmarkContext;
      const allowFallback = lastSticky && !!lastSticky.folderId && candidate.confidence <= BOOKMARK_CONTEXT_MIN_CONFIDENCE && now - lastSticky.capturedAt <= BOOKMARK_CONTEXT_BOOKMARKS_ONLY_STALE_MS;
      if (!candidate.folderId && allowFallback) {
        setBookmarkContext({
          folderId: lastSticky.folderId,
          pageUrl: candidate.pageUrl,
          source: lastSticky.source,
          capturedAt: now
        });
        return;
      }
      if (!candidate.folderId && routeIsBookmarks && (lastSticky == null ? void 0 : lastSticky.folderId) && now - lastSticky.capturedAt <= BOOKMARK_CONTEXT_BOOKMARKS_ONLY_STALE_MS) {
        setBookmarkContext({
          folderId: lastSticky.folderId,
          pageUrl: candidate.pageUrl,
          source: lastSticky.source,
          capturedAt: now
        });
        return;
      }
      this.lastStickyBookmarkContext = null;
      setBookmarkContext({
        folderId: null,
        pageUrl: candidate.pageUrl,
        source: candidate.source,
        capturedAt: now
      });
    }
    updateBookmarkRouteContext() {
      try {
        const pageContext = captureBookmarkRouteFromPage();
        this.applyBookmarkRouteCandidate(pageContext ?? null);
      } catch {
      }
    }
    installBookmarkContextTracking() {
      if (this.runtimeModes.safeMode || this.runtimeModes.hookMode === "off") {
        return;
      }
      this.updateBookmarkRouteContext();
      const historyObj = hookGlobalObject.history;
      if (!historyObj) return;
      const refreshContext = () => this.updateBookmarkRouteContext();
      const applyNavigationCandidate = (candidate) => {
        this.applyBookmarkRouteCandidate(candidate);
      };
      if (!hookGlobalObject.__twe_bookmark_context_listeners_v1) {
        hookGlobalObject.__twe_bookmark_context_listeners_v1 = true;
        const listenerTargets = [hookGlobalObject, window, globalThis];
        for (const target of listenerTargets) {
          addEventListenerSafe(target, "popstate", refreshContext);
          addEventListenerSafe(target, "hashchange", refreshContext);
        }
      }
      if (!hookGlobalObject.__twe_bookmark_context_bookmark_click_v1 && (document == null ? void 0 : document.body)) {
        const onClick = (event) => {
          try {
            const target = event.target;
            const candidate = captureBookmarkRouteFromEventTarget(target);
            if (candidate) {
              applyNavigationCandidate(candidate);
            }
          } catch {
          }
        };
        hookGlobalObject.__twe_bookmark_context_bookmark_click_v1 = true;
        const clickTargets = [hookGlobalObject, window, document, globalThis];
        let clickListenerInstalled = false;
        for (const target of clickTargets) {
          clickListenerInstalled = addEventListenerSafe(target, "click", onClick, { capture: true }) || clickListenerInstalled;
        }
        if (!clickListenerInstalled && (document == null ? void 0 : document.body)) {
          addEventListenerSafe(document.body, "click", onClick, { capture: true });
        }
        hookGlobalObject.__twe_bookmark_context_bookmark_click_handler_v1 = onClick;
      }
      if (!hookGlobalObject.__twe_bookmark_context_interval_v1) {
        hookGlobalObject.__twe_bookmark_context_interval_v1 = setInterval(() => {
          try {
            this.updateBookmarkRouteContext();
          } catch {
          }
        }, 1200);
      }
      if (!hookGlobalObject.__twe_bookmark_context_mutation_v1) {
        const doc = document;
        const hasMutationObserver = typeof MutationObserver !== "undefined";
        if ((doc == null ? void 0 : doc.body) && hasMutationObserver) {
          const observer = new MutationObserver(() => {
            this.updateBookmarkRouteContext();
          });
          observer.observe(doc.body, {
            subtree: true,
            childList: true,
            attributes: true,
            attributeFilter: ["href", "aria-selected", "data-state", "class"]
          });
          hookGlobalObject.__twe_bookmark_context_mutation_v1 = observer;
        }
      }
    }
    /**
     * Register and instantiate a new extension.
     *
     * @param ctor Extension constructor.
     */
    add(ctor) {
      try {
        logger.debug(`Register new extension: ${ctor.name}`);
        const instance = new ctor(this);
        const previous = this.extensions.get(instance.name);
        if (previous && previous !== instance) {
          try {
            if (previous.enabled) {
              previous.dispose();
            }
          } catch {
          }
        }
        this.extensions.set(instance.name, instance);
      } catch (err2) {
        logger.error(`Failed to register extension: ${ctor.name}`, err2);
      }
    }
    /**
     * Set up all enabled extensions.
     */
    start() {
      for (const ext of this.extensions.values()) {
        if (this.disabledExtensions.has(ext.name)) {
          this.disable(ext.name);
        } else {
          this.enable(ext.name);
        }
      }
    }
    enable(name2) {
      try {
        this.disabledExtensions.delete(name2);
        appOptionsManager.set("disabledExtensions", [...this.disabledExtensions]);
        const ext = this.extensions.get(name2);
        if (ext.enabled) return;
        ext.enabled = true;
        ext.setup();
        logger.debug(`Enabled extension: ${name2}`);
        this.signal.value++;
      } catch (err2) {
        logger.error(`Failed to enable extension: ${name2}`, err2);
      }
    }
    disable(name2) {
      try {
        this.disabledExtensions.add(name2);
        appOptionsManager.set("disabledExtensions", [...this.disabledExtensions]);
        const ext = this.extensions.get(name2);
        if (!ext.enabled) return;
        ext.enabled = false;
        ext.dispose();
        logger.debug(`Disabled extension: ${name2}`);
        this.signal.value++;
      } catch (err2) {
        logger.error(`Failed to disable extension: ${name2}`, err2);
      }
    }
    isDisposed() {
      return this.disposed;
    }
    getExtensions() {
      return [...this.extensions.values()];
    }
    installPageMessageBridge() {
      if (this.runtimeModes.hookMode === "off") {
        return;
      }
      if (this.pageMessageHandler) return;
      this.pageMessageHandler = (event) => {
        var _a2, _b2, _c;
        try {
          const origin = event.origin || "";
          if (origin && !/^https:\/\/(x|twitter|mobile\.x)\.com$/.test(origin)) return;
          const data = event.data;
          if (!data || data[HOOK_MESSAGE_FLAG] !== true) return;
          const messageRevision = getBridgeMessageRevision(data);
          const isLegacyMessage = messageRevision === null || messageRevision !== HOOK_REVISION;
          const requestCandidate = pickBridgeRequest(data);
          const bridgeReq = normalizeHookPayloadRequest(requestCandidate);
          const res = data.res;
          if (!res) return;
          const rawEnvelopeRequest = requestCandidate && typeof requestCandidate === "object" ? requestCandidate : null;
          const requestId = typeof bridgeReq.requestId === "string" && bridgeReq.requestId.trim().length ? bridgeReq.requestId : typeof (rawEnvelopeRequest == null ? void 0 : rawEnvelopeRequest.requestId) === "string" && ((_a2 = rawEnvelopeRequest.requestId) == null ? void 0 : _a2.trim().length) ? rawEnvelopeRequest.requestId : nextBookmarkRequestId();
          const incomingMethod = typeof bridgeReq.method === "string" && bridgeReq.method ? bridgeReq.method : typeof (rawEnvelopeRequest == null ? void 0 : rawEnvelopeRequest.method) === "string" ? rawEnvelopeRequest.method : "GET";
          const incomingUrl = typeof bridgeReq.url === "string" && bridgeReq.url.length ? bridgeReq.url : typeof (rawEnvelopeRequest == null ? void 0 : rawEnvelopeRequest.url) === "string" ? rawEnvelopeRequest.url : "";
          if (!incomingUrl) return;
          const incomingBody = typeof bridgeReq.body === "string" ? bridgeReq.body : typeof (rawEnvelopeRequest == null ? void 0 : rawEnvelopeRequest.body) === "string" ? rawEnvelopeRequest.body : void 0;
          const requestContextFromLegacyWrapper = rawEnvelopeRequest ? rawEnvelopeRequest.bookmarkContext : void 0;
          const requestContextFromAlternative = rawEnvelopeRequest && Object.prototype.hasOwnProperty.call(rawEnvelopeRequest, "requestContext") ? rawEnvelopeRequest.requestContext : void 0;
          const hasRequestContext = requestContextFromLegacyWrapper !== void 0 && requestContextFromLegacyWrapper !== null || requestContextFromAlternative !== void 0 && requestContextFromAlternative !== null || bridgeReq.bookmarkContext !== void 0;
          const hasBodyField = typeof bridgeReq.body === "string" || typeof (rawEnvelopeRequest == null ? void 0 : rawEnvelopeRequest.body) === "string";
          const hasRequestIdField = typeof bridgeReq.requestId === "string" && bridgeReq.requestId.trim().length > 0 || typeof (rawEnvelopeRequest == null ? void 0 : rawEnvelopeRequest.requestId) === "string" && (((_b2 = rawEnvelopeRequest.requestId) == null ? void 0 : _b2.trim().length) || 0) > 0;
          const req = buildNormalizedHookMessageRequest({
            method: incomingMethod,
            url: incomingUrl,
            body: incomingBody,
            bookmarkContext: bridgeReq.bookmarkContext ?? requestContextFromLegacyWrapper ?? requestContextFromAlternative,
            requestId
          });
          const bridgeRepaired = isLegacyMessage || !hasRequestContext || !hasBodyField || !hasRequestIdField;
          const isBookmarksMessage = isBookmarksApiRequest(req.url);
          const endpointKey = isBookmarksMessage ? extractBookmarksEndpoint(req.url) : null;
          const endpointStats = endpointKey ? this.getEndpointStats(endpointKey) : null;
          const now = Date.now();
          const responseText = typeof res.responseText === "string" ? res.responseText : "";
          if (!this.hookStats) return;
          if (bridgeRepaired) {
            this.hookStats.messagesRepairedAtBridge++;
          }
          if (!hasBodyField) {
            this.hookStats.messagesMissingBody++;
          }
          if (isLegacyMessage) {
            this.hookStats.messagesLegacyShape++;
          }
          if (endpointStats) {
            endpointStats.received += 1;
            endpointStats.lastAt = now;
            endpointStats.lastStatus = res.status ?? 0;
            endpointStats.lastUrl = req.url;
          }
          if (!hasRequestContext) {
            this.hookStats.messagesLegacyShape++;
            if (endpointStats) endpointStats.legacyShape += 1;
          }
          if (!hasRequestContext || req.bookmarkContext === null) {
            this.hookStats.messagesMissingContext++;
            if (endpointStats) endpointStats.missingContext += 1;
            req.bookmarkContext = resolveRequestBookmarkContext(req.url, req.body, {
              method: req.method,
              url: req.url,
              body: req.body,
              requestId
            });
          } else {
            req.bookmarkContext = normalizeBookmarkContextValue(req.bookmarkContext, {
              method: req.method,
              url: req.url,
              body: req.body,
              requestId,
              hasBody: !!req.body
            });
          }
          setBookmarkContext(req.bookmarkContext);
          appendBookmarkContextDump({
            requestId,
            ts: Date.now(),
            method: req.method,
            url: req.url,
            hasBody: !!req.body,
            confidenceSource: typeof ((_c = req.bookmarkContext) == null ? void 0 : _c.source) === "string" ? req.bookmarkContext.source : "unknown",
            context: req.bookmarkContext,
            normalizedRoute: resolveCanonicalRouteFromUrl(req.url).pageUrl
          });
          const method = (req.method || "GET").toUpperCase();
          const isBookmarkApiRequest = isBookmarksApiRequest(req.url);
          if (!isBookmarkApiRequest) {
            const dedupeKey = createRequestSignature(method, req.url, res.status ?? 0, responseText);
            const prev = this.recentResponseSigs.get(dedupeKey);
            if (prev && now - prev.at < RESPONSE_DEDUPE_WINDOW_MS) {
              this.hookStats.responsesSkippedDuplicate++;
              if (endpointStats) endpointStats.skippedDuplicate += 1;
              this.syncRuntimeStats();
              return;
            }
            this.recentResponseSigs.set(dedupeKey, { sig: dedupeKey, at: now });
            cleanupSignatureCache(this.recentResponseSigs);
          }
          this.hookStats.responsesProcessed++;
          if (endpointStats) {
            endpointStats.processed += 1;
            endpointStats.newUniqueTweets += countUniqueTweetIds(responseText);
          }
          this.hookStats.messagesTotal++;
          this.hookStats.lastMessageAt = now;
          if (this.hookStats) {
            this.hookStats.lastUrl = req.url;
            this.hookStats.lastAt = Date.now();
            if ((req.method || "").toUpperCase() === "GET") this.hookStats.xhrMessages++;
            else this.hookStats.fetchMessages++;
            if (this.debugEnabled && this.hookStats.loggedUrls < 5) {
              this.hookStats.loggedUrls++;
              logger.debug("Hook saw request", {
                method: req.method,
                url: req.url,
                status: res.status
              });
            }
            this.syncRuntimeStats();
          }
          const pseudoXhr = {
            status: res.status,
            responseText
          };
          this.runInterceptors(req, pseudoXhr);
        } catch (err2) {
          logger.debug("Failed to process hook message", err2);
        }
      };
      window.addEventListener("message", this.pageMessageHandler, false);
    }
    /**
     * Here we hooks the browser's XHR method to intercept Twitter's Web API calls.
     * This need to be done before any XHR request is made.
     */
    installHttpHooks(force = false) {
      var _a2, _b2, _c;
      if (!this.isHookModeEnabled("xhr")) {
        return;
      }
      this.refreshHookDebugConfig();
      const hookDebug = this.hookDebugConfig;
      this.emitHookDiag("xhr.install.begin", { force, ...hookDebug });
      if (hookDebug.disableXhrLoadListener) {
        this.emitHookDiag("xhr.load.listener.disabled", {}, { force: true });
      }
      let hookInstalled = false;
      let originalOpen;
      try {
        if (!((_a2 = hookGlobalObject.XMLHttpRequest) == null ? void 0 : _a2.prototype) || !((_c = (_b2 = hookGlobalObject.XMLHttpRequest) == null ? void 0 : _b2.prototype) == null ? void 0 : _c.open)) {
          throw new Error("XMLHttpRequest.prototype.open not available");
        }
        const proto = hookGlobalObject.XMLHttpRequest.prototype;
        const currentOpen = proto.open;
        const currentSend = proto.send;
        originalOpen = typeof proto[ORIG_XHR_OPEN_KEY] === "function" ? proto[ORIG_XHR_OPEN_KEY] : currentOpen;
        if (!proto[ORIG_XHR_OPEN_KEY] && typeof currentOpen === "function") {
          proto[ORIG_XHR_OPEN_KEY] = currentOpen;
        }
        if (!proto[ORIG_XHR_SEND_KEY] && typeof currentSend === "function") {
          proto[ORIG_XHR_SEND_KEY] = currentSend;
        }
        const wrappedSendFromState = getFunctionFromHookState(
          currentSend,
          "__twe_is_hook_send_v1",
          ORIG_XHR_SEND_KEY
        );
        const sendBase = typeof wrappedSendFromState === "function" ? wrappedSendFromState : typeof proto[ORIG_XHR_SEND_KEY] === "function" ? proto[ORIG_XHR_SEND_KEY] : currentSend;
        if (typeof sendBase !== "function") {
          throw new Error("XMLHttpRequest.prototype.send not available");
        }
        const sendNeedsRepair = !hookDebug.disableXhrSendWrap && (force || !hasHookVersion(currentSend, "__twe_is_hook_send_v1"));
        if (hookDebug.disableXhrSendWrap) {
          if (typeof proto[ORIG_XHR_SEND_KEY] === "function") {
            proto.send = proto[ORIG_XHR_SEND_KEY];
          }
          hookInstalled = true;
          this.emitHookDiag("xhr.send.wrap.disabled", {}, { force: true });
        } else if (sendNeedsRepair) {
          const emitHookDiag = this.emitHookDiag.bind(this);
          const enableSafeMode = this.enableSafeMode.bind(this);
          const sendWrapper = function(body) {
            let reqUrl = "";
            let reqMethod = "GET";
            let requestId = "";
            try {
              const xhr = this;
              const meta = loadXhrRequestMeta(xhr, hookDebug);
              reqMethod = String(meta.method || "GET");
              requestId = meta.requestId || nextBookmarkRequestId();
              meta.requestId = requestId;
              meta.body = serializeRequestBodyText(body) ?? "";
              reqUrl = String(meta.url || "");
              if (reqUrl) {
                const requestMeta = {
                  method: reqMethod,
                  url: reqUrl,
                  body: meta.body,
                  requestId
                };
                if (isBookmarksApiRequest(reqUrl)) {
                  meta.bookmarkContext = resolveRequestBookmarkContext(
                    reqUrl,
                    meta.body,
                    requestMeta
                  );
                  setBookmarkContext(meta.bookmarkContext);
                } else {
                  meta.bookmarkContext = null;
                }
              }
              storeXhrRequestMeta(xhr, hookDebug, meta);
            } catch {
            }
            try {
              if (hookDebug.forceCallNotApply) {
                sendBase.call(this, body);
              } else {
                sendBase.apply(this, [body]);
              }
              emitHookDiag("xhr.send.basecall.ok", {
                method: reqMethod,
                url: reqUrl
              });
            } catch {
              try {
                const recoveredResult = hookDebug.forceCallNotApply ? sendBase.apply(this, [body]) : sendBase.call(this, body);
                emitHookDiag(
                  "xhr.send.basecall.recovered",
                  {
                    method: reqMethod,
                    url: reqUrl
                  },
                  { force: true }
                );
                return recoveredResult;
              } catch (fallbackErr) {
                emitHookDiag(
                  "xhr.send.basecall.error",
                  {
                    method: reqMethod,
                    url: reqUrl,
                    requestId,
                    errName: fallbackErr instanceof Error ? fallbackErr.name : typeof fallbackErr,
                    errMsg: fallbackErr instanceof Error ? fallbackErr.message : String(fallbackErr)
                  },
                  { force: true }
                );
                logger.error("XHR send hook base invocation failed; enabling safe mode", {
                  method: reqMethod,
                  url: reqUrl,
                  err: fallbackErr
                });
                try {
                  enableSafeMode("xhr-send-basecall-failed", fallbackErr);
                } catch {
                }
                throw fallbackErr;
              }
            }
          };
          markHookFunction(sendWrapper, "__twe_is_hook_send_v1");
          const sendInstalled = defineOn(proto, "send", sendWrapper);
          if (!sendInstalled) {
            throw new Error("Failed to define XMLHttpRequest.send hook safely");
          }
          markHookFunction(proto.send, "__twe_is_hook_send_v1");
          hookInstalled = true;
        } else {
          hookInstalled = true;
        }
        if (!force && !hookDebug.disableXhrOpenWrap && hasHookVersion(currentOpen, "__twe_is_hook_open_v1") && (hookDebug.disableXhrSendWrap || hasHookVersion(currentSend, "__twe_is_hook_send_v1"))) {
          hookInstalled = true;
        } else {
          const wrappedOpenFromState = getFunctionFromHookState(
            currentOpen,
            "__twe_is_hook_open_v1",
            ORIG_XHR_OPEN_KEY
          );
          const openBase = typeof wrappedOpenFromState === "function" ? wrappedOpenFromState : typeof proto[ORIG_XHR_OPEN_KEY] === "function" ? proto[ORIG_XHR_OPEN_KEY] : currentOpen;
          if (typeof openBase !== "function") {
            throw new Error("XMLHttpRequest.prototype.open base function unavailable");
          }
          if (hookDebug.disableXhrOpenWrap) {
            proto.open = openBase;
            this.emitHookDiag("xhr.open.wrap.disabled", {}, { force: true });
            hookInstalled = true;
          } else {
            const emitHookDiag = this.emitHookDiag.bind(this);
            const enableSafeMode = this.enableSafeMode.bind(this);
            const openWrapper = function(...args) {
              let reqMethod = "";
              let reqUrl = "";
              let requestId = "";
              try {
                reqMethod = typeof args[0] === "string" ? args[0] : String(args[0] ?? "");
                const rawUrl = args[1];
                reqUrl = typeof rawUrl === "string" ? rawUrl : String(rawUrl ?? "");
                if (isCaptureCandidateApiUrl(reqUrl)) {
                  const self2 = this;
                  const meta = loadXhrRequestMeta(self2, hookDebug);
                  requestId = meta.requestId || nextBookmarkRequestId();
                  meta.requestId = requestId;
                  meta.method = reqMethod;
                  meta.url = reqUrl;
                  meta.body = "";
                  if (isBookmarksApiRequest(reqUrl)) {
                    meta.bookmarkContext = resolveRequestBookmarkContext(reqUrl, void 0, {
                      method: reqMethod,
                      url: reqUrl,
                      requestId: meta.requestId
                    });
                    setBookmarkContext(meta.bookmarkContext);
                  } else {
                    meta.bookmarkContext = null;
                  }
                  if (!hookDebug.disableXhrLoadListener && !meta.hooked) {
                    meta.hooked = true;
                    this.addEventListener("load", function() {
                      try {
                        const xhr = this;
                        const reqMeta = loadXhrRequestMeta(xhr, hookDebug);
                        const methodFallback = reqMethod || "GET";
                        const urlFallback = reqUrl;
                        const loadMethod = reqMeta.method || methodFallback;
                        const loadUrl = reqMeta.url || urlFallback;
                        if (!isCaptureCandidateApiUrl(loadUrl)) return;
                        const responseText = String(this.responseText ?? "");
                        const loadBody = reqMeta.body;
                        const loadRequestId = reqMeta.requestId || nextBookmarkRequestId();
                        reqMeta.requestId = loadRequestId;
                        const bookmarkRequest = isBookmarksApiRequest(loadUrl);
                        const requestContext = reqMeta.bookmarkContext || (bookmarkRequest ? resolveRequestBookmarkContext(loadUrl, loadBody, {
                          method: loadMethod,
                          url: loadUrl,
                          body: loadBody,
                          requestId: loadRequestId
                        }) : null);
                        if (!reqMeta.bookmarkContext) {
                          reqMeta.bookmarkContext = requestContext;
                        }
                        storeXhrRequestMeta(xhr, hookDebug, reqMeta);
                        if (bookmarkRequest && requestContext) {
                          setBookmarkContext(requestContext);
                        }
                        const normalizedReq = buildNormalizedHookMessageRequest({
                          method: loadMethod,
                          url: loadUrl,
                          body: loadBody || "",
                          bookmarkContext: requestContext ?? null,
                          requestId: loadRequestId
                        });
                        postHookMessage({
                          __twe_mcp_hook_v1: true,
                          req: normalizedReq,
                          res: { status: this.status ?? 0, responseText }
                        });
                      } catch {
                      }
                    });
                  }
                  storeXhrRequestMeta(self2, hookDebug, meta);
                }
              } catch {
              }
              try {
                const result = hookDebug.forceCallNotApply ? callFunctionWithArgs(openBase, this, args) : openBase.apply(this, args);
                emitHookDiag("xhr.open.basecall.ok", {
                  method: reqMethod,
                  url: reqUrl
                });
                return result;
              } catch {
                try {
                  const recoveredResult = hookDebug.forceCallNotApply ? openBase.apply(this, args) : callFunctionWithArgs(openBase, this, args);
                  emitHookDiag(
                    "xhr.open.basecall.recovered",
                    {
                      method: reqMethod,
                      url: reqUrl
                    },
                    { force: true }
                  );
                  return recoveredResult;
                } catch (fallbackErr) {
                  emitHookDiag(
                    "xhr.open.basecall.error",
                    {
                      method: reqMethod,
                      url: reqUrl,
                      requestId,
                      errName: fallbackErr instanceof Error ? fallbackErr.name : typeof fallbackErr,
                      errMsg: fallbackErr instanceof Error ? fallbackErr.message : String(fallbackErr)
                    },
                    { force: true }
                  );
                  logger.error("XHR open hook base invocation failed; enabling safe mode", {
                    method: reqMethod,
                    url: reqUrl,
                    err: fallbackErr
                  });
                  try {
                    enableSafeMode("xhr-open-basecall-failed", fallbackErr);
                  } catch {
                  }
                  throw fallbackErr;
                }
              }
            };
            markHookFunction(openWrapper, "__twe_is_hook_open_v1");
            const openInstalled = defineOn(proto, "open", openWrapper);
            if (!openInstalled) {
              throw new Error("Failed to define XMLHttpRequest.open hook safely");
            }
            hookInstalled = true;
            markHookFunction(proto.open, "__twe_is_hook_open_v1");
            markHookFunction(proto.send, "__twe_is_hook_send_v1");
          }
          proto[ORIG_XHR_OPEN_KEY] = openBase;
          proto[ORIG_XHR_SEND_KEY] = sendBase;
        }
      } catch (err2) {
        logger.error("Failed to hook into XMLHttpRequest", err2);
        this.enableSafeMode("xhr-hook-install-failed", err2);
      }
      if (this.debugEnabled) {
        logger.info(`Hooked into XMLHttpRequest (installed=${hookInstalled})`);
      }
      setTimeout(() => {
        var _a3, _b3;
        try {
          const capabilities = getRuntimeCapabilities();
          const openIsPatched = ((_b3 = (_a3 = hookGlobalObject.XMLHttpRequest) == null ? void 0 : _a3.prototype) == null ? void 0 : _b3.open) !== (typeof originalOpen === "function" ? originalOpen : void 0);
          if (!openIsPatched) {
            logger.error(
              `XHR hook not active (unsafeWindow=${capabilities.hasUnsafeWindow}, wrappedJSObject=${capabilities.hasWrappedJSObject}, exportFunction=${capabilities.hasExportFunction}). Bookmark capture will not work.`
            );
          } else if (this.debugEnabled) {
            logger.debug("XHR hook active", {
              unsafeWindow: capabilities.hasUnsafeWindow,
              wrappedJSObject: capabilities.hasWrappedJSObject,
              exportFunction: capabilities.hasExportFunction
            });
          }
        } catch (err2) {
          logger.debug("XHR hook diagnostics failed", err2);
        }
      }, 1e3);
    }
    installFetchHooks(force = false) {
      if (!this.isHookModeEnabled("fetch")) {
        return;
      }
      this.refreshHookDebugConfig();
      const hookDebug = this.hookDebugConfig;
      this.emitHookDiag("fetch.install.begin", { force, ...hookDebug });
      const hookTarget = getHookGlobalObject();
      const fetchNative = hookTarget.fetch;
      if (typeof fetchNative !== "function") {
        logger.warn("Fetch API not found, skipping fetch hooks");
        return;
      }
      const pageAny = hookTarget;
      const existingFetch = hookTarget.fetch;
      if (!force && hasHookVersion(existingFetch, "__twe_is_hook_fetch_v1")) {
        logger.debug("Fetch hook already installed");
        return;
      }
      const existingStoredBase = pageAny[ORIG_FETCH_KEY];
      const fetchBaseFromState = getFunctionFromHookState(
        existingFetch,
        "__twe_is_hook_fetch_v1",
        ORIG_FETCH_KEY
      );
      const fetchBaseCandidate = [existingStoredBase, fetchBaseFromState, fetchNative].find(isUsableFetchBaseCandidate) ?? null;
      if (!fetchBaseCandidate) {
        logger.error("Fetch API base function unavailable or unsafe; enabling safe mode");
        this.enableSafeMode("fetch-hook-base-unavailable");
        return;
      }
      const fetchBase = fetchBaseCandidate;
      if (hookDebug.disableFetchWrap) {
        hookTarget.fetch = fetchBase;
        this.emitHookDiag("fetch.wrap.disabled", {}, { force: true });
        return;
      }
      pageAny[ORIG_FETCH_KEY] = fetchBase;
      const emitHookDiag = this.emitHookDiag.bind(this);
      const enableSafeMode = this.enableSafeMode.bind(this);
      let fetchHookFatal = false;
      let preferredFetchContext = hookTarget;
      const callNativeFetchFallback = (input, init, argCount = 2) => {
        try {
          if (argCount <= 1) {
            return fetchBase(input);
          }
          return fetchBase(input, init);
        } catch (fallbackErr) {
          return Promise.reject(fallbackErr);
        }
      };
      const invokeFetchWithContexts = (fn, contexts, input, init, argCount = 2) => {
        const args = argCount <= 1 ? [input] : [input, init];
        let lastError = null;
        for (const ctx of contexts) {
          if (!ctx) continue;
          try {
            const response = callFunctionWithArgs(
              fn,
              ctx,
              args
            );
            preferredFetchContext = ctx;
            return response;
          } catch (err2) {
            lastError = err2;
          }
          if (!hookDebug.forceCallNotApply) {
            try {
              const response = Reflect.apply(
                fn,
                ctx,
                args
              );
              preferredFetchContext = ctx;
              return response;
            } catch (err2) {
              lastError = err2;
            }
          }
        }
        try {
          if (argCount <= 1) {
            return fn(input);
          }
          return fn(input, init);
        } catch (err2) {
          lastError = err2;
        }
        if (lastError instanceof Error) {
          throw lastError;
        }
        throw new Error(`fetch invocation failed (${getSafeErrorInfo(lastError).summary})`);
      };
      const fetchWrapper = function(input, init) {
        const fetchArgCount = arguments.length <= 1 ? 1 : 2;
        let method = "GET";
        let url = "";
        let serializedBody;
        let requestId = "";
        let requestContext;
        try {
          method = extractFetchLikeMethod(input, init);
        } catch {
          method = (init == null ? void 0 : init.method) ?? "GET";
        }
        try {
          url = extractFetchLikeUrl(input);
        } catch {
          url = "";
        }
        try {
          serializedBody = serializeRequestBodyText(init == null ? void 0 : init.body);
        } catch {
          serializedBody = void 0;
        }
        try {
          requestId = nextBookmarkRequestId();
        } catch {
          requestId = "";
        }
        emitHookDiag("fetch.wrapper.enter", { method, url, requestId });
        try {
          if (isBookmarksApiRequest(url)) {
            requestContext = resolveRequestBookmarkContext(url, serializedBody, {
              method,
              url,
              body: serializedBody,
              requestId
            });
            setBookmarkContext(requestContext);
          } else {
            requestContext = void 0;
          }
        } catch (err2) {
          logger.debug("fetch request context capture failed", { method, url, err: err2 });
        }
        const shouldObserveResponse = isCaptureCandidateApiUrl(url);
        if (!shouldObserveResponse) {
          return callNativeFetchFallback(input, init, fetchArgCount);
        }
        let responsePromise;
        try {
          const origFetch = pageAny[ORIG_FETCH_KEY] ?? fetchBase;
          if (typeof origFetch !== "function" || hasHookShape(origFetch, "__twe_is_hook_fetch_v1")) {
            throw new Error("fetch base function unavailable");
          }
          const callContextsRaw = [this, preferredFetchContext, hookTarget];
          if (typeof window !== "undefined") {
            callContextsRaw.push(window);
          }
          callContextsRaw.push(globalThis);
          const callContexts = [];
          const seenContexts = /* @__PURE__ */ new Set();
          for (const ctx of callContextsRaw) {
            if (!ctx || seenContexts.has(ctx)) continue;
            seenContexts.add(ctx);
            callContexts.push(ctx);
          }
          responsePromise = invokeFetchWithContexts(
            origFetch,
            callContexts,
            input,
            init,
            fetchArgCount
          );
        } catch (err2) {
          const errInfo = getSafeErrorInfo(err2);
          emitHookDiag("fetch.basecall.error", {
            method,
            url,
            requestId,
            errName: errInfo.name,
            errMsg: errInfo.message
          });
          if (!fetchHookFatal) {
            fetchHookFatal = true;
            logger.error("Fetch hook base invocation failed; enabling safe mode", {
              method,
              url,
              err: errInfo.summary
            });
            try {
              enableSafeMode("fetch-hook-invocation-failed", errInfo.summary);
            } catch {
            }
          }
          return callNativeFetchFallback(input, init, fetchArgCount).catch((fallbackErr) => {
            const fallbackInfo = getSafeErrorInfo(fallbackErr);
            emitHookDiag(
              "fetch.fallback.error",
              {
                method,
                url,
                requestId,
                errName: fallbackInfo.name,
                errMsg: fallbackInfo.message
              },
              { force: true }
            );
            throw fallbackErr ?? errInfo.summary;
          });
        }
        try {
          void responsePromise.then(
            (response) => {
              try {
                emitHookDiag("fetch.basecall.ok", { method, url });
                const contentType = response.headers.get("content-type") ?? "";
                const isTextualResponse = !contentType || contentType.includes("json") || contentType.startsWith("text/");
                if (!isTextualResponse) {
                  return;
                }
                void response.clone().text().then((responseText) => {
                  if (!responseText) return;
                  try {
                    const normalizedReq = buildNormalizedHookMessageRequest({
                      method,
                      url,
                      body: serializedBody || "",
                      bookmarkContext: requestContext ?? null,
                      requestId
                    });
                    postHookMessage({
                      __twe_mcp_hook_v1: true,
                      req: normalizedReq,
                      res: { status: response.status, responseText }
                    });
                  } catch {
                  }
                }).catch((err2) => {
                  const errInfo = getSafeErrorInfo(err2);
                  logger.debug("fetch clone.text() failed", {
                    method,
                    url,
                    err: errInfo.summary
                  });
                });
              } catch (err2) {
                const errInfo = getSafeErrorInfo(err2);
                logger.debug("fetch response hook observer callback failed", {
                  method,
                  url,
                  err: errInfo.summary
                });
              }
            },
            (err2) => {
              const errInfo = getSafeErrorInfo(err2);
              emitHookDiag("fetch.basecall.error", {
                method,
                url,
                requestId,
                errName: errInfo.name,
                errMsg: errInfo.message
              });
              if (!fetchHookFatal) {
                fetchHookFatal = true;
                logger.error("Fetch hook base invocation failed; enabling safe mode", {
                  method,
                  url,
                  err: errInfo.summary
                });
                try {
                  enableSafeMode("fetch-hook-invocation-failed", errInfo.summary);
                } catch {
                }
              }
            }
          ).catch((err2) => {
            const errInfo = getSafeErrorInfo(err2);
            logger.debug("fetch response hook observer promise failed", {
              method,
              url,
              err: errInfo.summary
            });
          });
        } catch (err2) {
          const errInfo = getSafeErrorInfo(err2);
          logger.debug("fetch response hook observer setup failed", {
            method,
            url,
            err: errInfo.summary
          });
        }
        return responsePromise;
      };
      markHookFunction(fetchWrapper, "__twe_is_hook_fetch_v1");
      const ok = defineOn(pageAny, "fetch", fetchWrapper);
      if (!ok) {
        this.enableSafeMode("fetch-hook-define-failed");
        return;
      }
      markHookFunction(pageAny.fetch, "__twe_is_hook_fetch_v1");
      this.emitHookDiag(
        "fetch.install.ok",
        {
          hasHookMarker: hasHookVersion(
            pageAny.fetch,
            "__twe_is_hook_fetch_v1"
          )
        },
        { force: true }
      );
      if (ok && this.debugEnabled) {
        logger.info("Hooked into fetch");
      }
      if (ok) {
        this.startFetchHookBootProbe(1200);
      }
    }
    runFetchHookBootProbePass() {
      if (this.runtimeModes.safeMode || !this.isHookModeEnabled("fetch")) {
        return;
      }
      const hookTarget = getHookGlobalObject();
      const fetchCandidate = hookTarget.fetch;
      if (typeof fetchCandidate !== "function") {
        this.enableSafeMode("fetch-hook-probe-missing-fetch");
        return;
      }
      this.emitHookDiag(
        "fetch.bootprobe.begin",
        {
          hasHookMarker: hasHookVersion(fetchCandidate, "__twe_is_hook_fetch_v1")
        },
        { force: true }
      );
      const probe = async () => {
        const contexts = [hookTarget];
        if (typeof window !== "undefined") {
          contexts.push(window);
        }
        contexts.push(globalThis);
        const probeUrl = typeof location !== "undefined" && location.origin ? `${location.origin}/favicon.ico?__twe_fetch_probe=1` : "https://x.com/favicon.ico?__twe_fetch_probe=1";
        let lastError = null;
        try {
          const response = await fetchCandidate(probeUrl);
          await response.text().catch(() => "");
          if (this.debugEnabled) {
            logger.debug("Fetch hook boot probe passed");
          }
          this.emitHookDiag("fetch.bootprobe.ok", {});
          return;
        } catch (err2) {
          lastError = err2;
        }
        for (const ctx of contexts) {
          try {
            const response = this.hookDebugConfig.forceCallNotApply ? await fetchCandidate.call(ctx, probeUrl) : await Reflect.apply(
              fetchCandidate,
              ctx,
              [probeUrl]
            );
            await response.text().catch(() => "");
            if (this.debugEnabled) {
              logger.debug("Fetch hook boot probe passed");
            }
            this.emitHookDiag("fetch.bootprobe.ok", {});
            return;
          } catch (err2) {
            lastError = err2;
          }
        }
        const lastErrorInfo = getSafeErrorInfo(lastError);
        this.emitHookDiag(
          "fetch.bootprobe.error",
          {
            errName: lastErrorInfo.name,
            errMsg: lastErrorInfo.message
          },
          { force: true }
        );
        if (isLikelyCrossRealmPermissionError(lastError)) {
          logger.warn(
            "Fetch boot probe hit cross-realm permission error; keeping fetch hook active",
            lastErrorInfo.summary
          );
          return;
        }
        this.enableSafeMode("fetch-hook-probe-failed", lastError);
      };
      void probe();
    }
    startFetchHookBootProbe(delayMs = 1200) {
      this.runtimeControlPlane.startFetchHookBootProbe(delayMs);
    }
    runHookRepairPass() {
      if (this.hookStats) {
        this.hookStats.repairCount += 1;
        this.syncRuntimeStats();
      }
      if (this.isHookModeEnabled("xhr")) {
        this.installHttpHooks(true);
      }
      if (this.isHookModeEnabled("fetch")) {
        this.installFetchHooks(true);
      }
      return this.runHookSelfTest();
    }
    runInterceptors(req, res) {
      this.interceptorDispatcher.dispatch(this.getExtensions(), req, res);
    }
  }
  var ExtensionType = /* @__PURE__ */ ((ExtensionType2) => {
    ExtensionType2["TWEET"] = "tweet";
    ExtensionType2["USER"] = "user";
    ExtensionType2["CUSTOM"] = "custom";
    ExtensionType2["NONE"] = "none";
    return ExtensionType2;
  })(ExtensionType || {});
  class Extension {
    constructor(manager) {
      __publicField(this, "name", "");
      __publicField(this, "enabled", true);
      __publicField(this, "type", "none");
      __publicField(this, "manager");
      this.manager = manager;
    }
    /**
     * Optionally run side effects when enabled.
     */
    setup() {
    }
    /**
     * Optionally clear side effects when disabled.
     */
    dispose() {
    }
    /**
     * Intercept HTTP responses.
     */
    intercept() {
      return null;
    }
    /**
     * Render extension UI.
     */
    render() {
      return null;
    }
  }
  let extensionManager = null;
  function getExtensionManager() {
    if (extensionManager && !extensionManager.isDisposed()) {
      return extensionManager;
    }
    extensionManager = new ExtensionManager();
    return extensionManager;
  }
  const extensionManagerProxy = new Proxy({}, {
    get(_target, prop, receiver) {
      const manager = getExtensionManager();
      const value = Reflect.get(manager, prop, receiver);
      return typeof value === "function" ? value.bind(manager) : value;
    },
    set(_target, prop, value, receiver) {
      const manager = getExtensionManager();
      return Reflect.set(manager, prop, value, receiver);
    }
  });
  function useWorkspaceShellState(openControlPanelLabel) {
    const extensions = signals.useSignal(extensionManagerProxy.getExtensions());
    const currentTheme = signals.useSignal(appOptionsManager.get("theme"));
    const showControlPanel = signals.useSignal(appOptionsManager.get("showControlPanel"));
    const hookStats = signals.useSignal(null);
    const runtimeModes = signals.useSignal(null);
    const rawCaptureStats = signals.useSignal(null);
    const toggleControlPanel = () => {
      showControlPanel.value = !showControlPanel.value;
      appOptionsManager.set("showControlPanel", showControlPanel.value);
    };
    hooks.useEffect(() => {
      extensions.value = extensionManagerProxy.getExtensions();
      currentTheme.value = appOptionsManager.get("theme");
      showControlPanel.value = appOptionsManager.get("showControlPanel");
      extensionManagerProxy.signal.subscribe(() => {
        extensions.value = extensionManagerProxy.getExtensions();
      });
      appOptionsManager.signal.subscribe(() => {
        currentTheme.value = appOptionsManager.get("theme");
        try {
          extensionManagerProxy.applyRuntimeModesFromOptions();
          runtimeModes.value = extensionManagerProxy.getRuntimeModesSnapshot();
        } catch {
          runtimeModes.value = null;
        }
      });
      try {
        extensionManagerProxy.applyRuntimeModesFromOptions();
      } catch {
      }
      if (typeof _GM_registerMenuCommand === "function") {
        _GM_registerMenuCommand(openControlPanelLabel, toggleControlPanel);
      }
      let timer = null;
      const refresh = () => {
        try {
          hookStats.value = extensionManagerProxy.getHookStatsSnapshot();
        } catch {
          hookStats.value = null;
        }
        try {
          runtimeModes.value = extensionManagerProxy.getRuntimeModesSnapshot();
        } catch {
          runtimeModes.value = null;
        }
        try {
          rawCaptureStats.value = globalThis.__twe_raw_capture_stats_v1;
        } catch {
          rawCaptureStats.value = null;
        }
      };
      const schedule = () => {
        const delay = typeof document !== "undefined" && document.hidden ? 8e3 : 2e3;
        timer = globalThis.setTimeout(() => {
          refresh();
          schedule();
        }, delay);
      };
      refresh();
      schedule();
      logger.debug("Workspace shell state effect executed");
      return () => {
        if (timer !== null) {
          globalThis.clearTimeout(timer);
        }
      };
    }, []);
    return {
      extensions,
      currentTheme,
      showControlPanel,
      hookStats,
      runtimeModes,
      rawCaptureStats,
      toggleControlPanel
    };
  }
  function ScrollmarkIcon(props) {
    return /* @__PURE__ */ u("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 100 100", "aria-hidden": "true", ...props, children: /* @__PURE__ */ u(
      "path",
      {
        fill: "currentColor",
        d: "m82.7 88.4c1.8-2.2 4.1-5.4 4.1-9.9s-1.9-7.3-4.5-9.8c1.8-3.2 6.6-5.4 9.8-5.4 0.8 0 2.2-1 2.2-2.8 0-3.4-3.3-7.3-7.9-7.3-1.1 0-1.9-0.1-1.9-3.9 0-0.6 0.1-1.4 0.1-2.4 6 0 14.3-3.1 14.3-11.4 0-4.7-4.6-13.6-13.8-18.7l-0.1-0.1v-0.1c0.3-5.9-1.6-8.9-4-8.9-2.5 0-4.4 3-5 4.6-1.4-3-3.9-6.5-7.1-6.5-2.2 0-4.4 2.1-4.4 6.6 0 2.8 0.7 5.8 1.7 8.1-2.5 3.4-4.6 7.9-4.6 15.1 0 8.7-9.9 8.8-16.6 20.5-3.9 6.4-4 12.5-3.8 18.6-1.4-1.8-1.8-7.3-0.7-12.2 2.2-8.7 9.4-12.8 12-22.9 0.7-2.5 1-5.1 1-7.9 0-12.5-9.5-28.2-27.2-28.2-13.6 0-24.8 11.3-24.8 24.5 0 5.5 1.7 10.9 5 13.7 1 1.1 2.3 1.4 2.5 0 0.5-2.3 3-5.7 7.3-5.7 4 0 7.6 2.3 7.6 6.6 0 4.7-4.1 8.2-6.5 16.5-0.7 2.4-1.1 5-1.1 7.9 0 16.1 11.7 29.4 32.3 29.4h33c4.3 0 6.6-0.6 6.6-2.8 0-2.4-2.1-4.3-5.5-5.2zm1-59.5c1.2 0 2.2 1 2.2 2.2s-0.9 2.2-2.2 2.5c-1.2 0-2.3-1-2.4-2.4 0-1.2 1.1-2.3 2.4-2.3z"
      }
    ) });
  }
  function ControlPanelLauncher({ currentTheme, onToggle }) {
    return /* @__PURE__ */ u(
      "div",
      {
        onClick: onToggle,
        "data-theme": currentTheme,
        class: "group w-12 h-12 fixed top-[60%] left-[-20px] cursor-pointer bg-transparent text-base-content",
        children: /* @__PURE__ */ u("div", { class: "w-full h-full origin origin-[bottom_center] transition-all duration-200 group-hover:translate-x-[5px] group-hover:rotate-[20deg] opacity-50 group-hover:opacity-90", children: /* @__PURE__ */ u(ScrollmarkIcon, { class: "h-full w-full" }) })
      }
    );
  }
  function extractDataFromResponse(response, extractInstructionsFromJson, extractDataFromTimelineEntry) {
    const json = JSON.parse(response.responseText);
    const instructions = extractInstructionsFromJson(json);
    const timelineEntries = extractTimelineItemEntries(instructions);
    const seenRestIds = /* @__PURE__ */ new Set();
    const newData = [];
    for (const entry of timelineEntries) {
      const data = extractDataFromTimelineEntry(entry);
      if (!data || seenRestIds.has(data.rest_id)) continue;
      seenRestIds.add(data.rest_id);
      newData.push(data);
    }
    return newData;
  }
  function extractTimelineItemEntries(instructions) {
    var _a2, _b2, _c, _d;
    const entries = [];
    for (const instruction of instructions) {
      if (instruction.type === "TimelineAddEntries") {
        const addEntriesInstruction = instruction;
        for (const entry of addEntriesInstruction.entries ?? []) {
          if (isTimelineEntryItem(entry)) {
            entries.push(entry);
          }
        }
        continue;
      }
      if (instruction.type === "TimelinePinEntry") {
        const pinInstruction = instruction;
        const entry = pinInstruction.entry;
        if (entry && isTimelineEntryItem(entry)) {
          entries.push(entry);
        }
        continue;
      }
      if (instruction.type === "TimelineAddToModule") {
        const addToModuleInstruction = instruction;
        for (const moduleItem of addToModuleInstruction.moduleItems ?? []) {
          const itemContent = (_a2 = moduleItem == null ? void 0 : moduleItem.item) == null ? void 0 : _a2.itemContent;
          if (!itemContent) continue;
          entries.push({
            entryId: moduleItem.entryId ?? "",
            sortIndex: "0",
            content: {
              entryType: "TimelineTimelineItem",
              __typename: "TimelineTimelineItem",
              itemContent,
              clientEventInfo: ((_b2 = moduleItem == null ? void 0 : moduleItem.item) == null ? void 0 : _b2.clientEventInfo) ?? null
            }
          });
        }
        continue;
      }
      const instAny = instruction;
      if (Array.isArray(instAny == null ? void 0 : instAny.entries)) {
        for (const entry of instAny.entries) {
          if (entry && isTimelineEntryItem(entry)) {
            entries.push(entry);
          }
        }
        continue;
      }
      if ((instAny == null ? void 0 : instAny.entry) && isTimelineEntryItem(instAny.entry)) {
        entries.push(instAny.entry);
        continue;
      }
      if (Array.isArray(instAny == null ? void 0 : instAny.moduleItems)) {
        for (const moduleItem of instAny.moduleItems ?? []) {
          const itemContent = (_c = moduleItem == null ? void 0 : moduleItem.item) == null ? void 0 : _c.itemContent;
          if (!itemContent) continue;
          entries.push({
            entryId: moduleItem.entryId ?? "",
            sortIndex: "0",
            content: {
              entryType: "TimelineTimelineItem",
              __typename: "TimelineTimelineItem",
              itemContent,
              clientEventInfo: ((_d = moduleItem == null ? void 0 : moduleItem.item) == null ? void 0 : _d.clientEventInfo) ?? null
            }
          });
        }
        continue;
      }
    }
    return entries;
  }
  function extractTimelineTweet(itemContent) {
    const tweetUnion = itemContent.tweet_results.result;
    if (!tweetUnion) {
      logger.warn(
        "TimelineTweet is empty. This could happen when the tweet's visibility is limited by Twitter.",
        itemContent
      );
      return null;
    }
    return extractTweetUnion(tweetUnion);
  }
  function extractTimelineUser(itemContent) {
    const user = itemContent.user_results.result;
    if (!user || user.__typename !== "User") {
      logger.warn(
        "TimelineUser is empty. This could happen when the user's account is suspended or deleted.",
        itemContent
      );
      return null;
    }
    return user;
  }
  function isTimelineEntryItem(entry) {
    return entry.content.entryType === "TimelineTimelineItem";
  }
  function isTimelineEntryTweet(entry) {
    return isTimelineEntryItem(entry) && entry.entryId.startsWith("tweet-") && entry.content.itemContent.__typename === "TimelineTweet";
  }
  function isTimelineEntryUser(entry) {
    return isTimelineEntryItem(entry) && entry.entryId.startsWith("user-") && entry.content.itemContent.__typename === "TimelineUser";
  }
  function isTimelineEntryModule(entry) {
    return entry.content.entryType === "TimelineTimelineModule";
  }
  function isTimelineEntryConversationThread(entry) {
    return isTimelineEntryModule(entry) && entry.entryId.startsWith("conversationthread-") && Array.isArray(entry.content.items);
  }
  function isTimelineEntryProfileConversation(entry) {
    return isTimelineEntryModule(entry) && entry.entryId.startsWith("profile-conversation-") && Array.isArray(entry.content.items);
  }
  function isTimelineEntryProfileGrid(entry) {
    return isTimelineEntryModule(entry) && entry.entryId.startsWith("profile-grid-") && Array.isArray(entry.content.items);
  }
  function isTimelineEntrySearchGrid(entry) {
    return isTimelineEntryModule(entry) && entry.entryId.startsWith("search-grid-") && Array.isArray(entry.content.items);
  }
  function isTimelineEntryListSearch(entry) {
    return isTimelineEntryModule(entry) && entry.entryId.startsWith("list-search-") && Array.isArray(entry.content.items);
  }
  function isTimelineEntryCommunitiesGrid(entry) {
    return isTimelineEntryModule(entry) && entry.entryId.startsWith("communities-grid-") && Array.isArray(entry.content.items);
  }
  function extractTweetUnion(tweet) {
    var _a2, _b2;
    try {
      if (tweet.__typename === "Tweet") {
        return filterEmptyTweet(tweet);
      }
      if (tweet.__typename === "TweetWithVisibilityResults") {
        return filterEmptyTweet(tweet.tweet);
      }
      if (tweet.__typename === "TweetTombstone") {
        logger.warn(`TweetTombstone received (Reason: ${(_b2 = (_a2 = tweet.tombstone) == null ? void 0 : _a2.text) == null ? void 0 : _b2.text})`, tweet);
        return null;
      }
      if (tweet.__typename === "TweetUnavailable") {
        logger.warn("TweetUnavailable received (Reason: unknown)", tweet);
        return null;
      }
      logger.debug(tweet);
      logger.errorWithBanner("Unknown tweet type received");
    } catch (err2) {
      logger.debug(tweet);
      logger.errorWithBanner("Failed to extract tweet", err2);
    }
    return null;
  }
  function extractRetweetedTweet(tweet) {
    var _a2;
    if ((_a2 = tweet.legacy.retweeted_status_result) == null ? void 0 : _a2.result) {
      return extractTweetUnion(tweet.legacy.retweeted_status_result.result);
    }
    return null;
  }
  function extractQuotedTweet(tweet) {
    var _a2;
    if ((_a2 = tweet.quoted_status_result) == null ? void 0 : _a2.result) {
      return extractTweetUnion(tweet.quoted_status_result.result);
    }
    return null;
  }
  function extractTweetArticle(tweet) {
    var _a2, _b2;
    const article = (_b2 = (_a2 = tweet.article) == null ? void 0 : _a2.article_results) == null ? void 0 : _b2.result;
    return article && typeof article === "object" ? article : null;
  }
  function extractArticleBlockText(article) {
    var _a2;
    const blocks = (_a2 = article == null ? void 0 : article.content_state) == null ? void 0 : _a2.blocks;
    if (!Array.isArray(blocks)) return [];
    const parts = [];
    for (const block of blocks) {
      const text = typeof (block == null ? void 0 : block.text) === "string" ? block.text.trim() : "";
      if (!text) continue;
      parts.push(text);
    }
    return parts;
  }
  function normalizeSyntheticImageUrl(url) {
    const value = String(url || "").trim();
    if (!value) return "";
    return value.replace(/\?(?:format|name)=[^&]+(?:&name=[^&]+)?$/i, "");
  }
  function buildSyntheticPhotoMedia(params) {
    const originalUrl = String(params.url || "").trim();
    if (!originalUrl) return null;
    const tweetUrl = getTweetURL(params.tweet);
    const mediaId = String(
      params.mediaId || `${params.idPrefix || "synthetic"}_${params.tweet.rest_id}`
    ).trim();
    const mediaKey = String(params.mediaKey || `${params.idPrefix || "synthetic"}_${mediaId}`).trim();
    const width = Number(params.width || 0) || 0;
    const height = Number(params.height || 0) || 0;
    return {
      display_url: originalUrl,
      expanded_url: tweetUrl,
      id_str: mediaId || params.tweet.rest_id,
      indices: [0, 0],
      media_url_https: originalUrl,
      type: "photo",
      url: originalUrl,
      sizes: {
        large: { h: height, w: width, resize: "fit" },
        medium: { h: height, w: width, resize: "fit" },
        small: { h: height, w: width, resize: "fit" },
        thumb: { h: height, w: width, resize: "fit" }
      },
      original_info: {
        height,
        width
      },
      media_results: {
        result: {
          media_key: mediaKey || `synthetic_${params.tweet.rest_id}`
        }
      },
      ext_alt_text: String(params.altText || "").trim() || void 0,
      media_key: mediaKey || `synthetic_${params.tweet.rest_id}`
    };
  }
  function buildSyntheticArticleMedia(tweet, article) {
    var _a2, _b2, _c, _d, _e, _f;
    if (!article) return [];
    const deduped = /* @__PURE__ */ new Map();
    const pushMedia = (media) => {
      if (!media) return;
      const key = normalizeSyntheticImageUrl(media.media_url_https);
      if (!key || deduped.has(key)) return;
      deduped.set(key, media);
    };
    for (const entity of article.media_entities ?? []) {
      pushMedia(
        buildSyntheticPhotoMedia({
          tweet,
          url: ((_a2 = entity == null ? void 0 : entity.media_info) == null ? void 0 : _a2.original_img_url) || "",
          mediaId: (entity == null ? void 0 : entity.media_id) || (entity == null ? void 0 : entity.id) || (article == null ? void 0 : article.rest_id) || tweet.rest_id,
          mediaKey: (entity == null ? void 0 : entity.media_key) || null,
          width: ((_b2 = entity == null ? void 0 : entity.media_info) == null ? void 0 : _b2.original_img_width) || 0,
          height: ((_c = entity == null ? void 0 : entity.media_info) == null ? void 0 : _c.original_img_height) || 0,
          altText: (article == null ? void 0 : article.title) || (article == null ? void 0 : article.preview_text) || null,
          idPrefix: "article"
        })
      );
    }
    const coverMedia = article.cover_media;
    pushMedia(
      buildSyntheticPhotoMedia({
        tweet,
        url: ((_d = coverMedia == null ? void 0 : coverMedia.media_info) == null ? void 0 : _d.original_img_url) || "",
        mediaId: (coverMedia == null ? void 0 : coverMedia.media_id) || (article == null ? void 0 : article.rest_id) || tweet.rest_id,
        mediaKey: (coverMedia == null ? void 0 : coverMedia.media_key) || null,
        width: ((_e = coverMedia == null ? void 0 : coverMedia.media_info) == null ? void 0 : _e.original_img_width) || 0,
        height: ((_f = coverMedia == null ? void 0 : coverMedia.media_info) == null ? void 0 : _f.original_img_height) || 0,
        altText: (article == null ? void 0 : article.title) || (article == null ? void 0 : article.preview_text) || null,
        idPrefix: "article"
      })
    );
    return Array.from(deduped.values());
  }
  function extractCardImageMedia(tweet) {
    const cardCandidates = [tweet.card, tweet.unified_card];
    const urls = /* @__PURE__ */ new Map();
    const visit = (value) => {
      if (!value) return;
      if (Array.isArray(value)) {
        for (const item of value) visit(item);
        return;
      }
      if (typeof value !== "object") return;
      const obj = value;
      for (const [key, nested] of Object.entries(obj)) {
        if (key === "url" && typeof nested === "string" && /https:\/\/pbs\.twimg\.com\/(?:card_img|media)\//.test(nested)) {
          const canonical = normalizeSyntheticImageUrl(nested);
          if (canonical && !urls.has(canonical)) {
            urls.set(canonical, nested);
          }
          continue;
        }
        visit(nested);
      }
    };
    for (const candidate of cardCandidates) {
      visit(candidate);
    }
    let index = 0;
    return Array.from(urls.values()).slice(0, 12).map(
      (url) => buildSyntheticPhotoMedia({
        tweet,
        url,
        mediaId: `${tweet.rest_id}_${index++}`,
        mediaKey: `card_${tweet.rest_id}_${index}`,
        altText: extractTweetFullText(tweet),
        idPrefix: "card"
      })
    ).filter((media) => !!media);
  }
  function extractTweetCreatedAtMs(tweet) {
    var _a2, _b2, _c;
    const legacyCreatedAt = (_a2 = tweet.legacy) == null ? void 0 : _a2.created_at;
    if (typeof legacyCreatedAt === "string" && legacyCreatedAt.trim()) {
      const parsed = +parseTwitterDateTime(legacyCreatedAt);
      if (Number.isFinite(parsed) && parsed > 0) {
        return parsed;
      }
    }
    const articleCreatedAtSecs = Number(
      ((_c = (_b2 = extractTweetArticle(tweet)) == null ? void 0 : _b2.metadata) == null ? void 0 : _c.first_published_at_secs) || 0
    );
    if (Number.isFinite(articleCreatedAtSecs) && articleCreatedAtSecs > 0) {
      return articleCreatedAtSecs * 1e3;
    }
    return Date.now();
  }
  function extractTweetUserScreenName(tweet) {
    return tweet.core.user_results.result.core.screen_name;
  }
  function extractTweetMedia(tweet) {
    var _a2, _b2, _c, _d;
    const realTweet = extractRetweetedTweet(tweet) ?? tweet;
    if ((_a2 = realTweet.legacy.extended_entities) == null ? void 0 : _a2.media) {
      return realTweet.legacy.extended_entities.media;
    }
    const legacyMedia = ((_c = (_b2 = realTweet.legacy) == null ? void 0 : _b2.entities) == null ? void 0 : _c.media) ?? [];
    if (legacyMedia.length) {
      return legacyMedia;
    }
    const articleMedia = buildSyntheticArticleMedia(realTweet, extractTweetArticle(realTweet));
    if (articleMedia.length) {
      return articleMedia;
    }
    const expectedMediaCount = Number(((_d = realTweet.twe_private_fields) == null ? void 0 : _d.media_count) || 0) || 0;
    if (expectedMediaCount > 0) {
      const cardMedia = extractCardImageMedia(realTweet);
      if (cardMedia.length) {
        return cardMedia;
      }
    }
    return [];
  }
  function hasOwnTweetMedia(tweet) {
    if (extractRetweetedTweet(tweet)) {
      return false;
    }
    return extractTweetMedia(tweet).length > 0;
  }
  function extractTweetMediaTags(tweet) {
    const media = extractTweetMedia(tweet);
    const dedupedTags = [];
    for (const item of media) {
      const tags = getMediaTags(item);
      for (const tag of tags) {
        if (dedupedTags.some((t) => t.user_id === tag.user_id)) {
          continue;
        }
        dedupedTags.push(tag);
      }
    }
    return dedupedTags;
  }
  function extractTweetFullText(tweet) {
    var _a2, _b2;
    const noteTweetText = (_a2 = tweet.note_tweet) == null ? void 0 : _a2.note_tweet_results.result.text;
    if (noteTweetText && noteTweetText.trim()) {
      return noteTweetText;
    }
    const legacyText = (_b2 = tweet.legacy) == null ? void 0 : _b2.full_text;
    if (legacyText && legacyText.trim()) {
      return legacyText;
    }
    const article = extractTweetArticle(tweet);
    const parts = [article == null ? void 0 : article.title, article == null ? void 0 : article.preview_text, ...extractArticleBlockText(article)].map((value) => String(value || "").trim()).filter(Boolean);
    const deduped = parts.filter((value, index) => parts.indexOf(value) === index);
    return deduped.join("\n\n");
  }
  function filterEmptyTweet(tweet) {
    var _a2, _b2;
    if (!tweet.legacy) {
      const article = extractTweetArticle(tweet);
      if (!article || !((_b2 = (_a2 = tweet.core) == null ? void 0 : _a2.user_results) == null ? void 0 : _b2.result)) {
        logger.warn("Empty tweet received", tweet);
        return null;
      }
      const createdAtMs = extractTweetCreatedAtMs(tweet);
      const fullText = extractTweetFullText(tweet);
      const syntheticMedia = buildSyntheticArticleMedia(tweet, article);
      const userRestId = tweet.core.user_results.result.rest_id || String(tweet.core.user_results.result.id || "").split(":").pop() || "";
      tweet.legacy = {
        bookmark_count: 0,
        bookmarked: false,
        created_at: new Date(createdAtMs).toUTCString(),
        conversation_id_str: tweet.rest_id,
        display_text_range: [0, fullText.length],
        entities: {
          media: syntheticMedia.length ? syntheticMedia : void 0,
          user_mentions: [],
          urls: [],
          hashtags: [],
          symbols: [],
          timestamps: []
        },
        extended_entities: syntheticMedia.length ? {
          media: syntheticMedia
        } : void 0,
        favorite_count: 0,
        favorited: false,
        full_text: fullText,
        is_quote_status: false,
        lang: "und",
        possibly_sensitive: false,
        possibly_sensitive_editable: false,
        quote_count: 0,
        reply_count: 0,
        retweet_count: 0,
        retweeted: false,
        user_id_str: userRestId,
        id_str: tweet.rest_id
      };
    }
    return tweet;
  }
  function getMediaIndex(tweet, media) {
    const key = media.media_key;
    return extractTweetMedia(tweet).findIndex((value) => value.media_key === key);
  }
  function getMediaOriginalUrl(media) {
    var _a2;
    if (media.type === "video" || media.type === "animated_gif") {
      const variants = ((_a2 = media.video_info) == null ? void 0 : _a2.variants) ?? [];
      let maxBitrateVariant = variants[0];
      for (const variant of variants) {
        if (variant.bitrate && variant.bitrate > ((maxBitrateVariant == null ? void 0 : maxBitrateVariant.bitrate) ?? 0)) {
          maxBitrateVariant = variant;
        }
      }
      return (maxBitrateVariant == null ? void 0 : maxBitrateVariant.url) ?? media.media_url_https;
    }
    return formatTwitterImage(media.media_url_https, "orig");
  }
  function getMediaTags(media) {
    var _a2, _b2;
    return ((_b2 = (_a2 = media.features) == null ? void 0 : _a2.all) == null ? void 0 : _b2.tags) ?? [];
  }
  function formatTwitterImage(imgUrl, name2 = "medium") {
    if (!imgUrl) return "";
    try {
      const parsed = new URL(imgUrl);
      if (parsed.hostname === "pbs.twimg.com") {
        const format = parsed.searchParams.get("format");
        const pathnameMatch = parsed.pathname.match(/^(\/media\/.+)\.(\w+)$/);
        if (pathnameMatch) {
          const [, pathWithoutExtension, ext2] = pathnameMatch;
          parsed.pathname = pathWithoutExtension || parsed.pathname;
          parsed.search = "";
          parsed.searchParams.set("format", ext2 || format || "jpg");
          parsed.searchParams.set("name", name2);
          return parsed.toString();
        }
        if (format) {
          parsed.searchParams.set("name", name2);
          return parsed.toString();
        }
        if (parsed.searchParams.has("name")) {
          parsed.searchParams.set("name", name2);
          return parsed.toString();
        }
      }
    } catch {
    }
    const regex = /^(https?:\/\/pbs\.twimg\.com\/media\/.+)\.(\w+)$/;
    const match = imgUrl.match(regex);
    if (!match) {
      const separator = imgUrl.includes("?") ? "&" : "?";
      return `${imgUrl}${separator}name=${name2}`;
    }
    const [, url, ext] = match;
    return `${url}?format=${ext}&name=${name2}`;
  }
  function getProfileImageOriginalUrl(url) {
    return url.replace(/_normal\.(jpe?g|png|gif)$/, ".$1");
  }
  function getFileExtensionFromUrl(url) {
    const regex = /format=(\w+)|\.(\w+)$|\.(\w+)\?.+$/;
    const match = regex.exec(url);
    return (match == null ? void 0 : match[1]) ?? (match == null ? void 0 : match[2]) ?? (match == null ? void 0 : match[3]) ?? "jpg";
  }
  function getTweetURL(tweet) {
    var _a2;
    const tweetId = String(((_a2 = tweet.legacy) == null ? void 0 : _a2.id_str) || tweet.rest_id || "").trim();
    return `https://twitter.com/${extractTweetUserScreenName(tweet)}/status/${tweetId}`;
  }
  function getUserURL(user) {
    return `https://twitter.com/${typeof user === "string" ? user : user.core.screen_name}`;
  }
  function getInReplyToTweetURL(tweet) {
    return `https://twitter.com/${tweet.legacy.in_reply_to_screen_name}/status/${tweet.legacy.in_reply_to_status_id_str}`;
  }
  function ExtensionPanel({
    title,
    description: description2,
    children,
    onClick,
    active,
    indicatorColor = "bg-secondary",
    panelClass,
    contentClass,
    buttonClass
  }) {
    return /* @__PURE__ */ u(
      "section",
      {
        class: cx(
          "module-panel -ml-2 overflow-visible pl-2 transition-colors duration-150",
          panelClass
        ),
        children: [
          /* @__PURE__ */ u("div", { class: cx("h-12 flex items-center justify-start", contentClass), children: [
            /* @__PURE__ */ u("div", { class: "relative flex h-4 w-4 mr-3 shrink-0", children: [
              active && /* @__PURE__ */ u(
                "span",
                {
                  class: cx(
                    "animate-ping absolute inline-flex h-full w-full rounded-full opacity-75",
                    indicatorColor
                  )
                }
              ),
              /* @__PURE__ */ u("span", { class: cx("relative inline-flex rounded-full h-4 w-4", indicatorColor) })
            ] }),
            /* @__PURE__ */ u("div", { class: "flex flex-col flex-grow", children: [
              /* @__PURE__ */ u("p", { class: "text-base m-0 font-medium leading-none", children: title }),
              /* @__PURE__ */ u("p", { class: "text-sm text-base-content leading-5 text-opacity-70 m-0", children: description2 })
            ] }),
            /* @__PURE__ */ u("button", { class: cx("btn btn-sm p-0 w-9 h-9", buttonClass), onClick, children: /* @__PURE__ */ u(IconArrowUpRight, {}) })
          ] }),
          children
        ]
      }
    );
  }
  function Modal({
    show,
    onClose,
    title,
    children,
    class: className,
    fullscreen
  }) {
    if (!show) {
      return /* @__PURE__ */ u("dialog", { class: "modal" });
    }
    return /* @__PURE__ */ u("dialog", { class: cx("modal modal-open z-[6000]", fullscreen && "!p-0"), open: true, children: [
      /* @__PURE__ */ u(
        "div",
        {
          class: cx(
            "modal-box p-3 flex min-h-0 flex-col",
            fullscreen && "h-screen w-screen max-h-screen max-w-none rounded-none border-0 p-0 shadow-none",
            className
          ),
          children: [
            /* @__PURE__ */ u(
              "header",
              {
                class: cx(
                  "flex items-center h-8 mb-1.5",
                  fullscreen && "mb-0 h-11 border-b border-base-300 px-3"
                ),
                children: [
                  /* @__PURE__ */ u(
                    "div",
                    {
                      onClick: onClose,
                      class: "w-8 h-8 mr-2 cursor-pointer flex justify-center items-center transition-colors duration-200 rounded-full hover:bg-base-200",
                      children: /* @__PURE__ */ u(IconX, {})
                    }
                  ),
                  /* @__PURE__ */ u("h2", { class: "leading-none text-lg m-0 font-semibold tracking-[0.01em]", children: title })
                ]
              }
            ),
            /* @__PURE__ */ u(ErrorBoundary, { children })
          ]
        }
      ),
      /* @__PURE__ */ u("form", { method: "dialog", class: "modal-backdrop", children: /* @__PURE__ */ u("div", { onClick: onClose }) })
    ] });
  }
  function MultiSelect(props) {
    const { options: options2, selected, onChange } = props;
    const selectedOptions = options2.filter((option) => selected.includes(option.value));
    const onInputChange = (e) => {
      const target = e.target;
      if (target.checked) {
        onChange([.../* @__PURE__ */ new Set([...selected, target.value])]);
      } else {
        onChange(selected.filter((value) => value !== target.value));
      }
    };
    return /* @__PURE__ */ u("div", { class: cx("dropdown", props.class), children: [
      /* @__PURE__ */ u(
        "div",
        {
          tabIndex: 0,
          class: "input input-bordered input-sm flex flex-row items-center space-x-1 cursor-pointer min-h-9",
          children: selectedOptions.length ? /* @__PURE__ */ u("div", { class: "min-w-0 truncate text-xs font-medium", children: props.selectedSummary ? props.selectedSummary(selectedOptions.length) : `${selectedOptions.length} selected` }) : /* @__PURE__ */ u("span", { class: "text-xs opacity-60 select-none", children: props.placeholder || "Select..." })
        }
      ),
      /* @__PURE__ */ u(
        "ul",
        {
          tabIndex: 0,
          class: "dropdown-content menu menu-sm z-10 w-full rounded-box bg-base-100 p-2 shadow",
          children: options2.map((option) => /* @__PURE__ */ u("li", { children: /* @__PURE__ */ u("label", { class: "label cursor-pointer justify-start", children: [
            /* @__PURE__ */ u(
              "input",
              {
                type: "checkbox",
                class: "checkbox checkbox-accent checkbox-sm",
                value: option.value,
                checked: selected.includes(option.value),
                onChange: onInputChange
              }
            ),
            /* @__PURE__ */ u("span", { class: "label-text ml-1", children: option.label })
          ] }) }, option.value))
        }
      )
    ] });
  }
  function MediaDisplayColumn({ data, onClick }) {
    return /* @__PURE__ */ u("div", { class: "flex flex-row items-start space-x-1 w-max", children: [
      data.map((media) => {
        var _a2;
        return /* @__PURE__ */ u(
          "div",
          {
            class: "flex-shrink-0 block cursor-pointer relative w-12 h-12 rounded bg-base-300 overflow-hidden",
            onClick: () => onClick(media),
            children: [
              /* @__PURE__ */ u(
                "img",
                {
                  class: "w-full h-full object-cover",
                  src: formatTwitterImage(media.media_url_https, "thumb"),
                  alt: media.ext_alt_text || "",
                  title: media.ext_alt_text || "",
                  loading: "lazy",
                  decoding: "async"
                }
              ),
              media.type !== "photo" && /* @__PURE__ */ u("div", { class: "absolute bottom-0.5 left-0.5 h-4 w-max px-0.5 text-xs text-white bg-black bg-opacity-30 leading-4 text-center rounded", children: media.type === "video" ? formatVideoDuration((_a2 = media.video_info) == null ? void 0 : _a2.duration_millis) : "GIF" }),
              media.type === "photo" && media.ext_alt_text && /* @__PURE__ */ u("div", { class: "absolute bottom-0.5 left-0.5 h-4 w-max px-0.5 text-xs text-white bg-black bg-opacity-30 leading-4 text-center rounded", children: "ALT" })
            ]
          },
          media.media_key ?? media.id_str
        );
      }),
      data.length ? null : "N/A"
    ] });
  }
  const scriptRel = /* @__PURE__ */ (function detectScriptRel() {
    const relList = typeof document !== "undefined" && document.createElement("link").relList;
    return relList && relList.supports && relList.supports("modulepreload") ? "modulepreload" : "preload";
  })();
  const assetsURL = function(dep) {
    return "/" + dep;
  };
  const seen = {};
  const __vitePreload = function preload(baseModule, deps, importerUrl) {
    let promise = Promise.resolve();
    if (deps && deps.length > 0) {
      let allSettled2 = function(promises) {
        return Promise.all(
          promises.map(
            (p) => Promise.resolve(p).then(
              (value) => ({ status: "fulfilled", value }),
              (reason) => ({ status: "rejected", reason })
            )
          )
        );
      };
      document.getElementsByTagName("link");
      const cspNonceMeta = document.querySelector(
        "meta[property=csp-nonce]"
      );
      const cspNonce = (cspNonceMeta == null ? void 0 : cspNonceMeta.nonce) || (cspNonceMeta == null ? void 0 : cspNonceMeta.getAttribute("nonce"));
      promise = allSettled2(
        deps.map((dep) => {
          dep = assetsURL(dep);
          if (dep in seen) return;
          seen[dep] = true;
          const isCss = dep.endsWith(".css");
          const cssSelector = isCss ? '[rel="stylesheet"]' : "";
          if (document.querySelector(`link[href="${dep}"]${cssSelector}`)) {
            return;
          }
          const link = document.createElement("link");
          link.rel = isCss ? "stylesheet" : scriptRel;
          if (!isCss) {
            link.as = "script";
          }
          link.crossOrigin = "";
          link.href = dep;
          if (cspNonce) {
            link.setAttribute("nonce", cspNonce);
          }
          document.head.appendChild(link);
          if (isCss) {
            return new Promise((res, rej) => {
              link.addEventListener("load", res);
              link.addEventListener(
                "error",
                () => rej(new Error(`Unable to preload CSS for ${dep}`))
              );
            });
          }
        })
      );
    }
    function handlePreloadError(err2) {
      const e = new Event("vite:preloadError", {
        cancelable: true
      });
      e.payload = err2;
      window.dispatchEvent(e);
      if (!e.defaultPrevented) {
        throw err2;
      }
    }
    return promise.then((res) => {
      for (const item of res || []) {
        if (item.status !== "rejected") continue;
        handlePreloadError(item.reason);
      }
      return baseModule().catch(handlePreloadError);
    });
  };
  const FALLBACK_HASH_MOD = 4294967296;
  function fallbackHash(value) {
    let hash = 2166136261;
    for (let index = 0; index < value.length; index += 1) {
      hash ^= value.charCodeAt(index);
      hash = Math.imul(hash, 16777619) >>> 0;
    }
    return hash.toString(16).padStart(8, "0");
  }
  async function sha256Hex(value) {
    var _a2;
    const bytes = typeof value === "string" ? new TextEncoder().encode(value) : value;
    if ((_a2 = globalThis.crypto) == null ? void 0 : _a2.subtle) {
      const digestInput = bytes.buffer.slice(
        bytes.byteOffset,
        bytes.byteOffset + bytes.byteLength
      );
      const digest = await globalThis.crypto.subtle.digest("SHA-256", digestInput);
      return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
    }
    let accumulator = "";
    for (let offset = 0; offset < bytes.length; offset += 4096) {
      accumulator += fallbackHash(String.fromCharCode(...bytes.slice(offset, offset + 4096)));
    }
    return fallbackHash(`${bytes.length}:${accumulator}:${FALLBACK_HASH_MOD}`);
  }
  async function createBundleId(seed) {
    return `bundle_${(await sha256Hex(seed)).slice(0, 24)}`;
  }
  async function createBundleRecordId(bundleId, kind, sourceId) {
    return `record_${(await sha256Hex(`${bundleId}:${kind}:${sourceId}`)).slice(0, 32)}`;
  }
  const SAFE_SHARED_DEFAULT_PRIVACY = {
    includeViewerAccountId: false,
    includeSourceCaptureTimes: false,
    includeRawGraphQL: false,
    includePrivateNotes: false,
    includeMediaBlobs: false,
    visibility: "shared_safe"
  };
  function buildBundlePrivacySummary(options2) {
    return {
      visibility: options2.visibility,
      includesViewerAccountId: options2.includeViewerAccountId,
      includesSourceCaptureTimes: options2.includeSourceCaptureTimes,
      includesRawGraphQL: options2.includeRawGraphQL,
      includesPrivateNotes: options2.includePrivateNotes,
      includesMediaBlobs: options2.includeMediaBlobs,
      warnings: describeBundlePrivacyWarnings(options2)
    };
  }
  function describeBundlePrivacyWarnings(options2) {
    const warnings = [];
    if (options2.includeViewerAccountId) warnings.push("Includes the exporting account identifier.");
    if (options2.includeSourceCaptureTimes) warnings.push("Includes local capture/import timestamps.");
    if (options2.includeRawGraphQL)
      warnings.push("Includes raw API payloads that may contain unrelated account context.");
    if (options2.includePrivateNotes) warnings.push("Includes user-authored local notes or labels.");
    if (options2.includeMediaBlobs)
      warnings.push("Includes downloaded media files, increasing size and redistribution risk.");
    if (options2.visibility === "public" && warnings.length) {
      warnings.unshift("Public bundle includes fields that should be reviewed before sharing.");
    }
    return warnings;
  }
  var u8 = Uint8Array, u16 = Uint16Array, i32 = Int32Array;
  var fleb = new u8([
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    1,
    1,
    1,
    1,
    2,
    2,
    2,
    2,
    3,
    3,
    3,
    3,
    4,
    4,
    4,
    4,
    5,
    5,
    5,
    5,
    0,
    /* unused */
    0,
    0,
    /* impossible */
    0
  ]);
  var fdeb = new u8([
    0,
    0,
    0,
    0,
    1,
    1,
    2,
    2,
    3,
    3,
    4,
    4,
    5,
    5,
    6,
    6,
    7,
    7,
    8,
    8,
    9,
    9,
    10,
    10,
    11,
    11,
    12,
    12,
    13,
    13,
    /* unused */
    0,
    0
  ]);
  var clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
  var freb = function(eb, start) {
    var b = new u16(31);
    for (var i = 0; i < 31; ++i) {
      b[i] = start += 1 << eb[i - 1];
    }
    var r = new i32(b[30]);
    for (var i = 1; i < 30; ++i) {
      for (var j = b[i]; j < b[i + 1]; ++j) {
        r[j] = j - b[i] << 5 | i;
      }
    }
    return { b, r };
  };
  var _a = freb(fleb, 2), fl = _a.b, revfl = _a.r;
  fl[28] = 258, revfl[258] = 28;
  var _b = freb(fdeb, 0), fd = _b.b, revfd = _b.r;
  var rev = new u16(32768);
  for (var i = 0; i < 32768; ++i) {
    var x = (i & 43690) >> 1 | (i & 21845) << 1;
    x = (x & 52428) >> 2 | (x & 13107) << 2;
    x = (x & 61680) >> 4 | (x & 3855) << 4;
    rev[i] = ((x & 65280) >> 8 | (x & 255) << 8) >> 1;
  }
  var hMap = (function(cd, mb, r) {
    var s = cd.length;
    var i = 0;
    var l = new u16(mb);
    for (; i < s; ++i) {
      if (cd[i])
        ++l[cd[i] - 1];
    }
    var le = new u16(mb);
    for (i = 1; i < mb; ++i) {
      le[i] = le[i - 1] + l[i - 1] << 1;
    }
    var co;
    if (r) {
      co = new u16(1 << mb);
      var rvb = 15 - mb;
      for (i = 0; i < s; ++i) {
        if (cd[i]) {
          var sv = i << 4 | cd[i];
          var r_1 = mb - cd[i];
          var v = le[cd[i] - 1]++ << r_1;
          for (var m = v | (1 << r_1) - 1; v <= m; ++v) {
            co[rev[v] >> rvb] = sv;
          }
        }
      }
    } else {
      co = new u16(s);
      for (i = 0; i < s; ++i) {
        if (cd[i]) {
          co[i] = rev[le[cd[i] - 1]++] >> 15 - cd[i];
        }
      }
    }
    return co;
  });
  var flt = new u8(288);
  for (var i = 0; i < 144; ++i)
    flt[i] = 8;
  for (var i = 144; i < 256; ++i)
    flt[i] = 9;
  for (var i = 256; i < 280; ++i)
    flt[i] = 7;
  for (var i = 280; i < 288; ++i)
    flt[i] = 8;
  var fdt = new u8(32);
  for (var i = 0; i < 32; ++i)
    fdt[i] = 5;
  var flm = /* @__PURE__ */ hMap(flt, 9, 0), flrm = /* @__PURE__ */ hMap(flt, 9, 1);
  var fdm = /* @__PURE__ */ hMap(fdt, 5, 0), fdrm = /* @__PURE__ */ hMap(fdt, 5, 1);
  var max = function(a) {
    var m = a[0];
    for (var i = 1; i < a.length; ++i) {
      if (a[i] > m)
        m = a[i];
    }
    return m;
  };
  var bits = function(d, p, m) {
    var o = p / 8 | 0;
    return (d[o] | d[o + 1] << 8) >> (p & 7) & m;
  };
  var bits16 = function(d, p) {
    var o = p / 8 | 0;
    return (d[o] | d[o + 1] << 8 | d[o + 2] << 16) >> (p & 7);
  };
  var shft = function(p) {
    return (p + 7) / 8 | 0;
  };
  var slc = function(v, s, e) {
    if (s == null || s < 0)
      s = 0;
    if (e == null || e > v.length)
      e = v.length;
    return new u8(v.subarray(s, e));
  };
  var ec = [
    "unexpected EOF",
    "invalid block type",
    "invalid length/literal",
    "invalid distance",
    "stream finished",
    "no stream handler",
    ,
    "no callback",
    "invalid UTF-8 data",
    "extra field too long",
    "date not in range 1980-2099",
    "filename too long",
    "stream finishing",
    "invalid zip data"
    // determined by unknown compression method
  ];
  var err = function(ind, msg, nt) {
    var e = new Error(msg || ec[ind]);
    e.code = ind;
    if (Error.captureStackTrace)
      Error.captureStackTrace(e, err);
    if (!nt)
      throw e;
    return e;
  };
  var inflt = function(dat, st, buf, dict) {
    var sl = dat.length, dl = dict ? dict.length : 0;
    if (!sl || st.f && !st.l)
      return buf || new u8(0);
    var noBuf = !buf;
    var resize = noBuf || st.i != 2;
    var noSt = st.i;
    if (noBuf)
      buf = new u8(sl * 3);
    var cbuf = function(l2) {
      var bl = buf.length;
      if (l2 > bl) {
        var nbuf = new u8(Math.max(bl * 2, l2));
        nbuf.set(buf);
        buf = nbuf;
      }
    };
    var final = st.f || 0, pos = st.p || 0, bt = st.b || 0, lm = st.l, dm = st.d, lbt = st.m, dbt = st.n;
    var tbts = sl * 8;
    do {
      if (!lm) {
        final = bits(dat, pos, 1);
        var type2 = bits(dat, pos + 1, 3);
        pos += 3;
        if (!type2) {
          var s = shft(pos) + 4, l = dat[s - 4] | dat[s - 3] << 8, t = s + l;
          if (t > sl) {
            if (noSt)
              err(0);
            break;
          }
          if (resize)
            cbuf(bt + l);
          buf.set(dat.subarray(s, t), bt);
          st.b = bt += l, st.p = pos = t * 8, st.f = final;
          continue;
        } else if (type2 == 1)
          lm = flrm, dm = fdrm, lbt = 9, dbt = 5;
        else if (type2 == 2) {
          var hLit = bits(dat, pos, 31) + 257, hcLen = bits(dat, pos + 10, 15) + 4;
          var tl = hLit + bits(dat, pos + 5, 31) + 1;
          pos += 14;
          var ldt = new u8(tl);
          var clt = new u8(19);
          for (var i = 0; i < hcLen; ++i) {
            clt[clim[i]] = bits(dat, pos + i * 3, 7);
          }
          pos += hcLen * 3;
          var clb = max(clt), clbmsk = (1 << clb) - 1;
          var clm = hMap(clt, clb, 1);
          for (var i = 0; i < tl; ) {
            var r = clm[bits(dat, pos, clbmsk)];
            pos += r & 15;
            var s = r >> 4;
            if (s < 16) {
              ldt[i++] = s;
            } else {
              var c = 0, n = 0;
              if (s == 16)
                n = 3 + bits(dat, pos, 3), pos += 2, c = ldt[i - 1];
              else if (s == 17)
                n = 3 + bits(dat, pos, 7), pos += 3;
              else if (s == 18)
                n = 11 + bits(dat, pos, 127), pos += 7;
              while (n--)
                ldt[i++] = c;
            }
          }
          var lt = ldt.subarray(0, hLit), dt = ldt.subarray(hLit);
          lbt = max(lt);
          dbt = max(dt);
          lm = hMap(lt, lbt, 1);
          dm = hMap(dt, dbt, 1);
        } else
          err(1);
        if (pos > tbts) {
          if (noSt)
            err(0);
          break;
        }
      }
      if (resize)
        cbuf(bt + 131072);
      var lms = (1 << lbt) - 1, dms = (1 << dbt) - 1;
      var lpos = pos;
      for (; ; lpos = pos) {
        var c = lm[bits16(dat, pos) & lms], sym = c >> 4;
        pos += c & 15;
        if (pos > tbts) {
          if (noSt)
            err(0);
          break;
        }
        if (!c)
          err(2);
        if (sym < 256)
          buf[bt++] = sym;
        else if (sym == 256) {
          lpos = pos, lm = null;
          break;
        } else {
          var add = sym - 254;
          if (sym > 264) {
            var i = sym - 257, b = fleb[i];
            add = bits(dat, pos, (1 << b) - 1) + fl[i];
            pos += b;
          }
          var d = dm[bits16(dat, pos) & dms], dsym = d >> 4;
          if (!d)
            err(3);
          pos += d & 15;
          var dt = fd[dsym];
          if (dsym > 3) {
            var b = fdeb[dsym];
            dt += bits16(dat, pos) & (1 << b) - 1, pos += b;
          }
          if (pos > tbts) {
            if (noSt)
              err(0);
            break;
          }
          if (resize)
            cbuf(bt + 131072);
          var end = bt + add;
          if (bt < dt) {
            var shift = dl - dt, dend = Math.min(dt, end);
            if (shift + bt < 0)
              err(3);
            for (; bt < dend; ++bt)
              buf[bt] = dict[shift + bt];
          }
          for (; bt < end; ++bt)
            buf[bt] = buf[bt - dt];
        }
      }
      st.l = lm, st.p = lpos, st.b = bt, st.f = final;
      if (lm)
        final = 1, st.m = lbt, st.d = dm, st.n = dbt;
    } while (!final);
    return bt != buf.length && noBuf ? slc(buf, 0, bt) : buf.subarray(0, bt);
  };
  var wbits = function(d, p, v) {
    v <<= p & 7;
    var o = p / 8 | 0;
    d[o] |= v;
    d[o + 1] |= v >> 8;
  };
  var wbits16 = function(d, p, v) {
    v <<= p & 7;
    var o = p / 8 | 0;
    d[o] |= v;
    d[o + 1] |= v >> 8;
    d[o + 2] |= v >> 16;
  };
  var hTree = function(d, mb) {
    var t = [];
    for (var i = 0; i < d.length; ++i) {
      if (d[i])
        t.push({ s: i, f: d[i] });
    }
    var s = t.length;
    var t2 = t.slice();
    if (!s)
      return { t: et, l: 0 };
    if (s == 1) {
      var v = new u8(t[0].s + 1);
      v[t[0].s] = 1;
      return { t: v, l: 1 };
    }
    t.sort(function(a, b) {
      return a.f - b.f;
    });
    t.push({ s: -1, f: 25001 });
    var l = t[0], r = t[1], i0 = 0, i1 = 1, i2 = 2;
    t[0] = { s: -1, f: l.f + r.f, l, r };
    while (i1 != s - 1) {
      l = t[t[i0].f < t[i2].f ? i0++ : i2++];
      r = t[i0 != i1 && t[i0].f < t[i2].f ? i0++ : i2++];
      t[i1++] = { s: -1, f: l.f + r.f, l, r };
    }
    var maxSym = t2[0].s;
    for (var i = 1; i < s; ++i) {
      if (t2[i].s > maxSym)
        maxSym = t2[i].s;
    }
    var tr = new u16(maxSym + 1);
    var mbt = ln(t[i1 - 1], tr, 0);
    if (mbt > mb) {
      var i = 0, dt = 0;
      var lft = mbt - mb, cst = 1 << lft;
      t2.sort(function(a, b) {
        return tr[b.s] - tr[a.s] || a.f - b.f;
      });
      for (; i < s; ++i) {
        var i2_1 = t2[i].s;
        if (tr[i2_1] > mb) {
          dt += cst - (1 << mbt - tr[i2_1]);
          tr[i2_1] = mb;
        } else
          break;
      }
      dt >>= lft;
      while (dt > 0) {
        var i2_2 = t2[i].s;
        if (tr[i2_2] < mb)
          dt -= 1 << mb - tr[i2_2]++ - 1;
        else
          ++i;
      }
      for (; i >= 0 && dt; --i) {
        var i2_3 = t2[i].s;
        if (tr[i2_3] == mb) {
          --tr[i2_3];
          ++dt;
        }
      }
      mbt = mb;
    }
    return { t: new u8(tr), l: mbt };
  };
  var ln = function(n, l, d) {
    return n.s == -1 ? Math.max(ln(n.l, l, d + 1), ln(n.r, l, d + 1)) : l[n.s] = d;
  };
  var lc = function(c) {
    var s = c.length;
    while (s && !c[--s])
      ;
    var cl = new u16(++s);
    var cli = 0, cln = c[0], cls = 1;
    var w = function(v) {
      cl[cli++] = v;
    };
    for (var i = 1; i <= s; ++i) {
      if (c[i] == cln && i != s)
        ++cls;
      else {
        if (!cln && cls > 2) {
          for (; cls > 138; cls -= 138)
            w(32754);
          if (cls > 2) {
            w(cls > 10 ? cls - 11 << 5 | 28690 : cls - 3 << 5 | 12305);
            cls = 0;
          }
        } else if (cls > 3) {
          w(cln), --cls;
          for (; cls > 6; cls -= 6)
            w(8304);
          if (cls > 2)
            w(cls - 3 << 5 | 8208), cls = 0;
        }
        while (cls--)
          w(cln);
        cls = 1;
        cln = c[i];
      }
    }
    return { c: cl.subarray(0, cli), n: s };
  };
  var clen = function(cf, cl) {
    var l = 0;
    for (var i = 0; i < cl.length; ++i)
      l += cf[i] * cl[i];
    return l;
  };
  var wfblk = function(out, pos, dat) {
    var s = dat.length;
    var o = shft(pos + 2);
    out[o] = s & 255;
    out[o + 1] = s >> 8;
    out[o + 2] = out[o] ^ 255;
    out[o + 3] = out[o + 1] ^ 255;
    for (var i = 0; i < s; ++i)
      out[o + i + 4] = dat[i];
    return (o + 4 + s) * 8;
  };
  var wblk = function(dat, out, final, syms, lf, df, eb, li, bs, bl, p) {
    wbits(out, p++, final);
    ++lf[256];
    var _a2 = hTree(lf, 15), dlt = _a2.t, mlb = _a2.l;
    var _b2 = hTree(df, 15), ddt = _b2.t, mdb = _b2.l;
    var _c = lc(dlt), lclt = _c.c, nlc = _c.n;
    var _d = lc(ddt), lcdt = _d.c, ndc = _d.n;
    var lcfreq = new u16(19);
    for (var i = 0; i < lclt.length; ++i)
      ++lcfreq[lclt[i] & 31];
    for (var i = 0; i < lcdt.length; ++i)
      ++lcfreq[lcdt[i] & 31];
    var _e = hTree(lcfreq, 7), lct = _e.t, mlcb = _e.l;
    var nlcc = 19;
    for (; nlcc > 4 && !lct[clim[nlcc - 1]]; --nlcc)
      ;
    var flen = bl + 5 << 3;
    var ftlen = clen(lf, flt) + clen(df, fdt) + eb;
    var dtlen = clen(lf, dlt) + clen(df, ddt) + eb + 14 + 3 * nlcc + clen(lcfreq, lct) + 2 * lcfreq[16] + 3 * lcfreq[17] + 7 * lcfreq[18];
    if (bs >= 0 && flen <= ftlen && flen <= dtlen)
      return wfblk(out, p, dat.subarray(bs, bs + bl));
    var lm, ll, dm, dl;
    wbits(out, p, 1 + (dtlen < ftlen)), p += 2;
    if (dtlen < ftlen) {
      lm = hMap(dlt, mlb, 0), ll = dlt, dm = hMap(ddt, mdb, 0), dl = ddt;
      var llm = hMap(lct, mlcb, 0);
      wbits(out, p, nlc - 257);
      wbits(out, p + 5, ndc - 1);
      wbits(out, p + 10, nlcc - 4);
      p += 14;
      for (var i = 0; i < nlcc; ++i)
        wbits(out, p + 3 * i, lct[clim[i]]);
      p += 3 * nlcc;
      var lcts = [lclt, lcdt];
      for (var it = 0; it < 2; ++it) {
        var clct = lcts[it];
        for (var i = 0; i < clct.length; ++i) {
          var len = clct[i] & 31;
          wbits(out, p, llm[len]), p += lct[len];
          if (len > 15)
            wbits(out, p, clct[i] >> 5 & 127), p += clct[i] >> 12;
        }
      }
    } else {
      lm = flm, ll = flt, dm = fdm, dl = fdt;
    }
    for (var i = 0; i < li; ++i) {
      var sym = syms[i];
      if (sym > 255) {
        var len = sym >> 18 & 31;
        wbits16(out, p, lm[len + 257]), p += ll[len + 257];
        if (len > 7)
          wbits(out, p, sym >> 23 & 31), p += fleb[len];
        var dst = sym & 31;
        wbits16(out, p, dm[dst]), p += dl[dst];
        if (dst > 3)
          wbits16(out, p, sym >> 5 & 8191), p += fdeb[dst];
      } else {
        wbits16(out, p, lm[sym]), p += ll[sym];
      }
    }
    wbits16(out, p, lm[256]);
    return p + ll[256];
  };
  var deo = /* @__PURE__ */ new i32([65540, 131080, 131088, 131104, 262176, 1048704, 1048832, 2114560, 2117632]);
  var et = /* @__PURE__ */ new u8(0);
  var dflt = function(dat, lvl, plvl, pre, post, st) {
    var s = st.z || dat.length;
    var o = new u8(pre + s + 5 * (1 + Math.ceil(s / 7e3)) + post);
    var w = o.subarray(pre, o.length - post);
    var lst = st.l;
    var pos = (st.r || 0) & 7;
    if (lvl) {
      if (pos)
        w[0] = st.r >> 3;
      var opt = deo[lvl - 1];
      var n = opt >> 13, c = opt & 8191;
      var msk_1 = (1 << plvl) - 1;
      var prev = st.p || new u16(32768), head = st.h || new u16(msk_1 + 1);
      var bs1_1 = Math.ceil(plvl / 3), bs2_1 = 2 * bs1_1;
      var hsh = function(i2) {
        return (dat[i2] ^ dat[i2 + 1] << bs1_1 ^ dat[i2 + 2] << bs2_1) & msk_1;
      };
      var syms = new i32(25e3);
      var lf = new u16(288), df = new u16(32);
      var lc_1 = 0, eb = 0, i = st.i || 0, li = 0, wi = st.w || 0, bs = 0;
      for (; i + 2 < s; ++i) {
        var hv = hsh(i);
        var imod = i & 32767, pimod = head[hv];
        prev[imod] = pimod;
        head[hv] = imod;
        if (wi <= i) {
          var rem = s - i;
          if ((lc_1 > 7e3 || li > 24576) && (rem > 423 || !lst)) {
            pos = wblk(dat, w, 0, syms, lf, df, eb, li, bs, i - bs, pos);
            li = lc_1 = eb = 0, bs = i;
            for (var j = 0; j < 286; ++j)
              lf[j] = 0;
            for (var j = 0; j < 30; ++j)
              df[j] = 0;
          }
          var l = 2, d = 0, ch_1 = c, dif = imod - pimod & 32767;
          if (rem > 2 && hv == hsh(i - dif)) {
            var maxn = Math.min(n, rem) - 1;
            var maxd = Math.min(32767, i);
            var ml = Math.min(258, rem);
            while (dif <= maxd && --ch_1 && imod != pimod) {
              if (dat[i + l] == dat[i + l - dif]) {
                var nl = 0;
                for (; nl < ml && dat[i + nl] == dat[i + nl - dif]; ++nl)
                  ;
                if (nl > l) {
                  l = nl, d = dif;
                  if (nl > maxn)
                    break;
                  var mmd = Math.min(dif, nl - 2);
                  var md = 0;
                  for (var j = 0; j < mmd; ++j) {
                    var ti = i - dif + j & 32767;
                    var pti = prev[ti];
                    var cd = ti - pti & 32767;
                    if (cd > md)
                      md = cd, pimod = ti;
                  }
                }
              }
              imod = pimod, pimod = prev[imod];
              dif += imod - pimod & 32767;
            }
          }
          if (d) {
            syms[li++] = 268435456 | revfl[l] << 18 | revfd[d];
            var lin = revfl[l] & 31, din = revfd[d] & 31;
            eb += fleb[lin] + fdeb[din];
            ++lf[257 + lin];
            ++df[din];
            wi = i + l;
            ++lc_1;
          } else {
            syms[li++] = dat[i];
            ++lf[dat[i]];
          }
        }
      }
      for (i = Math.max(i, wi); i < s; ++i) {
        syms[li++] = dat[i];
        ++lf[dat[i]];
      }
      pos = wblk(dat, w, lst, syms, lf, df, eb, li, bs, i - bs, pos);
      if (!lst) {
        st.r = pos & 7 | w[pos / 8 | 0] << 3;
        pos -= 7;
        st.h = head, st.p = prev, st.i = i, st.w = wi;
      }
    } else {
      for (var i = st.w || 0; i < s + lst; i += 65535) {
        var e = i + 65535;
        if (e >= s) {
          w[pos / 8 | 0] = lst;
          e = s;
        }
        pos = wfblk(w, pos + 1, dat.subarray(i, e));
      }
      st.i = s;
    }
    return slc(o, 0, pre + shft(pos) + post);
  };
  var crct = /* @__PURE__ */ (function() {
    var t = new Int32Array(256);
    for (var i = 0; i < 256; ++i) {
      var c = i, k = 9;
      while (--k)
        c = (c & 1 && -306674912) ^ c >>> 1;
      t[i] = c;
    }
    return t;
  })();
  var crc = function() {
    var c = -1;
    return {
      p: function(d) {
        var cr = c;
        for (var i = 0; i < d.length; ++i)
          cr = crct[cr & 255 ^ d[i]] ^ cr >>> 8;
        c = cr;
      },
      d: function() {
        return ~c;
      }
    };
  };
  var dopt = function(dat, opt, pre, post, st) {
    if (!st) {
      st = { l: 1 };
      if (opt.dictionary) {
        var dict = opt.dictionary.subarray(-32768);
        var newDat = new u8(dict.length + dat.length);
        newDat.set(dict);
        newDat.set(dat, dict.length);
        dat = newDat;
        st.w = dict.length;
      }
    }
    return dflt(dat, opt.level == null ? 6 : opt.level, opt.mem == null ? st.l ? Math.ceil(Math.max(8, Math.min(13, Math.log(dat.length))) * 1.5) : 20 : 12 + opt.mem, pre, post, st);
  };
  var mrg = function(a, b) {
    var o = {};
    for (var k in a)
      o[k] = a[k];
    for (var k in b)
      o[k] = b[k];
    return o;
  };
  var b2 = function(d, b) {
    return d[b] | d[b + 1] << 8;
  };
  var b4 = function(d, b) {
    return (d[b] | d[b + 1] << 8 | d[b + 2] << 16 | d[b + 3] << 24) >>> 0;
  };
  var b8 = function(d, b) {
    return b4(d, b) + b4(d, b + 4) * 4294967296;
  };
  var wbytes = function(d, b, v) {
    for (; v; ++b)
      d[b] = v, v >>>= 8;
  };
  function deflateSync(data, opts) {
    return dopt(data, opts || {}, 0, 0);
  }
  function inflateSync(data, opts) {
    return inflt(data, { i: 2 }, opts && opts.out, opts && opts.dictionary);
  }
  var fltn = function(d, p, t, o) {
    for (var k in d) {
      var val = d[k], n = p + k, op = o;
      if (Array.isArray(val))
        op = mrg(o, val[1]), val = val[0];
      if (val instanceof u8)
        t[n] = [val, op];
      else {
        t[n += "/"] = [new u8(0), op];
        fltn(val, n, t, o);
      }
    }
  };
  var te = typeof TextEncoder != "undefined" && /* @__PURE__ */ new TextEncoder();
  var td = typeof TextDecoder != "undefined" && /* @__PURE__ */ new TextDecoder();
  var tds = 0;
  try {
    td.decode(et, { stream: true });
    tds = 1;
  } catch (e) {
  }
  var dutf8 = function(d) {
    for (var r = "", i = 0; ; ) {
      var c = d[i++];
      var eb = (c > 127) + (c > 223) + (c > 239);
      if (i + eb > d.length)
        return { s: r, r: slc(d, i - 1) };
      if (!eb)
        r += String.fromCharCode(c);
      else if (eb == 3) {
        c = ((c & 15) << 18 | (d[i++] & 63) << 12 | (d[i++] & 63) << 6 | d[i++] & 63) - 65536, r += String.fromCharCode(55296 | c >> 10, 56320 | c & 1023);
      } else if (eb & 1)
        r += String.fromCharCode((c & 31) << 6 | d[i++] & 63);
      else
        r += String.fromCharCode((c & 15) << 12 | (d[i++] & 63) << 6 | d[i++] & 63);
    }
  };
  function strToU8(str, latin1) {
    var i;
    if (te)
      return te.encode(str);
    var l = str.length;
    var ar2 = new u8(str.length + (str.length >> 1));
    var ai = 0;
    var w = function(v) {
      ar2[ai++] = v;
    };
    for (var i = 0; i < l; ++i) {
      if (ai + 5 > ar2.length) {
        var n = new u8(ai + 8 + (l - i << 1));
        n.set(ar2);
        ar2 = n;
      }
      var c = str.charCodeAt(i);
      if (c < 128 || latin1)
        w(c);
      else if (c < 2048)
        w(192 | c >> 6), w(128 | c & 63);
      else if (c > 55295 && c < 57344)
        c = 65536 + (c & 1023 << 10) | str.charCodeAt(++i) & 1023, w(240 | c >> 18), w(128 | c >> 12 & 63), w(128 | c >> 6 & 63), w(128 | c & 63);
      else
        w(224 | c >> 12), w(128 | c >> 6 & 63), w(128 | c & 63);
    }
    return slc(ar2, 0, ai);
  }
  function strFromU8(dat, latin1) {
    if (latin1) {
      var r = "";
      for (var i = 0; i < dat.length; i += 16384)
        r += String.fromCharCode.apply(null, dat.subarray(i, i + 16384));
      return r;
    } else if (td) {
      return td.decode(dat);
    } else {
      var _a2 = dutf8(dat), s = _a2.s, r = _a2.r;
      if (r.length)
        err(8);
      return s;
    }
  }
  var slzh = function(d, b) {
    return b + 30 + b2(d, b + 26) + b2(d, b + 28);
  };
  var zh = function(d, b, z) {
    var fnl = b2(d, b + 28), fn = strFromU8(d.subarray(b + 46, b + 46 + fnl), !(b2(d, b + 8) & 2048)), es2 = b + 46 + fnl, bs = b4(d, b + 20);
    var _a2 = z && bs == 4294967295 ? z64e(d, es2) : [bs, b4(d, b + 24), b4(d, b + 42)], sc = _a2[0], su = _a2[1], off = _a2[2];
    return [b2(d, b + 10), sc, su, fn, es2 + b2(d, b + 30) + b2(d, b + 32), off];
  };
  var z64e = function(d, b) {
    for (; b2(d, b) != 1; b += 4 + b2(d, b + 2))
      ;
    return [b8(d, b + 12), b8(d, b + 4), b8(d, b + 20)];
  };
  var exfl = function(ex) {
    var le = 0;
    if (ex) {
      for (var k in ex) {
        var l = ex[k].length;
        if (l > 65535)
          err(9);
        le += l + 4;
      }
    }
    return le;
  };
  var wzh = function(d, b, f2, fn, u2, c, ce, co) {
    var fl2 = fn.length, ex = f2.extra, col = co && co.length;
    var exl = exfl(ex);
    wbytes(d, b, ce != null ? 33639248 : 67324752), b += 4;
    if (ce != null)
      d[b++] = 20, d[b++] = f2.os;
    d[b] = 20, b += 2;
    d[b++] = f2.flag << 1 | (c < 0 && 8), d[b++] = u2 && 8;
    d[b++] = f2.compression & 255, d[b++] = f2.compression >> 8;
    var dt = new Date(f2.mtime == null ? Date.now() : f2.mtime), y = dt.getFullYear() - 1980;
    if (y < 0 || y > 119)
      err(10);
    wbytes(d, b, y << 25 | dt.getMonth() + 1 << 21 | dt.getDate() << 16 | dt.getHours() << 11 | dt.getMinutes() << 5 | dt.getSeconds() >> 1), b += 4;
    if (c != -1) {
      wbytes(d, b, f2.crc);
      wbytes(d, b + 4, c < 0 ? -c - 2 : c);
      wbytes(d, b + 8, f2.size);
    }
    wbytes(d, b + 12, fl2);
    wbytes(d, b + 14, exl), b += 16;
    if (ce != null) {
      wbytes(d, b, col);
      wbytes(d, b + 6, f2.attrs);
      wbytes(d, b + 10, ce), b += 14;
    }
    d.set(fn, b);
    b += fl2;
    if (exl) {
      for (var k in ex) {
        var exf = ex[k], l = exf.length;
        wbytes(d, b, +k);
        wbytes(d, b + 2, l);
        d.set(exf, b + 4), b += 4 + l;
      }
    }
    if (col)
      d.set(co, b), b += col;
    return b;
  };
  var wzf = function(o, b, c, d, e) {
    wbytes(o, b, 101010256);
    wbytes(o, b + 8, c);
    wbytes(o, b + 10, c);
    wbytes(o, b + 12, d);
    wbytes(o, b + 16, e);
  };
  function zipSync(data, opts) {
    if (!opts)
      opts = {};
    var r = {};
    var files = [];
    fltn(data, "", r, opts);
    var o = 0;
    var tot = 0;
    for (var fn in r) {
      var _a2 = r[fn], file = _a2[0], p = _a2[1];
      var compression = p.level == 0 ? 0 : 8;
      var f2 = strToU8(fn), s = f2.length;
      var com = p.comment, m = com && strToU8(com), ms = m && m.length;
      var exl = exfl(p.extra);
      if (s > 65535)
        err(11);
      var d = compression ? deflateSync(file, p) : file, l = d.length;
      var c = crc();
      c.p(file);
      files.push(mrg(p, {
        size: file.length,
        crc: c.d(),
        c: d,
        f: f2,
        m,
        u: s != fn.length || m && com.length != ms,
        o,
        compression
      }));
      o += 30 + s + exl + l;
      tot += 76 + 2 * (s + exl) + (ms || 0) + l;
    }
    var out = new u8(tot + 22), oe = o, cdl = tot - o;
    for (var i = 0; i < files.length; ++i) {
      var f2 = files[i];
      wzh(out, f2.o, f2, f2.f, f2.u, f2.c.length);
      var badd = 30 + f2.f.length + exfl(f2.extra);
      out.set(f2.c, f2.o + badd);
      wzh(out, o, f2, f2.f, f2.u, f2.c.length, f2.o, f2.m), o += 16 + badd + (f2.m ? f2.m.length : 0);
    }
    wzf(out, o, files.length, cdl, oe);
    return out;
  }
  function unzipSync(data, opts) {
    var files = {};
    var e = data.length - 22;
    for (; b4(data, e) != 101010256; --e) {
      if (!e || data.length - e > 65558)
        err(13);
    }
    var c = b2(data, e + 8);
    if (!c)
      return {};
    var o = b4(data, e + 16);
    var z = o == 4294967295 || c == 65535;
    if (z) {
      var ze = b4(data, e - 12);
      z = b4(data, ze) == 101075792;
      if (z) {
        c = b4(data, ze + 32);
        o = b4(data, ze + 48);
      }
    }
    for (var i = 0; i < c; ++i) {
      var _a2 = zh(data, o, z), c_2 = _a2[0], sc = _a2[1], su = _a2[2], fn = _a2[3], no = _a2[4], off = _a2[5], b = slzh(data, off);
      o = no;
      {
        if (!c_2)
          files[fn] = slc(data, b, b + sc);
        else if (c_2 == 8)
          files[fn] = inflateSync(data.subarray(b, b + sc), { out: new u8(su) });
        else
          err(14, "unknown compression type " + c_2);
      }
    }
    return files;
  }
  const DEFAULT_MAX_ENTRIES = 1e4;
  const DEFAULT_MAX_ENTRY_BYTES = 128 * 1024 * 1024;
  const DEFAULT_MAX_TOTAL_BYTES = 512 * 1024 * 1024;
  function isBlobLike(value) {
    return !!value && typeof value === "object" && typeof value.arrayBuffer === "function";
  }
  function copyToLocalBytes(bytes) {
    const local = new Uint8Array(bytes.byteLength);
    local.set(bytes);
    return local;
  }
  async function bytesFromBlobLike(value) {
    const buffer = await value.arrayBuffer();
    return copyToLocalBytes(new Uint8Array(buffer));
  }
  function bytesFromBufferLike(value) {
    if (!value || typeof value !== "object") return null;
    if (ArrayBuffer.isView(value)) {
      const view = value;
      return copyToLocalBytes(new Uint8Array(view.buffer, view.byteOffset, view.byteLength));
    }
    const candidate = value;
    if (typeof candidate.byteLength === "number" && typeof candidate.slice === "function") {
      return copyToLocalBytes(new Uint8Array(value));
    }
    return null;
  }
  function validateBundleZipPath(path) {
    const normalized = path.replace(/\\/g, "/").replace(/^\/+/, "");
    if (!normalized || normalized.includes("..") || normalized.startsWith("/")) {
      throw new Error(`Unsafe bundle ZIP path: ${path}`);
    }
    return normalized;
  }
  function createBundleZip(entries) {
    const payload = {};
    for (const entry of entries) {
      const path = validateBundleZipPath(entry.path);
      const options2 = { level: entry.level ?? 6 };
      payload[path] = [typeof entry.data === "string" ? strToU8(entry.data) : entry.data, options2];
    }
    return zipSync(payload);
  }
  async function readBundleZip(data, options2 = {}) {
    const bytes = isBlobLike(data) ? await bytesFromBlobLike(data) : bytesFromBufferLike(data);
    if (!bytes) {
      throw new Error("Bundle ZIP input must be a File, Blob, ArrayBuffer, or Uint8Array.");
    }
    const maxEntries = options2.maxEntries ?? DEFAULT_MAX_ENTRIES;
    const maxEntryBytes = options2.maxEntryBytes ?? DEFAULT_MAX_ENTRY_BYTES;
    const maxTotalBytes = options2.maxTotalBytes ?? DEFAULT_MAX_TOTAL_BYTES;
    const unzipped = unzipSync(bytes);
    const entries = /* @__PURE__ */ new Map();
    let totalBytes = 0;
    for (const [rawPath, entryBytes] of Object.entries(unzipped)) {
      if (entries.size >= maxEntries) {
        throw new Error(`Bundle ZIP exceeds ${maxEntries} entries.`);
      }
      const path = validateBundleZipPath(rawPath);
      if (entryBytes.byteLength > maxEntryBytes) {
        throw new Error(`Bundle ZIP entry exceeds limit: ${path}`);
      }
      totalBytes += entryBytes.byteLength;
      if (totalBytes > maxTotalBytes) {
        throw new Error(`Bundle ZIP exceeds ${maxTotalBytes} bytes after decompression.`);
      }
      entries.set(path, copyToLocalBytes(entryBytes));
    }
    return { entries, totalBytes };
  }
  function decodeBundleTextEntry(entries, path) {
    const bytes = entries.get(path);
    if (!bytes) {
      throw new Error(`Missing bundle ZIP entry: ${path}`);
    }
    return strFromU8(bytes);
  }
  function normalizeFilename(value) {
    return String(value || "bundle").trim().replace(/[^a-zA-Z0-9._-]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").slice(0, 80) || "bundle";
  }
  function safeHttpUrl$1(value) {
    if (typeof value !== "string") return void 0;
    try {
      const parsed = new URL(value);
      return parsed.protocol === "http:" || parsed.protocol === "https:" ? parsed.href : void 0;
    } catch {
      return void 0;
    }
  }
  function inferKind$1(original, record) {
    const source = original && typeof original === "object" ? original : {};
    const typename = String(source.__typename || record.__typename || "").toLowerCase();
    if (typename.includes("tweet") || record.full_text || record.media) return "tweet";
    if (typename.includes("user") || record.screen_name || record.profile_image_url) return "user";
    return "unknown";
  }
  function extractObservedAt(original, record) {
    const source = original && typeof original === "object" ? original : {};
    const privateFields = source.twe_private_fields;
    const candidates = [
      privateFields == null ? void 0 : privateFields.created_at,
      privateFields == null ? void 0 : privateFields.updated_at,
      record.created_at,
      record.time,
      record.date
    ];
    for (const candidate of candidates) {
      const numeric = Number(candidate);
      if (Number.isFinite(numeric) && numeric > 0) {
        return numeric;
      }
      if (typeof candidate === "string") {
        const parsed = Date.parse(candidate);
        if (Number.isFinite(parsed)) {
          return parsed;
        }
      }
    }
    return void 0;
  }
  async function buildRecordEnvelope(bundleId, row, options2) {
    const kind = inferKind$1(row.original, row.record);
    const sourceId = String(row.id || row.record.id || row.record.rest_id || "");
    const id2 = await createBundleRecordId(bundleId, kind, sourceId || JSON.stringify(row.record));
    const data = options2.includeOriginalMetadata ? {
      ...row.record,
      metadata: row.original
    } : row.record;
    return {
      id: id2,
      kind,
      sourceId: sourceId || void 0,
      observedAt: extractObservedAt(row.original, row.record),
      sensitivity: "low",
      data,
      mediaRefs: Array.isArray(row.record.media) ? row.record.media.map((media, index) => ({
        id: `${id2}:media:${index}`,
        type: media.type === "photo" || media.type === "video" || media.type === "animated_gif" ? media.type : "unknown",
        url: safeHttpUrl$1(media.original),
        previewUrl: safeHttpUrl$1(media.thumbnail),
        altText: typeof media.ext_alt_text === "string" ? media.ext_alt_text : void 0
      })) : void 0
    };
  }
  function countBundleRecords(records) {
    return records.reduce(
      (acc, record) => {
        acc.records += 1;
        if (record.kind === "tweet") acc.tweets += 1;
        if (record.kind === "user") acc.users += 1;
        if (record.kind === "social_edge") acc.socialEdges += 1;
        if (record.kind === "capture") acc.captures += 1;
        acc.mediaBlobs += 0;
        return acc;
      },
      { records: 0, tweets: 0, users: 0, socialEdges: 0, captures: 0, mediaBlobs: 0 }
    );
  }
  async function createCanonicalBundleZip(rows, options2) {
    const now = Date.now();
    const startedAt = typeof performance !== "undefined" ? performance.now() : Date.now();
    const reportProgress = (phase, processedRecords) => {
      var _a2;
      (_a2 = options2.onProgress) == null ? void 0 : _a2.call(options2, {
        phase,
        processedRecords,
        totalRecords: rows.length,
        elapsedMs: (typeof performance !== "undefined" ? performance.now() : Date.now()) - startedAt
      });
    };
    const bundleId = await createBundleId(
      `${options2.title}:${options2.scope}:${options2.queryText || ""}:${now}:${rows.length}`
    );
    const records = [];
    for (let index = 0; index < rows.length; index += 1) {
      const row = rows[index];
      if (!row) continue;
      records.push(await buildRecordEnvelope(bundleId, row, options2));
      if (index === 0 || index + 1 === rows.length || (index + 1) % 100 === 0) {
        reportProgress("envelope", index + 1);
      }
    }
    const recordsJsonl = records.map((record) => JSON.stringify(record)).join("\n") + "\n";
    const privacy = buildBundlePrivacySummary(SAFE_SHARED_DEFAULT_PRIVACY);
    const files = [
      {
        path: "manifest.json",
        contentType: "application/json",
        role: "manifest"
      },
      {
        path: "records/records.jsonl",
        contentType: "application/x-ndjson",
        role: "records",
        bytes: new TextEncoder().encode(recordsJsonl).byteLength,
        sha256: await sha256Hex(recordsJsonl)
      }
    ];
    const mediaUrlLines = records.flatMap((record) => record.mediaRefs || []).map((media) => media.url).filter((url) => !!url);
    const mediaUrlsText = mediaUrlLines.join("\n") + (mediaUrlLines.length ? "\n" : "");
    if (mediaUrlLines.length) {
      files.push({
        path: "media/media-urls.txt",
        contentType: "text/plain",
        role: "media",
        bytes: new TextEncoder().encode(mediaUrlsText).byteLength,
        sha256: await sha256Hex(mediaUrlsText)
      });
    }
    const manifest = {
      id: bundleId,
      title: options2.title,
      description: options2.description,
      producer: {
        app: "twitter-web-exporter",
        appVersion: packageJson.version,
        schemaVersion: 1,
        exportedAt: now
      },
      privacy,
      counts: countBundleRecords(records),
      files
    };
    const manifestFile = files[0];
    if (manifestFile) {
      files[0] = {
        ...manifestFile,
        bytes: new TextEncoder().encode(JSON.stringify(manifest, void 0, 2)).byteLength
      };
    }
    reportProgress("manifest", records.length);
    const compressionLevel = options2.compressionLevel ?? 1;
    const entries = [
      {
        path: "manifest.json",
        data: JSON.stringify(manifest, void 0, 2),
        level: compressionLevel
      },
      {
        path: "records/records.jsonl",
        data: recordsJsonl,
        level: compressionLevel
      }
    ];
    if (mediaUrlLines.length) {
      entries.push({ path: "media/media-urls.txt", data: mediaUrlsText, level: compressionLevel });
    }
    reportProgress("zip", records.length);
    const result = {
      filename: `twe-bundle-${normalizeFilename(options2.title)}-${now}.zip`,
      bytes: createBundleZip(entries),
      manifest
    };
    reportProgress("done", records.length);
    return result;
  }
  async function exportCanonicalBundleZip(rows, options2) {
    const { filename, bytes } = await createCanonicalBundleZip(rows, options2);
    const blobBytes = bytes.buffer.slice(
      bytes.byteOffset,
      bytes.byteOffset + bytes.byteLength
    );
    const { saveFile: saveFile2 } = await __vitePreload(async () => {
      const { saveFile: saveFile3 } = await Promise.resolve().then(() => exporter);
      return { saveFile: saveFile3 };
    }, void 0 );
    saveFile2(filename, new Blob([blobBytes], { type: "application/zip" }));
    return filename;
  }
  function isObject$2(value) {
    return !!value && typeof value === "object" && !Array.isArray(value);
  }
  function pushError(issues, path, message) {
    issues.push({ path, message, severity: "error" });
  }
  function validateBundleManifest(value) {
    const issues = [];
    if (!isObject$2(value)) {
      pushError(issues, "$", "Manifest must be an object.");
      return { ok: false, issues };
    }
    const manifest = value;
    if (!manifest.id || typeof manifest.id !== "string")
      pushError(issues, "$.id", "Missing bundle id.");
    if (!manifest.title || typeof manifest.title !== "string")
      pushError(issues, "$.title", "Missing title.");
    if (!isObject$2(manifest.producer)) pushError(issues, "$.producer", "Missing producer block.");
    if (!isObject$2(manifest.privacy)) pushError(issues, "$.privacy", "Missing privacy block.");
    if (!isObject$2(manifest.counts)) pushError(issues, "$.counts", "Missing counts block.");
    if (!Array.isArray(manifest.files)) pushError(issues, "$.files", "Files must be an array.");
    return { ok: !issues.some((issue) => issue.severity === "error"), issues };
  }
  function validateBundleRecordEnvelope(value) {
    const issues = [];
    if (!isObject$2(value)) {
      pushError(issues, "$", "Record envelope must be an object.");
      return { ok: false, issues };
    }
    const record = value;
    if (!record.id || typeof record.id !== "string") pushError(issues, "$.id", "Missing record id.");
    if (!record.kind || typeof record.kind !== "string")
      pushError(issues, "$.kind", "Missing record kind.");
    if (!record.sensitivity || typeof record.sensitivity !== "string") {
      pushError(issues, "$.sensitivity", "Missing sensitivity.");
    }
    if (!("data" in record)) pushError(issues, "$.data", "Missing data payload.");
    return { ok: !issues.some((issue) => issue.severity === "error"), issues };
  }
  function getErrorMessage$2(error) {
    if (error && typeof error === "object") {
      const message = error.message;
      if (typeof message === "string" && message.trim()) {
        return message;
      }
    }
    return String(error);
  }
  function parseJsonLine(line, lineNumber) {
    const trimmed = line.trim();
    if (!trimmed) {
      return null;
    }
    try {
      return JSON.parse(trimmed);
    } catch (error) {
      throw new Error(`Invalid JSONL at records line ${lineNumber}: ${getErrorMessage$2(error)}`);
    }
  }
  function extractSearchText(record) {
    const parts = [];
    const data = record.data;
    for (const key of ["full_text", "text", "description", "name", "screen_name"]) {
      const value = data == null ? void 0 : data[key];
      if (typeof value === "string" && value.trim()) {
        parts.push(value);
      }
    }
    if (!parts.length) {
      parts.push(JSON.stringify(record.data).slice(0, 2e4));
    }
    return parts.join("\n");
  }
  function buildImportedBundle(manifest, now) {
    return {
      id: manifest.id,
      title: manifest.title,
      description: manifest.description,
      status: "importing",
      visibility: manifest.privacy.visibility,
      importedAt: now,
      updatedAt: now,
      schemaVersion: manifest.producer.schemaVersion,
      appVersion: manifest.producer.appVersion,
      recordCount: manifest.counts.records,
      mediaBlobCount: manifest.counts.mediaBlobs,
      manifest
    };
  }
  async function importBundleZip(database, data) {
    var _a2, _b2, _c;
    const now = Date.now();
    const { entries } = await readBundleZip(data);
    const manifest = JSON.parse(decodeBundleTextEntry(entries, "manifest.json"));
    const manifestValidation = validateBundleManifest(manifest);
    if (!manifestValidation.ok) {
      throw new Error(
        `Invalid bundle manifest: ${((_a2 = manifestValidation.issues[0]) == null ? void 0 : _a2.message) ?? "unknown error"}`
      );
    }
    const recordsPath = ((_b2 = manifest.files.find((file) => file.role === "records")) == null ? void 0 : _b2.path) ?? [...entries.keys()].find((path) => path.endsWith(".jsonl"));
    if (!recordsPath) {
      throw new Error("Bundle is missing a records JSONL file.");
    }
    const reportId = `${manifest.id}:import:${now}`;
    const recordsText = decodeBundleTextEntry(entries, recordsPath);
    const defaultCollection = {
      id: `${manifest.id}:all`,
      bundle_id: manifest.id,
      name: "All Records",
      kind: "mixed",
      record_count: 0,
      created_at: now,
      updated_at: now
    };
    const snapshots = [];
    const items = [];
    const warnings = [];
    let recordsSeen = 0;
    let recordsSkipped = 0;
    const lines = recordsText.split(/\r?\n/);
    for (let index = 0; index < lines.length; index += 1) {
      const record = parseJsonLine(lines[index] ?? "", index + 1);
      if (!record) {
        continue;
      }
      recordsSeen += 1;
      const validation = validateBundleRecordEnvelope(record);
      if (!validation.ok) {
        recordsSkipped += 1;
        warnings.push(
          `Skipped line ${index + 1}: ${((_c = validation.issues[0]) == null ? void 0 : _c.message) ?? "invalid record"}`
        );
        continue;
      }
      const snapshotId = `${manifest.id}:${record.id}`;
      snapshots.push({
        id: snapshotId,
        bundle_id: manifest.id,
        kind: record.kind,
        source_id: record.sourceId,
        source_extension: record.sourceExtension,
        observed_at: record.observedAt,
        sensitivity: record.sensitivity,
        data: record.data,
        media_refs: record.mediaRefs,
        search_text: extractSearchText(record),
        created_at: now,
        updated_at: now
      });
      items.push({
        id: `${defaultCollection.id}:${record.id}`,
        bundle_id: manifest.id,
        collection_id: defaultCollection.id,
        record_id: snapshotId,
        kind: record.kind,
        source_id: record.sourceId,
        sort_time: record.observedAt,
        created_at: now
      });
    }
    defaultCollection.record_count = items.length;
    const report = {
      id: reportId,
      bundle_id: manifest.id,
      started_at: now,
      finished_at: Date.now(),
      status: "ok",
      records_seen: recordsSeen,
      records_imported: snapshots.length,
      records_skipped: recordsSkipped,
      warnings
    };
    try {
      await database.bundlePutImportBatch({
        bundle: buildImportedBundle(
          {
            ...manifest,
            producer: {
              ...manifest.producer,
              appVersion: manifest.producer.appVersion || packageJson.version
            }
          },
          now
        ),
        collections: [defaultCollection],
        items,
        snapshots,
        report
      });
      await database.bundleMarkReady(manifest.id);
    } catch (error) {
      await database.bundleMarkFailed(manifest.id, getErrorMessage$2(error));
      throw error;
    }
    return {
      bundleId: manifest.id,
      recordsSeen,
      recordsImported: snapshots.length,
      recordsSkipped,
      warnings
    };
  }
  function getErrorMessage$1(error) {
    if (error && typeof error === "object") {
      const message = error.message;
      if (typeof message === "string" && message.trim()) {
        return message;
      }
    }
    return String(error);
  }
  function isObject$1(value) {
    return !!value && typeof value === "object" && !Array.isArray(value);
  }
  function inferKind(value) {
    if (value.metadata && isObject$1(value.metadata)) {
      return inferKind(value.metadata);
    }
    const typename = String(value.__typename || "").toLowerCase();
    if (typename.includes("tweet") || value.full_text || value.media || value.bookmark_folder_id)
      return "tweet";
    if (typename.includes("user") || value.screen_name || value.profile_image_url) return "user";
    return "unknown";
  }
  function safeHttpUrl(value) {
    if (typeof value !== "string") return void 0;
    try {
      const parsed = new URL(value);
      return parsed.protocol === "http:" || parsed.protocol === "https:" ? parsed.href : void 0;
    } catch {
      return void 0;
    }
  }
  function inferSourceId(value, fallback) {
    if (value.metadata && isObject$1(value.metadata)) {
      return inferSourceId(value.metadata, fallback);
    }
    return String(value.rest_id || value.id || value.user_id || value.tweet_id || fallback);
  }
  function inferObservedAt(value) {
    const metadata = isObject$1(value.metadata) ? value.metadata : null;
    const privateFields = isObject$1(metadata == null ? void 0 : metadata.twe_private_fields) ? metadata.twe_private_fields : isObject$1(value.twe_private_fields) ? value.twe_private_fields : null;
    const candidates = [
      privateFields == null ? void 0 : privateFields.created_at,
      privateFields == null ? void 0 : privateFields.updated_at,
      value.created_at,
      value.date,
      value.time
    ];
    for (const candidate of candidates) {
      const numeric = Number(candidate);
      if (Number.isFinite(numeric) && numeric > 0) return numeric;
      if (typeof candidate === "string") {
        const parsed = Date.parse(candidate);
        if (Number.isFinite(parsed)) return parsed;
      }
    }
    return void 0;
  }
  function extractRowsFromJson(value) {
    if (Array.isArray(value)) {
      return value.filter(isObject$1);
    }
    if (!isObject$1(value)) {
      return [];
    }
    if (Array.isArray(value.records)) {
      return value.records.filter(isObject$1);
    }
    if (Array.isArray(value.data)) {
      return value.data.filter(isObject$1);
    }
    if (Array.isArray(value.rows)) {
      return value.rows.filter(isObject$1);
    }
    return [value];
  }
  function parseLegacyText(text, filename) {
    const trimmed = text.trim();
    if (!trimmed) return [];
    if (filename.endsWith(".jsonl") || trimmed.includes("\n{")) {
      const rows = [];
      for (const line of trimmed.split(/\r?\n/)) {
        const part = line.trim();
        if (!part) continue;
        const parsed = JSON.parse(part);
        if (isObject$1(parsed)) rows.push(parsed);
      }
      if (rows.length) return rows;
    }
    return extractRowsFromJson(JSON.parse(trimmed));
  }
  async function createLegacyEnvelope(bundleId, row, index) {
    const kind = inferKind(row);
    const sourceId = inferSourceId(row, index);
    const id2 = await createBundleRecordId(bundleId, kind, sourceId);
    return {
      id: id2,
      kind,
      sourceId,
      observedAt: inferObservedAt(row),
      sensitivity: "low",
      data: row,
      mediaRefs: Array.isArray(row.media) ? row.media.map((media, mediaIndex) => ({
        id: `${id2}:media:${mediaIndex}`,
        type: media.type === "photo" || media.type === "video" || media.type === "animated_gif" ? media.type : "unknown",
        url: safeHttpUrl(media.original),
        previewUrl: safeHttpUrl(media.thumbnail),
        altText: typeof media.ext_alt_text === "string" ? media.ext_alt_text : void 0
      })) : void 0
    };
  }
  async function importLegacyBundleFile(database, file) {
    const now = Date.now();
    const text = await file.text();
    const rows = parseLegacyText(text, file.name);
    const bundleId = await createBundleId(`legacy:${file.name}:${file.size}:${now}`);
    const records = await Promise.all(
      rows.map((row, index) => createLegacyEnvelope(bundleId, row, index))
    );
    const privacy = buildBundlePrivacySummary({
      ...SAFE_SHARED_DEFAULT_PRIVACY,
      includeSourceCaptureTimes: true
    });
    const manifest = {
      id: bundleId,
      title: file.name.replace(/\.(json|jsonl)$/i, ""),
      description: `Imported from legacy ${file.name}`,
      producer: {
        app: "twitter-web-exporter",
        appVersion: packageJson.version,
        schemaVersion: 1,
        exportedAt: now
      },
      privacy,
      counts: {
        records: records.length,
        tweets: records.filter((record) => record.kind === "tweet").length,
        users: records.filter((record) => record.kind === "user").length,
        socialEdges: 0,
        captures: 0,
        mediaBlobs: 0
      },
      files: [
        {
          path: file.name,
          contentType: file.name.endsWith(".jsonl") ? "application/x-ndjson" : "application/json",
          role: "records",
          bytes: file.size
        }
      ]
    };
    const collection = {
      id: `${bundleId}:all`,
      bundle_id: bundleId,
      name: "All Records",
      kind: "mixed",
      record_count: records.length,
      created_at: now,
      updated_at: now
    };
    const snapshots = records.map((record) => ({
      id: `${bundleId}:${record.id}`,
      bundle_id: bundleId,
      kind: record.kind,
      source_id: record.sourceId,
      observed_at: record.observedAt,
      sensitivity: record.sensitivity,
      data: record.data,
      media_refs: record.mediaRefs,
      search_text: JSON.stringify(record.data).slice(0, 2e4),
      created_at: now,
      updated_at: now
    }));
    const items = snapshots.map((snapshot) => ({
      id: `${collection.id}:${snapshot.id}`,
      bundle_id: bundleId,
      collection_id: collection.id,
      record_id: snapshot.id,
      kind: snapshot.kind,
      source_id: snapshot.source_id,
      sort_time: snapshot.observed_at,
      created_at: now
    }));
    const report = {
      id: `${bundleId}:import:${now}`,
      bundle_id: bundleId,
      started_at: now,
      finished_at: Date.now(),
      status: "ok",
      records_seen: rows.length,
      records_imported: snapshots.length,
      records_skipped: rows.length - snapshots.length,
      warnings: ["Imported through legacy JSON/JSONL compatibility path."]
    };
    try {
      await database.bundlePutImportBatch({
        bundle: {
          id: bundleId,
          title: manifest.title,
          description: manifest.description,
          status: "importing",
          visibility: manifest.privacy.visibility,
          importedAt: now,
          updatedAt: now,
          schemaVersion: manifest.producer.schemaVersion,
          appVersion: packageJson.version,
          recordCount: snapshots.length,
          mediaBlobCount: 0,
          manifest
        },
        collections: [collection],
        items,
        snapshots,
        report
      });
      await database.bundleMarkReady(bundleId);
    } catch (error) {
      await database.bundleMarkFailed(bundleId, getErrorMessage$1(error));
      throw error;
    }
    return {
      bundleId,
      recordsSeen: rows.length,
      recordsImported: snapshots.length,
      recordsSkipped: rows.length - snapshots.length,
      warnings: report.warnings
    };
  }
  function isObject(value) {
    return !!value && typeof value === "object" && !Array.isArray(value);
  }
  function cloneObject(value) {
    if (!isObject(value)) {
      return {};
    }
    try {
      return JSON.parse(JSON.stringify(value));
    } catch {
      return { ...value };
    }
  }
  function unwrapMetadataRecord(data) {
    const root = cloneObject(data);
    const metadata = root.metadata;
    if (isObject(metadata)) {
      return cloneObject(metadata);
    }
    return root;
  }
  function readString(row, key) {
    const value = row[key];
    return typeof value === "string" ? value : "";
  }
  function readNumber(row, key) {
    const value = row[key];
    if (typeof value === "number" && Number.isFinite(value)) return value;
    if (typeof value === "string") {
      const parsed = Number(value);
      return Number.isFinite(parsed) ? parsed : 0;
    }
    return 0;
  }
  function normalizeExportedTweetData(projected, snapshot) {
    if (projected.legacy && projected.core) {
      return projected;
    }
    const row = projected;
    const restId = String(projected.rest_id || snapshot.source_id || snapshot.id);
    const screenName = readString(row, "screen_name") || readString(row, "author_screen_name");
    const profileName = readString(row, "name") || readString(row, "profile_name") || screenName;
    const profileImageUrl = readString(row, "profile_image_url");
    const createdAt = readString(row, "created_at") || (snapshot.observed_at ? new Date(snapshot.observed_at).toUTCString() : "");
    const fullText = readString(row, "full_text") || readString(row, "text") || readString(row, "content");
    const rawMedia = Array.isArray(row.media) ? row.media : [];
    const media = rawMedia.map((item, index) => {
      const original = readString(item, "original") || readString(item, "url");
      const thumbnail = readString(item, "thumbnail") || readString(item, "previewUrl") || original;
      return {
        id_str: readString(item, "id_str") || `${restId}-media-${index}`,
        media_key: readString(item, "media_key") || `${restId}-media-${index}`,
        type: readString(item, "type") || "photo",
        url: thumbnail,
        media_url_https: thumbnail || original,
        expanded_url: original,
        ext_alt_text: readString(item, "ext_alt_text") || readString(item, "altText"),
        original_info: {
          width: readNumber(item, "width") || void 0,
          height: readNumber(item, "height") || void 0
        },
        sizes: {
          large: {
            w: readNumber(item, "width") || 1,
            h: readNumber(item, "height") || 1,
            resize: "fit"
          }
        }
      };
    });
    projected.rest_id = restId;
    projected.legacy = {
      id_str: restId,
      full_text: fullText,
      created_at: createdAt,
      favorite_count: readNumber(row, "favorite_count") || readNumber(row, "favorites"),
      retweet_count: readNumber(row, "retweet_count") || readNumber(row, "retweets"),
      reply_count: readNumber(row, "reply_count") || readNumber(row, "replies"),
      bookmark_count: readNumber(row, "bookmark_count") || readNumber(row, "bookmarks"),
      quote_count: readNumber(row, "quote_count") || readNumber(row, "quotes"),
      entities: {
        urls: [],
        media
      },
      extended_entities: {
        media
      }
    };
    projected.core = {
      user_results: {
        result: {
          rest_id: readString(row, "user_id") || readString(row, "author_id") || "",
          core: {
            screen_name: screenName,
            name: profileName
          },
          avatar: {
            image_url: profileImageUrl
          },
          legacy: {
            screen_name: screenName,
            name: profileName,
            profile_image_url_https: profileImageUrl
          }
        }
      }
    };
    return projected;
  }
  function projectImportedSnapshot(snapshot) {
    const projected = unwrapMetadataRecord(snapshot.data);
    projected.__twe_imported_bundle_id = snapshot.bundle_id;
    projected.__twe_imported_snapshot_id = snapshot.id;
    projected.__twe_imported_source_id = snapshot.source_id;
    if (!projected.rest_id && snapshot.source_id) {
      projected.rest_id = snapshot.source_id;
    }
    if (snapshot.kind === "tweet") {
      normalizeExportedTweetData(projected, snapshot);
    }
    if (!projected.twe_private_fields || typeof projected.twe_private_fields !== "object") {
      projected.twe_private_fields = {
        created_at: snapshot.observed_at || snapshot.created_at,
        updated_at: snapshot.updated_at
      };
    }
    return projected;
  }
  function projectImportedSnapshots(snapshots, kind) {
    return snapshots.filter((snapshot) => !kind || snapshot.kind === kind).map((snapshot) => projectImportedSnapshot(snapshot));
  }
  const EXPORT_FORMAT = {
    JSON: "JSON",
    HTML: "HTML",
    CSV: "CSV"
  };
  function csvEscapeStr(str) {
    return `"${str.replace(/"/g, '""').replace(/\n/g, "\\n").replace(/\r/g, "\\r")}"`;
  }
  function saveFile(filename, content, prependBOM = false) {
    const link = document.createElement("a");
    let blob2;
    if (content instanceof Blob) {
      blob2 = content;
    } else {
      blob2 = new Blob(prependBOM ? [new Uint8Array([239, 187, 191]), content] : [content], {
        type: "text/plain;charset=utf-8"
      });
    }
    const url = URL.createObjectURL(blob2);
    link.href = url;
    link.download = filename;
    link.click();
    URL.revokeObjectURL(url);
  }
  async function exportData(data, format, filename, translations) {
    try {
      let content = "";
      let prependBOM = false;
      logger.info(`Exporting to ${format} file: ${filename}`);
      switch (format) {
        case EXPORT_FORMAT.JSON:
          content = await jsonExporter(data);
          break;
        case EXPORT_FORMAT.HTML:
          content = await htmlExporter(data, translations);
          break;
        case EXPORT_FORMAT.CSV:
          prependBOM = true;
          content = await csvExporter(data);
          break;
      }
      saveFile(filename, content, prependBOM);
    } catch (err2) {
      logger.errorWithBanner("Failed to export file", err2);
    }
  }
  async function jsonExporter(data) {
    return JSON.stringify(data, void 0, "  ");
  }
  async function htmlExporter(data, translations) {
    const table = document.createElement("table");
    const thead = document.createElement("thead");
    const tbody = document.createElement("tbody");
    const exportKeys = Object.keys(data[0] ?? {});
    const headerRow = document.createElement("tr");
    for (const exportKey of exportKeys) {
      const th = document.createElement("th");
      th.textContent = translations[exportKey] ?? exportKey;
      headerRow.appendChild(th);
    }
    thead.appendChild(headerRow);
    table.appendChild(thead);
    table.className = "table table-striped";
    for (const row of data) {
      const tr = document.createElement("tr");
      for (const exportKey of exportKeys) {
        const td2 = document.createElement("td");
        const value = row[exportKey];
        if (exportKey === "profile_image_url" || exportKey === "profile_banner_url") {
          const img = document.createElement("img");
          img.src = value;
          img.width = 50;
          td2.innerHTML = "";
          td2.appendChild(img);
        } else if (exportKey === "media") {
          if ((value == null ? void 0 : value.length) > 0) {
            for (const media of value) {
              const img = document.createElement("img");
              img.src = media.thumbnail;
              img.width = 50;
              img.alt = media.ext_alt_text || "";
              img.title = media.ext_alt_text || "";
              const link = document.createElement("a");
              link.href = media.original;
              link.target = "_blank";
              link.style.marginRight = "0.5em";
              link.appendChild(img);
              td2.appendChild(link);
            }
          }
        } else if (exportKey === "full_text" || exportKey === "description") {
          const p = document.createElement("p");
          p.textContent = typeof value === "string" ? value : JSON.stringify(value);
          p.style.whiteSpace = "pre-wrap";
          p.style.maxWidth = "640px";
          td2.appendChild(p);
        } else if (exportKey === "metadata") {
          const details = document.createElement("details");
          const summary = document.createElement("summary");
          summary.textContent = "Expand";
          details.appendChild(summary);
          const pre = document.createElement("pre");
          pre.textContent = JSON.stringify(value, void 0, "  ");
          details.appendChild(pre);
          td2.appendChild(details);
        } else if (exportKey === "url") {
          const link = document.createElement("a");
          link.href = value;
          link.target = "_blank";
          link.textContent = value;
          td2.appendChild(link);
        } else {
          td2.textContent = typeof value === "string" ? value : JSON.stringify(row[exportKey]);
        }
        tr.appendChild(td2);
      }
      tbody.appendChild(tr);
    }
    table.appendChild(tbody);
    return `
    <html>
      <head>
        <meta charset="utf-8">
        <title>Exported Data ${(/* @__PURE__ */ new Date()).toISOString()}</title>
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
      </head>
      <body>
        ${table.outerHTML}
      </body>
    </html>
  `;
  }
  async function csvExporter(data) {
    const headers = Object.keys(data[0] ?? {});
    let content = headers.join(",") + "\n";
    for (const row of data) {
      const values = headers.map((header) => {
        const value = row[header];
        if (typeof value === "string") {
          return csvEscapeStr(value);
        }
        if (typeof value === "object") {
          return csvEscapeStr(JSON.stringify(value));
        }
        return value;
      });
      content += values.join(",");
      content += "\n";
    }
    return content;
  }
  const exporter = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
    __proto__: null,
    EXPORT_FORMAT,
    csvEscapeStr,
    csvExporter,
    exportData,
    htmlExporter,
    jsonExporter,
    saveFile
  }, Symbol.toStringTag, { value: "Module" }));
  const ZIP_LOCAL_FILE_HEADER_SIGNATURE = 67324752;
  const ZIP_CENTRAL_FILE_HEADER_SIGNATURE = 33639248;
  const ZIP_END_OF_CENTRAL_DIRECTORY_SIGNATURE = 101010256;
  const ZIP_UTF8_FLAG = 2048;
  const ZIP_VERSION = 20;
  let crc32TableCache = null;
  function getCrc32Table() {
    if (crc32TableCache) return crc32TableCache;
    const table = new Uint32Array(256);
    for (let i = 0; i < 256; i++) {
      let c = i;
      for (let j = 0; j < 8; j++) {
        c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
      }
      table[i] = c >>> 0;
    }
    crc32TableCache = table;
    return table;
  }
  function crc32(bytes) {
    const table = getCrc32Table();
    let c = 4294967295;
    for (let i = 0; i < bytes.length; i++) {
      const byte = bytes[i] ?? 0;
      c = (table[(c ^ byte) & 255] ?? 0) ^ c >>> 8;
    }
    return (c ^ 4294967295) >>> 0;
  }
  function toDosTime(date) {
    const seconds = Math.floor(date.getSeconds() / 2);
    return (date.getHours() << 11 | date.getMinutes() << 5 | seconds) & 65535;
  }
  function toDosDate(date) {
    const year = Math.max(1980, date.getFullYear());
    return (year - 1980 << 9 | date.getMonth() + 1 << 5 | date.getDate()) & 65535;
  }
  function buildLocalFileHeader(nameBytes, crc2, size, dosTime, dosDate) {
    const header = new Uint8Array(30 + nameBytes.length);
    const view = new DataView(header.buffer, header.byteOffset, header.byteLength);
    view.setUint32(0, ZIP_LOCAL_FILE_HEADER_SIGNATURE, true);
    view.setUint16(4, ZIP_VERSION, true);
    view.setUint16(6, ZIP_UTF8_FLAG, true);
    view.setUint16(8, 0, true);
    view.setUint16(10, dosTime, true);
    view.setUint16(12, dosDate, true);
    view.setUint32(14, crc2, true);
    view.setUint32(18, size, true);
    view.setUint32(22, size, true);
    view.setUint16(26, nameBytes.length, true);
    view.setUint16(28, 0, true);
    header.set(nameBytes, 30);
    return header;
  }
  function buildCentralFileHeader(nameBytes, crc2, size, dosTime, dosDate, localHeaderOffset) {
    const header = new Uint8Array(46 + nameBytes.length);
    const view = new DataView(header.buffer, header.byteOffset, header.byteLength);
    view.setUint32(0, ZIP_CENTRAL_FILE_HEADER_SIGNATURE, true);
    view.setUint16(4, ZIP_VERSION, true);
    view.setUint16(6, ZIP_VERSION, true);
    view.setUint16(8, ZIP_UTF8_FLAG, true);
    view.setUint16(10, 0, true);
    view.setUint16(12, dosTime, true);
    view.setUint16(14, dosDate, true);
    view.setUint32(16, crc2, true);
    view.setUint32(20, size, true);
    view.setUint32(24, size, true);
    view.setUint16(28, nameBytes.length, true);
    view.setUint16(30, 0, true);
    view.setUint16(32, 0, true);
    view.setUint16(34, 0, true);
    view.setUint16(36, 0, true);
    view.setUint32(38, 0, true);
    view.setUint32(42, localHeaderOffset, true);
    header.set(nameBytes, 46);
    return header;
  }
  function buildEndOfCentralDirectory(entryCount, centralSize, centralOffset) {
    const footer = new Uint8Array(22);
    const view = new DataView(footer.buffer, footer.byteOffset, footer.byteLength);
    view.setUint32(0, ZIP_END_OF_CENTRAL_DIRECTORY_SIGNATURE, true);
    view.setUint16(4, 0, true);
    view.setUint16(6, 0, true);
    view.setUint16(8, entryCount, true);
    view.setUint16(10, entryCount, true);
    view.setUint32(12, centralSize, true);
    view.setUint32(16, centralOffset, true);
    view.setUint16(20, 0, true);
    return footer;
  }
  function toBlobPart(bytes) {
    return toOwnedArrayBuffer(bytes);
  }
  function toOwnedArrayBuffer(bytes) {
    const copy = new Uint8Array(bytes.byteLength);
    copy.set(bytes);
    return copy.buffer;
  }
  function sleep(ms) {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }
  function nextFrame() {
    return new Promise((resolve) => {
      if (typeof requestAnimationFrame === "function") {
        requestAnimationFrame(() => resolve());
        return;
      }
      setTimeout(resolve, 0);
    });
  }
  function isVideoLike(file) {
    return file.type === "video" || file.type === "animated_gif" || /\.mp4(?:[?#].*)?$/i.test(file.url);
  }
  function hostForUrl(url) {
    try {
      return new URL(url).hostname;
    } catch {
      return "";
    }
  }
  function normalizeDownloadOptions(optionsOrRateLimit) {
    if (typeof optionsOrRateLimit === "number") {
      return {
        minDelayBetweenStartsMs: Math.max(0, optionsOrRateLimit),
        globalConcurrency: 1,
        perHostConcurrency: 1,
        videoConcurrency: 1,
        maxRetries: 0
      };
    }
    const options2 = optionsOrRateLimit || {};
    return {
      minDelayBetweenStartsMs: Math.max(0, options2.minDelayBetweenStartsMs ?? 100),
      globalConcurrency: Math.max(1, Math.min(32, options2.globalConcurrency ?? 8)),
      perHostConcurrency: Math.max(1, Math.min(32, options2.perHostConcurrency ?? 8)),
      videoConcurrency: Math.max(1, Math.min(16, options2.videoConcurrency ?? 3)),
      maxRetries: Math.max(0, Math.min(8, options2.maxRetries ?? 3))
    };
  }
  function retryDelayMs(attempt) {
    const base = Math.min(3e4, 750 * 2 ** Math.max(0, attempt - 1));
    const jitter = 0.8 + Math.random() * 0.4;
    return Math.round(base * jitter);
  }
  async function fetchBlobWithRetry(file, maxRetries) {
    let lastError = null;
    for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
      try {
        const response = await fetch(file.url);
        if (!response.ok) {
          const error = new Error(`Failed to fetch ${file.url}: HTTP ${response.status}`);
          if (response.status === 403 || response.status === 404) {
            throw error;
          }
          lastError = error;
        } else {
          return await response.blob();
        }
      } catch (error) {
        lastError = error;
        const message = error instanceof Error ? error.message : String(error);
        if (/HTTP (403|404)/.test(message)) {
          throw error;
        }
      }
      if (attempt < maxRetries) {
        await sleep(retryDelayMs(attempt + 1));
      }
    }
    throw lastError instanceof Error ? lastError : new Error(`Failed to fetch ${file.url}`);
  }
  async function zipStreamDownload(zipFilename, files, onProgress, optionsOrRateLimit) {
    const options2 = normalizeDownloadOptions(optionsOrRateLimit);
    const blobFiles = new Array(files.length);
    const total = files.length;
    let nextIndex = 0;
    let completed = 0;
    let lastStartAt = 0;
    let activeGlobal = 0;
    let activeVideo = 0;
    const activeByHost = /* @__PURE__ */ new Map();
    const acquireStartSlot = async (file) => {
      const host = hostForUrl(file.url);
      const video = isVideoLike(file);
      while (true) {
        const hostActive = activeByHost.get(host) || 0;
        const elapsed = Date.now() - lastStartAt;
        if (activeGlobal < options2.globalConcurrency && hostActive < options2.perHostConcurrency && (!video || activeVideo < options2.videoConcurrency) && elapsed >= options2.minDelayBetweenStartsMs) {
          activeGlobal += 1;
          if (video) activeVideo += 1;
          activeByHost.set(host, hostActive + 1);
          lastStartAt = Date.now();
          return () => {
            activeGlobal -= 1;
            if (video) activeVideo -= 1;
            const currentHostActive = activeByHost.get(host) || 1;
            if (currentHostActive <= 1) {
              activeByHost.delete(host);
            } else {
              activeByHost.set(host, currentHostActive - 1);
            }
          };
        }
        await sleep(Math.max(25, Math.min(100, options2.minDelayBetweenStartsMs - elapsed)));
      }
    };
    const worker = async () => {
      while (nextIndex < files.length) {
        const index = nextIndex++;
        const file = files[index];
        if (!file) continue;
        const release = await acquireStartSlot(file);
        const start = Date.now();
        try {
          logger.debug(`Start downloading ${file.filename} from ${file.url}`);
          blobFiles[index] = {
            filename: file.filename,
            blob: await fetchBlobWithRetry(file, options2.maxRetries)
          };
          completed += 1;
          onProgress == null ? void 0 : onProgress(completed, total, file);
          logger.debug(`Finished downloading ${file.filename} in ${Date.now() - start}ms`);
        } finally {
          release();
        }
      }
    };
    const workerCount = Math.min(options2.globalConcurrency, files.length || 1);
    await Promise.all(Array.from({ length: workerCount }, () => worker()));
    await zipBlobFiles(
      zipFilename,
      blobFiles.filter((file) => !!file),
      typeof optionsOrRateLimit === "object" ? optionsOrRateLimit.onZipProgress : void 0
    );
  }
  async function zipBlobFiles(zipFilename, files, onProgress) {
    logger.info(`Exporting to ZIP file: ${zipFilename}`);
    const now = /* @__PURE__ */ new Date();
    const dosTime = toDosTime(now);
    const dosDate = toDosDate(now);
    const zipParts = [];
    const centralParts = [];
    let currentOffset = 0;
    let centralSize = 0;
    const textEncoder = new TextEncoder();
    const total = files.length;
    for (const [index, file] of files.entries()) {
      const { filename, blob: blob22 } = file;
      const nameBytes = textEncoder.encode(filename);
      const data = new Uint8Array(await blob22.arrayBuffer());
      const dataCrc = crc32(data);
      const size = data.byteLength;
      const localHeaderOffset = currentOffset;
      const localHeader = buildLocalFileHeader(nameBytes, dataCrc, size, dosTime, dosDate);
      zipParts.push(toBlobPart(localHeader), toBlobPart(data));
      currentOffset += localHeader.byteLength + data.byteLength;
      const centralHeader = buildCentralFileHeader(
        nameBytes,
        dataCrc,
        size,
        dosTime,
        dosDate,
        localHeaderOffset
      );
      centralParts.push(centralHeader);
      centralSize += centralHeader.byteLength;
      onProgress == null ? void 0 : onProgress(index + 1, total, file);
      if (index % 4 === 3) {
        await nextFrame();
      }
    }
    for (const centralPart of centralParts) {
      zipParts.push(toBlobPart(centralPart));
    }
    const footer = buildEndOfCentralDirectory(files.length, centralSize, currentOffset);
    zipParts.push(toBlobPart(footer));
    const blob2 = new Blob(zipParts, { type: "application/zip" });
    logger.info("Zip stream closed.");
    fileSaverEs.saveAs(blob2, zipFilename);
    return blob2;
  }
  async function migration_20250609(tx) {
    logger.info("Running migration_20250609: Migrating legacy user data format");
    let userCount = 0;
    await tx.table("users").toCollection().modify((user) => {
      if (user.core && user.avatar) {
        return;
      }
      migrateFromLegacyUser(user);
      logger.debug(`Migrated user: ${user.rest_id}`);
      user.twe_private_fields.migrated_at = Date.now();
      userCount++;
    });
    let tweetCount = 0;
    await tx.table("tweets").toCollection().modify((tweet) => {
      var _a2, _b2, _c, _d, _e, _f;
      const user = (_b2 = (_a2 = tweet.core) == null ? void 0 : _a2.user_results) == null ? void 0 : _b2.result;
      if (user && user.core && user.avatar) {
        return;
      }
      migrateFromLegacyUser(user);
      logger.debug(`Migrated tweet user: ${tweet.rest_id} `);
      const rtSource = extractRetweetedTweet(tweet);
      if ((_d = (_c = rtSource == null ? void 0 : rtSource.core) == null ? void 0 : _c.user_results) == null ? void 0 : _d.result) {
        migrateFromLegacyUser(rtSource.core.user_results.result);
        logger.debug(`Migrated retweeted user: ${rtSource.rest_id}`);
      }
      const qtSource = extractQuotedTweet(tweet);
      if ((_f = (_e = qtSource == null ? void 0 : qtSource.core) == null ? void 0 : _e.user_results) == null ? void 0 : _f.result) {
        migrateFromLegacyUser(qtSource.core.user_results.result);
        logger.debug(`Migrated quoted user: ${qtSource.rest_id}`);
      }
      tweet.twe_private_fields.migrated_at = Date.now();
      tweetCount++;
    });
    logger.info(`Migration completed: ${userCount} users and ${tweetCount} tweets updated.`);
  }
  function migrateFromLegacyUser(user) {
    const ul = user;
    if (!(ul == null ? void 0 : ul.legacy)) {
      logger.debug("migrateFromLegacyUser", user);
      logger.warn(`User ${user == null ? void 0 : user.rest_id} has no legacy data to migrate.`);
      return;
    }
    user.core ?? (user.core = {
      created_at: ul.legacy.created_at,
      name: ul.legacy.name,
      screen_name: ul.legacy.screen_name
    });
    user.avatar ?? (user.avatar = {
      image_url: ul.legacy.profile_image_url_https
    });
    user.verification ?? (user.verification = {
      verified: ul.legacy.verified,
      verified_type: ul.legacy.verified_type
    });
    user.relationship_perspectives ?? (user.relationship_perspectives = {
      following: ul.legacy.following,
      followed_by: ul.legacy.followed_by
    });
    user.dm_permissions ?? (user.dm_permissions = {
      can_dm: ul.legacy.can_dm
    });
    user.privacy ?? (user.privacy = {
      protected: ul.legacy.protected
    });
    user.location ?? (user.location = {
      location: ul.legacy.location
    });
  }
  function buildRelationshipFields(edges) {
    const relationTypes = Array.from(new Set(edges.map((edge) => edge.relation_type))).sort();
    const subjectUserIds = Array.from(new Set(edges.map((edge) => edge.subject_user_id))).sort();
    const subjectScreenNames = Array.from(
      new Set(edges.map((edge) => String(edge.subject_screen_name || "").trim()).filter(Boolean))
    ).sort();
    const lastObservedAt = Math.max(...edges.map((edge) => Number(edge.observed_at) || 0), 0);
    return {
      relation_types: relationTypes,
      subject_user_ids: subjectUserIds,
      subject_screen_names: subjectScreenNames,
      last_observed_at: lastObservedAt,
      edge_count: edges.length
    };
  }
  function enrichUsersWithRelationshipFields(users, edges) {
    if (!users.length || !edges.length) {
      return users;
    }
    const edgeMap = /* @__PURE__ */ new Map();
    for (const edge of edges) {
      const key = String(edge.related_user_id || "").trim();
      if (!key) continue;
      const bucket = edgeMap.get(key);
      if (bucket) {
        bucket.push(edge);
      } else {
        edgeMap.set(key, [edge]);
      }
    }
    return users.map((user) => {
      const relatedEdges = edgeMap.get(String(user.rest_id || "").trim());
      if (!(relatedEdges == null ? void 0 : relatedEdges.length)) {
        return user;
      }
      return {
        ...user,
        twe_relationship_fields: buildRelationshipFields(relatedEdges)
      };
    });
  }
  const PERF_BUFFER_KEY = "__twe_perf_events_v1";
  const PERF_SUMMARY_KEY = "__twe_perf_summary_v1";
  const PERF_EVENT_NAME = "twe:perf-event-v1";
  const PERF_BUFFER_LIMIT = 1e3;
  let longTaskObserverStarted = false;
  function getGlobalRecord() {
    return globalThis;
  }
  function createId() {
    if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
      return crypto.randomUUID();
    }
    return `perf-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
  }
  function readEventsMutable() {
    const g = getGlobalRecord();
    if (!Array.isArray(g[PERF_BUFFER_KEY])) {
      g[PERF_BUFFER_KEY] = [];
    }
    return g[PERF_BUFFER_KEY];
  }
  function readSummaryMutable() {
    const g = getGlobalRecord();
    const current = g[PERF_SUMMARY_KEY];
    if (!current || typeof current !== "object") {
      const next = { buckets: {}, counters: {}, workers: {} };
      g[PERF_SUMMARY_KEY] = next;
      return next;
    }
    const state = current;
    state.buckets || (state.buckets = {});
    state.counters || (state.counters = {});
    state.workers || (state.workers = {});
    return state;
  }
  function bucketKey(kind, name2) {
    return `${kind}:${name2}`;
  }
  function percentile(values, ratio) {
    var _a2;
    if (!values.length) return 0;
    const sorted = [...values].sort((a, b) => a - b);
    const index = Math.min(sorted.length - 1, Math.max(0, Math.ceil(sorted.length * ratio) - 1));
    return Number(((_a2 = sorted[index]) == null ? void 0 : _a2.toFixed(2)) || 0);
  }
  function sanitizeTags(tags) {
    if (!tags) return void 0;
    const out = {};
    for (const [key, value] of Object.entries(tags)) {
      if (value === void 0) continue;
      if (typeof value === "string") {
        out[key] = value.length > 160 ? `${value.slice(0, 160)}...` : value;
        continue;
      }
      out[key] = value;
    }
    return out;
  }
  function recordPerfMetric(args) {
    const event = {
      schema: "twe.perf.event.v1",
      id: createId(),
      name: args.name,
      kind: args.kind,
      atMs: Date.now(),
      durationMs: Number.isFinite(args.durationMs) ? Number(args.durationMs) : void 0,
      value: Number.isFinite(args.value) ? Number(args.value) : void 0,
      tags: sanitizeTags(args.tags)
    };
    const events = readEventsMutable();
    events.push(event);
    if (events.length > PERF_BUFFER_LIMIT) {
      events.splice(0, events.length - PERF_BUFFER_LIMIT);
    }
    const summary = readSummaryMutable();
    summary.counters[`${event.kind}:${event.name}:count`] = (summary.counters[`${event.kind}:${event.name}:count`] || 0) + 1;
    if (typeof event.value === "number") {
      summary.counters[`${event.kind}:${event.name}:value`] = event.value;
    }
    if (typeof event.durationMs === "number") {
      const key = bucketKey(event.kind, event.name);
      const bucket = summary.buckets[key] || { count: 0, totalMs: 0, maxMs: 0, values: [] };
      bucket.count += 1;
      bucket.totalMs += event.durationMs;
      bucket.maxMs = Math.max(bucket.maxMs, event.durationMs);
      bucket.values.push(event.durationMs);
      if (bucket.values.length > 300) {
        bucket.values.splice(0, bucket.values.length - 300);
      }
      summary.buckets[key] = bucket;
    }
    if (typeof window !== "undefined" && typeof window.dispatchEvent === "function") {
      try {
        window.dispatchEvent(new CustomEvent(PERF_EVENT_NAME, { detail: event }));
      } catch {
      }
    }
    return event;
  }
  function incrementPerfCounter(name2, value = 1) {
    const summary = readSummaryMutable();
    summary.counters[name2] = (summary.counters[name2] || 0) + value;
  }
  function setWorkerAvailability(worker, available) {
    const summary = readSummaryMutable();
    if (worker === "search") {
      summary.workers.searchWorkerAvailable = available;
    } else {
      summary.workers.exportWorkerAvailable = available;
    }
    recordPerfMetric({
      kind: "worker",
      name: `${worker}-availability`,
      value: available ? 1 : 0
    });
  }
  function readPerfDiagnostics() {
    const events = [...readEventsMutable()];
    const summary = readSummaryMutable();
    const buckets = {};
    for (const [key, bucket] of Object.entries(summary.buckets)) {
      buckets[key] = {
        count: bucket.count,
        p50Ms: percentile(bucket.values, 0.5),
        p95Ms: percentile(bucket.values, 0.95),
        maxMs: Number(bucket.maxMs.toFixed(2)),
        avgMs: bucket.count ? Number((bucket.totalMs / bucket.count).toFixed(2)) : 0
      };
    }
    return {
      schema: "twe.perf.diagnostics.v1",
      generatedAtMs: Date.now(),
      eventsBuffered: events.length,
      buckets,
      counters: { ...summary.counters },
      workers: { ...summary.workers },
      recent: events.slice(-100)
    };
  }
  function nowMs() {
    if (typeof performance !== "undefined" && typeof performance.now === "function") {
      return performance.now();
    }
    return Date.now();
  }
  function initializePerformanceMonitoring() {
    if (longTaskObserverStarted) return;
    longTaskObserverStarted = true;
    try {
      const ObserverCtor = globalThis.PerformanceObserver;
      if (!ObserverCtor) return;
      const supported = ObserverCtor.supportedEntryTypes;
      if (Array.isArray(supported) && !supported.includes("longtask")) return;
      const observer = new ObserverCtor((list) => {
        for (const entry of list.getEntries()) {
          recordPerfMetric({
            kind: "longtask",
            name: "main-thread-longtask",
            durationMs: entry.duration,
            tags: { entryType: entry.entryType }
          });
        }
      });
      observer.observe({ entryTypes: ["longtask"] });
    } catch {
    }
  }
  const DB_MUTATION_STORAGE_KEY$1 = "__twe_db_mutation_v1";
  const DB_MUTATION_CHANNEL_NAME = "twe-db-mutation-v1";
  const globalMutationVersion = signals.signal(0);
  const extensionMutationVersions = /* @__PURE__ */ new Map();
  let bridgeReady = false;
  let broadcastChannel = null;
  function getExtensionSignal(extName) {
    let extSignal = extensionMutationVersions.get(extName);
    if (!extSignal) {
      extSignal = signals.signal(0);
      extensionMutationVersions.set(extName, extSignal);
    }
    return extSignal;
  }
  function bumpMutationVersion(extName) {
    if (extName) {
      getExtensionSignal(extName).value += 1;
      return;
    }
    globalMutationVersion.value += 1;
  }
  function parseMutationMessage(value) {
    if (!value || typeof value !== "object") {
      return null;
    }
    const candidate = value;
    return {
      extension: typeof candidate.extension === "string" ? candidate.extension : void 0,
      operation: typeof candidate.operation === "string" ? candidate.operation : void 0
    };
  }
  function handleExternalMutationMessage(raw) {
    const parsed = parseMutationMessage(raw);
    if (!parsed) {
      return;
    }
    bumpMutationVersion(parsed == null ? void 0 : parsed.extension);
  }
  function ensureMutationBridge() {
    if (bridgeReady || typeof window === "undefined") {
      return;
    }
    bridgeReady = true;
    window.addEventListener("storage", (event) => {
      if (event.key !== DB_MUTATION_STORAGE_KEY$1 || !event.newValue) {
        return;
      }
      try {
        const parsed = JSON.parse(event.newValue);
        handleExternalMutationMessage(parsed);
      } catch {
      }
    });
    if (typeof BroadcastChannel !== "undefined") {
      try {
        broadcastChannel = new BroadcastChannel(DB_MUTATION_CHANNEL_NAME);
        broadcastChannel.onmessage = (event) => {
          handleExternalMutationMessage(event.data);
        };
      } catch {
        broadcastChannel = null;
      }
    }
  }
  function emitDatabaseMutation(event = {}) {
    ensureMutationBridge();
    const payload = {
      extension: event.extension,
      operation: event.operation,
      at: Date.now(),
      nonce: Math.random().toString(36).slice(2)
    };
    bumpMutationVersion(payload.extension);
    if (broadcastChannel) {
      try {
        broadcastChannel.postMessage(payload);
      } catch {
      }
    }
    try {
      if (typeof localStorage !== "undefined") {
        localStorage.setItem(DB_MUTATION_STORAGE_KEY$1, JSON.stringify(payload));
      }
    } catch {
    }
    recordDiagnosticDbEvent({
      ts: payload.at || Date.now(),
      extension: event.extension,
      operation: event.operation,
      count: typeof event.count === "number" ? event.count : void 0,
      keys: Array.isArray(event.keys) ? event.keys.slice(0, 20) : void 0
    });
  }
  function useDatabaseMutationVersion(extName) {
    ensureMutationBridge();
    const globalVersion = globalMutationVersion.value;
    if (!extName) {
      return globalVersion;
    }
    return globalVersion + getExtensionSignal(extName).value;
  }
  const DB_NAME = "twitter-web-exporter";
  const DB_VERSION = 6;
  const CAPTURE_COUNT_SNAPSHOT_KEY$1 = "__twe_capture_counts_v1";
  const CAPTURE_COUNT_SNAPSHOT_V2_KEY$1 = "__twe_capture_counts_v2";
  const ACTIVE_DB_NAME_KEY$1 = "__twe_active_db_name_v1";
  const CAPTURE_COUNT_EVENT_NAME$1 = "twe:capture-count-updated-v1";
  const BOOKMARK_CONTEXT_FIELDS = [
    "__bookmark_folder_id",
    "__bookmark_folder_name",
    "__bookmark_folder_name_source",
    "__bookmark_folder_url"
  ];
  function readPath$1(obj, path) {
    let current = obj;
    for (const part of path.split(".")) {
      if (!current || typeof current !== "object") return void 0;
      current = current[part];
    }
    return current;
  }
  function asSearchText(value) {
    if (typeof value === "string") return value.trim();
    if (typeof value === "number" && Number.isFinite(value)) return String(value);
    return "";
  }
  function simpleHash(value) {
    let hash = 0;
    for (let index = 0; index < value.length; index += 1) {
      hash = hash * 31 + value.charCodeAt(index) | 0;
    }
    return Math.abs(hash).toString(36);
  }
  function uniqText(values) {
    return [...new Set(values.map((value) => value.trim()).filter(Boolean))].join(" ");
  }
  function mergeTweetMetadata(existing, incoming) {
    if (!existing || typeof existing !== "object") {
      return incoming;
    }
    const merged = { ...incoming };
    const existingObj = existing;
    for (const field of BOOKMARK_CONTEXT_FIELDS) {
      const existingValue = existingObj[field];
      const incomingValue = incoming[field];
      if (incomingValue === void 0 && existingValue !== void 0) {
        merged[field] = existingValue;
        continue;
      }
      if (incomingValue === null && existingValue !== void 0 && existingValue !== null) {
        merged[field] = existingValue;
        continue;
      }
      if (field === "__bookmark_folder_name_source") {
        const incomingSource = String(incomingValue || "");
        const existingSource = String(existingValue || "");
        if (incomingSource === "id-only" && existingSource === "api") {
          merged[field] = existingSource;
        }
      }
    }
    return merged;
  }
  class DatabaseManager {
    constructor() {
      __publicField(this, "db");
      var _a2;
      let userId = "unknown";
      try {
        const globalObject = _unsafeWindow ?? (typeof window !== "undefined" ? window : void 0) ?? globalThis;
        userId = ((_a2 = globalObject.__META_DATA__) == null ? void 0 : _a2.userId) ?? "unknown";
      } catch {
        userId = "unknown";
      }
      const suffix = appOptionsManager.get("dedicatedDbForAccounts") ? `_${userId}` : "";
      logger.debug(`Using database: ${DB_NAME}${suffix} for userId: ${userId}`);
      this.db = new Dexie(`${DB_NAME}${suffix}`);
      this.publishActiveDatabaseName();
      this.init();
    }
    /*
    |--------------------------------------------------------------------------
    | Type-Safe Table Accessors
    |--------------------------------------------------------------------------
    */
    tweets() {
      return this.db.table("tweets");
    }
    users() {
      return this.db.table("users");
    }
    captures() {
      return this.db.table("captures");
    }
    socialEdges() {
      return this.db.table("social_edges");
    }
    importedBundles() {
      return this.db.table("imported_bundles");
    }
    importedBundleCollections() {
      return this.db.table("imported_bundle_collections");
    }
    importedBundleItems() {
      return this.db.table("imported_bundle_items");
    }
    importedEntitySnapshots() {
      return this.db.table("imported_entity_snapshots");
    }
    importedBundleImportReports() {
      return this.db.table("imported_bundle_import_reports");
    }
    searchDocuments() {
      return this.db.table("search_documents");
    }
    /*
    |--------------------------------------------------------------------------
    | Read Methods for Extensions
    |--------------------------------------------------------------------------
    */
    async extGetCaptures(extName) {
      return this.captures().where("extension").equals(extName).toArray().catch(this.logError);
    }
    async extGetCaptureCount(extName, type2) {
      if (type2) {
        return this.captures().where("[extension+type]").equals([extName, type2]).count().catch(this.logError);
      }
      return this.captures().where("extension").equals(extName).count().catch(this.logError);
    }
    async extGetCaptureDataKeys(extName) {
      const captures = await this.extGetCaptures(extName);
      if (!captures) {
        return [];
      }
      return this.normalizeDataKeys(captures.map((capture) => capture.data_key));
    }
    async extGetCapturePage(extName, args = {}) {
      const startedAt = nowMs();
      const offset = Math.max(0, Number(args.offset) || 0);
      const limit = Math.max(1, Math.min(1e3, Number(args.limit) || 100));
      let collection = args.type ? this.captures().where("[extension+type+created_at]").between([extName, args.type, Dexie.minKey], [extName, args.type, Dexie.maxKey]) : this.captures().where("extension").equals(extName);
      if (args.order !== "oldest") {
        collection = collection.reverse();
      }
      let rows = await collection.offset(offset).limit(limit).toArray().catch(this.logError);
      rows || (rows = []);
      if (args.type && !rows.every((capture) => capture.type === args.type)) {
        rows = rows.filter((capture) => capture.type === args.type);
      }
      const result = rows.slice(0, limit);
      recordPerfMetric({
        kind: "db",
        name: "capture-page",
        durationMs: nowMs() - startedAt,
        value: result.length,
        tags: { extName, type: args.type, offset, limit }
      });
      return result;
    }
    async extGetCaptureIdsPage(extName, args = {}) {
      const rows = await this.extGetCapturePage(extName, args);
      return this.normalizeDataKeys(rows.map((capture) => capture.data_key));
    }
    async extGetTweetsByIds(tweetIds) {
      const startedAt = nowMs();
      const ids = this.normalizeDataKeys(tweetIds);
      if (!ids.length) {
        return [];
      }
      return this.tweets().bulkGet(ids).then((rows) => {
        const result = rows.filter((row) => !!row && this.filterEmptyData(row));
        recordPerfMetric({
          kind: "db",
          name: "tweets-by-ids",
          durationMs: nowMs() - startedAt,
          value: result.length,
          tags: { requested: ids.length }
        });
        return result;
      }).catch(this.logError);
    }
    async extGetUsersByIds(userIds) {
      const startedAt = nowMs();
      const ids = this.normalizeDataKeys(userIds);
      if (!ids.length) {
        return [];
      }
      return this.users().bulkGet(ids).then((rows) => {
        const result = rows.filter((row) => !!row && this.filterEmptyData(row));
        recordPerfMetric({
          kind: "db",
          name: "users-by-ids",
          durationMs: nowMs() - startedAt,
          value: result.length,
          tags: { requested: ids.length }
        });
        return result;
      }).catch(this.logError);
    }
    async extGetCapturedTweets(extName, capturesOverride) {
      const captures = capturesOverride ?? await this.extGetCaptures(extName);
      if (!captures) {
        return [];
      }
      return this.extGetTweetsByIds(captures.map((capture) => capture.data_key));
    }
    async extGetCapturedUsers(extName, capturesOverride) {
      const captures = capturesOverride ?? await this.extGetCaptures(extName);
      if (!captures) {
        return [];
      }
      const users = await this.extGetUsersByIds(captures.map((capture) => capture.data_key)) ?? [];
      return this.enrichUsersWithRelationshipContext(extName, users);
    }
    async extGetSocialEdges(extName) {
      return this.socialEdges().where("extension").equals(extName).toArray().catch(this.logError);
    }
    async extGetSearchDocuments(extName, type2) {
      const startedAt = nowMs();
      const entityType = type2 === ExtensionType.USER ? "user" : type2 === ExtensionType.TWEET ? "tweet" : "";
      const rows = type2 && entityType ? await this.searchDocuments().where("[extension_name+entity_type]").equals([extName, entityType]).toArray().catch(this.logError) : await this.searchDocuments().where("extension_name").equals(extName).toArray().catch(this.logError);
      const result = rows ?? [];
      recordPerfMetric({
        kind: "db",
        name: "search-documents",
        durationMs: nowMs() - startedAt,
        value: result.length,
        tags: { extName, type: type2 }
      });
      return result;
    }
    async searchDocumentsForSource(sourceKey, entityType) {
      const rows = await this.searchDocuments().where("source_key").equals(sourceKey).toArray().catch(this.logError);
      if (!rows) return [];
      return entityType ? rows.filter((row) => row.entity_type === entityType) : rows;
    }
    async bundleList() {
      return this.importedBundles().orderBy("updatedAt").reverse().toArray().catch(this.logError);
    }
    async bundleGet(bundleId) {
      return this.importedBundles().get(bundleId).catch(this.logError);
    }
    async bundleGetCollections(bundleId) {
      return this.importedBundleCollections().where("bundle_id").equals(bundleId).toArray().catch(this.logError);
    }
    async bundleGetItems(bundleId, limit = 5e3) {
      return this.importedBundleItems().where("bundle_id").equals(bundleId).limit(limit).toArray().catch(this.logError);
    }
    async bundleGetSnapshots(bundleId, limit = 5e3) {
      return this.importedEntitySnapshots().where("bundle_id").equals(bundleId).limit(limit).toArray().catch(this.logError);
    }
    async bundleGetSnapshotCount(bundleId, kind) {
      if (kind) {
        return this.importedEntitySnapshots().where("[bundle_id+kind]").equals([bundleId, kind]).count().catch(this.logError);
      }
      return this.importedEntitySnapshots().where("bundle_id").equals(bundleId).count().catch(this.logError);
    }
    async bundleGetSnapshotPage(bundleId, args = {}) {
      const offset = Math.max(0, Number(args.offset || 0));
      const limit = Math.max(1, Number(args.limit || 5e3));
      const rows = args.kind ? await this.importedEntitySnapshots().where("[bundle_id+kind]").equals([bundleId, args.kind]).toArray().catch(this.logError) : await this.importedEntitySnapshots().where("bundle_id").equals(bundleId).toArray().catch(this.logError);
      return (rows ?? []).sort((left, right) => {
        const leftTime = Number(left.observed_at || left.created_at || left.updated_at || 0);
        const rightTime = Number(right.observed_at || right.created_at || right.updated_at || 0);
        if (leftTime !== rightTime) {
          return args.order === "oldest" ? leftTime - rightTime : rightTime - leftTime;
        }
        return right.id.localeCompare(left.id);
      }).slice(offset, offset + limit);
    }
    async bundleGetSnapshotsByIds(snapshotIds) {
      const ids = this.normalizeDataKeys(snapshotIds);
      if (!ids.length) return [];
      return this.importedEntitySnapshots().bulkGet(ids).then((rows) => rows.filter((row) => !!row)).catch(this.logError);
    }
    async bundleSearchSnapshots(bundleId, query, limit = 5e3) {
      const normalized = query.trim().toLowerCase();
      const table = this.importedEntitySnapshots().where("bundle_id").equals(bundleId);
      if (!normalized) {
        return table.limit(limit).toArray().catch(this.logError);
      }
      return table.filter(
        (snapshot) => String(snapshot.search_text || "").toLowerCase().includes(normalized)
      ).limit(limit).toArray().catch(this.logError);
    }
    /*
    |--------------------------------------------------------------------------
    | Write Methods for Extensions
    |--------------------------------------------------------------------------
    */
    async extAddTweets(extName, tweets) {
      if (!tweets.length) {
        return;
      }
      await this.upsertTweets(tweets);
      await this.upsertCaptures(
        tweets.map((tweet) => ({
          id: `${extName}-${tweet.rest_id}`,
          extension: extName,
          type: ExtensionType.TWEET,
          data_key: tweet.rest_id,
          created_at: Date.now()
        }))
      );
      await this.upsertSearchDocuments(this.buildTweetSearchDocuments(extName, tweets));
      emitDatabaseMutation({
        extension: extName,
        operation: "extAddTweets",
        count: tweets.length,
        keys: tweets.map((tweet) => tweet.rest_id)
      });
      void this.publishCaptureCountSnapshot(extName);
    }
    async extAddUsers(extName, users) {
      if (!users.length) {
        return;
      }
      await this.upsertUsers(users);
      await this.upsertCaptures(
        users.map((user) => ({
          id: `${extName}-${user.rest_id}`,
          extension: extName,
          type: ExtensionType.USER,
          data_key: user.rest_id,
          created_at: Date.now()
        }))
      );
      await this.upsertSearchDocuments(this.buildUserSearchDocuments(extName, users));
      emitDatabaseMutation({
        extension: extName,
        operation: "extAddUsers",
        count: users.length,
        keys: users.map((user) => user.rest_id)
      });
      void this.publishCaptureCountSnapshot(extName);
    }
    async extAddCustomCaptures(extName, items) {
      if (!items.length) {
        return;
      }
      const captures = [];
      for (const item of items) {
        const id2 = String(item.id || "").trim();
        const dataKey = String(item.data_key || "").trim();
        if (!id2 || !dataKey) {
          continue;
        }
        captures.push({
          id: `${extName}-${id2}`,
          extension: extName,
          type: ExtensionType.CUSTOM,
          data_key: dataKey,
          created_at: Number(item.created_at) || Date.now()
        });
      }
      if (!captures.length) {
        return;
      }
      await this.upsertCaptures(captures);
      emitDatabaseMutation({
        extension: extName,
        operation: "extAddCustomCaptures",
        count: captures.length,
        keys: captures.map((capture) => capture.data_key)
      });
      void this.publishCaptureCountSnapshot(extName);
    }
    async extAddSocialEdges(extName, edges) {
      const normalized = edges.map((edge) => ({
        ...edge,
        extension: extName,
        observed_at: Number(edge.observed_at) || Date.now()
      })).filter(
        (edge) => edge.subject_user_id && edge.related_user_id && edge.relation_type && edge.id
      );
      if (!normalized.length) {
        return;
      }
      await this.upsertSocialEdges(normalized);
      emitDatabaseMutation({
        extension: extName,
        operation: "extAddSocialEdges",
        count: normalized.length,
        keys: normalized.map((edge) => edge.id)
      });
    }
    async extAddTweetCaptureIds(extName, tweetIds, mutateExisting) {
      const ids = this.normalizeDataKeys(tweetIds);
      if (!ids.length) {
        return;
      }
      await this.db.transaction("rw", this.tweets(), this.captures(), this.searchDocuments(), async () => {
        let existingRows = [];
        if (mutateExisting) {
          existingRows = await this.tweets().where("rest_id").anyOf(ids).toArray();
          if (existingRows.length) {
            await this.tweets().bulkPut(existingRows.map((row) => mutateExisting(row)));
          }
        } else {
          existingRows = await this.tweets().where("rest_id").anyOf(ids).toArray();
        }
        await this.captures().bulkPut(
          ids.map((tweetId) => ({
            id: `${extName}-${tweetId}`,
            extension: extName,
            type: ExtensionType.TWEET,
            data_key: tweetId,
            created_at: Date.now()
          }))
        );
        await this.searchDocuments().bulkPut(this.buildTweetSearchDocuments(extName, existingRows));
      }).catch(this.logError);
      emitDatabaseMutation({
        extension: extName,
        operation: "extAddTweetCaptureIds",
        count: ids.length,
        keys: ids
      });
      void this.publishCaptureCountSnapshot(extName);
    }
    async extBackfillTweetCapturesFromAllTweets(extName) {
      const existingCount = await this.extGetCaptureCount(extName);
      if (existingCount) {
        return;
      }
      const keys = await this.tweets().toCollection().primaryKeys().then((items) => items.map((item) => String(item || "")).filter(Boolean)).catch(this.logError);
      if (!(keys == null ? void 0 : keys.length)) {
        return;
      }
      await this.extAddTweetCaptureIds(extName, keys);
    }
    async extRemoveTweetCaptureIds(extName, tweetIds, mutateExisting) {
      const ids = this.normalizeDataKeys(tweetIds);
      if (!ids.length) {
        return;
      }
      await this.db.transaction("rw", this.tweets(), this.captures(), this.searchDocuments(), async () => {
        if (mutateExisting) {
          const existingRows = await this.tweets().where("rest_id").anyOf(ids).toArray();
          if (existingRows.length) {
            await this.tweets().bulkPut(existingRows.map((row) => mutateExisting(row)));
          }
        }
        await this.captures().bulkDelete(ids.map((tweetId) => `${extName}-${tweetId}`));
        await this.searchDocuments().bulkDelete(
          ids.map((tweetId) => `live:${extName}:tweet:${tweetId}`)
        );
      }).catch(this.logError);
      emitDatabaseMutation({
        extension: extName,
        operation: "extRemoveTweetCaptureIds",
        count: ids.length,
        keys: ids
      });
      void this.publishCaptureCountSnapshot(extName);
    }
    async extAddUserCaptureIds(extName, userIds, mutateExisting) {
      const ids = this.normalizeDataKeys(userIds);
      if (!ids.length) {
        return;
      }
      await this.db.transaction("rw", this.users(), this.captures(), this.searchDocuments(), async () => {
        let existingRows = [];
        if (mutateExisting) {
          existingRows = await this.users().where("rest_id").anyOf(ids).toArray();
          if (existingRows.length) {
            await this.users().bulkPut(existingRows.map((row) => mutateExisting(row)));
          }
        } else {
          existingRows = await this.users().where("rest_id").anyOf(ids).toArray();
        }
        await this.captures().bulkPut(
          ids.map((userId) => ({
            id: `${extName}-${userId}`,
            extension: extName,
            type: ExtensionType.USER,
            data_key: userId,
            created_at: Date.now()
          }))
        );
        await this.searchDocuments().bulkPut(this.buildUserSearchDocuments(extName, existingRows));
      }).catch(this.logError);
      emitDatabaseMutation({
        extension: extName,
        operation: "extAddUserCaptureIds",
        count: ids.length,
        keys: ids
      });
      void this.publishCaptureCountSnapshot(extName);
    }
    async extRemoveUserCaptureIds(extName, userIds, mutateExisting) {
      const ids = this.normalizeDataKeys(userIds);
      if (!ids.length) {
        return;
      }
      await this.db.transaction("rw", this.users(), this.captures(), this.searchDocuments(), async () => {
        if (mutateExisting) {
          const existingRows = await this.users().where("rest_id").anyOf(ids).toArray();
          if (existingRows.length) {
            await this.users().bulkPut(existingRows.map((row) => mutateExisting(row)));
          }
        }
        await this.captures().bulkDelete(ids.map((userId) => `${extName}-${userId}`));
        await this.searchDocuments().bulkDelete(
          ids.map((userId) => `live:${extName}:user:${userId}`)
        );
      }).catch(this.logError);
      emitDatabaseMutation({
        extension: extName,
        operation: "extRemoveUserCaptureIds",
        count: ids.length,
        keys: ids
      });
      void this.publishCaptureCountSnapshot(extName);
    }
    async bundlePutImportBatch(args) {
      var _a2;
      const now = Date.now();
      const bundle = {
        ...args.bundle,
        updatedAt: now
      };
      await this.db.transaction(
        "rw",
        [
          this.importedBundles(),
          this.importedBundleCollections(),
          this.importedBundleItems(),
          this.importedEntitySnapshots(),
          this.importedBundleImportReports(),
          this.searchDocuments()
        ],
        async () => {
          var _a3, _b2, _c;
          await this.importedBundles().put(bundle);
          if ((_a3 = args.collections) == null ? void 0 : _a3.length) {
            await this.importedBundleCollections().bulkPut(args.collections);
          }
          if ((_b2 = args.items) == null ? void 0 : _b2.length) {
            await this.importedBundleItems().bulkPut(args.items);
          }
          if ((_c = args.snapshots) == null ? void 0 : _c.length) {
            await this.importedEntitySnapshots().bulkPut(args.snapshots);
            await this.searchDocuments().bulkPut(
              this.buildImportedSnapshotSearchDocuments(bundle.id, args.snapshots)
            );
          }
          if (args.report) {
            await this.importedBundleImportReports().put(args.report);
          }
        }
      ).catch(this.logError);
      emitDatabaseMutation({
        operation: "bundlePutImportBatch",
        count: ((_a2 = args.snapshots) == null ? void 0 : _a2.length) ?? 0,
        keys: [bundle.id]
      });
    }
    async bundleMarkReady(bundleId) {
      await this.importedBundles().update(bundleId, {
        status: "ready",
        updatedAt: Date.now()
      }).catch(this.logError);
      emitDatabaseMutation({ operation: "bundleMarkReady", keys: [bundleId] });
    }
    async bundleMarkFailed(bundleId, error) {
      await this.importedBundles().update(bundleId, {
        status: "failed",
        error,
        updatedAt: Date.now()
      }).catch(this.logError);
      emitDatabaseMutation({ operation: "bundleMarkFailed", keys: [bundleId] });
    }
    /*
    |--------------------------------------------------------------------------
    | Delete Methods for Extensions
    |--------------------------------------------------------------------------
    */
    async extClearCaptures(extName) {
      const captures = await this.extGetCaptures(extName);
      if (!captures) {
        return;
      }
      const result = await this.db.transaction("rw", this.captures(), this.searchDocuments(), async () => {
        const deleted = await this.captures().bulkDelete(captures.map((capture) => capture.id));
        const searchDocIds = captures.map((capture) => {
          if (capture.type === ExtensionType.TWEET) {
            return `live:${extName}:tweet:${capture.data_key}`;
          }
          if (capture.type === ExtensionType.USER) {
            return `live:${extName}:user:${capture.data_key}`;
          }
          return "";
        }).filter(Boolean);
        if (searchDocIds.length) {
          await this.searchDocuments().bulkDelete(searchDocIds);
        }
        return deleted;
      }).catch(this.logError);
      emitDatabaseMutation({
        extension: extName,
        operation: "extClearCaptures",
        count: captures.length,
        keys: captures.map((capture) => capture.data_key)
      });
      void this.publishCaptureCountSnapshot(extName);
      return result;
    }
    async extBackfillRecentBookmarkFolderName(extName, folderId, folderName, options2 = {}) {
      if (!extName || !folderId || !folderName) {
        return { candidates: 0, inspected: 0, updated: 0 };
      }
      const candidateLimit = Math.max(1, Math.min(1e3, Number(options2.candidateLimit) || 250));
      const recentCaptureScanLimit = Math.max(
        100,
        Math.min(5e3, Number(options2.recentCaptureScanLimit) || 1800)
      );
      const candidateIds = /* @__PURE__ */ new Set();
      for (const id2 of options2.candidateTweetIds || []) {
        if (typeof id2 !== "string") continue;
        const normalized = id2.trim();
        if (!normalized) continue;
        candidateIds.add(normalized);
        if (candidateIds.size >= candidateLimit) break;
      }
      if (candidateIds.size < candidateLimit) {
        const recent = await this.captures().orderBy("created_at").reverse().limit(recentCaptureScanLimit).toArray().catch(this.logError);
        for (const row of recent || []) {
          if ((row == null ? void 0 : row.extension) !== extName || (row == null ? void 0 : row.type) !== ExtensionType.TWEET) {
            continue;
          }
          const normalized = String((row == null ? void 0 : row.data_key) || "").trim();
          if (!normalized || candidateIds.has(normalized)) {
            continue;
          }
          candidateIds.add(normalized);
          if (candidateIds.size >= candidateLimit) {
            break;
          }
        }
      }
      if (!candidateIds.size) {
        return { candidates: 0, inspected: 0, updated: 0 };
      }
      const candidateArray = [...candidateIds];
      return await this.db.transaction("rw", this.tweets(), this.searchDocuments(), async () => {
        const rows = await this.tweets().where("rest_id").anyOf(candidateArray).toArray();
        const updates = [];
        for (const row of rows) {
          const current = row;
          if (String(current.__bookmark_folder_id || "") !== folderId) {
            continue;
          }
          const currentName = String(current.__bookmark_folder_name || "");
          const currentSource = String(current.__bookmark_folder_name_source || "");
          if (currentName === folderName && currentSource === "api") {
            continue;
          }
          updates.push({
            ...row,
            ...{
              __bookmark_folder_name: folderName,
              __bookmark_folder_name_source: "api"
            }
          });
        }
        if (updates.length) {
          await this.tweets().bulkPut(updates);
          await this.searchDocuments().bulkPut(this.buildTweetSearchDocuments(extName, updates));
          emitDatabaseMutation({
            extension: extName,
            operation: "bookmarkFolderNameBackfill"
          });
        }
        return {
          candidates: candidateArray.length,
          inspected: rows.length,
          updated: updates.length
        };
      }).catch((error) => {
        this.logError(error);
        return {
          candidates: candidateArray.length,
          inspected: 0,
          updated: 0
        };
      });
    }
    /*
    |--------------------------------------------------------------------------
    | Export and Import Methods
    |--------------------------------------------------------------------------
    */
    async export() {
      return dexieExportImport.exportDB(this.db).catch(this.logError);
    }
    async import(data) {
      const result = await dexieExportImport.importInto(this.db, data).catch(this.logError);
      emitDatabaseMutation({
        operation: "import"
      });
      this.publishCaptureCountSnapshotForAllKnownExtensions();
      return result;
    }
    async clear() {
      await this.deleteAllCaptures();
      await this.deleteAllSocialEdges();
      await this.deleteAllSearchDocuments();
      await this.deleteAllTweets();
      await this.deleteAllUsers();
      emitDatabaseMutation({
        operation: "clear"
      });
      this.publishCaptureCountSnapshotForAllKnownExtensions();
      logger.info("Database cleared");
    }
    async count() {
      try {
        return {
          tweets: await this.tweets().count(),
          users: await this.users().count(),
          captures: await this.captures().count(),
          social_edges: await this.socialEdges().count(),
          imported_bundles: await this.importedBundles().count(),
          imported_entity_snapshots: await this.importedEntitySnapshots().count(),
          search_documents: await this.searchDocuments().count()
        };
      } catch (error) {
        this.logError(error);
        return null;
      }
    }
    async publishCaptureCountSnapshot(extName) {
      try {
        const count = Number(await this.extGetCaptureCount(extName) || 0);
        const dbName = this.db.name;
        const updatedAt = Date.now();
        const globalObject = globalThis;
        const current = globalObject[CAPTURE_COUNT_SNAPSHOT_KEY$1];
        const map = current && typeof current === "object" ? { ...current } : {};
        map[extName] = count;
        const currentV2 = globalObject[CAPTURE_COUNT_SNAPSHOT_V2_KEY$1];
        const mapV2 = currentV2 && typeof currentV2 === "object" ? { ...currentV2 } : {};
        mapV2[extName] = { count, dbName, updatedAt };
        globalObject[CAPTURE_COUNT_SNAPSHOT_KEY$1] = map;
        globalObject[CAPTURE_COUNT_SNAPSHOT_V2_KEY$1] = mapV2;
        if (typeof window !== "undefined") {
          window[CAPTURE_COUNT_SNAPSHOT_KEY$1] = map;
          window[CAPTURE_COUNT_SNAPSHOT_V2_KEY$1] = mapV2;
        }
        try {
          if (typeof localStorage !== "undefined") {
            localStorage.setItem(CAPTURE_COUNT_SNAPSHOT_KEY$1, JSON.stringify(map));
            localStorage.setItem(CAPTURE_COUNT_SNAPSHOT_V2_KEY$1, JSON.stringify(mapV2));
          }
        } catch {
        }
        try {
          if (typeof window !== "undefined" && typeof window.dispatchEvent === "function") {
            const detail = {
              extension: extName,
              count,
              dbName,
              updatedAt
            };
            try {
              window.dispatchEvent(
                new CustomEvent(CAPTURE_COUNT_EVENT_NAME$1, {
                  detail
                })
              );
            } catch {
              window.dispatchEvent(new Event(CAPTURE_COUNT_EVENT_NAME$1));
            }
          }
        } catch {
        }
      } catch {
      }
    }
    publishCaptureCountSnapshotForAllKnownExtensions() {
      void this.captures().toArray().then((rows) => {
        const set = /* @__PURE__ */ new Set();
        for (const row of rows) {
          if (row == null ? void 0 : row.extension) {
            set.add(String(row.extension));
          }
        }
        return Promise.all([...set].map((extName) => this.publishCaptureCountSnapshot(extName)));
      }).catch(() => {
      });
    }
    publishActiveDatabaseName() {
      try {
        const dbName = this.db.name;
        const globalObject = globalThis;
        globalObject[ACTIVE_DB_NAME_KEY$1] = dbName;
        if (typeof window !== "undefined") {
          window[ACTIVE_DB_NAME_KEY$1] = dbName;
        }
        if (typeof localStorage !== "undefined") {
          localStorage.setItem(ACTIVE_DB_NAME_KEY$1, dbName);
        }
      } catch {
      }
    }
    /*
    |--------------------------------------------------------------------------
    | Common Methods
    |--------------------------------------------------------------------------
    */
    buildTweetSearchDocuments(extName, tweets) {
      const now = Date.now();
      const rows = [];
      for (const tweet of tweets) {
        const obj = tweet;
        const id2 = String(tweet.rest_id || readPath$1(obj, "legacy.id_str") || "").trim();
        if (!id2) continue;
        const articleTitle = asSearchText(readPath$1(obj, "article.article_results.result.title"));
        const articlePreview = asSearchText(
          readPath$1(obj, "article.article_results.result.preview_text")
        );
        const fullText = uniqText([
          asSearchText(readPath$1(obj, "note_tweet.note_tweet_results.result.text")),
          articleTitle,
          articlePreview,
          asSearchText(readPath$1(obj, "legacy.full_text")),
          asSearchText(readPath$1(obj, "legacy.text"))
        ]);
        const quotedText = uniqText([
          asSearchText(
            readPath$1(obj, "quoted_status_result.result.note_tweet.note_tweet_results.result.text")
          ),
          asSearchText(readPath$1(obj, "quoted_status_result.result.legacy.full_text")),
          asSearchText(readPath$1(obj, "quoted_status_result.result.legacy.text"))
        ]);
        const authorScreenName = asSearchText(
          readPath$1(obj, "core.user_results.result.core.screen_name") || readPath$1(obj, "core.screen_name")
        ).toLowerCase();
        const authorId = asSearchText(
          readPath$1(obj, "core.user_results.result.rest_id") || readPath$1(obj, "author_id")
        );
        const folderId = asSearchText(readPath$1(obj, "__bookmark_folder_id"));
        const folderName = asSearchText(readPath$1(obj, "__bookmark_folder_name"));
        const createdAtMs = extractTweetCreatedAtMs(tweet);
        const primaryText = uniqText([fullText, authorScreenName, folderId, folderName]);
        const auxiliaryText = uniqText([
          asSearchText(readPath$1(obj, "legacy.lang")),
          asSearchText(readPath$1(obj, "card.name")),
          asSearchText(readPath$1(obj, "card.card_platform.card_name"))
        ]);
        const hasMedia = extractTweetMedia(tweet).length > 0;
        const docHash = simpleHash([primaryText, quotedText, auxiliaryText].join("\n"));
        rows.push({
          id: `live:${extName}:tweet:${id2}`,
          source_key: `live:${extName}`,
          source_kind: "live",
          entity_type: "tweet",
          entity_id: id2,
          extension_name: extName,
          updated_at_ms: now,
          created_at_ms: createdAtMs || void 0,
          observed_at_ms: now,
          primary_text: primaryText,
          quoted_text: quotedText || void 0,
          auxiliary_text: auxiliaryText || void 0,
          author_screen_name: authorScreenName || void 0,
          author_id: authorId || void 0,
          folder_id: folderId || void 0,
          folder_name: folderName || void 0,
          route_type: asSearchText(readPath$1(obj, "__route_type")) || void 0,
          lang: asSearchText(readPath$1(obj, "legacy.lang")) || void 0,
          flags_json: { has_media: hasMedia },
          exact_json: {
            author: authorScreenName ? [authorScreenName, `@${authorScreenName}`] : [],
            folder: [folderId, folderName].filter(Boolean)
          },
          numeric_json: {
            favorite_count: Number(readPath$1(obj, "legacy.favorite_count") || 0),
            retweet_count: Number(readPath$1(obj, "legacy.retweet_count") || 0),
            reply_count: Number(readPath$1(obj, "legacy.reply_count") || 0),
            bookmark_count: Number(readPath$1(obj, "legacy.bookmark_count") || 0)
          },
          raw_ref_table: "tweets",
          raw_ref_key: id2,
          doc_hash: docHash
        });
      }
      return rows;
    }
    buildUserSearchDocuments(extName, users) {
      var _a2;
      const now = Date.now();
      const rows = [];
      for (const user of users) {
        const obj = user;
        const id2 = String(user.rest_id || "").trim();
        if (!id2) continue;
        const screenName = asSearchText(readPath$1(obj, "core.screen_name")).toLowerCase();
        const name2 = asSearchText(readPath$1(obj, "core.name"));
        const description2 = asSearchText(readPath$1(obj, "legacy.description"));
        const primaryText = uniqText([screenName, name2, description2]);
        rows.push({
          id: `live:${extName}:user:${id2}`,
          source_key: `live:${extName}`,
          source_kind: "live",
          entity_type: "user",
          entity_id: id2,
          extension_name: extName,
          updated_at_ms: now,
          created_at_ms: Number(((_a2 = user.twe_private_fields) == null ? void 0 : _a2.created_at) || 0) || void 0,
          observed_at_ms: now,
          primary_text: primaryText,
          author_screen_name: screenName || void 0,
          author_id: id2,
          flags_json: {
            is_blue_verified: Boolean(readPath$1(obj, "is_blue_verified"))
          },
          exact_json: {
            author: screenName ? [screenName, `@${screenName}`] : []
          },
          raw_ref_table: "users",
          raw_ref_key: id2,
          doc_hash: simpleHash(primaryText)
        });
      }
      return rows;
    }
    buildImportedSnapshotSearchDocuments(bundleId, snapshots) {
      const now = Date.now();
      return snapshots.map((snapshot) => {
        const data = snapshot.data;
        const projected = snapshot.kind === "tweet" || snapshot.kind === "user" ? projectImportedSnapshot(snapshot) : data;
        const sourceId = String(snapshot.source_id || snapshot.id).trim();
        const searchText = asSearchText(snapshot.search_text) || uniqText([JSON.stringify(data)]);
        const folderId = asSearchText(readPath$1(projected, "__bookmark_folder_id"));
        const folderName = asSearchText(readPath$1(projected, "__bookmark_folder_name"));
        return {
          id: `bundle:${bundleId}:${snapshot.kind}:${snapshot.id}`,
          source_key: `bundle:${bundleId}`,
          source_kind: "bundle",
          entity_type: snapshot.kind === "user" ? "user" : snapshot.kind === "tweet" ? "tweet" : "bundle_item",
          entity_id: sourceId,
          bundle_id: bundleId,
          bundle_item_id: snapshot.id,
          extension_name: snapshot.source_extension,
          updated_at_ms: now,
          created_at_ms: snapshot.created_at,
          observed_at_ms: snapshot.observed_at,
          primary_text: searchText,
          folder_id: folderId || void 0,
          folder_name: folderName || void 0,
          exact_json: {
            folder: [folderId, folderName].filter(Boolean)
          },
          raw_ref_table: "imported_entity_snapshots",
          raw_ref_key: snapshot.id,
          doc_hash: simpleHash(searchText)
        };
      });
    }
    async upsertSearchDocuments(rows) {
      if (!rows.length) return;
      const startedAt = nowMs();
      return this.db.transaction("rw", this.searchDocuments(), () => {
        return this.searchDocuments().bulkPut(rows).catch(this.logError);
      }).then((result) => {
        recordPerfMetric({
          kind: "db",
          name: "search-documents-upsert",
          durationMs: nowMs() - startedAt,
          value: rows.length
        });
        return result;
      }).catch(this.logError);
    }
    async extBackfillSearchDocuments(extName, type2, chunkSize = 640) {
      const startedAt = nowMs();
      const entityType = type2 === ExtensionType.USER ? "user" : type2 === ExtensionType.TWEET ? "tweet" : null;
      if (!entityType) {
        return { processed: 0, documents: 0 };
      }
      let offset = 0;
      let processed = 0;
      let documents = 0;
      while (true) {
        const captures = await this.extGetCapturePage(extName, {
          type: type2,
          offset,
          limit: chunkSize,
          order: "newest"
        });
        if (!captures.length) break;
        const observedAtByKey = new Map(
          captures.map((capture) => [capture.data_key, Number(capture.created_at) || Date.now()])
        );
        if (type2 === ExtensionType.USER) {
          const users = await this.extGetCapturedUsers(extName, captures) ?? [];
          const rows = this.buildUserSearchDocuments(extName, users);
          rows.forEach((row) => {
            row.observed_at_ms = observedAtByKey.get(row.raw_ref_key) || row.observed_at_ms;
          });
          await this.upsertSearchDocuments(rows);
          documents += rows.length;
        } else {
          const tweets = await this.extGetCapturedTweets(extName, captures) ?? [];
          const rows = this.buildTweetSearchDocuments(extName, tweets);
          rows.forEach((row) => {
            row.observed_at_ms = observedAtByKey.get(row.raw_ref_key) || row.observed_at_ms;
          });
          await this.upsertSearchDocuments(rows);
          documents += rows.length;
        }
        processed += captures.length;
        offset += captures.length;
        if (captures.length < chunkSize) break;
      }
      recordPerfMetric({
        kind: "db",
        name: "search-documents-backfill",
        durationMs: nowMs() - startedAt,
        value: documents,
        tags: { extName, type: type2, processed }
      });
      emitDatabaseMutation({ extension: extName, operation: "searchDocumentsBackfill" });
      return { processed, documents };
    }
    async upsertTweets(tweets) {
      if (!tweets.length) {
        return;
      }
      return this.db.transaction("rw", this.tweets(), async () => {
        const ids = tweets.map((tweet) => tweet.rest_id);
        const existingRows = await this.tweets().where("rest_id").anyOf(ids).toArray();
        const existingById = new Map(existingRows.map((row) => [String(row.rest_id), row]));
        const data = tweets.map((tweet) => {
          const normalized = {
            ...tweet,
            twe_private_fields: {
              created_at: extractTweetCreatedAtMs(tweet),
              updated_at: Date.now(),
              media_count: extractTweetMedia(tweet).length
            }
          };
          return mergeTweetMetadata(existingById.get(tweet.rest_id) ?? null, normalized);
        });
        return this.tweets().bulkPut(data);
      }).catch(this.logError);
    }
    async upsertUsers(users) {
      return this.db.transaction("rw", this.users(), () => {
        const data = users.map((user) => ({
          ...user,
          twe_private_fields: {
            created_at: +parseTwitterDateTime(user.core.created_at),
            updated_at: Date.now()
          }
        }));
        return this.users().bulkPut(data);
      }).catch(this.logError);
    }
    async upsertCaptures(captures) {
      return this.db.transaction("rw", this.captures(), () => {
        return this.captures().bulkPut(captures).catch(this.logError);
      }).catch(this.logError);
    }
    async upsertSocialEdges(edges) {
      return this.db.transaction("rw", this.socialEdges(), () => {
        return this.socialEdges().bulkPut(edges).catch(this.logError);
      }).catch(this.logError);
    }
    async deleteAllTweets() {
      return this.tweets().clear().catch(this.logError);
    }
    async deleteAllUsers() {
      return this.users().clear().catch(this.logError);
    }
    async deleteAllCaptures() {
      return this.captures().clear().catch(this.logError);
    }
    async deleteAllSocialEdges() {
      return this.socialEdges().clear().catch(this.logError);
    }
    async deleteAllSearchDocuments() {
      return this.searchDocuments().clear().catch(this.logError);
    }
    async bundleDelete(bundleId) {
      await this.db.transaction(
        "rw",
        [
          this.importedBundles(),
          this.importedBundleCollections(),
          this.importedBundleItems(),
          this.importedEntitySnapshots(),
          this.importedBundleImportReports(),
          this.searchDocuments()
        ],
        async () => {
          const collections = await this.importedBundleCollections().where("bundle_id").equals(bundleId).primaryKeys();
          const items = await this.importedBundleItems().where("bundle_id").equals(bundleId).primaryKeys();
          const snapshots = await this.importedEntitySnapshots().where("bundle_id").equals(bundleId).primaryKeys();
          const reports = await this.importedBundleImportReports().where("bundle_id").equals(bundleId).primaryKeys();
          const searchDocs = await this.searchDocuments().where("bundle_id").equals(bundleId).primaryKeys();
          await this.importedBundleCollections().bulkDelete(collections);
          await this.importedBundleItems().bulkDelete(items);
          await this.importedEntitySnapshots().bulkDelete(snapshots);
          await this.importedBundleImportReports().bulkDelete(reports);
          await this.searchDocuments().bulkDelete(searchDocs);
          await this.importedBundles().delete(bundleId);
        }
      ).catch(this.logError);
      emitDatabaseMutation({ operation: "bundleDelete", keys: [bundleId] });
    }
    async enrichUsersWithRelationshipContext(extName, users) {
      if (!users.length || extName !== "FollowersModule" && extName !== "FollowingModule") {
        return users;
      }
      const ids = this.normalizeDataKeys(users.map((user) => user.rest_id));
      if (!ids.length) {
        return users;
      }
      const edgeRows = await this.socialEdges().where("[extension+related_user_id]").anyOf(ids.map((id2) => [extName, id2])).toArray().catch(this.logError);
      if (!(edgeRows == null ? void 0 : edgeRows.length)) {
        return users;
      }
      return enrichUsersWithRelationshipFields(users, edgeRows);
    }
    normalizeDataKeys(values) {
      const normalized = /* @__PURE__ */ new Set();
      for (const value of values) {
        const key = String(value || "").trim();
        if (!key) continue;
        normalized.add(key);
      }
      return [...normalized];
    }
    filterEmptyData(data) {
      if (!data) {
        logger.warn("Empty data found in DB", data);
        return false;
      }
      if (data.__typename === "Tweet") {
        const tweet = data;
        if (!tweet.legacy && !tweet.article) {
          logger.warn("Empty data found in DB", data);
          return false;
        }
        return true;
      }
      if (!data.legacy) {
        logger.warn("Empty data found in DB", data);
        return false;
      }
      return true;
    }
    /*
    |--------------------------------------------------------------------------
    | Migrations
    |--------------------------------------------------------------------------
    */
    async init() {
      const tweetIndexPaths = [
        "rest_id",
        "twe_private_fields.created_at",
        "twe_private_fields.updated_at",
        "twe_private_fields.media_count",
        "core.user_results.result.core.screen_name",
        "legacy.favorite_count",
        "legacy.retweet_count",
        "legacy.bookmark_count",
        "legacy.quote_count",
        "legacy.reply_count",
        "views.count",
        "legacy.favorited",
        "legacy.retweeted",
        "legacy.bookmarked"
      ];
      const userIndexPaths = [
        "rest_id",
        "twe_private_fields.created_at",
        "twe_private_fields.updated_at",
        "core.screen_name",
        "legacy.followers_count",
        "legacy.friends_count",
        "legacy.statuses_count",
        "legacy.favourites_count",
        "legacy.listed_count",
        "verification.verified_type",
        "is_blue_verified",
        "relationship_perspectives.following",
        "relationship_perspectives.followed_by"
      ];
      const captureIndexPaths = [
        "id",
        "extension",
        "type",
        "created_at",
        "[extension+type]",
        "[extension+type+created_at]"
      ];
      const socialEdgeIndexPaths = [
        "id",
        "extension",
        "relation_type",
        "subject_user_id",
        "related_user_id",
        "observed_at",
        "[extension+relation_type]",
        "[extension+subject_user_id]",
        "[extension+related_user_id]"
      ];
      const importedBundleIndexPaths = [
        "id",
        "status",
        "visibility",
        "importedAt",
        "updatedAt",
        "recordCount"
      ];
      const importedBundleCollectionIndexPaths = [
        "id",
        "bundle_id",
        "kind",
        "[bundle_id+kind]"
      ];
      const importedBundleItemIndexPaths = [
        "id",
        "bundle_id",
        "collection_id",
        "record_id",
        "kind",
        "source_id",
        "sort_time",
        "[bundle_id+kind]",
        "[bundle_id+sort_time]"
      ];
      const importedEntitySnapshotIndexPaths = [
        "id",
        "bundle_id",
        "kind",
        "source_id",
        "source_extension",
        "observed_at",
        "updated_at",
        "[bundle_id+kind]",
        "[kind+source_id]"
      ];
      const importedBundleImportReportIndexPaths = [
        "id",
        "bundle_id",
        "started_at",
        "finished_at",
        "status"
      ];
      const searchDocumentIndexPaths = [
        "id",
        "source_key",
        "source_kind",
        "entity_type",
        "entity_id",
        "extension_name",
        "bundle_id",
        "bundle_item_id",
        "updated_at_ms",
        "created_at_ms",
        "observed_at_ms",
        "author_screen_name",
        "author_id",
        "folder_id",
        "[source_key+entity_type]",
        "[extension_name+entity_type]",
        "[bundle_id+entity_type]",
        "[entity_type+entity_id]"
      ];
      try {
        this.db.version(2).stores({
          tweets: tweetIndexPaths.join(","),
          users: userIndexPaths.join(","),
          captures: captureIndexPaths.join(",")
        }).upgrade(async (tx) => {
          logger.info("Upgrading database schema...");
          await migration_20250609(tx);
          logger.info("Database upgraded");
        });
        this.db.version(DB_VERSION).stores({
          tweets: tweetIndexPaths.join(","),
          users: userIndexPaths.join(","),
          captures: captureIndexPaths.join(","),
          social_edges: socialEdgeIndexPaths.join(","),
          imported_bundles: importedBundleIndexPaths.join(","),
          imported_bundle_collections: importedBundleCollectionIndexPaths.join(","),
          imported_bundle_items: importedBundleItemIndexPaths.join(","),
          imported_entity_snapshots: importedEntitySnapshotIndexPaths.join(","),
          imported_bundle_import_reports: importedBundleImportReportIndexPaths.join(","),
          search_documents: searchDocumentIndexPaths.join(",")
        });
        await this.db.open();
        logger.info(`Database connected: ${this.db.name}`);
      } catch (error) {
        this.logError(error);
      }
    }
    /*
    |--------------------------------------------------------------------------
    | Loggers
    |--------------------------------------------------------------------------
    */
    logError(error) {
      logger.error(`Database Error: ${error.message}`, error);
    }
  }
  let databaseManager = null;
  function getDatabaseManager() {
    if (databaseManager) {
      return databaseManager;
    }
    databaseManager = new DatabaseManager();
    return databaseManager;
  }
  const dbProxy = new Proxy({}, {
    get(_target, prop, receiver) {
      const manager = getDatabaseManager();
      const value = Reflect.get(manager, prop, receiver);
      return typeof value === "function" ? value.bind(manager) : value;
    },
    set(_target, prop, value, receiver) {
      const manager = getDatabaseManager();
      return Reflect.set(manager, prop, value, receiver);
    }
  });
  function readUserscriptManagerInfo() {
    try {
      const info = globalThis.GM_info;
      if (!info || typeof info !== "object") return {};
      const row = info;
      const script = row.script && typeof row.script === "object" ? row.script : {};
      return {
        scriptHandler: row.scriptHandler,
        version: row.version,
        platform: row.platform,
        script
      };
    } catch {
      return {};
    }
  }
  function readBrowserInfo() {
    const nav = typeof navigator !== "undefined" ? navigator : null;
    return {
      userAgent: (nav == null ? void 0 : nav.userAgent) ?? "",
      vendor: (nav == null ? void 0 : nav.vendor) ?? "",
      platform: (nav == null ? void 0 : nav.platform) ?? "",
      language: (nav == null ? void 0 : nav.language) ?? "",
      languages: (nav == null ? void 0 : nav.languages) ? [...nav.languages] : [],
      cookieEnabled: (nav == null ? void 0 : nav.cookieEnabled) ?? null,
      webdriver: (nav == null ? void 0 : nav.webdriver) ?? null
    };
  }
  async function collectReleaseReadinessReport() {
    const databaseCounts = await dbProxy.count().catch(() => null);
    const importedBundles = (await dbProxy.bundleList().catch(() => []) ?? []).map((bundle) => ({
      id: bundle.id,
      title: bundle.title,
      status: bundle.status,
      recordCount: bundle.recordCount,
      schemaVersion: bundle.schemaVersion,
      importedAt: bundle.importedAt,
      updatedAt: bundle.updatedAt
    }));
    return {
      generated_at_ms: Date.now(),
      browser: readBrowserInfo(),
      userscript_manager: readUserscriptManagerInfo(),
      runtime_modes: readRuntimeModes(),
      hook_stats: readHookStats(),
      raw_capture_stats: readRawStats(),
      diagnostic_capture_enabled: isDiagnosticCaptureEnabled(),
      database_counts: databaseCounts,
      imported_bundle_count: importedBundles.length,
      imported_bundles: importedBundles,
      checklist: {
        canonical_bundle_export: "manual-qc-required",
        canonical_bundle_import: "manual-qc-required",
        legacy_json_import: "manual-qc-required",
        malicious_import_safety: "manual-qc-required",
        chrome_parity: "manual-qc-required",
        firefox_parity: "manual-qc-required"
      }
    };
  }
  async function collectDiagnosticsBundle() {
    const now = Date.now();
    const nowIso = new Date(now).toISOString();
    const globals = globalThis;
    const dbNames = await (async () => {
      try {
        if (!(indexedDB == null ? void 0 : indexedDB.databases)) return [];
        const rows = await indexedDB.databases();
        return (Array.isArray(rows) ? rows : []).map((entry) => entry == null ? void 0 : entry.name).filter((name2) => typeof name2 === "string");
      } catch {
        return [];
      }
    })();
    const rawEventsRecent = (() => {
      try {
        const value = globals.__twe_raw_events_v1;
        if (!Array.isArray(value)) return [];
        return value.slice(-10).map((event) => {
          if (!event || typeof event !== "object") return null;
          const row = event;
          return {
            event_id: typeof row.event_id === "string" ? row.event_id : null,
            kind: typeof row.kind === "string" ? row.kind : null,
            wall_time_ms: typeof row.wall_time_ms === "number" ? row.wall_time_ms : null,
            route_type: typeof row.route_type === "string" ? row.route_type : null
          };
        });
      } catch {
        return [];
      }
    })();
    const rawEventsFull = (() => {
      try {
        const value = globals.__twe_raw_events_v1;
        return Array.isArray(value) ? value.slice() : [];
      } catch {
        return [];
      }
    })();
    const diagnosticBuffers = readDiagnosticBuffers();
    const recentLogs = logLinesSignal.value.slice(-400);
    const storageKeys = [
      "twe_safe_mode_v1",
      "twe_hook_mode_v1",
      "twe_repair_mode_v1",
      "twe_raw_capture_enabled_v1",
      "twe_raw_capture_encryption_ready_v1",
      "twe_raw_capture_dm_session_armed_until_ms_v1",
      "twe_raw_capture_stream_enabled_v1",
      "twe_raw_capture_daemon_url_v1"
    ];
    const storage = {};
    for (const key of storageKeys) {
      try {
        storage[key] = localStorage.getItem(key);
      } catch {
        storage[key] = null;
      }
    }
    const appOptionsSnapshot = {
      directMessagesCaptureEnabled: appOptionsManager.get("directMessagesCaptureEnabled", false),
      rawCaptureEncryptedStorageReady: appOptionsManager.get("rawCaptureEncryptedStorageReady", false),
      rawCapturePolicyPublicEnabled: appOptionsManager.get("rawCapturePolicyPublicEnabled", true),
      rawCapturePolicySensitiveEnabled: appOptionsManager.get("rawCapturePolicySensitiveEnabled", true),
      rawCapturePolicyDmEnabled: appOptionsManager.get("rawCapturePolicyDmEnabled", true)
    };
    return {
      generated_at_ms: now,
      generated_at_iso: nowIso,
      location: typeof location !== "undefined" ? location.href : "",
      title: typeof document !== "undefined" ? document.title : "",
      runtime_modes: readRuntimeModes(),
      hook_stats: readHookStats(),
      browser: readBrowserInfo(),
      userscript_manager: readUserscriptManagerInfo(),
      release_readiness: await collectReleaseReadinessReport(),
      performance: readPerfDiagnostics(),
      raw_capture_stats: readRawStats(),
      raw_events_recent: rawEventsRecent,
      raw_events_count: rawEventsFull.length,
      diagnostic_capture_enabled: isDiagnosticCaptureEnabled(),
      diagnostic_buffer_counts: {
        parser: diagnosticBuffers.parser.length,
        db: diagnosticBuffers.db.length,
        interaction: diagnosticBuffers.interaction.length,
        logs: recentLogs.length
      },
      indexeddb_names: dbNames,
      local_storage: storage,
      app_options: appOptionsSnapshot,
      raw_events_full: rawEventsFull,
      diagnostic_buffers: diagnosticBuffers,
      recent_logs: recentLogs
    };
  }
  function downloadJson(payload, filename) {
    const blob2 = new Blob([JSON.stringify(payload, null, 2)], {
      type: "application/json"
    });
    const url = URL.createObjectURL(blob2);
    const a = document.createElement("a");
    a.href = url;
    a.download = filename;
    a.click();
    URL.revokeObjectURL(url);
  }
  async function exportDiagnosticsBundleZip() {
    const bundle = await collectDiagnosticsBundle();
    const now = Date.now();
    const {
      raw_events_full: rawEventsFull = [],
      diagnostic_buffers: diagnosticBuffers = { parser: [], db: [], interaction: [] },
      recent_logs: recentLogs = [],
      ...summary
    } = bundle;
    const files = [
      {
        filename: "summary.json",
        blob: new Blob([JSON.stringify(summary, null, 2)], { type: "application/json" })
      },
      {
        filename: "raw-events.json",
        blob: new Blob([JSON.stringify(rawEventsFull, null, 2)], { type: "application/json" })
      },
      {
        filename: "parser-events.json",
        blob: new Blob([JSON.stringify(diagnosticBuffers.parser ?? [], null, 2)], {
          type: "application/json"
        })
      },
      {
        filename: "db-events.json",
        blob: new Blob([JSON.stringify(diagnosticBuffers.db ?? [], null, 2)], {
          type: "application/json"
        })
      },
      {
        filename: "interaction-events.json",
        blob: new Blob([JSON.stringify(diagnosticBuffers.interaction ?? [], null, 2)], {
          type: "application/json"
        })
      },
      {
        filename: "recent-logs.json",
        blob: new Blob([JSON.stringify(recentLogs, null, 2)], {
          type: "application/json"
        })
      },
      {
        filename: "release-readiness.json",
        blob: new Blob([JSON.stringify(summary.release_readiness ?? {}, null, 2)], {
          type: "application/json"
        })
      }
    ];
    await zipBlobFiles(`twe-diagnostics-${now}.zip`, files);
  }
  function readRawStats() {
    try {
      const value = globalThis.__twe_raw_capture_stats_v1;
      if (!value || typeof value !== "object") return {};
      return value;
    } catch {
      return {};
    }
  }
  function readRuntimeModes() {
    try {
      const value = globalThis.__twe_runtime_modes_v1;
      if (!value || typeof value !== "object") return {};
      return value;
    } catch {
      return {};
    }
  }
  function readHookStats() {
    try {
      const value = globalThis.__twe_hook_stats_v1;
      if (!value || typeof value !== "object") return {};
      return value;
    } catch {
      return {};
    }
  }
  function sanitizeFilename(value) {
    return String(value || "").replace(/[^a-zA-Z0-9._-]/g, "_").replace(/_+/g, "_");
  }
  function extractSnapshotText(snapshot) {
    const data = snapshot.data && typeof snapshot.data === "object" ? snapshot.data : {};
    const metadata = data.metadata && typeof data.metadata === "object" ? data.metadata : {};
    const candidates = [
      data.full_text,
      data.text,
      data.description,
      metadata.legacy && typeof metadata.legacy === "object" ? metadata.legacy.full_text : void 0,
      data.name,
      data.screen_name
    ];
    for (const candidate of candidates) {
      if (typeof candidate === "string" && candidate.trim()) {
        return candidate.trim();
      }
    }
    return String(snapshot.search_text || "").slice(0, 240);
  }
  function safePreviewUrl(value) {
    if (typeof value !== "string") return null;
    try {
      const parsed = new URL(value);
      return parsed.protocol === "http:" || parsed.protocol === "https:" ? parsed.href : null;
    } catch {
      return null;
    }
  }
  function extractSnapshotMediaUrls(snapshot) {
    const urls = /* @__PURE__ */ new Set();
    for (const ref of snapshot.media_refs || []) {
      const preview = safePreviewUrl(ref.previewUrl);
      const url = safePreviewUrl(ref.url);
      if (preview) urls.add(preview);
      if (url) urls.add(url);
    }
    const data = snapshot.data && typeof snapshot.data === "object" ? snapshot.data : {};
    const media = Array.isArray(data.media) ? data.media : [];
    for (const item of media) {
      if (!item || typeof item !== "object") continue;
      const obj = item;
      for (const key of ["thumbnail", "original", "url", "media_url_https"]) {
        const value = obj[key];
        const url = safePreviewUrl(value);
        if (url) urls.add(url);
      }
    }
    return [...urls].slice(0, 8);
  }
  async function yieldToMainThread() {
    await new Promise((resolve) => {
      setTimeout(resolve, 0);
    });
  }
  function Settings() {
    const { t, i18n } = useTranslation();
    const currentTheme = signals.useSignal(appOptionsManager.get("theme"));
    const optionsVersion = signals.useSignal(0);
    const [showSettings, toggleSettings] = useToggle(false);
    const [showBundleExport, toggleBundleExport] = useToggle(false);
    const [showBundleLibrary, toggleBundleLibrary] = useToggle(false);
    const [showQcSession, toggleQcSession] = useToggle(false);
    const bundleIncludeDb = signals.useSignal(true);
    const bundleIncludeModules = signals.useSignal(true);
    const bundleBusy = signals.useSignal(false);
    const bundleProgress = signals.useSignal({ current: 0, total: 0 });
    const bundleStatus = signals.useSignal("");
    const bundleResult = signals.useSignal(null);
    const bundleLastFilename = signals.useSignal("");
    const bundleLastDownloadUrl = signals.useSignal("");
    const importedBundles = signals.useSignal([]);
    const importedBundleSnapshots = signals.useSignal([]);
    const selectedBundleId = signals.useSignal("");
    const bundleSearchQuery = signals.useSignal("");
    const bundleKindFilter = signals.useSignal("all");
    const bundleLibraryStatus = signals.useSignal("");
    const bundleLibraryBusy = signals.useSignal(false);
    const qcStatus = signals.useSignal("");
    const qcBusy = signals.useSignal(false);
    const qcDiagnosticCaptureEnabled = signals.useSignal(isDiagnosticCaptureEnabled());
    const styles = {
      subtitle: "mb-2 text-base-content ml-4 opacity-50 font-semibold text-xs",
      block: "text-sm mb-2 w-full flex px-4 py-2 text-base-content bg-base-200 rounded-box justify-between",
      item: "label cursor-pointer flex justify-between h-8 items-center p-0",
      wrapButton: "btn btn-xs h-auto min-h-6 max-w-full whitespace-normal break-words text-center leading-tight"
    };
    const onBundleExport = async () => {
      if (bundleBusy.value) {
        return;
      }
      bundleBusy.value = true;
      bundleResult.value = null;
      bundleStatus.value = "Preparing export...";
      bundleProgress.value = { current: 0, total: 0 };
      const now = Date.now();
      const files = [];
      const moduleManifest = [];
      let skippedModules = 0;
      try {
        if (bundleIncludeDb.value) {
          bundleStatus.value = "Exporting DB snapshot...";
          const blob2 = await dbProxy.export();
          if (blob2) {
            files.push({
              filename: "database/twitter-web-exporter-db.json",
              blob: blob2
            });
          }
        }
        if (bundleIncludeModules.value) {
          bundleStatus.value = "Collecting module exports...";
          const extensions = extensionManagerProxy.getExtensions();
          const totalExtensions = extensions.length;
          for (const [index, extension] of extensions.entries()) {
            bundleStatus.value = `Collecting module exports (${index + 1}/${totalExtensions}): ${extension.name}`;
            const captures = await dbProxy.extGetCaptures(extension.name) ?? [];
            if (!captures.length) {
              skippedModules += 1;
              await yieldToMainThread();
              continue;
            }
            let records = [];
            if (extension.type === ExtensionType.TWEET) {
              records = await dbProxy.extGetCapturedTweets(extension.name, captures) ?? [];
            } else if (extension.type === ExtensionType.USER) {
              records = await dbProxy.extGetCapturedUsers(extension.name, captures) ?? [];
            }
            const safeName = sanitizeFilename(extension.name);
            const filename = `modules/${safeName}.json`;
            const payload = {
              extension: extension.name,
              type: extension.type,
              exported_at_ms: now,
              capture_count: captures.length,
              record_count: records.length,
              captures,
              records
            };
            files.push({
              filename,
              blob: new Blob([JSON.stringify(payload)], {
                type: "application/json"
              })
            });
            moduleManifest.push({
              extension: extension.name,
              type: extension.type,
              capture_count: captures.length,
              record_count: records.length,
              filename
            });
            await yieldToMainThread();
          }
        }
        const manifest = {
          generated_at_ms: now,
          generated_at_iso: new Date(now).toISOString(),
          include_db_snapshot: bundleIncludeDb.value,
          include_module_exports: bundleIncludeModules.value,
          files_total: files.length + 1,
          modules_exported: moduleManifest.length,
          modules_skipped_empty: skippedModules,
          modules: moduleManifest
        };
        files.unshift({
          filename: "manifest.json",
          blob: new Blob([JSON.stringify(manifest, void 0, 2)], {
            type: "application/json"
          })
        });
        if (!files.length) {
          throw new Error("No files prepared for bundle export.");
        }
        const zipFilename = `twitter-web-exporter-bundle-${now}.zip`;
        bundleStatus.value = "Creating ZIP...";
        bundleProgress.value = { current: 0, total: files.length };
        const zipBlob = await zipBlobFiles(zipFilename, files, (current, total) => {
          bundleProgress.value = { current, total };
        });
        if (bundleLastDownloadUrl.value) {
          URL.revokeObjectURL(bundleLastDownloadUrl.value);
          bundleLastDownloadUrl.value = "";
        }
        bundleLastFilename.value = zipFilename;
        bundleLastDownloadUrl.value = URL.createObjectURL(zipBlob);
        bundleStatus.value = `Bundle export completed: ${zipFilename}`;
        bundleResult.value = {
          modules: moduleManifest.length,
          files: files.length,
          skipped: skippedModules
        };
      } catch (err2) {
        bundleStatus.value = `Bundle export failed: ${(err2 == null ? void 0 : err2.message) ?? "Unknown error"}`;
      } finally {
        bundleBusy.value = false;
      }
    };
    const refreshImportedBundles = async () => {
      importedBundles.value = await dbProxy.bundleList() ?? [];
    };
    const onBundleImportFile = async (file) => {
      if (!file || bundleLibraryBusy.value) {
        return;
      }
      bundleLibraryBusy.value = true;
      bundleLibraryStatus.value = `Importing ${file.name}...`;
      try {
        const isZip = file.name.toLowerCase().endsWith(".zip") || file.type === "application/zip" || file.type === "application/x-zip-compressed";
        const result = isZip ? await importBundleZip(dbProxy, file) : await importLegacyBundleFile(dbProxy, file);
        bundleLibraryStatus.value = `Imported ${result.recordsImported}/${result.recordsSeen} records from ${file.name}`;
        await refreshImportedBundles();
      } catch (err2) {
        bundleLibraryStatus.value = `Import failed: ${err2.message}`;
      } finally {
        bundleLibraryBusy.value = false;
      }
    };
    const loadSelectedBundleSnapshots = async () => {
      if (!selectedBundleId.value) {
        importedBundleSnapshots.value = [];
        return;
      }
      const rows = await dbProxy.bundleSearchSnapshots(
        selectedBundleId.value,
        bundleSearchQuery.value,
        1e3
      ) ?? [];
      importedBundleSnapshots.value = bundleKindFilter.value === "all" ? rows : rows.filter((snapshot) => snapshot.kind === bundleKindFilter.value);
    };
    const exportLoadedImportedSnapshots = async () => {
      const bundle = importedBundles.value.find((item) => item.id === selectedBundleId.value);
      if (!bundle || !importedBundleSnapshots.value.length) return;
      await exportCanonicalBundleZip(
        importedBundleSnapshots.value.map((snapshot, index) => ({
          id: snapshot.source_id || snapshot.id || String(index),
          original: snapshot.data,
          record: snapshot.data && typeof snapshot.data === "object" ? snapshot.data : { value: snapshot.data }
        })),
        {
          title: `${bundle.title}-subset`,
          description: `Re-exported subset from imported bundle ${bundle.id}`,
          scope: "bundle",
          queryText: bundleSearchQuery.value
        }
      );
    };
    const exportQcDiagnostics = async () => {
      if (qcBusy.value) return;
      qcBusy.value = true;
      qcStatus.value = "Preparing QC diagnostics bundle...";
      try {
        await exportDiagnosticsBundleZip();
        qcStatus.value = "QC diagnostics bundle exported.";
      } catch (err2) {
        qcStatus.value = `QC diagnostics export failed: ${err2.message}`;
      } finally {
        qcBusy.value = false;
      }
    };
    hooks.useEffect(() => {
      const disposeOptions = appOptionsManager.signal.subscribe(() => {
        optionsVersion.value++;
        currentTheme.value = appOptionsManager.get("theme");
      });
      return () => {
        if (typeof disposeOptions === "function") {
          disposeOptions();
        }
        if (bundleLastDownloadUrl.value) {
          URL.revokeObjectURL(bundleLastDownloadUrl.value);
        }
      };
    }, []);
    hooks.useEffect(() => {
      if (showBundleLibrary) {
        void refreshImportedBundles();
      }
    }, [showBundleLibrary]);
    hooks.useEffect(() => {
      if (typeof _GM_registerMenuCommand === "function") {
        _GM_registerMenuCommand(`${t("Version")} ${packageJson.version}`, () => {
          window.open(packageJson.homepage, "_blank");
        });
      }
    }, []);
    return /* @__PURE__ */ u(preact.Fragment, { children: [
      /* @__PURE__ */ u(
        "div",
        {
          onClick: toggleSettings,
          class: "w-9 h-9 mr-2 cursor-pointer flex justify-center items-center transition-colors duration-200 rounded-full hover:bg-base-200",
          children: /* @__PURE__ */ u(IconSettings, {})
        }
      ),
      /* @__PURE__ */ u(Modal, { title: t("Settings"), show: showSettings, onClose: toggleSettings, class: "max-w-lg", children: [
        /* @__PURE__ */ u("p", { class: styles.subtitle, children: t("General") }),
        /* @__PURE__ */ u("div", { class: cx(styles.block, "flex-col"), children: [
          /* @__PURE__ */ u("label", { class: styles.item, children: [
            /* @__PURE__ */ u("span", { class: "label-text whitespace-nowrap", children: t("Theme") }),
            /* @__PURE__ */ u(
              "select",
              {
                class: "select select-xs",
                onChange: (e) => {
                  var _a2;
                  currentTheme.value = ((_a2 = e.target) == null ? void 0 : _a2.value) ?? DEFAULT_APP_OPTIONS.theme;
                  appOptionsManager.set("theme", currentTheme.value);
                },
                children: THEMES.map((theme) => /* @__PURE__ */ u("option", { value: theme, selected: currentTheme.value === theme, children: capitalizeFirstLetter(theme) }, theme))
              }
            )
          ] }),
          /* @__PURE__ */ u("label", { class: styles.item, children: [
            /* @__PURE__ */ u("span", { class: "label-text whitespace-nowrap", children: t("Language") }),
            /* @__PURE__ */ u(
              "select",
              {
                class: "select select-xs",
                onChange: (e) => {
                  var _a2;
                  const language = ((_a2 = e.target) == null ? void 0 : _a2.value) ?? detectBrowserLanguage();
                  i18n.changeLanguage(language);
                  appOptionsManager.set("language", language);
                },
                children: Object.entries(LANGUAGES_CONFIG).map(([langTag, langConf]) => /* @__PURE__ */ u(
                  "option",
                  {
                    value: langTag,
                    selected: appOptionsManager.get("language") === langTag,
                    children: [
                      langConf.nameEn,
                      " - ",
                      langConf.name
                    ]
                  },
                  langTag
                ))
              }
            )
          ] }),
          /* @__PURE__ */ u("label", { class: styles.item, children: [
            /* @__PURE__ */ u("span", { class: "label-text whitespace-nowrap", children: t("Debug") }),
            /* @__PURE__ */ u(
              "input",
              {
                type: "checkbox",
                class: "toggle toggle-primary",
                checked: appOptionsManager.get("debug"),
                onChange: (e) => {
                  var _a2;
                  appOptionsManager.set("debug", (_a2 = e.target) == null ? void 0 : _a2.checked);
                }
              }
            )
          ] }),
          /* @__PURE__ */ u("label", { class: styles.item, children: [
            /* @__PURE__ */ u("div", { class: "flex items-center", children: [
              /* @__PURE__ */ u("span", { class: "label-text whitespace-nowrap", children: "Safe mode" }),
              /* @__PURE__ */ u(
                "a",
                {
                  class: "tooltip tooltip-bottom ml-0.5 before:max-w-40",
                  "data-tip": "Disables hook-based capture when enabled. Turn this off for normal browsing and diagnostic capture.",
                  children: /* @__PURE__ */ u(IconHelp, { size: 20 })
                }
              )
            ] }),
            /* @__PURE__ */ u(
              "input",
              {
                type: "checkbox",
                class: "toggle toggle-warning",
                checked: appOptionsManager.get("safeMode"),
                onChange: (e) => {
                  var _a2;
                  appOptionsManager.set("safeMode", (_a2 = e.target) == null ? void 0 : _a2.checked);
                }
              },
              `safe-mode-${optionsVersion.value}`
            )
          ] }),
          /* @__PURE__ */ u("label", { class: styles.item, children: [
            /* @__PURE__ */ u("div", { class: "flex items-center", children: [
              /* @__PURE__ */ u("span", { class: "label-text whitespace-nowrap", children: "Hook mode" }),
              /* @__PURE__ */ u(
                "a",
                {
                  class: "tooltip tooltip-bottom ml-0.5 before:max-w-40",
                  "data-tip": "Controls whether the userscript hooks XHR, fetch, both, or neither.",
                  children: /* @__PURE__ */ u(IconHelp, { size: 20 })
                }
              )
            ] }),
            /* @__PURE__ */ u(
              "select",
              {
                class: "select select-xs",
                value: appOptionsManager.get("hookMode"),
                onChange: (e) => {
                  var _a2;
                  appOptionsManager.set(
                    "hookMode",
                    (_a2 = e.target) == null ? void 0 : _a2.value
                  );
                },
                children: [
                  /* @__PURE__ */ u("option", { value: "both", children: "both" }),
                  /* @__PURE__ */ u("option", { value: "xhr", children: "xhr" }),
                  /* @__PURE__ */ u("option", { value: "fetch", children: "fetch" }),
                  /* @__PURE__ */ u("option", { value: "off", children: "off" })
                ]
              }
            )
          ] }),
          /* @__PURE__ */ u("label", { class: styles.item, children: [
            /* @__PURE__ */ u("div", { class: "flex items-center", children: [
              /* @__PURE__ */ u("span", { class: "label-text whitespace-nowrap", children: "Repair mode" }),
              /* @__PURE__ */ u(
                "a",
                {
                  class: "tooltip tooltip-bottom ml-0.5 before:max-w-40",
                  "data-tip": "Controls whether hook repair watchdog behavior is active.",
                  children: /* @__PURE__ */ u(IconHelp, { size: 20 })
                }
              )
            ] }),
            /* @__PURE__ */ u(
              "select",
              {
                class: "select select-xs",
                value: appOptionsManager.get("repairMode"),
                onChange: (e) => {
                  var _a2;
                  appOptionsManager.set(
                    "repairMode",
                    (_a2 = e.target) == null ? void 0 : _a2.value
                  );
                },
                children: [
                  /* @__PURE__ */ u("option", { value: "watchdog", children: "watchdog" }),
                  /* @__PURE__ */ u("option", { value: "off", children: "off" })
                ]
              }
            )
          ] }),
          /* @__PURE__ */ u("label", { class: styles.item, children: [
            /* @__PURE__ */ u("div", { class: "flex items-center", children: [
              /* @__PURE__ */ u("span", { class: "label-text whitespace-nowrap", children: t("Date Time Format") }),
              /* @__PURE__ */ u(
                "a",
                {
                  href: "https://day.js.org/docs/en/display/format",
                  target: "_blank",
                  rel: "noopener noreferrer",
                  class: "tooltip tooltip-bottom ml-0.5 before:max-w-40",
                  "data-tip": t(
                    "Click for more information. This will take effect on both previewer and exported files."
                  ),
                  children: /* @__PURE__ */ u(IconHelp, { size: 20 })
                }
              )
            ] }),
            /* @__PURE__ */ u(
              "input",
              {
                type: "text",
                class: "input input-bordered input-xs w-48",
                value: appOptionsManager.get("dateTimeFormat"),
                onChange: (e) => {
                  var _a2;
                  appOptionsManager.set("dateTimeFormat", (_a2 = e.target) == null ? void 0 : _a2.value);
                }
              }
            )
          ] }),
          /* @__PURE__ */ u("label", { class: styles.item, children: [
            /* @__PURE__ */ u("div", { class: "flex items-center", children: [
              /* @__PURE__ */ u("span", { class: "label-text whitespace-nowrap", children: t("Use dedicated DB for accounts") }),
              /* @__PURE__ */ u(
                "a",
                {
                  class: "tooltip tooltip-bottom ml-0.5 before:max-w-40",
                  "data-tip": t(
                    "This will create separate database for each Twitter account, which can help reduce the chance of data mixing when you use multiple accounts."
                  ),
                  children: /* @__PURE__ */ u(IconHelp, { size: 20 })
                }
              )
            ] }),
            /* @__PURE__ */ u(
              "input",
              {
                type: "checkbox",
                class: "toggle toggle-primary",
                checked: appOptionsManager.get("dedicatedDbForAccounts"),
                onChange: (e) => {
                  var _a2;
                  appOptionsManager.set("dedicatedDbForAccounts", (_a2 = e.target) == null ? void 0 : _a2.checked);
                }
              }
            )
          ] }),
          /* @__PURE__ */ u("div", { class: "flex w-full flex-col gap-2 py-1", children: [
            /* @__PURE__ */ u("div", { class: "flex items-center justify-between gap-2", children: /* @__PURE__ */ u("span", { class: "label-text whitespace-nowrap", children: t("Local Database") }) }),
            /* @__PURE__ */ u("div", { class: "grid min-w-0 grid-cols-2 gap-2 sm:grid-cols-3", children: [
              /* @__PURE__ */ u(
                "button",
                {
                  class: cx(styles.wrapButton, "btn-neutral"),
                  onClick: async () => {
                    let storageUsageText = "Storage usage: N/A";
                    if (typeof navigator.storage.estimate === "function") {
                      const { quota = 1, usage = 0 } = await navigator.storage.estimate();
                      const usageMB = (usage / 1024 / 1024).toFixed(2);
                      const quotaMB = (quota / 1024 / 1024).toFixed(2);
                      storageUsageText = `Storage usage: ${usageMB}MB / ${quotaMB}MB`;
                    }
                    const count = await dbProxy.count();
                    alert(
                      storageUsageText + "\n\nIndexedDB tables count:\n" + JSON.stringify(count, void 0, "  ")
                    );
                  },
                  children: [
                    /* @__PURE__ */ u(IconReportAnalytics, { size: 20 }),
                    t("Analyze DB")
                  ]
                }
              ),
              /* @__PURE__ */ u(
                "button",
                {
                  class: cx(styles.wrapButton, "btn-primary"),
                  onClick: async () => {
                    const blob2 = await dbProxy.export();
                    if (blob2) {
                      saveFile(`twitter-web-exporter-${Date.now()}.json`, blob2);
                    }
                  },
                  children: [
                    /* @__PURE__ */ u(IconDatabaseExport, { size: 20 }),
                    t("Export DB")
                  ]
                }
              ),
              /* @__PURE__ */ u("button", { class: cx(styles.wrapButton, "btn-info"), onClick: toggleBundleExport, children: [
                /* @__PURE__ */ u(IconDatabaseExport, { size: 20 }),
                t("Bundle Export")
              ] }),
              /* @__PURE__ */ u("button", { class: cx(styles.wrapButton, "btn-secondary"), onClick: toggleBundleLibrary, children: [
                /* @__PURE__ */ u(IconDatabaseExport, { size: 20 }),
                t("Bundle Library")
              ] }),
              /* @__PURE__ */ u("button", { class: cx(styles.wrapButton, "btn-accent"), onClick: toggleQcSession, children: [
                /* @__PURE__ */ u(IconReportAnalytics, { size: 20 }),
                t("QC Session")
              ] }),
              /* @__PURE__ */ u(
                "button",
                {
                  class: cx(styles.wrapButton, "btn-warning"),
                  onClick: async () => {
                    if (confirm(t("Are you sure to clear all data in the database?"))) {
                      await dbProxy.clear();
                    }
                  },
                  children: [
                    /* @__PURE__ */ u(IconTrashX, { size: 20 }),
                    t("Clear DB")
                  ]
                }
              )
            ] })
          ] })
        ] }),
        /* @__PURE__ */ u("p", { class: styles.subtitle, children: t("Modules (Scroll to see more)") }),
        /* @__PURE__ */ u("div", { class: cx(styles.block, "flex-col", "max-h-44 overflow-scroll"), children: extensionManagerProxy.getExtensions().map((extension) => /* @__PURE__ */ u("label", { class: cx(styles.item, "flex-shrink-0"), children: [
          /* @__PURE__ */ u("span", { children: [
            t(extension.name.replace("Module", "")),
            " ",
            t("Module")
          ] }),
          /* @__PURE__ */ u(
            "input",
            {
              type: "checkbox",
              class: "toggle toggle-secondary",
              checked: extension.enabled,
              onChange: () => {
                if (extension.enabled) {
                  extensionManagerProxy.disable(extension.name);
                } else {
                  extensionManagerProxy.enable(extension.name);
                }
              }
            }
          )
        ] }, extension.name)) }),
        /* @__PURE__ */ u("p", { class: styles.subtitle, children: t("About") }),
        /* @__PURE__ */ u("div", { class: styles.block, children: [
          /* @__PURE__ */ u("span", { class: "label-text whitespace-nowrap", children: [
            t("Version"),
            " ",
            packageJson.version
          ] }),
          /* @__PURE__ */ u("a", { class: "btn btn-xs btn-ghost", target: "_blank", href: packageJson.homepage, children: [
            /* @__PURE__ */ u(IconBrandGithubFilled, { class: "[&>path]:stroke-0" }),
            "GitHub"
          ] })
        ] })
      ] }),
      /* @__PURE__ */ u(
        Modal,
        {
          title: "Bundle Export",
          show: showBundleExport,
          onClose: toggleBundleExport,
          class: "max-w-lg",
          children: [
            /* @__PURE__ */ u("div", { class: "px-4 text-base", children: [
              /* @__PURE__ */ u("p", { class: "text-base-content text-opacity-60 mb-2 leading-5 text-sm", children: "Create one ZIP with a DB snapshot and per-module JSON exports." }),
              /* @__PURE__ */ u("label", { class: styles.item, children: [
                /* @__PURE__ */ u("span", { class: "label-text whitespace-nowrap", children: "Include DB snapshot" }),
                /* @__PURE__ */ u(
                  "input",
                  {
                    type: "checkbox",
                    class: "toggle toggle-primary",
                    checked: bundleIncludeDb.value,
                    onChange: (e) => {
                      var _a2;
                      bundleIncludeDb.value = (_a2 = e.target) == null ? void 0 : _a2.checked;
                    }
                  }
                )
              ] }),
              /* @__PURE__ */ u("label", { class: styles.item, children: [
                /* @__PURE__ */ u("span", { class: "label-text whitespace-nowrap", children: "Include module exports" }),
                /* @__PURE__ */ u(
                  "input",
                  {
                    type: "checkbox",
                    class: "toggle toggle-primary",
                    checked: bundleIncludeModules.value,
                    onChange: (e) => {
                      var _a2;
                      bundleIncludeModules.value = (_a2 = e.target) == null ? void 0 : _a2.checked;
                    }
                  }
                )
              ] }),
              /* @__PURE__ */ u("div", { class: "text-xs text-base-content text-opacity-70 mt-2", children: [
                "Status: ",
                bundleStatus.value || "Idle"
              ] }),
              /* @__PURE__ */ u("div", { class: "text-xs text-base-content text-opacity-70", children: [
                "Progress: ",
                bundleProgress.value.current,
                "/",
                bundleProgress.value.total
              ] }),
              bundleResult.value && /* @__PURE__ */ u("div", { class: "text-xs text-base-content text-opacity-70 mt-1", children: [
                "Exported modules: ",
                bundleResult.value.modules,
                " | files: ",
                bundleResult.value.files,
                " | skipped empty modules: ",
                bundleResult.value.skipped
              ] }),
              bundleLastFilename.value && /* @__PURE__ */ u("div", { class: "text-xs text-base-content text-opacity-70 mt-1 break-all", children: [
                "Last ZIP: ",
                /* @__PURE__ */ u("code", { children: bundleLastFilename.value })
              ] }),
              bundleLastDownloadUrl.value && /* @__PURE__ */ u("div", { class: "text-xs mt-1", children: /* @__PURE__ */ u(
                "a",
                {
                  class: "link link-primary",
                  href: bundleLastDownloadUrl.value,
                  download: bundleLastFilename.value,
                  children: "Download again"
                }
              ) })
            ] }),
            /* @__PURE__ */ u("div", { class: "flex space-x-2", children: [
              /* @__PURE__ */ u("span", { class: "flex-grow" }),
              /* @__PURE__ */ u("button", { class: "btn", onClick: toggleBundleExport, children: "Cancel" }),
              /* @__PURE__ */ u(
                "button",
                {
                  class: cx("btn btn-primary", bundleBusy.value && "btn-disabled"),
                  onClick: onBundleExport,
                  children: [
                    bundleBusy.value && /* @__PURE__ */ u("span", { class: "loading loading-spinner" }),
                    "Export Bundle ZIP"
                  ]
                }
              )
            ] })
          ]
        }
      ),
      /* @__PURE__ */ u(
        Modal,
        {
          title: "Bundle Library",
          show: showBundleLibrary,
          onClose: toggleBundleLibrary,
          class: "max-w-2xl",
          children: [
            /* @__PURE__ */ u("div", { class: "px-4 text-sm", children: [
              /* @__PURE__ */ u("p", { class: "text-base-content text-opacity-60 mb-3 leading-5", children: "Import canonical TWE bundle ZIPs into isolated local bundle tables. Imported bundles do not mutate live captures or your X account." }),
              /* @__PURE__ */ u("div", { class: "rounded-box-half border border-base-300 bg-base-200/70 p-3", children: [
                /* @__PURE__ */ u("label", { class: "flex items-center justify-between gap-3", children: [
                  /* @__PURE__ */ u("span", { class: "font-semibold", children: "Import bundle ZIP or legacy JSON/JSONL" }),
                  /* @__PURE__ */ u(
                    "input",
                    {
                      type: "file",
                      accept: ".zip,.json,.jsonl,application/zip,application/json,application/x-ndjson",
                      class: "file-input file-input-bordered file-input-sm max-w-xs",
                      disabled: bundleLibraryBusy.value,
                      onChange: (event) => {
                        var _a2;
                        const input = event.target;
                        void onBundleImportFile((_a2 = input.files) == null ? void 0 : _a2[0]);
                        input.value = "";
                      }
                    }
                  )
                ] }),
                /* @__PURE__ */ u("div", { class: "mt-2 font-mono text-xs opacity-70", children: [
                  bundleLibraryBusy.value ? "busy: " : "",
                  bundleLibraryStatus.value || "Idle"
                ] })
              ] }),
              /* @__PURE__ */ u("div", { class: "mt-3 flex items-center justify-between", children: [
                /* @__PURE__ */ u("h3", { class: "font-semibold", children: "Imported bundles" }),
                /* @__PURE__ */ u("button", { class: "btn btn-xs btn-outline", onClick: () => void refreshImportedBundles(), children: "Refresh" })
              ] }),
              /* @__PURE__ */ u("div", { class: "mt-2 max-h-80 overflow-y-auto rounded-box-half border border-base-300", children: importedBundles.value.length ? /* @__PURE__ */ u("table", { class: "table table-sm", children: [
                /* @__PURE__ */ u("thead", { children: /* @__PURE__ */ u("tr", { children: [
                  /* @__PURE__ */ u("th", { children: "Title" }),
                  /* @__PURE__ */ u("th", { children: "Status" }),
                  /* @__PURE__ */ u("th", { children: "Records" }),
                  /* @__PURE__ */ u("th", { children: "Imported" }),
                  /* @__PURE__ */ u("th", {})
                ] }) }),
                /* @__PURE__ */ u("tbody", { children: importedBundles.value.map((bundle) => /* @__PURE__ */ u("tr", { children: [
                  /* @__PURE__ */ u("td", { children: [
                    /* @__PURE__ */ u("div", { class: "font-semibold", children: bundle.title }),
                    /* @__PURE__ */ u("div", { class: "font-mono text-[10px] opacity-60", children: bundle.id })
                  ] }),
                  /* @__PURE__ */ u("td", { children: /* @__PURE__ */ u("span", { class: "badge badge-sm badge-outline", children: bundle.status }) }),
                  /* @__PURE__ */ u("td", { class: "font-mono text-xs", children: bundle.recordCount }),
                  /* @__PURE__ */ u("td", { class: "font-mono text-xs", children: new Date(bundle.importedAt).toLocaleString() }),
                  /* @__PURE__ */ u("td", { children: [
                    /* @__PURE__ */ u(
                      "button",
                      {
                        class: "btn btn-xs btn-primary btn-outline mr-1",
                        onClick: async () => {
                          selectedBundleId.value = bundle.id;
                          bundleSearchQuery.value = "";
                          bundleKindFilter.value = "all";
                          await loadSelectedBundleSnapshots();
                        },
                        children: "View"
                      }
                    ),
                    /* @__PURE__ */ u(
                      "button",
                      {
                        class: "btn btn-xs btn-error btn-outline",
                        onClick: async () => {
                          if (!confirm(`Delete imported bundle "${bundle.title}"?`)) return;
                          await dbProxy.bundleDelete(bundle.id);
                          if (selectedBundleId.value === bundle.id) {
                            selectedBundleId.value = "";
                            importedBundleSnapshots.value = [];
                          }
                          await refreshImportedBundles();
                        },
                        children: "Delete"
                      }
                    )
                  ] })
                ] }, bundle.id)) })
              ] }) : /* @__PURE__ */ u("div", { class: "p-6 text-center text-base-content/50", children: "No imported bundles yet." }) }),
              selectedBundleId.value ? /* @__PURE__ */ u("div", { class: "mt-3 rounded-box-half border border-base-300 bg-base-200/50 p-3", children: [
                /* @__PURE__ */ u("div", { class: "mb-2 flex items-center justify-between gap-2", children: [
                  /* @__PURE__ */ u("div", { children: [
                    /* @__PURE__ */ u("h3", { class: "font-semibold", children: "Bundle Viewer" }),
                    /* @__PURE__ */ u("div", { class: "font-mono text-[10px] opacity-60", children: selectedBundleId.value })
                  ] }),
                  /* @__PURE__ */ u(
                    "button",
                    {
                      class: "btn btn-xs btn-outline",
                      onClick: () => {
                        selectedBundleId.value = "";
                        importedBundleSnapshots.value = [];
                      },
                      children: "Close Viewer"
                    }
                  )
                ] }),
                /* @__PURE__ */ u("label", { class: "input input-bordered input-sm mb-2 flex items-center gap-2", children: [
                  /* @__PURE__ */ u("span", { class: "text-xs opacity-60", children: "Search" }),
                  /* @__PURE__ */ u(
                    "input",
                    {
                      class: "grow bg-transparent",
                      value: bundleSearchQuery.value,
                      onInput: (event) => {
                        bundleSearchQuery.value = event.target.value;
                      },
                      onKeyDown: (event) => {
                        if (event.key === "Enter") {
                          void loadSelectedBundleSnapshots();
                        }
                      }
                    }
                  ),
                  /* @__PURE__ */ u("button", { class: "btn btn-xs", onClick: () => void loadSelectedBundleSnapshots(), children: "Run" })
                ] }),
                /* @__PURE__ */ u("div", { class: "mb-2 flex items-center justify-between gap-2", children: [
                  /* @__PURE__ */ u("div", { class: "flex items-center gap-2", children: [
                    /* @__PURE__ */ u(
                      "select",
                      {
                        class: "select select-bordered select-xs",
                        value: bundleKindFilter.value,
                        onChange: (event) => {
                          bundleKindFilter.value = event.target.value;
                          void loadSelectedBundleSnapshots();
                        },
                        children: [
                          /* @__PURE__ */ u("option", { value: "all", children: "all kinds" }),
                          /* @__PURE__ */ u("option", { value: "tweet", children: "tweets" }),
                          /* @__PURE__ */ u("option", { value: "user", children: "users" }),
                          /* @__PURE__ */ u("option", { value: "unknown", children: "unknown" })
                        ]
                      }
                    ),
                    /* @__PURE__ */ u("span", { class: "font-mono text-xs opacity-70", children: [
                      "Showing ",
                      importedBundleSnapshots.value.length,
                      " snapshots"
                    ] })
                  ] }),
                  /* @__PURE__ */ u(
                    "button",
                    {
                      class: "btn btn-xs btn-secondary",
                      disabled: !importedBundleSnapshots.value.length,
                      onClick: () => void exportLoadedImportedSnapshots(),
                      children: "Export Loaded Subset"
                    }
                  )
                ] }),
                /* @__PURE__ */ u("div", { class: "max-h-72 overflow-y-auto rounded-box-half border border-base-300 bg-base-100", children: importedBundleSnapshots.value.length ? importedBundleSnapshots.value.map((snapshot) => /* @__PURE__ */ u("details", { class: "border-b border-base-300 p-2 text-xs", children: [
                  /* @__PURE__ */ u("summary", { class: "cursor-pointer", children: [
                    /* @__PURE__ */ u("span", { class: "badge badge-xs badge-outline mr-2", children: snapshot.kind }),
                    /* @__PURE__ */ u("span", { class: "font-mono mr-2", children: snapshot.source_id || snapshot.id }),
                    /* @__PURE__ */ u("span", { class: "opacity-60", children: snapshot.observed_at ? new Date(snapshot.observed_at).toLocaleString() : "" })
                  ] }),
                  /* @__PURE__ */ u("div", { class: "mt-2 rounded bg-base-100 p-2", children: [
                    /* @__PURE__ */ u("p", { class: "whitespace-pre-wrap text-sm leading-5", children: extractSnapshotText(snapshot) || "(no text preview)" }),
                    extractSnapshotMediaUrls(snapshot).length ? /* @__PURE__ */ u("div", { class: "mt-2 grid grid-cols-4 gap-2", children: extractSnapshotMediaUrls(snapshot).map((url) => /* @__PURE__ */ u("a", { href: url, target: "_blank", rel: "noopener noreferrer", children: url.includes(".mp4") ? /* @__PURE__ */ u("video", { src: url, class: "h-20 w-full rounded object-cover" }) : /* @__PURE__ */ u(
                      "img",
                      {
                        src: url,
                        class: "h-20 w-full rounded object-cover",
                        loading: "lazy"
                      }
                    ) }, url)) }) : null
                  ] }),
                  /* @__PURE__ */ u("pre", { class: "mt-2 max-h-52 overflow-auto whitespace-pre-wrap rounded bg-base-200 p-2 text-[10px] leading-4", children: JSON.stringify(snapshot.data, null, 2) })
                ] }, snapshot.id)) : /* @__PURE__ */ u("div", { class: "p-6 text-center text-base-content/50", children: "No snapshots loaded." }) })
              ] }) : null
            ] }),
            /* @__PURE__ */ u("div", { class: "flex space-x-2", children: [
              /* @__PURE__ */ u("span", { class: "flex-grow" }),
              /* @__PURE__ */ u("button", { class: "btn", onClick: toggleBundleLibrary, children: "Close" })
            ] })
          ]
        }
      ),
      /* @__PURE__ */ u(
        Modal,
        {
          title: "Unified QC Session",
          show: showQcSession,
          onClose: toggleQcSession,
          class: "max-w-3xl",
          children: [
            /* @__PURE__ */ u("div", { class: "px-4 text-sm", children: [
              /* @__PURE__ */ u("p", { class: "mb-3 text-base-content/70", children: "Use this single session to QC Bundle Library, core release workflows, and Firefox/Chrome parity. Run Firefox first, export diagnostics, then repeat the browser parity subset in Chrome and export diagnostics again." }),
              /* @__PURE__ */ u("div", { class: "grid gap-3 md:grid-cols-2", children: [
                /* @__PURE__ */ u("section", { class: "rounded-box-half border border-base-300 bg-base-200/60 p-3", children: [
                  /* @__PURE__ */ u("h3", { class: "mb-2 font-semibold", children: "Preparation" }),
                  /* @__PURE__ */ u("ol", { class: "list-decimal space-y-1 pl-5 text-xs leading-5", children: [
                    /* @__PURE__ */ u("li", { children: "Install the current local userscript in Firefox." }),
                    /* @__PURE__ */ u("li", { children: "Open Settings and set Safe mode off, Hook mode both, Repair mode watchdog." }),
                    /* @__PURE__ */ u("li", { children: "Enable Diagnostic capture below." }),
                    /* @__PURE__ */ u("li", { children: "Browse X normally long enough to confirm counters increment." }),
                    /* @__PURE__ */ u("li", { children: "Repeat the parity subset in Chrome after Firefox is complete." })
                  ] })
                ] }),
                /* @__PURE__ */ u("section", { class: "rounded-box-half border border-base-300 bg-base-200/60 p-3", children: [
                  /* @__PURE__ */ u("h3", { class: "mb-2 font-semibold", children: "Bundle Library" }),
                  /* @__PURE__ */ u("ol", { class: "list-decimal space-y-1 pl-5 text-xs leading-5", children: [
                    /* @__PURE__ */ u("li", { children: [
                      "Export a filtered result set with Export Data ",
                      "->",
                      " Export Bundle ZIP."
                    ] }),
                    /* @__PURE__ */ u("li", { children: [
                      "Import that ZIP through Settings ",
                      "->",
                      " Bundle Library."
                    ] }),
                    /* @__PURE__ */ u("li", { children: "Search exact text from one imported row." }),
                    /* @__PURE__ */ u("li", { children: "Filter by tweets/users, inspect text/media/raw JSON." }),
                    /* @__PURE__ */ u("li", { children: "Export Loaded Subset, then import that subset ZIP." }),
                    /* @__PURE__ */ u("li", { children: "Import the legacy and malicious fixtures from e2e/fixtures/bundles." })
                  ] })
                ] }),
                /* @__PURE__ */ u("section", { class: "rounded-box-half border border-base-300 bg-base-200/60 p-3", children: [
                  /* @__PURE__ */ u("h3", { class: "mb-2 font-semibold", children: "Core Release Smoke" }),
                  /* @__PURE__ */ u("ol", { class: "list-decimal space-y-1 pl-5 text-xs leading-5", children: [
                    /* @__PURE__ */ u("li", { children: "Bookmarks folder scroll indexes rows and folder metadata." }),
                    /* @__PURE__ */ u("li", { children: "Bookmark-from-feed increments and later resolves folder details." }),
                    /* @__PURE__ */ u("li", { children: "Search exact snippets and phrases rank correctly enough for QC." }),
                    /* @__PURE__ */ u("li", { children: "Table fullscreen and masonry scrolling remain stable." }),
                    /* @__PURE__ */ u("li", { children: "Export Data and Export Media complete without browser freeze." })
                  ] })
                ] }),
                /* @__PURE__ */ u("section", { class: "rounded-box-half border border-base-300 bg-base-200/60 p-3", children: [
                  /* @__PURE__ */ u("h3", { class: "mb-2 font-semibold", children: "Chrome Parity" }),
                  /* @__PURE__ */ u("ol", { class: "list-decimal space-y-1 pl-5 text-xs leading-5", children: [
                    /* @__PURE__ */ u("li", { children: "Install in Chrome userscript manager." }),
                    /* @__PURE__ */ u("li", { children: "Confirm widget loads on x.com and settings toggles work." }),
                    /* @__PURE__ */ u("li", { children: "Confirm hooks/counters increment with Safe mode off." }),
                    /* @__PURE__ */ u("li", { children: "Run Bundle ZIP export/import and legacy import." }),
                    /* @__PURE__ */ u("li", { children: "Run one media export and one diagnostics export." }),
                    /* @__PURE__ */ u("li", { children: "Record any Chrome-only console errors or CSP differences." })
                  ] })
                ] })
              ] }),
              /* @__PURE__ */ u("div", { class: "mt-3 rounded-box-half border border-base-300 bg-base-100 p-3", children: [
                /* @__PURE__ */ u("div", { class: "grid min-w-0 grid-cols-2 items-center gap-2", children: [
                  /* @__PURE__ */ u("label", { class: "label min-w-0 cursor-pointer gap-2 py-0", children: [
                    /* @__PURE__ */ u("span", { class: "text-xs", children: t("Diagnostic capture") }),
                    /* @__PURE__ */ u(
                      "input",
                      {
                        type: "checkbox",
                        class: "toggle toggle-sm",
                        checked: qcDiagnosticCaptureEnabled.value,
                        onChange: (event) => {
                          const next = event.target.checked;
                          setDiagnosticCaptureEnabled(next);
                          qcDiagnosticCaptureEnabled.value = next;
                        }
                      }
                    )
                  ] }),
                  /* @__PURE__ */ u(
                    "button",
                    {
                      class: cx(styles.wrapButton, "btn-outline"),
                      onClick: () => {
                        clearDiagnosticBuffers();
                        qcStatus.value = t("Diagnostic buffers cleared.");
                      },
                      children: t("Clear Buffers")
                    }
                  ),
                  /* @__PURE__ */ u(
                    "button",
                    {
                      class: cx(styles.wrapButton, "btn-primary col-span-2"),
                      disabled: qcBusy.value,
                      onClick: exportQcDiagnostics,
                      children: qcBusy.value ? t("Preparing...") : t("Export QC Diagnostics")
                    }
                  )
                ] }),
                /* @__PURE__ */ u("div", { class: "mt-2 font-mono text-xs opacity-70", children: qcStatus.value || t("QC idle.") }),
                /* @__PURE__ */ u("div", { class: "mt-2 text-xs opacity-70", children: [
                  "Full runbook: ",
                  /* @__PURE__ */ u("code", { children: "docs/release/unified-qc-session-runbook.md" })
                ] })
              ] })
            ] }),
            /* @__PURE__ */ u("div", { class: "flex space-x-2", children: [
              /* @__PURE__ */ u("span", { class: "flex-grow" }),
              /* @__PURE__ */ u("button", { class: "btn", onClick: toggleQcSession, children: "Close" })
            ] })
          ]
        }
      )
    ] });
  }
  function ControlPanelShell({
    currentTheme,
    show,
    title,
    byline,
    description: description2,
    hookLine,
    healthLine,
    onToggle,
    children
  }) {
    return /* @__PURE__ */ u(
      "section",
      {
        "data-theme": currentTheme,
        class: cx(
          "card card-compact bg-base-100 fixed border shadow-xl w-80 leading-loose text-base-content py-3 rounded-box border-solid border-neutral-content border-opacity-50 left-8 top-8 transition-transform duration-500 flex flex-col overflow-hidden",
          show ? "translate-x-0 transform-none" : "translate-x-[-500px]"
        ),
        style: { maxHeight: "calc(100vh - 4rem)" },
        children: [
          /* @__PURE__ */ u("header", { class: "mx-4 mb-1 flex h-9 items-center", children: [
            /* @__PURE__ */ u(IconBrandTwitterFilled, { class: "mr-2 shrink-0 text-base-content" }),
            /* @__PURE__ */ u("div", { class: "flex-grow leading-none", children: [
              /* @__PURE__ */ u("h2", { class: "font-semibold leading-none text-xl m-0", children: title }),
              byline ? /* @__PURE__ */ u("p", { class: "font-mono text-[10px] opacity-70 m-0 mt-1", children: byline }) : null
            ] }),
            /* @__PURE__ */ u(ErrorBoundary, { children: /* @__PURE__ */ u(Settings, {}) }),
            /* @__PURE__ */ u(
              "div",
              {
                onClick: onToggle,
                class: "w-9 h-9 cursor-pointer flex justify-center items-center transition-colors duration-200 rounded-full hover:bg-base-200",
                children: /* @__PURE__ */ u(IconX, {})
              }
            )
          ] }),
          /* @__PURE__ */ u("p", { class: "mx-4 mb-1 text-sm leading-none text-base-content text-opacity-70", children: description2 }),
          /* @__PURE__ */ u("p", { class: "mx-4 mb-1 font-mono text-xs leading-none text-base-content text-opacity-60", children: hookLine }),
          /* @__PURE__ */ u("p", { class: "mx-4 mb-1 font-mono text-xs leading-none text-base-content text-opacity-60", children: healthLine }),
          /* @__PURE__ */ u("div", { class: "divider mt-0 mb-0" }),
          /* @__PURE__ */ u("main", { class: "min-h-0 grow overflow-y-auto overscroll-contain scroll-smooth", children: /* @__PURE__ */ u("div", { class: "pl-4 pr-2", children }) })
        ]
      }
    );
  }
  function createResultSetId() {
    if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
      return crypto.randomUUID();
    }
    return `rs-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
  }
  function extractStableRecordId(record, index) {
    var _a2, _b2;
    if (!record || typeof record !== "object") {
      return `row-${index}`;
    }
    const row = record;
    const normalizeScalar = (value) => {
      if (typeof value === "string" && value.trim()) {
        return value.trim();
      }
      if (typeof value === "number" && Number.isFinite(value)) {
        return String(value);
      }
      return "";
    };
    const firstScalar = (...values) => {
      for (const value of values) {
        const normalized = normalizeScalar(value);
        if (normalized) return normalized;
      }
      return "";
    };
    const baseId = firstScalar(
      row.id,
      row.rest_id,
      row.id_str,
      (_a2 = row.legacy) == null ? void 0 : _a2.id_str,
      (_b2 = row.core) == null ? void 0 : _b2.screen_name,
      row.screen_name
    );
    const contextParts = [
      row.__bookmark_folder_id,
      row.__twe_imported_bundle_id,
      row.__twe_imported_snapshot_id,
      row.entryId,
      row.conversationId,
      row.conversation_id,
      row.dm_conversation_id
    ].map(normalizeScalar).filter(Boolean);
    const id2 = baseId && contextParts.length ? `${baseId}::${contextParts.join("::")}` : baseId;
    return id2 || `row-${index}`;
  }
  function readRecordPath$1(record, path) {
    let current = record;
    for (const part of path.split(".")) {
      if (!current || typeof current !== "object") return void 0;
      current = current[part];
    }
    return current;
  }
  function normalizeLookupId(value) {
    if (typeof value === "string" && value.trim()) return value.trim();
    if (typeof value === "number" && Number.isFinite(value)) return String(value);
    return "";
  }
  function collectRecordLookupIds(record, fallbackIndex) {
    const ids = /* @__PURE__ */ new Set();
    const add = (value) => {
      const normalized = normalizeLookupId(value);
      if (normalized) ids.add(normalized);
    };
    add(extractStableRecordId(record, fallbackIndex));
    if (record && typeof record === "object") {
      const row = record;
      add(row.id);
      add(row.rest_id);
      add(row.id_str);
      add(readRecordPath$1(row, "legacy.id_str"));
      add(readRecordPath$1(row, "legacy.id"));
      add(readRecordPath$1(row, "core.user_results.result.rest_id"));
      add(readRecordPath$1(row, "core.screen_name"));
      add(row.__twe_imported_snapshot_id);
      add(row.__twe_imported_source_id);
    }
    return [...ids];
  }
  function resolveOrderedAvailableRecords(ids, recordById, attemptedIds = /* @__PURE__ */ new Set()) {
    const records = [];
    for (const id2 of ids) {
      const record = recordById.get(id2);
      if (record) {
        records.push(record);
        continue;
      }
      if (attemptedIds.has(id2)) {
        continue;
      }
      break;
    }
    return records;
  }
  function serializeSortingState(sorting) {
    if (!(sorting == null ? void 0 : sorting.length)) return "default";
    return sorting.map((entry) => `${entry.id}:${entry.desc ? "desc" : "asc"}`).join(",");
  }
  function createResultSetSnapshot(args) {
    return {
      resultSetId: createResultSetId(),
      scope: "table",
      engine: "local-sync",
      generatedAtMs: Date.now(),
      queryText: args.queryText,
      sort: args.sort,
      totalMatches: args.totalMatches,
      ids: [...args.ids],
      warnings: [...args.warnings]
    };
  }
  const field_path_pattern = "^[a-zA-Z0-9_.]+$";
  const known_filter_keys = ["from", "from_id", "author_id", "to", "to_id", "in_reply_to_id", "lang", "since", "until", "since_time", "until_time", "since_id", "max_id", "conversation_id", "min_faves", "min_likes", "min_retweets", "min_replies", "min_bookmarks", "url", "domain", "id", "source", "card_name", "filter", "include", "is", "has", "bookmark_folder", "folder", "route", "mention", "hashtag", "cashtag"];
  const non_highlight_filters = ["since", "until", "since_time", "until_time", "since_id", "max_id", "min_faves", "min_likes", "min_retweets", "min_replies", "min_bookmarks", "id", "from_id", "author_id", "to_id", "in_reply_to_id", "conversation_id"];
  const free_text = { "min_content_term_length": 2, "full_run_exact_min_terms": 2, "stop_terms": ["a", "an", "the", "and", "or", "but", "if", "then", "than", "to", "of", "in", "on", "for", "with", "by", "at", "from", "into", "onto", "as", "is", "are", "was", "were", "be", "been", "being", "do", "does", "did", "done", "have", "has", "had", "can", "could", "should", "would", "will", "just", "about", "what", "when", "where", "why", "how", "who", "whom", "whose", "this", "that", "these", "those", "it", "its", "my", "your", "our", "their", "his", "her", "me", "you", "we", "they", "he", "she", "them", "us", "post", "tweet", "too"], "bigram_slop": 2, "trigram_slop": 3, "fourgram_slop": 4, "bigram_boost": 20, "trigram_boost": 60, "fourgram_boost": 140, "full_run_exact_boost": 320 };
  const prefix = { "min_term_length": 3, "max_expansions": 128 };
  const fuzzy = { "min_term_length": 5, "max_edit_distance": 1, "prefix_root_length": 4, "max_expansions": 64 };
  const contract = {
    field_path_pattern,
    known_filter_keys,
    non_highlight_filters,
    free_text,
    prefix,
    fuzzy
  };
  const SEARCH_FIELD_PATH_PATTERN = new RegExp(contract.field_path_pattern);
  const SEARCH_KNOWN_FILTER_KEYS = new Set(contract.known_filter_keys);
  const SEARCH_NON_HIGHLIGHT_FILTERS = new Set(contract.non_highlight_filters);
  const SEARCH_FREE_TEXT = contract.free_text;
  const SEARCH_PREFIX = contract.prefix;
  const SEARCH_FUZZY = contract.fuzzy;
  const SEARCH_FREE_TEXT_STOP_TERMS = new Set(
    (contract.free_text.stop_terms || []).map(
      (value) => String(value || "").trim().toLowerCase()
    ).filter(Boolean)
  );
  function isRawLexicalToken(token) {
    return token.kind === "term" || token.kind === "phrase";
  }
  const TERM_TOKEN_PATTERN$1 = /[\p{L}\p{N}_]+(?:['’][\p{L}\p{N}_]+)*/gu;
  const MAX_HIGHLIGHT_TERMS = 32;
  const SEARCH_FREE_TEXT_MIN_CONTENT_TERM_LENGTH = Math.max(
    1,
    Number(SEARCH_FREE_TEXT.min_content_term_length)
  );
  const SEARCH_FREE_TEXT_FULL_RUN_EXACT_MIN_TERMS = Math.max(
    2,
    Number(SEARCH_FREE_TEXT.full_run_exact_min_terms)
  );
  const SEARCH_PREFIX_MIN_TERM_LENGTH$1 = Math.max(1, Number(SEARCH_PREFIX.min_term_length));
  const SEARCH_FUZZY_MIN_TERM_LENGTH$1 = Math.max(1, Number(SEARCH_FUZZY.min_term_length));
  const SEARCH_OPERATOR_HELP_ENTRIES = [
    {
      category: "lexical",
      syntax: "plain free text",
      description: "Unstructured text expands into content-term matches plus boosted adjacent phrase windows; common filler words are deprioritized unless quoted.",
      examples: ["tour guides in France", "distributed systems design"]
    },
    {
      category: "lexical",
      syntax: '"exact phrase"~2',
      description: "Phrase search with optional slop.",
      examples: ['"design system"', '"design system"~2', "machine^2"]
    },
    {
      category: "lexical",
      syntax: "AND / OR / NOT / (...)",
      description: "Boolean operators with standard precedence and parentheses.",
      examples: [
        "machine OR reliability",
        "(machine OR reliability) AND fragile",
        "machine AND NOT reliability"
      ]
    },
    {
      category: "identity",
      syntax: "from: / from_id: / author_id:",
      description: "Match the author handle or author id.",
      examples: ["from:alice", "from_id:12345"]
    },
    {
      category: "identity",
      syntax: "@user",
      description: "Shorthand for an enforced author constraint, equivalent to from:user.",
      examples: ["@sama", "@openai"]
    },
    {
      category: "identity",
      syntax: "to: / to_id: / in_reply_to_id: / id: / conversation_id:",
      description: "Match reply targets, entity ids, or conversation ids.",
      examples: ["to:alice", "in_reply_to_id:1888", "id:1999"]
    },
    {
      category: "metadata",
      syntax: "bookmark_folder: / folder:",
      description: "Match bookmark folder id or folder name.",
      examples: ["bookmark_folder:12345", 'folder:"Design References"']
    },
    {
      category: "metadata",
      syntax: "lang: / route: / source: / card_name:",
      description: "Match language, route surface, source text, or card name.",
      examples: ["lang:en", "route:bookmarks", "source:iphone"]
    },
    {
      category: "metadata",
      syntax: "domain: / url:",
      description: "Match domains or URLs found in tweets.",
      examples: ["domain:github.com", "url:openai.com"]
    },
    {
      category: "presence",
      syntax: "is:",
      description: "Boolean state filters.",
      examples: ["is:bookmarked", "is:reply", "is:verified", "is:blue_verified"]
    },
    {
      category: "presence",
      syntax: "has:",
      description: "Presence filters for media, links, mentions, hashtags, cashtags, engagement, and polls.",
      examples: ["has:media", "has:links", "has:hashtags", "has:engagement"]
    },
    {
      category: "compatibility",
      syntax: "filter: / include:",
      description: "Compatibility aliases retained for Twitter-style queries.",
      examples: ["filter:media", "filter:replies", "include:nativeretweets"]
    },
    {
      category: "numeric_date",
      syntax: "min_likes: / min_retweets: / min_replies: / min_bookmarks:",
      description: "Numeric threshold filters.",
      examples: ["min_likes:50", "min_bookmarks:10"]
    },
    {
      category: "numeric_date",
      syntax: "since: / until: / since_time: / until_time: / since_id: / max_id:",
      description: "Date, timestamp, and Snowflake-style boundary filters.",
      examples: ["since:2026-03-01", "until:2026-03-31", "since_id:1900"]
    },
    {
      category: "compatibility",
      syntax: "mention: / #tag / $symbol",
      description: "Explicit mention filter plus shorthand hashtag and cashtag filters.",
      examples: ["mention:alice", "#ai", "$tsla"]
    },
    {
      category: "metadata",
      syntax: 'field:value / field:"quoted phrase"',
      description: "Field-scoped lexical search over raw nested paths, including dotted object paths and arrays.",
      examples: [
        "md.collection:B",
        "legacy.entities.hashtags.text:ai",
        'core.user_results.result.legacy.name:"Jane Doe"'
      ]
    }
  ];
  function warning(code, message, token = "") {
    return {
      code,
      message,
      token: token || void 0,
      severity: "warn"
    };
  }
  function clampBoost(value) {
    const parsed = Number(value);
    if (!Number.isFinite(parsed)) return 1;
    return Math.max(0.05, Math.min(100, parsed));
  }
  function tokenizeText$1(value) {
    if (!value) return [];
    const matches = value.toLowerCase().match(TERM_TOKEN_PATTERN$1);
    return matches ? matches.map((token) => token.replace(/['’]/g, "")).filter(Boolean) : [];
  }
  function isLowSignalFreeTextTerm(term) {
    const normalized = String(term || "").trim().toLowerCase();
    if (!normalized) return true;
    if (normalized.length < SEARCH_FREE_TEXT_MIN_CONTENT_TERM_LENGTH) {
      return true;
    }
    return SEARCH_FREE_TEXT_STOP_TERMS.has(normalized);
  }
  function getFreeTextRunContentTerms(tokens) {
    const allTerms = tokens.flatMap((token) => tokenizeText$1(token.value));
    const contentTerms = allTerms.filter((term) => !isLowSignalFreeTextTerm(term));
    return contentTerms.length ? contentTerms : allTerms;
  }
  function getFreeTextRunSingletonTokens(tokens) {
    const contentTerms = new Set(getFreeTextRunContentTerms(tokens));
    if (!contentTerms.size) {
      return tokens;
    }
    const filtered = tokens.filter(
      (token) => contentTerms.has(
        String(token.value || "").trim().toLowerCase()
      )
    );
    return filtered.length ? filtered : tokens;
  }
  function countContentTermsInFreeTextWindow(tokens) {
    let count = 0;
    for (const token of tokens) {
      for (const term of tokenizeText$1(token.value)) {
        if (!isLowSignalFreeTextTerm(term)) {
          count += 1;
        }
      }
    }
    return count;
  }
  function normalizeTermValue(value) {
    return tokenizeText$1(value).join(" ").trim();
  }
  function roundBoost(value) {
    if (!Number.isFinite(value)) return 1;
    return Math.round(value * 100) / 100;
  }
  function parseNumericSuffix(raw, separator) {
    const index = raw.lastIndexOf(separator);
    if (index <= 0) {
      return { base: raw, value: null };
    }
    const candidate = raw.slice(index + 1);
    if (!candidate || !/^\d+(?:\.\d+)?$/.test(candidate)) {
      return { base: raw, value: null };
    }
    return { base: raw.slice(0, index), value: candidate };
  }
  function tokenizeQuery(query) {
    const text = String(query || "");
    const out = [];
    let index = 0;
    while (index < text.length) {
      while (index < text.length && /\s/.test(text[index] ?? "")) {
        index += 1;
      }
      if (index >= text.length) break;
      const ch = text[index] ?? "";
      if (ch === "(") {
        out.push({ kind: "lparen" });
        index += 1;
        continue;
      }
      if (ch === ")") {
        out.push({ kind: "rparen" });
        index += 1;
        continue;
      }
      let negated = false;
      if (ch === "-") {
        negated = true;
        index += 1;
        while (index < text.length && /\s/.test(text[index] ?? "")) {
          index += 1;
        }
        if (index >= text.length) break;
        if ((text[index] ?? "") === "(") {
          out.push({ kind: "op", op: "NOT" });
          continue;
        }
      }
      let explicitField;
      const fieldScanStart = index;
      while (index < text.length && /[a-zA-Z0-9_.]/.test(text[index] ?? "")) {
        index += 1;
      }
      if (index > fieldScanStart && (text[index] ?? "") === ":" && (text[index + 1] ?? "") === '"' && SEARCH_FIELD_PATH_PATTERN.test(text.slice(fieldScanStart, index))) {
        explicitField = text.slice(fieldScanStart, index);
        index += 1;
      } else {
        index = fieldScanStart;
      }
      if ((text[index] ?? "") === '"') {
        index += 1;
        let buffer = "";
        while (index < text.length) {
          const current = text[index] ?? "";
          if (current === "\\" && index + 1 < text.length) {
            buffer += text[index + 1] ?? "";
            index += 2;
            continue;
          }
          if (current === '"') {
            index += 1;
            break;
          }
          buffer += current;
          index += 1;
        }
        let slop = 0;
        let boost = 1;
        if ((text[index] ?? "") === "~") {
          index += 1;
          const start2 = index;
          while (index < text.length && /\d/.test(text[index] ?? "")) {
            index += 1;
          }
          if (index > start2) {
            slop = Math.max(0, Number(text.slice(start2, index)) || 0);
          }
        }
        if ((text[index] ?? "") === "^") {
          index += 1;
          const start2 = index;
          while (index < text.length && /[\d.]/.test(text[index] ?? "")) {
            index += 1;
          }
          if (index > start2) {
            boost = clampBoost(text.slice(start2, index));
          }
        }
        out.push({
          kind: "phrase",
          value: buffer.trim(),
          negated,
          boost,
          slop,
          field: explicitField,
          quoted: true
        });
        continue;
      }
      const start = index;
      while (index < text.length && !/\s/.test(text[index] ?? "") && (text[index] ?? "") !== "(" && (text[index] ?? "") !== ")") {
        index += 1;
      }
      const raw = text.slice(start, index).trim();
      if (!raw) continue;
      const { base: rawBase, value: boostRaw } = parseNumericSuffix(raw, "^");
      const upper = rawBase.toUpperCase();
      if ((upper === "AND" || upper === "OR" || upper === "NOT") && !negated) {
        out.push({ kind: "op", op: upper });
        continue;
      }
      out.push({
        kind: "term",
        value: rawBase,
        negated,
        boost: boostRaw ? clampBoost(boostRaw) : 1,
        slop: 0
      });
    }
    return out;
  }
  function markTerminalLooseQueryToken(query, tokens) {
    if (!tokens.length) return;
    if (/\s$/.test(String(query || ""))) return;
    for (let index = tokens.length - 1; index >= 0; index -= 1) {
      const token = tokens[index];
      if (!token || !isRawLexicalToken(token)) continue;
      if (token.kind !== "term" || token.field || token.quoted) return;
      const normalized = normalizeTermValue(token.value);
      if (normalized.length >= SEARCH_PREFIX_MIN_TERM_LENGTH$1) {
        token.prefix = true;
      }
      if (normalized.length >= SEARCH_FUZZY_MIN_TERM_LENGTH$1) {
        token.fuzzy = true;
      }
      return;
    }
  }
  function isLexicalOperand(token) {
    if (!token) return false;
    return token.kind === "term" || token.kind === "phrase";
  }
  function buildLexicalAst(tokens) {
    const warnings = [];
    if (!tokens.length) {
      return { ast: null, warnings };
    }
    const infix = [];
    let previous = null;
    for (const token of tokens) {
      let needsAnd = false;
      if (previous) {
        const previousIsValue = isLexicalOperand(previous) || previous.kind === "rparen";
        const currentStartsValue = isLexicalOperand(token) || token.kind === "lparen" || token.kind === "op" && token.op === "NOT";
        if (previousIsValue && currentStartsValue) {
          needsAnd = true;
        }
      }
      if (needsAnd) {
        infix.push({ kind: "op", op: "AND" });
      }
      infix.push(token);
      previous = token;
    }
    const precedence = { OR: 1, AND: 2, NOT: 3 };
    const output = [];
    const stack = [];
    for (const token of infix) {
      if (isLexicalOperand(token)) {
        output.push(token);
        continue;
      }
      if (token.kind === "lparen") {
        stack.push(token);
        continue;
      }
      if (token.kind === "rparen") {
        let foundLParen = false;
        while (stack.length) {
          const top = stack.pop();
          if (!top) break;
          if (top.kind === "lparen") {
            foundLParen = true;
            break;
          }
          output.push(top);
        }
        if (!foundLParen) {
          warnings.push(warning("boolean_syntax", "unmatched closing parenthesis in query"));
        }
        continue;
      }
      if (token.kind !== "op") {
        continue;
      }
      while (stack.length) {
        const top = stack[stack.length - 1];
        if (!top || top.kind !== "op") break;
        const shouldPop = token.op === "NOT" ? precedence[token.op] < precedence[top.op] : precedence[token.op] <= precedence[top.op];
        if (!shouldPop) break;
        output.push(stack.pop());
      }
      stack.push(token);
    }
    while (stack.length) {
      const top = stack.pop();
      if (!top) break;
      if (top.kind === "lparen") {
        warnings.push(warning("boolean_syntax", "unmatched opening parenthesis in query"));
        continue;
      }
      output.push(top);
    }
    const astStack = [];
    for (const token of output) {
      if (isLexicalOperand(token)) {
        astStack.push(token);
        continue;
      }
      if (token.kind !== "op") continue;
      if (token.op === "NOT") {
        const child = astStack.pop();
        if (!child) {
          warnings.push(warning("boolean_syntax", "dangling NOT operator in query", "NOT"));
          continue;
        }
        astStack.push({ kind: "op", op: "NOT", child });
        continue;
      }
      const right = astStack.pop();
      const left = astStack.pop();
      if (!left || !right) {
        warnings.push(warning("boolean_syntax", `dangling ${token.op} operator in query`, token.op));
        continue;
      }
      astStack.push({ kind: "op", op: token.op, left, right });
    }
    if (astStack.length === 1) {
      return { ast: astStack[0] ?? null, warnings };
    }
    const operands = tokens.filter(isLexicalOperand);
    if (!operands.length) {
      return { ast: null, warnings };
    }
    const firstOperand = operands[0];
    if (!firstOperand) {
      return { ast: null, warnings };
    }
    let fallback = firstOperand;
    for (let index = 1; index < operands.length; index += 1) {
      const operand = operands[index];
      if (!operand) continue;
      fallback = { kind: "op", op: "AND", left: fallback, right: operand };
    }
    warnings.push(
      warning(
        "boolean_syntax",
        "query boolean expression was malformed; fell back to implicit AND between lexical terms"
      )
    );
    return { ast: fallback, warnings };
  }
  function collectLexicalTokens(node, polarity) {
    const out = [];
    const walk = (current, currentPolarity) => {
      if (!current) return;
      if (isLexicalOperand(current)) {
        if (currentPolarity === polarity) {
          out.push(current);
        }
        return;
      }
      if (current.op === "NOT") {
        walk(current.child, !currentPolarity);
        return;
      }
      walk(current.left, currentPolarity);
      walk(current.right, currentPolarity);
    };
    walk(node, true);
    return out;
  }
  function collectFlatBooleanChain(node, op, out) {
    if (!node) return;
    if (!isLexicalOperand(node) && node.op === op) {
      collectFlatBooleanChain(node.left, op, out);
      collectFlatBooleanChain(node.right, op, out);
      return;
    }
    out.push(node);
  }
  function astToString(node) {
    if (!node) return "";
    if (isLexicalOperand(node)) {
      let out = node.kind === "phrase" ? `"${node.value}"` : node.value;
      if (node.prefix) {
        out += "*";
      }
      if (node.kind === "phrase" && node.slop > 0) {
        out += `~${node.slop}`;
      }
      if (Math.abs(node.boost - 1) > 1e-9) {
        out += `^${node.boost}`;
      }
      if (node.field) {
        out = `${node.field}:${out}`;
      }
      return out;
    }
    if (node.op === "NOT") {
      const child = astToString(node.child);
      return child ? `NOT (${child})` : "NOT (?)";
    }
    const parts = [];
    collectFlatBooleanChain(node, node.op, parts);
    const rendered = parts.map((part) => {
      const text = astToString(part);
      if (!text) return "";
      if (!isLexicalOperand(part) && part.op !== node.op) {
        return `(${text})`;
      }
      return text;
    }).filter(Boolean);
    return rendered.join(` ${node.op} `);
  }
  function isExpandableFreeTextToken(token) {
    return !token.field && token.kind === "term" && tokenizeText$1(token.value).length === 1;
  }
  function buildExpandedFreeTextRun(tokens) {
    var _a2, _b2, _c, _d, _e, _f, _g, _h;
    if (tokens.length <= 1) {
      return tokens;
    }
    const out = [];
    const seen2 = /* @__PURE__ */ new Set();
    const pushUnique = (token) => {
      const key = `${token.kind}|${token.field || ""}|${token.value}|${token.slop}|${token.boost}`;
      if (seen2.has(key)) return;
      seen2.add(key);
      out.push(token);
    };
    const phraseBoostForWindow = (window2, baseBoost) => {
      const averageBoost = window2.reduce((sum, token) => sum + token.boost, 0) / window2.length;
      return roundBoost(baseBoost * averageBoost);
    };
    for (const token of getFreeTextRunSingletonTokens(tokens)) {
      pushUnique(token);
    }
    for (let index = 0; index <= tokens.length - 2; index += 1) {
      const window2 = tokens.slice(index, index + 2);
      if (countContentTermsInFreeTextWindow(window2) < 2) {
        continue;
      }
      pushUnique({
        kind: "phrase",
        value: window2.map((token) => token.value).join(" "),
        slop: SEARCH_FREE_TEXT.bigram_slop,
        boost: phraseBoostForWindow(window2, SEARCH_FREE_TEXT.bigram_boost),
        prefix: !!((_a2 = window2[window2.length - 1]) == null ? void 0 : _a2.prefix),
        fuzzy: !!((_b2 = window2[window2.length - 1]) == null ? void 0 : _b2.fuzzy)
      });
    }
    for (let index = 0; index <= tokens.length - 3; index += 1) {
      const window2 = tokens.slice(index, index + 3);
      if (countContentTermsInFreeTextWindow(window2) < 2) {
        continue;
      }
      pushUnique({
        kind: "phrase",
        value: window2.map((token) => token.value).join(" "),
        slop: SEARCH_FREE_TEXT.trigram_slop,
        boost: phraseBoostForWindow(window2, SEARCH_FREE_TEXT.trigram_boost),
        prefix: !!((_c = window2[window2.length - 1]) == null ? void 0 : _c.prefix),
        fuzzy: !!((_d = window2[window2.length - 1]) == null ? void 0 : _d.fuzzy)
      });
    }
    for (let index = 0; index <= tokens.length - 4; index += 1) {
      const window2 = tokens.slice(index, index + 4);
      if (countContentTermsInFreeTextWindow(window2) < 2) {
        continue;
      }
      pushUnique({
        kind: "phrase",
        value: window2.map((token) => token.value).join(" "),
        slop: SEARCH_FREE_TEXT.fourgram_slop,
        boost: phraseBoostForWindow(window2, SEARCH_FREE_TEXT.fourgram_boost),
        prefix: !!((_e = window2[window2.length - 1]) == null ? void 0 : _e.prefix),
        fuzzy: !!((_f = window2[window2.length - 1]) == null ? void 0 : _f.fuzzy)
      });
    }
    if (tokens.length >= SEARCH_FREE_TEXT_FULL_RUN_EXACT_MIN_TERMS) {
      pushUnique({
        kind: "phrase",
        value: tokens.map((token) => token.value).join(" "),
        slop: 0,
        boost: phraseBoostForWindow(tokens, SEARCH_FREE_TEXT.full_run_exact_boost),
        prefix: !!((_g = tokens[tokens.length - 1]) == null ? void 0 : _g.prefix),
        fuzzy: !!((_h = tokens[tokens.length - 1]) == null ? void 0 : _h.fuzzy)
      });
    }
    return out;
  }
  function pushLexicalTokenRun(destination, pendingTerms) {
    if (!pendingTerms.length) return;
    if (pendingTerms.length === 1) {
      destination.push(pendingTerms[0]);
      pendingTerms.length = 0;
      return;
    }
    const expanded = buildExpandedFreeTextRun(pendingTerms);
    destination.push({ kind: "lparen" });
    expanded.forEach((token, index) => {
      if (index > 0) {
        destination.push({ kind: "op", op: "OR" });
      }
      destination.push(token);
    });
    destination.push({ kind: "rparen" });
    pendingTerms.length = 0;
  }
  function uniqueTerms(values) {
    const seen2 = /* @__PURE__ */ new Set();
    const out = [];
    for (const value of values) {
      for (const term of tokenizeText$1(value)) {
        if (!term || seen2.has(term)) continue;
        seen2.add(term);
        out.push(term);
      }
    }
    return out;
  }
  function collectHighlightTerms(positiveLexicalTokens, filters) {
    const terms = /* @__PURE__ */ new Set();
    for (const token of positiveLexicalTokens) {
      for (const term of tokenizeText$1(token.value)) {
        if (term && !isLowSignalFreeTextTerm(term)) {
          terms.add(term);
        }
      }
    }
    for (const filter of filters) {
      if (filter.negated || SEARCH_NON_HIGHLIGHT_FILTERS.has(filter.name)) continue;
      for (const term of tokenizeText$1(filter.value)) {
        if (term) {
          terms.add(term);
        }
      }
    }
    return [...terms].slice(0, MAX_HIGHLIGHT_TERMS);
  }
  function parseSearchQuery(query) {
    var _a2;
    const rawTokens = tokenizeQuery(query);
    markTerminalLooseQueryToken(query, rawTokens);
    const filters = [];
    const unsupported = [];
    const lexicalTokens = [];
    const pendingFreeTextTerms = [];
    const positiveTermSourceValues = [];
    const orderedTermSourceValues = [];
    const flushPendingFreeTextTerms = () => {
      if (pendingFreeTextTerms.length) {
        const runTerms = getFreeTextRunContentTerms(pendingFreeTextTerms);
        orderedTermSourceValues.push(...runTerms);
        positiveTermSourceValues.push(...runTerms);
      }
      pushLexicalTokenRun(lexicalTokens, pendingFreeTextTerms);
    };
    for (const token of rawTokens) {
      if (!isRawLexicalToken(token)) {
        flushPendingFreeTextTerms();
        lexicalTokens.push(token);
        continue;
      }
      const rawValue = String(token.value || "").trim();
      if (!rawValue) continue;
      let lexicalKind = token.kind;
      let lexicalValue = rawValue;
      let lexicalField = (_a2 = token.field) == null ? void 0 : _a2.trim();
      let handledAsFilter = false;
      if (token.kind === "term" && rawValue.startsWith("@") && rawValue.length > 1) {
        const value = normalizeTermValue(rawValue.slice(1));
        if (value) {
          filters.push({ name: "from", value, negated: token.negated });
          flushPendingFreeTextTerms();
          continue;
        }
      } else if (token.kind === "term" && rawValue.startsWith("#") && rawValue.length > 1) {
        const value = normalizeTermValue(rawValue.slice(1));
        if (value) {
          filters.push({ name: "hashtag", value, negated: token.negated });
          lexicalKind = "term";
          lexicalValue = value;
          handledAsFilter = true;
        }
      } else if (token.kind === "term" && rawValue.startsWith("$") && rawValue.length > 1) {
        const value = normalizeTermValue(rawValue.slice(1));
        if (value) {
          filters.push({ name: "cashtag", value, negated: token.negated });
          lexicalKind = "term";
          lexicalValue = value;
          handledAsFilter = true;
        }
      } else if (token.kind === "term" && rawValue.includes(":")) {
        const [rawKey, ...rawRest] = rawValue.split(":");
        const key = String(rawKey || "").trim().toLowerCase();
        const value = rawRest.join(":").trim();
        if (SEARCH_KNOWN_FILTER_KEYS.has(key)) {
          if (!value) {
            unsupported.push(rawValue);
            continue;
          }
          filters.push({ name: key, value, negated: token.negated });
          flushPendingFreeTextTerms();
          continue;
        }
        if (key && value && SEARCH_FIELD_PATH_PATTERN.test(key)) {
          lexicalKind = "term";
          lexicalValue = value;
          lexicalField = key;
        }
      }
      const lexicalToken = {
        kind: lexicalKind,
        value: lexicalKind === "term" ? normalizeTermValue(lexicalValue) : lexicalValue.trim(),
        boost: token.boost,
        slop: token.slop,
        field: lexicalField || void 0,
        quoted: !!token.quoted,
        prefix: !!token.prefix,
        fuzzy: !!token.fuzzy
      };
      if (!lexicalToken.value) {
        continue;
      }
      if (token.negated) {
        flushPendingFreeTextTerms();
        lexicalTokens.push({ kind: "op", op: "NOT" });
        lexicalTokens.push(lexicalToken);
        continue;
      }
      if (!handledAsFilter && isExpandableFreeTextToken(lexicalToken)) {
        pendingFreeTextTerms.push(lexicalToken);
        continue;
      }
      flushPendingFreeTextTerms();
      if (!lexicalToken.field) {
        const tokenTerms = tokenizeText$1(lexicalToken.value);
        orderedTermSourceValues.push(...tokenTerms);
        positiveTermSourceValues.push(...tokenTerms);
      }
      lexicalTokens.push(lexicalToken);
    }
    flushPendingFreeTextTerms();
    const { ast: lexicalAst, warnings } = buildLexicalAst(lexicalTokens);
    const positiveLexicalTokens = collectLexicalTokens(lexicalAst, true);
    const negativeLexicalTokens = collectLexicalTokens(lexicalAst, false);
    const orderedTerms = orderedTermSourceValues.filter(Boolean);
    const positiveTerms = uniqueTerms(positiveTermSourceValues);
    const highlightTerms = collectHighlightTerms(positiveLexicalTokens, filters);
    const unsupportedWarnings = unsupported.map(
      (tokenValue) => warning("unsupported_token", `unsupported token: ${tokenValue}`, tokenValue)
    );
    return {
      query: String(query || ""),
      lexicalTokens,
      lexicalAst,
      lexicalExpression: astToString(lexicalAst),
      positiveLexicalTokens,
      negativeLexicalTokens,
      positiveTerms,
      filters,
      unsupported,
      orderedTerms,
      highlightTerms,
      warnings: [...warnings, ...unsupportedWarnings],
      hasPositiveLexical: positiveLexicalTokens.length > 0,
      filterBooleanSemantics: "global_and"
    };
  }
  const SEARCH_HISTORY_STORAGE_KEY = "twe_search_history_v1";
  function canUseStorage() {
    return typeof localStorage !== "undefined";
  }
  function normalizeFolderIds(values) {
    return [...new Set(values.map((value) => value.trim()).filter(Boolean))].sort();
  }
  function makeEntryIdentity(scope, normalizedQuery, folderIds) {
    return `${scope}::${normalizedQuery}::${normalizeFolderIds(folderIds).join(",")}`;
  }
  function readSearchHistory(scope) {
    if (!canUseStorage()) return [];
    try {
      const raw = localStorage.getItem(SEARCH_HISTORY_STORAGE_KEY);
      if (!raw) return [];
      const parsed = JSON.parse(raw);
      if (!Array.isArray(parsed)) return [];
      const rows = parsed.filter(
        (item) => !!item && typeof item === "object"
      );
      if (!scope) {
        return rows;
      }
      return rows.filter((row) => row.scope === scope);
    } catch {
      return [];
    }
  }
  function appendSearchHistoryEntry(entry) {
    const now = Date.now();
    const nextEntry = {
      ...entry,
      selected_folders: normalizeFolderIds(entry.selected_folders),
      id: `${entry.scope}:${now}:${Math.random().toString(36).slice(2, 8)}`,
      searched_at_iso: new Date(now).toISOString(),
      repeat_count: 1
    };
    const current = readSearchHistory();
    const next = [...current];
    const identity = makeEntryIdentity(
      nextEntry.scope,
      nextEntry.normalized_query,
      nextEntry.selected_folders
    );
    const last = next[next.length - 1];
    if (last) {
      const lastIdentity = makeEntryIdentity(
        last.scope,
        last.normalized_query,
        last.selected_folders || []
      );
      if (lastIdentity === identity) {
        next[next.length - 1] = {
          ...last,
          query: nextEntry.query,
          title: nextEntry.title,
          searched_at_ms: nextEntry.searched_at_ms,
          searched_at_iso: nextEntry.searched_at_iso,
          result_count: nextEntry.result_count,
          total_records: nextEntry.total_records,
          lexical_expression: nextEntry.lexical_expression,
          warning_messages: [...nextEntry.warning_messages],
          repeat_count: (last.repeat_count || 1) + 1
        };
      } else {
        next.push(nextEntry);
      }
    } else {
      next.push(nextEntry);
    }
    const trimmed = next.slice(-4e3);
    if (canUseStorage()) {
      try {
        localStorage.setItem(SEARCH_HISTORY_STORAGE_KEY, JSON.stringify(trimmed));
      } catch {
      }
    }
    return trimmed;
  }
  function flexRender(Comp, props) {
    return !Comp ? null : isComponent(Comp) ? /* @__PURE__ */ u(Comp, { ...props }) : Comp;
  }
  function isComponent(component) {
    return typeof component === "function";
  }
  /**
   * @license MIT
   * https://github.com/TanStack/table/blob/main/packages/react-table/src/index.tsx
   */
  function useReactTable(options2) {
    const resolvedOptions = {
      state: {},
      // Dummy state
      onStateChange: () => {
      },
      // noop
      renderFallbackValue: null,
      ...options2
    };
    const [tableRef] = hooks.useState(() => ({
      current: tableCore.createTable(resolvedOptions)
    }));
    const [state, setState] = hooks.useState(() => tableRef.current.initialState);
    tableRef.current.setOptions((prev) => ({
      ...prev,
      ...options2,
      state: {
        ...state,
        ...options2.state
      },
      // Similarly, we'll maintain both our internal state and any user-provided
      // state.
      onStateChange: (updater) => {
        var _a2;
        setState(updater);
        (_a2 = options2.onStateChange) == null ? void 0 : _a2.call(options2, updater);
      }
    }));
    return tableRef.current;
  }
  const jsContent$1 = '(function() {\n  "use strict";\n  const version = "1.0.0";\n  var packageJson = {\n    version\n  };\n  const FALLBACK_HASH_MOD = 4294967296;\n  function fallbackHash(value) {\n    let hash = 2166136261;\n    for (let index = 0; index < value.length; index += 1) {\n      hash ^= value.charCodeAt(index);\n      hash = Math.imul(hash, 16777619) >>> 0;\n    }\n    return hash.toString(16).padStart(8, "0");\n  }\n  async function sha256Hex(value) {\n    var _a2;\n    const bytes = typeof value === "string" ? new TextEncoder().encode(value) : value;\n    if ((_a2 = globalThis.crypto) == null ? void 0 : _a2.subtle) {\n      const digestInput = bytes.buffer.slice(\n        bytes.byteOffset,\n        bytes.byteOffset + bytes.byteLength\n      );\n      const digest = await globalThis.crypto.subtle.digest("SHA-256", digestInput);\n      return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");\n    }\n    let accumulator = "";\n    for (let offset = 0; offset < bytes.length; offset += 4096) {\n      accumulator += fallbackHash(String.fromCharCode(...bytes.slice(offset, offset + 4096)));\n    }\n    return fallbackHash(`${bytes.length}:${accumulator}:${FALLBACK_HASH_MOD}`);\n  }\n  async function createBundleId(seed) {\n    return `bundle_${(await sha256Hex(seed)).slice(0, 24)}`;\n  }\n  async function createBundleRecordId(bundleId, kind, sourceId) {\n    return `record_${(await sha256Hex(`${bundleId}:${kind}:${sourceId}`)).slice(0, 32)}`;\n  }\n  const SAFE_SHARED_DEFAULT_PRIVACY = {\n    includeViewerAccountId: false,\n    includeSourceCaptureTimes: false,\n    includeRawGraphQL: false,\n    includePrivateNotes: false,\n    includeMediaBlobs: false,\n    visibility: "shared_safe"\n  };\n  function buildBundlePrivacySummary(options) {\n    return {\n      visibility: options.visibility,\n      includesViewerAccountId: options.includeViewerAccountId,\n      includesSourceCaptureTimes: options.includeSourceCaptureTimes,\n      includesRawGraphQL: options.includeRawGraphQL,\n      includesPrivateNotes: options.includePrivateNotes,\n      includesMediaBlobs: options.includeMediaBlobs,\n      warnings: describeBundlePrivacyWarnings(options)\n    };\n  }\n  function describeBundlePrivacyWarnings(options) {\n    const warnings = [];\n    if (options.includeViewerAccountId) warnings.push("Includes the exporting account identifier.");\n    if (options.includeSourceCaptureTimes) warnings.push("Includes local capture/import timestamps.");\n    if (options.includeRawGraphQL)\n      warnings.push("Includes raw API payloads that may contain unrelated account context.");\n    if (options.includePrivateNotes) warnings.push("Includes user-authored local notes or labels.");\n    if (options.includeMediaBlobs)\n      warnings.push("Includes downloaded media files, increasing size and redistribution risk.");\n    if (options.visibility === "public" && warnings.length) {\n      warnings.unshift("Public bundle includes fields that should be reviewed before sharing.");\n    }\n    return warnings;\n  }\n  var u8 = Uint8Array, u16 = Uint16Array, i32 = Int32Array;\n  var fleb = new u8([\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    1,\n    1,\n    1,\n    1,\n    2,\n    2,\n    2,\n    2,\n    3,\n    3,\n    3,\n    3,\n    4,\n    4,\n    4,\n    4,\n    5,\n    5,\n    5,\n    5,\n    0,\n    /* unused */\n    0,\n    0,\n    /* impossible */\n    0\n  ]);\n  var fdeb = new u8([\n    0,\n    0,\n    0,\n    0,\n    1,\n    1,\n    2,\n    2,\n    3,\n    3,\n    4,\n    4,\n    5,\n    5,\n    6,\n    6,\n    7,\n    7,\n    8,\n    8,\n    9,\n    9,\n    10,\n    10,\n    11,\n    11,\n    12,\n    12,\n    13,\n    13,\n    /* unused */\n    0,\n    0\n  ]);\n  var clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);\n  var freb = function(eb, start) {\n    var b = new u16(31);\n    for (var i2 = 0; i2 < 31; ++i2) {\n      b[i2] = start += 1 << eb[i2 - 1];\n    }\n    var r = new i32(b[30]);\n    for (var i2 = 1; i2 < 30; ++i2) {\n      for (var j = b[i2]; j < b[i2 + 1]; ++j) {\n        r[j] = j - b[i2] << 5 | i2;\n      }\n    }\n    return { b, r };\n  };\n  var _a = freb(fleb, 2), fl = _a.b, revfl = _a.r;\n  fl[28] = 258, revfl[258] = 28;\n  var _b = freb(fdeb, 0), revfd = _b.r;\n  var rev = new u16(32768);\n  for (var i = 0; i < 32768; ++i) {\n    var x = (i & 43690) >> 1 | (i & 21845) << 1;\n    x = (x & 52428) >> 2 | (x & 13107) << 2;\n    x = (x & 61680) >> 4 | (x & 3855) << 4;\n    rev[i] = ((x & 65280) >> 8 | (x & 255) << 8) >> 1;\n  }\n  var hMap = (function(cd, mb, r) {\n    var s = cd.length;\n    var i2 = 0;\n    var l = new u16(mb);\n    for (; i2 < s; ++i2) {\n      if (cd[i2])\n        ++l[cd[i2] - 1];\n    }\n    var le = new u16(mb);\n    for (i2 = 1; i2 < mb; ++i2) {\n      le[i2] = le[i2 - 1] + l[i2 - 1] << 1;\n    }\n    var co;\n    if (r) {\n      co = new u16(1 << mb);\n      var rvb = 15 - mb;\n      for (i2 = 0; i2 < s; ++i2) {\n        if (cd[i2]) {\n          var sv = i2 << 4 | cd[i2];\n          var r_1 = mb - cd[i2];\n          var v = le[cd[i2] - 1]++ << r_1;\n          for (var m = v | (1 << r_1) - 1; v <= m; ++v) {\n            co[rev[v] >> rvb] = sv;\n          }\n        }\n      }\n    } else {\n      co = new u16(s);\n      for (i2 = 0; i2 < s; ++i2) {\n        if (cd[i2]) {\n          co[i2] = rev[le[cd[i2] - 1]++] >> 15 - cd[i2];\n        }\n      }\n    }\n    return co;\n  });\n  var flt = new u8(288);\n  for (var i = 0; i < 144; ++i)\n    flt[i] = 8;\n  for (var i = 144; i < 256; ++i)\n    flt[i] = 9;\n  for (var i = 256; i < 280; ++i)\n    flt[i] = 7;\n  for (var i = 280; i < 288; ++i)\n    flt[i] = 8;\n  var fdt = new u8(32);\n  for (var i = 0; i < 32; ++i)\n    fdt[i] = 5;\n  var flm = /* @__PURE__ */ hMap(flt, 9, 0);\n  var fdm = /* @__PURE__ */ hMap(fdt, 5, 0);\n  var shft = function(p) {\n    return (p + 7) / 8 | 0;\n  };\n  var slc = function(v, s, e) {\n    if (e == null || e > v.length)\n      e = v.length;\n    return new u8(v.subarray(s, e));\n  };\n  var ec = [\n    "unexpected EOF",\n    "invalid block type",\n    "invalid length/literal",\n    "invalid distance",\n    "stream finished",\n    "no stream handler",\n    ,\n    "no callback",\n    "invalid UTF-8 data",\n    "extra field too long",\n    "date not in range 1980-2099",\n    "filename too long",\n    "stream finishing",\n    "invalid zip data"\n    // determined by unknown compression method\n  ];\n  var err = function(ind, msg, nt) {\n    var e = new Error(msg || ec[ind]);\n    e.code = ind;\n    if (Error.captureStackTrace)\n      Error.captureStackTrace(e, err);\n    if (!nt)\n      throw e;\n    return e;\n  };\n  var wbits = function(d, p, v) {\n    v <<= p & 7;\n    var o = p / 8 | 0;\n    d[o] |= v;\n    d[o + 1] |= v >> 8;\n  };\n  var wbits16 = function(d, p, v) {\n    v <<= p & 7;\n    var o = p / 8 | 0;\n    d[o] |= v;\n    d[o + 1] |= v >> 8;\n    d[o + 2] |= v >> 16;\n  };\n  var hTree = function(d, mb) {\n    var t = [];\n    for (var i2 = 0; i2 < d.length; ++i2) {\n      if (d[i2])\n        t.push({ s: i2, f: d[i2] });\n    }\n    var s = t.length;\n    var t2 = t.slice();\n    if (!s)\n      return { t: et, l: 0 };\n    if (s == 1) {\n      var v = new u8(t[0].s + 1);\n      v[t[0].s] = 1;\n      return { t: v, l: 1 };\n    }\n    t.sort(function(a, b) {\n      return a.f - b.f;\n    });\n    t.push({ s: -1, f: 25001 });\n    var l = t[0], r = t[1], i0 = 0, i1 = 1, i22 = 2;\n    t[0] = { s: -1, f: l.f + r.f, l, r };\n    while (i1 != s - 1) {\n      l = t[t[i0].f < t[i22].f ? i0++ : i22++];\n      r = t[i0 != i1 && t[i0].f < t[i22].f ? i0++ : i22++];\n      t[i1++] = { s: -1, f: l.f + r.f, l, r };\n    }\n    var maxSym = t2[0].s;\n    for (var i2 = 1; i2 < s; ++i2) {\n      if (t2[i2].s > maxSym)\n        maxSym = t2[i2].s;\n    }\n    var tr = new u16(maxSym + 1);\n    var mbt = ln(t[i1 - 1], tr, 0);\n    if (mbt > mb) {\n      var i2 = 0, dt = 0;\n      var lft = mbt - mb, cst = 1 << lft;\n      t2.sort(function(a, b) {\n        return tr[b.s] - tr[a.s] || a.f - b.f;\n      });\n      for (; i2 < s; ++i2) {\n        var i2_1 = t2[i2].s;\n        if (tr[i2_1] > mb) {\n          dt += cst - (1 << mbt - tr[i2_1]);\n          tr[i2_1] = mb;\n        } else\n          break;\n      }\n      dt >>= lft;\n      while (dt > 0) {\n        var i2_2 = t2[i2].s;\n        if (tr[i2_2] < mb)\n          dt -= 1 << mb - tr[i2_2]++ - 1;\n        else\n          ++i2;\n      }\n      for (; i2 >= 0 && dt; --i2) {\n        var i2_3 = t2[i2].s;\n        if (tr[i2_3] == mb) {\n          --tr[i2_3];\n          ++dt;\n        }\n      }\n      mbt = mb;\n    }\n    return { t: new u8(tr), l: mbt };\n  };\n  var ln = function(n, l, d) {\n    return n.s == -1 ? Math.max(ln(n.l, l, d + 1), ln(n.r, l, d + 1)) : l[n.s] = d;\n  };\n  var lc = function(c) {\n    var s = c.length;\n    while (s && !c[--s])\n      ;\n    var cl = new u16(++s);\n    var cli = 0, cln = c[0], cls = 1;\n    var w = function(v) {\n      cl[cli++] = v;\n    };\n    for (var i2 = 1; i2 <= s; ++i2) {\n      if (c[i2] == cln && i2 != s)\n        ++cls;\n      else {\n        if (!cln && cls > 2) {\n          for (; cls > 138; cls -= 138)\n            w(32754);\n          if (cls > 2) {\n            w(cls > 10 ? cls - 11 << 5 | 28690 : cls - 3 << 5 | 12305);\n            cls = 0;\n          }\n        } else if (cls > 3) {\n          w(cln), --cls;\n          for (; cls > 6; cls -= 6)\n            w(8304);\n          if (cls > 2)\n            w(cls - 3 << 5 | 8208), cls = 0;\n        }\n        while (cls--)\n          w(cln);\n        cls = 1;\n        cln = c[i2];\n      }\n    }\n    return { c: cl.subarray(0, cli), n: s };\n  };\n  var clen = function(cf, cl) {\n    var l = 0;\n    for (var i2 = 0; i2 < cl.length; ++i2)\n      l += cf[i2] * cl[i2];\n    return l;\n  };\n  var wfblk = function(out, pos, dat) {\n    var s = dat.length;\n    var o = shft(pos + 2);\n    out[o] = s & 255;\n    out[o + 1] = s >> 8;\n    out[o + 2] = out[o] ^ 255;\n    out[o + 3] = out[o + 1] ^ 255;\n    for (var i2 = 0; i2 < s; ++i2)\n      out[o + i2 + 4] = dat[i2];\n    return (o + 4 + s) * 8;\n  };\n  var wblk = function(dat, out, final, syms, lf, df, eb, li, bs, bl, p) {\n    wbits(out, p++, final);\n    ++lf[256];\n    var _a2 = hTree(lf, 15), dlt = _a2.t, mlb = _a2.l;\n    var _b2 = hTree(df, 15), ddt = _b2.t, mdb = _b2.l;\n    var _c = lc(dlt), lclt = _c.c, nlc = _c.n;\n    var _d = lc(ddt), lcdt = _d.c, ndc = _d.n;\n    var lcfreq = new u16(19);\n    for (var i2 = 0; i2 < lclt.length; ++i2)\n      ++lcfreq[lclt[i2] & 31];\n    for (var i2 = 0; i2 < lcdt.length; ++i2)\n      ++lcfreq[lcdt[i2] & 31];\n    var _e = hTree(lcfreq, 7), lct = _e.t, mlcb = _e.l;\n    var nlcc = 19;\n    for (; nlcc > 4 && !lct[clim[nlcc - 1]]; --nlcc)\n      ;\n    var flen = bl + 5 << 3;\n    var ftlen = clen(lf, flt) + clen(df, fdt) + eb;\n    var dtlen = clen(lf, dlt) + clen(df, ddt) + eb + 14 + 3 * nlcc + clen(lcfreq, lct) + 2 * lcfreq[16] + 3 * lcfreq[17] + 7 * lcfreq[18];\n    if (bs >= 0 && flen <= ftlen && flen <= dtlen)\n      return wfblk(out, p, dat.subarray(bs, bs + bl));\n    var lm, ll, dm, dl;\n    wbits(out, p, 1 + (dtlen < ftlen)), p += 2;\n    if (dtlen < ftlen) {\n      lm = hMap(dlt, mlb, 0), ll = dlt, dm = hMap(ddt, mdb, 0), dl = ddt;\n      var llm = hMap(lct, mlcb, 0);\n      wbits(out, p, nlc - 257);\n      wbits(out, p + 5, ndc - 1);\n      wbits(out, p + 10, nlcc - 4);\n      p += 14;\n      for (var i2 = 0; i2 < nlcc; ++i2)\n        wbits(out, p + 3 * i2, lct[clim[i2]]);\n      p += 3 * nlcc;\n      var lcts = [lclt, lcdt];\n      for (var it = 0; it < 2; ++it) {\n        var clct = lcts[it];\n        for (var i2 = 0; i2 < clct.length; ++i2) {\n          var len = clct[i2] & 31;\n          wbits(out, p, llm[len]), p += lct[len];\n          if (len > 15)\n            wbits(out, p, clct[i2] >> 5 & 127), p += clct[i2] >> 12;\n        }\n      }\n    } else {\n      lm = flm, ll = flt, dm = fdm, dl = fdt;\n    }\n    for (var i2 = 0; i2 < li; ++i2) {\n      var sym = syms[i2];\n      if (sym > 255) {\n        var len = sym >> 18 & 31;\n        wbits16(out, p, lm[len + 257]), p += ll[len + 257];\n        if (len > 7)\n          wbits(out, p, sym >> 23 & 31), p += fleb[len];\n        var dst = sym & 31;\n        wbits16(out, p, dm[dst]), p += dl[dst];\n        if (dst > 3)\n          wbits16(out, p, sym >> 5 & 8191), p += fdeb[dst];\n      } else {\n        wbits16(out, p, lm[sym]), p += ll[sym];\n      }\n    }\n    wbits16(out, p, lm[256]);\n    return p + ll[256];\n  };\n  var deo = /* @__PURE__ */ new i32([65540, 131080, 131088, 131104, 262176, 1048704, 1048832, 2114560, 2117632]);\n  var et = /* @__PURE__ */ new u8(0);\n  var dflt = function(dat, lvl, plvl, pre, post2, st) {\n    var s = st.z || dat.length;\n    var o = new u8(pre + s + 5 * (1 + Math.ceil(s / 7e3)) + post2);\n    var w = o.subarray(pre, o.length - post2);\n    var lst = st.l;\n    var pos = (st.r || 0) & 7;\n    if (lvl) {\n      if (pos)\n        w[0] = st.r >> 3;\n      var opt = deo[lvl - 1];\n      var n = opt >> 13, c = opt & 8191;\n      var msk_1 = (1 << plvl) - 1;\n      var prev = st.p || new u16(32768), head = st.h || new u16(msk_1 + 1);\n      var bs1_1 = Math.ceil(plvl / 3), bs2_1 = 2 * bs1_1;\n      var hsh = function(i3) {\n        return (dat[i3] ^ dat[i3 + 1] << bs1_1 ^ dat[i3 + 2] << bs2_1) & msk_1;\n      };\n      var syms = new i32(25e3);\n      var lf = new u16(288), df = new u16(32);\n      var lc_1 = 0, eb = 0, i2 = st.i || 0, li = 0, wi = st.w || 0, bs = 0;\n      for (; i2 + 2 < s; ++i2) {\n        var hv = hsh(i2);\n        var imod = i2 & 32767, pimod = head[hv];\n        prev[imod] = pimod;\n        head[hv] = imod;\n        if (wi <= i2) {\n          var rem = s - i2;\n          if ((lc_1 > 7e3 || li > 24576) && (rem > 423 || !lst)) {\n            pos = wblk(dat, w, 0, syms, lf, df, eb, li, bs, i2 - bs, pos);\n            li = lc_1 = eb = 0, bs = i2;\n            for (var j = 0; j < 286; ++j)\n              lf[j] = 0;\n            for (var j = 0; j < 30; ++j)\n              df[j] = 0;\n          }\n          var l = 2, d = 0, ch_1 = c, dif = imod - pimod & 32767;\n          if (rem > 2 && hv == hsh(i2 - dif)) {\n            var maxn = Math.min(n, rem) - 1;\n            var maxd = Math.min(32767, i2);\n            var ml = Math.min(258, rem);\n            while (dif <= maxd && --ch_1 && imod != pimod) {\n              if (dat[i2 + l] == dat[i2 + l - dif]) {\n                var nl = 0;\n                for (; nl < ml && dat[i2 + nl] == dat[i2 + nl - dif]; ++nl)\n                  ;\n                if (nl > l) {\n                  l = nl, d = dif;\n                  if (nl > maxn)\n                    break;\n                  var mmd = Math.min(dif, nl - 2);\n                  var md = 0;\n                  for (var j = 0; j < mmd; ++j) {\n                    var ti = i2 - dif + j & 32767;\n                    var pti = prev[ti];\n                    var cd = ti - pti & 32767;\n                    if (cd > md)\n                      md = cd, pimod = ti;\n                  }\n                }\n              }\n              imod = pimod, pimod = prev[imod];\n              dif += imod - pimod & 32767;\n            }\n          }\n          if (d) {\n            syms[li++] = 268435456 | revfl[l] << 18 | revfd[d];\n            var lin = revfl[l] & 31, din = revfd[d] & 31;\n            eb += fleb[lin] + fdeb[din];\n            ++lf[257 + lin];\n            ++df[din];\n            wi = i2 + l;\n            ++lc_1;\n          } else {\n            syms[li++] = dat[i2];\n            ++lf[dat[i2]];\n          }\n        }\n      }\n      for (i2 = Math.max(i2, wi); i2 < s; ++i2) {\n        syms[li++] = dat[i2];\n        ++lf[dat[i2]];\n      }\n      pos = wblk(dat, w, lst, syms, lf, df, eb, li, bs, i2 - bs, pos);\n      if (!lst) {\n        st.r = pos & 7 | w[pos / 8 | 0] << 3;\n        pos -= 7;\n        st.h = head, st.p = prev, st.i = i2, st.w = wi;\n      }\n    } else {\n      for (var i2 = st.w || 0; i2 < s + lst; i2 += 65535) {\n        var e = i2 + 65535;\n        if (e >= s) {\n          w[pos / 8 | 0] = lst;\n          e = s;\n        }\n        pos = wfblk(w, pos + 1, dat.subarray(i2, e));\n      }\n      st.i = s;\n    }\n    return slc(o, 0, pre + shft(pos) + post2);\n  };\n  var crct = /* @__PURE__ */ (function() {\n    var t = new Int32Array(256);\n    for (var i2 = 0; i2 < 256; ++i2) {\n      var c = i2, k = 9;\n      while (--k)\n        c = (c & 1 && -306674912) ^ c >>> 1;\n      t[i2] = c;\n    }\n    return t;\n  })();\n  var crc = function() {\n    var c = -1;\n    return {\n      p: function(d) {\n        var cr = c;\n        for (var i2 = 0; i2 < d.length; ++i2)\n          cr = crct[cr & 255 ^ d[i2]] ^ cr >>> 8;\n        c = cr;\n      },\n      d: function() {\n        return ~c;\n      }\n    };\n  };\n  var dopt = function(dat, opt, pre, post2, st) {\n    if (!st) {\n      st = { l: 1 };\n      if (opt.dictionary) {\n        var dict = opt.dictionary.subarray(-32768);\n        var newDat = new u8(dict.length + dat.length);\n        newDat.set(dict);\n        newDat.set(dat, dict.length);\n        dat = newDat;\n        st.w = dict.length;\n      }\n    }\n    return dflt(dat, opt.level == null ? 6 : opt.level, opt.mem == null ? st.l ? Math.ceil(Math.max(8, Math.min(13, Math.log(dat.length))) * 1.5) : 20 : 12 + opt.mem, pre, post2, st);\n  };\n  var mrg = function(a, b) {\n    var o = {};\n    for (var k in a)\n      o[k] = a[k];\n    for (var k in b)\n      o[k] = b[k];\n    return o;\n  };\n  var wbytes = function(d, b, v) {\n    for (; v; ++b)\n      d[b] = v, v >>>= 8;\n  };\n  function deflateSync(data, opts) {\n    return dopt(data, opts || {}, 0, 0);\n  }\n  var fltn = function(d, p, t, o) {\n    for (var k in d) {\n      var val = d[k], n = p + k, op = o;\n      if (Array.isArray(val))\n        op = mrg(o, val[1]), val = val[0];\n      if (val instanceof u8)\n        t[n] = [val, op];\n      else {\n        t[n += "/"] = [new u8(0), op];\n        fltn(val, n, t, o);\n      }\n    }\n  };\n  var te = typeof TextEncoder != "undefined" && /* @__PURE__ */ new TextEncoder();\n  var td = typeof TextDecoder != "undefined" && /* @__PURE__ */ new TextDecoder();\n  var tds = 0;\n  try {\n    td.decode(et, { stream: true });\n    tds = 1;\n  } catch (e) {\n  }\n  function strToU8(str, latin1) {\n    var i2;\n    if (te)\n      return te.encode(str);\n    var l = str.length;\n    var ar = new u8(str.length + (str.length >> 1));\n    var ai = 0;\n    var w = function(v) {\n      ar[ai++] = v;\n    };\n    for (var i2 = 0; i2 < l; ++i2) {\n      if (ai + 5 > ar.length) {\n        var n = new u8(ai + 8 + (l - i2 << 1));\n        n.set(ar);\n        ar = n;\n      }\n      var c = str.charCodeAt(i2);\n      if (c < 128 || latin1)\n        w(c);\n      else if (c < 2048)\n        w(192 | c >> 6), w(128 | c & 63);\n      else if (c > 55295 && c < 57344)\n        c = 65536 + (c & 1023 << 10) | str.charCodeAt(++i2) & 1023, w(240 | c >> 18), w(128 | c >> 12 & 63), w(128 | c >> 6 & 63), w(128 | c & 63);\n      else\n        w(224 | c >> 12), w(128 | c >> 6 & 63), w(128 | c & 63);\n    }\n    return slc(ar, 0, ai);\n  }\n  var exfl = function(ex) {\n    var le = 0;\n    if (ex) {\n      for (var k in ex) {\n        var l = ex[k].length;\n        if (l > 65535)\n          err(9);\n        le += l + 4;\n      }\n    }\n    return le;\n  };\n  var wzh = function(d, b, f, fn, u, c, ce, co) {\n    var fl2 = fn.length, ex = f.extra, col = co && co.length;\n    var exl = exfl(ex);\n    wbytes(d, b, ce != null ? 33639248 : 67324752), b += 4;\n    if (ce != null)\n      d[b++] = 20, d[b++] = f.os;\n    d[b] = 20, b += 2;\n    d[b++] = f.flag << 1 | (c < 0 && 8), d[b++] = u && 8;\n    d[b++] = f.compression & 255, d[b++] = f.compression >> 8;\n    var dt = new Date(f.mtime == null ? Date.now() : f.mtime), y = dt.getFullYear() - 1980;\n    if (y < 0 || y > 119)\n      err(10);\n    wbytes(d, b, y << 25 | dt.getMonth() + 1 << 21 | dt.getDate() << 16 | dt.getHours() << 11 | dt.getMinutes() << 5 | dt.getSeconds() >> 1), b += 4;\n    if (c != -1) {\n      wbytes(d, b, f.crc);\n      wbytes(d, b + 4, c < 0 ? -c - 2 : c);\n      wbytes(d, b + 8, f.size);\n    }\n    wbytes(d, b + 12, fl2);\n    wbytes(d, b + 14, exl), b += 16;\n    if (ce != null) {\n      wbytes(d, b, col);\n      wbytes(d, b + 6, f.attrs);\n      wbytes(d, b + 10, ce), b += 14;\n    }\n    d.set(fn, b);\n    b += fl2;\n    if (exl) {\n      for (var k in ex) {\n        var exf = ex[k], l = exf.length;\n        wbytes(d, b, +k);\n        wbytes(d, b + 2, l);\n        d.set(exf, b + 4), b += 4 + l;\n      }\n    }\n    if (col)\n      d.set(co, b), b += col;\n    return b;\n  };\n  var wzf = function(o, b, c, d, e) {\n    wbytes(o, b, 101010256);\n    wbytes(o, b + 8, c);\n    wbytes(o, b + 10, c);\n    wbytes(o, b + 12, d);\n    wbytes(o, b + 16, e);\n  };\n  function zipSync(data, opts) {\n    if (!opts)\n      opts = {};\n    var r = {};\n    var files = [];\n    fltn(data, "", r, opts);\n    var o = 0;\n    var tot = 0;\n    for (var fn in r) {\n      var _a2 = r[fn], file = _a2[0], p = _a2[1];\n      var compression = p.level == 0 ? 0 : 8;\n      var f = strToU8(fn), s = f.length;\n      var com = p.comment, m = com && strToU8(com), ms = m && m.length;\n      var exl = exfl(p.extra);\n      if (s > 65535)\n        err(11);\n      var d = compression ? deflateSync(file, p) : file, l = d.length;\n      var c = crc();\n      c.p(file);\n      files.push(mrg(p, {\n        size: file.length,\n        crc: c.d(),\n        c: d,\n        f,\n        m,\n        u: s != fn.length || m && com.length != ms,\n        o,\n        compression\n      }));\n      o += 30 + s + exl + l;\n      tot += 76 + 2 * (s + exl) + (ms || 0) + l;\n    }\n    var out = new u8(tot + 22), oe = o, cdl = tot - o;\n    for (var i2 = 0; i2 < files.length; ++i2) {\n      var f = files[i2];\n      wzh(out, f.o, f, f.f, f.u, f.c.length);\n      var badd = 30 + f.f.length + exfl(f.extra);\n      out.set(f.c, f.o + badd);\n      wzh(out, o, f, f.f, f.u, f.c.length, f.o, f.m), o += 16 + badd + (f.m ? f.m.length : 0);\n    }\n    wzf(out, o, files.length, cdl, oe);\n    return out;\n  }\n  function validateBundleZipPath(path) {\n    const normalized = path.replace(/\\\\/g, "/").replace(/^\\/+/, "");\n    if (!normalized || normalized.includes("..") || normalized.startsWith("/")) {\n      throw new Error(`Unsafe bundle ZIP path: ${path}`);\n    }\n    return normalized;\n  }\n  function createBundleZip(entries) {\n    const payload = {};\n    for (const entry of entries) {\n      const path = validateBundleZipPath(entry.path);\n      const options = { level: entry.level ?? 6 };\n      payload[path] = [typeof entry.data === "string" ? strToU8(entry.data) : entry.data, options];\n    }\n    return zipSync(payload);\n  }\n  function normalizeFilename(value) {\n    return String(value || "bundle").trim().replace(/[^a-zA-Z0-9._-]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").slice(0, 80) || "bundle";\n  }\n  function safeHttpUrl(value) {\n    if (typeof value !== "string") return void 0;\n    try {\n      const parsed = new URL(value);\n      return parsed.protocol === "http:" || parsed.protocol === "https:" ? parsed.href : void 0;\n    } catch {\n      return void 0;\n    }\n  }\n  function inferKind(original, record) {\n    const source = original && typeof original === "object" ? original : {};\n    const typename = String(source.__typename || record.__typename || "").toLowerCase();\n    if (typename.includes("tweet") || record.full_text || record.media) return "tweet";\n    if (typename.includes("user") || record.screen_name || record.profile_image_url) return "user";\n    return "unknown";\n  }\n  function extractObservedAt(original, record) {\n    const source = original && typeof original === "object" ? original : {};\n    const privateFields = source.twe_private_fields;\n    const candidates = [\n      privateFields == null ? void 0 : privateFields.created_at,\n      privateFields == null ? void 0 : privateFields.updated_at,\n      record.created_at,\n      record.time,\n      record.date\n    ];\n    for (const candidate of candidates) {\n      const numeric = Number(candidate);\n      if (Number.isFinite(numeric) && numeric > 0) {\n        return numeric;\n      }\n      if (typeof candidate === "string") {\n        const parsed = Date.parse(candidate);\n        if (Number.isFinite(parsed)) {\n          return parsed;\n        }\n      }\n    }\n    return void 0;\n  }\n  async function buildRecordEnvelope(bundleId, row, options) {\n    const kind = inferKind(row.original, row.record);\n    const sourceId = String(row.id || row.record.id || row.record.rest_id || "");\n    const id = await createBundleRecordId(bundleId, kind, sourceId || JSON.stringify(row.record));\n    const data = options.includeOriginalMetadata ? {\n      ...row.record,\n      metadata: row.original\n    } : row.record;\n    return {\n      id,\n      kind,\n      sourceId: sourceId || void 0,\n      observedAt: extractObservedAt(row.original, row.record),\n      sensitivity: "low",\n      data,\n      mediaRefs: Array.isArray(row.record.media) ? row.record.media.map((media, index) => ({\n        id: `${id}:media:${index}`,\n        type: media.type === "photo" || media.type === "video" || media.type === "animated_gif" ? media.type : "unknown",\n        url: safeHttpUrl(media.original),\n        previewUrl: safeHttpUrl(media.thumbnail),\n        altText: typeof media.ext_alt_text === "string" ? media.ext_alt_text : void 0\n      })) : void 0\n    };\n  }\n  function countBundleRecords(records) {\n    return records.reduce(\n      (acc, record) => {\n        acc.records += 1;\n        if (record.kind === "tweet") acc.tweets += 1;\n        if (record.kind === "user") acc.users += 1;\n        if (record.kind === "social_edge") acc.socialEdges += 1;\n        if (record.kind === "capture") acc.captures += 1;\n        acc.mediaBlobs += 0;\n        return acc;\n      },\n      { records: 0, tweets: 0, users: 0, socialEdges: 0, captures: 0, mediaBlobs: 0 }\n    );\n  }\n  async function createCanonicalBundleZip(rows, options) {\n    const now = Date.now();\n    const startedAt = typeof performance !== "undefined" ? performance.now() : Date.now();\n    const reportProgress = (phase, processedRecords) => {\n      var _a2;\n      (_a2 = options.onProgress) == null ? void 0 : _a2.call(options, {\n        phase,\n        processedRecords,\n        totalRecords: rows.length,\n        elapsedMs: (typeof performance !== "undefined" ? performance.now() : Date.now()) - startedAt\n      });\n    };\n    const bundleId = await createBundleId(\n      `${options.title}:${options.scope}:${options.queryText || ""}:${now}:${rows.length}`\n    );\n    const records = [];\n    for (let index = 0; index < rows.length; index += 1) {\n      const row = rows[index];\n      if (!row) continue;\n      records.push(await buildRecordEnvelope(bundleId, row, options));\n      if (index === 0 || index + 1 === rows.length || (index + 1) % 100 === 0) {\n        reportProgress("envelope", index + 1);\n      }\n    }\n    const recordsJsonl = records.map((record) => JSON.stringify(record)).join("\\n") + "\\n";\n    const privacy = buildBundlePrivacySummary(SAFE_SHARED_DEFAULT_PRIVACY);\n    const files = [\n      {\n        path: "manifest.json",\n        contentType: "application/json",\n        role: "manifest"\n      },\n      {\n        path: "records/records.jsonl",\n        contentType: "application/x-ndjson",\n        role: "records",\n        bytes: new TextEncoder().encode(recordsJsonl).byteLength,\n        sha256: await sha256Hex(recordsJsonl)\n      }\n    ];\n    const mediaUrlLines = records.flatMap((record) => record.mediaRefs || []).map((media) => media.url).filter((url) => !!url);\n    const mediaUrlsText = mediaUrlLines.join("\\n") + (mediaUrlLines.length ? "\\n" : "");\n    if (mediaUrlLines.length) {\n      files.push({\n        path: "media/media-urls.txt",\n        contentType: "text/plain",\n        role: "media",\n        bytes: new TextEncoder().encode(mediaUrlsText).byteLength,\n        sha256: await sha256Hex(mediaUrlsText)\n      });\n    }\n    const manifest = {\n      id: bundleId,\n      title: options.title,\n      description: options.description,\n      producer: {\n        app: "twitter-web-exporter",\n        appVersion: packageJson.version,\n        schemaVersion: 1,\n        exportedAt: now\n      },\n      privacy,\n      counts: countBundleRecords(records),\n      files\n    };\n    const manifestFile = files[0];\n    if (manifestFile) {\n      files[0] = {\n        ...manifestFile,\n        bytes: new TextEncoder().encode(JSON.stringify(manifest, void 0, 2)).byteLength\n      };\n    }\n    reportProgress("manifest", records.length);\n    const compressionLevel = options.compressionLevel ?? 1;\n    const entries = [\n      {\n        path: "manifest.json",\n        data: JSON.stringify(manifest, void 0, 2),\n        level: compressionLevel\n      },\n      {\n        path: "records/records.jsonl",\n        data: recordsJsonl,\n        level: compressionLevel\n      }\n    ];\n    if (mediaUrlLines.length) {\n      entries.push({ path: "media/media-urls.txt", data: mediaUrlsText, level: compressionLevel });\n    }\n    reportProgress("zip", records.length);\n    const result = {\n      filename: `twe-bundle-${normalizeFilename(options.title)}-${now}.zip`,\n      bytes: createBundleZip(entries),\n      manifest\n    };\n    reportProgress("done", records.length);\n    return result;\n  }\n  const cancelledJobs = /* @__PURE__ */ new Set();\n  function nowMs() {\n    if (typeof performance !== "undefined" && typeof performance.now === "function") {\n      return performance.now();\n    }\n    return Date.now();\n  }\n  function post(message, transfer) {\n    self.postMessage(message, { transfer: transfer || [] });\n  }\n  function errorMessage(error) {\n    return error instanceof Error ? error.message : String(error);\n  }\n  self.onmessage = (event) => {\n    const request = event.data;\n    if (!request || typeof request !== "object") return;\n    if (request.type === "bundle-export:cancel") {\n      cancelledJobs.add(request.jobId);\n      return;\n    }\n    if (request.type !== "bundle-export:start") return;\n    const startedAt = nowMs();\n    void (async () => {\n      try {\n        if (cancelledJobs.has(request.jobId)) {\n          cancelledJobs.delete(request.jobId);\n          return;\n        }\n        const result = await createCanonicalBundleZip(request.rows, {\n          ...request.options,\n          onProgress: (progress) => {\n            if (cancelledJobs.has(request.jobId)) {\n              throw new Error("Bundle export cancelled.");\n            }\n            post({ type: "bundle-export:progress", jobId: request.jobId, progress });\n          }\n        });\n        if (cancelledJobs.has(request.jobId)) {\n          cancelledJobs.delete(request.jobId);\n          return;\n        }\n        const buffer = result.bytes.buffer.slice(\n          result.bytes.byteOffset,\n          result.bytes.byteOffset + result.bytes.byteLength\n        );\n        post(\n          {\n            type: "bundle-export:done",\n            jobId: request.jobId,\n            filename: result.filename,\n            buffer,\n            manifest: result.manifest,\n            elapsedMs: nowMs() - startedAt\n          },\n          [buffer]\n        );\n      } catch (error) {\n        post({\n          type: "bundle-export:error",\n          jobId: request.jobId,\n          error: errorMessage(error),\n          elapsedMs: nowMs() - startedAt\n        });\n      } finally {\n        cancelledJobs.delete(request.jobId);\n      }\n    })();\n  };\n})();\n';
  const blob$1 = typeof self !== "undefined" && self.Blob && new Blob([jsContent$1], { type: "text/javascript;charset=utf-8" });
  function WorkerWrapper$1(options2) {
    let objURL;
    try {
      objURL = blob$1 && (self.URL || self.webkitURL).createObjectURL(blob$1);
      if (!objURL) throw "";
      const worker = new Worker(objURL, {
        name: options2 == null ? void 0 : options2.name
      });
      worker.addEventListener("error", () => {
        (self.URL || self.webkitURL).revokeObjectURL(objURL);
      });
      return worker;
    } catch (e) {
      return new Worker(
        "data:text/javascript;charset=utf-8," + encodeURIComponent(jsContent$1),
        {
          name: options2 == null ? void 0 : options2.name
        }
      );
    } finally {
      objURL && (self.URL || self.webkitURL).revokeObjectURL(objURL);
    }
  }
  function createJobId() {
    if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
      return `bundle-${crypto.randomUUID()}`;
    }
    return `bundle-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
  }
  function exportCanonicalBundleZipWithWorker({
    rows,
    options: options2,
    onProgress
  }) {
    const jobId = createJobId();
    const startedAt = nowMs();
    let worker = null;
    let settled = false;
    let rejectJob = null;
    const promise = new Promise((resolve, reject) => {
      rejectJob = reject;
      try {
        worker = new WorkerWrapper$1();
        setWorkerAvailability("export", true);
      } catch (error) {
        setWorkerAvailability("export", false);
        reject(error instanceof Error ? error : new Error(String(error)));
        return;
      }
      worker.onmessage = (event) => {
        const message = event.data;
        if (!message || message.jobId !== jobId) return;
        if (message.type === "bundle-export:progress") {
          onProgress == null ? void 0 : onProgress(message.progress);
          return;
        }
        settled = true;
        worker == null ? void 0 : worker.terminate();
        worker = null;
        if (message.type === "bundle-export:error") {
          recordPerfMetric({
            kind: "export",
            name: "bundle-worker-error",
            durationMs: message.elapsedMs,
            tags: { error: message.error }
          });
          reject(new Error(message.error));
          return;
        }
        const blob2 = new Blob([message.buffer], { type: "application/zip" });
        saveFile(message.filename, blob2);
        recordPerfMetric({
          kind: "export",
          name: "bundle-worker-complete",
          durationMs: nowMs() - startedAt,
          value: message.buffer.byteLength,
          tags: {
            records: message.manifest.counts.records,
            compressionLevel: options2.compressionLevel ?? 1
          }
        });
        resolve(message.filename);
      };
      worker.onerror = (event) => {
        if (settled) return;
        settled = true;
        setWorkerAvailability("export", false);
        worker == null ? void 0 : worker.terminate();
        worker = null;
        reject(new Error(event.message || "Bundle export worker failed."));
      };
      worker.postMessage({
        type: "bundle-export:start",
        jobId,
        rows,
        options: {
          ...options2,
          compressionLevel: options2.compressionLevel ?? 1
        }
      });
    });
    return {
      jobId,
      promise,
      cancel: () => {
        if (settled) return;
        settled = true;
        worker == null ? void 0 : worker.postMessage({
          type: "bundle-export:cancel",
          jobId
        });
        worker == null ? void 0 : worker.terminate();
        worker = null;
        rejectJob == null ? void 0 : rejectJob(new Error("Bundle export cancelled."));
        recordPerfMetric({
          kind: "export",
          name: "bundle-worker-cancel",
          durationMs: nowMs() - startedAt
        });
      }
    };
  }
  function cloneSnapshotValue$1(value) {
    if (value === null || value === void 0 || typeof value !== "object") {
      return value;
    }
    if (typeof structuredClone === "function") {
      try {
        return structuredClone(value);
      } catch {
      }
    }
    try {
      return JSON.parse(JSON.stringify(value));
    } catch {
      return value;
    }
  }
  function getAccessorPathValue$1(record, path) {
    if (!record || typeof record !== "object") return void 0;
    const parts = path.split(".");
    let current = record;
    for (const part of parts) {
      if (!current || typeof current !== "object") return void 0;
      current = current[part];
    }
    return current;
  }
  function flattenLeafColumns$1(columns2) {
    const out = [];
    for (const column of columns2) {
      if ("columns" in column && Array.isArray(column.columns)) {
        out.push(...flattenLeafColumns$1(column.columns));
        continue;
      }
      out.push(column);
    }
    return out;
  }
  function resolveColumnId$1(column) {
    if ("id" in column && typeof column.id === "string" && column.id) {
      return column.id;
    }
    if ("accessorKey" in column && typeof column.accessorKey === "string") {
      return column.accessorKey;
    }
    return "";
  }
  function resolveColumnValue$1(column, record, rowIndex) {
    if ("accessorFn" in column && typeof column.accessorFn === "function") {
      return column.accessorFn(record, rowIndex);
    }
    if ("accessorKey" in column) {
      if (typeof column.accessorKey === "string") {
        return getAccessorPathValue$1(record, column.accessorKey);
      }
      if (typeof column.accessorKey === "number" && Array.isArray(record)) {
        return record[column.accessorKey];
      }
    }
    return void 0;
  }
  function snapshotExportRow(recordSource, columns2, rowIndex) {
    const record = {};
    const leafColumns = flattenLeafColumns$1(columns2);
    for (const column of leafColumns) {
      const meta = column.meta;
      if ((meta == null ? void 0 : meta.exportable) === false) {
        continue;
      }
      const baseValue = resolveColumnValue$1(column, recordSource, rowIndex);
      const exportRowLike = {
        original: recordSource
      };
      let exportValue = (meta == null ? void 0 : meta.exportValue) ? meta.exportValue(exportRowLike) : baseValue;
      if (exportValue === void 0) {
        exportValue = null;
      }
      record[(meta == null ? void 0 : meta.exportKey) || resolveColumnId$1(column)] = cloneSnapshotValue$1(exportValue);
    }
    const originalRecord = cloneSnapshotValue$1(recordSource);
    if (originalRecord && (originalRecord.__bookmark_folder_id || originalRecord.__bookmark_folder_name || originalRecord.__bookmark_folder_url)) {
      const trustedFolderName = originalRecord.__bookmark_folder_name_source === "api" ? originalRecord.__bookmark_folder_name ?? null : null;
      record.bookmark_folder_id = originalRecord.__bookmark_folder_id ?? null;
      record.bookmark_folder_name = trustedFolderName;
      record.bookmark_folder_url = originalRecord.__bookmark_folder_url ?? null;
    }
    return {
      id: String((originalRecord == null ? void 0 : originalRecord.rest_id) || rowIndex),
      original: cloneSnapshotValue$1(recordSource),
      record
    };
  }
  function ExportDataModal({
    title,
    columns: columns2,
    resultRecords,
    selectedRecords,
    resultSetSnapshot,
    selectionMode,
    preparingFullDataset = false,
    show,
    onClose
  }) {
    const { t } = useTranslation("exporter");
    const [selectedFormat, setSelectedFormat] = useSignalState(EXPORT_FORMAT.JSON);
    const [loading, setLoading] = useSignalState(false);
    const [bundleLoading, setBundleLoading] = useSignalState(false);
    const [includeMetadata, toggleIncludeMetadata] = useToggle(false);
    const [currentProgress, setCurrentProgress] = useSignalState(0);
    const [totalProgress, setTotalProgress] = useSignalState(0);
    const [exportScope, setExportScope] = useSignalState("result_set");
    const [bundleCompressionLevel, setBundleCompressionLevel] = useSignalState(1);
    const [pinnedResultSetSnapshot, setPinnedResultSetSnapshot] = hooks.useState(
      null
    );
    const wasOpenRef = hooks.useRef(false);
    const bundleJobRef = hooks.useRef(null);
    hooks.useEffect(() => {
      if (!show) {
        wasOpenRef.current = false;
        return;
      }
      if (wasOpenRef.current) {
        return;
      }
      wasOpenRef.current = true;
      setPinnedResultSetSnapshot({
        ...resultSetSnapshot,
        ids: [...resultSetSnapshot.ids],
        warnings: [...resultSetSnapshot.warnings]
      });
      setExportScope(
        selectedRecords.length > 0 && selectionMode === "explicit" ? "selected" : "result_set"
      );
      setCurrentProgress(0);
      setTotalProgress(0);
    }, [
      resultSetSnapshot,
      selectedRecords,
      selectionMode,
      setCurrentProgress,
      setExportScope,
      setTotalProgress,
      show
    ]);
    const activeSourceRecords = exportScope === "selected" ? selectedRecords : resultRecords;
    const resultSetPreparing = preparingFullDataset && exportScope === "result_set";
    const canExport = activeSourceRecords.length > 0 && !resultSetPreparing;
    const buildActiveRows = () => activeSourceRecords.map((record, index) => snapshotExportRow(record, columns2, index));
    const onExport = async () => {
      if (!canExport) return;
      setLoading(true);
      setCurrentProgress(0);
      setTotalProgress(activeSourceRecords.length);
      const allRecords = [];
      for (const row of buildActiveRows()) {
        const record = cloneSnapshotValue$1(row.record);
        if (includeMetadata) {
          record.metadata = cloneSnapshotValue$1(row.original);
        }
        allRecords.push(record);
        setCurrentProgress(allRecords.length);
      }
      const headerTranslations = flattenLeafColumns$1(columns2).reduce(
        (acc, column) => {
          var _a2, _b2;
          const key = ((_a2 = column.meta) == null ? void 0 : _a2.exportKey) || resolveColumnId$1(column);
          const header = ((_b2 = column.meta) == null ? void 0 : _b2.exportHeader) || resolveColumnId$1(column);
          acc[key] = t(header);
          return acc;
        },
        {}
      );
      await exportData(
        allRecords,
        selectedFormat,
        `twitter-${title}-${exportScope === "selected" ? "selected" : "results"}-${Date.now()}.${selectedFormat.toLowerCase()}`,
        headerTranslations
      );
      setLoading(false);
    };
    const onExportBundle = async () => {
      if (!canExport) return;
      setBundleLoading(true);
      setCurrentProgress(0);
      setTotalProgress(activeSourceRecords.length);
      try {
        const activeRows = buildActiveRows();
        const job = exportCanonicalBundleZipWithWorker({
          rows: activeRows,
          options: {
            title,
            scope: exportScope,
            queryText: pinnedResultSetSnapshot == null ? void 0 : pinnedResultSetSnapshot.queryText,
            sort: pinnedResultSetSnapshot == null ? void 0 : pinnedResultSetSnapshot.sort,
            includeOriginalMetadata: includeMetadata,
            compressionLevel: bundleCompressionLevel
          },
          onProgress: (progress) => {
            setCurrentProgress(progress.processedRecords);
            setTotalProgress(progress.totalRecords);
          }
        });
        bundleJobRef.current = job;
        await job.promise;
        setCurrentProgress(activeSourceRecords.length);
      } catch (error) {
        const message = error instanceof Error ? error.message : String(error);
        if (!/cancelled/i.test(message)) {
          console.error("[twitter-web-exporter] Failed to export bundle ZIP.", error);
        }
      } finally {
        bundleJobRef.current = null;
        setBundleLoading(false);
      }
    };
    const onCancel = () => {
      var _a2;
      if (bundleLoading) {
        (_a2 = bundleJobRef.current) == null ? void 0 : _a2.cancel();
        bundleJobRef.current = null;
        setBundleLoading(false);
        return;
      }
      onClose == null ? void 0 : onClose();
    };
    return /* @__PURE__ */ u(
      Modal,
      {
        class: "max-w-sm md:max-w-screen-sm sm:max-w-screen-sm max-h-full",
        title: `${title} ${t("Data")}`,
        show,
        onClose,
        children: [
          /* @__PURE__ */ u("div", { class: "px-4 text-base", children: [
            /* @__PURE__ */ u("p", { class: "text-base-content text-opacity-60 mb-2 leading-5 text-sm", children: t(
              "Export captured data as JSON/HTML/CSV file. This may take a while depending on the amount of data. The exported file does not include media files such as images and videos but only the URLs."
            ) }),
            /* @__PURE__ */ u("div", { class: "flex items-center", children: [
              /* @__PURE__ */ u("p", { class: "mr-2 leading-8", children: t("Data length:") }),
              /* @__PURE__ */ u("span", { class: "font-mono leading-6 h-6 bg-base-200 px-2 rounded-md", children: activeSourceRecords.length }),
              resultSetPreparing ? /* @__PURE__ */ u("span", { class: "ml-2 inline-flex items-center gap-1 text-xs opacity-70", children: [
                /* @__PURE__ */ u("span", { class: "loading loading-spinner loading-xs" }),
                "loading remaining rows"
              ] }) : null
            ] }),
            /* @__PURE__ */ u("div", { class: "flex items-center gap-4", children: [
              /* @__PURE__ */ u("p", { class: "leading-8", children: t("Export scope:") }),
              /* @__PURE__ */ u("label", { class: "label cursor-pointer gap-2 py-0", children: [
                /* @__PURE__ */ u(
                  "input",
                  {
                    type: "radio",
                    name: "export-scope",
                    class: "radio radio-sm",
                    checked: exportScope === "result_set",
                    onChange: () => setExportScope("result_set")
                  }
                ),
                /* @__PURE__ */ u("span", { children: t("All current results") }),
                /* @__PURE__ */ u("span", { class: "font-mono opacity-60", children: [
                  "(",
                  resultRecords.length,
                  ")"
                ] })
              ] }),
              /* @__PURE__ */ u(
                "label",
                {
                  class: cx("label cursor-pointer gap-2 py-0", !selectedRecords.length && "opacity-50"),
                  children: [
                    /* @__PURE__ */ u(
                      "input",
                      {
                        type: "radio",
                        name: "export-scope",
                        class: "radio radio-sm",
                        checked: exportScope === "selected",
                        disabled: !selectedRecords.length,
                        onChange: () => setExportScope("selected")
                      }
                    ),
                    /* @__PURE__ */ u("span", { children: t("Selected rows") }),
                    /* @__PURE__ */ u("span", { class: "font-mono opacity-60", children: [
                      "(",
                      selectedRecords.length,
                      ")"
                    ] })
                  ]
                }
              )
            ] }),
            pinnedResultSetSnapshot ? /* @__PURE__ */ u("div", { class: "rounded-box-half border border-base-300 bg-base-200/60 px-3 py-2 text-xs leading-5", children: [
              /* @__PURE__ */ u("div", { class: "font-semibold", children: t("Pinned result set") }),
              /* @__PURE__ */ u("div", { class: "font-mono opacity-70", children: pinnedResultSetSnapshot.resultSetId }),
              /* @__PURE__ */ u("div", { children: [
                t("Query"),
                ":",
                " ",
                /* @__PURE__ */ u("span", { class: "font-mono", children: pinnedResultSetSnapshot.queryText || "-" })
              ] }),
              /* @__PURE__ */ u("div", { children: [
                t("Sort"),
                ": ",
                /* @__PURE__ */ u("span", { class: "font-mono", children: pinnedResultSetSnapshot.sort })
              ] })
            ] }) : null,
            /* @__PURE__ */ u("div", { class: "flex items-center", children: [
              /* @__PURE__ */ u("p", { class: "mr-2 leading-8", children: t("Include all metadata:") }),
              /* @__PURE__ */ u(
                "input",
                {
                  type: "checkbox",
                  class: "checkbox checkbox-sm",
                  checked: includeMetadata,
                  onChange: toggleIncludeMetadata
                }
              )
            ] }),
            /* @__PURE__ */ u("div", { class: "flex", children: [
              /* @__PURE__ */ u("p", { class: "mr-2 leading-8", children: t("Export as:") }),
              /* @__PURE__ */ u(
                "select",
                {
                  class: "select select-bordered select-sm w-32",
                  onChange: (e) => {
                    setSelectedFormat(e.target.value);
                  },
                  children: Object.values(EXPORT_FORMAT).map((type2) => /* @__PURE__ */ u("option", { selected: type2 === selectedFormat, children: type2 }, type2))
                }
              )
            ] }),
            /* @__PURE__ */ u("div", { class: "flex items-center gap-2", children: [
              /* @__PURE__ */ u("p", { class: "leading-8", children: "Bundle ZIP compression:" }),
              /* @__PURE__ */ u(
                "select",
                {
                  class: "select select-bordered select-sm w-44",
                  value: String(bundleCompressionLevel),
                  onChange: (e) => setBundleCompressionLevel(Number(e.target.value)),
                  children: [
                    /* @__PURE__ */ u("option", { value: "0", children: "Fastest / store" }),
                    /* @__PURE__ */ u("option", { value: "1", children: "Balanced / fast" }),
                    /* @__PURE__ */ u("option", { value: "6", children: "Smaller / slower" })
                  ]
                }
              )
            ] }),
            activeSourceRecords.length > 0 ? null : /* @__PURE__ */ u("div", { class: "flex items-center justify-center h-28 w-full", children: /* @__PURE__ */ u("p", { class: "text-base-content text-opacity-50", children: t("No data selected.") }) }),
            /* @__PURE__ */ u("div", { class: "flex flex-col mt-6", children: [
              /* @__PURE__ */ u(
                "progress",
                {
                  class: "progress progress-primary w-full",
                  value: currentProgress / (totalProgress || 1) * 100,
                  max: "100"
                }
              ),
              /* @__PURE__ */ u("span", { class: "text-sm leading-none mt-2 text-base-content text-opacity-60", children: `${currentProgress}/${activeSourceRecords.length}` })
            ] })
          ] }),
          /* @__PURE__ */ u("div", { class: "flex space-x-2", children: [
            /* @__PURE__ */ u("span", { class: "flex-grow" }),
            /* @__PURE__ */ u("button", { class: "btn", onClick: onCancel, children: bundleLoading ? "Cancel Export" : t("Cancel") }),
            /* @__PURE__ */ u(
              "button",
              {
                class: cx("btn btn-secondary", (bundleLoading || !canExport) && "btn-disabled"),
                onClick: onExportBundle,
                title: "Export a canonical portable ZIP bundle for sharing/importing.",
                children: [
                  bundleLoading && /* @__PURE__ */ u("span", { class: "loading loading-spinner" }),
                  "Export Bundle ZIP"
                ]
              }
            ),
            /* @__PURE__ */ u(
              "button",
              {
                class: cx("btn btn-primary", (loading || !canExport) && "btn-disabled"),
                onClick: onExport,
                children: [
                  loading && /* @__PURE__ */ u("span", { class: "loading loading-spinner" }),
                  t("Start Export")
                ]
              }
            )
          ] })
        ]
      }
    );
  }
  const TERM_TOKEN_PATTERN = /[\p{L}\p{N}_]+(?:['’][\p{L}\p{N}_]+)*/gu;
  const SEARCH_RANKING_DEFAULTS = {
    bm25: 1,
    lexical: 1,
    cover_density: 1,
    recency: 0,
    term_match: 1,
    phrase_match: 8,
    quoted_phrase_match: 256,
    cover_bigram: 10,
    cover_trigram: 30
  };
  const SEARCH_RANKING_STORAGE_KEY = "twe_raw_search_ranking_v1";
  const SEARCH_QUERY_PARSE_CACHE_LIMIT = 128;
  const SEARCH_PREFIX_MIN_TERM_LENGTH = Math.max(1, Number(SEARCH_PREFIX.min_term_length));
  const SEARCH_PREFIX_MAX_EXPANSIONS = Math.max(8, Number(SEARCH_PREFIX.max_expansions));
  const SEARCH_FUZZY_MIN_TERM_LENGTH = Math.max(1, Number(SEARCH_FUZZY.min_term_length));
  const SEARCH_FUZZY_MAX_EDIT_DISTANCE = Math.max(0, Number(SEARCH_FUZZY.max_edit_distance));
  const SEARCH_FUZZY_PREFIX_ROOT_LENGTH = Math.max(1, Number(SEARCH_FUZZY.prefix_root_length));
  const SEARCH_FUZZY_MAX_EXPANSIONS = Math.max(8, Number(SEARCH_FUZZY.max_expansions));
  const SEARCH_ANCHOR_MIN_TERMS = 3;
  const SEARCH_ANCHOR_MAX_TERMS = 4;
  const SEARCH_ANCHOR_COMMON_DOC_FRACTION = 0.65;
  const SEARCH_ANCHOR_MAX_RELATIVE_DOC_FREQ = 1.5;
  const parsedQueryCache = /* @__PURE__ */ new Map();
  const searchDocCache = /* @__PURE__ */ new WeakMap();
  function readPath(obj, path) {
    const parts = path.split(".");
    let current = obj;
    for (const part of parts) {
      if (!current || typeof current !== "object") return null;
      current = current[part];
    }
    return current;
  }
  function asString(value) {
    return typeof value === "string" ? value : "";
  }
  function toBool(value) {
    if (typeof value === "boolean") return value;
    if (typeof value === "number") return value > 0;
    if (typeof value === "string") {
      const normalized = value.trim().toLowerCase();
      return normalized === "1" || normalized === "true" || normalized === "yes";
    }
    return false;
  }
  function toNumber$1(value) {
    if (typeof value === "number" && Number.isFinite(value)) return value;
    if (typeof value === "string") {
      const parsed = Number(value);
      if (Number.isFinite(parsed)) return parsed;
    }
    return 0;
  }
  function normalizeTextToken(value) {
    return value.trim().toLowerCase();
  }
  function tokenizeText(value) {
    if (!value) return [];
    const matches = value.toLowerCase().match(TERM_TOKEN_PATTERN);
    return matches ? matches.map((token) => token.replace(/['’]/g, "")).filter(Boolean) : [];
  }
  function buildTokenFrequency(tokens) {
    const tokenFreq = /* @__PURE__ */ new Map();
    for (let index = 0; index < tokens.length; index += 1) {
      const token = tokens[index];
      if (!token) continue;
      tokenFreq.set(token, (tokenFreq.get(token) || 0) + 1);
    }
    return tokenFreq;
  }
  function levenshteinDistance(a, b) {
    if (a === b) return 0;
    if (!a.length) return b.length;
    if (!b.length) return a.length;
    const previous = new Array(b.length + 1);
    const current = new Array(b.length + 1);
    for (let j = 0; j <= b.length; j += 1) previous[j] = j;
    for (let i = 1; i <= a.length; i += 1) {
      current[0] = i;
      for (let j = 1; j <= b.length; j += 1) {
        const cost = a[i - 1] === b[j - 1] ? 0 : 1;
        current[j] = Math.min(
          (previous[j] ?? 0) + 1,
          (current[j - 1] ?? 0) + 1,
          (previous[j - 1] ?? 0) + cost
        );
      }
      for (let j = 0; j <= b.length; j += 1) {
        previous[j] = current[j] ?? 0;
      }
    }
    return previous[b.length] ?? Math.max(a.length, b.length);
  }
  function parseDateToMs(value, endOfDay = false) {
    if (!value) return 0;
    if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
      const suffix = endOfDay ? "T23:59:59.999Z" : "T00:00:00.000Z";
      const parsed2 = Date.parse(`${value}${suffix}`);
      return Number.isFinite(parsed2) ? parsed2 : 0;
    }
    const parsed = Date.parse(value);
    return Number.isFinite(parsed) ? parsed : 0;
  }
  function parseSourceText(sourceHtml) {
    if (!sourceHtml) return "";
    return sourceHtml.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
  }
  function extractUrls(obj) {
    const out = /* @__PURE__ */ new Set();
    const collectUrlObjects = (value) => {
      if (!Array.isArray(value)) return;
      for (const item of value) {
        if (!item || typeof item !== "object") continue;
        const row = item;
        const expanded = asString(row.expanded_url).trim();
        const display = asString(row.display_url).trim();
        const raw = asString(row.url).trim();
        if (expanded) out.add(expanded);
        if (display) out.add(display);
        if (raw) out.add(raw);
      }
    };
    collectUrlObjects(readPath(obj, "legacy.entities.urls"));
    collectUrlObjects(readPath(obj, "legacy.entities.media"));
    collectUrlObjects(readPath(obj, "legacy.entities.description.urls"));
    collectUrlObjects(readPath(obj, "legacy.entities.url.urls"));
    return [...out];
  }
  function extractEntityValues(obj, path, key = "text") {
    const value = readPath(obj, path);
    if (!Array.isArray(value)) return [];
    const out = /* @__PURE__ */ new Set();
    for (const item of value) {
      if (!item || typeof item !== "object") continue;
      const row = item;
      const candidate = normalizeTextToken(asString(row[key]).trim());
      if (candidate) {
        out.add(candidate);
      }
    }
    return [...out];
  }
  function extractMediaTypes(obj) {
    const media = (Array.isArray(readPath(obj, "legacy.extended_entities.media")) ? readPath(obj, "legacy.extended_entities.media") : readPath(obj, "legacy.entities.media")) || [];
    if (!Array.isArray(media)) return [];
    const out = /* @__PURE__ */ new Set();
    for (const item of media) {
      if (!item || typeof item !== "object") continue;
      const row = item;
      const candidate = normalizeTextToken(asString(row.type).trim());
      if (candidate) {
        out.add(candidate);
      }
    }
    return [...out];
  }
  function extractArticleBlockTexts(obj) {
    const blocks = readPath(obj, "article.article_results.result.content_state.blocks");
    if (!Array.isArray(blocks)) return [];
    const out = [];
    for (const item of blocks) {
      if (!item || typeof item !== "object") continue;
      const text = asString(item.text).trim();
      if (!text) continue;
      out.push(text);
    }
    return out;
  }
  function extractDomains(urls) {
    const out = /* @__PURE__ */ new Set();
    for (const value of urls) {
      try {
        const normalized = value.startsWith("http") ? value : `https://${value}`;
        const domain = new URL(normalized).hostname.replace(/^www\./, "").toLowerCase();
        if (domain) out.add(domain);
      } catch {
      }
    }
    return [...out];
  }
  function phraseSlop(tokens, phraseTerms, prefixLastTerm = false) {
    if (!phraseTerms.length) return 0;
    const first = phraseTerms[0];
    const firstPositions = [];
    for (let i = 0; i < tokens.length; i += 1) {
      if (tokens[i] === first) firstPositions.push(i);
    }
    if (!firstPositions.length) return null;
    let best = null;
    for (const startPos of firstPositions) {
      let prev = startPos;
      let ok = true;
      for (let termIndex = 1; termIndex < phraseTerms.length; termIndex += 1) {
        const needle = phraseTerms[termIndex];
        const isLastNeedle = prefixLastTerm && termIndex === phraseTerms.length - 1;
        let nextPos = -1;
        for (let cursor = prev + 1; cursor < tokens.length; cursor += 1) {
          const candidate = tokens[cursor];
          if (isLastNeedle && needle && (candidate == null ? void 0 : candidate.startsWith(needle)) || candidate === needle) {
            nextPos = cursor;
            break;
          }
        }
        if (nextPos < 0) {
          ok = false;
          break;
        }
        prev = nextPos;
      }
      if (!ok) continue;
      const span = prev - startPos;
      const baseSpan = Math.max(0, phraseTerms.length - 1);
      const slop = Math.max(0, span - baseSpan);
      if (best === null || slop < best) best = slop;
    }
    return best;
  }
  function extractSearchDoc(record) {
    const obj = record || {};
    const id2 = asString(readPath(obj, "rest_id")) || asString(readPath(obj, "legacy.id_str")) || asString(readPath(obj, "id_str")) || `${Math.random().toString(36).slice(2)}`;
    const fullText = asString(readPath(obj, "note_tweet.note_tweet_results.result.text")) || [
      asString(readPath(obj, "article.article_results.result.title")),
      asString(readPath(obj, "article.article_results.result.preview_text")),
      ...extractArticleBlockTexts(obj)
    ].map((value) => value.trim()).filter(Boolean).filter((value, index, arr) => arr.indexOf(value) === index).join("\n\n") || asString(readPath(obj, "legacy.full_text")) || asString(readPath(obj, "legacy.text")) || asString(readPath(obj, "legacy.description"));
    const quotedText = asString(
      readPath(obj, "quoted_status_result.result.note_tweet.note_tweet_results.result.text")
    ) || asString(readPath(obj, "quoted_status_result.result.legacy.full_text")) || asString(readPath(obj, "quoted_status_result.result.legacy.text")) || asString(readPath(obj, "quoted_status_result.result.article.article_results.result.title")) || asString(
      readPath(obj, "quoted_status_result.result.article.article_results.result.preview_text")
    );
    const quotedAuthorScreenName = normalizeTextToken(
      asString(
        readPath(obj, "quoted_status_result.result.core.user_results.result.core.screen_name")
      )
    );
    const quotedAuthorName = asString(
      readPath(obj, "quoted_status_result.result.core.user_results.result.core.name")
    );
    const authorScreenName = normalizeTextToken(
      asString(readPath(obj, "core.user_results.result.core.screen_name")) || asString(readPath(obj, "core.screen_name"))
    );
    const authorName = asString(readPath(obj, "core.user_results.result.core.name")) || asString(readPath(obj, "core.name"));
    const authorId = asString(readPath(obj, "core.user_results.result.rest_id")) || asString(readPath(obj, "rest_id"));
    const createdAtRaw = asString(readPath(obj, "legacy.created_at")) || asString(readPath(obj, "core.created_at"));
    const createdAtMs = createdAtRaw ? Number(parseTwitterDateTime(createdAtRaw) || 0) : Number(
      readPath(obj, "article.article_results.result.metadata.first_published_at_secs") || 0
    ) * 1e3;
    const sourceText = parseSourceText(asString(readPath(obj, "legacy.source")));
    const cardName = normalizeTextToken(
      asString(readPath(obj, "card.card_platform.card_name")) || asString(readPath(obj, "card.name")) || asString(readPath(obj, "__card_name"))
    );
    const bookmarkFolderId = asString(readPath(obj, "__bookmark_folder_id")).trim();
    const bookmarkFolderName2 = asString(readPath(obj, "__bookmark_folder_name")).trim();
    const urls = extractUrls(obj);
    const domains = extractDomains(urls);
    const mentions = extractEntityValues(obj, "legacy.entities.user_mentions", "screen_name");
    const hashtags = extractEntityValues(obj, "legacy.entities.hashtags");
    const cashtags = extractEntityValues(obj, "legacy.entities.symbols");
    const mediaTypes = extractMediaTypes(obj);
    const relationshipSubjectScreenNames = flattenFieldValues(
      readPath(obj, "twe_relationship_fields.subject_screen_names")
    ).map((value) => normalizeTextToken(value));
    const relationshipSubjectUserIds = flattenFieldValues(
      readPath(obj, "twe_relationship_fields.subject_user_ids")
    ).map((value) => String(value).trim());
    const relationshipTypes = flattenFieldValues(
      readPath(obj, "twe_relationship_fields.relation_types")
    ).map((value) => normalizeTextToken(value));
    const toUser = normalizeTextToken(asString(readPath(obj, "legacy.in_reply_to_screen_name")));
    const toUserId = asString(readPath(obj, "legacy.in_reply_to_user_id_str")).trim();
    const inReplyToId = asString(readPath(obj, "legacy.in_reply_to_status_id_str")).trim();
    const conversationId = asString(readPath(obj, "legacy.conversation_id_str")) || asString(readPath(obj, "conversation_id_str"));
    const lang = normalizeTextToken(asString(readPath(obj, "legacy.lang")));
    const routeType = normalizeTextToken(asString(readPath(obj, "__route_type")));
    const hasMedia = mediaTypes.length > 0;
    const hasImages = mediaTypes.includes("photo");
    const hasVideo = mediaTypes.includes("video") || mediaTypes.includes("animated_gif");
    const hasLinks = urls.length > 0;
    const isRetweet = !!readPath(obj, "legacy.retweeted_status_result") || asString(readPath(obj, "legacy.full_text")).startsWith("RT @");
    const isQuote = !!readPath(obj, "quoted_status_result");
    const isReply = !!inReplyToId;
    const isVerified = normalizeTextToken(
      asString(readPath(obj, "core.user_results.result.verification.verified_type"))
    ) === "verified" || normalizeTextToken(asString(readPath(obj, "verification.verified_type"))) === "verified";
    const isBlueVerified = toBool(readPath(obj, "core.user_results.result.is_blue_verified")) || toBool(readPath(obj, "is_blue_verified"));
    const favorited = toBool(readPath(obj, "legacy.favorited"));
    const retweeted = toBool(readPath(obj, "legacy.retweeted"));
    const bookmarked = toBool(readPath(obj, "legacy.bookmarked"));
    const favoriteCount = toNumber$1(readPath(obj, "legacy.favorite_count"));
    const retweetCount = toNumber$1(readPath(obj, "legacy.retweet_count"));
    const replyCount = toNumber$1(readPath(obj, "legacy.reply_count"));
    const bookmarkCount = toNumber$1(readPath(obj, "legacy.bookmark_count"));
    const searchText = [
      fullText,
      quotedText,
      sourceText,
      authorScreenName,
      authorName,
      quotedAuthorScreenName,
      quotedAuthorName,
      toUser,
      bookmarkFolderId,
      bookmarkFolderName2,
      cardName,
      ...relationshipSubjectScreenNames,
      ...relationshipSubjectUserIds,
      ...relationshipTypes,
      ...mentions,
      ...hashtags,
      ...cashtags,
      ...urls,
      ...domains
    ].filter(Boolean).join(" ").trim();
    const primaryText = [
      fullText,
      sourceText,
      authorScreenName,
      authorName,
      toUser,
      bookmarkFolderId,
      bookmarkFolderName2,
      cardName,
      ...relationshipSubjectScreenNames,
      ...relationshipSubjectUserIds,
      ...relationshipTypes,
      ...mentions,
      ...hashtags,
      ...cashtags,
      ...urls,
      ...domains
    ].filter(Boolean).join(" ").trim();
    const quoteAuxText = [quotedText, quotedAuthorScreenName, quotedAuthorName].filter(Boolean).join(" ").trim();
    const tokens = tokenizeText(searchText);
    const primaryTokens = tokenizeText(primaryText);
    const quotedTokens = tokenizeText(quoteAuxText);
    const tokenFreq = buildTokenFrequency(tokens);
    const primaryTokenFreq = buildTokenFrequency(primaryTokens);
    const quotedTokenFreq = buildTokenFrequency(quotedTokens);
    return {
      raw: record,
      id: id2,
      text: searchText,
      primaryText,
      quotedText: quoteAuxText,
      tokens,
      primaryTokens,
      quotedTokens,
      tokenFreq,
      primaryTokenFreq,
      quotedTokenFreq,
      createdAtMs,
      authorScreenName,
      authorId,
      toUser,
      toUserId,
      inReplyToId,
      conversationId,
      lang,
      routeType,
      sourceText: normalizeTextToken(sourceText),
      cardName,
      bookmarkFolderId,
      bookmarkFolderName: normalizeTextToken(bookmarkFolderName2),
      mentions,
      hashtags,
      cashtags,
      urls: urls.map((value) => value.toLowerCase()),
      domains,
      favoriteCount,
      retweetCount,
      replyCount,
      bookmarkCount,
      favorited,
      retweeted,
      bookmarked,
      hasMedia,
      hasImages,
      hasVideo,
      hasLinks,
      isReply,
      isRetweet,
      isQuote,
      isVerified,
      isBlueVerified,
      fieldSearchCache: /* @__PURE__ */ new Map()
    };
  }
  function getCachedSearchDoc(record) {
    if (!record || typeof record !== "object") {
      return extractSearchDoc(record);
    }
    const cached = searchDocCache.get(record);
    if (cached) {
      return cached;
    }
    const built = extractSearchDoc(record);
    searchDocCache.set(record, built);
    return built;
  }
  function flattenFieldValues(value) {
    if (value === null || value === void 0) return [];
    if (typeof value === "string") {
      const text = value.trim();
      return text ? [text] : [];
    }
    if (typeof value === "number" || typeof value === "boolean") {
      return [String(value)];
    }
    if (Array.isArray(value)) {
      return value.flatMap((item) => flattenFieldValues(item));
    }
    if (typeof value === "object") {
      return Object.values(value).flatMap(
        (item) => flattenFieldValues(item)
      );
    }
    return [];
  }
  function resolveFieldValue(doc, fieldPath) {
    switch (fieldPath) {
      case "text":
        return doc.text;
      case "id":
        return doc.id;
      case "author_screen_name":
        return doc.authorScreenName;
      case "author_id":
        return doc.authorId;
      case "to_user":
        return doc.toUser;
      case "to_user_id":
        return doc.toUserId;
      case "conversation_id":
        return doc.conversationId;
      case "lang":
        return doc.lang;
      case "route":
      case "route_type":
        return doc.routeType;
      case "source":
      case "source_text":
        return doc.sourceText;
      case "card_name":
        return doc.cardName;
      case "bookmark_folder_id":
        return doc.bookmarkFolderId;
      case "bookmark_folder_name":
        return doc.bookmarkFolderName;
      case "subject_screen_names":
        return flattenFieldValues(
          readPath(
            doc.raw || {},
            "twe_relationship_fields.subject_screen_names"
          )
        );
      case "subject_user_ids":
        return flattenFieldValues(
          readPath(
            doc.raw || {},
            "twe_relationship_fields.subject_user_ids"
          )
        );
      case "relation_types":
        return flattenFieldValues(
          readPath(
            doc.raw || {},
            "twe_relationship_fields.relation_types"
          )
        );
      case "mentions":
        return doc.mentions;
      case "hashtags":
        return doc.hashtags;
      case "cashtags":
        return doc.cashtags;
      case "urls":
        return doc.urls;
      case "domains":
        return doc.domains;
      default:
        return readPath(doc.raw || {}, fieldPath);
    }
  }
  function getFieldSearchData(doc, fieldPath) {
    const key = String(fieldPath || "").trim();
    if (!key) {
      return {
        text: doc.text,
        tokens: doc.tokens,
        tokenFreq: doc.tokenFreq
      };
    }
    const cached = doc.fieldSearchCache.get(key);
    if (cached) return cached;
    const values = flattenFieldValues(resolveFieldValue(doc, key));
    const text = values.join(" ").trim();
    const tokens = tokenizeText(text);
    const tokenFreq = /* @__PURE__ */ new Map();
    for (const token of tokens) {
      tokenFreq.set(token, (tokenFreq.get(token) || 0) + 1);
    }
    const built = { text, tokens, tokenFreq };
    doc.fieldSearchCache.set(key, built);
    return built;
  }
  function buildRankingContext(docs) {
    const termDocFreq = /* @__PURE__ */ new Map();
    let totalLength = 0;
    for (const doc of docs) {
      totalLength += doc.tokens.length;
      for (const token of doc.tokenFreq.keys()) {
        termDocFreq.set(token, (termDocFreq.get(token) || 0) + 1);
      }
    }
    return {
      docCount: docs.length,
      avgDocLength: docs.length ? totalLength / docs.length : 1,
      termDocFreq
    };
  }
  function pushIndexedValue(index, key, docIndex) {
    if (!key) return;
    const existing = index.get(key);
    if (existing) {
      existing.push(docIndex);
      return;
    }
    index.set(key, [docIndex]);
  }
  function buildExactFilterKey(name2, value) {
    return `${name2}:${value}`;
  }
  function buildSearchCorpusIndexes(docs) {
    const tokenDocs = /* @__PURE__ */ new Map();
    const exactFilterDocs = /* @__PURE__ */ new Map();
    for (let docIndex = 0; docIndex < docs.length; docIndex += 1) {
      const doc = docs[docIndex];
      if (!doc) continue;
      for (const token of doc.tokenFreq.keys()) {
        pushIndexedValue(tokenDocs, token, docIndex);
      }
      pushIndexedValue(exactFilterDocs, buildExactFilterKey("id", doc.id), docIndex);
      pushIndexedValue(exactFilterDocs, buildExactFilterKey("from", doc.authorScreenName), docIndex);
      pushIndexedValue(exactFilterDocs, buildExactFilterKey("from_id", doc.authorId), docIndex);
      pushIndexedValue(exactFilterDocs, buildExactFilterKey("author_id", doc.authorId), docIndex);
      pushIndexedValue(exactFilterDocs, buildExactFilterKey("to", doc.toUser), docIndex);
      pushIndexedValue(exactFilterDocs, buildExactFilterKey("to_id", doc.toUserId), docIndex);
      pushIndexedValue(
        exactFilterDocs,
        buildExactFilterKey("in_reply_to_id", doc.inReplyToId),
        docIndex
      );
      pushIndexedValue(
        exactFilterDocs,
        buildExactFilterKey("conversation_id", doc.conversationId),
        docIndex
      );
      pushIndexedValue(exactFilterDocs, buildExactFilterKey("lang", doc.lang), docIndex);
      pushIndexedValue(exactFilterDocs, buildExactFilterKey("route", doc.routeType), docIndex);
      pushIndexedValue(
        exactFilterDocs,
        buildExactFilterKey("bookmark_folder", doc.bookmarkFolderId),
        docIndex
      );
      for (const value of doc.mentions) {
        pushIndexedValue(exactFilterDocs, buildExactFilterKey("mention", value), docIndex);
      }
      for (const value of doc.hashtags) {
        pushIndexedValue(exactFilterDocs, buildExactFilterKey("hashtag", value), docIndex);
      }
      for (const value of doc.cashtags) {
        pushIndexedValue(exactFilterDocs, buildExactFilterKey("cashtag", value), docIndex);
      }
      if (doc.hasMedia)
        pushIndexedValue(exactFilterDocs, buildExactFilterKey("is", "media"), docIndex);
      if (doc.hasImages)
        pushIndexedValue(exactFilterDocs, buildExactFilterKey("is", "images"), docIndex);
      if (doc.hasVideo)
        pushIndexedValue(exactFilterDocs, buildExactFilterKey("is", "videos"), docIndex);
      if (doc.hasLinks)
        pushIndexedValue(exactFilterDocs, buildExactFilterKey("is", "links"), docIndex);
      if (doc.bookmarked)
        pushIndexedValue(exactFilterDocs, buildExactFilterKey("is", "bookmarked"), docIndex);
      if (doc.favorited)
        pushIndexedValue(exactFilterDocs, buildExactFilterKey("is", "liked"), docIndex);
      if (doc.retweeted)
        pushIndexedValue(exactFilterDocs, buildExactFilterKey("is", "retweeted"), docIndex);
      if (doc.isReply)
        pushIndexedValue(exactFilterDocs, buildExactFilterKey("is", "reply"), docIndex);
      if (doc.isRetweet)
        pushIndexedValue(exactFilterDocs, buildExactFilterKey("is", "retweet"), docIndex);
      if (doc.isQuote)
        pushIndexedValue(exactFilterDocs, buildExactFilterKey("is", "quote"), docIndex);
      if (doc.isVerified)
        pushIndexedValue(exactFilterDocs, buildExactFilterKey("is", "verified"), docIndex);
      if (doc.isBlueVerified) {
        pushIndexedValue(exactFilterDocs, buildExactFilterKey("is", "blue_verified"), docIndex);
      }
    }
    return {
      tokenDocs,
      exactFilterDocs,
      tokenVocabulary: [...tokenDocs.keys()].sort()
    };
  }
  function intersectDocSets(current, next) {
    const nextSet = next instanceof Set ? next : new Set(next);
    if (!current) {
      return new Set(nextSet);
    }
    const out = /* @__PURE__ */ new Set();
    for (const value of current) {
      if (nextSet.has(value)) {
        out.add(value);
      }
    }
    return out;
  }
  function unionDocSets(groups) {
    const out = /* @__PURE__ */ new Set();
    for (const group of groups) {
      for (const value of group) {
        out.add(value);
      }
    }
    return out;
  }
  function intersectDocArrays(left, right) {
    if (!left.length || !right.length) return [];
    const smaller = left.length <= right.length ? left : right;
    const larger = left.length <= right.length ? right : left;
    const largerSet = new Set(larger);
    const out = [];
    for (const value of smaller) {
      if (largerSet.has(value)) {
        out.push(value);
      }
    }
    return out;
  }
  function buildAdjacentTermAnchorCandidates(prepared, parsed) {
    const orderedTerms = parsed.orderedTerms.filter(Boolean);
    if (orderedTerms.length < SEARCH_ANCHOR_MIN_TERMS) return null;
    const windowGroups = [];
    for (let index = 0; index < orderedTerms.length - 1; index += 1) {
      const leftDocs = prepared.indexes.tokenDocs.get(orderedTerms[index] || "") || [];
      const rightDocs = prepared.indexes.tokenDocs.get(orderedTerms[index + 1] || "") || [];
      const windowDocs = intersectDocArrays(leftDocs, rightDocs);
      if (windowDocs.length) {
        windowGroups.push(windowDocs);
      }
    }
    if (!windowGroups.length) return null;
    const candidateSet = unionDocSets(windowGroups);
    if (!candidateSet.size || candidateSet.size >= prepared.docs.length) {
      return null;
    }
    return candidateSet;
  }
  function buildRareTermAnchorCandidates(prepared, parsed) {
    var _a2;
    const positiveTerms = parsed.positiveTerms.filter(Boolean);
    if (positiveTerms.length < SEARCH_ANCHOR_MIN_TERMS) return null;
    const docCount = Math.max(1, prepared.rankingContext.docCount);
    const rankedTerms = positiveTerms.map((term) => ({
      term,
      docFreq: prepared.rankingContext.termDocFreq.get(term) || 0
    })).filter((entry) => entry.docFreq > 0).sort((left, right) => {
      if (left.docFreq !== right.docFreq) return left.docFreq - right.docFreq;
      return left.term.localeCompare(right.term);
    });
    if (!rankedTerms.length) return null;
    const nonCommon = rankedTerms.filter(
      (entry) => entry.docFreq / docCount <= SEARCH_ANCHOR_COMMON_DOC_FRACTION
    );
    const anchorPool = nonCommon.length ? nonCommon : rankedTerms;
    const rarestDocFreq = ((_a2 = anchorPool[0]) == null ? void 0 : _a2.docFreq) || 0;
    const comparableRarity = rarestDocFreq > 0 ? anchorPool.filter(
      (entry) => entry.docFreq <= rarestDocFreq * SEARCH_ANCHOR_MAX_RELATIVE_DOC_FREQ
    ) : anchorPool;
    const anchorTerms = (comparableRarity.length ? comparableRarity : anchorPool).slice(
      0,
      SEARCH_ANCHOR_MAX_TERMS
    );
    if (!anchorTerms.length) return null;
    const candidateSet = unionDocSets(
      anchorTerms.map((entry) => prepared.indexes.tokenDocs.get(entry.term) || [])
    );
    if (!candidateSet.size || candidateSet.size >= prepared.docs.length) {
      return null;
    }
    return candidateSet;
  }
  function getPrefixExpandedTerms(vocabulary, prefix2, maxExpansions) {
    if (!prefix2 || prefix2.length < SEARCH_PREFIX_MIN_TERM_LENGTH) return [];
    const out = [];
    for (const candidate of vocabulary) {
      if (!candidate.startsWith(prefix2)) continue;
      out.push(candidate);
      if (out.length >= maxExpansions) break;
    }
    return out;
  }
  function getFuzzyExpandedTerms(vocabulary, value, maxExpansions) {
    if (!value || value.length < SEARCH_FUZZY_MIN_TERM_LENGTH || SEARCH_FUZZY_MAX_EDIT_DISTANCE <= 0) {
      return [];
    }
    const prefixRoot = value.slice(0, SEARCH_FUZZY_PREFIX_ROOT_LENGTH);
    const out = [];
    for (const candidate of vocabulary) {
      if (!candidate.startsWith(prefixRoot)) continue;
      if (Math.abs(candidate.length - value.length) > SEARCH_FUZZY_MAX_EDIT_DISTANCE) continue;
      if (levenshteinDistance(candidate, value) > SEARCH_FUZZY_MAX_EDIT_DISTANCE) continue;
      out.push(candidate);
      if (out.length >= maxExpansions) break;
    }
    return out;
  }
  function getLexicalTokenCandidateDocs(prepared, token) {
    const index = prepared.indexes.tokenDocs;
    const vocabulary = prepared.indexes.tokenVocabulary;
    const terms = tokenizeText(token.value);
    if (!terms.length) return [];
    const candidateTerms = /* @__PURE__ */ new Set();
    for (let idx = 0; idx < terms.length; idx += 1) {
      const term = terms[idx];
      if (!term) continue;
      candidateTerms.add(term);
      const isLast = idx === terms.length - 1;
      if (!isLast) continue;
      if (token.prefix) {
        for (const expanded of getPrefixExpandedTerms(
          vocabulary,
          term,
          SEARCH_PREFIX_MAX_EXPANSIONS
        )) {
          candidateTerms.add(expanded);
        }
      }
      if (token.fuzzy) {
        for (const expanded of getFuzzyExpandedTerms(vocabulary, term, SEARCH_FUZZY_MAX_EXPANSIONS)) {
          candidateTerms.add(expanded);
        }
      }
    }
    return [...candidateTerms].flatMap((term) => index.get(term) || []);
  }
  function getIndexedFilterCandidates(prepared, filter) {
    const rawValue = String(filter.value || "").trim();
    const normalized = normalizeTextToken(rawValue);
    if (!rawValue) return null;
    const exactIndex = prepared.indexes.exactFilterDocs;
    switch (filter.name) {
      case "from":
      case "to":
      case "lang":
      case "route":
      case "mention":
      case "hashtag":
      case "cashtag":
      case "id":
        return new Set(exactIndex.get(buildExactFilterKey(filter.name, normalized)) || []);
      case "from_id":
      case "author_id":
      case "to_id":
      case "in_reply_to_id":
      case "conversation_id":
        return new Set(exactIndex.get(buildExactFilterKey(filter.name, rawValue)) || []);
      case "bookmark_folder":
        if (/^\d+$/.test(rawValue)) {
          return new Set(exactIndex.get(buildExactFilterKey("bookmark_folder", rawValue)) || []);
        }
        return null;
      case "folder":
        if (/^\d+$/.test(rawValue)) {
          return new Set(exactIndex.get(buildExactFilterKey("bookmark_folder", rawValue)) || []);
        }
        return null;
      case "is":
        return new Set(exactIndex.get(buildExactFilterKey("is", normalized)) || []);
      default:
        return null;
    }
  }
  function buildIndexedCandidateSet(prepared, parsed, scopedFolderIds) {
    let candidateSet = null;
    if (scopedFolderIds.size) {
      const folderCandidates = unionDocSets(
        [...scopedFolderIds].map(
          (folderId) => prepared.indexes.exactFilterDocs.get(buildExactFilterKey("bookmark_folder", folderId)) || []
        )
      );
      candidateSet = intersectDocSets(candidateSet, folderCandidates);
    }
    for (const filter of parsed.filters) {
      const indexed = getIndexedFilterCandidates(prepared, filter);
      if (!indexed) continue;
      if (filter.negated) {
        if (!candidateSet) continue;
        for (const value of indexed) {
          candidateSet.delete(value);
        }
        continue;
      }
      candidateSet = intersectDocSets(candidateSet, indexed);
    }
    const anchorCandidates = buildAdjacentTermAnchorCandidates(prepared, parsed) || buildRareTermAnchorCandidates(prepared, parsed);
    const lexicalTokens = parsed.positiveLexicalTokens.filter((token) => !token.field);
    const lexicalTermCandidates = anchorCandidates || unionDocSets(lexicalTokens.map((token) => getLexicalTokenCandidateDocs(prepared, token)));
    if (lexicalTermCandidates.size) {
      candidateSet = intersectDocSets(candidateSet, lexicalTermCandidates);
    }
    return candidateSet;
  }
  function resolveRankingFromStorage() {
    const ranking = { ...SEARCH_RANKING_DEFAULTS };
    try {
      if (typeof localStorage === "undefined") return ranking;
      const raw = localStorage.getItem(SEARCH_RANKING_STORAGE_KEY);
      if (!raw) return ranking;
      const parsed = JSON.parse(raw);
      for (const key of Object.keys(SEARCH_RANKING_DEFAULTS)) {
        const value = Number(parsed[key]);
        if (Number.isFinite(value)) ranking[key] = value;
      }
      return ranking;
    } catch {
      return ranking;
    }
  }
  function getCachedParsedSearchQuery(query) {
    const normalized = String(query || "").trim();
    const cached = parsedQueryCache.get(normalized);
    if (cached) {
      return cached;
    }
    const parsed = parseSearchQuery(normalized);
    parsedQueryCache.set(normalized, parsed);
    if (parsedQueryCache.size > SEARCH_QUERY_PARSE_CACHE_LIMIT) {
      const oldestKey = parsedQueryCache.keys().next().value;
      if (typeof oldestKey === "string") {
        parsedQueryCache.delete(oldestKey);
      }
    }
    return parsed;
  }
  function scoreTermBM25(doc, term, rankingContext) {
    const tf = doc.tokenFreq.get(term) || 0;
    if (!tf) return 0;
    const df = rankingContext.termDocFreq.get(term) || 0;
    const docCount = Math.max(1, rankingContext.docCount);
    const avgDocLength = Math.max(1, rankingContext.avgDocLength);
    const dl = Math.max(1, doc.tokens.length);
    const k1 = 1.2;
    const b = 0.75;
    const idf = Math.log(1 + (docCount - df + 0.5) / (df + 0.5));
    const denom = tf + k1 * (1 - b + b * dl / avgDocLength);
    return idf * (tf * (k1 + 1)) / denom;
  }
  function buildFilterWarnings(filters) {
    const warnings = [];
    const isModes = /* @__PURE__ */ new Set([
      "bookmarked",
      "liked",
      "retweeted",
      "reply",
      "retweet",
      "quote",
      "media",
      "images",
      "videos",
      "links",
      "verified",
      "blue_verified"
    ]);
    const hasModes = /* @__PURE__ */ new Set([
      "media",
      "images",
      "videos",
      "links",
      "mentions",
      "hashtags",
      "cashtags",
      "engagement",
      "polls"
    ]);
    const compatibilityModes = /* @__PURE__ */ new Set([
      "replies",
      "retweets",
      "nativeretweets",
      "quote",
      "media",
      "images",
      "videos",
      "links",
      "mentions",
      "hashtags",
      "verified",
      "blue_verified",
      "twimg",
      "native_video",
      "consumer_video",
      "pro_video",
      "has_engagement"
    ]);
    for (const filter of filters) {
      const token = `${filter.name}:${filter.value}`;
      const value = String(filter.value || "").trim();
      if (!value) continue;
      if (["since", "until"].includes(filter.name) && !parseDateToMs(value, filter.name === "until")) {
        warnings.push({
          code: "invalid_filter_value",
          message: `invalid ${token}`,
          token,
          severity: "warn"
        });
        continue;
      }
      if ([
        "since_time",
        "until_time",
        "since_id",
        "max_id",
        "min_faves",
        "min_likes",
        "min_retweets",
        "min_replies",
        "min_bookmarks"
      ].includes(filter.name) && !Number.isFinite(Number(value))) {
        warnings.push({
          code: "invalid_filter_value",
          message: `invalid ${token}`,
          token,
          severity: "warn"
        });
        continue;
      }
      if (filter.name === "is" && !isModes.has(normalizeTextToken(value))) {
        warnings.push({
          code: "unsupported_filter",
          message: `unsupported ${token}`,
          token,
          severity: "warn"
        });
        continue;
      }
      if (filter.name === "has" && !hasModes.has(normalizeTextToken(value))) {
        warnings.push({
          code: "unsupported_filter",
          message: `unsupported ${token}`,
          token,
          severity: "warn"
        });
        continue;
      }
      if (["filter", "include"].includes(filter.name) && !compatibilityModes.has(normalizeTextToken(value))) {
        warnings.push({
          code: "unsupported_filter",
          message: `unsupported ${token}`,
          token,
          severity: "warn"
        });
        continue;
      }
    }
    return warnings;
  }
  function evaluateFilter(doc, filter) {
    const rawValue = String(filter.value || "").trim();
    const value = normalizeTextToken(rawValue);
    if (!rawValue) return true;
    const evaluate = () => {
      switch (filter.name) {
        case "from":
          return doc.authorScreenName === value;
        case "from_id":
        case "author_id":
          return doc.authorId === rawValue;
        case "to":
          return doc.toUser === value;
        case "to_id":
          return doc.toUserId === rawValue;
        case "in_reply_to_id":
          return doc.inReplyToId === rawValue;
        case "id":
          return doc.id === rawValue;
        case "lang":
          return doc.lang === value;
        case "route":
          return doc.routeType === value;
        case "conversation_id":
          return doc.conversationId === rawValue;
        case "bookmark_folder":
        case "folder":
          if (/^\d+$/.test(rawValue)) {
            return doc.bookmarkFolderId === rawValue;
          }
          return doc.bookmarkFolderName === value || doc.bookmarkFolderName.includes(value);
        case "mention":
          return doc.mentions.includes(value);
        case "hashtag":
          return doc.hashtags.includes(value);
        case "cashtag":
          return doc.cashtags.includes(value);
        case "source":
          return doc.sourceText.includes(value);
        case "card_name": {
          const normalizedCard = value.replace(/\s+/g, "_");
          return doc.cardName === normalizedCard || doc.cardName.includes(normalizedCard);
        }
        case "domain":
          return doc.domains.some((domain) => domain === value || domain.endsWith(`.${value}`));
        case "url":
          return doc.urls.some((url) => url.includes(value));
        case "is": {
          if (value === "bookmarked") return doc.bookmarked;
          if (value === "liked") return doc.favorited;
          if (value === "retweeted") return doc.retweeted;
          if (value === "reply") return doc.isReply;
          if (value === "retweet") return doc.isRetweet;
          if (value === "quote") return doc.isQuote;
          if (value === "media") return doc.hasMedia;
          if (value === "images") return doc.hasImages;
          if (value === "videos") return doc.hasVideo;
          if (value === "links") return doc.hasLinks;
          if (value === "verified") return doc.isVerified;
          if (value === "blue_verified") return doc.isBlueVerified;
          return true;
        }
        case "has": {
          if (value === "media") return doc.hasMedia;
          if (value === "images") return doc.hasImages;
          if (value === "videos") return doc.hasVideo;
          if (value === "links") return doc.hasLinks;
          if (value === "mentions") return doc.mentions.length > 0;
          if (value === "hashtags") return doc.hashtags.length > 0;
          if (value === "cashtags") return doc.cashtags.length > 0;
          if (value === "engagement") {
            return doc.favoriteCount + doc.retweetCount + doc.replyCount + doc.bookmarkCount > 0;
          }
          if (value === "polls") return doc.cardName.includes("poll");
          return true;
        }
        case "filter":
        case "include": {
          if (filter.name === "include" && value === "nativeretweets" && !filter.negated) return true;
          if (value === "replies") return doc.isReply;
          if (value === "retweets") return doc.isRetweet || doc.isQuote;
          if (value === "nativeretweets") return doc.isRetweet;
          if (value === "quote") return doc.isQuote;
          if (value === "media") return doc.hasMedia;
          if (value === "images") return doc.hasImages;
          if (value === "videos" || value === "native_video" || value === "consumer_video" || value === "pro_video") {
            return doc.hasVideo;
          }
          if (value === "links") return doc.hasLinks;
          if (value === "mentions") return doc.mentions.length > 0;
          if (value === "hashtags") return doc.hashtags.length > 0;
          if (value === "verified") return doc.isVerified;
          if (value === "blue_verified") return doc.isBlueVerified;
          if (value === "twimg") {
            return doc.urls.some(
              (url) => url.includes("pbs.twimg.com") || url.includes("pic.twitter.com")
            );
          }
          if (value === "has_engagement") {
            return doc.favoriteCount + doc.retweetCount + doc.replyCount + doc.bookmarkCount > 0;
          }
          return true;
        }
        case "since": {
          const minTs = parseDateToMs(rawValue, false);
          if (!minTs || !doc.createdAtMs) return true;
          return doc.createdAtMs >= minTs;
        }
        case "until": {
          const maxTs = parseDateToMs(rawValue, true);
          if (!maxTs || !doc.createdAtMs) return true;
          return doc.createdAtMs < maxTs;
        }
        case "since_time": {
          const minTs = Number(rawValue) * 1e3;
          if (!Number.isFinite(minTs) || !doc.createdAtMs) return true;
          return doc.createdAtMs >= minTs;
        }
        case "until_time": {
          const maxTs = Number(rawValue) * 1e3;
          if (!Number.isFinite(maxTs) || !doc.createdAtMs) return true;
          return doc.createdAtMs < maxTs;
        }
        case "since_id":
          return /^\d+$/.test(doc.id) && Number(doc.id) > Number(rawValue);
        case "max_id":
          return /^\d+$/.test(doc.id) && Number(doc.id) <= Number(rawValue);
        case "min_faves":
        case "min_likes":
          return doc.favoriteCount >= Number(rawValue);
        case "min_retweets":
          return doc.retweetCount >= Number(rawValue);
        case "min_replies":
          return doc.replyCount >= Number(rawValue);
        case "min_bookmarks":
          return doc.bookmarkCount >= Number(rawValue);
        default:
          return true;
      }
    };
    const matched = evaluate();
    return filter.negated ? !matched : matched;
  }
  function tokenMatchesTokenSet(targetTokens, targetFreq, token) {
    if (token.kind === "phrase") {
      const phraseTokens = tokenizeText(token.value);
      const slop = phraseSlop(targetTokens, phraseTokens, !!token.prefix);
      if (slop === null) {
        if (token.fuzzy && phraseTokens.length) {
          const fuzzyLast = phraseTokens[phraseTokens.length - 1];
          const fuzzyPrefix = fuzzyLast && fuzzyLast.length >= SEARCH_FUZZY_MIN_TERM_LENGTH ? getFuzzyExpandedTerms(
            [...new Set(targetTokens)],
            fuzzyLast,
            SEARCH_FUZZY_MAX_EXPANSIONS
          ) : [];
          for (const candidate of fuzzyPrefix) {
            const mutated = [...phraseTokens];
            mutated[mutated.length - 1] = candidate;
            const fuzzySlop = phraseSlop(targetTokens, mutated, false);
            if (fuzzySlop !== null && fuzzySlop <= token.slop) {
              return { matched: true, slopUsed: fuzzySlop };
            }
          }
        }
        return { matched: false, slopUsed: Number.POSITIVE_INFINITY };
      }
      if (slop > token.slop) return { matched: false, slopUsed: slop };
      return { matched: true, slopUsed: slop };
    }
    const normalized = tokenizeText(token.value);
    if (!normalized.length) {
      return { matched: false, slopUsed: Number.POSITIVE_INFINITY };
    }
    if (normalized.length === 1) {
      const first = normalized[0];
      if (!first) {
        return { matched: false, slopUsed: Number.POSITIVE_INFINITY };
      }
      let matched = targetFreq.has(first);
      if (!matched && token.prefix && first.length >= SEARCH_PREFIX_MIN_TERM_LENGTH) {
        matched = [...targetFreq.keys()].some((candidate) => candidate.startsWith(first));
      }
      if (!matched && token.fuzzy && first.length >= SEARCH_FUZZY_MIN_TERM_LENGTH) {
        matched = [...targetFreq.keys()].some(
          (candidate) => candidate.startsWith(first.slice(0, SEARCH_FUZZY_PREFIX_ROOT_LENGTH)) && Math.abs(candidate.length - first.length) <= SEARCH_FUZZY_MAX_EDIT_DISTANCE && levenshteinDistance(candidate, first) <= SEARCH_FUZZY_MAX_EDIT_DISTANCE
        );
      }
      return { matched, slopUsed: matched ? 0 : Number.POSITIVE_INFINITY };
    }
    const slopUsed = phraseSlop(targetTokens, normalized, !!token.prefix);
    return {
      matched: slopUsed !== null && slopUsed <= 0,
      slopUsed: slopUsed ?? Number.POSITIVE_INFINITY
    };
  }
  function tokenMatchesDoc(doc, token) {
    const fieldSearch = token.field ? getFieldSearchData(doc, token.field) : null;
    if (fieldSearch) {
      const match = tokenMatchesTokenSet(fieldSearch.tokens, fieldSearch.tokenFreq, token);
      return {
        matched: match.matched,
        slopUsed: match.slopUsed,
        primaryMatched: match.matched,
        quotedMatched: false
      };
    }
    const primary = tokenMatchesTokenSet(doc.primaryTokens, doc.primaryTokenFreq, token);
    const quoted = doc.quotedTokens.length > 0 ? tokenMatchesTokenSet(doc.quotedTokens, doc.quotedTokenFreq, token) : { matched: false, slopUsed: Number.POSITIVE_INFINITY };
    if (!primary.matched && !quoted.matched) {
      return {
        matched: false,
        slopUsed: Number.POSITIVE_INFINITY,
        primaryMatched: false,
        quotedMatched: false
      };
    }
    let slopUsed = Number.POSITIVE_INFINITY;
    if (primary.matched) slopUsed = Math.min(slopUsed, primary.slopUsed);
    if (quoted.matched) slopUsed = Math.min(slopUsed, quoted.slopUsed);
    return {
      matched: true,
      slopUsed,
      primaryMatched: primary.matched,
      quotedMatched: quoted.matched
    };
  }
  function tokenScore(token, slopUsed, rankingValues, quoteOnly = false) {
    if (token.kind === "phrase") {
      const base = token.quoted && token.slop === 0 ? rankingValues.quoted_phrase_match : rankingValues.phrase_match;
      const scaled2 = base * token.boost / (1 + Math.max(0, slopUsed));
      return quoteOnly ? scaled2 * 0.2 : scaled2;
    }
    const scaled = rankingValues.term_match * token.boost;
    return quoteOnly ? scaled * 0.2 : scaled;
  }
  function computeExactPhraseTieBreak(doc, positiveLexicalTokens) {
    let exactPhraseTerms = 0;
    let exactQuotedPhraseTerms = 0;
    let exactPrimaryPhraseTerms = 0;
    let exactPrimaryQuotedPhraseTerms = 0;
    for (const token of positiveLexicalTokens) {
      if (token.kind !== "phrase") continue;
      const match = tokenMatchesDoc(doc, token);
      if (!match.matched || match.slopUsed !== 0) continue;
      const phraseTerms = tokenizeText(token.value);
      const termCount = phraseTerms.length;
      if (termCount > exactPhraseTerms) {
        exactPhraseTerms = termCount;
      }
      if (match.primaryMatched && termCount > exactPrimaryPhraseTerms) {
        exactPrimaryPhraseTerms = termCount;
      }
      if (token.quoted && token.slop === 0 && termCount > exactQuotedPhraseTerms) {
        exactQuotedPhraseTerms = termCount;
      }
      if (token.quoted && token.slop === 0 && match.primaryMatched && termCount > exactPrimaryQuotedPhraseTerms) {
        exactPrimaryQuotedPhraseTerms = termCount;
      }
    }
    return {
      exactPhraseTerms,
      exactQuotedPhraseTerms,
      exactPrimaryPhraseTerms,
      exactPrimaryQuotedPhraseTerms
    };
  }
  function evaluateLexicalAst(doc, node, rankingValues) {
    if (node.kind === "term" || node.kind === "phrase") {
      const match = tokenMatchesDoc(doc, node);
      if (!match.matched) {
        return { matched: false, lexicalRaw: 0 };
      }
      return {
        matched: true,
        lexicalRaw: tokenScore(
          node,
          match.slopUsed,
          rankingValues,
          match.quotedMatched && !match.primaryMatched
        )
      };
    }
    if (node.kind !== "op") {
      return { matched: true, lexicalRaw: 0 };
    }
    if (node.op === "NOT") {
      const child = evaluateLexicalAst(doc, node.child, rankingValues);
      return { matched: !child.matched, lexicalRaw: 0 };
    }
    if (node.op === "AND") {
      const left2 = evaluateLexicalAst(doc, node.left, rankingValues);
      if (!left2.matched) return { matched: false, lexicalRaw: 0 };
      const right2 = evaluateLexicalAst(doc, node.right, rankingValues);
      if (!right2.matched) return { matched: false, lexicalRaw: 0 };
      return { matched: true, lexicalRaw: left2.lexicalRaw + right2.lexicalRaw };
    }
    const left = evaluateLexicalAst(doc, node.left, rankingValues);
    const right = evaluateLexicalAst(doc, node.right, rankingValues);
    if (!left.matched && !right.matched) {
      return { matched: false, lexicalRaw: 0 };
    }
    if (left.matched && right.matched) {
      return { matched: true, lexicalRaw: left.lexicalRaw + right.lexicalRaw };
    }
    return left.matched ? left : right;
  }
  function computeCoverDensity(doc, orderedTerms, rankingValues) {
    const terms = orderedTerms.filter(Boolean);
    if (!terms.length) return 0;
    let density = 0;
    if (terms.length >= 2) {
      for (let index = 0; index < terms.length - 1; index += 1) {
        const phraseTerms = terms.slice(index, index + 2);
        const slop = phraseSlop(doc.tokens, phraseTerms);
        if (slop === null || slop > 2) continue;
        density += rankingValues.cover_bigram / (1 + slop);
      }
    }
    if (terms.length >= 3) {
      for (let index = 0; index < terms.length - 2; index += 1) {
        const phraseTerms = terms.slice(index, index + 3);
        const slop = phraseSlop(doc.tokens, phraseTerms);
        if (slop === null || slop > 3) continue;
        density += rankingValues.cover_trigram / (1 + slop);
      }
    }
    return density;
  }
  function applyBookmarkFolderFilter(records, folderIds) {
    if (!folderIds.length) return records;
    const wanted = new Set(folderIds.map((value) => String(value || "").trim()).filter(Boolean));
    if (!wanted.size) return records;
    return records.filter((record) => {
      const row = record || {};
      const folderId = String(readPath(row, "__bookmark_folder_id") || "").trim();
      return folderId ? wanted.has(folderId) : false;
    });
  }
  function prepareAdvancedTableSearchCorpus(records) {
    const docs = records.map((record) => getCachedSearchDoc(record));
    return {
      records,
      docs,
      rankingContext: buildRankingContext(docs),
      indexes: buildSearchCorpusIndexes(docs)
    };
  }
  function runAdvancedTableSearchPrepared(prepared, query, options2 = {}) {
    const normalizedQuery = String(query || "").trim();
    const scopedFolderIds = new Set(
      (options2.bookmarkFolderIds || []).map((value) => String(value || "").trim()).filter(Boolean)
    );
    const hasFolderScope = scopedFolderIds.size > 0;
    if (!normalizedQuery) {
      const records = hasFolderScope ? prepared.docs.filter((doc) => doc.bookmarkFolderId && scopedFolderIds.has(doc.bookmarkFolderId)).map((doc) => doc.raw) : prepared.records;
      return {
        records,
        highlightTerms: [],
        totalMatches: records.length,
        warnings: [],
        warningObjects: [],
        parsed: {
          query: "",
          lexicalExpression: "",
          filterBooleanSemantics: "global_and"
        }
      };
    }
    const rankingValues = resolveRankingFromStorage();
    const parsed = getCachedParsedSearchQuery(normalizedQuery);
    const warningObjects = [...parsed.warnings, ...buildFilterWarnings(parsed.filters)];
    const now = Date.now();
    const candidateSet = buildIndexedCandidateSet(prepared, parsed, scopedFolderIds);
    const candidateDocs = candidateSet && candidateSet.size < prepared.docs.length ? [...candidateSet].sort((a, b) => a - b).map((index) => prepared.docs[index]).filter((doc) => !!doc) : prepared.docs;
    const matches = [];
    for (const doc of candidateDocs) {
      if (hasFolderScope && (!doc.bookmarkFolderId || !scopedFolderIds.has(doc.bookmarkFolderId))) {
        continue;
      }
      let filtersOk = true;
      for (const filter of parsed.filters) {
        if (!evaluateFilter(doc, filter)) {
          filtersOk = false;
          break;
        }
      }
      if (!filtersOk) continue;
      let lexicalMatched = !parsed.hasPositiveLexical;
      let lexicalRaw = 0;
      if (parsed.lexicalAst) {
        const lexical = evaluateLexicalAst(doc, parsed.lexicalAst, rankingValues);
        lexicalMatched = lexical.matched;
        lexicalRaw = lexical.lexicalRaw;
      }
      if (!lexicalMatched) continue;
      let bm25Raw = 0;
      for (const term of parsed.positiveTerms) {
        bm25Raw += scoreTermBM25(doc, term, prepared.rankingContext);
      }
      const coverDensityRaw = computeCoverDensity(doc, parsed.orderedTerms, rankingValues);
      const weightedBm25 = bm25Raw * rankingValues.bm25;
      const weightedLexical = lexicalRaw * rankingValues.lexical;
      const weightedDensity = coverDensityRaw * rankingValues.cover_density;
      const recencyBonus = (doc.createdAtMs ? doc.createdAtMs / 1e15 : now / 1e15) * rankingValues.recency;
      const phraseTieBreak = computeExactPhraseTieBreak(doc, parsed.positiveLexicalTokens);
      const score = weightedBm25 + weightedLexical + weightedDensity + recencyBonus;
      matches.push({
        doc,
        score,
        weightedBm25,
        weightedLexical,
        weightedDensity,
        exactPhraseTerms: phraseTieBreak.exactPhraseTerms,
        exactQuotedPhraseTerms: phraseTieBreak.exactQuotedPhraseTerms,
        exactPrimaryPhraseTerms: phraseTieBreak.exactPrimaryPhraseTerms,
        exactPrimaryQuotedPhraseTerms: phraseTieBreak.exactPrimaryQuotedPhraseTerms
      });
    }
    matches.sort((left, right) => {
      if (right.score !== left.score) return right.score - left.score;
      if (right.exactPrimaryQuotedPhraseTerms !== left.exactPrimaryQuotedPhraseTerms) {
        return right.exactPrimaryQuotedPhraseTerms - left.exactPrimaryQuotedPhraseTerms;
      }
      if (right.exactPrimaryPhraseTerms !== left.exactPrimaryPhraseTerms) {
        return right.exactPrimaryPhraseTerms - left.exactPrimaryPhraseTerms;
      }
      if (right.exactQuotedPhraseTerms !== left.exactQuotedPhraseTerms) {
        return right.exactQuotedPhraseTerms - left.exactQuotedPhraseTerms;
      }
      if (right.exactPhraseTerms !== left.exactPhraseTerms) {
        return right.exactPhraseTerms - left.exactPhraseTerms;
      }
      if (right.weightedLexical !== left.weightedLexical)
        return right.weightedLexical - left.weightedLexical;
      if (right.weightedDensity !== left.weightedDensity)
        return right.weightedDensity - left.weightedDensity;
      if (right.doc.createdAtMs !== left.doc.createdAtMs)
        return right.doc.createdAtMs - left.doc.createdAtMs;
      return right.doc.id.localeCompare(left.doc.id);
    });
    const dedupedMatches = [];
    const seenSearchSignatures = /* @__PURE__ */ new Set();
    for (const entry of matches) {
      const signature = [
        entry.doc.authorId || entry.doc.authorScreenName || "",
        entry.doc.text.replace(/\s+/g, " ").trim().toLowerCase()
      ].join("::");
      if (signature !== "::" && seenSearchSignatures.has(signature)) {
        continue;
      }
      if (signature !== "::") {
        seenSearchSignatures.add(signature);
      }
      dedupedMatches.push(entry);
    }
    const resultLimit = Number(options2.limit || 0);
    const resultMatches = Number.isFinite(resultLimit) && resultLimit > 0 ? dedupedMatches.slice(0, Math.max(1, Math.floor(resultLimit))) : dedupedMatches;
    return {
      records: resultMatches.map((entry) => entry.doc.raw),
      highlightTerms: parsed.highlightTerms,
      totalMatches: dedupedMatches.length,
      warnings: warningObjects.map((item) => item.message),
      warningObjects,
      parsed: {
        query: parsed.query,
        lexicalExpression: parsed.lexicalExpression,
        filterBooleanSemantics: parsed.filterBooleanSemantics
      }
    };
  }
  function runAdvancedTableSearch(records, query, options2 = {}) {
    const scoped = applyBookmarkFolderFilter(records, options2.bookmarkFolderIds || []);
    const normalizedQuery = String(query || "").trim();
    if (!normalizedQuery) {
      return {
        records: scoped,
        highlightTerms: [],
        totalMatches: scoped.length,
        warnings: [],
        warningObjects: [],
        parsed: {
          query: "",
          lexicalExpression: "",
          filterBooleanSemantics: "global_and"
        }
      };
    }
    return runAdvancedTableSearchPrepared(prepareAdvancedTableSearchCorpus(scoped), normalizedQuery);
  }
  const jsContent = '(function() {\n  "use strict";\n  function getDefaultExportFromCjs(x2) {\n    return x2 && x2.__esModule && Object.prototype.hasOwnProperty.call(x2, "default") ? x2["default"] : x2;\n  }\n  var dayjs_min$1 = { exports: {} };\n  var dayjs_min = dayjs_min$1.exports;\n  var hasRequiredDayjs_min;\n  function requireDayjs_min() {\n    if (hasRequiredDayjs_min) return dayjs_min$1.exports;\n    hasRequiredDayjs_min = 1;\n    (function(module, exports$1) {\n      !(function(t2, e2) {\n        module.exports = e2();\n      })(dayjs_min, (function() {\n        var t2 = 1e3, e2 = 6e4, n = 36e5, r2 = "millisecond", i2 = "second", s2 = "minute", u2 = "hour", a2 = "day", o2 = "week", c2 = "month", f2 = "quarter", h2 = "year", d2 = "date", l2 = "Invalid Date", $ = /^(\\d{4})[-/]?(\\d{1,2})?[-/]?(\\d{0,2})[Tt\\s]*(\\d{1,2})?:?(\\d{1,2})?:?(\\d{1,2})?[.:]?(\\d+)?$/, y2 = /\\[([^\\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g, M = { name: "en", weekdays: "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"), months: "January_February_March_April_May_June_July_August_September_October_November_December".split("_"), ordinal: function(t3) {\n          var e3 = ["th", "st", "nd", "rd"], n2 = t3 % 100;\n          return "[" + t3 + (e3[(n2 - 20) % 10] || e3[n2] || e3[0]) + "]";\n        } }, m2 = function(t3, e3, n2) {\n          var r3 = String(t3);\n          return !r3 || r3.length >= e3 ? t3 : "" + Array(e3 + 1 - r3.length).join(n2) + t3;\n        }, v2 = { s: m2, z: function(t3) {\n          var e3 = -t3.utcOffset(), n2 = Math.abs(e3), r3 = Math.floor(n2 / 60), i3 = n2 % 60;\n          return (e3 <= 0 ? "+" : "-") + m2(r3, 2, "0") + ":" + m2(i3, 2, "0");\n        }, m: function t3(e3, n2) {\n          if (e3.date() < n2.date()) return -t3(n2, e3);\n          var r3 = 12 * (n2.year() - e3.year()) + (n2.month() - e3.month()), i3 = e3.clone().add(r3, c2), s3 = n2 - i3 < 0, u3 = e3.clone().add(r3 + (s3 ? -1 : 1), c2);\n          return +(-(r3 + (n2 - i3) / (s3 ? i3 - u3 : u3 - i3)) || 0);\n        }, a: function(t3) {\n          return t3 < 0 ? Math.ceil(t3) || 0 : Math.floor(t3);\n        }, p: function(t3) {\n          return { M: c2, y: h2, w: o2, d: a2, D: d2, h: u2, m: s2, s: i2, ms: r2, Q: f2 }[t3] || String(t3 || "").toLowerCase().replace(/s$/, "");\n        }, u: function(t3) {\n          return void 0 === t3;\n        } }, g2 = "en", D = {};\n        D[g2] = M;\n        var p2 = "$isDayjsObject", S = function(t3) {\n          return t3 instanceof _2 || !(!t3 || !t3[p2]);\n        }, w2 = function t3(e3, n2, r3) {\n          var i3;\n          if (!e3) return g2;\n          if ("string" == typeof e3) {\n            var s3 = e3.toLowerCase();\n            D[s3] && (i3 = s3), n2 && (D[s3] = n2, i3 = s3);\n            var u3 = e3.split("-");\n            if (!i3 && u3.length > 1) return t3(u3[0]);\n          } else {\n            var a3 = e3.name;\n            D[a3] = e3, i3 = a3;\n          }\n          return !r3 && i3 && (g2 = i3), i3 || !r3 && g2;\n        }, O = function(t3, e3) {\n          if (S(t3)) return t3.clone();\n          var n2 = "object" == typeof e3 ? e3 : {};\n          return n2.date = t3, n2.args = arguments, new _2(n2);\n        }, b2 = v2;\n        b2.l = w2, b2.i = S, b2.w = function(t3, e3) {\n          return O(t3, { locale: e3.$L, utc: e3.$u, x: e3.$x, $offset: e3.$offset });\n        };\n        var _2 = (function() {\n          function M2(t3) {\n            this.$L = w2(t3.locale, null, true), this.parse(t3), this.$x = this.$x || t3.x || {}, this[p2] = true;\n          }\n          var m3 = M2.prototype;\n          return m3.parse = function(t3) {\n            this.$d = (function(t4) {\n              var e3 = t4.date, n2 = t4.utc;\n              if (null === e3) return /* @__PURE__ */ new Date(NaN);\n              if (b2.u(e3)) return /* @__PURE__ */ new Date();\n              if (e3 instanceof Date) return new Date(e3);\n              if ("string" == typeof e3 && !/Z$/i.test(e3)) {\n                var r3 = e3.match($);\n                if (r3) {\n                  var i3 = r3[2] - 1 || 0, s3 = (r3[7] || "0").substring(0, 3);\n                  return n2 ? new Date(Date.UTC(r3[1], i3, r3[3] || 1, r3[4] || 0, r3[5] || 0, r3[6] || 0, s3)) : new Date(r3[1], i3, r3[3] || 1, r3[4] || 0, r3[5] || 0, r3[6] || 0, s3);\n                }\n              }\n              return new Date(e3);\n            })(t3), this.init();\n          }, m3.init = function() {\n            var t3 = this.$d;\n            this.$y = t3.getFullYear(), this.$M = t3.getMonth(), this.$D = t3.getDate(), this.$W = t3.getDay(), this.$H = t3.getHours(), this.$m = t3.getMinutes(), this.$s = t3.getSeconds(), this.$ms = t3.getMilliseconds();\n          }, m3.$utils = function() {\n            return b2;\n          }, m3.isValid = function() {\n            return !(this.$d.toString() === l2);\n          }, m3.isSame = function(t3, e3) {\n            var n2 = O(t3);\n            return this.startOf(e3) <= n2 && n2 <= this.endOf(e3);\n          }, m3.isAfter = function(t3, e3) {\n            return O(t3) < this.startOf(e3);\n          }, m3.isBefore = function(t3, e3) {\n            return this.endOf(e3) < O(t3);\n          }, m3.$g = function(t3, e3, n2) {\n            return b2.u(t3) ? this[e3] : this.set(n2, t3);\n          }, m3.unix = function() {\n            return Math.floor(this.valueOf() / 1e3);\n          }, m3.valueOf = function() {\n            return this.$d.getTime();\n          }, m3.startOf = function(t3, e3) {\n            var n2 = this, r3 = !!b2.u(e3) || e3, f3 = b2.p(t3), l3 = function(t4, e4) {\n              var i3 = b2.w(n2.$u ? Date.UTC(n2.$y, e4, t4) : new Date(n2.$y, e4, t4), n2);\n              return r3 ? i3 : i3.endOf(a2);\n            }, $2 = function(t4, e4) {\n              return b2.w(n2.toDate()[t4].apply(n2.toDate("s"), (r3 ? [0, 0, 0, 0] : [23, 59, 59, 999]).slice(e4)), n2);\n            }, y3 = this.$W, M3 = this.$M, m4 = this.$D, v3 = "set" + (this.$u ? "UTC" : "");\n            switch (f3) {\n              case h2:\n                return r3 ? l3(1, 0) : l3(31, 11);\n              case c2:\n                return r3 ? l3(1, M3) : l3(0, M3 + 1);\n              case o2:\n                var g3 = this.$locale().weekStart || 0, D2 = (y3 < g3 ? y3 + 7 : y3) - g3;\n                return l3(r3 ? m4 - D2 : m4 + (6 - D2), M3);\n              case a2:\n              case d2:\n                return $2(v3 + "Hours", 0);\n              case u2:\n                return $2(v3 + "Minutes", 1);\n              case s2:\n                return $2(v3 + "Seconds", 2);\n              case i2:\n                return $2(v3 + "Milliseconds", 3);\n              default:\n                return this.clone();\n            }\n          }, m3.endOf = function(t3) {\n            return this.startOf(t3, false);\n          }, m3.$set = function(t3, e3) {\n            var n2, o3 = b2.p(t3), f3 = "set" + (this.$u ? "UTC" : ""), l3 = (n2 = {}, n2[a2] = f3 + "Date", n2[d2] = f3 + "Date", n2[c2] = f3 + "Month", n2[h2] = f3 + "FullYear", n2[u2] = f3 + "Hours", n2[s2] = f3 + "Minutes", n2[i2] = f3 + "Seconds", n2[r2] = f3 + "Milliseconds", n2)[o3], $2 = o3 === a2 ? this.$D + (e3 - this.$W) : e3;\n            if (o3 === c2 || o3 === h2) {\n              var y3 = this.clone().set(d2, 1);\n              y3.$d[l3]($2), y3.init(), this.$d = y3.set(d2, Math.min(this.$D, y3.daysInMonth())).$d;\n            } else l3 && this.$d[l3]($2);\n            return this.init(), this;\n          }, m3.set = function(t3, e3) {\n            return this.clone().$set(t3, e3);\n          }, m3.get = function(t3) {\n            return this[b2.p(t3)]();\n          }, m3.add = function(r3, f3) {\n            var d3, l3 = this;\n            r3 = Number(r3);\n            var $2 = b2.p(f3), y3 = function(t3) {\n              var e3 = O(l3);\n              return b2.w(e3.date(e3.date() + Math.round(t3 * r3)), l3);\n            };\n            if ($2 === c2) return this.set(c2, this.$M + r3);\n            if ($2 === h2) return this.set(h2, this.$y + r3);\n            if ($2 === a2) return y3(1);\n            if ($2 === o2) return y3(7);\n            var M3 = (d3 = {}, d3[s2] = e2, d3[u2] = n, d3[i2] = t2, d3)[$2] || 1, m4 = this.$d.getTime() + r3 * M3;\n            return b2.w(m4, this);\n          }, m3.subtract = function(t3, e3) {\n            return this.add(-1 * t3, e3);\n          }, m3.format = function(t3) {\n            var e3 = this, n2 = this.$locale();\n            if (!this.isValid()) return n2.invalidDate || l2;\n            var r3 = t3 || "YYYY-MM-DDTHH:mm:ssZ", i3 = b2.z(this), s3 = this.$H, u3 = this.$m, a3 = this.$M, o3 = n2.weekdays, c3 = n2.months, f3 = n2.meridiem, h3 = function(t4, n3, i4, s4) {\n              return t4 && (t4[n3] || t4(e3, r3)) || i4[n3].slice(0, s4);\n            }, d3 = function(t4) {\n              return b2.s(s3 % 12 || 12, t4, "0");\n            }, $2 = f3 || function(t4, e4, n3) {\n              var r4 = t4 < 12 ? "AM" : "PM";\n              return n3 ? r4.toLowerCase() : r4;\n            };\n            return r3.replace(y2, (function(t4, r4) {\n              return r4 || (function(t5) {\n                switch (t5) {\n                  case "YY":\n                    return String(e3.$y).slice(-2);\n                  case "YYYY":\n                    return b2.s(e3.$y, 4, "0");\n                  case "M":\n                    return a3 + 1;\n                  case "MM":\n                    return b2.s(a3 + 1, 2, "0");\n                  case "MMM":\n                    return h3(n2.monthsShort, a3, c3, 3);\n                  case "MMMM":\n                    return h3(c3, a3);\n                  case "D":\n                    return e3.$D;\n                  case "DD":\n                    return b2.s(e3.$D, 2, "0");\n                  case "d":\n                    return String(e3.$W);\n                  case "dd":\n                    return h3(n2.weekdaysMin, e3.$W, o3, 2);\n                  case "ddd":\n                    return h3(n2.weekdaysShort, e3.$W, o3, 3);\n                  case "dddd":\n                    return o3[e3.$W];\n                  case "H":\n                    return String(s3);\n                  case "HH":\n                    return b2.s(s3, 2, "0");\n                  case "h":\n                    return d3(1);\n                  case "hh":\n                    return d3(2);\n                  case "a":\n                    return $2(s3, u3, true);\n                  case "A":\n                    return $2(s3, u3, false);\n                  case "m":\n                    return String(u3);\n                  case "mm":\n                    return b2.s(u3, 2, "0");\n                  case "s":\n                    return String(e3.$s);\n                  case "ss":\n                    return b2.s(e3.$s, 2, "0");\n                  case "SSS":\n                    return b2.s(e3.$ms, 3, "0");\n                  case "Z":\n                    return i3;\n                }\n                return null;\n              })(t4) || i3.replace(":", "");\n            }));\n          }, m3.utcOffset = function() {\n            return 15 * -Math.round(this.$d.getTimezoneOffset() / 15);\n          }, m3.diff = function(r3, d3, l3) {\n            var $2, y3 = this, M3 = b2.p(d3), m4 = O(r3), v3 = (m4.utcOffset() - this.utcOffset()) * e2, g3 = this - m4, D2 = function() {\n              return b2.m(y3, m4);\n            };\n            switch (M3) {\n              case h2:\n                $2 = D2() / 12;\n                break;\n              case c2:\n                $2 = D2();\n                break;\n              case f2:\n                $2 = D2() / 3;\n                break;\n              case o2:\n                $2 = (g3 - v3) / 6048e5;\n                break;\n              case a2:\n                $2 = (g3 - v3) / 864e5;\n                break;\n              case u2:\n                $2 = g3 / n;\n                break;\n              case s2:\n                $2 = g3 / e2;\n                break;\n              case i2:\n                $2 = g3 / t2;\n                break;\n              default:\n                $2 = g3;\n            }\n            return l3 ? $2 : b2.a($2);\n          }, m3.daysInMonth = function() {\n            return this.endOf(c2).$D;\n          }, m3.$locale = function() {\n            return D[this.$L];\n          }, m3.locale = function(t3, e3) {\n            if (!t3) return this.$L;\n            var n2 = this.clone(), r3 = w2(t3, e3, true);\n            return r3 && (n2.$L = r3), n2;\n          }, m3.clone = function() {\n            return b2.w(this.$d, this);\n          }, m3.toDate = function() {\n            return new Date(this.valueOf());\n          }, m3.toJSON = function() {\n            return this.isValid() ? this.toISOString() : null;\n          }, m3.toISOString = function() {\n            return this.$d.toISOString();\n          }, m3.toString = function() {\n            return this.$d.toUTCString();\n          }, M2;\n        })(), k2 = _2.prototype;\n        return O.prototype = k2, [["$ms", r2], ["$s", i2], ["$m", s2], ["$H", u2], ["$W", a2], ["$M", c2], ["$y", h2], ["$D", d2]].forEach((function(t3) {\n          k2[t3[1]] = function(e3) {\n            return this.$g(e3, t3[0], t3[1]);\n          };\n        })), O.extend = function(t3, e3) {\n          return t3.$i || (t3(e3, _2, O), t3.$i = true), O;\n        }, O.locale = w2, O.isDayjs = S, O.unix = function(t3) {\n          return O(1e3 * t3);\n        }, O.en = D[g2], O.Ls = D, O.p = {}, O;\n      }));\n    })(dayjs_min$1);\n    return dayjs_min$1.exports;\n  }\n  var dayjs_minExports = requireDayjs_min();\n  var dayjs = /* @__PURE__ */ getDefaultExportFromCjs(dayjs_minExports);\n  var l$3, u$2;\n  l$3 = { __e: function(n, l2, t2, u2) {\n    for (var i2, r2, o2; l2 = l2.__; ) if ((i2 = l2.__c) && !i2.__) try {\n      if ((r2 = i2.constructor) && null != r2.getDerivedStateFromError && (i2.setState(r2.getDerivedStateFromError(n)), o2 = i2.__d), null != i2.componentDidCatch && (i2.componentDidCatch(n, u2 || {}), o2 = i2.__d), o2) return i2.__E = i2;\n    } catch (l3) {\n      n = l3;\n    }\n    throw n;\n  } }, u$2 = function(n) {\n    return null != n && null == n.constructor;\n  }, "function" == typeof Promise ? Promise.prototype.then.bind(Promise.resolve()) : setTimeout;\n  var t$1, r$1, u$1, i$1, o$1 = 0, f$1 = [], c$1 = l$3, e$1 = c$1.__b, a$1 = c$1.__r, v$1 = c$1.diffed, l$2 = c$1.__c, m$1 = c$1.unmount, s$1 = c$1.__;\n  function p$2(n, t2) {\n    c$1.__h && c$1.__h(r$1, n, o$1 || t2), o$1 = 0;\n    var u2 = r$1.__H || (r$1.__H = { __: [], __h: [] });\n    return n >= u2.__.length && u2.__.push({}), u2.__[n];\n  }\n  function T(n, r2) {\n    var u2 = p$2(t$1++, 7);\n    return C(u2.__H, r2) && (u2.__ = n(), u2.__H = r2, u2.__h = n), u2.__;\n  }\n  function j() {\n    for (var n; n = f$1.shift(); ) if (n.__P && n.__H) try {\n      n.__H.__h.forEach(z), n.__H.__h.forEach(B), n.__H.__h = [];\n    } catch (t2) {\n      n.__H.__h = [], c$1.__e(t2, n.__v);\n    }\n  }\n  c$1.__b = function(n) {\n    r$1 = null, e$1 && e$1(n);\n  }, c$1.__ = function(n, t2) {\n    n && t2.__k && t2.__k.__m && (n.__m = t2.__k.__m), s$1 && s$1(n, t2);\n  }, c$1.__r = function(n) {\n    a$1 && a$1(n), t$1 = 0;\n    var i2 = (r$1 = n.__c).__H;\n    i2 && (u$1 === r$1 ? (i2.__h = [], r$1.__h = [], i2.__.forEach(function(n2) {\n      n2.__N && (n2.__ = n2.__N), n2.u = n2.__N = void 0;\n    })) : (i2.__h.forEach(z), i2.__h.forEach(B), i2.__h = [], t$1 = 0)), u$1 = r$1;\n  }, c$1.diffed = function(n) {\n    v$1 && v$1(n);\n    var t2 = n.__c;\n    t2 && t2.__H && (t2.__H.__h.length && (1 !== f$1.push(t2) && i$1 === c$1.requestAnimationFrame || ((i$1 = c$1.requestAnimationFrame) || w$1)(j)), t2.__H.__.forEach(function(n2) {\n      n2.u && (n2.__H = n2.u), n2.u = void 0;\n    })), u$1 = r$1 = null;\n  }, c$1.__c = function(n, t2) {\n    t2.some(function(n2) {\n      try {\n        n2.__h.forEach(z), n2.__h = n2.__h.filter(function(n3) {\n          return !n3.__ || B(n3);\n        });\n      } catch (r2) {\n        t2.some(function(n3) {\n          n3.__h && (n3.__h = []);\n        }), t2 = [], c$1.__e(r2, n2.__v);\n      }\n    }), l$2 && l$2(n, t2);\n  }, c$1.unmount = function(n) {\n    m$1 && m$1(n);\n    var t2, r2 = n.__c;\n    r2 && r2.__H && (r2.__H.__.forEach(function(n2) {\n      try {\n        z(n2);\n      } catch (n3) {\n        t2 = n3;\n      }\n    }), r2.__H = void 0, t2 && c$1.__e(t2, r2.__v));\n  };\n  var k = "function" == typeof requestAnimationFrame;\n  function w$1(n) {\n    var t2, r2 = function() {\n      clearTimeout(u2), k && cancelAnimationFrame(t2), setTimeout(n);\n    }, u2 = setTimeout(r2, 100);\n    k && (t2 = requestAnimationFrame(r2));\n  }\n  function z(n) {\n    var t2 = r$1, u2 = n.__c;\n    "function" == typeof u2 && (n.__c = void 0, u2()), r$1 = t2;\n  }\n  function B(n) {\n    var t2 = r$1;\n    n.__c = n.__(), r$1 = t2;\n  }\n  function C(n, t2) {\n    return !n || n.length !== t2.length || t2.some(function(t3, r2) {\n      return t3 !== n[r2];\n    });\n  }\n  var i = Symbol.for("preact-signals");\n  function t() {\n    if (!(s > 1)) {\n      var i2, t2 = false;\n      while (void 0 !== h$1) {\n        var r2 = h$1;\n        h$1 = void 0;\n        f++;\n        while (void 0 !== r2) {\n          var o2 = r2.o;\n          r2.o = void 0;\n          r2.f &= -3;\n          if (!(8 & r2.f) && c(r2)) try {\n            r2.c();\n          } catch (r3) {\n            if (!t2) {\n              i2 = r3;\n              t2 = true;\n            }\n          }\n          r2 = o2;\n        }\n      }\n      f = 0;\n      s--;\n      if (t2) throw i2;\n    } else s--;\n  }\n  function r(i2) {\n    if (s > 0) return i2();\n    s++;\n    try {\n      return i2();\n    } finally {\n      t();\n    }\n  }\n  var o = void 0;\n  var h$1 = void 0, s = 0, f = 0, v = 0;\n  function e(i2) {\n    if (void 0 !== o) {\n      var t2 = i2.n;\n      if (void 0 === t2 || t2.t !== o) {\n        t2 = { i: 0, S: i2, p: o.s, n: void 0, t: o, e: void 0, x: void 0, r: t2 };\n        if (void 0 !== o.s) o.s.n = t2;\n        o.s = t2;\n        i2.n = t2;\n        if (32 & o.f) i2.S(t2);\n        return t2;\n      } else if (-1 === t2.i) {\n        t2.i = 0;\n        if (void 0 !== t2.n) {\n          t2.n.p = t2.p;\n          if (void 0 !== t2.p) t2.p.n = t2.n;\n          t2.p = o.s;\n          t2.n = void 0;\n          o.s.n = t2;\n          o.s = t2;\n        }\n        return t2;\n      }\n    }\n  }\n  function u(i2) {\n    this.v = i2;\n    this.i = 0;\n    this.n = void 0;\n    this.t = void 0;\n  }\n  u.prototype.brand = i;\n  u.prototype.h = function() {\n    return true;\n  };\n  u.prototype.S = function(i2) {\n    if (this.t !== i2 && void 0 === i2.e) {\n      i2.x = this.t;\n      if (void 0 !== this.t) this.t.e = i2;\n      this.t = i2;\n    }\n  };\n  u.prototype.U = function(i2) {\n    if (void 0 !== this.t) {\n      var t2 = i2.e, r2 = i2.x;\n      if (void 0 !== t2) {\n        t2.x = r2;\n        i2.e = void 0;\n      }\n      if (void 0 !== r2) {\n        r2.e = t2;\n        i2.x = void 0;\n      }\n      if (i2 === this.t) this.t = r2;\n    }\n  };\n  u.prototype.subscribe = function(i2) {\n    var t2 = this;\n    return E(function() {\n      var r2 = t2.value, n = o;\n      o = void 0;\n      try {\n        i2(r2);\n      } finally {\n        o = n;\n      }\n    });\n  };\n  u.prototype.valueOf = function() {\n    return this.value;\n  };\n  u.prototype.toString = function() {\n    return this.value + "";\n  };\n  u.prototype.toJSON = function() {\n    return this.value;\n  };\n  u.prototype.peek = function() {\n    var i2 = o;\n    o = void 0;\n    try {\n      return this.value;\n    } finally {\n      o = i2;\n    }\n  };\n  Object.defineProperty(u.prototype, "value", { get: function() {\n    var i2 = e(this);\n    if (void 0 !== i2) i2.i = this.i;\n    return this.v;\n  }, set: function(i2) {\n    if (i2 !== this.v) {\n      if (f > 100) throw new Error("Cycle detected");\n      this.v = i2;\n      this.i++;\n      v++;\n      s++;\n      try {\n        for (var r2 = this.t; void 0 !== r2; r2 = r2.x) r2.t.N();\n      } finally {\n        t();\n      }\n    }\n  } });\n  function d$1(i2) {\n    return new u(i2);\n  }\n  function c(i2) {\n    for (var t2 = i2.s; void 0 !== t2; t2 = t2.n) if (t2.S.i !== t2.i || !t2.S.h() || t2.S.i !== t2.i) return true;\n    return false;\n  }\n  function a(i2) {\n    for (var t2 = i2.s; void 0 !== t2; t2 = t2.n) {\n      var r2 = t2.S.n;\n      if (void 0 !== r2) t2.r = r2;\n      t2.S.n = t2;\n      t2.i = -1;\n      if (void 0 === t2.n) {\n        i2.s = t2;\n        break;\n      }\n    }\n  }\n  function l$1(i2) {\n    var t2 = i2.s, r2 = void 0;\n    while (void 0 !== t2) {\n      var o2 = t2.p;\n      if (-1 === t2.i) {\n        t2.S.U(t2);\n        if (void 0 !== o2) o2.n = t2.n;\n        if (void 0 !== t2.n) t2.n.p = o2;\n      } else r2 = t2;\n      t2.S.n = t2.r;\n      if (void 0 !== t2.r) t2.r = void 0;\n      t2 = o2;\n    }\n    i2.s = r2;\n  }\n  function y$1(i2) {\n    u.call(this, void 0);\n    this.x = i2;\n    this.s = void 0;\n    this.g = v - 1;\n    this.f = 4;\n  }\n  (y$1.prototype = new u()).h = function() {\n    this.f &= -3;\n    if (1 & this.f) return false;\n    if (32 == (36 & this.f)) return true;\n    this.f &= -5;\n    if (this.g === v) return true;\n    this.g = v;\n    this.f |= 1;\n    if (this.i > 0 && !c(this)) {\n      this.f &= -2;\n      return true;\n    }\n    var i2 = o;\n    try {\n      a(this);\n      o = this;\n      var t2 = this.x();\n      if (16 & this.f || this.v !== t2 || 0 === this.i) {\n        this.v = t2;\n        this.f &= -17;\n        this.i++;\n      }\n    } catch (i3) {\n      this.v = i3;\n      this.f |= 16;\n      this.i++;\n    }\n    o = i2;\n    l$1(this);\n    this.f &= -2;\n    return true;\n  };\n  y$1.prototype.S = function(i2) {\n    if (void 0 === this.t) {\n      this.f |= 36;\n      for (var t2 = this.s; void 0 !== t2; t2 = t2.n) t2.S.S(t2);\n    }\n    u.prototype.S.call(this, i2);\n  };\n  y$1.prototype.U = function(i2) {\n    if (void 0 !== this.t) {\n      u.prototype.U.call(this, i2);\n      if (void 0 === this.t) {\n        this.f &= -33;\n        for (var t2 = this.s; void 0 !== t2; t2 = t2.n) t2.S.U(t2);\n      }\n    }\n  };\n  y$1.prototype.N = function() {\n    if (!(2 & this.f)) {\n      this.f |= 6;\n      for (var i2 = this.t; void 0 !== i2; i2 = i2.x) i2.t.N();\n    }\n  };\n  Object.defineProperty(y$1.prototype, "value", { get: function() {\n    if (1 & this.f) throw new Error("Cycle detected");\n    var i2 = e(this);\n    this.h();\n    if (void 0 !== i2) i2.i = this.i;\n    if (16 & this.f) throw this.v;\n    return this.v;\n  } });\n  function w(i2) {\n    return new y$1(i2);\n  }\n  function _$1(i2) {\n    var r2 = i2.u;\n    i2.u = void 0;\n    if ("function" == typeof r2) {\n      s++;\n      var n = o;\n      o = void 0;\n      try {\n        r2();\n      } catch (t2) {\n        i2.f &= -2;\n        i2.f |= 8;\n        g(i2);\n        throw t2;\n      } finally {\n        o = n;\n        t();\n      }\n    }\n  }\n  function g(i2) {\n    for (var t2 = i2.s; void 0 !== t2; t2 = t2.n) t2.S.U(t2);\n    i2.x = void 0;\n    i2.s = void 0;\n    _$1(i2);\n  }\n  function p$1(i2) {\n    if (o !== this) throw new Error("Out-of-order effect");\n    l$1(this);\n    o = i2;\n    this.f &= -2;\n    if (8 & this.f) g(this);\n    t();\n  }\n  function b$1(i2) {\n    this.x = i2;\n    this.u = void 0;\n    this.s = void 0;\n    this.o = void 0;\n    this.f = 32;\n  }\n  b$1.prototype.c = function() {\n    var i2 = this.S();\n    try {\n      if (8 & this.f) return;\n      if (void 0 === this.x) return;\n      var t2 = this.x();\n      if ("function" == typeof t2) this.u = t2;\n    } finally {\n      i2();\n    }\n  };\n  b$1.prototype.S = function() {\n    if (1 & this.f) throw new Error("Cycle detected");\n    this.f |= 1;\n    this.f &= -9;\n    _$1(this);\n    a(this);\n    s++;\n    var i2 = o;\n    o = this;\n    return p$1.bind(this, i2);\n  };\n  b$1.prototype.N = function() {\n    if (!(2 & this.f)) {\n      this.f |= 2;\n      this.o = h$1;\n      h$1 = this;\n    }\n  };\n  b$1.prototype.d = function() {\n    this.f |= 8;\n    if (!(1 & this.f)) g(this);\n  };\n  function E(i2) {\n    var t2 = new b$1(i2);\n    try {\n      t2.c();\n    } catch (i3) {\n      t2.d();\n      throw i3;\n    }\n    return t2.d.bind(t2);\n  }\n  var l;\n  function d(i2, r2) {\n    l$3[i2] = r2.bind(null, l$3[i2] || function() {\n    });\n  }\n  function h(i2) {\n    if (l) l();\n    l = i2 && i2.S();\n  }\n  function p(i2) {\n    var n = this, f2 = i2.data, o2 = useSignal(f2);\n    o2.value = f2;\n    var u2 = T(function() {\n      var i3 = n, t2 = n.__v;\n      while (t2 = t2.__) if (t2.__c) {\n        t2.__c.__$f |= 4;\n        break;\n      }\n      var f3 = w(function() {\n        var i4 = o2.value.value;\n        return 0 === i4 ? 0 : true === i4 ? "" : i4 || "";\n      }), c2 = w(function() {\n        var i4;\n        return u$2(f3.value) || 3 !== (null == (i4 = n.base) ? void 0 : i4.nodeType);\n      });\n      n.__$u.c = function() {\n        var i4;\n        if (!u$2(u2.peek()) && 3 === (null == (i4 = n.base) ? void 0 : i4.nodeType)) n.base.data = u2.peek();\n        else {\n          n.__$f |= 1;\n          n.setState({});\n        }\n      };\n      E(function() {\n        if (!m) m = this.N;\n        this.N = A;\n        if (c2.value && i3.base) i3.base.data = f3.value;\n      });\n      return f3;\n    }, []);\n    return u2.value;\n  }\n  p.displayName = "_st";\n  Object.defineProperties(u.prototype, { constructor: { configurable: true, value: void 0 }, type: { configurable: true, value: p }, props: { configurable: true, get: function() {\n    return { data: this };\n  } }, __b: { configurable: true, value: 1 } });\n  d("__b", function(i2, n) {\n    if ("string" == typeof n.type) {\n      var r2, t2 = n.props;\n      for (var f2 in t2) if ("children" !== f2) {\n        var o2 = t2[f2];\n        if (o2 instanceof u) {\n          if (!r2) n.__np = r2 = {};\n          r2[f2] = o2;\n          t2[f2] = o2.peek();\n        }\n      }\n    }\n    i2(n);\n  });\n  d("__r", function(i2, n) {\n    h();\n    var r2, t2 = n.__c;\n    if (t2) {\n      t2.__$f &= -2;\n      if (void 0 === (r2 = t2.__$u)) t2.__$u = r2 = (function(i3) {\n        var n2;\n        E(function() {\n          n2 = this;\n        });\n        n2.c = function() {\n          t2.__$f |= 1;\n          t2.setState({});\n        };\n        return n2;\n      })();\n    }\n    h(r2);\n    i2(n);\n  });\n  d("__e", function(i2, n, r2, t2) {\n    h();\n    i2(n, r2, t2);\n  });\n  d("diffed", function(i2, n) {\n    h();\n    var r2;\n    if ("string" == typeof n.type && (r2 = n.__e)) {\n      var t2 = n.__np, f2 = n.props;\n      if (t2) {\n        var o2 = r2.U;\n        if (o2) for (var u2 in o2) {\n          var e2 = o2[u2];\n          if (void 0 !== e2 && !(u2 in t2)) {\n            e2.d();\n            o2[u2] = void 0;\n          }\n        }\n        else r2.U = o2 = {};\n        for (var a2 in t2) {\n          var c2 = o2[a2], v2 = t2[a2];\n          if (void 0 === c2) {\n            c2 = _(r2, a2, v2, f2);\n            o2[a2] = c2;\n          } else c2.o(v2, f2);\n        }\n      }\n    }\n    i2(n);\n  });\n  function _(i2, n, r2, t2) {\n    var f2 = n in i2 && void 0 === i2.ownerSVGElement, o2 = d$1(r2);\n    return { o: function(i3, n2) {\n      o2.value = i3;\n      t2 = n2;\n    }, d: E(function() {\n      if (!m) m = this.N;\n      this.N = A;\n      var r3 = o2.value.value;\n      if (t2[n] !== r3) {\n        t2[n] = r3;\n        if (f2) i2[n] = r3;\n        else if (r3) i2.setAttribute(n, r3);\n        else i2.removeAttribute(n);\n      }\n    }) };\n  }\n  d("unmount", function(i2, n) {\n    if ("string" == typeof n.type) {\n      var r2 = n.__e;\n      if (r2) {\n        var t2 = r2.U;\n        if (t2) {\n          r2.U = void 0;\n          for (var f2 in t2) {\n            var o2 = t2[f2];\n            if (o2) o2.d();\n          }\n        }\n      }\n    } else {\n      var u2 = n.__c;\n      if (u2) {\n        var e2 = u2.__$u;\n        if (e2) {\n          u2.__$u = void 0;\n          e2.d();\n        }\n      }\n    }\n    i2(n);\n  });\n  d("__h", function(i2, n, r2, t2) {\n    if (t2 < 3 || 9 === t2) n.__$f |= 2;\n    i2(n, r2, t2);\n  });\n  function useSignal(i2) {\n    return T(function() {\n      return d$1(i2);\n    }, []);\n  }\n  var m, b = [], y = function(i2) {\n    queueMicrotask(function() {\n      queueMicrotask(i2);\n    });\n  };\n  function x() {\n    r(function() {\n      var i2;\n      while (i2 = b.shift()) m.call(i2);\n    });\n  }\n  function A() {\n    if (1 === b.push(this)) (l$3.requestAnimationFrame || y)(x);\n  }\n  function parseTwitterDateTime(str) {\n    if (!str) {\n      return dayjs(0);\n    }\n    const trimmed = str.replace(/^\\w+ (.*)$/, "$1");\n    return dayjs(trimmed, "MMM DD HH:mm:ss ZZ YYYY", "en");\n  }\n  const field_path_pattern = "^[a-zA-Z0-9_.]+$";\n  const known_filter_keys = ["from", "from_id", "author_id", "to", "to_id", "in_reply_to_id", "lang", "since", "until", "since_time", "until_time", "since_id", "max_id", "conversation_id", "min_faves", "min_likes", "min_retweets", "min_replies", "min_bookmarks", "url", "domain", "id", "source", "card_name", "filter", "include", "is", "has", "bookmark_folder", "folder", "route", "mention", "hashtag", "cashtag"];\n  const non_highlight_filters = ["since", "until", "since_time", "until_time", "since_id", "max_id", "min_faves", "min_likes", "min_retweets", "min_replies", "min_bookmarks", "id", "from_id", "author_id", "to_id", "in_reply_to_id", "conversation_id"];\n  const free_text = { "min_content_term_length": 2, "full_run_exact_min_terms": 2, "stop_terms": ["a", "an", "the", "and", "or", "but", "if", "then", "than", "to", "of", "in", "on", "for", "with", "by", "at", "from", "into", "onto", "as", "is", "are", "was", "were", "be", "been", "being", "do", "does", "did", "done", "have", "has", "had", "can", "could", "should", "would", "will", "just", "about", "what", "when", "where", "why", "how", "who", "whom", "whose", "this", "that", "these", "those", "it", "its", "my", "your", "our", "their", "his", "her", "me", "you", "we", "they", "he", "she", "them", "us", "post", "tweet", "too"], "bigram_slop": 2, "trigram_slop": 3, "fourgram_slop": 4, "bigram_boost": 20, "trigram_boost": 60, "fourgram_boost": 140, "full_run_exact_boost": 320 };\n  const prefix = { "min_term_length": 3, "max_expansions": 128 };\n  const fuzzy = { "min_term_length": 5, "max_edit_distance": 1, "prefix_root_length": 4, "max_expansions": 64 };\n  var contract = {\n    field_path_pattern,\n    known_filter_keys,\n    non_highlight_filters,\n    free_text,\n    prefix,\n    fuzzy\n  };\n  const SEARCH_FIELD_PATH_PATTERN = new RegExp(contract.field_path_pattern);\n  const SEARCH_KNOWN_FILTER_KEYS = new Set(contract.known_filter_keys);\n  const SEARCH_NON_HIGHLIGHT_FILTERS = new Set(contract.non_highlight_filters);\n  const SEARCH_FREE_TEXT = contract.free_text;\n  const SEARCH_PREFIX = contract.prefix;\n  const SEARCH_FUZZY = contract.fuzzy;\n  const SEARCH_FREE_TEXT_STOP_TERMS = new Set(\n    (contract.free_text.stop_terms || []).map(\n      (value) => String(value || "").trim().toLowerCase()\n    ).filter(Boolean)\n  );\n  function isRawLexicalToken(token) {\n    return token.kind === "term" || token.kind === "phrase";\n  }\n  const TERM_TOKEN_PATTERN$1 = /[\\p{L}\\p{N}_]+(?:[\'’][\\p{L}\\p{N}_]+)*/gu;\n  const MAX_HIGHLIGHT_TERMS = 32;\n  const SEARCH_FREE_TEXT_MIN_CONTENT_TERM_LENGTH = Math.max(\n    1,\n    Number(SEARCH_FREE_TEXT.min_content_term_length)\n  );\n  const SEARCH_FREE_TEXT_FULL_RUN_EXACT_MIN_TERMS = Math.max(\n    2,\n    Number(SEARCH_FREE_TEXT.full_run_exact_min_terms)\n  );\n  const SEARCH_PREFIX_MIN_TERM_LENGTH$1 = Math.max(1, Number(SEARCH_PREFIX.min_term_length));\n  const SEARCH_FUZZY_MIN_TERM_LENGTH$1 = Math.max(1, Number(SEARCH_FUZZY.min_term_length));\n  function warning(code, message, token = "") {\n    return {\n      code,\n      message,\n      token: token || void 0,\n      severity: "warn"\n    };\n  }\n  function clampBoost(value) {\n    const parsed = Number(value);\n    if (!Number.isFinite(parsed)) return 1;\n    return Math.max(0.05, Math.min(100, parsed));\n  }\n  function tokenizeText$1(value) {\n    if (!value) return [];\n    const matches = value.toLowerCase().match(TERM_TOKEN_PATTERN$1);\n    return matches ? matches.map((token) => token.replace(/[\'’]/g, "")).filter(Boolean) : [];\n  }\n  function isLowSignalFreeTextTerm(term) {\n    const normalized = String(term || "").trim().toLowerCase();\n    if (!normalized) return true;\n    if (normalized.length < SEARCH_FREE_TEXT_MIN_CONTENT_TERM_LENGTH) {\n      return true;\n    }\n    return SEARCH_FREE_TEXT_STOP_TERMS.has(normalized);\n  }\n  function getFreeTextRunContentTerms(tokens) {\n    const allTerms = tokens.flatMap((token) => tokenizeText$1(token.value));\n    const contentTerms = allTerms.filter((term) => !isLowSignalFreeTextTerm(term));\n    return contentTerms.length ? contentTerms : allTerms;\n  }\n  function getFreeTextRunSingletonTokens(tokens) {\n    const contentTerms = new Set(getFreeTextRunContentTerms(tokens));\n    if (!contentTerms.size) {\n      return tokens;\n    }\n    const filtered = tokens.filter(\n      (token) => contentTerms.has(\n        String(token.value || "").trim().toLowerCase()\n      )\n    );\n    return filtered.length ? filtered : tokens;\n  }\n  function countContentTermsInFreeTextWindow(tokens) {\n    let count = 0;\n    for (const token of tokens) {\n      for (const term of tokenizeText$1(token.value)) {\n        if (!isLowSignalFreeTextTerm(term)) {\n          count += 1;\n        }\n      }\n    }\n    return count;\n  }\n  function normalizeTermValue(value) {\n    return tokenizeText$1(value).join(" ").trim();\n  }\n  function roundBoost(value) {\n    if (!Number.isFinite(value)) return 1;\n    return Math.round(value * 100) / 100;\n  }\n  function parseNumericSuffix(raw, separator) {\n    const index = raw.lastIndexOf(separator);\n    if (index <= 0) {\n      return { base: raw, value: null };\n    }\n    const candidate = raw.slice(index + 1);\n    if (!candidate || !/^\\d+(?:\\.\\d+)?$/.test(candidate)) {\n      return { base: raw, value: null };\n    }\n    return { base: raw.slice(0, index), value: candidate };\n  }\n  function tokenizeQuery(query) {\n    const text = String(query || "");\n    const out = [];\n    let index = 0;\n    while (index < text.length) {\n      while (index < text.length && /\\s/.test(text[index] ?? "")) {\n        index += 1;\n      }\n      if (index >= text.length) break;\n      const ch = text[index] ?? "";\n      if (ch === "(") {\n        out.push({ kind: "lparen" });\n        index += 1;\n        continue;\n      }\n      if (ch === ")") {\n        out.push({ kind: "rparen" });\n        index += 1;\n        continue;\n      }\n      let negated = false;\n      if (ch === "-") {\n        negated = true;\n        index += 1;\n        while (index < text.length && /\\s/.test(text[index] ?? "")) {\n          index += 1;\n        }\n        if (index >= text.length) break;\n        if ((text[index] ?? "") === "(") {\n          out.push({ kind: "op", op: "NOT" });\n          continue;\n        }\n      }\n      let explicitField;\n      const fieldScanStart = index;\n      while (index < text.length && /[a-zA-Z0-9_.]/.test(text[index] ?? "")) {\n        index += 1;\n      }\n      if (index > fieldScanStart && (text[index] ?? "") === ":" && (text[index + 1] ?? "") === \'"\' && SEARCH_FIELD_PATH_PATTERN.test(text.slice(fieldScanStart, index))) {\n        explicitField = text.slice(fieldScanStart, index);\n        index += 1;\n      } else {\n        index = fieldScanStart;\n      }\n      if ((text[index] ?? "") === \'"\') {\n        index += 1;\n        let buffer = "";\n        while (index < text.length) {\n          const current = text[index] ?? "";\n          if (current === "\\\\" && index + 1 < text.length) {\n            buffer += text[index + 1] ?? "";\n            index += 2;\n            continue;\n          }\n          if (current === \'"\') {\n            index += 1;\n            break;\n          }\n          buffer += current;\n          index += 1;\n        }\n        let slop = 0;\n        let boost = 1;\n        if ((text[index] ?? "") === "~") {\n          index += 1;\n          const start2 = index;\n          while (index < text.length && /\\d/.test(text[index] ?? "")) {\n            index += 1;\n          }\n          if (index > start2) {\n            slop = Math.max(0, Number(text.slice(start2, index)) || 0);\n          }\n        }\n        if ((text[index] ?? "") === "^") {\n          index += 1;\n          const start2 = index;\n          while (index < text.length && /[\\d.]/.test(text[index] ?? "")) {\n            index += 1;\n          }\n          if (index > start2) {\n            boost = clampBoost(text.slice(start2, index));\n          }\n        }\n        out.push({\n          kind: "phrase",\n          value: buffer.trim(),\n          negated,\n          boost,\n          slop,\n          field: explicitField,\n          quoted: true\n        });\n        continue;\n      }\n      const start = index;\n      while (index < text.length && !/\\s/.test(text[index] ?? "") && (text[index] ?? "") !== "(" && (text[index] ?? "") !== ")") {\n        index += 1;\n      }\n      const raw = text.slice(start, index).trim();\n      if (!raw) continue;\n      const { base: rawBase, value: boostRaw } = parseNumericSuffix(raw, "^");\n      const upper = rawBase.toUpperCase();\n      if ((upper === "AND" || upper === "OR" || upper === "NOT") && !negated) {\n        out.push({ kind: "op", op: upper });\n        continue;\n      }\n      out.push({\n        kind: "term",\n        value: rawBase,\n        negated,\n        boost: boostRaw ? clampBoost(boostRaw) : 1,\n        slop: 0\n      });\n    }\n    return out;\n  }\n  function markTerminalLooseQueryToken(query, tokens) {\n    if (!tokens.length) return;\n    if (/\\s$/.test(String(query || ""))) return;\n    for (let index = tokens.length - 1; index >= 0; index -= 1) {\n      const token = tokens[index];\n      if (!token || !isRawLexicalToken(token)) continue;\n      if (token.kind !== "term" || token.field || token.quoted) return;\n      const normalized = normalizeTermValue(token.value);\n      if (normalized.length >= SEARCH_PREFIX_MIN_TERM_LENGTH$1) {\n        token.prefix = true;\n      }\n      if (normalized.length >= SEARCH_FUZZY_MIN_TERM_LENGTH$1) {\n        token.fuzzy = true;\n      }\n      return;\n    }\n  }\n  function isLexicalOperand(token) {\n    if (!token) return false;\n    return token.kind === "term" || token.kind === "phrase";\n  }\n  function buildLexicalAst(tokens) {\n    const warnings = [];\n    if (!tokens.length) {\n      return { ast: null, warnings };\n    }\n    const infix = [];\n    let previous = null;\n    for (const token of tokens) {\n      let needsAnd = false;\n      if (previous) {\n        const previousIsValue = isLexicalOperand(previous) || previous.kind === "rparen";\n        const currentStartsValue = isLexicalOperand(token) || token.kind === "lparen" || token.kind === "op" && token.op === "NOT";\n        if (previousIsValue && currentStartsValue) {\n          needsAnd = true;\n        }\n      }\n      if (needsAnd) {\n        infix.push({ kind: "op", op: "AND" });\n      }\n      infix.push(token);\n      previous = token;\n    }\n    const precedence = { OR: 1, AND: 2, NOT: 3 };\n    const output = [];\n    const stack = [];\n    for (const token of infix) {\n      if (isLexicalOperand(token)) {\n        output.push(token);\n        continue;\n      }\n      if (token.kind === "lparen") {\n        stack.push(token);\n        continue;\n      }\n      if (token.kind === "rparen") {\n        let foundLParen = false;\n        while (stack.length) {\n          const top = stack.pop();\n          if (!top) break;\n          if (top.kind === "lparen") {\n            foundLParen = true;\n            break;\n          }\n          output.push(top);\n        }\n        if (!foundLParen) {\n          warnings.push(warning("boolean_syntax", "unmatched closing parenthesis in query"));\n        }\n        continue;\n      }\n      if (token.kind !== "op") {\n        continue;\n      }\n      while (stack.length) {\n        const top = stack[stack.length - 1];\n        if (!top || top.kind !== "op") break;\n        const shouldPop = token.op === "NOT" ? precedence[token.op] < precedence[top.op] : precedence[token.op] <= precedence[top.op];\n        if (!shouldPop) break;\n        output.push(stack.pop());\n      }\n      stack.push(token);\n    }\n    while (stack.length) {\n      const top = stack.pop();\n      if (!top) break;\n      if (top.kind === "lparen") {\n        warnings.push(warning("boolean_syntax", "unmatched opening parenthesis in query"));\n        continue;\n      }\n      output.push(top);\n    }\n    const astStack = [];\n    for (const token of output) {\n      if (isLexicalOperand(token)) {\n        astStack.push(token);\n        continue;\n      }\n      if (token.kind !== "op") continue;\n      if (token.op === "NOT") {\n        const child = astStack.pop();\n        if (!child) {\n          warnings.push(warning("boolean_syntax", "dangling NOT operator in query", "NOT"));\n          continue;\n        }\n        astStack.push({ kind: "op", op: "NOT", child });\n        continue;\n      }\n      const right = astStack.pop();\n      const left = astStack.pop();\n      if (!left || !right) {\n        warnings.push(warning("boolean_syntax", `dangling ${token.op} operator in query`, token.op));\n        continue;\n      }\n      astStack.push({ kind: "op", op: token.op, left, right });\n    }\n    if (astStack.length === 1) {\n      return { ast: astStack[0] ?? null, warnings };\n    }\n    const operands = tokens.filter(isLexicalOperand);\n    if (!operands.length) {\n      return { ast: null, warnings };\n    }\n    const firstOperand = operands[0];\n    if (!firstOperand) {\n      return { ast: null, warnings };\n    }\n    let fallback = firstOperand;\n    for (let index = 1; index < operands.length; index += 1) {\n      const operand = operands[index];\n      if (!operand) continue;\n      fallback = { kind: "op", op: "AND", left: fallback, right: operand };\n    }\n    warnings.push(\n      warning(\n        "boolean_syntax",\n        "query boolean expression was malformed; fell back to implicit AND between lexical terms"\n      )\n    );\n    return { ast: fallback, warnings };\n  }\n  function collectLexicalTokens(node, polarity) {\n    const out = [];\n    const walk = (current, currentPolarity) => {\n      if (!current) return;\n      if (isLexicalOperand(current)) {\n        if (currentPolarity === polarity) {\n          out.push(current);\n        }\n        return;\n      }\n      if (current.op === "NOT") {\n        walk(current.child, !currentPolarity);\n        return;\n      }\n      walk(current.left, currentPolarity);\n      walk(current.right, currentPolarity);\n    };\n    walk(node, true);\n    return out;\n  }\n  function collectFlatBooleanChain(node, op, out) {\n    if (!node) return;\n    if (!isLexicalOperand(node) && node.op === op) {\n      collectFlatBooleanChain(node.left, op, out);\n      collectFlatBooleanChain(node.right, op, out);\n      return;\n    }\n    out.push(node);\n  }\n  function astToString(node) {\n    if (!node) return "";\n    if (isLexicalOperand(node)) {\n      let out = node.kind === "phrase" ? `"${node.value}"` : node.value;\n      if (node.prefix) {\n        out += "*";\n      }\n      if (node.kind === "phrase" && node.slop > 0) {\n        out += `~${node.slop}`;\n      }\n      if (Math.abs(node.boost - 1) > 1e-9) {\n        out += `^${node.boost}`;\n      }\n      if (node.field) {\n        out = `${node.field}:${out}`;\n      }\n      return out;\n    }\n    if (node.op === "NOT") {\n      const child = astToString(node.child);\n      return child ? `NOT (${child})` : "NOT (?)";\n    }\n    const parts = [];\n    collectFlatBooleanChain(node, node.op, parts);\n    const rendered = parts.map((part) => {\n      const text = astToString(part);\n      if (!text) return "";\n      if (!isLexicalOperand(part) && part.op !== node.op) {\n        return `(${text})`;\n      }\n      return text;\n    }).filter(Boolean);\n    return rendered.join(` ${node.op} `);\n  }\n  function isExpandableFreeTextToken(token) {\n    return !token.field && token.kind === "term" && tokenizeText$1(token.value).length === 1;\n  }\n  function buildExpandedFreeTextRun(tokens) {\n    var _a, _b, _c, _d, _e, _f, _g, _h;\n    if (tokens.length <= 1) {\n      return tokens;\n    }\n    const out = [];\n    const seen = /* @__PURE__ */ new Set();\n    const pushUnique = (token) => {\n      const key = `${token.kind}|${token.field || ""}|${token.value}|${token.slop}|${token.boost}`;\n      if (seen.has(key)) return;\n      seen.add(key);\n      out.push(token);\n    };\n    const phraseBoostForWindow = (window, baseBoost) => {\n      const averageBoost = window.reduce((sum, token) => sum + token.boost, 0) / window.length;\n      return roundBoost(baseBoost * averageBoost);\n    };\n    for (const token of getFreeTextRunSingletonTokens(tokens)) {\n      pushUnique(token);\n    }\n    for (let index = 0; index <= tokens.length - 2; index += 1) {\n      const window = tokens.slice(index, index + 2);\n      if (countContentTermsInFreeTextWindow(window) < 2) {\n        continue;\n      }\n      pushUnique({\n        kind: "phrase",\n        value: window.map((token) => token.value).join(" "),\n        slop: SEARCH_FREE_TEXT.bigram_slop,\n        boost: phraseBoostForWindow(window, SEARCH_FREE_TEXT.bigram_boost),\n        prefix: !!((_a = window[window.length - 1]) == null ? void 0 : _a.prefix),\n        fuzzy: !!((_b = window[window.length - 1]) == null ? void 0 : _b.fuzzy)\n      });\n    }\n    for (let index = 0; index <= tokens.length - 3; index += 1) {\n      const window = tokens.slice(index, index + 3);\n      if (countContentTermsInFreeTextWindow(window) < 2) {\n        continue;\n      }\n      pushUnique({\n        kind: "phrase",\n        value: window.map((token) => token.value).join(" "),\n        slop: SEARCH_FREE_TEXT.trigram_slop,\n        boost: phraseBoostForWindow(window, SEARCH_FREE_TEXT.trigram_boost),\n        prefix: !!((_c = window[window.length - 1]) == null ? void 0 : _c.prefix),\n        fuzzy: !!((_d = window[window.length - 1]) == null ? void 0 : _d.fuzzy)\n      });\n    }\n    for (let index = 0; index <= tokens.length - 4; index += 1) {\n      const window = tokens.slice(index, index + 4);\n      if (countContentTermsInFreeTextWindow(window) < 2) {\n        continue;\n      }\n      pushUnique({\n        kind: "phrase",\n        value: window.map((token) => token.value).join(" "),\n        slop: SEARCH_FREE_TEXT.fourgram_slop,\n        boost: phraseBoostForWindow(window, SEARCH_FREE_TEXT.fourgram_boost),\n        prefix: !!((_e = window[window.length - 1]) == null ? void 0 : _e.prefix),\n        fuzzy: !!((_f = window[window.length - 1]) == null ? void 0 : _f.fuzzy)\n      });\n    }\n    if (tokens.length >= SEARCH_FREE_TEXT_FULL_RUN_EXACT_MIN_TERMS) {\n      pushUnique({\n        kind: "phrase",\n        value: tokens.map((token) => token.value).join(" "),\n        slop: 0,\n        boost: phraseBoostForWindow(tokens, SEARCH_FREE_TEXT.full_run_exact_boost),\n        prefix: !!((_g = tokens[tokens.length - 1]) == null ? void 0 : _g.prefix),\n        fuzzy: !!((_h = tokens[tokens.length - 1]) == null ? void 0 : _h.fuzzy)\n      });\n    }\n    return out;\n  }\n  function pushLexicalTokenRun(destination, pendingTerms) {\n    if (!pendingTerms.length) return;\n    if (pendingTerms.length === 1) {\n      destination.push(pendingTerms[0]);\n      pendingTerms.length = 0;\n      return;\n    }\n    const expanded = buildExpandedFreeTextRun(pendingTerms);\n    destination.push({ kind: "lparen" });\n    expanded.forEach((token, index) => {\n      if (index > 0) {\n        destination.push({ kind: "op", op: "OR" });\n      }\n      destination.push(token);\n    });\n    destination.push({ kind: "rparen" });\n    pendingTerms.length = 0;\n  }\n  function uniqueTerms(values) {\n    const seen = /* @__PURE__ */ new Set();\n    const out = [];\n    for (const value of values) {\n      for (const term of tokenizeText$1(value)) {\n        if (!term || seen.has(term)) continue;\n        seen.add(term);\n        out.push(term);\n      }\n    }\n    return out;\n  }\n  function collectHighlightTerms(positiveLexicalTokens, filters) {\n    const terms = /* @__PURE__ */ new Set();\n    for (const token of positiveLexicalTokens) {\n      for (const term of tokenizeText$1(token.value)) {\n        if (term && !isLowSignalFreeTextTerm(term)) {\n          terms.add(term);\n        }\n      }\n    }\n    for (const filter of filters) {\n      if (filter.negated || SEARCH_NON_HIGHLIGHT_FILTERS.has(filter.name)) continue;\n      for (const term of tokenizeText$1(filter.value)) {\n        if (term) {\n          terms.add(term);\n        }\n      }\n    }\n    return [...terms].slice(0, MAX_HIGHLIGHT_TERMS);\n  }\n  function parseSearchQuery(query) {\n    var _a;\n    const rawTokens = tokenizeQuery(query);\n    markTerminalLooseQueryToken(query, rawTokens);\n    const filters = [];\n    const unsupported = [];\n    const lexicalTokens = [];\n    const pendingFreeTextTerms = [];\n    const positiveTermSourceValues = [];\n    const orderedTermSourceValues = [];\n    const flushPendingFreeTextTerms = () => {\n      if (pendingFreeTextTerms.length) {\n        const runTerms = getFreeTextRunContentTerms(pendingFreeTextTerms);\n        orderedTermSourceValues.push(...runTerms);\n        positiveTermSourceValues.push(...runTerms);\n      }\n      pushLexicalTokenRun(lexicalTokens, pendingFreeTextTerms);\n    };\n    for (const token of rawTokens) {\n      if (!isRawLexicalToken(token)) {\n        flushPendingFreeTextTerms();\n        lexicalTokens.push(token);\n        continue;\n      }\n      const rawValue = String(token.value || "").trim();\n      if (!rawValue) continue;\n      let lexicalKind = token.kind;\n      let lexicalValue = rawValue;\n      let lexicalField = (_a = token.field) == null ? void 0 : _a.trim();\n      let handledAsFilter = false;\n      if (token.kind === "term" && rawValue.startsWith("@") && rawValue.length > 1) {\n        const value = normalizeTermValue(rawValue.slice(1));\n        if (value) {\n          filters.push({ name: "from", value, negated: token.negated });\n          flushPendingFreeTextTerms();\n          continue;\n        }\n      } else if (token.kind === "term" && rawValue.startsWith("#") && rawValue.length > 1) {\n        const value = normalizeTermValue(rawValue.slice(1));\n        if (value) {\n          filters.push({ name: "hashtag", value, negated: token.negated });\n          lexicalKind = "term";\n          lexicalValue = value;\n          handledAsFilter = true;\n        }\n      } else if (token.kind === "term" && rawValue.startsWith("$") && rawValue.length > 1) {\n        const value = normalizeTermValue(rawValue.slice(1));\n        if (value) {\n          filters.push({ name: "cashtag", value, negated: token.negated });\n          lexicalKind = "term";\n          lexicalValue = value;\n          handledAsFilter = true;\n        }\n      } else if (token.kind === "term" && rawValue.includes(":")) {\n        const [rawKey, ...rawRest] = rawValue.split(":");\n        const key = String(rawKey || "").trim().toLowerCase();\n        const value = rawRest.join(":").trim();\n        if (SEARCH_KNOWN_FILTER_KEYS.has(key)) {\n          if (!value) {\n            unsupported.push(rawValue);\n            continue;\n          }\n          filters.push({ name: key, value, negated: token.negated });\n          flushPendingFreeTextTerms();\n          continue;\n        }\n        if (key && value && SEARCH_FIELD_PATH_PATTERN.test(key)) {\n          lexicalKind = "term";\n          lexicalValue = value;\n          lexicalField = key;\n        }\n      }\n      const lexicalToken = {\n        kind: lexicalKind,\n        value: lexicalKind === "term" ? normalizeTermValue(lexicalValue) : lexicalValue.trim(),\n        boost: token.boost,\n        slop: token.slop,\n        field: lexicalField || void 0,\n        quoted: !!token.quoted,\n        prefix: !!token.prefix,\n        fuzzy: !!token.fuzzy\n      };\n      if (!lexicalToken.value) {\n        continue;\n      }\n      if (token.negated) {\n        flushPendingFreeTextTerms();\n        lexicalTokens.push({ kind: "op", op: "NOT" });\n        lexicalTokens.push(lexicalToken);\n        continue;\n      }\n      if (!handledAsFilter && isExpandableFreeTextToken(lexicalToken)) {\n        pendingFreeTextTerms.push(lexicalToken);\n        continue;\n      }\n      flushPendingFreeTextTerms();\n      if (!lexicalToken.field) {\n        const tokenTerms = tokenizeText$1(lexicalToken.value);\n        orderedTermSourceValues.push(...tokenTerms);\n        positiveTermSourceValues.push(...tokenTerms);\n      }\n      lexicalTokens.push(lexicalToken);\n    }\n    flushPendingFreeTextTerms();\n    const { ast: lexicalAst, warnings } = buildLexicalAst(lexicalTokens);\n    const positiveLexicalTokens = collectLexicalTokens(lexicalAst, true);\n    const negativeLexicalTokens = collectLexicalTokens(lexicalAst, false);\n    const orderedTerms = orderedTermSourceValues.filter(Boolean);\n    const positiveTerms = uniqueTerms(positiveTermSourceValues);\n    const highlightTerms = collectHighlightTerms(positiveLexicalTokens, filters);\n    const unsupportedWarnings = unsupported.map(\n      (tokenValue) => warning("unsupported_token", `unsupported token: ${tokenValue}`, tokenValue)\n    );\n    return {\n      query: String(query || ""),\n      lexicalTokens,\n      lexicalAst,\n      lexicalExpression: astToString(lexicalAst),\n      positiveLexicalTokens,\n      negativeLexicalTokens,\n      positiveTerms,\n      filters,\n      unsupported,\n      orderedTerms,\n      highlightTerms,\n      warnings: [...warnings, ...unsupportedWarnings],\n      hasPositiveLexical: positiveLexicalTokens.length > 0,\n      filterBooleanSemantics: "global_and"\n    };\n  }\n  const TERM_TOKEN_PATTERN = /[\\p{L}\\p{N}_]+(?:[\'’][\\p{L}\\p{N}_]+)*/gu;\n  const SEARCH_RANKING_DEFAULTS = {\n    bm25: 1,\n    lexical: 1,\n    cover_density: 1,\n    recency: 0,\n    term_match: 1,\n    phrase_match: 8,\n    quoted_phrase_match: 256,\n    cover_bigram: 10,\n    cover_trigram: 30\n  };\n  const SEARCH_RANKING_STORAGE_KEY = "twe_raw_search_ranking_v1";\n  const SEARCH_QUERY_PARSE_CACHE_LIMIT = 128;\n  const SEARCH_PREFIX_MIN_TERM_LENGTH = Math.max(1, Number(SEARCH_PREFIX.min_term_length));\n  const SEARCH_PREFIX_MAX_EXPANSIONS = Math.max(8, Number(SEARCH_PREFIX.max_expansions));\n  const SEARCH_FUZZY_MIN_TERM_LENGTH = Math.max(1, Number(SEARCH_FUZZY.min_term_length));\n  const SEARCH_FUZZY_MAX_EDIT_DISTANCE = Math.max(0, Number(SEARCH_FUZZY.max_edit_distance));\n  const SEARCH_FUZZY_PREFIX_ROOT_LENGTH = Math.max(1, Number(SEARCH_FUZZY.prefix_root_length));\n  const SEARCH_FUZZY_MAX_EXPANSIONS = Math.max(8, Number(SEARCH_FUZZY.max_expansions));\n  const SEARCH_ANCHOR_MIN_TERMS = 3;\n  const SEARCH_ANCHOR_MAX_TERMS = 4;\n  const SEARCH_ANCHOR_COMMON_DOC_FRACTION = 0.65;\n  const SEARCH_ANCHOR_MAX_RELATIVE_DOC_FREQ = 1.5;\n  const parsedQueryCache = /* @__PURE__ */ new Map();\n  const searchDocCache = /* @__PURE__ */ new WeakMap();\n  function readPath(obj, path) {\n    const parts = path.split(".");\n    let current = obj;\n    for (const part of parts) {\n      if (!current || typeof current !== "object") return null;\n      current = current[part];\n    }\n    return current;\n  }\n  function asString(value) {\n    return typeof value === "string" ? value : "";\n  }\n  function toBool(value) {\n    if (typeof value === "boolean") return value;\n    if (typeof value === "number") return value > 0;\n    if (typeof value === "string") {\n      const normalized = value.trim().toLowerCase();\n      return normalized === "1" || normalized === "true" || normalized === "yes";\n    }\n    return false;\n  }\n  function toNumber(value) {\n    if (typeof value === "number" && Number.isFinite(value)) return value;\n    if (typeof value === "string") {\n      const parsed = Number(value);\n      if (Number.isFinite(parsed)) return parsed;\n    }\n    return 0;\n  }\n  function normalizeTextToken(value) {\n    return value.trim().toLowerCase();\n  }\n  function tokenizeText(value) {\n    if (!value) return [];\n    const matches = value.toLowerCase().match(TERM_TOKEN_PATTERN);\n    return matches ? matches.map((token) => token.replace(/[\'’]/g, "")).filter(Boolean) : [];\n  }\n  function buildTokenFrequency(tokens) {\n    const tokenFreq = /* @__PURE__ */ new Map();\n    for (let index = 0; index < tokens.length; index += 1) {\n      const token = tokens[index];\n      if (!token) continue;\n      tokenFreq.set(token, (tokenFreq.get(token) || 0) + 1);\n    }\n    return tokenFreq;\n  }\n  function levenshteinDistance(a2, b2) {\n    if (a2 === b2) return 0;\n    if (!a2.length) return b2.length;\n    if (!b2.length) return a2.length;\n    const previous = new Array(b2.length + 1);\n    const current = new Array(b2.length + 1);\n    for (let j2 = 0; j2 <= b2.length; j2 += 1) previous[j2] = j2;\n    for (let i2 = 1; i2 <= a2.length; i2 += 1) {\n      current[0] = i2;\n      for (let j2 = 1; j2 <= b2.length; j2 += 1) {\n        const cost = a2[i2 - 1] === b2[j2 - 1] ? 0 : 1;\n        current[j2] = Math.min(\n          (previous[j2] ?? 0) + 1,\n          (current[j2 - 1] ?? 0) + 1,\n          (previous[j2 - 1] ?? 0) + cost\n        );\n      }\n      for (let j2 = 0; j2 <= b2.length; j2 += 1) {\n        previous[j2] = current[j2] ?? 0;\n      }\n    }\n    return previous[b2.length] ?? Math.max(a2.length, b2.length);\n  }\n  function parseDateToMs(value, endOfDay = false) {\n    if (!value) return 0;\n    if (/^\\d{4}-\\d{2}-\\d{2}$/.test(value)) {\n      const suffix = endOfDay ? "T23:59:59.999Z" : "T00:00:00.000Z";\n      const parsed2 = Date.parse(`${value}${suffix}`);\n      return Number.isFinite(parsed2) ? parsed2 : 0;\n    }\n    const parsed = Date.parse(value);\n    return Number.isFinite(parsed) ? parsed : 0;\n  }\n  function parseSourceText(sourceHtml) {\n    if (!sourceHtml) return "";\n    return sourceHtml.replace(/<[^>]+>/g, " ").replace(/\\s+/g, " ").trim();\n  }\n  function extractUrls(obj) {\n    const out = /* @__PURE__ */ new Set();\n    const collectUrlObjects = (value) => {\n      if (!Array.isArray(value)) return;\n      for (const item of value) {\n        if (!item || typeof item !== "object") continue;\n        const row = item;\n        const expanded = asString(row.expanded_url).trim();\n        const display = asString(row.display_url).trim();\n        const raw = asString(row.url).trim();\n        if (expanded) out.add(expanded);\n        if (display) out.add(display);\n        if (raw) out.add(raw);\n      }\n    };\n    collectUrlObjects(readPath(obj, "legacy.entities.urls"));\n    collectUrlObjects(readPath(obj, "legacy.entities.media"));\n    collectUrlObjects(readPath(obj, "legacy.entities.description.urls"));\n    collectUrlObjects(readPath(obj, "legacy.entities.url.urls"));\n    return [...out];\n  }\n  function extractEntityValues(obj, path, key = "text") {\n    const value = readPath(obj, path);\n    if (!Array.isArray(value)) return [];\n    const out = /* @__PURE__ */ new Set();\n    for (const item of value) {\n      if (!item || typeof item !== "object") continue;\n      const row = item;\n      const candidate = normalizeTextToken(asString(row[key]).trim());\n      if (candidate) {\n        out.add(candidate);\n      }\n    }\n    return [...out];\n  }\n  function extractMediaTypes(obj) {\n    const media = (Array.isArray(readPath(obj, "legacy.extended_entities.media")) ? readPath(obj, "legacy.extended_entities.media") : readPath(obj, "legacy.entities.media")) || [];\n    if (!Array.isArray(media)) return [];\n    const out = /* @__PURE__ */ new Set();\n    for (const item of media) {\n      if (!item || typeof item !== "object") continue;\n      const row = item;\n      const candidate = normalizeTextToken(asString(row.type).trim());\n      if (candidate) {\n        out.add(candidate);\n      }\n    }\n    return [...out];\n  }\n  function extractArticleBlockTexts(obj) {\n    const blocks = readPath(obj, "article.article_results.result.content_state.blocks");\n    if (!Array.isArray(blocks)) return [];\n    const out = [];\n    for (const item of blocks) {\n      if (!item || typeof item !== "object") continue;\n      const text = asString(item.text).trim();\n      if (!text) continue;\n      out.push(text);\n    }\n    return out;\n  }\n  function extractDomains(urls) {\n    const out = /* @__PURE__ */ new Set();\n    for (const value of urls) {\n      try {\n        const normalized = value.startsWith("http") ? value : `https://${value}`;\n        const domain = new URL(normalized).hostname.replace(/^www\\./, "").toLowerCase();\n        if (domain) out.add(domain);\n      } catch {\n      }\n    }\n    return [...out];\n  }\n  function phraseSlop(tokens, phraseTerms, prefixLastTerm = false) {\n    if (!phraseTerms.length) return 0;\n    const first = phraseTerms[0];\n    const firstPositions = [];\n    for (let i2 = 0; i2 < tokens.length; i2 += 1) {\n      if (tokens[i2] === first) firstPositions.push(i2);\n    }\n    if (!firstPositions.length) return null;\n    let best = null;\n    for (const startPos of firstPositions) {\n      let prev = startPos;\n      let ok = true;\n      for (let termIndex = 1; termIndex < phraseTerms.length; termIndex += 1) {\n        const needle = phraseTerms[termIndex];\n        const isLastNeedle = prefixLastTerm && termIndex === phraseTerms.length - 1;\n        let nextPos = -1;\n        for (let cursor = prev + 1; cursor < tokens.length; cursor += 1) {\n          const candidate = tokens[cursor];\n          if (isLastNeedle && needle && (candidate == null ? void 0 : candidate.startsWith(needle)) || candidate === needle) {\n            nextPos = cursor;\n            break;\n          }\n        }\n        if (nextPos < 0) {\n          ok = false;\n          break;\n        }\n        prev = nextPos;\n      }\n      if (!ok) continue;\n      const span = prev - startPos;\n      const baseSpan = Math.max(0, phraseTerms.length - 1);\n      const slop = Math.max(0, span - baseSpan);\n      if (best === null || slop < best) best = slop;\n    }\n    return best;\n  }\n  function extractSearchDoc(record) {\n    const obj = record || {};\n    const id = asString(readPath(obj, "rest_id")) || asString(readPath(obj, "legacy.id_str")) || asString(readPath(obj, "id_str")) || `${Math.random().toString(36).slice(2)}`;\n    const fullText = asString(readPath(obj, "note_tweet.note_tweet_results.result.text")) || [\n      asString(readPath(obj, "article.article_results.result.title")),\n      asString(readPath(obj, "article.article_results.result.preview_text")),\n      ...extractArticleBlockTexts(obj)\n    ].map((value) => value.trim()).filter(Boolean).filter((value, index, arr) => arr.indexOf(value) === index).join("\\n\\n") || asString(readPath(obj, "legacy.full_text")) || asString(readPath(obj, "legacy.text")) || asString(readPath(obj, "legacy.description"));\n    const quotedText = asString(\n      readPath(obj, "quoted_status_result.result.note_tweet.note_tweet_results.result.text")\n    ) || asString(readPath(obj, "quoted_status_result.result.legacy.full_text")) || asString(readPath(obj, "quoted_status_result.result.legacy.text")) || asString(readPath(obj, "quoted_status_result.result.article.article_results.result.title")) || asString(\n      readPath(obj, "quoted_status_result.result.article.article_results.result.preview_text")\n    );\n    const quotedAuthorScreenName = normalizeTextToken(\n      asString(\n        readPath(obj, "quoted_status_result.result.core.user_results.result.core.screen_name")\n      )\n    );\n    const quotedAuthorName = asString(\n      readPath(obj, "quoted_status_result.result.core.user_results.result.core.name")\n    );\n    const authorScreenName = normalizeTextToken(\n      asString(readPath(obj, "core.user_results.result.core.screen_name")) || asString(readPath(obj, "core.screen_name"))\n    );\n    const authorName = asString(readPath(obj, "core.user_results.result.core.name")) || asString(readPath(obj, "core.name"));\n    const authorId = asString(readPath(obj, "core.user_results.result.rest_id")) || asString(readPath(obj, "rest_id"));\n    const createdAtRaw = asString(readPath(obj, "legacy.created_at")) || asString(readPath(obj, "core.created_at"));\n    const createdAtMs = createdAtRaw ? Number(parseTwitterDateTime(createdAtRaw) || 0) : Number(\n      readPath(obj, "article.article_results.result.metadata.first_published_at_secs") || 0\n    ) * 1e3;\n    const sourceText = parseSourceText(asString(readPath(obj, "legacy.source")));\n    const cardName = normalizeTextToken(\n      asString(readPath(obj, "card.card_platform.card_name")) || asString(readPath(obj, "card.name")) || asString(readPath(obj, "__card_name"))\n    );\n    const bookmarkFolderId = asString(readPath(obj, "__bookmark_folder_id")).trim();\n    const bookmarkFolderName = asString(readPath(obj, "__bookmark_folder_name")).trim();\n    const urls = extractUrls(obj);\n    const domains = extractDomains(urls);\n    const mentions = extractEntityValues(obj, "legacy.entities.user_mentions", "screen_name");\n    const hashtags = extractEntityValues(obj, "legacy.entities.hashtags");\n    const cashtags = extractEntityValues(obj, "legacy.entities.symbols");\n    const mediaTypes = extractMediaTypes(obj);\n    const relationshipSubjectScreenNames = flattenFieldValues(\n      readPath(obj, "twe_relationship_fields.subject_screen_names")\n    ).map((value) => normalizeTextToken(value));\n    const relationshipSubjectUserIds = flattenFieldValues(\n      readPath(obj, "twe_relationship_fields.subject_user_ids")\n    ).map((value) => String(value).trim());\n    const relationshipTypes = flattenFieldValues(\n      readPath(obj, "twe_relationship_fields.relation_types")\n    ).map((value) => normalizeTextToken(value));\n    const toUser = normalizeTextToken(asString(readPath(obj, "legacy.in_reply_to_screen_name")));\n    const toUserId = asString(readPath(obj, "legacy.in_reply_to_user_id_str")).trim();\n    const inReplyToId = asString(readPath(obj, "legacy.in_reply_to_status_id_str")).trim();\n    const conversationId = asString(readPath(obj, "legacy.conversation_id_str")) || asString(readPath(obj, "conversation_id_str"));\n    const lang = normalizeTextToken(asString(readPath(obj, "legacy.lang")));\n    const routeType = normalizeTextToken(asString(readPath(obj, "__route_type")));\n    const hasMedia = mediaTypes.length > 0;\n    const hasImages = mediaTypes.includes("photo");\n    const hasVideo = mediaTypes.includes("video") || mediaTypes.includes("animated_gif");\n    const hasLinks = urls.length > 0;\n    const isRetweet = !!readPath(obj, "legacy.retweeted_status_result") || asString(readPath(obj, "legacy.full_text")).startsWith("RT @");\n    const isQuote = !!readPath(obj, "quoted_status_result");\n    const isReply = !!inReplyToId;\n    const isVerified = normalizeTextToken(\n      asString(readPath(obj, "core.user_results.result.verification.verified_type"))\n    ) === "verified" || normalizeTextToken(asString(readPath(obj, "verification.verified_type"))) === "verified";\n    const isBlueVerified = toBool(readPath(obj, "core.user_results.result.is_blue_verified")) || toBool(readPath(obj, "is_blue_verified"));\n    const favorited = toBool(readPath(obj, "legacy.favorited"));\n    const retweeted = toBool(readPath(obj, "legacy.retweeted"));\n    const bookmarked = toBool(readPath(obj, "legacy.bookmarked"));\n    const favoriteCount = toNumber(readPath(obj, "legacy.favorite_count"));\n    const retweetCount = toNumber(readPath(obj, "legacy.retweet_count"));\n    const replyCount = toNumber(readPath(obj, "legacy.reply_count"));\n    const bookmarkCount = toNumber(readPath(obj, "legacy.bookmark_count"));\n    const searchText = [\n      fullText,\n      quotedText,\n      sourceText,\n      authorScreenName,\n      authorName,\n      quotedAuthorScreenName,\n      quotedAuthorName,\n      toUser,\n      bookmarkFolderId,\n      bookmarkFolderName,\n      cardName,\n      ...relationshipSubjectScreenNames,\n      ...relationshipSubjectUserIds,\n      ...relationshipTypes,\n      ...mentions,\n      ...hashtags,\n      ...cashtags,\n      ...urls,\n      ...domains\n    ].filter(Boolean).join(" ").trim();\n    const primaryText = [\n      fullText,\n      sourceText,\n      authorScreenName,\n      authorName,\n      toUser,\n      bookmarkFolderId,\n      bookmarkFolderName,\n      cardName,\n      ...relationshipSubjectScreenNames,\n      ...relationshipSubjectUserIds,\n      ...relationshipTypes,\n      ...mentions,\n      ...hashtags,\n      ...cashtags,\n      ...urls,\n      ...domains\n    ].filter(Boolean).join(" ").trim();\n    const quoteAuxText = [quotedText, quotedAuthorScreenName, quotedAuthorName].filter(Boolean).join(" ").trim();\n    const tokens = tokenizeText(searchText);\n    const primaryTokens = tokenizeText(primaryText);\n    const quotedTokens = tokenizeText(quoteAuxText);\n    const tokenFreq = buildTokenFrequency(tokens);\n    const primaryTokenFreq = buildTokenFrequency(primaryTokens);\n    const quotedTokenFreq = buildTokenFrequency(quotedTokens);\n    return {\n      raw: record,\n      id,\n      text: searchText,\n      primaryText,\n      quotedText: quoteAuxText,\n      tokens,\n      primaryTokens,\n      quotedTokens,\n      tokenFreq,\n      primaryTokenFreq,\n      quotedTokenFreq,\n      createdAtMs,\n      authorScreenName,\n      authorId,\n      toUser,\n      toUserId,\n      inReplyToId,\n      conversationId,\n      lang,\n      routeType,\n      sourceText: normalizeTextToken(sourceText),\n      cardName,\n      bookmarkFolderId,\n      bookmarkFolderName: normalizeTextToken(bookmarkFolderName),\n      mentions,\n      hashtags,\n      cashtags,\n      urls: urls.map((value) => value.toLowerCase()),\n      domains,\n      favoriteCount,\n      retweetCount,\n      replyCount,\n      bookmarkCount,\n      favorited,\n      retweeted,\n      bookmarked,\n      hasMedia,\n      hasImages,\n      hasVideo,\n      hasLinks,\n      isReply,\n      isRetweet,\n      isQuote,\n      isVerified,\n      isBlueVerified,\n      fieldSearchCache: /* @__PURE__ */ new Map()\n    };\n  }\n  function getCachedSearchDoc(record) {\n    if (!record || typeof record !== "object") {\n      return extractSearchDoc(record);\n    }\n    const cached = searchDocCache.get(record);\n    if (cached) {\n      return cached;\n    }\n    const built = extractSearchDoc(record);\n    searchDocCache.set(record, built);\n    return built;\n  }\n  function flattenFieldValues(value) {\n    if (value === null || value === void 0) return [];\n    if (typeof value === "string") {\n      const text = value.trim();\n      return text ? [text] : [];\n    }\n    if (typeof value === "number" || typeof value === "boolean") {\n      return [String(value)];\n    }\n    if (Array.isArray(value)) {\n      return value.flatMap((item) => flattenFieldValues(item));\n    }\n    if (typeof value === "object") {\n      return Object.values(value).flatMap(\n        (item) => flattenFieldValues(item)\n      );\n    }\n    return [];\n  }\n  function resolveFieldValue(doc, fieldPath) {\n    switch (fieldPath) {\n      case "text":\n        return doc.text;\n      case "id":\n        return doc.id;\n      case "author_screen_name":\n        return doc.authorScreenName;\n      case "author_id":\n        return doc.authorId;\n      case "to_user":\n        return doc.toUser;\n      case "to_user_id":\n        return doc.toUserId;\n      case "conversation_id":\n        return doc.conversationId;\n      case "lang":\n        return doc.lang;\n      case "route":\n      case "route_type":\n        return doc.routeType;\n      case "source":\n      case "source_text":\n        return doc.sourceText;\n      case "card_name":\n        return doc.cardName;\n      case "bookmark_folder_id":\n        return doc.bookmarkFolderId;\n      case "bookmark_folder_name":\n        return doc.bookmarkFolderName;\n      case "subject_screen_names":\n        return flattenFieldValues(\n          readPath(\n            doc.raw || {},\n            "twe_relationship_fields.subject_screen_names"\n          )\n        );\n      case "subject_user_ids":\n        return flattenFieldValues(\n          readPath(\n            doc.raw || {},\n            "twe_relationship_fields.subject_user_ids"\n          )\n        );\n      case "relation_types":\n        return flattenFieldValues(\n          readPath(\n            doc.raw || {},\n            "twe_relationship_fields.relation_types"\n          )\n        );\n      case "mentions":\n        return doc.mentions;\n      case "hashtags":\n        return doc.hashtags;\n      case "cashtags":\n        return doc.cashtags;\n      case "urls":\n        return doc.urls;\n      case "domains":\n        return doc.domains;\n      default:\n        return readPath(doc.raw || {}, fieldPath);\n    }\n  }\n  function getFieldSearchData(doc, fieldPath) {\n    const key = String(fieldPath || "").trim();\n    if (!key) {\n      return {\n        text: doc.text,\n        tokens: doc.tokens,\n        tokenFreq: doc.tokenFreq\n      };\n    }\n    const cached = doc.fieldSearchCache.get(key);\n    if (cached) return cached;\n    const values = flattenFieldValues(resolveFieldValue(doc, key));\n    const text = values.join(" ").trim();\n    const tokens = tokenizeText(text);\n    const tokenFreq = /* @__PURE__ */ new Map();\n    for (const token of tokens) {\n      tokenFreq.set(token, (tokenFreq.get(token) || 0) + 1);\n    }\n    const built = { text, tokens, tokenFreq };\n    doc.fieldSearchCache.set(key, built);\n    return built;\n  }\n  function buildRankingContext(docs) {\n    const termDocFreq = /* @__PURE__ */ new Map();\n    let totalLength = 0;\n    for (const doc of docs) {\n      totalLength += doc.tokens.length;\n      for (const token of doc.tokenFreq.keys()) {\n        termDocFreq.set(token, (termDocFreq.get(token) || 0) + 1);\n      }\n    }\n    return {\n      docCount: docs.length,\n      avgDocLength: docs.length ? totalLength / docs.length : 1,\n      termDocFreq\n    };\n  }\n  function pushIndexedValue(index, key, docIndex) {\n    if (!key) return;\n    const existing = index.get(key);\n    if (existing) {\n      existing.push(docIndex);\n      return;\n    }\n    index.set(key, [docIndex]);\n  }\n  function buildExactFilterKey(name, value) {\n    return `${name}:${value}`;\n  }\n  function buildSearchCorpusIndexes(docs) {\n    const tokenDocs = /* @__PURE__ */ new Map();\n    const exactFilterDocs = /* @__PURE__ */ new Map();\n    for (let docIndex = 0; docIndex < docs.length; docIndex += 1) {\n      const doc = docs[docIndex];\n      if (!doc) continue;\n      for (const token of doc.tokenFreq.keys()) {\n        pushIndexedValue(tokenDocs, token, docIndex);\n      }\n      pushIndexedValue(exactFilterDocs, buildExactFilterKey("id", doc.id), docIndex);\n      pushIndexedValue(exactFilterDocs, buildExactFilterKey("from", doc.authorScreenName), docIndex);\n      pushIndexedValue(exactFilterDocs, buildExactFilterKey("from_id", doc.authorId), docIndex);\n      pushIndexedValue(exactFilterDocs, buildExactFilterKey("author_id", doc.authorId), docIndex);\n      pushIndexedValue(exactFilterDocs, buildExactFilterKey("to", doc.toUser), docIndex);\n      pushIndexedValue(exactFilterDocs, buildExactFilterKey("to_id", doc.toUserId), docIndex);\n      pushIndexedValue(\n        exactFilterDocs,\n        buildExactFilterKey("in_reply_to_id", doc.inReplyToId),\n        docIndex\n      );\n      pushIndexedValue(\n        exactFilterDocs,\n        buildExactFilterKey("conversation_id", doc.conversationId),\n        docIndex\n      );\n      pushIndexedValue(exactFilterDocs, buildExactFilterKey("lang", doc.lang), docIndex);\n      pushIndexedValue(exactFilterDocs, buildExactFilterKey("route", doc.routeType), docIndex);\n      pushIndexedValue(\n        exactFilterDocs,\n        buildExactFilterKey("bookmark_folder", doc.bookmarkFolderId),\n        docIndex\n      );\n      for (const value of doc.mentions) {\n        pushIndexedValue(exactFilterDocs, buildExactFilterKey("mention", value), docIndex);\n      }\n      for (const value of doc.hashtags) {\n        pushIndexedValue(exactFilterDocs, buildExactFilterKey("hashtag", value), docIndex);\n      }\n      for (const value of doc.cashtags) {\n        pushIndexedValue(exactFilterDocs, buildExactFilterKey("cashtag", value), docIndex);\n      }\n      if (doc.hasMedia)\n        pushIndexedValue(exactFilterDocs, buildExactFilterKey("is", "media"), docIndex);\n      if (doc.hasImages)\n        pushIndexedValue(exactFilterDocs, buildExactFilterKey("is", "images"), docIndex);\n      if (doc.hasVideo)\n        pushIndexedValue(exactFilterDocs, buildExactFilterKey("is", "videos"), docIndex);\n      if (doc.hasLinks)\n        pushIndexedValue(exactFilterDocs, buildExactFilterKey("is", "links"), docIndex);\n      if (doc.bookmarked)\n        pushIndexedValue(exactFilterDocs, buildExactFilterKey("is", "bookmarked"), docIndex);\n      if (doc.favorited)\n        pushIndexedValue(exactFilterDocs, buildExactFilterKey("is", "liked"), docIndex);\n      if (doc.retweeted)\n        pushIndexedValue(exactFilterDocs, buildExactFilterKey("is", "retweeted"), docIndex);\n      if (doc.isReply)\n        pushIndexedValue(exactFilterDocs, buildExactFilterKey("is", "reply"), docIndex);\n      if (doc.isRetweet)\n        pushIndexedValue(exactFilterDocs, buildExactFilterKey("is", "retweet"), docIndex);\n      if (doc.isQuote)\n        pushIndexedValue(exactFilterDocs, buildExactFilterKey("is", "quote"), docIndex);\n      if (doc.isVerified)\n        pushIndexedValue(exactFilterDocs, buildExactFilterKey("is", "verified"), docIndex);\n      if (doc.isBlueVerified) {\n        pushIndexedValue(exactFilterDocs, buildExactFilterKey("is", "blue_verified"), docIndex);\n      }\n    }\n    return {\n      tokenDocs,\n      exactFilterDocs,\n      tokenVocabulary: [...tokenDocs.keys()].sort()\n    };\n  }\n  function intersectDocSets(current, next) {\n    const nextSet = next instanceof Set ? next : new Set(next);\n    if (!current) {\n      return new Set(nextSet);\n    }\n    const out = /* @__PURE__ */ new Set();\n    for (const value of current) {\n      if (nextSet.has(value)) {\n        out.add(value);\n      }\n    }\n    return out;\n  }\n  function unionDocSets(groups) {\n    const out = /* @__PURE__ */ new Set();\n    for (const group of groups) {\n      for (const value of group) {\n        out.add(value);\n      }\n    }\n    return out;\n  }\n  function intersectDocArrays(left, right) {\n    if (!left.length || !right.length) return [];\n    const smaller = left.length <= right.length ? left : right;\n    const larger = left.length <= right.length ? right : left;\n    const largerSet = new Set(larger);\n    const out = [];\n    for (const value of smaller) {\n      if (largerSet.has(value)) {\n        out.push(value);\n      }\n    }\n    return out;\n  }\n  function buildAdjacentTermAnchorCandidates(prepared, parsed) {\n    const orderedTerms = parsed.orderedTerms.filter(Boolean);\n    if (orderedTerms.length < SEARCH_ANCHOR_MIN_TERMS) return null;\n    const windowGroups = [];\n    for (let index = 0; index < orderedTerms.length - 1; index += 1) {\n      const leftDocs = prepared.indexes.tokenDocs.get(orderedTerms[index] || "") || [];\n      const rightDocs = prepared.indexes.tokenDocs.get(orderedTerms[index + 1] || "") || [];\n      const windowDocs = intersectDocArrays(leftDocs, rightDocs);\n      if (windowDocs.length) {\n        windowGroups.push(windowDocs);\n      }\n    }\n    if (!windowGroups.length) return null;\n    const candidateSet = unionDocSets(windowGroups);\n    if (!candidateSet.size || candidateSet.size >= prepared.docs.length) {\n      return null;\n    }\n    return candidateSet;\n  }\n  function buildRareTermAnchorCandidates(prepared, parsed) {\n    var _a;\n    const positiveTerms = parsed.positiveTerms.filter(Boolean);\n    if (positiveTerms.length < SEARCH_ANCHOR_MIN_TERMS) return null;\n    const docCount = Math.max(1, prepared.rankingContext.docCount);\n    const rankedTerms = positiveTerms.map((term) => ({\n      term,\n      docFreq: prepared.rankingContext.termDocFreq.get(term) || 0\n    })).filter((entry) => entry.docFreq > 0).sort((left, right) => {\n      if (left.docFreq !== right.docFreq) return left.docFreq - right.docFreq;\n      return left.term.localeCompare(right.term);\n    });\n    if (!rankedTerms.length) return null;\n    const nonCommon = rankedTerms.filter(\n      (entry) => entry.docFreq / docCount <= SEARCH_ANCHOR_COMMON_DOC_FRACTION\n    );\n    const anchorPool = nonCommon.length ? nonCommon : rankedTerms;\n    const rarestDocFreq = ((_a = anchorPool[0]) == null ? void 0 : _a.docFreq) || 0;\n    const comparableRarity = rarestDocFreq > 0 ? anchorPool.filter(\n      (entry) => entry.docFreq <= rarestDocFreq * SEARCH_ANCHOR_MAX_RELATIVE_DOC_FREQ\n    ) : anchorPool;\n    const anchorTerms = (comparableRarity.length ? comparableRarity : anchorPool).slice(\n      0,\n      SEARCH_ANCHOR_MAX_TERMS\n    );\n    if (!anchorTerms.length) return null;\n    const candidateSet = unionDocSets(\n      anchorTerms.map((entry) => prepared.indexes.tokenDocs.get(entry.term) || [])\n    );\n    if (!candidateSet.size || candidateSet.size >= prepared.docs.length) {\n      return null;\n    }\n    return candidateSet;\n  }\n  function getPrefixExpandedTerms(vocabulary, prefix2, maxExpansions) {\n    if (!prefix2 || prefix2.length < SEARCH_PREFIX_MIN_TERM_LENGTH) return [];\n    const out = [];\n    for (const candidate of vocabulary) {\n      if (!candidate.startsWith(prefix2)) continue;\n      out.push(candidate);\n      if (out.length >= maxExpansions) break;\n    }\n    return out;\n  }\n  function getFuzzyExpandedTerms(vocabulary, value, maxExpansions) {\n    if (!value || value.length < SEARCH_FUZZY_MIN_TERM_LENGTH || SEARCH_FUZZY_MAX_EDIT_DISTANCE <= 0) {\n      return [];\n    }\n    const prefixRoot = value.slice(0, SEARCH_FUZZY_PREFIX_ROOT_LENGTH);\n    const out = [];\n    for (const candidate of vocabulary) {\n      if (!candidate.startsWith(prefixRoot)) continue;\n      if (Math.abs(candidate.length - value.length) > SEARCH_FUZZY_MAX_EDIT_DISTANCE) continue;\n      if (levenshteinDistance(candidate, value) > SEARCH_FUZZY_MAX_EDIT_DISTANCE) continue;\n      out.push(candidate);\n      if (out.length >= maxExpansions) break;\n    }\n    return out;\n  }\n  function getLexicalTokenCandidateDocs(prepared, token) {\n    const index = prepared.indexes.tokenDocs;\n    const vocabulary = prepared.indexes.tokenVocabulary;\n    const terms = tokenizeText(token.value);\n    if (!terms.length) return [];\n    const candidateTerms = /* @__PURE__ */ new Set();\n    for (let idx = 0; idx < terms.length; idx += 1) {\n      const term = terms[idx];\n      if (!term) continue;\n      candidateTerms.add(term);\n      const isLast = idx === terms.length - 1;\n      if (!isLast) continue;\n      if (token.prefix) {\n        for (const expanded of getPrefixExpandedTerms(\n          vocabulary,\n          term,\n          SEARCH_PREFIX_MAX_EXPANSIONS\n        )) {\n          candidateTerms.add(expanded);\n        }\n      }\n      if (token.fuzzy) {\n        for (const expanded of getFuzzyExpandedTerms(vocabulary, term, SEARCH_FUZZY_MAX_EXPANSIONS)) {\n          candidateTerms.add(expanded);\n        }\n      }\n    }\n    return [...candidateTerms].flatMap((term) => index.get(term) || []);\n  }\n  function getIndexedFilterCandidates(prepared, filter) {\n    const rawValue = String(filter.value || "").trim();\n    const normalized = normalizeTextToken(rawValue);\n    if (!rawValue) return null;\n    const exactIndex = prepared.indexes.exactFilterDocs;\n    switch (filter.name) {\n      case "from":\n      case "to":\n      case "lang":\n      case "route":\n      case "mention":\n      case "hashtag":\n      case "cashtag":\n      case "id":\n        return new Set(exactIndex.get(buildExactFilterKey(filter.name, normalized)) || []);\n      case "from_id":\n      case "author_id":\n      case "to_id":\n      case "in_reply_to_id":\n      case "conversation_id":\n        return new Set(exactIndex.get(buildExactFilterKey(filter.name, rawValue)) || []);\n      case "bookmark_folder":\n        if (/^\\d+$/.test(rawValue)) {\n          return new Set(exactIndex.get(buildExactFilterKey("bookmark_folder", rawValue)) || []);\n        }\n        return null;\n      case "folder":\n        if (/^\\d+$/.test(rawValue)) {\n          return new Set(exactIndex.get(buildExactFilterKey("bookmark_folder", rawValue)) || []);\n        }\n        return null;\n      case "is":\n        return new Set(exactIndex.get(buildExactFilterKey("is", normalized)) || []);\n      default:\n        return null;\n    }\n  }\n  function buildIndexedCandidateSet(prepared, parsed, scopedFolderIds) {\n    let candidateSet = null;\n    if (scopedFolderIds.size) {\n      const folderCandidates = unionDocSets(\n        [...scopedFolderIds].map(\n          (folderId) => prepared.indexes.exactFilterDocs.get(buildExactFilterKey("bookmark_folder", folderId)) || []\n        )\n      );\n      candidateSet = intersectDocSets(candidateSet, folderCandidates);\n    }\n    for (const filter of parsed.filters) {\n      const indexed = getIndexedFilterCandidates(prepared, filter);\n      if (!indexed) continue;\n      if (filter.negated) {\n        if (!candidateSet) continue;\n        for (const value of indexed) {\n          candidateSet.delete(value);\n        }\n        continue;\n      }\n      candidateSet = intersectDocSets(candidateSet, indexed);\n    }\n    const anchorCandidates = buildAdjacentTermAnchorCandidates(prepared, parsed) || buildRareTermAnchorCandidates(prepared, parsed);\n    const lexicalTokens = parsed.positiveLexicalTokens.filter((token) => !token.field);\n    const lexicalTermCandidates = anchorCandidates || unionDocSets(lexicalTokens.map((token) => getLexicalTokenCandidateDocs(prepared, token)));\n    if (lexicalTermCandidates.size) {\n      candidateSet = intersectDocSets(candidateSet, lexicalTermCandidates);\n    }\n    return candidateSet;\n  }\n  function resolveRankingFromStorage() {\n    const ranking = { ...SEARCH_RANKING_DEFAULTS };\n    try {\n      if (typeof localStorage === "undefined") return ranking;\n      const raw = localStorage.getItem(SEARCH_RANKING_STORAGE_KEY);\n      if (!raw) return ranking;\n      const parsed = JSON.parse(raw);\n      for (const key of Object.keys(SEARCH_RANKING_DEFAULTS)) {\n        const value = Number(parsed[key]);\n        if (Number.isFinite(value)) ranking[key] = value;\n      }\n      return ranking;\n    } catch {\n      return ranking;\n    }\n  }\n  function getCachedParsedSearchQuery(query) {\n    const normalized = String(query || "").trim();\n    const cached = parsedQueryCache.get(normalized);\n    if (cached) {\n      return cached;\n    }\n    const parsed = parseSearchQuery(normalized);\n    parsedQueryCache.set(normalized, parsed);\n    if (parsedQueryCache.size > SEARCH_QUERY_PARSE_CACHE_LIMIT) {\n      const oldestKey = parsedQueryCache.keys().next().value;\n      if (typeof oldestKey === "string") {\n        parsedQueryCache.delete(oldestKey);\n      }\n    }\n    return parsed;\n  }\n  function scoreTermBM25(doc, term, rankingContext) {\n    const tf = doc.tokenFreq.get(term) || 0;\n    if (!tf) return 0;\n    const df = rankingContext.termDocFreq.get(term) || 0;\n    const docCount = Math.max(1, rankingContext.docCount);\n    const avgDocLength = Math.max(1, rankingContext.avgDocLength);\n    const dl = Math.max(1, doc.tokens.length);\n    const k1 = 1.2;\n    const b2 = 0.75;\n    const idf = Math.log(1 + (docCount - df + 0.5) / (df + 0.5));\n    const denom = tf + k1 * (1 - b2 + b2 * dl / avgDocLength);\n    return idf * (tf * (k1 + 1)) / denom;\n  }\n  function buildFilterWarnings(filters) {\n    const warnings = [];\n    const isModes = /* @__PURE__ */ new Set([\n      "bookmarked",\n      "liked",\n      "retweeted",\n      "reply",\n      "retweet",\n      "quote",\n      "media",\n      "images",\n      "videos",\n      "links",\n      "verified",\n      "blue_verified"\n    ]);\n    const hasModes = /* @__PURE__ */ new Set([\n      "media",\n      "images",\n      "videos",\n      "links",\n      "mentions",\n      "hashtags",\n      "cashtags",\n      "engagement",\n      "polls"\n    ]);\n    const compatibilityModes = /* @__PURE__ */ new Set([\n      "replies",\n      "retweets",\n      "nativeretweets",\n      "quote",\n      "media",\n      "images",\n      "videos",\n      "links",\n      "mentions",\n      "hashtags",\n      "verified",\n      "blue_verified",\n      "twimg",\n      "native_video",\n      "consumer_video",\n      "pro_video",\n      "has_engagement"\n    ]);\n    for (const filter of filters) {\n      const token = `${filter.name}:${filter.value}`;\n      const value = String(filter.value || "").trim();\n      if (!value) continue;\n      if (["since", "until"].includes(filter.name) && !parseDateToMs(value, filter.name === "until")) {\n        warnings.push({\n          code: "invalid_filter_value",\n          message: `invalid ${token}`,\n          token,\n          severity: "warn"\n        });\n        continue;\n      }\n      if ([\n        "since_time",\n        "until_time",\n        "since_id",\n        "max_id",\n        "min_faves",\n        "min_likes",\n        "min_retweets",\n        "min_replies",\n        "min_bookmarks"\n      ].includes(filter.name) && !Number.isFinite(Number(value))) {\n        warnings.push({\n          code: "invalid_filter_value",\n          message: `invalid ${token}`,\n          token,\n          severity: "warn"\n        });\n        continue;\n      }\n      if (filter.name === "is" && !isModes.has(normalizeTextToken(value))) {\n        warnings.push({\n          code: "unsupported_filter",\n          message: `unsupported ${token}`,\n          token,\n          severity: "warn"\n        });\n        continue;\n      }\n      if (filter.name === "has" && !hasModes.has(normalizeTextToken(value))) {\n        warnings.push({\n          code: "unsupported_filter",\n          message: `unsupported ${token}`,\n          token,\n          severity: "warn"\n        });\n        continue;\n      }\n      if (["filter", "include"].includes(filter.name) && !compatibilityModes.has(normalizeTextToken(value))) {\n        warnings.push({\n          code: "unsupported_filter",\n          message: `unsupported ${token}`,\n          token,\n          severity: "warn"\n        });\n        continue;\n      }\n    }\n    return warnings;\n  }\n  function evaluateFilter(doc, filter) {\n    const rawValue = String(filter.value || "").trim();\n    const value = normalizeTextToken(rawValue);\n    if (!rawValue) return true;\n    const evaluate = () => {\n      switch (filter.name) {\n        case "from":\n          return doc.authorScreenName === value;\n        case "from_id":\n        case "author_id":\n          return doc.authorId === rawValue;\n        case "to":\n          return doc.toUser === value;\n        case "to_id":\n          return doc.toUserId === rawValue;\n        case "in_reply_to_id":\n          return doc.inReplyToId === rawValue;\n        case "id":\n          return doc.id === rawValue;\n        case "lang":\n          return doc.lang === value;\n        case "route":\n          return doc.routeType === value;\n        case "conversation_id":\n          return doc.conversationId === rawValue;\n        case "bookmark_folder":\n        case "folder":\n          if (/^\\d+$/.test(rawValue)) {\n            return doc.bookmarkFolderId === rawValue;\n          }\n          return doc.bookmarkFolderName === value || doc.bookmarkFolderName.includes(value);\n        case "mention":\n          return doc.mentions.includes(value);\n        case "hashtag":\n          return doc.hashtags.includes(value);\n        case "cashtag":\n          return doc.cashtags.includes(value);\n        case "source":\n          return doc.sourceText.includes(value);\n        case "card_name": {\n          const normalizedCard = value.replace(/\\s+/g, "_");\n          return doc.cardName === normalizedCard || doc.cardName.includes(normalizedCard);\n        }\n        case "domain":\n          return doc.domains.some((domain) => domain === value || domain.endsWith(`.${value}`));\n        case "url":\n          return doc.urls.some((url) => url.includes(value));\n        case "is": {\n          if (value === "bookmarked") return doc.bookmarked;\n          if (value === "liked") return doc.favorited;\n          if (value === "retweeted") return doc.retweeted;\n          if (value === "reply") return doc.isReply;\n          if (value === "retweet") return doc.isRetweet;\n          if (value === "quote") return doc.isQuote;\n          if (value === "media") return doc.hasMedia;\n          if (value === "images") return doc.hasImages;\n          if (value === "videos") return doc.hasVideo;\n          if (value === "links") return doc.hasLinks;\n          if (value === "verified") return doc.isVerified;\n          if (value === "blue_verified") return doc.isBlueVerified;\n          return true;\n        }\n        case "has": {\n          if (value === "media") return doc.hasMedia;\n          if (value === "images") return doc.hasImages;\n          if (value === "videos") return doc.hasVideo;\n          if (value === "links") return doc.hasLinks;\n          if (value === "mentions") return doc.mentions.length > 0;\n          if (value === "hashtags") return doc.hashtags.length > 0;\n          if (value === "cashtags") return doc.cashtags.length > 0;\n          if (value === "engagement") {\n            return doc.favoriteCount + doc.retweetCount + doc.replyCount + doc.bookmarkCount > 0;\n          }\n          if (value === "polls") return doc.cardName.includes("poll");\n          return true;\n        }\n        case "filter":\n        case "include": {\n          if (filter.name === "include" && value === "nativeretweets" && !filter.negated) return true;\n          if (value === "replies") return doc.isReply;\n          if (value === "retweets") return doc.isRetweet || doc.isQuote;\n          if (value === "nativeretweets") return doc.isRetweet;\n          if (value === "quote") return doc.isQuote;\n          if (value === "media") return doc.hasMedia;\n          if (value === "images") return doc.hasImages;\n          if (value === "videos" || value === "native_video" || value === "consumer_video" || value === "pro_video") {\n            return doc.hasVideo;\n          }\n          if (value === "links") return doc.hasLinks;\n          if (value === "mentions") return doc.mentions.length > 0;\n          if (value === "hashtags") return doc.hashtags.length > 0;\n          if (value === "verified") return doc.isVerified;\n          if (value === "blue_verified") return doc.isBlueVerified;\n          if (value === "twimg") {\n            return doc.urls.some(\n              (url) => url.includes("pbs.twimg.com") || url.includes("pic.twitter.com")\n            );\n          }\n          if (value === "has_engagement") {\n            return doc.favoriteCount + doc.retweetCount + doc.replyCount + doc.bookmarkCount > 0;\n          }\n          return true;\n        }\n        case "since": {\n          const minTs = parseDateToMs(rawValue, false);\n          if (!minTs || !doc.createdAtMs) return true;\n          return doc.createdAtMs >= minTs;\n        }\n        case "until": {\n          const maxTs = parseDateToMs(rawValue, true);\n          if (!maxTs || !doc.createdAtMs) return true;\n          return doc.createdAtMs < maxTs;\n        }\n        case "since_time": {\n          const minTs = Number(rawValue) * 1e3;\n          if (!Number.isFinite(minTs) || !doc.createdAtMs) return true;\n          return doc.createdAtMs >= minTs;\n        }\n        case "until_time": {\n          const maxTs = Number(rawValue) * 1e3;\n          if (!Number.isFinite(maxTs) || !doc.createdAtMs) return true;\n          return doc.createdAtMs < maxTs;\n        }\n        case "since_id":\n          return /^\\d+$/.test(doc.id) && Number(doc.id) > Number(rawValue);\n        case "max_id":\n          return /^\\d+$/.test(doc.id) && Number(doc.id) <= Number(rawValue);\n        case "min_faves":\n        case "min_likes":\n          return doc.favoriteCount >= Number(rawValue);\n        case "min_retweets":\n          return doc.retweetCount >= Number(rawValue);\n        case "min_replies":\n          return doc.replyCount >= Number(rawValue);\n        case "min_bookmarks":\n          return doc.bookmarkCount >= Number(rawValue);\n        default:\n          return true;\n      }\n    };\n    const matched = evaluate();\n    return filter.negated ? !matched : matched;\n  }\n  function tokenMatchesTokenSet(targetTokens, targetFreq, token) {\n    if (token.kind === "phrase") {\n      const phraseTokens = tokenizeText(token.value);\n      const slop = phraseSlop(targetTokens, phraseTokens, !!token.prefix);\n      if (slop === null) {\n        if (token.fuzzy && phraseTokens.length) {\n          const fuzzyLast = phraseTokens[phraseTokens.length - 1];\n          const fuzzyPrefix = fuzzyLast && fuzzyLast.length >= SEARCH_FUZZY_MIN_TERM_LENGTH ? getFuzzyExpandedTerms(\n            [...new Set(targetTokens)],\n            fuzzyLast,\n            SEARCH_FUZZY_MAX_EXPANSIONS\n          ) : [];\n          for (const candidate of fuzzyPrefix) {\n            const mutated = [...phraseTokens];\n            mutated[mutated.length - 1] = candidate;\n            const fuzzySlop = phraseSlop(targetTokens, mutated, false);\n            if (fuzzySlop !== null && fuzzySlop <= token.slop) {\n              return { matched: true, slopUsed: fuzzySlop };\n            }\n          }\n        }\n        return { matched: false, slopUsed: Number.POSITIVE_INFINITY };\n      }\n      if (slop > token.slop) return { matched: false, slopUsed: slop };\n      return { matched: true, slopUsed: slop };\n    }\n    const normalized = tokenizeText(token.value);\n    if (!normalized.length) {\n      return { matched: false, slopUsed: Number.POSITIVE_INFINITY };\n    }\n    if (normalized.length === 1) {\n      const first = normalized[0];\n      if (!first) {\n        return { matched: false, slopUsed: Number.POSITIVE_INFINITY };\n      }\n      let matched = targetFreq.has(first);\n      if (!matched && token.prefix && first.length >= SEARCH_PREFIX_MIN_TERM_LENGTH) {\n        matched = [...targetFreq.keys()].some((candidate) => candidate.startsWith(first));\n      }\n      if (!matched && token.fuzzy && first.length >= SEARCH_FUZZY_MIN_TERM_LENGTH) {\n        matched = [...targetFreq.keys()].some(\n          (candidate) => candidate.startsWith(first.slice(0, SEARCH_FUZZY_PREFIX_ROOT_LENGTH)) && Math.abs(candidate.length - first.length) <= SEARCH_FUZZY_MAX_EDIT_DISTANCE && levenshteinDistance(candidate, first) <= SEARCH_FUZZY_MAX_EDIT_DISTANCE\n        );\n      }\n      return { matched, slopUsed: matched ? 0 : Number.POSITIVE_INFINITY };\n    }\n    const slopUsed = phraseSlop(targetTokens, normalized, !!token.prefix);\n    return {\n      matched: slopUsed !== null && slopUsed <= 0,\n      slopUsed: slopUsed ?? Number.POSITIVE_INFINITY\n    };\n  }\n  function tokenMatchesDoc(doc, token) {\n    const fieldSearch = token.field ? getFieldSearchData(doc, token.field) : null;\n    if (fieldSearch) {\n      const match = tokenMatchesTokenSet(fieldSearch.tokens, fieldSearch.tokenFreq, token);\n      return {\n        matched: match.matched,\n        slopUsed: match.slopUsed,\n        primaryMatched: match.matched,\n        quotedMatched: false\n      };\n    }\n    const primary = tokenMatchesTokenSet(doc.primaryTokens, doc.primaryTokenFreq, token);\n    const quoted = doc.quotedTokens.length > 0 ? tokenMatchesTokenSet(doc.quotedTokens, doc.quotedTokenFreq, token) : { matched: false, slopUsed: Number.POSITIVE_INFINITY };\n    if (!primary.matched && !quoted.matched) {\n      return {\n        matched: false,\n        slopUsed: Number.POSITIVE_INFINITY,\n        primaryMatched: false,\n        quotedMatched: false\n      };\n    }\n    let slopUsed = Number.POSITIVE_INFINITY;\n    if (primary.matched) slopUsed = Math.min(slopUsed, primary.slopUsed);\n    if (quoted.matched) slopUsed = Math.min(slopUsed, quoted.slopUsed);\n    return {\n      matched: true,\n      slopUsed,\n      primaryMatched: primary.matched,\n      quotedMatched: quoted.matched\n    };\n  }\n  function tokenScore(token, slopUsed, rankingValues, quoteOnly = false) {\n    if (token.kind === "phrase") {\n      const base = token.quoted && token.slop === 0 ? rankingValues.quoted_phrase_match : rankingValues.phrase_match;\n      const scaled2 = base * token.boost / (1 + Math.max(0, slopUsed));\n      return quoteOnly ? scaled2 * 0.2 : scaled2;\n    }\n    const scaled = rankingValues.term_match * token.boost;\n    return quoteOnly ? scaled * 0.2 : scaled;\n  }\n  function computeExactPhraseTieBreak(doc, positiveLexicalTokens) {\n    let exactPhraseTerms = 0;\n    let exactQuotedPhraseTerms = 0;\n    let exactPrimaryPhraseTerms = 0;\n    let exactPrimaryQuotedPhraseTerms = 0;\n    for (const token of positiveLexicalTokens) {\n      if (token.kind !== "phrase") continue;\n      const match = tokenMatchesDoc(doc, token);\n      if (!match.matched || match.slopUsed !== 0) continue;\n      const phraseTerms = tokenizeText(token.value);\n      const termCount = phraseTerms.length;\n      if (termCount > exactPhraseTerms) {\n        exactPhraseTerms = termCount;\n      }\n      if (match.primaryMatched && termCount > exactPrimaryPhraseTerms) {\n        exactPrimaryPhraseTerms = termCount;\n      }\n      if (token.quoted && token.slop === 0 && termCount > exactQuotedPhraseTerms) {\n        exactQuotedPhraseTerms = termCount;\n      }\n      if (token.quoted && token.slop === 0 && match.primaryMatched && termCount > exactPrimaryQuotedPhraseTerms) {\n        exactPrimaryQuotedPhraseTerms = termCount;\n      }\n    }\n    return {\n      exactPhraseTerms,\n      exactQuotedPhraseTerms,\n      exactPrimaryPhraseTerms,\n      exactPrimaryQuotedPhraseTerms\n    };\n  }\n  function evaluateLexicalAst(doc, node, rankingValues) {\n    if (node.kind === "term" || node.kind === "phrase") {\n      const match = tokenMatchesDoc(doc, node);\n      if (!match.matched) {\n        return { matched: false, lexicalRaw: 0 };\n      }\n      return {\n        matched: true,\n        lexicalRaw: tokenScore(\n          node,\n          match.slopUsed,\n          rankingValues,\n          match.quotedMatched && !match.primaryMatched\n        )\n      };\n    }\n    if (node.kind !== "op") {\n      return { matched: true, lexicalRaw: 0 };\n    }\n    if (node.op === "NOT") {\n      const child = evaluateLexicalAst(doc, node.child, rankingValues);\n      return { matched: !child.matched, lexicalRaw: 0 };\n    }\n    if (node.op === "AND") {\n      const left2 = evaluateLexicalAst(doc, node.left, rankingValues);\n      if (!left2.matched) return { matched: false, lexicalRaw: 0 };\n      const right2 = evaluateLexicalAst(doc, node.right, rankingValues);\n      if (!right2.matched) return { matched: false, lexicalRaw: 0 };\n      return { matched: true, lexicalRaw: left2.lexicalRaw + right2.lexicalRaw };\n    }\n    const left = evaluateLexicalAst(doc, node.left, rankingValues);\n    const right = evaluateLexicalAst(doc, node.right, rankingValues);\n    if (!left.matched && !right.matched) {\n      return { matched: false, lexicalRaw: 0 };\n    }\n    if (left.matched && right.matched) {\n      return { matched: true, lexicalRaw: left.lexicalRaw + right.lexicalRaw };\n    }\n    return left.matched ? left : right;\n  }\n  function computeCoverDensity(doc, orderedTerms, rankingValues) {\n    const terms = orderedTerms.filter(Boolean);\n    if (!terms.length) return 0;\n    let density = 0;\n    if (terms.length >= 2) {\n      for (let index = 0; index < terms.length - 1; index += 1) {\n        const phraseTerms = terms.slice(index, index + 2);\n        const slop = phraseSlop(doc.tokens, phraseTerms);\n        if (slop === null || slop > 2) continue;\n        density += rankingValues.cover_bigram / (1 + slop);\n      }\n    }\n    if (terms.length >= 3) {\n      for (let index = 0; index < terms.length - 2; index += 1) {\n        const phraseTerms = terms.slice(index, index + 3);\n        const slop = phraseSlop(doc.tokens, phraseTerms);\n        if (slop === null || slop > 3) continue;\n        density += rankingValues.cover_trigram / (1 + slop);\n      }\n    }\n    return density;\n  }\n  function prepareAdvancedTableSearchCorpus(records) {\n    const docs = records.map((record) => getCachedSearchDoc(record));\n    return {\n      records,\n      docs,\n      rankingContext: buildRankingContext(docs),\n      indexes: buildSearchCorpusIndexes(docs)\n    };\n  }\n  function runAdvancedTableSearchPrepared(prepared, query, options = {}) {\n    const normalizedQuery = String(query || "").trim();\n    const scopedFolderIds = new Set(\n      (options.bookmarkFolderIds || []).map((value) => String(value || "").trim()).filter(Boolean)\n    );\n    const hasFolderScope = scopedFolderIds.size > 0;\n    if (!normalizedQuery) {\n      const records = hasFolderScope ? prepared.docs.filter((doc) => doc.bookmarkFolderId && scopedFolderIds.has(doc.bookmarkFolderId)).map((doc) => doc.raw) : prepared.records;\n      return {\n        records,\n        highlightTerms: [],\n        totalMatches: records.length,\n        warnings: [],\n        warningObjects: [],\n        parsed: {\n          query: "",\n          lexicalExpression: "",\n          filterBooleanSemantics: "global_and"\n        }\n      };\n    }\n    const rankingValues = resolveRankingFromStorage();\n    const parsed = getCachedParsedSearchQuery(normalizedQuery);\n    const warningObjects = [...parsed.warnings, ...buildFilterWarnings(parsed.filters)];\n    const now = Date.now();\n    const candidateSet = buildIndexedCandidateSet(prepared, parsed, scopedFolderIds);\n    const candidateDocs = candidateSet && candidateSet.size < prepared.docs.length ? [...candidateSet].sort((a2, b2) => a2 - b2).map((index) => prepared.docs[index]).filter((doc) => !!doc) : prepared.docs;\n    const matches = [];\n    for (const doc of candidateDocs) {\n      if (hasFolderScope && (!doc.bookmarkFolderId || !scopedFolderIds.has(doc.bookmarkFolderId))) {\n        continue;\n      }\n      let filtersOk = true;\n      for (const filter of parsed.filters) {\n        if (!evaluateFilter(doc, filter)) {\n          filtersOk = false;\n          break;\n        }\n      }\n      if (!filtersOk) continue;\n      let lexicalMatched = !parsed.hasPositiveLexical;\n      let lexicalRaw = 0;\n      if (parsed.lexicalAst) {\n        const lexical = evaluateLexicalAst(doc, parsed.lexicalAst, rankingValues);\n        lexicalMatched = lexical.matched;\n        lexicalRaw = lexical.lexicalRaw;\n      }\n      if (!lexicalMatched) continue;\n      let bm25Raw = 0;\n      for (const term of parsed.positiveTerms) {\n        bm25Raw += scoreTermBM25(doc, term, prepared.rankingContext);\n      }\n      const coverDensityRaw = computeCoverDensity(doc, parsed.orderedTerms, rankingValues);\n      const weightedBm25 = bm25Raw * rankingValues.bm25;\n      const weightedLexical = lexicalRaw * rankingValues.lexical;\n      const weightedDensity = coverDensityRaw * rankingValues.cover_density;\n      const recencyBonus = (doc.createdAtMs ? doc.createdAtMs / 1e15 : now / 1e15) * rankingValues.recency;\n      const phraseTieBreak = computeExactPhraseTieBreak(doc, parsed.positiveLexicalTokens);\n      const score = weightedBm25 + weightedLexical + weightedDensity + recencyBonus;\n      matches.push({\n        doc,\n        score,\n        weightedBm25,\n        weightedLexical,\n        weightedDensity,\n        exactPhraseTerms: phraseTieBreak.exactPhraseTerms,\n        exactQuotedPhraseTerms: phraseTieBreak.exactQuotedPhraseTerms,\n        exactPrimaryPhraseTerms: phraseTieBreak.exactPrimaryPhraseTerms,\n        exactPrimaryQuotedPhraseTerms: phraseTieBreak.exactPrimaryQuotedPhraseTerms\n      });\n    }\n    matches.sort((left, right) => {\n      if (right.score !== left.score) return right.score - left.score;\n      if (right.exactPrimaryQuotedPhraseTerms !== left.exactPrimaryQuotedPhraseTerms) {\n        return right.exactPrimaryQuotedPhraseTerms - left.exactPrimaryQuotedPhraseTerms;\n      }\n      if (right.exactPrimaryPhraseTerms !== left.exactPrimaryPhraseTerms) {\n        return right.exactPrimaryPhraseTerms - left.exactPrimaryPhraseTerms;\n      }\n      if (right.exactQuotedPhraseTerms !== left.exactQuotedPhraseTerms) {\n        return right.exactQuotedPhraseTerms - left.exactQuotedPhraseTerms;\n      }\n      if (right.exactPhraseTerms !== left.exactPhraseTerms) {\n        return right.exactPhraseTerms - left.exactPhraseTerms;\n      }\n      if (right.weightedLexical !== left.weightedLexical)\n        return right.weightedLexical - left.weightedLexical;\n      if (right.weightedDensity !== left.weightedDensity)\n        return right.weightedDensity - left.weightedDensity;\n      if (right.doc.createdAtMs !== left.doc.createdAtMs)\n        return right.doc.createdAtMs - left.doc.createdAtMs;\n      return right.doc.id.localeCompare(left.doc.id);\n    });\n    const dedupedMatches = [];\n    const seenSearchSignatures = /* @__PURE__ */ new Set();\n    for (const entry of matches) {\n      const signature = [\n        entry.doc.authorId || entry.doc.authorScreenName || "",\n        entry.doc.text.replace(/\\s+/g, " ").trim().toLowerCase()\n      ].join("::");\n      if (signature !== "::" && seenSearchSignatures.has(signature)) {\n        continue;\n      }\n      if (signature !== "::") {\n        seenSearchSignatures.add(signature);\n      }\n      dedupedMatches.push(entry);\n    }\n    const resultLimit = Number(options.limit || 0);\n    const resultMatches = Number.isFinite(resultLimit) && resultLimit > 0 ? dedupedMatches.slice(0, Math.max(1, Math.floor(resultLimit))) : dedupedMatches;\n    return {\n      records: resultMatches.map((entry) => entry.doc.raw),\n      highlightTerms: parsed.highlightTerms,\n      totalMatches: dedupedMatches.length,\n      warnings: warningObjects.map((item) => item.message),\n      warningObjects,\n      parsed: {\n        query: parsed.query,\n        lexicalExpression: parsed.lexicalExpression,\n        filterBooleanSemantics: parsed.filterBooleanSemantics\n      }\n    };\n  }\n  function nowMs() {\n    if (typeof performance !== "undefined" && typeof performance.now === "function") {\n      return performance.now();\n    }\n    return Date.now();\n  }\n  const corpora = /* @__PURE__ */ new Map();\n  const cancelledRequests = /* @__PURE__ */ new Set();\n  function post(message) {\n    self.postMessage(message);\n  }\n  function errorMessage(error) {\n    return error instanceof Error ? error.message : String(error);\n  }\n  function buildCorpus(scopeKey, rows) {\n    const records = rows.map((row) => row.record);\n    const idByRecord = /* @__PURE__ */ new Map();\n    rows.forEach((row, index) => {\n      idByRecord.set(row.record, row.id || `row-${index}`);\n    });\n    return {\n      rows,\n      prepared: prepareAdvancedTableSearchCorpus(records),\n      idByRecord\n    };\n  }\n  self.onmessage = (event) => {\n    const request = event.data;\n    if (!request || typeof request !== "object") return;\n    if (request.type === "search:cancel") {\n      cancelledRequests.add(request.requestId);\n      return;\n    }\n    if (request.type === "search:dispose") {\n      if (request.scopeKey) {\n        corpora.delete(request.scopeKey);\n      } else {\n        corpora.clear();\n      }\n      cancelledRequests.delete(request.requestId);\n      return;\n    }\n    const start = nowMs();\n    try {\n      if (request.type === "search:set-corpus") {\n        const corpus = buildCorpus(request.scopeKey, request.records || []);\n        corpora.set(request.scopeKey, corpus);\n        post({\n          type: "search:corpus-ready",\n          requestId: request.requestId,\n          scopeKey: request.scopeKey,\n          corpusSize: corpus.rows.length,\n          elapsedMs: nowMs() - start\n        });\n        return;\n      }\n      if (request.type === "search:query") {\n        const corpus = corpora.get(request.scopeKey);\n        if (!corpus) {\n          post({\n            type: "search:error",\n            requestId: request.requestId,\n            scopeKey: request.scopeKey,\n            error: `Search corpus not ready for scope: ${request.scopeKey}`,\n            elapsedMs: nowMs() - start\n          });\n          return;\n        }\n        if (cancelledRequests.has(request.requestId)) {\n          cancelledRequests.delete(request.requestId);\n          return;\n        }\n        const result = runAdvancedTableSearchPrepared(\n          corpus.prepared,\n          request.query,\n          request.options\n        );\n        if (cancelledRequests.has(request.requestId)) {\n          cancelledRequests.delete(request.requestId);\n          return;\n        }\n        const ids = result.records.map((record, index) => {\n          var _a;\n          return corpus.idByRecord.get(record) || ((_a = corpus.rows[index]) == null ? void 0 : _a.id) || `row-${index}`;\n        });\n        post({\n          type: "search:result",\n          requestId: request.requestId,\n          scopeKey: request.scopeKey,\n          query: request.query,\n          ids,\n          elapsedMs: nowMs() - start,\n          corpusSize: corpus.rows.length,\n          result: {\n            highlightTerms: result.highlightTerms,\n            totalMatches: result.totalMatches,\n            warnings: result.warnings,\n            warningObjects: result.warningObjects,\n            parsed: result.parsed\n          }\n        });\n      }\n    } catch (error) {\n      post({\n        type: "search:error",\n        requestId: request.requestId,\n        scopeKey: "scopeKey" in request ? request.scopeKey : void 0,\n        error: errorMessage(error),\n        elapsedMs: nowMs() - start\n      });\n    }\n  };\n})();\n';
  const blob = typeof self !== "undefined" && self.Blob && new Blob([jsContent], { type: "text/javascript;charset=utf-8" });
  function WorkerWrapper(options2) {
    let objURL;
    try {
      objURL = blob && (self.URL || self.webkitURL).createObjectURL(blob);
      if (!objURL) throw "";
      const worker = new Worker(objURL, {
        name: options2 == null ? void 0 : options2.name
      });
      worker.addEventListener("error", () => {
        (self.URL || self.webkitURL).revokeObjectURL(objURL);
      });
      return worker;
    } catch (e) {
      return new Worker(
        "data:text/javascript;charset=utf-8," + encodeURIComponent(jsContent),
        {
          name: options2 == null ? void 0 : options2.name
        }
      );
    } finally {
      objURL && (self.URL || self.webkitURL).revokeObjectURL(objURL);
    }
  }
  function createRequestId(prefix2) {
    if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
      return `${prefix2}-${crypto.randomUUID()}`;
    }
    return `${prefix2}-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
  }
  class SearchWorkerClient {
    constructor() {
      __publicField(this, "worker", null);
      __publicField(this, "pending", /* @__PURE__ */ new Map());
      __publicField(this, "available", false);
      try {
        this.worker = new WorkerWrapper();
        this.worker.onmessage = (event) => this.handleMessage(event.data);
        this.worker.onerror = (event) => {
          setWorkerAvailability("search", false);
          incrementPerfCounter("search:worker:error");
          for (const [requestId, pending] of this.pending) {
            pending.reject(new Error(event.message || `Search worker error: ${requestId}`));
          }
          this.pending.clear();
        };
        this.available = true;
        setWorkerAvailability("search", true);
      } catch (error) {
        this.worker = null;
        this.available = false;
        setWorkerAvailability("search", false);
        recordPerfMetric({
          kind: "worker",
          name: "search-create-failed",
          tags: { error: error instanceof Error ? error.message : String(error) }
        });
      }
    }
    isAvailable() {
      return this.available && !!this.worker;
    }
    async setCorpus(scopeKey, records) {
      const requestId = createRequestId("corpus");
      return this.request({ type: "search:set-corpus", requestId, scopeKey, records });
    }
    async query(args) {
      const requestId = args.requestId || createRequestId("query");
      const response = await this.request({
        type: "search:query",
        requestId,
        scopeKey: args.scopeKey,
        query: args.query,
        options: args.options
      });
      if (response.type === "search:result") return response;
      if (response.type === "search:error") throw new Error(response.error);
      throw new Error(`Unexpected search worker response: ${response.type}`);
    }
    cancel(requestId) {
      const pending = this.pending.get(requestId);
      if (pending) {
        pending.reject(new Error(`Search request cancelled: ${requestId}`));
        this.pending.delete(requestId);
      }
      if (!this.worker) return;
      this.worker.postMessage({ type: "search:cancel", requestId });
      incrementPerfCounter("search:cancelled");
    }
    dispose() {
      var _a2;
      for (const [requestId, pending] of this.pending) {
        pending.reject(new Error(`Search worker disposed: ${requestId}`));
      }
      this.pending.clear();
      (_a2 = this.worker) == null ? void 0 : _a2.terminate();
      this.worker = null;
      this.available = false;
    }
    request(message) {
      if (!this.worker) {
        return Promise.reject(new Error("Search worker unavailable"));
      }
      const startedAt = nowMs();
      return new Promise((resolve, reject) => {
        var _a2;
        this.pending.set(message.requestId, { resolve, reject, startedAt });
        (_a2 = this.worker) == null ? void 0 : _a2.postMessage(message);
      });
    }
    handleMessage(message) {
      const pending = this.pending.get(message.requestId);
      if (!pending) {
        incrementPerfCounter("search:stale-response");
        return;
      }
      this.pending.delete(message.requestId);
      recordPerfMetric({
        kind: "search",
        name: message.type === "search:result" ? "worker-query" : "worker-corpus",
        durationMs: nowMs() - pending.startedAt,
        tags: {
          type: message.type,
          corpusSize: "corpusSize" in message ? message.corpusSize : void 0,
          resultCount: message.type === "search:result" ? message.ids.length : void 0
        }
      });
      pending.resolve(message);
    }
  }
  function getAccessorPathValue(record, path) {
    if (!record || typeof record !== "object") return void 0;
    const parts = path.split(".");
    let current = record;
    for (const part of parts) {
      if (!current || typeof current !== "object") return void 0;
      current = current[part];
    }
    return current;
  }
  function normalizeSortValue(value) {
    if (value === null || value === void 0) return "";
    if (typeof value === "number" && Number.isFinite(value)) return value;
    if (typeof value === "boolean") return value ? 1 : 0;
    if (value instanceof Date) return value.getTime();
    return String(value).toLowerCase();
  }
  function compareSortValues(left, right) {
    const a = normalizeSortValue(left);
    const b = normalizeSortValue(right);
    if (typeof a === "number" && typeof b === "number") {
      return a - b;
    }
    return String(a).localeCompare(String(b), void 0, {
      numeric: true,
      sensitivity: "base"
    });
  }
  function flattenLeafColumns(columns2) {
    const out = [];
    for (const column of columns2) {
      if ("columns" in column && Array.isArray(column.columns)) {
        out.push(...flattenLeafColumns(column.columns));
        continue;
      }
      out.push(column);
    }
    return out;
  }
  function resolveColumnId(column) {
    if ("id" in column && typeof column.id === "string" && column.id) {
      return column.id;
    }
    if ("accessorKey" in column && typeof column.accessorKey === "string") {
      return column.accessorKey;
    }
    return "";
  }
  function resolveColumnValue(column, record, rowIndex) {
    if ("accessorFn" in column && typeof column.accessorFn === "function") {
      return column.accessorFn(record, rowIndex);
    }
    if ("accessorKey" in column) {
      if (typeof column.accessorKey === "string") {
        return getAccessorPathValue(record, column.accessorKey);
      }
      if (typeof column.accessorKey === "number" && Array.isArray(record)) {
        return record[column.accessorKey];
      }
    }
    return void 0;
  }
  function asFiniteNumber(value) {
    if (typeof value === "number" && Number.isFinite(value)) return value;
    const numeric = Number(value);
    return Number.isFinite(numeric) ? numeric : null;
  }
  function asTimestamp(value) {
    if (value === null || value === void 0 || value === "") return null;
    const numeric = asFiniteNumber(value);
    if (numeric !== null) {
      return numeric > 1e12 ? numeric : numeric * 1e3;
    }
    if (typeof value === "string") {
      const parsedTwitter = Number(parseTwitterDateTime(value) || 0);
      if (Number.isFinite(parsedTwitter) && parsedTwitter > 0) return parsedTwitter;
      const parsedDate = Date.parse(value);
      if (Number.isFinite(parsedDate)) return parsedDate;
    }
    return null;
  }
  function resolveRecordRecency(record) {
    if (!record || typeof record !== "object") return 0;
    const candidatePaths = [
      "twe_private_fields.created_at",
      "created_at",
      "legacy.created_at",
      "core.created_at",
      "article.published_at",
      "__seen_at"
    ];
    for (const path of candidatePaths) {
      const value = getAccessorPathValue(record, path);
      const ts = asTimestamp(value);
      if (ts !== null && ts > 0) return ts;
    }
    return 0;
  }
  const MAX_QUERY_HYDRATE_RECORDS = 1200;
  const MAX_FOLDER_HYDRATE_RECORDS = 6e3;
  const FOLDER_HYDRATE_BATCH_SIZE = 320;
  function createEmptySearchResult(query, warnings = []) {
    return {
      records: [],
      highlightTerms: [],
      totalMatches: 0,
      warnings,
      warningObjects: warnings.map((message) => ({
        code: "unsupported_token",
        message,
        severity: "warn"
      })),
      parsed: {
        query,
        lexicalExpression: "",
        filterBooleanSemantics: "global_and"
      }
    };
  }
  function createSearchResultFromRecords(args) {
    return {
      records: args.records,
      highlightTerms: [],
      totalMatches: args.totalMatches,
      warnings: [],
      warningObjects: [],
      parsed: {
        query: args.query,
        lexicalExpression: "",
        filterBooleanSemantics: "global_and"
      }
    };
  }
  function createSearchRequestId() {
    if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
      return `search-${crypto.randomUUID()}`;
    }
    return `search-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
  }
  function readRecordPath(record, path) {
    let current = record;
    for (const part of path.split(".")) {
      if (!current || typeof current !== "object") return void 0;
      current = current[part];
    }
    return current;
  }
  function createWorkerSearchRecord(record) {
    if (!record || typeof record !== "object") return record;
    const row = record;
    return {
      __typename: row.__typename,
      rest_id: row.rest_id,
      id_str: row.id_str,
      __bookmark_folder_id: row.__bookmark_folder_id,
      __bookmark_folder_name: row.__bookmark_folder_name,
      __route_type: row.__route_type,
      twe_relationship_fields: row.twe_relationship_fields,
      note_tweet: row.note_tweet,
      article: row.article,
      quoted_status_result: row.quoted_status_result,
      card: row.card,
      views: row.views,
      core: row.core,
      verification: row.verification,
      is_blue_verified: row.is_blue_verified,
      legacy: {
        id_str: readRecordPath(row, "legacy.id_str"),
        full_text: readRecordPath(row, "legacy.full_text"),
        text: readRecordPath(row, "legacy.text"),
        description: readRecordPath(row, "legacy.description"),
        created_at: readRecordPath(row, "legacy.created_at"),
        source: readRecordPath(row, "legacy.source"),
        lang: readRecordPath(row, "legacy.lang"),
        entities: readRecordPath(row, "legacy.entities"),
        extended_entities: readRecordPath(row, "legacy.extended_entities"),
        in_reply_to_screen_name: readRecordPath(row, "legacy.in_reply_to_screen_name"),
        in_reply_to_user_id_str: readRecordPath(row, "legacy.in_reply_to_user_id_str"),
        in_reply_to_status_id_str: readRecordPath(row, "legacy.in_reply_to_status_id_str"),
        conversation_id_str: readRecordPath(row, "legacy.conversation_id_str"),
        favorite_count: readRecordPath(row, "legacy.favorite_count"),
        retweet_count: readRecordPath(row, "legacy.retweet_count"),
        reply_count: readRecordPath(row, "legacy.reply_count"),
        bookmark_count: readRecordPath(row, "legacy.bookmark_count"),
        quote_count: readRecordPath(row, "legacy.quote_count"),
        favorited: readRecordPath(row, "legacy.favorited"),
        retweeted: readRecordPath(row, "legacy.retweeted"),
        bookmarked: readRecordPath(row, "legacy.bookmarked"),
        retweeted_status_result: readRecordPath(row, "legacy.retweeted_status_result")
      }
    };
  }
  function createWorkerSearchRecordFromDocument(document2) {
    var _a2, _b2, _c, _d, _e;
    const entityId = document2.raw_ref_key || document2.entity_id;
    const primaryText = [document2.primary_text, document2.quoted_text, document2.auxiliary_text].filter((value) => typeof value === "string" && value.trim().length > 0).join("\n\n");
    return {
      rest_id: entityId,
      id_str: entityId,
      __bookmark_folder_id: document2.folder_id,
      __bookmark_folder_name: document2.folder_name,
      __route_type: document2.route_type,
      core: {
        user_results: {
          result: {
            rest_id: document2.author_id,
            core: {
              screen_name: document2.author_screen_name,
              name: document2.author_screen_name
            },
            legacy: {
              screen_name: document2.author_screen_name,
              name: document2.author_screen_name
            }
          }
        }
      },
      legacy: {
        id_str: entityId,
        full_text: primaryText,
        text: primaryText,
        created_at: document2.created_at_ms ? new Date(document2.created_at_ms).toUTCString() : void 0,
        lang: document2.lang,
        favorite_count: (_a2 = document2.numeric_json) == null ? void 0 : _a2.favorite_count,
        retweet_count: (_b2 = document2.numeric_json) == null ? void 0 : _b2.retweet_count,
        reply_count: (_c = document2.numeric_json) == null ? void 0 : _c.reply_count,
        bookmark_count: (_d = document2.numeric_json) == null ? void 0 : _d.bookmark_count,
        quote_count: (_e = document2.numeric_json) == null ? void 0 : _e.quote_count
      }
    };
  }
  function useResultSetController({
    title,
    viewStateKey,
    fullscreen,
    onFullscreenChange,
    records,
    searchDocuments,
    hydrateRecordsByIds,
    columns: columns2,
    alternateViews
  }) {
    const [searchQuery, setSearchQuery] = hooks.useState("");
    const [debouncedSearchQuery, setDebouncedSearchQuery] = hooks.useState("");
    const [selectedFolders, setSelectedFolders] = hooks.useState([]);
    const [rowSelection, setRowSelection] = hooks.useState({});
    const [sorting, setSorting] = hooks.useState([]);
    const [selectionMode, setSelectionMode] = hooks.useState("all");
    const [internalFullscreen, setInternalFullscreen] = hooks.useState(false);
    const [activeViewId, setActiveViewId] = hooks.useState("table");
    const [asyncSearchState, setAsyncSearchState] = hooks.useState(null);
    const [searchHydratedRecords, setSearchHydratedRecords] = hooks.useState([]);
    const [folderHydrationAttemptState, setFolderHydrationAttemptState] = hooks.useState(() => ({ key: "", ids: /* @__PURE__ */ new Set() }));
    const searchClientRef = hooks.useRef(null);
    const latestSearchRequestIdRef = hooks.useRef("");
    const inFlightSearchKeyRef = hooks.useRef("");
    const completedSearchKeyRef = hooks.useRef("");
    const readyCorpusKeyRef = hooks.useRef("");
    const warmingCorpusKeyRef = hooks.useRef("");
    const warmingCorpusPromiseRef = hooks.useRef(null);
    const activeFolderHydrationKeyRef = hooks.useRef("");
    const completedFolderHydrationKeyRef = hooks.useRef("");
    const normalizedSearchQuery = debouncedSearchQuery.trim();
    const hasSearchQuery = normalizedSearchQuery.length > 0;
    const hasFolderScope = selectedFolders.length > 0;
    const needsWorkerSearch = hasSearchQuery;
    const searchDebounceMs = hooks.useMemo(() => {
      const trimmed = searchQuery.trim();
      if (!trimmed) return 60;
      const hasStructuredSyntax = /["():]|^[@#$-]/.test(trimmed);
      const tokenCount = trimmed.split(/\s+/).filter(Boolean).length;
      if (hasStructuredSyntax || tokenCount > 1) {
        return 180;
      }
      return 240;
    }, [searchQuery]);
    const isFullscreen = fullscreen ?? internalFullscreen;
    const setIsFullscreen = (value) => {
      const nextValue = typeof value === "function" ? value(isFullscreen) : value;
      if (fullscreen === void 0) {
        setInternalFullscreen(nextValue);
      }
      onFullscreenChange == null ? void 0 : onFullscreenChange(nextValue);
    };
    const resolvedViewStateKey = hooks.useMemo(
      () => `twe_table_view_state_v2:${viewStateKey || title}`,
      [title, viewStateKey]
    );
    const scopeKey = hooks.useMemo(() => {
      if (searchDocuments == null ? void 0 : searchDocuments.length) {
        return `${resolvedViewStateKey}:search-documents:${searchDocuments.length}`;
      }
      return `${resolvedViewStateKey}:records:${records.length}`;
    }, [records.length, resolvedViewStateKey, searchDocuments == null ? void 0 : searchDocuments.length]);
    const selectedFolderKey = hooks.useMemo(() => [...selectedFolders].sort().join(","), [selectedFolders]);
    const recordIds = hooks.useMemo(
      () => records.map((record, index) => extractStableRecordId(record, index)),
      [records]
    );
    const corpusIdentityKey = hooks.useMemo(() => {
      if (searchDocuments == null ? void 0 : searchDocuments.length) {
        const first = searchDocuments[0];
        const last = searchDocuments[searchDocuments.length - 1];
        return [
          scopeKey,
          "docs",
          searchDocuments.length,
          (first == null ? void 0 : first.id) || "",
          (first == null ? void 0 : first.updated_at_ms) || "",
          (last == null ? void 0 : last.id) || "",
          (last == null ? void 0 : last.updated_at_ms) || ""
        ].join(":");
      }
      return [
        scopeKey,
        "records",
        records.length,
        recordIds[0] || "",
        recordIds[recordIds.length - 1] || ""
      ].join(":");
    }, [recordIds, records.length, scopeKey, searchDocuments]);
    const asyncSearchKey = `${corpusIdentityKey}:${normalizedSearchQuery}:${selectedFolderKey}`;
    const recordById = hooks.useMemo(() => {
      const map = /* @__PURE__ */ new Map();
      records.forEach((record, index) => {
        for (const id2 of collectRecordLookupIds(record, index)) {
          map.set(id2, record);
        }
        map.set(recordIds[index] || `row-${index}`, record);
      });
      searchHydratedRecords.forEach((record, index) => {
        for (const id2 of collectRecordLookupIds(record, index)) {
          map.set(id2, record);
        }
      });
      return map;
    }, [recordIds, records, searchHydratedRecords]);
    const recordByIdRef = hooks.useRef(recordById);
    hooks.useEffect(() => {
      recordByIdRef.current = recordById;
    }, [recordById]);
    hooks.useEffect(() => {
      setSearchHydratedRecords([]);
      setFolderHydrationAttemptState({ key: "", ids: /* @__PURE__ */ new Set() });
      activeFolderHydrationKeyRef.current = "";
      completedFolderHydrationKeyRef.current = "";
      warmingCorpusPromiseRef.current = null;
      inFlightSearchKeyRef.current = "";
      completedSearchKeyRef.current = "";
    }, [scopeKey]);
    const searchDocumentCorpusRows = hooks.useMemo(
      () => (searchDocuments == null ? void 0 : searchDocuments.map((document2, index) => ({
        id: document2.raw_ref_key || document2.entity_id || `search-doc-${index}`,
        record: createWorkerSearchRecordFromDocument(document2)
      }))) ?? [],
      [searchDocuments]
    );
    const recordCorpusRows = hooks.useMemo(
      () => records.map((record, index) => ({
        id: recordIds[index] || `row-${index}`,
        record: createWorkerSearchRecord(record)
      })),
      [recordIds, records]
    );
    const workerCorpusRows = (searchDocuments == null ? void 0 : searchDocuments.length) ? searchDocumentCorpusRows : recordCorpusRows;
    const workerCorpusRowsRef = hooks.useRef(workerCorpusRows);
    const selectedFoldersRef = hooks.useRef(selectedFolders);
    const hydrateRecordsByIdsRef = hooks.useRef(hydrateRecordsByIds);
    hooks.useEffect(() => {
      workerCorpusRowsRef.current = workerCorpusRows;
    }, [workerCorpusRows]);
    hooks.useEffect(() => {
      selectedFoldersRef.current = selectedFolders;
    }, [selectedFolders]);
    hooks.useEffect(() => {
      hydrateRecordsByIdsRef.current = hydrateRecordsByIds;
    }, [hydrateRecordsByIds]);
    const folderScopedDocuments = hooks.useMemo(() => {
      if (hasSearchQuery || !hasFolderScope || !(searchDocuments == null ? void 0 : searchDocuments.length)) return [];
      const folderIds = new Set(selectedFolders);
      return searchDocuments.filter((document2) => document2.folder_id && folderIds.has(document2.folder_id)).sort((left, right) => {
        const rightTime = right.observed_at_ms || right.created_at_ms || right.updated_at_ms || 0;
        const leftTime = left.observed_at_ms || left.created_at_ms || left.updated_at_ms || 0;
        if (rightTime !== leftTime) return rightTime - leftTime;
        return (right.raw_ref_key || right.entity_id).localeCompare(
          left.raw_ref_key || left.entity_id
        );
      });
    }, [hasFolderScope, hasSearchQuery, searchDocuments, selectedFolders]);
    const folderScopedDocumentIds = hooks.useMemo(
      () => folderScopedDocuments.map((document2) => document2.raw_ref_key || document2.entity_id).filter(Boolean).slice(0, MAX_FOLDER_HYDRATE_RECORDS),
      [folderScopedDocuments]
    );
    const folderHydrationKey = hooks.useMemo(() => {
      if (!folderScopedDocumentIds.length) return "";
      const first = folderScopedDocumentIds[0] || "";
      const last = folderScopedDocumentIds[folderScopedDocumentIds.length - 1] || "";
      return `${scopeKey}:folders:${selectedFolderKey}:${folderScopedDocumentIds.length}:${first}:${last}`;
    }, [folderScopedDocumentIds, scopeKey, selectedFolderKey]);
    hooks.useEffect(() => {
      if (hasSearchQuery || !hasFolderScope || !folderScopedDocumentIds.length || !hydrateRecordsByIds) {
        return;
      }
      if (activeFolderHydrationKeyRef.current === folderHydrationKey || completedFolderHydrationKeyRef.current === folderHydrationKey) {
        return;
      }
      activeFolderHydrationKeyRef.current = folderHydrationKey;
      setFolderHydrationAttemptState(
        (current) => current.key === folderHydrationKey ? current : { key: folderHydrationKey, ids: /* @__PURE__ */ new Set() }
      );
      let cancelled = false;
      const appendHydratedRecords = (hydratedRecords) => {
        if (!hydratedRecords.length) return;
        setSearchHydratedRecords((current) => {
          const currentIds = new Set(
            current.flatMap((record, index) => collectRecordLookupIds(record, index))
          );
          const additions = hydratedRecords.filter((record, index) => {
            const ids = collectRecordLookupIds(record, index);
            if (ids.some((id2) => currentIds.has(id2))) return false;
            ids.forEach((id2) => currentIds.add(id2));
            return true;
          });
          return additions.length ? [...current, ...additions].slice(-MAX_FOLDER_HYDRATE_RECORDS) : current;
        });
      };
      const hydrate = async () => {
        const startedAt = nowMs();
        let hydratedCount = 0;
        const missingIds = folderScopedDocumentIds.filter((id2) => !recordByIdRef.current.has(id2));
        for (let offset = 0; offset < missingIds.length && !cancelled; offset += FOLDER_HYDRATE_BATCH_SIZE) {
          const batch = missingIds.slice(offset, offset + FOLDER_HYDRATE_BATCH_SIZE);
          const hydratedRecords = await hydrateRecordsByIds(batch);
          if (cancelled || activeFolderHydrationKeyRef.current !== folderHydrationKey) return;
          hydratedCount += hydratedRecords.length;
          setFolderHydrationAttemptState((current) => {
            if (current.key !== folderHydrationKey) return current;
            const ids = new Set(current.ids);
            batch.forEach((id2) => ids.add(id2));
            return { key: folderHydrationKey, ids };
          });
          appendHydratedRecords(hydratedRecords);
          await new Promise((resolve) => globalThis.setTimeout(resolve, 0));
        }
        if (!cancelled && activeFolderHydrationKeyRef.current === folderHydrationKey) {
          completedFolderHydrationKeyRef.current = folderHydrationKey;
          activeFolderHydrationKeyRef.current = "";
        }
        recordPerfMetric({
          kind: "viewer",
          name: "folder-result-hydrate",
          durationMs: nowMs() - startedAt,
          value: hydratedCount,
          tags: {
            selectedFolders: selectedFolderKey,
            totalMatches: folderScopedDocumentIds.length,
            missingIds: missingIds.length,
            batchSize: FOLDER_HYDRATE_BATCH_SIZE
          }
        });
      };
      void hydrate().catch((error) => {
        if (activeFolderHydrationKeyRef.current === folderHydrationKey) {
          activeFolderHydrationKeyRef.current = "";
        }
        recordPerfMetric({
          kind: "viewer",
          name: "folder-result-hydrate-error",
          tags: { error: error instanceof Error ? error.message : String(error) }
        });
      });
      return () => {
        cancelled = true;
        if (activeFolderHydrationKeyRef.current === folderHydrationKey) {
          activeFolderHydrationKeyRef.current = "";
        }
      };
    }, [
      folderHydrationKey,
      folderScopedDocumentIds,
      hasFolderScope,
      hasSearchQuery,
      hydrateRecordsByIds,
      selectedFolderKey
    ]);
    hooks.useEffect(() => {
      const client = new SearchWorkerClient();
      searchClientRef.current = client;
      return () => {
        client.dispose();
        searchClientRef.current = null;
        readyCorpusKeyRef.current = "";
      };
    }, []);
    hooks.useEffect(() => {
      readyCorpusKeyRef.current = "";
      warmingCorpusKeyRef.current = "";
    }, [scopeKey]);
    hooks.useEffect(() => {
      const client = searchClientRef.current;
      if (!(client == null ? void 0 : client.isAvailable()) || !workerCorpusRowsRef.current.length || readyCorpusKeyRef.current === corpusIdentityKey || warmingCorpusKeyRef.current === corpusIdentityKey) {
        return;
      }
      let cancelled = false;
      const warmCorpus = () => {
        if (cancelled || readyCorpusKeyRef.current === corpusIdentityKey) return;
        warmingCorpusKeyRef.current = corpusIdentityKey;
        const startedAt = nowMs();
        const warmPromise = client.setCorpus(scopeKey, workerCorpusRowsRef.current);
        warmingCorpusPromiseRef.current = warmPromise;
        void warmPromise.then((response) => {
          if (cancelled) return;
          if (response.type === "search:corpus-ready") {
            readyCorpusKeyRef.current = corpusIdentityKey;
            recordPerfMetric({
              kind: "search",
              name: "corpus-warm-ready",
              durationMs: nowMs() - startedAt,
              tags: { corpusSize: response.corpusSize }
            });
          }
        }).catch((error) => {
          if (cancelled) return;
          recordPerfMetric({
            kind: "search",
            name: "corpus-warm-error",
            durationMs: nowMs() - startedAt,
            tags: { error: error instanceof Error ? error.message : String(error) }
          });
        }).finally(() => {
          if (warmingCorpusKeyRef.current === corpusIdentityKey) {
            warmingCorpusKeyRef.current = "";
          }
          if (warmingCorpusPromiseRef.current === warmPromise) {
            warmingCorpusPromiseRef.current = null;
          }
        });
      };
      const timeoutHandle = globalThis.setTimeout(warmCorpus, 120);
      return () => {
        cancelled = true;
        globalThis.clearTimeout(timeoutHandle);
      };
    }, [corpusIdentityKey, scopeKey]);
    hooks.useEffect(() => {
      if (!needsWorkerSearch) {
        inFlightSearchKeyRef.current = "";
        completedSearchKeyRef.current = "";
        setAsyncSearchState(null);
        return;
      }
      const client = searchClientRef.current;
      if (completedSearchKeyRef.current === asyncSearchKey) {
        return;
      }
      if (inFlightSearchKeyRef.current === asyncSearchKey) {
        return;
      }
      inFlightSearchKeyRef.current = asyncSearchKey;
      const requestId = createSearchRequestId();
      latestSearchRequestIdRef.current = requestId;
      const startedAt = nowMs();
      const queryText = normalizedSearchQuery;
      if (!(client == null ? void 0 : client.isAvailable())) {
        if (records.length <= 1500 || !(searchDocuments == null ? void 0 : searchDocuments.length)) {
          const fallback = runAdvancedTableSearch(records ?? [], queryText, {
            bookmarkFolderIds: selectedFoldersRef.current
          });
          completedSearchKeyRef.current = asyncSearchKey;
          inFlightSearchKeyRef.current = "";
          setAsyncSearchState({ key: asyncSearchKey, pending: false, result: fallback });
          return;
        }
        const warning2 = (searchDocuments == null ? void 0 : searchDocuments.length) ? "Search worker unavailable; large-corpus indexed folder/search hydration was blocked." : "Search worker unavailable; large-corpus main-thread search was blocked.";
        setAsyncSearchState({
          key: asyncSearchKey,
          pending: false,
          result: createEmptySearchResult(queryText, [warning2]),
          error: warning2
        });
        inFlightSearchKeyRef.current = "";
        incrementPerfCounter("search:large-fallback-blocked");
        return;
      }
      setAsyncSearchState((current) => ({
        key: asyncSearchKey,
        pending: true,
        result: (current == null ? void 0 : current.key) === asyncSearchKey ? current.result : null
      }));
      const run = async () => {
        if (readyCorpusKeyRef.current !== corpusIdentityKey) {
          const corpusStartedAt = nowMs();
          let corpusResponse = null;
          if (warmingCorpusKeyRef.current === corpusIdentityKey && warmingCorpusPromiseRef.current) {
            try {
              corpusResponse = await warmingCorpusPromiseRef.current;
            } catch {
              corpusResponse = null;
            }
          }
          if (!corpusResponse || corpusResponse.type !== "search:corpus-ready") {
            corpusResponse = await client.setCorpus(scopeKey, workerCorpusRowsRef.current);
          }
          if (latestSearchRequestIdRef.current !== requestId) {
            incrementPerfCounter("search:stale-corpus-ignored");
            return;
          }
          if (corpusResponse.type === "search:corpus-ready") {
            readyCorpusKeyRef.current = corpusIdentityKey;
            recordPerfMetric({
              kind: "search",
              name: "corpus-ready",
              durationMs: nowMs() - corpusStartedAt,
              tags: { corpusSize: corpusResponse.corpusSize }
            });
          }
        }
        return await client.query({
          scopeKey,
          query: queryText,
          options: {
            bookmarkFolderIds: selectedFoldersRef.current,
            limit: MAX_QUERY_HYDRATE_RECORDS
          },
          requestId
        });
      };
      void run().then((response) => {
        if (!response) {
          if (inFlightSearchKeyRef.current === asyncSearchKey) {
            inFlightSearchKeyRef.current = "";
          }
          return;
        }
        if (latestSearchRequestIdRef.current !== requestId) {
          if (inFlightSearchKeyRef.current === asyncSearchKey) {
            inFlightSearchKeyRef.current = "";
          }
          incrementPerfCounter("search:stale-ignored");
          return;
        }
        const currentRecordById = recordByIdRef.current;
        const resultRecords = response.ids.map((id2) => currentRecordById.get(id2)).filter((record) => !!record);
        setAsyncSearchState({
          key: asyncSearchKey,
          pending: false,
          result: {
            ...response.result,
            records: resultRecords
          }
        });
        completedSearchKeyRef.current = asyncSearchKey;
        if (inFlightSearchKeyRef.current === asyncSearchKey) {
          inFlightSearchKeyRef.current = "";
        }
        const hydrateLimit = hasSearchQuery ? MAX_QUERY_HYDRATE_RECORDS : MAX_FOLDER_HYDRATE_RECORDS;
        const activeHydrateRecordsByIds = hydrateRecordsByIdsRef.current;
        const missingIds = activeHydrateRecordsByIds ? response.ids.filter((id2) => !currentRecordById.has(id2)).slice(0, hydrateLimit) : [];
        if (missingIds.length) {
          const hydrateStartedAt = nowMs();
          void (activeHydrateRecordsByIds == null ? void 0 : activeHydrateRecordsByIds(missingIds).then((hydratedRecords) => {
            if (latestSearchRequestIdRef.current !== requestId) return;
            const hydratedMap = new Map(recordByIdRef.current);
            hydratedRecords.forEach((record, index) => {
              for (const id2 of collectRecordLookupIds(record, index)) {
                hydratedMap.set(id2, record);
              }
            });
            setSearchHydratedRecords((current) => {
              const currentIds = new Set(
                current.flatMap((record, index) => collectRecordLookupIds(record, index))
              );
              const additions = hydratedRecords.filter((record, index) => {
                const ids = collectRecordLookupIds(record, index);
                if (ids.some((id2) => currentIds.has(id2))) return false;
                ids.forEach((id2) => currentIds.add(id2));
                return true;
              });
              return additions.length ? [...current, ...additions].slice(-MAX_FOLDER_HYDRATE_RECORDS) : current;
            });
            const nextResultRecords = response.ids.map((id2) => hydratedMap.get(id2)).filter((record) => !!record);
            setAsyncSearchState({
              key: asyncSearchKey,
              pending: false,
              result: {
                ...response.result,
                records: nextResultRecords
              }
            });
            recordPerfMetric({
              kind: "viewer",
              name: "search-result-hydrate",
              durationMs: nowMs() - hydrateStartedAt,
              value: hydratedRecords.length,
              tags: { missingIds: missingIds.length, resultCount: response.ids.length }
            });
          }).catch((error) => {
            recordPerfMetric({
              kind: "viewer",
              name: "search-result-hydrate-error",
              durationMs: nowMs() - hydrateStartedAt,
              tags: { error: error instanceof Error ? error.message : String(error) }
            });
          }));
        }
        recordPerfMetric({
          kind: "search",
          name: "query-total",
          durationMs: nowMs() - startedAt,
          tags: {
            corpusSize: response.corpusSize,
            resultCount: response.ids.length,
            queryLength: queryText.length
          }
        });
      }).catch((error) => {
        if (latestSearchRequestIdRef.current !== requestId) return;
        const message = error instanceof Error ? error.message : String(error);
        setAsyncSearchState({
          key: asyncSearchKey,
          pending: false,
          result: createEmptySearchResult(queryText, [message]),
          error: message
        });
        if (inFlightSearchKeyRef.current === asyncSearchKey) {
          inFlightSearchKeyRef.current = "";
        }
        recordPerfMetric({
          kind: "search",
          name: "query-error",
          durationMs: nowMs() - startedAt,
          tags: { error: message, queryLength: queryText.length }
        });
      });
      return () => {
        if (inFlightSearchKeyRef.current === asyncSearchKey) {
          inFlightSearchKeyRef.current = "";
        }
        client.cancel(requestId);
      };
    }, [
      asyncSearchKey,
      hasSearchQuery,
      needsWorkerSearch,
      normalizedSearchQuery,
      corpusIdentityKey,
      scopeKey,
      searchDocuments == null ? void 0 : searchDocuments.length
    ]);
    const searchResult = hooks.useMemo(() => {
      if (!hasSearchQuery && hasFolderScope && (searchDocuments == null ? void 0 : searchDocuments.length)) {
        const attemptedIds = folderHydrationAttemptState.key === folderHydrationKey ? folderHydrationAttemptState.ids : /* @__PURE__ */ new Set();
        const recordsInOrder = resolveOrderedAvailableRecords(
          folderScopedDocumentIds,
          recordById,
          attemptedIds
        );
        return createSearchResultFromRecords({
          query: "",
          records: recordsInOrder,
          totalMatches: folderScopedDocuments.length
        });
      }
      if (!needsWorkerSearch) {
        return runAdvancedTableSearch(records ?? [], "", {
          bookmarkFolderIds: selectedFolders
        });
      }
      if ((asyncSearchState == null ? void 0 : asyncSearchState.key) === asyncSearchKey && asyncSearchState.result) {
        return asyncSearchState.result;
      }
      return createEmptySearchResult(normalizedSearchQuery);
    }, [
      asyncSearchKey,
      asyncSearchState,
      folderScopedDocumentIds,
      folderScopedDocuments.length,
      folderHydrationAttemptState,
      folderHydrationKey,
      hasFolderScope,
      hasSearchQuery,
      needsWorkerSearch,
      normalizedSearchQuery,
      recordById,
      records,
      searchDocuments == null ? void 0 : searchDocuments.length,
      selectedFolders
    ]);
    const searchPending = !!(needsWorkerSearch && (asyncSearchState == null ? void 0 : asyncSearchState.key) === asyncSearchKey && asyncSearchState.pending);
    const sortableColumns = hooks.useMemo(() => {
      const leaves = flattenLeafColumns(columns2);
      const map = /* @__PURE__ */ new Map();
      for (const column of leaves) {
        const id2 = resolveColumnId(column);
        if (id2) {
          map.set(id2, column);
        }
      }
      return map;
    }, [columns2]);
    const sortedRecords = hooks.useMemo(() => {
      if (!sorting.length) {
        if (hasSearchQuery || hasFolderScope && (searchDocuments == null ? void 0 : searchDocuments.length)) {
          return searchResult.records;
        }
        return [...searchResult.records].sort((left, right) => {
          const rightRecency = resolveRecordRecency(right);
          const leftRecency = resolveRecordRecency(left);
          if (rightRecency !== leftRecency) {
            return rightRecency - leftRecency;
          }
          return 0;
        });
      }
      const indexMap = /* @__PURE__ */ new Map();
      searchResult.records.forEach((record, index) => {
        indexMap.set(record, index);
      });
      const sortable = [...searchResult.records];
      sortable.sort((left, right) => {
        const leftIndex = indexMap.get(left) ?? 0;
        const rightIndex = indexMap.get(right) ?? 0;
        for (const sortEntry of sorting) {
          const column = sortableColumns.get(sortEntry.id);
          if (!column) continue;
          const leftValue = resolveColumnValue(column, left, leftIndex);
          const rightValue = resolveColumnValue(column, right, rightIndex);
          const compared = compareSortValues(leftValue, rightValue);
          if (compared !== 0) {
            return sortEntry.desc ? -compared : compared;
          }
        }
        return leftIndex - rightIndex;
      });
      return sortable;
    }, [hasSearchQuery, searchResult.records, sortableColumns, sorting]);
    const currentResultIds = hooks.useMemo(
      () => sortedRecords.map((record, index) => extractStableRecordId(record, index)),
      [sortedRecords]
    );
    const handleRowSelectionChange = (updater) => {
      const next = tableCore.functionalUpdate(updater, rowSelection);
      setRowSelection(next);
      if (!currentResultIds.length) {
        return;
      }
      const allSelected = currentResultIds.every((id2) => !!next[id2]);
      setSelectionMode(allSelected ? "all" : "explicit");
    };
    const selectedRecords = hooks.useMemo(() => {
      if (selectionMode === "all") {
        return sortedRecords;
      }
      return sortedRecords.filter((record, index) => {
        const id2 = extractStableRecordId(record, index);
        return !!rowSelection[id2];
      });
    }, [rowSelection, selectionMode, sortedRecords]);
    const resultSetSnapshot = hooks.useMemo(
      () => createResultSetSnapshot({
        queryText: normalizedSearchQuery,
        sort: serializeSortingState(sorting),
        ids: currentResultIds,
        totalMatches: searchResult.totalMatches,
        warnings: searchResult.warnings
      }),
      [
        currentResultIds,
        normalizedSearchQuery,
        searchResult.totalMatches,
        searchResult.warnings,
        sorting
      ]
    );
    hooks.useEffect(() => {
      const handle = globalThis.setTimeout(() => {
        setDebouncedSearchQuery(searchQuery);
      }, searchDebounceMs);
      return () => {
        globalThis.clearTimeout(handle);
      };
    }, [searchDebounceMs, searchQuery]);
    hooks.useEffect(() => {
      if (!(alternateViews == null ? void 0 : alternateViews.length) && activeViewId !== "table") {
        setActiveViewId("table");
        return;
      }
      if (activeViewId !== "table" && !(alternateViews == null ? void 0 : alternateViews.some((view) => view.id === activeViewId))) {
        setActiveViewId("table");
      }
    }, [activeViewId, alternateViews]);
    hooks.useEffect(() => {
      try {
        if (typeof localStorage === "undefined") return;
        const raw = localStorage.getItem(resolvedViewStateKey);
        if (!raw) return;
        const parsed = JSON.parse(raw);
        if (fullscreen === void 0 && typeof parsed.fullscreen === "boolean") {
          setInternalFullscreen(parsed.fullscreen);
        }
        if (typeof parsed.activeViewId === "string" && parsed.activeViewId.trim()) {
          setActiveViewId(parsed.activeViewId);
        }
      } catch {
      }
    }, [resolvedViewStateKey]);
    hooks.useEffect(() => {
      try {
        if (typeof localStorage === "undefined") return;
        localStorage.setItem(
          resolvedViewStateKey,
          JSON.stringify({
            fullscreen: isFullscreen,
            activeViewId
          })
        );
      } catch {
      }
    }, [activeViewId, isFullscreen, resolvedViewStateKey]);
    return {
      searchQuery,
      setSearchQuery,
      normalizedSearchQuery,
      selectedFolders,
      setSelectedFolders,
      rowSelection,
      sorting,
      setSorting,
      selectionMode,
      isFullscreen,
      setIsFullscreen,
      activeViewId,
      setActiveViewId,
      searchResult,
      searchPending,
      sortedRecords,
      currentResultIds,
      handleRowSelectionChange,
      selectedRecords,
      resultSetSnapshot,
      resolvedViewStateKey
    };
  }
  const VIRTUAL_OVERSCAN_ROWS = 12;
  const VIRTUAL_INITIAL_ROW_HEIGHT = 74;
  const VIEWER_PREFETCH_VIEWPORTS = 4;
  const VIRTUAL_OVERSCAN_PX = 1600;
  const VIRTUAL_MAX_WINDOW_ROWS = 90;
  const VIRTUAL_SCROLL_UPDATE_PX = 24;
  const HIGHLIGHT_ATTRIBUTE = "data-twe-highlight-v1";
  const CSS_HIGHLIGHT_PREFIX = "scrollmark-table-search-";
  const cssHighlightNamesByRoot = /* @__PURE__ */ new WeakMap();
  let cssHighlightId = 0;
  function unwrapHighlightMark(mark) {
    const parent = mark.parentNode;
    if (!parent) return;
    while (mark.firstChild) {
      parent.insertBefore(mark.firstChild, mark);
    }
    parent.removeChild(mark);
    parent.normalize();
  }
  function clearTextHighlights(root) {
    const cssHighlightName = cssHighlightNamesByRoot.get(root);
    const cssHighlights = getCssHighlightRegistry();
    if (cssHighlightName && cssHighlights) {
      cssHighlights.delete(cssHighlightName);
      removeCssHighlightStyle(cssHighlightName);
    }
    const highlighted = root.querySelectorAll(`mark[${HIGHLIGHT_ATTRIBUTE}="1"]`);
    highlighted.forEach((node) => {
      if (node instanceof HTMLElement) {
        unwrapHighlightMark(node);
      }
    });
  }
  function getCssHighlightRegistry() {
    const css = globalThis.CSS;
    const highlightCtor = getCssHighlightConstructor();
    if (!(css == null ? void 0 : css.highlights) || !highlightCtor) return null;
    return css.highlights;
  }
  function getCssHighlightConstructor() {
    const highlightCtor = globalThis.Highlight;
    return typeof highlightCtor === "function" ? highlightCtor : null;
  }
  function getCssHighlightName(root) {
    const existing = cssHighlightNamesByRoot.get(root);
    if (existing) {
      ensureCssHighlightStyle(existing);
      return existing;
    }
    cssHighlightId += 1;
    const name2 = `${CSS_HIGHLIGHT_PREFIX}${cssHighlightId}`;
    cssHighlightNamesByRoot.set(root, name2);
    ensureCssHighlightStyle(name2);
    return name2;
  }
  function ensureCssHighlightStyle(name2) {
    if (typeof document === "undefined") return;
    const id2 = `twe-css-highlight-style-${name2}`;
    if (document.getElementById(id2)) return;
    const style = document.createElement("style");
    style.id = id2;
    style.textContent = `
::highlight(${name2}) {
  background-color: rgba(250, 204, 21, 0.32);
  color: inherit;
}
`;
    document.head.appendChild(style);
  }
  function removeCssHighlightStyle(name2) {
    var _a2;
    if (typeof document === "undefined") return;
    (_a2 = document.getElementById(`twe-css-highlight-style-${name2}`)) == null ? void 0 : _a2.remove();
  }
  function escapeRegex(value) {
    return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
  }
  function applyTextHighlights(root, terms) {
    clearTextHighlights(root);
    if (!terms.length) return;
    const normalizedTerms = [...new Set(terms.map((term) => term.trim().toLowerCase()))].filter((term) => term.length >= 2).sort((a, b) => b.length - a.length).slice(0, 24);
    if (!normalizedTerms.length) return;
    const cssHighlights = getCssHighlightRegistry();
    const HighlightCtor = getCssHighlightConstructor();
    if (!cssHighlights || !HighlightCtor) {
      return;
    }
    const pattern = new RegExp(
      `(${normalizedTerms.map((term) => escapeRegex(term)).join("|")})`,
      "ig"
    );
    const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
      acceptNode: (node) => {
        if (!node.nodeValue || !node.nodeValue.trim()) {
          return NodeFilter.FILTER_REJECT;
        }
        const parent = node.parentElement;
        if (!parent) return NodeFilter.FILTER_REJECT;
        if (parent.closest(
          "mark,button,input,textarea,select,option,svg,code,pre,.btn,.checkbox,.label,.dropdown-content"
        )) {
          return NodeFilter.FILTER_REJECT;
        }
        return NodeFilter.FILTER_ACCEPT;
      }
    });
    const textNodes = [];
    while (walker.nextNode()) {
      textNodes.push(walker.currentNode);
    }
    const ranges = [];
    for (const node of textNodes) {
      const text = node.nodeValue || "";
      pattern.lastIndex = 0;
      if (!pattern.test(text)) continue;
      pattern.lastIndex = 0;
      text.replace(pattern, (match, _capture, index) => {
        const range = document.createRange();
        range.setStart(node, index);
        range.setEnd(node, index + match.length);
        ranges.push(range);
        return match;
      });
    }
    if (!ranges.length) return;
    cssHighlights.set(getCssHighlightName(root), new HighlightCtor(...ranges));
  }
  function findVirtualIndexForOffset(offsets, offset) {
    if (offset <= 0 || offsets.length <= 1) return 0;
    let low = 0;
    let high = offsets.length - 1;
    while (low < high) {
      const mid = Math.floor((low + high + 1) / 2);
      if ((offsets[mid] || 0) <= offset) {
        low = mid;
      } else {
        high = mid - 1;
      }
    }
    return Math.max(0, Math.min(offsets.length - 2, low));
  }
  function BaseTableView({
    title,
    viewStateKey,
    searchHistoryScope,
    fullscreen,
    onFullscreenChange,
    loading = false,
    loadingMore = false,
    loadedCount = 0,
    totalCount = 0,
    hasMore = false,
    loadMore,
    loadAll,
    hydrateRecordsByIds,
    records,
    searchDocuments,
    columns: columns2,
    clear,
    showClearButton = true,
    renderActions,
    renderExtra,
    bookmarkFolderOptions,
    alternateViews
  }) {
    const { t } = useTranslation();
    const openedAtRef = hooks.useRef(nowMs());
    const firstRowsReportedRef = hooks.useRef(false);
    const [mediaPreview, setMediaPreview] = useSignalState("");
    const [showMediaModal, setShowMediaModal] = useSignalState(false);
    const [rawDataPreview, setRawDataPreview] = useSignalState(null);
    const [showSearchHelp, toggleShowSearchHelp] = useToggle(false);
    const [searchHistoryCount, setSearchHistoryCount] = hooks.useState(0);
    const {
      searchQuery,
      setSearchQuery,
      normalizedSearchQuery,
      selectedFolders,
      setSelectedFolders,
      rowSelection,
      sorting,
      setSorting,
      selectionMode,
      isFullscreen,
      setIsFullscreen,
      activeViewId,
      setActiveViewId,
      searchResult,
      searchPending,
      sortedRecords,
      currentResultIds,
      handleRowSelectionChange,
      selectedRecords,
      resultSetSnapshot,
      resolvedViewStateKey
    } = useResultSetController({
      title,
      viewStateKey,
      fullscreen,
      onFullscreenChange,
      records,
      columns: columns2,
      alternateViews,
      searchDocuments,
      hydrateRecordsByIds
    });
    const operatorHelpGroups = hooks.useMemo(() => {
      const groups = /* @__PURE__ */ new Map();
      for (const entry of SEARCH_OPERATOR_HELP_ENTRIES) {
        const rows = groups.get(entry.category) || [];
        rows.push(entry);
        groups.set(entry.category, rows);
      }
      return [...groups.entries()];
    }, []);
    const activeAlternateView = hooks.useMemo(
      () => (alternateViews == null ? void 0 : alternateViews.find((view) => view.id === activeViewId)) ?? null,
      [activeViewId, alternateViews]
    );
    const scrollAreaRef = hooks.useRef(null);
    const tbodyRef = hooks.useRef(null);
    const scrollTopRef = hooks.useRef(0);
    const scrollRafRef = hooks.useRef(null);
    const rowHeightsRef = hooks.useRef(/* @__PURE__ */ new Map());
    const highlightHadTermsRef = hooks.useRef(false);
    const [virtualScrollTop, setVirtualScrollTop] = hooks.useState(0);
    const [viewportHeight, setViewportHeight] = hooks.useState(520);
    const [estimatedRowHeight, setEstimatedRowHeight] = hooks.useState(VIRTUAL_INITIAL_ROW_HEIGHT);
    const [rowHeightsVersion, setRowHeightsVersion] = hooks.useState(0);
    hooks.useEffect(() => {
      const area = scrollAreaRef.current;
      if (area) {
        area.scrollTop = 0;
        scrollTopRef.current = 0;
        setVirtualScrollTop(0);
        setViewportHeight(Math.max(320, area.clientHeight || 520));
      }
    }, [activeViewId, normalizedSearchQuery, selectedFolders]);
    const onTableScroll = () => {
      const area = scrollAreaRef.current;
      if (!area) return;
      scrollTopRef.current = area.scrollTop;
      if (scrollRafRef.current !== null) return;
      scrollRafRef.current = window.requestAnimationFrame(() => {
        scrollRafRef.current = null;
        const nextTop = scrollTopRef.current;
        setVirtualScrollTop((current) => {
          if (Math.abs(nextTop - current) < VIRTUAL_SCROLL_UPDATE_PX) {
            return current;
          }
          return nextTop;
        });
      });
    };
    hooks.useEffect(() => {
      const area = scrollAreaRef.current;
      if (!area || typeof ResizeObserver === "undefined") return;
      const observer = new ResizeObserver(() => {
        const nextHeight = Math.max(320, area.clientHeight || 520);
        setViewportHeight((current) => Math.abs(current - nextHeight) > 12 ? nextHeight : current);
      });
      observer.observe(area);
      return () => observer.disconnect();
    }, []);
    hooks.useEffect(() => {
      return () => {
        if (scrollRafRef.current !== null) {
          window.cancelAnimationFrame(scrollRafRef.current);
        }
      };
    }, []);
    const safeRowHeight = Math.max(32, estimatedRowHeight || VIRTUAL_INITIAL_ROW_HEIGHT);
    const totalRows = sortedRecords.length;
    const rowKeys = currentResultIds;
    const virtualOffsets = hooks.useMemo(() => {
      const offsets = new Array(totalRows + 1);
      offsets[0] = 0;
      for (let index = 0; index < totalRows; index += 1) {
        const key = rowKeys[index] || `row-${index}`;
        const height = rowHeightsRef.current.get(key) || safeRowHeight;
        offsets[index + 1] = (offsets[index] || 0) + Math.max(24, height);
      }
      return offsets;
    }, [rowHeightsVersion, rowKeys, safeRowHeight, totalRows]);
    const totalVirtualHeight = virtualOffsets[totalRows] || 0;
    const startIndex = Math.max(
      0,
      findVirtualIndexForOffset(virtualOffsets, virtualScrollTop - VIRTUAL_OVERSCAN_PX) - VIRTUAL_OVERSCAN_ROWS
    );
    const requestedEndIndex = findVirtualIndexForOffset(
      virtualOffsets,
      virtualScrollTop + viewportHeight + VIRTUAL_OVERSCAN_PX
    ) + VIRTUAL_OVERSCAN_ROWS + 1;
    const endIndex = Math.min(
      totalRows,
      Math.max(startIndex + 1, Math.min(requestedEndIndex, startIndex + VIRTUAL_MAX_WINDOW_ROWS))
    );
    const visibleRecords = hooks.useMemo(
      () => sortedRecords.slice(startIndex, endIndex),
      [sortedRecords, startIndex, endIndex]
    );
    hooks.useEffect(() => {
      if (!hasMore || loading || loadingMore || activeAlternateView || normalizedSearchQuery) {
        return;
      }
      const remainingPx = Math.max(0, totalVirtualHeight - (virtualScrollTop + viewportHeight));
      const prefetchThresholdPx = Math.max(
        900,
        viewportHeight * VIEWER_PREFETCH_VIEWPORTS,
        safeRowHeight * 80
      );
      if (totalRows > 0 && (endIndex >= totalRows - VIRTUAL_OVERSCAN_ROWS || remainingPx <= prefetchThresholdPx)) {
        void (loadMore == null ? void 0 : loadMore());
      }
    }, [
      activeAlternateView,
      endIndex,
      hasMore,
      loadMore,
      loading,
      loadingMore,
      normalizedSearchQuery,
      safeRowHeight,
      totalVirtualHeight,
      totalRows,
      virtualScrollTop,
      viewportHeight
    ]);
    const topSpacerHeight = virtualOffsets[startIndex] || 0;
    const bottomSpacerHeight = Math.max(
      0,
      totalVirtualHeight - (virtualOffsets[endIndex] || totalVirtualHeight)
    );
    const toggleResultRowSelected = (rowId) => {
      if (selectionMode === "all") {
        handleRowSelectionChange(() => {
          const next = {};
          currentResultIds.forEach((id2) => {
            if (id2 !== rowId) {
              next[id2] = true;
            }
          });
          return next;
        });
        return;
      }
      handleRowSelectionChange((current) => ({
        ...current,
        [rowId]: !current[rowId]
      }));
    };
    const toggleAllResultRowsSelected = () => {
      const shouldSelectAll = selectionMode !== "all" || currentResultIds.some((id2) => !rowSelection[id2]);
      handleRowSelectionChange(() => {
        if (!shouldSelectAll) {
          return {};
        }
        if (selectionMode === "all") {
          return rowSelection;
        }
        const next = {};
        currentResultIds.forEach((id2) => {
          next[id2] = true;
        });
        return next;
      });
    };
    const table = useReactTable({
      data: visibleRecords,
      columns: columns2,
      enableRowSelection: true,
      getCoreRowModel: tableCore.getCoreRowModel(),
      getRowId: (record, index) => extractStableRecordId(record, startIndex + index),
      manualSorting: true,
      onSortingChange: setSorting,
      onRowSelectionChange: handleRowSelectionChange,
      state: {
        rowSelection,
        sorting
      },
      meta: {
        mediaPreview,
        setMediaPreview: (url) => {
          setMediaPreview(url);
          setShowMediaModal(true);
        },
        rawDataPreview,
        setRawDataPreview: (data) => setRawDataPreview(data),
        isAllResultRowsSelected: () => currentResultIds.length > 0 && (selectionMode === "all" || currentResultIds.every((id2) => !!rowSelection[id2])),
        isSomeResultRowsSelected: () => selectionMode === "all" ? false : currentResultIds.some((id2) => !!rowSelection[id2]),
        toggleAllResultRowsSelected,
        isResultRowSelected: (rowId) => selectionMode === "all" ? true : !!rowSelection[rowId],
        toggleResultRowSelected
      }
    });
    const visibleRows = table.getRowModel().rows;
    hooks.useEffect(() => {
      if (firstRowsReportedRef.current || !visibleRows.length) return;
      firstRowsReportedRef.current = true;
      recordPerfMetric({
        kind: "viewer",
        name: "first-visible-rows",
        durationMs: nowMs() - openedAtRef.current,
        tags: {
          title,
          records: records.length,
          visibleRows: visibleRows.length,
          loading
        }
      });
    }, [loading, records.length, title, visibleRows.length]);
    hooks.useEffect(() => {
      const body = tbodyRef.current;
      if (!body) return;
      const renderedRows = body.querySelectorAll('tr[data-vrow="1"]');
      if (!renderedRows.length) return;
      let totalHeight = 0;
      let measuredCount = 0;
      let changed = false;
      renderedRows.forEach((row) => {
        if (row instanceof HTMLTableRowElement) {
          const measuredHeight = row.getBoundingClientRect().height;
          totalHeight += measuredHeight;
          measuredCount += 1;
          const key = row.dataset.vrowKey;
          if (key && Number.isFinite(measuredHeight) && measuredHeight > 0) {
            const previous = rowHeightsRef.current.get(key);
            if (!previous || Math.abs(previous - measuredHeight) > 2) {
              rowHeightsRef.current.set(key, measuredHeight);
              changed = true;
            }
          }
        }
      });
      const average = totalHeight / measuredCount;
      if (Number.isFinite(average) && average > 16) {
        setEstimatedRowHeight((current) => {
          const next = Math.max(24, current * 0.85 + average * 0.15);
          return Math.abs(next - current) > 3 ? next : current;
        });
      }
      if (changed) {
        setRowHeightsVersion((version2) => version2 + 1);
      }
    }, [endIndex, normalizedSearchQuery, selectedFolders, startIndex, visibleRows]);
    hooks.useEffect(() => {
      const root = tbodyRef.current;
      if (!root) return;
      if (!searchResult.highlightTerms.length) {
        if (highlightHadTermsRef.current) {
          clearTextHighlights(root);
          highlightHadTermsRef.current = false;
        }
        return;
      }
      highlightHadTermsRef.current = true;
      applyTextHighlights(root, searchResult.highlightTerms);
      return () => {
        clearTextHighlights(root);
      };
    }, [visibleRows, searchResult.highlightTerms]);
    hooks.useEffect(() => {
      if (!isFullscreen) return;
      const onKeyDown = (event) => {
        if (event.key === "Escape") {
          setIsFullscreen(false);
        }
      };
      window.addEventListener("keydown", onKeyDown);
      return () => window.removeEventListener("keydown", onKeyDown);
    }, [isFullscreen]);
    const lastSearchHistoryKeyRef = hooks.useRef("");
    const pendingSearchHistoryWriteRef = hooks.useRef(null);
    hooks.useEffect(() => {
      if (!searchHistoryScope || typeof localStorage === "undefined") {
        return;
      }
      setSearchHistoryCount(readSearchHistory(searchHistoryScope).length);
    }, [searchHistoryScope]);
    hooks.useEffect(() => {
      if (!searchHistoryScope) return;
      if (!normalizedSearchQuery) {
        lastSearchHistoryKeyRef.current = "";
        return;
      }
      const folderKey = [...selectedFolders].sort().join(",");
      const identity = `${searchHistoryScope}::${normalizedSearchQuery}::${folderKey}`;
      if (lastSearchHistoryKeyRef.current === identity) {
        return;
      }
      lastSearchHistoryKeyRef.current = identity;
      const scheduleWrite = () => {
        const next = appendSearchHistoryEntry({
          scope: searchHistoryScope,
          title,
          query: searchQuery,
          normalized_query: normalizedSearchQuery,
          searched_at_ms: Date.now(),
          result_count: searchResult.totalMatches,
          total_records: records.length,
          selected_folders: selectedFolders,
          lexical_expression: searchResult.parsed.lexicalExpression,
          warning_messages: searchResult.warnings
        });
        setSearchHistoryCount(next.filter((entry) => entry.scope === searchHistoryScope).length);
      };
      if (typeof window !== "undefined" && "requestIdleCallback" in window && typeof window.requestIdleCallback === "function") {
        pendingSearchHistoryWriteRef.current = window.requestIdleCallback(() => {
          pendingSearchHistoryWriteRef.current = null;
          scheduleWrite();
        });
      } else {
        pendingSearchHistoryWriteRef.current = window.setTimeout(() => {
          pendingSearchHistoryWriteRef.current = null;
          scheduleWrite();
        }, 160);
      }
      return () => {
        if (pendingSearchHistoryWriteRef.current === null) return;
        if (typeof window !== "undefined" && "cancelIdleCallback" in window && typeof window.cancelIdleCallback === "function") {
          window.cancelIdleCallback(
            pendingSearchHistoryWriteRef.current
          );
        } else {
          window.clearTimeout(pendingSearchHistoryWriteRef.current);
        }
        pendingSearchHistoryWriteRef.current = null;
      };
    }, [
      records.length,
      searchHistoryScope,
      searchQuery,
      searchResult.parsed.lexicalExpression,
      searchResult.totalMatches,
      searchResult.warnings,
      normalizedSearchQuery,
      selectedFolders,
      title
    ]);
    hooks.useEffect(() => {
      if (!isFullscreen || typeof document === "undefined") return;
      const { body } = document;
      const previousOverflow = body.style.overflow;
      body.style.overflow = "hidden";
      return () => {
        body.style.overflow = previousOverflow;
      };
    }, [isFullscreen]);
    const [showExportDataModal, toggleShowExportDataModal] = useToggle();
    const [preparingExport, setPreparingExport] = hooks.useState(false);
    const openExportDataModal = () => {
      toggleShowExportDataModal();
      if (hasMore && !normalizedSearchQuery && loadAll && !preparingExport) {
        setPreparingExport(true);
        void loadAll().finally(() => {
          setPreparingExport(false);
        });
      }
    };
    const rootClass = isFullscreen ? "relative flex h-full min-h-0 flex-col overflow-hidden bg-base-100 text-base-content" : "relative flex h-full min-h-0 flex-col";
    const ActiveAlternateView = activeAlternateView == null ? void 0 : activeAlternateView.component;
    return /* @__PURE__ */ u("div", { class: rootClass, children: [
      /* @__PURE__ */ u(
        "section",
        {
          class: isFullscreen ? "border-b border-base-300 bg-base-100 px-3 py-2" : "mb-1.5 rounded-box-half border border-base-300 bg-base-200 px-2 py-1.5",
          children: [
            /* @__PURE__ */ u("div", { class: "flex items-center gap-2", children: [
              /* @__PURE__ */ u("label", { class: "input input-bordered input-sm flex h-9 flex-1 items-center gap-2", children: [
                /* @__PURE__ */ u(IconSearch, { size: 18, class: "opacity-70" }),
                /* @__PURE__ */ u(
                  "input",
                  {
                    type: "text",
                    class: "grow bg-transparent text-sm",
                    value: searchQuery,
                    placeholder: 'Search with operators, phrases, and boolean logic: from:alice ("design system"~2 OR reliability)',
                    onInput: (event) => setSearchQuery(event.target.value),
                    onKeyDown: (event) => {
                      if (event.key === "Escape") {
                        setSearchQuery("");
                      }
                    }
                  }
                ),
                searchQuery ? /* @__PURE__ */ u(
                  "button",
                  {
                    class: "btn btn-ghost btn-xs",
                    title: "Clear search",
                    onClick: () => setSearchQuery(""),
                    children: /* @__PURE__ */ u(IconX, { size: 14 })
                  }
                ) : null
              ] }),
              (bookmarkFolderOptions == null ? void 0 : bookmarkFolderOptions.length) ? /* @__PURE__ */ u(
                MultiSelect,
                {
                  class: "w-56",
                  options: bookmarkFolderOptions,
                  selected: selectedFolders,
                  onChange: setSelectedFolders,
                  placeholder: t("Bookmark folders"),
                  selectedSummary: (count) => count === 1 ? t("1 folder selected") : t("{{count}} folders selected", { count })
                }
              ) : null,
              /* @__PURE__ */ u("button", { class: "btn btn-ghost btn-sm", onClick: toggleShowSearchHelp, title: "Search help", children: /* @__PURE__ */ u(IconInfoCircle, { size: 18 }) }),
              (alternateViews == null ? void 0 : alternateViews.length) ? /* @__PURE__ */ u("div", { class: "join", children: [
                /* @__PURE__ */ u(
                  "button",
                  {
                    class: `btn join-item btn-sm ${activeViewId === "table" ? "btn-primary" : "btn-ghost"}`,
                    onClick: () => setActiveViewId("table"),
                    title: "Table view",
                    children: /* @__PURE__ */ u(IconTable, { size: 16 })
                  }
                ),
                alternateViews.map((view) => /* @__PURE__ */ u(
                  "button",
                  {
                    class: `btn join-item btn-sm ${activeViewId === view.id ? "btn-primary" : "btn-ghost"}`,
                    onClick: () => setActiveViewId(view.id),
                    title: view.label,
                    children: view.icon === "grid" ? /* @__PURE__ */ u(IconLayoutGrid, { size: 16 }) : /* @__PURE__ */ u(IconTable, { size: 16 })
                  },
                  view.id
                ))
              ] }) : null,
              /* @__PURE__ */ u(
                "button",
                {
                  class: "btn btn-ghost btn-sm",
                  onClick: () => setIsFullscreen((current) => !current),
                  title: isFullscreen ? "Exit fullscreen" : "Fullscreen",
                  children: isFullscreen ? /* @__PURE__ */ u(IconArrowsMinimize, { size: 18 }) : /* @__PURE__ */ u(IconArrowsMaximize, { size: 18 })
                }
              )
            ] }),
            normalizedSearchQuery ? /* @__PURE__ */ u("div", { class: "mt-1.5 space-y-1 text-[10px] leading-4", children: [
              searchPending ? /* @__PURE__ */ u("div", { class: "font-mono opacity-70", children: "searching local index..." }) : null,
              searchResult.parsed.lexicalExpression ? /* @__PURE__ */ u("div", { class: "font-mono opacity-70 break-all", children: [
                "parsed: ",
                searchResult.parsed.lexicalExpression
              ] }) : null,
              searchResult.warningObjects.length ? /* @__PURE__ */ u("div", { class: "rounded-box-half border border-warning/40 bg-warning/10 px-2 py-1 font-mono text-warning", children: searchResult.warningObjects.map((warning2, index) => /* @__PURE__ */ u("div", { children: [
                "[",
                warning2.code,
                "] ",
                warning2.message,
                warning2.token ? ` (${warning2.token})` : ""
              ] }, `search-warning-${index}`)) }) : null
            ] }) : null,
            /* @__PURE__ */ u("div", { class: "mt-1 flex items-center justify-between text-[10px] leading-4 font-mono opacity-70", children: [
              /* @__PURE__ */ u("span", { children: [
                loading ? `loading ${loadedCount}/${Math.max(totalCount, records.length)}` : normalizedSearchQuery ? `${searchPending ? "searching" : "matches"} ${searchResult.totalMatches}/${records.length}` : hasMore || totalCount > records.length ? `rows ${records.length}/${Math.max(totalCount, records.length)}` : `rows ${records.length}`,
                !normalizedSearchQuery && loadingMore ? " buffering..." : ""
              ] }),
              /* @__PURE__ */ u("div", { class: "flex items-center gap-3", children: [
                searchHistoryScope ? /* @__PURE__ */ u("span", { children: [
                  "history ",
                  searchHistoryCount
                ] }) : null,
                /* @__PURE__ */ u("span", { children: [
                  "selected ",
                  selectedRecords.length,
                  " (",
                  selectionMode,
                  ")"
                ] }),
                /* @__PURE__ */ u("span", { children: [
                  "rendered ",
                  visibleRows.length,
                  "/",
                  sortedRecords.length,
                  " (window ",
                  startIndex + 1,
                  "-",
                  endIndex || 0,
                  ")"
                ] })
              ] })
            ] })
          ]
        }
      ),
      /* @__PURE__ */ u(
        "main",
        {
          ref: scrollAreaRef,
          onScroll: onTableScroll,
          class: "max-w-full grow overflow-y-auto overflow-x-auto bg-base-200 overscroll-none rounded-box-half border border-base-300",
          children: ActiveAlternateView ? /* @__PURE__ */ u(
            ActiveAlternateView,
            {
              records: sortedRecords,
              scrollParentRef: scrollAreaRef,
              storageKey: `${resolvedViewStateKey}:${(activeAlternateView == null ? void 0 : activeAlternateView.id) || "table"}`,
              fullscreen: isFullscreen,
              onOpenMedia: (url) => {
                setMediaPreview(url);
                setShowMediaModal(true);
              }
            }
          ) : /* @__PURE__ */ u(preact.Fragment, { children: [
            /* @__PURE__ */ u("table", { class: "table table-pin-rows table-border-bc table-padding-sm", children: [
              /* @__PURE__ */ u("thead", { children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ u("tr", { children: headerGroup.headers.map((header) => /* @__PURE__ */ u(
                "th",
                {
                  className: header.column.getCanSort() ? "cursor-pointer select-none" : "",
                  onClick: header.column.getToggleSortingHandler(),
                  children: [
                    flexRender(header.column.columnDef.header, header.getContext()),
                    header.column.getIsSorted() === "asc" && /* @__PURE__ */ u(IconSortAscending, { size: 15, class: "inline align-top ml-1" }),
                    header.column.getIsSorted() === "desc" && /* @__PURE__ */ u(IconSortDescending, { size: 15, class: "inline align-top ml-1" })
                  ]
                },
                header.id
              )) }, headerGroup.id)) }),
              /* @__PURE__ */ u("tbody", { ref: tbodyRef, children: [
                topSpacerHeight > 0 ? /* @__PURE__ */ u("tr", { "aria-hidden": "true", children: /* @__PURE__ */ u(
                  "td",
                  {
                    colSpan: Math.max(1, table.getVisibleFlatColumns().length),
                    style: { height: `${topSpacerHeight}px`, padding: 0, border: 0 }
                  }
                ) }) : null,
                visibleRows.map((row, index) => /* @__PURE__ */ u(
                  "tr",
                  {
                    "data-vrow": "1",
                    "data-vrow-key": rowKeys[startIndex + index] || row.id,
                    children: row.getVisibleCells().map((cell) => /* @__PURE__ */ u("td", { children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id))
                  },
                  row.id
                )),
                bottomSpacerHeight > 0 ? /* @__PURE__ */ u("tr", { "aria-hidden": "true", children: /* @__PURE__ */ u(
                  "td",
                  {
                    colSpan: Math.max(1, table.getVisibleFlatColumns().length),
                    style: { height: `${bottomSpacerHeight}px`, padding: 0, border: 0 }
                  }
                ) }) : null
              ] })
            ] }),
            sortedRecords.length > 0 ? null : /* @__PURE__ */ u("div", { class: "flex items-center justify-center h-[320px] w-full", children: /* @__PURE__ */ u("p", { class: "text-base-content text-opacity-50", children: t("No data available.") }) })
          ] })
        }
      ),
      /* @__PURE__ */ u("div", { class: "flex mt-1.5 items-center gap-2 border-t border-base-300 px-2 py-1.5", children: [
        showClearButton ? /* @__PURE__ */ u("button", { class: "btn btn-sm btn-neutral btn-ghost", onClick: clear, children: t("Clear") }) : null,
        /* @__PURE__ */ u("span", { class: "flex-grow" }),
        renderActions == null ? void 0 : renderActions(table, {
          loading,
          loadingMore,
          loadedCount,
          totalCount,
          resultRecords: sortedRecords,
          visibleRecords
        }),
        /* @__PURE__ */ u(
          "button",
          {
            class: "btn btn-sm btn-primary",
            onClick: openExportDataModal,
            disabled: loading,
            title: preparingExport ? "Export menu is open while remaining rows load in the background." : loading ? "Wait for records to finish loading before exporting." : hasMore && !normalizedSearchQuery ? "Opens immediately and loads remaining rows in the background." : void 0,
            children: [
              preparingExport ? /* @__PURE__ */ u("span", { class: "loading loading-spinner" }) : null,
              t("Export Data")
            ]
          }
        )
      ] }),
      mediaPreview && !showMediaModal ? /* @__PURE__ */ u("aside", { class: "absolute right-2 bottom-14 z-[2] w-56 rounded-box-half border border-base-300 bg-base-100 shadow-lg", children: [
        /* @__PURE__ */ u("header", { class: "flex items-center justify-between border-b border-base-300 px-2 py-1 text-xs font-semibold", children: [
          /* @__PURE__ */ u("span", { children: t("Media View") }),
          /* @__PURE__ */ u("div", { class: "flex items-center gap-1", children: [
            /* @__PURE__ */ u("button", { class: "btn btn-ghost btn-xs", onClick: () => setShowMediaModal(true), children: "Open" }),
            /* @__PURE__ */ u("button", { class: "btn btn-ghost btn-xs", onClick: () => setMediaPreview(""), children: /* @__PURE__ */ u(IconX, { size: 12 }) })
          ] })
        ] }),
        /* @__PURE__ */ u("div", { class: "h-36 overflow-hidden bg-base-200", children: mediaPreview.includes(".mp4") ? /* @__PURE__ */ u("video", { controls: true, class: "h-full w-full object-contain", src: mediaPreview }) : /* @__PURE__ */ u("img", { class: "h-full w-full object-contain", src: mediaPreview }) })
      ] }) : null,
      /* @__PURE__ */ u(
        Modal,
        {
          title: t("JSON View"),
          class: "max-w-xl",
          show: !!rawDataPreview,
          onClose: () => setRawDataPreview(null),
          children: /* @__PURE__ */ u("main", { class: "max-w-full max-h-[500px] overflow-scroll overscroll-none", children: typeof rawDataPreview === "string" ? /* @__PURE__ */ u("p", { class: "whitespace-pre-wrap", children: rawDataPreview }) : /* @__PURE__ */ u("pre", { class: "text-xs leading-none", children: JSON.stringify(rawDataPreview, null, 2) }) })
        }
      ),
      /* @__PURE__ */ u(
        Modal,
        {
          title: t("Media View"),
          class: "max-w-xl",
          show: showMediaModal && !!mediaPreview,
          onClose: () => setShowMediaModal(false),
          children: /* @__PURE__ */ u("main", { class: "max-w-full", children: mediaPreview.includes(".mp4") ? /* @__PURE__ */ u("video", { controls: true, class: "w-full max-h-[400px] object-contain", src: mediaPreview }) : /* @__PURE__ */ u("img", { class: "w-full max-h-[400px] object-contain", src: mediaPreview }) })
        }
      ),
      /* @__PURE__ */ u(
        Modal,
        {
          title: "Search Operators",
          class: "max-w-2xl",
          show: showSearchHelp,
          onClose: toggleShowSearchHelp,
          children: /* @__PURE__ */ u("div", { class: "text-sm leading-6", children: [
            /* @__PURE__ */ u("p", { class: "mb-2", children: [
              "Query semantics now follow recorder-style precedence:",
              /* @__PURE__ */ u("code", { class: "ml-1", children: "NOT" }),
              ",",
              /* @__PURE__ */ u("code", { class: "ml-1", children: "AND" }),
              ",",
              /* @__PURE__ */ u("code", { class: "ml-1", children: "OR" }),
              ", with implicit",
              /* @__PURE__ */ u("code", { class: "ml-1", children: "AND" }),
              " between adjacent terms."
            ] }),
            /* @__PURE__ */ u("div", { class: "grid gap-3 md:grid-cols-2", children: operatorHelpGroups.map(([category, entries]) => /* @__PURE__ */ u(
              "section",
              {
                class: "rounded-box-half border border-base-300 bg-base-200/70 p-3",
                children: [
                  /* @__PURE__ */ u("h4", { class: "mb-2 text-xs font-semibold uppercase tracking-[0.08em] opacity-70", children: category.replace("_", " ") }),
                  /* @__PURE__ */ u("div", { class: "space-y-2", children: entries.map((entry) => /* @__PURE__ */ u("div", { class: "text-xs", children: [
                    /* @__PURE__ */ u("div", { class: "font-mono text-[11px] text-info", children: entry.syntax }),
                    /* @__PURE__ */ u("div", { children: entry.description }),
                    /* @__PURE__ */ u("div", { class: "font-mono opacity-70", children: entry.examples.join(" | ") })
                  ] }, `${category}-${entry.syntax}`)) })
                ]
              },
              category
            )) })
          ] })
        }
      ),
      /* @__PURE__ */ u(
        ExportDataModal,
        {
          title,
          columns: columns2,
          resultRecords: sortedRecords,
          selectedRecords,
          resultSetSnapshot,
          selectionMode,
          preparingFullDataset: preparingExport,
          show: showExportDataModal,
          onClose: toggleShowExportDataModal
        }
      ),
      renderExtra == null ? void 0 : renderExtra(table, {
        resultSetSnapshot,
        resultRecords: sortedRecords,
        selectedRecords,
        selectionMode
      })
    ] });
  }
  const columnHelper$2 = tableCore.createColumnHelper();
  const tweetTableDerivedCache = /* @__PURE__ */ new WeakMap();
  function getDerivedTweetTableData(row) {
    var _a2;
    const cached = tweetTableDerivedCache.get(row);
    if (cached) {
      return cached;
    }
    const bookmarkFolderName2 = (() => {
      const obj = row;
      const source = obj.__bookmark_folder_name_source;
      const name2 = obj.__bookmark_folder_name;
      if (source === "api" && typeof name2 === "string" && name2.trim()) return name2;
      return null;
    })();
    const bookmarkFolderId = (() => {
      const id2 = row.__bookmark_folder_id;
      return typeof id2 === "string" && id2.trim() ? id2 : null;
    })();
    const derived = {
      createdAtMs: typeof ((_a2 = row.twe_private_fields) == null ? void 0 : _a2.created_at) === "number" ? row.twe_private_fields.created_at : extractTweetCreatedAtMs(row),
      fullText: extractTweetFullText(row),
      media: extractTweetMedia(row),
      mediaTags: extractTweetMediaTags(row),
      retweetedTweet: extractRetweetedTweet(row),
      quotedTweet: extractQuotedTweet(row),
      bookmarkFolderName: bookmarkFolderName2,
      bookmarkFolderId
    };
    tweetTableDerivedCache.set(row, derived);
    return derived;
  }
  const rtSourceAccessor = (row) => {
    const source = getDerivedTweetTableData(row).retweetedTweet;
    return source ? extractTweetUserScreenName(source) : null;
  };
  const quoteSourceAccessor = (row) => {
    const source = getDerivedTweetTableData(row).quotedTweet;
    return source ? extractTweetUserScreenName(source) : null;
  };
  const bookmarkFolderNameAccessor = (row) => getDerivedTweetTableData(row).bookmarkFolderName;
  const bookmarkFolderIdAccessor = (row) => getDerivedTweetTableData(row).bookmarkFolderId;
  const columns$2 = [
    columnHelper$2.display({
      id: "select",
      meta: { exportable: false },
      header: ({ table }) => {
        var _a2, _b2, _c, _d;
        return /* @__PURE__ */ u(
          "input",
          {
            type: "checkbox",
            class: "checkbox checkbox-sm align-middle",
            checked: ((_b2 = (_a2 = table.options.meta) == null ? void 0 : _a2.isAllResultRowsSelected) == null ? void 0 : _b2.call(_a2)) ?? table.getIsAllRowsSelected(),
            indeterminate: ((_d = (_c = table.options.meta) == null ? void 0 : _c.isSomeResultRowsSelected) == null ? void 0 : _d.call(_c)) ?? table.getIsSomeRowsSelected(),
            onChange: () => {
              var _a3;
              if ((_a3 = table.options.meta) == null ? void 0 : _a3.toggleAllResultRowsSelected) {
                table.options.meta.toggleAllResultRowsSelected();
                return;
              }
              table.toggleAllRowsSelected();
            }
          }
        );
      },
      cell: ({ row, table }) => {
        var _a2, _b2;
        return /* @__PURE__ */ u(
          "input",
          {
            type: "checkbox",
            class: "checkbox checkbox-sm",
            checked: ((_b2 = (_a2 = table.options.meta) == null ? void 0 : _a2.isResultRowSelected) == null ? void 0 : _b2.call(_a2, row.id)) ?? row.getIsSelected(),
            disabled: !row.getCanSelect(),
            indeterminate: row.getIsSomeSelected(),
            onChange: () => {
              var _a3;
              if ((_a3 = table.options.meta) == null ? void 0 : _a3.toggleResultRowSelected) {
                table.options.meta.toggleResultRowSelected(row.id);
                return;
              }
              row.toggleSelected();
            }
          }
        );
      }
    }),
    columnHelper$2.accessor("rest_id", {
      meta: { exportKey: "id", exportHeader: "ID" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "ID" }),
      cell: (info) => /* @__PURE__ */ u("p", { class: "w-20 break-all font-mono text-xs", children: info.getValue() })
    }),
    columnHelper$2.accessor((row) => getDerivedTweetTableData(row).createdAtMs, {
      id: "created_at",
      meta: {
        exportKey: "created_at",
        exportHeader: "Date",
        exportValue: (row) => formatDateTime(
          getDerivedTweetTableData(row.original).createdAtMs,
          appOptionsManager.get("dateTimeFormat")
        )
      },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Date" }),
      cell: (info) => /* @__PURE__ */ u("p", { class: "w-24", children: /* @__PURE__ */ u("a", { class: "link", target: "_blank", href: getTweetURL(info.row.original), children: formatDateTime(info.getValue(), appOptionsManager.get("dateTimeFormat")) }) })
    }),
    columnHelper$2.accessor("legacy.full_text", {
      meta: {
        exportKey: "full_text",
        exportHeader: "Content",
        exportValue: (row) => getDerivedTweetTableData(row.original).fullText
      },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Content" }),
      cell: (info) => {
        var _a2, _b2, _c, _d;
        return /* @__PURE__ */ u("div", { children: [
          /* @__PURE__ */ u(
            "p",
            {
              class: "w-60 whitespace-pre-wrap",
              dangerouslySetInnerHTML: {
                __html: strEntitiesToHTML(getDerivedTweetTableData(info.row.original).fullText, [
                  ...((_b2 = (_a2 = info.row.original.legacy) == null ? void 0 : _a2.entities) == null ? void 0 : _b2.urls) ?? [],
                  ...((_d = (_c = info.row.original.legacy) == null ? void 0 : _c.entities) == null ? void 0 : _d.media) ?? []
                ])
              }
            }
          ),
          info.row.original.note_tweet && /* @__PURE__ */ u(
            "button",
            {
              class: "link",
              onClick: () => {
                var _a3;
                return (_a3 = info.table.options.meta) == null ? void 0 : _a3.setRawDataPreview(
                  getDerivedTweetTableData(info.row.original).fullText
                );
              },
              children: [
                ">>",
                " ",
                /* @__PURE__ */ u(Trans, { i18nKey: "Show Full Text" })
              ]
            }
          )
        ] });
      }
    }),
    columnHelper$2.accessor((row) => getDerivedTweetTableData(row).media.length, {
      id: "media",
      meta: {
        exportKey: "media",
        exportHeader: "Media",
        exportValue: (row) => getDerivedTweetTableData(row.original).media.map((media) => ({
          type: media.type,
          url: media.url,
          thumbnail: formatTwitterImage(media.media_url_https, "thumb"),
          original: getMediaOriginalUrl(media),
          ext_alt_text: media.ext_alt_text
        }))
      },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Media" }),
      cell: (info) => /* @__PURE__ */ u(
        MediaDisplayColumn,
        {
          data: getDerivedTweetTableData(info.row.original).media,
          onClick: (media) => {
            var _a2;
            return (_a2 = info.table.options.meta) == null ? void 0 : _a2.setMediaPreview(getMediaOriginalUrl(media));
          }
        }
      )
    }),
    columnHelper$2.accessor("core.user_results.result.core.screen_name", {
      meta: { exportKey: "screen_name", exportHeader: "Screen Name" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Screen Name" }),
      cell: (info) => /* @__PURE__ */ u("p", { class: "whitespace-pre", children: /* @__PURE__ */ u(
        "a",
        {
          class: "link",
          target: "_blank",
          href: getUserURL(info.row.original.core.user_results.result),
          children: [
            "@",
            info.getValue()
          ]
        }
      ) })
    }),
    columnHelper$2.accessor("core.user_results.result.core.name", {
      meta: { exportKey: "name", exportHeader: "Profile Name" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Profile Name" }),
      cell: (info) => /* @__PURE__ */ u("p", { class: "w-32", children: info.getValue() })
    }),
    columnHelper$2.accessor("core.user_results.result.avatar.image_url", {
      meta: { exportKey: "profile_image_url", exportHeader: "Profile Image" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Profile Image" }),
      cell: (info) => /* @__PURE__ */ u(
        "div",
        {
          class: "cursor-pointer",
          onClick: () => {
            var _a2;
            return (_a2 = info.table.options.meta) == null ? void 0 : _a2.setMediaPreview(getProfileImageOriginalUrl(info.getValue()));
          },
          children: /* @__PURE__ */ u("img", { class: "w-12 h-12 rounded", src: info.getValue(), loading: "lazy", decoding: "async" })
        }
      )
    }),
    columnHelper$2.accessor("core.user_results.result.rest_id", {
      meta: { exportKey: "user_id", exportHeader: "User ID" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "User ID" }),
      cell: (info) => /* @__PURE__ */ u("p", { class: "w-20 break-all font-mono text-xs", children: info.getValue() })
    }),
    columnHelper$2.accessor("legacy.in_reply_to_screen_name", {
      meta: {
        exportKey: "in_reply_to",
        exportHeader: "Replying To",
        exportValue: (row) => row.original.legacy.in_reply_to_status_id_str
      },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Replying To" }),
      cell: (info) => /* @__PURE__ */ u("p", { class: "whitespace-pre", children: info.row.original.legacy.in_reply_to_status_id_str ? /* @__PURE__ */ u("a", { class: "link", target: "_blank", href: getInReplyToTweetURL(info.row.original), children: [
        "@",
        info.getValue()
      ] }) : "N/A" })
    }),
    columnHelper$2.accessor(rtSourceAccessor, {
      id: "rt_source",
      meta: {
        exportKey: "retweeted_status",
        exportHeader: "RT Source",
        exportValue: (row) => {
          var _a2;
          return (_a2 = extractRetweetedTweet(row.original)) == null ? void 0 : _a2.rest_id;
        }
      },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "RT Source" }),
      cell: (info) => {
        const source = getDerivedTweetTableData(info.row.original).retweetedTweet;
        return /* @__PURE__ */ u("p", { class: "whitespace-pre", children: source ? /* @__PURE__ */ u("a", { class: "link", target: "_blank", href: getTweetURL(source), children: [
          "@",
          info.getValue()
        ] }) : "N/A" });
      }
    }),
    columnHelper$2.accessor(quoteSourceAccessor, {
      id: "quote_source",
      meta: {
        exportKey: "quoted_status",
        exportHeader: "Quote Source",
        exportValue: (row) => {
          var _a2;
          return (_a2 = extractQuotedTweet(row.original)) == null ? void 0 : _a2.rest_id;
        }
      },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Quote Source" }),
      cell: (info) => {
        const source = getDerivedTweetTableData(info.row.original).quotedTweet;
        return /* @__PURE__ */ u("p", { class: "whitespace-pre", children: source ? /* @__PURE__ */ u("a", { class: "link", target: "_blank", href: getTweetURL(source), children: [
          "@",
          info.getValue()
        ] }) : "N/A" });
      }
    }),
    columnHelper$2.display({
      id: "media_tags",
      meta: {
        exportKey: "media_tags",
        exportHeader: "Media Tags",
        exportValue: (row) => getDerivedTweetTableData(row.original).mediaTags
      },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Media Tags" }),
      cell: (info) => /* @__PURE__ */ u("p", { children: getDerivedTweetTableData(info.row.original).mediaTags.length ? getDerivedTweetTableData(info.row.original).mediaTags.map((tag) => /* @__PURE__ */ u("a", { class: "link inline-block", target: "_blank", href: getUserURL(tag.screen_name), children: [
        "@",
        tag.screen_name
      ] })) : "N/A" })
    }),
    columnHelper$2.accessor("legacy.favorite_count", {
      meta: { exportKey: "favorite_count", exportHeader: "Favorites" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Favorites" }),
      cell: (info) => /* @__PURE__ */ u("p", { children: info.getValue() })
    }),
    columnHelper$2.accessor("legacy.retweet_count", {
      meta: { exportKey: "retweet_count", exportHeader: "Retweets" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Retweets" }),
      cell: (info) => /* @__PURE__ */ u("p", { children: info.getValue() })
    }),
    columnHelper$2.accessor("legacy.bookmark_count", {
      meta: { exportKey: "bookmark_count", exportHeader: "Bookmarks" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Bookmarks" }),
      cell: (info) => /* @__PURE__ */ u("p", { children: info.getValue() })
    }),
    columnHelper$2.accessor("legacy.quote_count", {
      meta: { exportKey: "quote_count", exportHeader: "Quotes" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Quotes" }),
      cell: (info) => /* @__PURE__ */ u("p", { children: info.getValue() })
    }),
    columnHelper$2.accessor("legacy.reply_count", {
      meta: { exportKey: "reply_count", exportHeader: "Replies" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Replies" }),
      cell: (info) => /* @__PURE__ */ u("p", { children: info.getValue() })
    }),
    columnHelper$2.accessor("views.count", {
      meta: {
        exportKey: "views_count",
        exportHeader: "Views",
        exportValue: (row) => {
          var _a2;
          return typeof ((_a2 = row.original.views) == null ? void 0 : _a2.count) === "undefined" ? null : +row.original.views.count;
        }
      },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Views" }),
      cell: (info) => /* @__PURE__ */ u("p", { children: info.getValue() ?? "N/A" })
    }),
    columnHelper$2.accessor("legacy.favorited", {
      meta: { exportKey: "favorited", exportHeader: "Favorited" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Favorited" }),
      cell: (info) => /* @__PURE__ */ u("p", { children: info.getValue() ? "YES" : "NO" })
    }),
    columnHelper$2.accessor("legacy.retweeted", {
      meta: { exportKey: "retweeted", exportHeader: "Retweeted" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Retweeted" }),
      cell: (info) => /* @__PURE__ */ u("p", { children: info.getValue() ? "YES" : "NO" })
    }),
    columnHelper$2.accessor("legacy.bookmarked", {
      meta: { exportKey: "bookmarked", exportHeader: "Bookmarked" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Bookmarked" }),
      cell: (info) => /* @__PURE__ */ u("p", { children: info.getValue() ? "YES" : "NO" })
    }),
    columnHelper$2.accessor(bookmarkFolderNameAccessor, {
      id: "bookmark_folder_name",
      meta: {
        exportKey: "bookmark_folder_name",
        exportHeader: "Bookmark Folder",
        exportValue: (row) => bookmarkFolderNameAccessor(row.original)
      },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Bookmark Folder" }),
      cell: (info) => {
        const val = info.getValue();
        return val ? /* @__PURE__ */ u("p", { class: "w-36 truncate", title: String(val), children: val }) : /* @__PURE__ */ u("p", { class: "text-gray-400", children: "—" });
      }
    }),
    columnHelper$2.accessor(bookmarkFolderIdAccessor, {
      id: "bookmark_folder_id",
      meta: {
        exportKey: "bookmark_folder_id",
        exportHeader: "Bookmark Folder ID",
        exportValue: (row) => bookmarkFolderIdAccessor(row.original)
      },
      header: () => /* @__PURE__ */ u("span", { children: "Bookmark Folder ID" }),
      cell: (info) => {
        const val = info.getValue();
        return val ? /* @__PURE__ */ u("p", { class: "w-32 break-all font-mono text-xs", children: val }) : /* @__PURE__ */ u("p", { class: "text-gray-400", children: "—" });
      }
    }),
    columnHelper$2.display({
      id: "url",
      meta: {
        exportKey: "url",
        exportHeader: "URL",
        exportValue: (row) => getTweetURL(row.original)
      },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "URL" }),
      cell: (info) => /* @__PURE__ */ u("a", { href: getTweetURL(info.row.original), target: "_blank", children: /* @__PURE__ */ u(IconLink, {}) })
    }),
    columnHelper$2.display({
      id: "actions",
      meta: { exportable: false },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Actions" }),
      cell: (info) => /* @__PURE__ */ u("div", { class: "flex flex-row items-start space-x-1", children: /* @__PURE__ */ u(
        "button",
        {
          onClick: () => {
            var _a2;
            return (_a2 = info.table.options.meta) == null ? void 0 : _a2.setRawDataPreview(info.row.original);
          },
          class: "btn btn-xs btn-neutral whitespace-nowrap",
          children: /* @__PURE__ */ u(Trans, { i18nKey: "Details" })
        }
      ) })
    })
  ];
  const columnHelper$1 = tableCore.createColumnHelper();
  const columns$1 = [
    columnHelper$1.display({
      id: "select",
      meta: { exportable: false },
      header: ({ table }) => {
        var _a2, _b2, _c, _d;
        return /* @__PURE__ */ u(
          "input",
          {
            type: "checkbox",
            class: "checkbox checkbox-sm align-middle",
            checked: ((_b2 = (_a2 = table.options.meta) == null ? void 0 : _a2.isAllResultRowsSelected) == null ? void 0 : _b2.call(_a2)) ?? table.getIsAllRowsSelected(),
            indeterminate: ((_d = (_c = table.options.meta) == null ? void 0 : _c.isSomeResultRowsSelected) == null ? void 0 : _d.call(_c)) ?? table.getIsSomeRowsSelected(),
            onChange: () => {
              var _a3;
              if ((_a3 = table.options.meta) == null ? void 0 : _a3.toggleAllResultRowsSelected) {
                table.options.meta.toggleAllResultRowsSelected();
                return;
              }
              table.toggleAllRowsSelected();
            }
          }
        );
      },
      cell: ({ row, table }) => {
        var _a2, _b2;
        return /* @__PURE__ */ u(
          "input",
          {
            type: "checkbox",
            class: "checkbox checkbox-sm",
            checked: ((_b2 = (_a2 = table.options.meta) == null ? void 0 : _a2.isResultRowSelected) == null ? void 0 : _b2.call(_a2, row.id)) ?? row.getIsSelected(),
            disabled: !row.getCanSelect(),
            indeterminate: row.getIsSomeSelected(),
            onChange: () => {
              var _a3;
              if ((_a3 = table.options.meta) == null ? void 0 : _a3.toggleResultRowSelected) {
                table.options.meta.toggleResultRowSelected(row.id);
                return;
              }
              row.toggleSelected();
            }
          }
        );
      }
    }),
    columnHelper$1.accessor("rest_id", {
      meta: { exportKey: "id", exportHeader: "ID" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "ID" }),
      cell: (info) => /* @__PURE__ */ u("p", { class: "w-20 break-all font-mono text-xs", children: info.getValue() })
    }),
    columnHelper$1.accessor("core.screen_name", {
      meta: { exportKey: "screen_name", exportHeader: "Screen Name" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Screen Name" }),
      cell: (info) => /* @__PURE__ */ u("p", { class: "whitespace-pre", children: /* @__PURE__ */ u("a", { class: "link", target: "_blank", href: getUserURL(info.row.original), children: [
        "@",
        info.getValue()
      ] }) })
    }),
    columnHelper$1.accessor("core.name", {
      meta: { exportKey: "name", exportHeader: "Profile Name" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Profile Name" }),
      cell: (info) => /* @__PURE__ */ u("p", { class: "w-32", children: info.getValue() })
    }),
    columnHelper$1.accessor("legacy.description", {
      meta: { exportKey: "description", exportHeader: "Description" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Description" }),
      cell: (info) => {
        var _a2;
        return /* @__PURE__ */ u(
          "p",
          {
            class: "w-52 break-words",
            dangerouslySetInnerHTML: {
              __html: strEntitiesToHTML(
                info.row.original.legacy.description || "N/A",
                (_a2 = info.row.original.legacy.entities) == null ? void 0 : _a2.description.urls
              )
            }
          }
        );
      }
    }),
    columnHelper$1.accessor("avatar.image_url", {
      meta: { exportKey: "profile_image_url", exportHeader: "Profile Image" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Profile Image" }),
      cell: (info) => /* @__PURE__ */ u(
        "div",
        {
          class: "cursor-pointer",
          onClick: () => {
            var _a2;
            return (_a2 = info.table.options.meta) == null ? void 0 : _a2.setMediaPreview(getProfileImageOriginalUrl(info.getValue()));
          },
          children: /* @__PURE__ */ u("img", { class: "w-12 h-12 rounded", src: info.getValue(), loading: "lazy", decoding: "async" })
        }
      )
    }),
    columnHelper$1.accessor("legacy.profile_banner_url", {
      meta: { exportKey: "profile_banner_url", exportHeader: "Profile Banner" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Profile Banner" }),
      cell: (info) => /* @__PURE__ */ u(
        "div",
        {
          class: "cursor-pointer w-36 h-12",
          onClick: () => {
            var _a2;
            return (_a2 = info.table.options.meta) == null ? void 0 : _a2.setMediaPreview(info.getValue() ?? "");
          },
          children: info.getValue() ? /* @__PURE__ */ u(
            "img",
            {
              class: "w-auto h-12 rounded",
              src: `${info.getValue()}/600x200`,
              loading: "lazy",
              decoding: "async"
            }
          ) : /* @__PURE__ */ u("span", { class: "leading-[48px]", children: "N/A" })
        }
      )
    }),
    columnHelper$1.accessor("legacy.followers_count", {
      meta: { exportKey: "followers_count", exportHeader: "Followers" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Followers" }),
      cell: (info) => /* @__PURE__ */ u("p", { children: info.getValue() ?? "N/A" })
    }),
    columnHelper$1.accessor("legacy.friends_count", {
      meta: { exportKey: "friends_count", exportHeader: "FollowingCount" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "FollowingCount" }),
      cell: (info) => /* @__PURE__ */ u("p", { children: info.getValue() ?? "N/A" })
    }),
    columnHelper$1.accessor("legacy.statuses_count", {
      meta: { exportKey: "statuses_count", exportHeader: "Statuses" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Statuses" }),
      cell: (info) => /* @__PURE__ */ u("p", { children: info.getValue() ?? "N/A" })
    }),
    columnHelper$1.accessor("legacy.favourites_count", {
      meta: { exportKey: "favourites_count", exportHeader: "Favourites" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Favourites" }),
      cell: (info) => /* @__PURE__ */ u("p", { children: info.getValue() ?? "N/A" })
    }),
    columnHelper$1.accessor("legacy.listed_count", {
      meta: { exportKey: "listed_count", exportHeader: "Listed" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Listed" }),
      cell: (info) => /* @__PURE__ */ u("p", { children: info.getValue() ?? "N/A" })
    }),
    columnHelper$1.accessor("location.location", {
      meta: { exportKey: "location", exportHeader: "Location" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Location" }),
      cell: (info) => /* @__PURE__ */ u("p", { children: info.getValue() ?? "N/A" })
    }),
    columnHelper$1.accessor("legacy.url", {
      meta: { exportKey: "website", exportHeader: "Website" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Website" }),
      cell: (info) => {
        var _a2, _b2;
        return /* @__PURE__ */ u(
          "p",
          {
            dangerouslySetInnerHTML: {
              __html: strEntitiesToHTML(
                info.row.original.legacy.url || "N/A",
                (_b2 = (_a2 = info.row.original.legacy.entities) == null ? void 0 : _a2.url) == null ? void 0 : _b2.urls
              )
            }
          }
        );
      }
    }),
    columnHelper$1.accessor("legacy_extended_profile.birthdate", {
      meta: {
        exportKey: "birthdate",
        exportHeader: "Birthdate",
        exportValue: (row) => {
          var _a2;
          return formatTwitterBirthdate((_a2 = row.original.legacy_extended_profile) == null ? void 0 : _a2.birthdate);
        }
      },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Birthdate" }),
      cell: (info) => /* @__PURE__ */ u("p", { children: formatTwitterBirthdate(info.getValue()) ?? "N/A" })
    }),
    columnHelper$1.accessor("verification.verified_type", {
      meta: { exportKey: "verified_type", exportHeader: "Verified Type" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Verified Type" }),
      cell: (info) => /* @__PURE__ */ u("p", { children: info.getValue() ?? "N/A" })
    }),
    columnHelper$1.accessor("is_blue_verified", {
      meta: { exportKey: "is_blue_verified", exportHeader: "Blue Verified" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Blue Verified" }),
      cell: (info) => /* @__PURE__ */ u("p", { children: info.getValue() ? "YES" : "NO" })
    }),
    columnHelper$1.accessor("relationship_perspectives.following", {
      meta: { exportKey: "following", exportHeader: "Following" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Following" }),
      cell: (info) => /* @__PURE__ */ u("p", { children: info.getValue() ? "YES" : "NO" })
    }),
    columnHelper$1.accessor("relationship_perspectives.followed_by", {
      meta: { exportKey: "followed_by", exportHeader: "Follows You" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Follows You" }),
      cell: (info) => /* @__PURE__ */ u("p", { children: info.getValue() ? "YES" : "NO" })
    }),
    columnHelper$1.accessor((row) => {
      var _a2, _b2;
      return ((_b2 = (_a2 = row.twe_relationship_fields) == null ? void 0 : _a2.relation_types) == null ? void 0 : _b2.join(", ")) || "";
    }, {
      id: "relation_types",
      meta: { exportKey: "relation_types", exportHeader: "Relation Types" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Relation Types" }),
      cell: (info) => /* @__PURE__ */ u("p", { children: info.getValue() || "N/A" })
    }),
    columnHelper$1.accessor(
      (row) => {
        var _a2, _b2;
        return ((_b2 = (_a2 = row.twe_relationship_fields) == null ? void 0 : _a2.subject_screen_names) == null ? void 0 : _b2.join(", ")) || "";
      },
      {
        id: "subject_screen_names",
        meta: { exportKey: "subject_screen_names", exportHeader: "Subject Accounts" },
        header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Subject Accounts" }),
        cell: (info) => /* @__PURE__ */ u("p", { class: "w-40 break-words", children: info.getValue() || "N/A" })
      }
    ),
    columnHelper$1.accessor((row) => {
      var _a2, _b2;
      return ((_b2 = (_a2 = row.twe_relationship_fields) == null ? void 0 : _a2.subject_user_ids) == null ? void 0 : _b2.join(", ")) || "";
    }, {
      id: "subject_user_ids",
      meta: { exportKey: "subject_user_ids", exportHeader: "Subject User IDs" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Subject User IDs" }),
      cell: (info) => /* @__PURE__ */ u("p", { class: "w-28 break-all font-mono text-xs", children: info.getValue() || "N/A" })
    }),
    columnHelper$1.accessor((row) => {
      var _a2;
      return ((_a2 = row.twe_relationship_fields) == null ? void 0 : _a2.last_observed_at) || 0;
    }, {
      id: "last_observed_at",
      meta: {
        exportKey: "last_observed_at",
        exportHeader: "Last Observed At",
        exportValue: (row) => {
          var _a2;
          return ((_a2 = row.original.twe_relationship_fields) == null ? void 0 : _a2.last_observed_at) ? formatDateTime(
            row.original.twe_relationship_fields.last_observed_at,
            appOptionsManager.get("dateTimeFormat")
          ) : "";
        }
      },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Last Observed At" }),
      cell: (info) => /* @__PURE__ */ u("p", { class: "w-24", children: info.getValue() ? formatDateTime(info.getValue(), appOptionsManager.get("dateTimeFormat")) : "N/A" })
    }),
    columnHelper$1.accessor("dm_permissions.can_dm", {
      meta: { exportKey: "can_dm", exportHeader: "Can DM" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Can DM" }),
      cell: (info) => /* @__PURE__ */ u("p", { children: info.getValue() ? "YES" : "NO" })
    }),
    columnHelper$1.accessor("privacy.protected", {
      meta: { exportKey: "protected", exportHeader: "Protected" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Protected" }),
      cell: (info) => /* @__PURE__ */ u("p", { children: info.getValue() ? "YES" : "NO" })
    }),
    columnHelper$1.accessor((row) => +parseTwitterDateTime(row.core.created_at), {
      id: "created_at",
      meta: {
        exportKey: "created_at",
        exportHeader: "Created At",
        exportValue: (row) => formatDateTime(
          parseTwitterDateTime(row.original.core.created_at),
          appOptionsManager.get("dateTimeFormat")
        )
      },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Created At" }),
      cell: (info) => /* @__PURE__ */ u("p", { class: "w-24", children: info.getValue() ? formatDateTime(info.getValue(), appOptionsManager.get("dateTimeFormat")) : "N/A" })
    }),
    columnHelper$1.display({
      id: "url",
      meta: {
        exportKey: "url",
        exportHeader: "URL",
        exportValue: (row) => getUserURL(row.original)
      },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "URL" }),
      cell: (info) => /* @__PURE__ */ u("a", { href: getUserURL(info.row.original), target: "_blank", children: /* @__PURE__ */ u(IconLink, {}) })
    }),
    columnHelper$1.display({
      id: "actions",
      meta: { exportable: false },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Actions" }),
      cell: (info) => /* @__PURE__ */ u("div", { class: "flex flex-row items-start space-x-1", children: /* @__PURE__ */ u(
        "button",
        {
          onClick: () => {
            var _a2;
            return (_a2 = info.table.options.meta) == null ? void 0 : _a2.setRawDataPreview(info.row.original);
          },
          class: "btn btn-xs btn-neutral whitespace-nowrap",
          children: /* @__PURE__ */ u(Trans, { i18nKey: "Details" })
        }
      ) })
    })
  ];
  const INITIAL_BATCH = 42;
  const BATCH_SIZE = 24;
  const SCROLL_THRESHOLD_PX = 900;
  const COMFORTABLE_CARD_WIDTH = 320;
  const COMPACT_CARD_WIDTH = 264;
  const NARROW_COMFORTABLE_CARD_WIDTH = 228;
  const NARROW_COMPACT_CARD_WIDTH = 196;
  const COMFORTABLE_GAP = 16;
  const COMPACT_GAP = 14;
  function extractOriginalTweetMedia(tweet) {
    if (extractRetweetedTweet(tweet)) {
      return [];
    }
    const media = extractTweetMedia(tweet);
    return media.filter(
      (item) => item.type === "photo" || item.type === "video" || item.type === "animated_gif"
    );
  }
  function bookmarkFolderName(tweet) {
    const row = tweet;
    return row.__bookmark_folder_name_source === "api" && typeof row.__bookmark_folder_name === "string" ? row.__bookmark_folder_name.trim() : "";
  }
  function mediaPreviewUrl(media) {
    if (media.type === "photo") {
      return formatTwitterImage(media.media_url_https, "large");
    }
    return formatTwitterImage(media.media_url_https, "medium");
  }
  function mediaAspectRatio(media) {
    var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j;
    const width = ((_a2 = media.original_info) == null ? void 0 : _a2.width) || ((_c = (_b2 = media.sizes) == null ? void 0 : _b2.large) == null ? void 0 : _c.w) || ((_e = (_d = media.sizes) == null ? void 0 : _d.medium) == null ? void 0 : _e.w) || 1;
    const height = ((_f = media.original_info) == null ? void 0 : _f.height) || ((_h = (_g = media.sizes) == null ? void 0 : _g.large) == null ? void 0 : _h.h) || ((_j = (_i = media.sizes) == null ? void 0 : _i.medium) == null ? void 0 : _j.h) || 1;
    return Math.max(0.56, Math.min(1.8, width / height));
  }
  function clampLineEstimate(text, density) {
    if (!text.trim()) return 0;
    const charsPerLine = density === "compact" ? 34 : 42;
    const maxLines = density === "compact" ? 3 : 4;
    return Math.max(1, Math.min(maxLines, Math.ceil(text.trim().length / charsPerLine)));
  }
  function estimateItemHeight(item, columnWidth, density) {
    const mediaHeight = columnWidth / Math.max(0.56, item.aspectRatio || 1);
    const textLines = clampLineEstimate(item.fullText, density);
    const textHeight = textLines * 20;
    const headerHeight = density === "compact" ? 96 : 108;
    const footerHeight = 34;
    const badgeHeight = item.bookmarkFolderName ? 22 : 0;
    const durationHeight = item.durationLabel ? 4 : 0;
    const spacing = density === "compact" ? 24 : 30;
    return mediaHeight + headerHeight + footerHeight + badgeHeight + textHeight + durationHeight + spacing;
  }
  function buildStableColumns(items, columnCount, columnWidth, density) {
    const normalizedCount = Math.max(1, columnCount);
    const columns2 = Array.from({ length: normalizedCount }, () => ({
      items: [],
      estimatedHeight: 0
    }));
    for (const item of items) {
      let targetIndex = 0;
      for (let index = 1; index < columns2.length; index += 1) {
        if (columns2[index].estimatedHeight < columns2[targetIndex].estimatedHeight) {
          targetIndex = index;
        }
      }
      columns2[targetIndex].items.push(item);
      columns2[targetIndex].estimatedHeight += estimateItemHeight(item, columnWidth, density);
    }
    return columns2;
  }
  function TweetMediaMasonry({
    records,
    scrollParentRef,
    onOpenMedia,
    storageKey,
    fullscreen
  }) {
    var _a2;
    const { t } = useTranslation();
    const rootRef = hooks.useRef(null);
    const firstItemIdRef = hooks.useRef("");
    const [visibleCount, setVisibleCount] = hooks.useState(INITIAL_BATCH);
    const [density, setDensity] = hooks.useState("comfortable");
    const [containerWidth, setContainerWidth] = hooks.useState(0);
    hooks.useEffect(() => {
      try {
        if (typeof localStorage === "undefined" || !storageKey) return;
        const raw = localStorage.getItem(`${storageKey}:density`);
        if (raw === "compact" || raw === "comfortable") {
          setDensity(raw);
        }
      } catch {
      }
    }, [storageKey]);
    hooks.useEffect(() => {
      try {
        if (typeof localStorage === "undefined" || !storageKey) return;
        localStorage.setItem(`${storageKey}:density`, density);
      } catch {
      }
    }, [density, storageKey]);
    const items = hooks.useMemo(() => {
      return records.flatMap((tweet) => {
        const mediaList = extractOriginalTweetMedia(tweet);
        if (!mediaList.length) return [];
        const screenName = extractTweetUserScreenName(tweet);
        const fullText = extractTweetFullText(tweet).trim();
        const createdAtLabel = formatDateTime(
          extractTweetCreatedAtMs(tweet),
          appOptionsManager.get("dateTimeFormat")
        );
        const tweetUrl = getTweetURL(tweet);
        const folderName = bookmarkFolderName(tweet);
        return mediaList.map((media, index) => {
          var _a3, _b2, _c, _d, _e;
          return {
            id: `${tweet.rest_id}:${media.media_key || media.id_str || index}`,
            tweet,
            media,
            screenName,
            fullText,
            createdAtLabel,
            tweetUrl,
            previewUrl: mediaPreviewUrl(media),
            originalUrl: getMediaOriginalUrl(media),
            aspectRatio: mediaAspectRatio(media),
            bookmarkFolderName: folderName,
            favoriteCount: Number(((_a3 = tweet.legacy) == null ? void 0 : _a3.favorite_count) || 0),
            retweetCount: Number(((_b2 = tweet.legacy) == null ? void 0 : _b2.retweet_count) || 0),
            bookmarkCount: Number(((_c = tweet.legacy) == null ? void 0 : _c.bookmark_count) || 0),
            replyCount: Number(((_d = tweet.legacy) == null ? void 0 : _d.reply_count) || 0),
            durationLabel: media.type === "photo" ? "" : formatVideoDuration((_e = media.video_info) == null ? void 0 : _e.duration_millis)
          };
        });
      });
    }, [records]);
    const firstItemId = ((_a2 = items[0]) == null ? void 0 : _a2.id) || "";
    hooks.useEffect(() => {
      setVisibleCount((current) => {
        if (firstItemIdRef.current !== firstItemId) {
          firstItemIdRef.current = firstItemId;
          return Math.min(INITIAL_BATCH, Math.max(0, items.length));
        }
        if (items.length <= INITIAL_BATCH) {
          return Math.min(INITIAL_BATCH, Math.max(0, items.length));
        }
        if (current > items.length) {
          return items.length;
        }
        return Math.max(current, Math.min(INITIAL_BATCH, items.length));
      });
    }, [firstItemId, items.length]);
    hooks.useEffect(() => {
      const scrollParent = scrollParentRef.current;
      if (!scrollParent) return;
      const maybeGrow = () => {
        const remaining = scrollParent.scrollHeight - (scrollParent.scrollTop + scrollParent.clientHeight);
        if (remaining <= SCROLL_THRESHOLD_PX) {
          setVisibleCount((current) => Math.min(items.length, current + BATCH_SIZE));
        }
      };
      maybeGrow();
      scrollParent.addEventListener("scroll", maybeGrow, { passive: true });
      return () => scrollParent.removeEventListener("scroll", maybeGrow);
    }, [items.length, scrollParentRef]);
    hooks.useLayoutEffect(() => {
      const node = rootRef.current;
      const scrollParent = scrollParentRef.current;
      if (!node && !scrollParent) return;
      const measure = () => {
        const nodeWidth = (node == null ? void 0 : node.clientWidth) || 0;
        const scrollParentWidth = (scrollParent == null ? void 0 : scrollParent.clientWidth) || 0;
        setContainerWidth(Math.max(nodeWidth, scrollParentWidth, 0));
      };
      measure();
      const observer = new ResizeObserver(() => measure());
      if (node) observer.observe(node);
      if (scrollParent && scrollParent !== node) observer.observe(scrollParent);
      return () => observer.disconnect();
    }, [scrollParentRef]);
    const visibleItems = items.slice(0, visibleCount);
    const gapPx = density === "compact" ? COMPACT_GAP : COMFORTABLE_GAP;
    const useNarrowCards = !fullscreen;
    const targetCardWidth = density === "compact" ? useNarrowCards ? NARROW_COMPACT_CARD_WIDTH : COMPACT_CARD_WIDTH : useNarrowCards ? NARROW_COMFORTABLE_CARD_WIDTH : COMFORTABLE_CARD_WIDTH;
    const computedColumnCount = Math.max(
      1,
      containerWidth ? Math.floor((containerWidth + gapPx) / (targetCardWidth + gapPx)) : 1
    );
    const minColumnCount = !fullscreen && containerWidth >= 520 ? 2 : 1;
    const maxColumnCount = fullscreen ? 6 : 4;
    const columnCount = Math.max(minColumnCount, Math.min(maxColumnCount, computedColumnCount));
    const columnWidth = containerWidth > 0 ? (containerWidth - gapPx * Math.max(0, columnCount - 1)) / columnCount : targetCardWidth;
    const columns2 = hooks.useMemo(
      () => buildStableColumns(visibleItems, columnCount, columnWidth, density),
      [columnCount, columnWidth, density, visibleItems]
    );
    if (!items.length) {
      return /* @__PURE__ */ u("div", { class: "flex h-[320px] items-center justify-center text-sm opacity-60", children: t("No media available.") });
    }
    return /* @__PURE__ */ u("div", { ref: rootRef, class: "w-full min-w-0 px-3 py-3", children: [
      /* @__PURE__ */ u("div", { class: "mb-3 flex items-center justify-between gap-3 text-[11px] font-mono opacity-70", children: [
        /* @__PURE__ */ u("div", { class: "flex items-center gap-3", children: [
          /* @__PURE__ */ u("span", { children: [
            "media ",
            visibleItems.length,
            "/",
            items.length
          ] }),
          /* @__PURE__ */ u("span", { children: "original tweet attachments only" })
        ] }),
        /* @__PURE__ */ u("div", { class: "join", children: [
          /* @__PURE__ */ u(
            "button",
            {
              class: `btn join-item btn-xs ${density === "comfortable" ? "btn-primary" : "btn-ghost"}`,
              onClick: () => setDensity("comfortable"),
              title: "Comfortable density",
              children: /* @__PURE__ */ u(IconTable, { size: 14 })
            }
          ),
          /* @__PURE__ */ u(
            "button",
            {
              class: `btn join-item btn-xs ${density === "compact" ? "btn-primary" : "btn-ghost"}`,
              onClick: () => setDensity("compact"),
              title: "Compact density",
              children: /* @__PURE__ */ u(IconLayoutColumns, { size: 14 })
            }
          )
        ] })
      ] }),
      /* @__PURE__ */ u("div", { class: "flex items-start", style: { gap: `${gapPx}px` }, children: columns2.map((column, columnIndex) => /* @__PURE__ */ u("div", { class: "min-w-0 flex-1", children: column.items.map((item) => /* @__PURE__ */ u(
        "article",
        {
          class: `overflow-hidden rounded-[20px] border border-base-300 bg-gradient-to-b from-base-100 to-base-200/80 shadow-md ${density === "compact" ? "mb-3" : "mb-4"}`,
          children: [
            /* @__PURE__ */ u(
              "button",
              {
                class: "group relative block w-full bg-base-300 text-left",
                onClick: () => onOpenMedia(item.originalUrl),
                children: [
                  /* @__PURE__ */ u(
                    "div",
                    {
                      class: "w-full overflow-hidden",
                      style: { aspectRatio: `${item.aspectRatio}` },
                      children: /* @__PURE__ */ u(
                        "img",
                        {
                          class: "h-full w-full object-cover transition duration-300 group-hover:scale-[1.02]",
                          src: item.previewUrl,
                          loading: "lazy",
                          decoding: "async"
                        }
                      )
                    }
                  ),
                  /* @__PURE__ */ u("div", { class: "pointer-events-none absolute inset-x-0 bottom-0 flex items-end justify-between bg-gradient-to-t from-black/70 via-black/20 to-transparent px-3 pb-3 pt-8 text-white", children: [
                    /* @__PURE__ */ u("div", { class: "flex items-center gap-2 text-[11px] font-semibold uppercase tracking-[0.08em]", children: [
                      item.media.type === "photo" ? /* @__PURE__ */ u(IconPhoto, { size: 14 }) : /* @__PURE__ */ u(IconPlayerPlayFilled, { size: 14 }),
                      /* @__PURE__ */ u("span", { children: item.media.type === "photo" ? "Photo" : "Video" })
                    ] }),
                    item.durationLabel ? /* @__PURE__ */ u("div", { class: "rounded-full bg-black/40 px-2 py-1 text-[10px] font-semibold", children: item.durationLabel }) : null
                  ] })
                ]
              }
            ),
            /* @__PURE__ */ u("div", { class: `space-y-2 px-3 ${density === "compact" ? "py-2.5" : "py-3"}`, children: [
              /* @__PURE__ */ u("div", { class: "flex items-start justify-between gap-2", children: [
                /* @__PURE__ */ u("div", { class: "min-w-0", children: [
                  /* @__PURE__ */ u("div", { class: "truncate text-sm font-semibold", children: [
                    "@",
                    item.screenName
                  ] }),
                  /* @__PURE__ */ u("div", { class: "text-[11px] opacity-60", children: item.createdAtLabel })
                ] }),
                /* @__PURE__ */ u(
                  "a",
                  {
                    class: "btn btn-ghost btn-xs",
                    href: item.tweetUrl,
                    target: "_blank",
                    rel: "noreferrer",
                    title: "Open tweet",
                    children: /* @__PURE__ */ u(IconExternalLink, { size: 14 })
                  }
                )
              ] }),
              item.bookmarkFolderName ? /* @__PURE__ */ u("div", { class: "badge badge-outline badge-sm", children: item.bookmarkFolderName }) : null,
              item.fullText ? /* @__PURE__ */ u(
                "p",
                {
                  class: `text-xs leading-5 opacity-80 ${density === "compact" ? "line-clamp-3" : "line-clamp-4"}`,
                  children: item.fullText
                }
              ) : null,
              /* @__PURE__ */ u("div", { class: "flex flex-wrap items-center gap-2 pt-1 text-[10px] font-semibold uppercase tracking-[0.08em] opacity-65", children: [
                /* @__PURE__ */ u("span", { class: "inline-flex items-center gap-1", children: [
                  /* @__PURE__ */ u(IconHeart, { size: 12 }),
                  item.favoriteCount
                ] }),
                /* @__PURE__ */ u("span", { class: "inline-flex items-center gap-1", children: [
                  /* @__PURE__ */ u(IconRepeat, { size: 12 }),
                  item.retweetCount
                ] }),
                /* @__PURE__ */ u("span", { class: "inline-flex items-center gap-1", children: [
                  /* @__PURE__ */ u(IconBookmark, { size: 12 }),
                  item.bookmarkCount
                ] }),
                /* @__PURE__ */ u("span", { children: [
                  "Replies ",
                  item.replyCount
                ] })
              ] })
            ] })
          ]
        },
        item.id
      )) }, `column-${columnIndex}`)) })
    ] });
  }
  const INITIAL_PAGE_SIZE = 160;
  const NEXT_PAGE_SIZE = 320;
  const WARM_PREFETCH_TARGET = 960;
  function isZipFile(file) {
    return file.name.toLowerCase().endsWith(".zip") || file.type === "application/zip";
  }
  function getErrorMessage(error) {
    if (error && typeof error === "object") {
      const message = error.message;
      if (typeof message === "string" && message.trim()) {
        return message;
      }
    }
    return String(error);
  }
  function defaultBundleKind(bundle) {
    var _a2, _b2, _c, _d;
    if (!bundle) return "tweet";
    if ((_b2 = (_a2 = bundle.manifest) == null ? void 0 : _a2.counts) == null ? void 0 : _b2.tweets) return "tweet";
    if ((_d = (_c = bundle.manifest) == null ? void 0 : _c.counts) == null ? void 0 : _d.users) return "user";
    return "tweet";
  }
  function useImportedBundles(show) {
    const mutationVersion = useDatabaseMutationVersion();
    const [bundles, setBundles] = hooks.useState([]);
    const refresh = hooks.useCallback(async () => {
      setBundles(await dbProxy.bundleList() ?? []);
    }, []);
    hooks.useEffect(() => {
      if (!show) return;
      void refresh();
    }, [mutationVersion, refresh, show]);
    return { bundles, refresh };
  }
  function useBundleSearchDocuments(bundleId, kind) {
    const mutationVersion = useDatabaseMutationVersion();
    const [documents, setDocuments] = hooks.useState([]);
    hooks.useEffect(() => {
      let cancelled = false;
      if (!bundleId) {
        setDocuments([]);
        return;
      }
      void dbProxy.searchDocumentsForSource(`bundle:${bundleId}`, kind).then((rows) => {
        if (!cancelled) {
          setDocuments(rows ?? []);
        }
      }).catch(() => {
        if (!cancelled) setDocuments([]);
      });
      return () => {
        cancelled = true;
      };
    }, [bundleId, kind, mutationVersion]);
    return documents;
  }
  function useBundleRecords(bundleId, kind) {
    const mutationVersion = useDatabaseMutationVersion();
    const [state, setState] = hooks.useState({
      records: [],
      loading: false,
      loadingMore: false,
      loadedCount: 0,
      totalCount: 0,
      hasMore: false
    });
    const offsetRef = hooks.useRef(0);
    const recordsRef = hooks.useRef([]);
    const loadingMoreRef = hooks.useRef(false);
    const project = hooks.useCallback(
      (snapshots) => projectImportedSnapshots(snapshots, kind),
      [kind]
    );
    const hydrateRecordsBySnapshotIds = hooks.useCallback(
      async (ids) => {
        const snapshots = (await dbProxy.bundleGetSnapshotsByIds(ids) ?? []).filter(
          (snapshot) => snapshot.kind === kind
        );
        return project(snapshots);
      },
      [kind, project]
    );
    const loadPage = hooks.useCallback(
      async (offset, limit) => {
        const snapshots = await dbProxy.bundleGetSnapshotPage(bundleId, {
          kind,
          offset,
          limit,
          order: "newest"
        }) ?? [];
        return project(snapshots);
      },
      [bundleId, kind, project]
    );
    const loadMore = hooks.useCallback(async () => {
      if (!bundleId || loadingMoreRef.current) return;
      if (state.totalCount > 0 && offsetRef.current >= state.totalCount) return;
      loadingMoreRef.current = true;
      setState((current) => ({ ...current, loadingMore: true }));
      try {
        const nextRecords = await loadPage(offsetRef.current, NEXT_PAGE_SIZE);
        const merged = [...recordsRef.current, ...nextRecords];
        offsetRef.current += nextRecords.length;
        recordsRef.current = merged;
        setState((current) => ({
          records: merged,
          loading: false,
          loadingMore: false,
          loadedCount: merged.length,
          totalCount: Math.max(current.totalCount, merged.length),
          hasMore: nextRecords.length >= NEXT_PAGE_SIZE
        }));
      } finally {
        loadingMoreRef.current = false;
        setState((current) => ({ ...current, loadingMore: false }));
      }
    }, [bundleId, loadPage, state.totalCount]);
    const loadAll = hooks.useCallback(async () => {
      while (state.hasMore && offsetRef.current < state.totalCount) {
        const before = offsetRef.current;
        await loadMore();
        if (offsetRef.current === before) break;
      }
    }, [loadMore, state.hasMore, state.totalCount]);
    hooks.useEffect(() => {
      let cancelled = false;
      offsetRef.current = 0;
      recordsRef.current = [];
      if (!bundleId) {
        setState({
          records: [],
          loading: false,
          loadingMore: false,
          loadedCount: 0,
          totalCount: 0,
          hasMore: false
        });
        return;
      }
      const load = async () => {
        setState({
          records: [],
          loading: true,
          loadingMore: false,
          loadedCount: 0,
          totalCount: 0,
          hasMore: false
        });
        const [totalCount, firstRecords] = await Promise.all([
          dbProxy.bundleGetSnapshotCount(bundleId, kind),
          loadPage(0, INITIAL_PAGE_SIZE)
        ]);
        if (cancelled) return;
        offsetRef.current = firstRecords.length;
        recordsRef.current = firstRecords;
        setState({
          records: firstRecords,
          loading: false,
          loadingMore: false,
          loadedCount: firstRecords.length,
          totalCount: totalCount ?? firstRecords.length,
          hasMore: firstRecords.length < (totalCount ?? firstRecords.length)
        });
      };
      void load();
      return () => {
        cancelled = true;
      };
    }, [bundleId, kind, loadPage, mutationVersion]);
    hooks.useEffect(() => {
      if (state.loading || state.loadingMore || !state.hasMore) return;
      if (state.loadedCount >= WARM_PREFETCH_TARGET) return;
      const handle = globalThis.setTimeout(() => {
        void loadMore();
      }, 120);
      return () => globalThis.clearTimeout(handle);
    }, [loadMore, state.hasMore, state.loadedCount, state.loading, state.loadingMore]);
    return {
      ...state,
      loadMore,
      loadAll,
      hydrateRecordsBySnapshotIds
    };
  }
  function BundleViewerPanel() {
    const { t } = useTranslation();
    const [showModal, toggleShowModal] = useToggle(false);
    const [isViewerFullscreen, setIsViewerFullscreen] = hooks.useState(false);
    const [selectedBundleId, setSelectedBundleId] = hooks.useState("");
    const [kind, setKind] = hooks.useState("tweet");
    const [importStatus, setImportStatus] = hooks.useState("Idle");
    const [importing, setImporting] = hooks.useState(false);
    const { bundles, refresh } = useImportedBundles(showModal);
    const selectedBundle = bundles.find((bundle) => bundle.id === selectedBundleId) ?? null;
    const searchDocuments = useBundleSearchDocuments(selectedBundleId, kind);
    const recordsState = useBundleRecords(selectedBundleId, kind);
    const bookmarkFolderOptions = hooks.useMemo(() => {
      const counter = /* @__PURE__ */ new Map();
      for (const document2 of searchDocuments) {
        const folderId = String(document2.folder_id || "").trim();
        if (!folderId) continue;
        const folderName = String(document2.folder_name || "").trim();
        const current = counter.get(folderId);
        if (current) {
          current.count += 1;
        } else {
          counter.set(folderId, {
            label: folderName || `Folder ${folderId}`,
            count: 1
          });
        }
      }
      return [...counter.entries()].sort((left, right) => {
        if (right[1].count !== left[1].count) return right[1].count - left[1].count;
        return left[1].label.localeCompare(right[1].label);
      }).map(([value, meta]) => ({
        value,
        label: `${meta.label} (${meta.count})`
      }));
    }, [searchDocuments]);
    hooks.useEffect(() => {
      if (!showModal || selectedBundleId || !bundles.length) return;
      const firstReady = bundles.find((bundle) => bundle.status === "ready") ?? bundles[0];
      if (firstReady) {
        setSelectedBundleId(firstReady.id);
        setKind(defaultBundleKind(firstReady));
      }
    }, [bundles, selectedBundleId, showModal]);
    const importFile = async (file) => {
      if (!file || importing) return;
      const fileName = file.name || "selected bundle";
      setImporting(true);
      setImportStatus(`Importing ${fileName}...`);
      let importedBundleId = "";
      try {
        const result = isZipFile(file) ? await importBundleZip(dbProxy, file) : await importLegacyBundleFile(dbProxy, file);
        importedBundleId = result.bundleId;
        setImportStatus(
          `Imported ${result.recordsImported}/${result.recordsSeen} records from ${fileName}`
        );
      } catch (error) {
        setImportStatus(`Import failed: ${getErrorMessage(error)}`);
        setImporting(false);
        return;
      }
      try {
        await refresh();
        setSelectedBundleId(importedBundleId);
      } catch (error) {
        setImportStatus(`Imported ${fileName}, but refresh failed: ${getErrorMessage(error)}`);
      } finally {
        setImporting(false);
      }
    };
    const title = selectedBundle ? `${t("Bundle Viewer")}: ${selectedBundle.title}` : t("Bundle Viewer");
    const hasBundles = bundles.length > 0;
    return /* @__PURE__ */ u(
      ExtensionPanel,
      {
        title: t("Bundle Viewer"),
        description: t("{{count}} imported bundles", { count: bundles.length }),
        active: hasBundles,
        onClick: toggleShowModal,
        indicatorColor: "bg-accent",
        panelClass: "my-1 rounded-box-half border border-accent/40 bg-accent/10 px-2 shadow-sm",
        buttonClass: "btn-accent",
        children: /* @__PURE__ */ u(
          Modal,
          {
            class: isViewerFullscreen ? "h-screen max-h-screen max-w-none" : "max-w-4xl md:max-w-screen-md sm:max-w-screen-sm h-[82vh] max-h-[calc(100vh-4rem)]",
            title: t("Bundle Viewer"),
            show: showModal,
            fullscreen: isViewerFullscreen,
            onClose: () => {
              setIsViewerFullscreen(false);
              toggleShowModal();
            },
            children: /* @__PURE__ */ u("div", { class: "flex min-h-0 grow flex-col gap-2", children: [
              /* @__PURE__ */ u("section", { class: "rounded-box-half border border-base-300 bg-base-200 px-2 py-1.5", children: /* @__PURE__ */ u("div", { class: "flex flex-wrap items-center gap-2", children: [
                /* @__PURE__ */ u("label", { class: "btn btn-sm btn-outline", children: [
                  /* @__PURE__ */ u(IconDatabaseImport, { size: 16 }),
                  "Import Bundle",
                  /* @__PURE__ */ u(
                    "input",
                    {
                      type: "file",
                      accept: ".zip,.json,.jsonl,application/zip,application/json,application/x-ndjson",
                      class: "hidden",
                      disabled: importing,
                      onChange: (event) => {
                        var _a2;
                        const input = event.target;
                        void importFile((_a2 = input.files) == null ? void 0 : _a2[0]);
                        input.value = "";
                      }
                    }
                  )
                ] }),
                /* @__PURE__ */ u(
                  "select",
                  {
                    class: "select select-bordered select-sm min-w-56",
                    value: selectedBundleId,
                    onChange: (event) => {
                      const nextId = event.target.value;
                      const nextBundle = bundles.find((bundle) => bundle.id === nextId) ?? null;
                      setSelectedBundleId(nextId);
                      setKind(defaultBundleKind(nextBundle));
                    },
                    children: [
                      /* @__PURE__ */ u("option", { value: "", children: "Select imported bundle" }),
                      bundles.map((bundle) => /* @__PURE__ */ u("option", { value: bundle.id, children: [
                        bundle.title,
                        " (",
                        bundle.recordCount,
                        ")"
                      ] }, bundle.id))
                    ]
                  }
                ),
                /* @__PURE__ */ u(
                  "select",
                  {
                    class: "select select-bordered select-sm",
                    value: kind,
                    onChange: (event) => setKind(event.target.value),
                    children: [
                      /* @__PURE__ */ u("option", { value: "tweet", children: "Tweets" }),
                      /* @__PURE__ */ u("option", { value: "user", children: "Users" })
                    ]
                  }
                ),
                /* @__PURE__ */ u("button", { class: "btn btn-sm btn-ghost", onClick: () => void refresh(), children: [
                  /* @__PURE__ */ u(IconRefresh, { size: 16 }),
                  "Refresh"
                ] }),
                selectedBundle ? /* @__PURE__ */ u(
                  "button",
                  {
                    class: "btn btn-sm btn-error btn-outline",
                    onClick: async () => {
                      if (!confirm(`Delete imported bundle "${selectedBundle.title}"?`)) return;
                      await dbProxy.bundleDelete(selectedBundle.id);
                      setSelectedBundleId("");
                      await refresh();
                    },
                    children: [
                      /* @__PURE__ */ u(IconTrash, { size: 16 }),
                      "Delete"
                    ]
                  }
                ) : null,
                /* @__PURE__ */ u("span", { class: "font-mono text-[10px] opacity-70", children: [
                  importing ? "busy: " : "",
                  importStatus
                ] })
              ] }) }),
              selectedBundleId ? kind === "tweet" ? /* @__PURE__ */ u(
                BaseTableView,
                {
                  title,
                  viewStateKey: `bundle:${selectedBundleId}:tweet`,
                  fullscreen: isViewerFullscreen,
                  onFullscreenChange: setIsViewerFullscreen,
                  loading: recordsState.loading,
                  loadingMore: recordsState.loadingMore,
                  loadedCount: recordsState.loadedCount,
                  totalCount: recordsState.totalCount,
                  hasMore: recordsState.hasMore,
                  loadMore: recordsState.loadMore,
                  loadAll: recordsState.loadAll,
                  hydrateRecordsByIds: recordsState.hydrateRecordsBySnapshotIds,
                  records: recordsState.records,
                  searchDocuments,
                  columns: columns$2,
                  clear: () => void 0,
                  showClearButton: false,
                  alternateViews: [
                    {
                      id: "media-masonry",
                      label: "Media masonry",
                      icon: "grid",
                      component: TweetMediaMasonry
                    }
                  ],
                  bookmarkFolderOptions,
                  renderActions: () => /* @__PURE__ */ u("span", { class: "badge badge-outline badge-sm font-mono", children: [
                    "imported bundle: ",
                    (selectedBundle == null ? void 0 : selectedBundle.recordCount) ?? 0,
                    " records"
                  ] })
                }
              ) : /* @__PURE__ */ u(
                BaseTableView,
                {
                  title,
                  viewStateKey: `bundle:${selectedBundleId}:user`,
                  fullscreen: isViewerFullscreen,
                  onFullscreenChange: setIsViewerFullscreen,
                  loading: recordsState.loading,
                  loadingMore: recordsState.loadingMore,
                  loadedCount: recordsState.loadedCount,
                  totalCount: recordsState.totalCount,
                  hasMore: recordsState.hasMore,
                  loadMore: recordsState.loadMore,
                  loadAll: recordsState.loadAll,
                  hydrateRecordsByIds: recordsState.hydrateRecordsBySnapshotIds,
                  records: recordsState.records,
                  searchDocuments,
                  columns: columns$1,
                  clear: () => void 0,
                  showClearButton: false,
                  renderActions: () => /* @__PURE__ */ u("span", { class: "badge badge-outline badge-sm font-mono", children: [
                    "imported bundle: ",
                    (selectedBundle == null ? void 0 : selectedBundle.recordCount) ?? 0,
                    " records"
                  ] })
                }
              ) : /* @__PURE__ */ u("div", { class: "flex grow items-center justify-center rounded-box-half border border-base-300 bg-base-200 text-sm opacity-70", children: "Import or select a bundle to open it in the explorer." })
            ] })
          }
        )
      }
    );
  }
  const MODULE_RANKS = {
    BookmarksModule: 10,
    TweetIndexModule: 20,
    UserDetailModule: 30,
    UserTweetsModule: 40,
    UserMediaModule: 50,
    LikesModule: 60,
    QuotesModule: 70,
    TweetDetailModule: 80,
    SearchTimelineModule: 90,
    FollowersModule: 120,
    FollowingModule: 130,
    RetweetersModule: 140,
    HomeTimelineModule: 220,
    ListTimelineModule: 230,
    CommunityTimelineModule: 240,
    ListMembersModule: 320,
    ListSubscribersModule: 330,
    CommunityMembersModule: 340,
    DirectMessagesModule: 420,
    LocalSearchModule: 9e3,
    InteractionEventsModule: 9010,
    RuntimeLogsModule: 9990
  };
  const MODULE_TITLE_KEYS = {
    BookmarksModule: "Bookmarks",
    TweetIndexModule: "Tweets",
    UserDetailModule: "Users",
    UserTweetsModule: "User Tweets",
    UserMediaModule: "User Media",
    TweetDetailModule: "Tweet Details",
    SearchTimelineModule: "Search Timeline",
    HomeTimelineModule: "Home Timeline",
    ListTimelineModule: "List Timeline",
    CommunityTimelineModule: "Community Timeline",
    CommunityMembersModule: "Community Members",
    ListMembersModule: "List Members",
    ListSubscribersModule: "List Subscribers",
    DirectMessagesModule: "Direct Messages",
    InteractionEventsModule: "Interaction Events",
    LocalSearchModule: "Local Search",
    RuntimeLogsModule: "Runtime Logs"
  };
  const MODULE_TONES = {
    BookmarksModule: {
      indicatorColor: "bg-warning",
      panelClass: "border-l-2 border-warning/60 pl-2"
    },
    TweetIndexModule: {
      indicatorColor: "bg-info",
      panelClass: "border-l-2 border-info/50 pl-2"
    },
    UserDetailModule: {
      indicatorColor: "bg-success",
      panelClass: "border-l-2 border-success/50 pl-2"
    },
    UserTweetsModule: { indicatorColor: "bg-info" },
    UserMediaModule: { indicatorColor: "bg-info" },
    TweetDetailModule: { indicatorColor: "bg-info" },
    SearchTimelineModule: { indicatorColor: "bg-info" },
    LikesModule: { indicatorColor: "bg-secondary" },
    QuotesModule: { indicatorColor: "bg-secondary" },
    RetweetersModule: { indicatorColor: "bg-secondary" },
    FollowersModule: { indicatorColor: "bg-success" },
    FollowingModule: { indicatorColor: "bg-success" },
    HomeTimelineModule: { indicatorColor: "bg-primary" },
    ListTimelineModule: { indicatorColor: "bg-primary" },
    CommunityTimelineModule: { indicatorColor: "bg-primary" },
    ListMembersModule: { indicatorColor: "bg-success" },
    ListSubscribersModule: { indicatorColor: "bg-success" },
    CommunityMembersModule: { indicatorColor: "bg-success" },
    DirectMessagesModule: { indicatorColor: "bg-accent" },
    LocalSearchModule: {
      indicatorColor: "bg-neutral",
      panelClass: "opacity-90"
    },
    InteractionEventsModule: {
      indicatorColor: "bg-neutral",
      panelClass: "opacity-90"
    },
    RuntimeLogsModule: {
      indicatorColor: "bg-neutral",
      panelClass: "opacity-90"
    }
  };
  function fallbackTone(type2) {
    if (type2 === ExtensionType.TWEET) return { indicatorColor: "bg-info" };
    if (type2 === ExtensionType.USER) return { indicatorColor: "bg-success" };
    if (type2 === ExtensionType.CUSTOM) return { indicatorColor: "bg-accent" };
    return { indicatorColor: "bg-neutral" };
  }
  function getWidgetPresentation(extension) {
    const tone = MODULE_TONES[extension.name] ?? fallbackTone(extension.type);
    return {
      rank: MODULE_RANKS[extension.name] ?? 5e3,
      titleKey: MODULE_TITLE_KEYS[extension.name],
      ...tone
    };
  }
  function compareWidgetExtensions(left, right) {
    const leftRank = getWidgetPresentation(left).rank;
    const rightRank = getWidgetPresentation(right).rank;
    if (leftRank !== rightRank) return leftRank - rightRank;
    return left.name.localeCompare(right.name);
  }
  function isBottomUtilityWidget(extension) {
    return getWidgetPresentation(extension).rank >= 9e3;
  }
  function App() {
    const { t } = useTranslation();
    const {
      extensions,
      currentTheme,
      showControlPanel,
      hookStats,
      runtimeModes,
      rawCaptureStats,
      toggleControlPanel
    } = useWorkspaceShellState(t("Open Control Panel"));
    const hookLine = (() => {
      const hs = hookStats.value;
      if (!hs) return "Hooks: unknown";
      const ageSec = hs.lastAt ? Math.max(0, Math.floor((Date.now() - hs.lastAt) / 1e3)) : null;
      let short = hs.lastUrl || "";
      try {
        const u2 = new URL(short);
        short = `${u2.hostname}${u2.pathname}`;
      } catch {
      }
      if (short.length > 48) short = short.slice(0, 45) + "...";
      const age = ageSec === null ? "" : ` (${ageSec}s ago)`;
      return `Hooks: xhr ${hs.xhrMessages}, fetch ${hs.fetchMessages}` + (hs.lastUrl ? `, last ${short}${age}` : "");
    })();
    const healthLine = (() => {
      const modes = runtimeModes.value;
      const raw = rawCaptureStats.value;
      const safeMode = (modes == null ? void 0 : modes.safeMode) ? "on" : "off";
      const hookMode = (modes == null ? void 0 : modes.hookMode) || "unknown";
      const repairMode = (modes == null ? void 0 : modes.repairMode) || "unknown";
      const rawTotal = Number((raw == null ? void 0 : raw.total) || 0);
      const spool = Number((raw == null ? void 0 : raw.spool_count) || 0);
      const daemon = (raw == null ? void 0 : raw.daemon_online) ? "on" : "off";
      const monitorRole = (raw == null ? void 0 : raw.monitor_role) || "unknown";
      const rawAgeSec = (raw == null ? void 0 : raw.last_at) ? Math.max(0, Math.floor((Date.now() - raw.last_at) / 1e3)) : null;
      const age = rawAgeSec === null ? "" : `, raw ${rawAgeSec}s ago`;
      return `Mode: safe ${safeMode}, hook ${hookMode}, repair ${repairMode} | raw ${rawTotal}, spool ${spool}, daemon ${daemon}, monitor ${monitorRole}${age}`;
    })();
    const sortedExtensions = extensions.value.slice().sort(compareWidgetExtensions);
    const primaryExtensions = sortedExtensions.filter((ext) => !isBottomUtilityWidget(ext));
    const bottomExtensions = sortedExtensions.filter(isBottomUtilityWidget);
    const renderExtension = (ext) => {
      const Component2 = ext.render();
      if (ext.enabled && Component2) {
        return /* @__PURE__ */ u(ErrorBoundary, { children: /* @__PURE__ */ u(Component2, { extension: ext }) }, ext.name);
      }
      return null;
    };
    return /* @__PURE__ */ u(preact.Fragment, { children: [
      /* @__PURE__ */ u(
        ControlPanelLauncher,
        {
          currentTheme: currentTheme.value || "system",
          onToggle: toggleControlPanel
        }
      ),
      /* @__PURE__ */ u(
        ControlPanelShell,
        {
          currentTheme: currentTheme.value || "system",
          show: !!showControlPanel.value,
          title: "Scrollmark",
          byline: "By Kyle McCleary",
          description: t("Browse around to capture more data."),
          hookLine,
          healthLine,
          onToggle: toggleControlPanel,
          children: /* @__PURE__ */ u(ErrorBoundary, { children: [
            primaryExtensions.map(renderExtension),
            /* @__PURE__ */ u(ErrorBoundary, { children: /* @__PURE__ */ u(BundleViewerPanel, {}) }),
            bottomExtensions.length ? /* @__PURE__ */ u("div", { class: "divider mb-0 mt-1 opacity-60" }) : null,
            bottomExtensions.map(renderExtension)
          ] })
        }
      )
    ] });
  }
  const patterns = {
    id: {
      description: "The tweet ID",
      extractor: (tweet) => tweet.rest_id
    },
    screen_name: {
      description: "The username of tweet author",
      extractor: (tweet) => tweet.core.user_results.result.core.screen_name
    },
    name: {
      description: "The profile name of tweet author",
      extractor: (tweet) => tweet.core.user_results.result.core.name
    },
    index: {
      description: "The media index in tweet (start from 0)",
      extractor: (tweet, media) => String(getMediaIndex(tweet, media))
    },
    num: {
      description: "The order of media in tweet (1/2/3/4)",
      extractor: (tweet, media) => String(getMediaIndex(tweet, media) + 1)
    },
    date: {
      description: "The post date in YYYYMMDD format",
      extractor: (tweet) => formatDateTime(extractTweetCreatedAtMs(tweet), "YYYYMMDD")
    },
    time: {
      description: "The post time in HHmmss format",
      extractor: (tweet) => formatDateTime(extractTweetCreatedAtMs(tweet), "HHmmss")
    },
    type: {
      description: "The media type (photo/video/animated_gif)",
      extractor: (tweet, media) => media.type
    },
    ext: {
      description: "The file extension of media (jpg/png/mp4)",
      extractor: (tweet, media) => getFileExtensionFromUrl(getMediaOriginalUrl(media))
    }
  };
  const DEFAULT_MEDIA_TYPES = ["photo", "video", "animated_gif"];
  function extractMedia$1(data, includeRetweets, filenamePattern) {
    var _a2;
    const gallery = /* @__PURE__ */ new Map();
    for (const item of data) {
      if (item.__typename === "Tweet" || typeof item.__typename === "undefined" && "core" in item) {
        if (!includeRetweets && ((_a2 = item.legacy) == null ? void 0 : _a2.retweeted_status_result)) {
          continue;
        }
        const tweetMedia = extractTweetMedia(item).map((media) => {
          let filename = filenamePattern;
          for (const [key, value] of Object.entries(patterns)) {
            filename = filename.replace(`{${key}}`, value.extractor(item, media));
          }
          return { filename, type: media.type, url: getMediaOriginalUrl(media) };
        });
        for (const media of tweetMedia) {
          gallery.set(media.filename, media);
        }
      }
      if (item.__typename === "User") {
        if (item.avatar.image_url) {
          const ext = getFileExtensionFromUrl(item.avatar.image_url);
          const filename = `${item.core.screen_name}_profile_image.${ext}`;
          gallery.set(filename, {
            filename,
            type: "photo",
            url: getProfileImageOriginalUrl(item.avatar.image_url)
          });
        }
        if (item.legacy.profile_banner_url) {
          const ext = getFileExtensionFromUrl(item.legacy.profile_banner_url);
          const filename = `${item.core.screen_name}_profile_banner.${ext}`;
          gallery.set(filename, {
            filename,
            type: "photo",
            url: item.legacy.profile_banner_url
          });
        }
      }
    }
    return Array.from(gallery.values());
  }
  function cloneSnapshotValue(value) {
    if (value === null || value === void 0 || typeof value !== "object") {
      return value;
    }
    if (typeof structuredClone === "function") {
      try {
        return structuredClone(value);
      } catch {
      }
    }
    try {
      return JSON.parse(JSON.stringify(value));
    } catch {
      return value;
    }
  }
  function ExportMediaModal({
    title,
    resultRecords,
    selectedRecords,
    resultSetSnapshot,
    selectionMode,
    isTweet,
    show,
    onClose
  }) {
    const { t } = useTranslation("exporter");
    const [loading, setLoading] = useSignalState(false);
    const [copied, setCopied] = useSignalState(false);
    const [useAria2Format, toggleUseAria2Format] = useToggle(false);
    const [rateLimitInput, setRateLimitInput] = hooks.useState("75");
    const [globalConcurrencyInput, setGlobalConcurrencyInput] = hooks.useState("10");
    const [perHostConcurrencyInput, setPerHostConcurrencyInput] = hooks.useState("8");
    const [videoConcurrencyInput, setVideoConcurrencyInput] = hooks.useState("3");
    const [maxRetriesInput, setMaxRetriesInput] = hooks.useState("3");
    const [filenamePattern, setFilenamePattern] = useSignalState(appOptionsManager.get("filenamePattern"));
    const [currentProgress, setCurrentProgress] = useSignalState(0);
    const [totalProgress, setTotalProgress] = useSignalState(0);
    const [zipProgress, setZipProgress] = useSignalState(0);
    const [exportScope, setExportScope] = useSignalState("result_set");
    const [pinnedResultRecords, setPinnedResultRecords] = hooks.useState([]);
    const [pinnedSelectedRecords, setPinnedSelectedRecords] = hooks.useState([]);
    const [pinnedResultSetSnapshot, setPinnedResultSetSnapshot] = hooks.useState(
      null
    );
    const taskStatusSignal = signals.useSignal({});
    const wasOpenRef = hooks.useRef(false);
    const lastProgressPublishRef = hooks.useRef(0);
    const [filters, setFilters] = useSignalState([
      ...DEFAULT_MEDIA_TYPES,
      ...isTweet ? ["retweet"] : []
    ]);
    const includeRetweets = filters.includes("retweet");
    const activeRecords = hooks.useMemo(
      () => exportScope === "selected" ? pinnedSelectedRecords : pinnedResultRecords,
      [exportScope, pinnedResultRecords, pinnedSelectedRecords]
    );
    const mediaList = hooks.useMemo(
      () => extractMedia$1(
        activeRecords,
        includeRetweets,
        filenamePattern ?? ""
      ).filter((media) => filters.includes(media.type)),
      [activeRecords, filters, filenamePattern, includeRetweets]
    );
    const previewMediaList = hooks.useMemo(() => mediaList.slice(0, 250), [mediaList]);
    const previewFilenameSet = hooks.useMemo(
      () => new Set(previewMediaList.map((media) => media.filename)),
      [previewMediaList]
    );
    hooks.useEffect(() => {
      if (!show) {
        wasOpenRef.current = false;
        return;
      }
      if (wasOpenRef.current) {
        return;
      }
      wasOpenRef.current = true;
      setPinnedResultRecords(resultRecords.map((row) => cloneSnapshotValue(row)));
      setPinnedSelectedRecords(selectedRecords.map((row) => cloneSnapshotValue(row)));
      setPinnedResultSetSnapshot({
        ...resultSetSnapshot,
        ids: [...resultSetSnapshot.ids],
        warnings: [...resultSetSnapshot.warnings]
      });
      setExportScope(
        selectedRecords.length > 0 && selectionMode === "explicit" ? "selected" : "result_set"
      );
      setCurrentProgress(0);
      setTotalProgress(0);
      setZipProgress(0);
      taskStatusSignal.value = {};
    }, [
      resultRecords,
      resultSetSnapshot,
      selectedRecords,
      selectionMode,
      setCurrentProgress,
      setExportScope,
      setTotalProgress,
      setZipProgress,
      show,
      taskStatusSignal
    ]);
    const onProgress = (current, total, value) => {
      const now = Date.now();
      if (current === total || now - lastProgressPublishRef.current > 120) {
        lastProgressPublishRef.current = now;
        setCurrentProgress(current);
        setTotalProgress(total);
      }
      if ((value == null ? void 0 : value.filename) && previewFilenameSet.has(value.filename)) {
        const updated = { ...taskStatusSignal.value, [value.filename]: 100 };
        taskStatusSignal.value = updated;
      }
    };
    const onExport = async () => {
      try {
        const schedulerOptions = {
          minDelayBetweenStartsMs: Math.max(0, parseInt(rateLimitInput, 10) || 0),
          globalConcurrency: Math.max(1, parseInt(globalConcurrencyInput, 10) || 1),
          perHostConcurrency: Math.max(1, parseInt(perHostConcurrencyInput, 10) || 1),
          videoConcurrency: Math.max(1, parseInt(videoConcurrencyInput, 10) || 1),
          maxRetries: Math.max(0, parseInt(maxRetriesInput, 10) || 0),
          onZipProgress: (current) => setZipProgress(current)
        };
        setLoading(true);
        lastProgressPublishRef.current = 0;
        setCurrentProgress(0);
        setTotalProgress(mediaList.length);
        setZipProgress(0);
        await zipStreamDownload(
          `twitter-${title}-${exportScope === "selected" ? "selected" : "results"}-${Date.now()}-media.zip`,
          mediaList,
          onProgress,
          schedulerOptions
        );
        setLoading(false);
      } catch (err2) {
        setLoading(false);
        logger.error(t("Failed to export media. Open DevTools for more details."), err2);
      }
    };
    const onCopy = (saveAsFile = false) => {
      const text = mediaList.map((media) => useAria2Format ? `${media.url}
  out=${media.filename}` : media.url).join("\n");
      try {
        if (saveAsFile) {
          fileSaverEs.saveAs(new Blob([text], { type: "text/plain;charset=utf-8" }), "media-urls.txt");
          return;
        }
        navigator.clipboard.writeText(text);
        setCopied(true);
        setTimeout(() => setCopied(false), 2e3);
      } catch (err2) {
        logger.error(t("Failed to copy media URLs. Open DevTools for more details."), err2);
      }
    };
    return /* @__PURE__ */ u(
      Modal,
      {
        class: "max-w-sm md:max-w-screen-sm sm:max-w-screen-sm max-h-full",
        title: `${title} ${t("Media")}`,
        show,
        onClose,
        children: [
          /* @__PURE__ */ u("div", { class: "px-4 text-base overflow-y-scroll overscroll-none", children: [
            /* @__PURE__ */ u("p", { class: "text-base-content text-opacity-60 leading-5 text-sm", children: t(
              "Download and save media files from captured data. This may take a while depending on the amount of data. Media that will be downloaded includes: profile images, profile banners (for users), images, videos (for tweets)."
            ) }),
            /* @__PURE__ */ u("div", { role: "alert", class: "alert text-sm py-2 mt-2 mb-2 grid-cols-[auto_minmax(auto,1fr)]", children: [
              /* @__PURE__ */ u(IconInfoCircle, { size: 24 }),
              /* @__PURE__ */ u("span", { children: t(
                "Browser ZIP export now uses bounded parallel downloads. For very large video-heavy jobs, URL or aria2 export is still the safest low-memory path."
              ) })
            ] }),
            /* @__PURE__ */ u("div", { class: "flex items-center gap-4 mb-1", children: [
              /* @__PURE__ */ u("p", { class: "leading-8", children: t("Export scope:") }),
              /* @__PURE__ */ u("label", { class: "label cursor-pointer gap-2 py-0", children: [
                /* @__PURE__ */ u(
                  "input",
                  {
                    type: "radio",
                    name: "media-export-scope",
                    class: "radio radio-sm",
                    checked: exportScope === "result_set",
                    onChange: () => setExportScope("result_set")
                  }
                ),
                /* @__PURE__ */ u("span", { children: t("All current results") }),
                /* @__PURE__ */ u("span", { class: "font-mono opacity-60", children: [
                  "(",
                  pinnedResultRecords.length,
                  ")"
                ] })
              ] }),
              /* @__PURE__ */ u(
                "label",
                {
                  class: cx(
                    "label cursor-pointer gap-2 py-0",
                    !pinnedSelectedRecords.length && "opacity-50"
                  ),
                  children: [
                    /* @__PURE__ */ u(
                      "input",
                      {
                        type: "radio",
                        name: "media-export-scope",
                        class: "radio radio-sm",
                        checked: exportScope === "selected",
                        disabled: !pinnedSelectedRecords.length,
                        onChange: () => setExportScope("selected")
                      }
                    ),
                    /* @__PURE__ */ u("span", { children: t("Selected rows") }),
                    /* @__PURE__ */ u("span", { class: "font-mono opacity-60", children: [
                      "(",
                      pinnedSelectedRecords.length,
                      ")"
                    ] })
                  ]
                }
              )
            ] }),
            pinnedResultSetSnapshot ? /* @__PURE__ */ u("div", { class: "rounded-box-half border border-base-300 bg-base-200/60 px-3 py-2 text-xs leading-5 mb-2", children: [
              /* @__PURE__ */ u("div", { class: "font-semibold", children: t("Pinned result set") }),
              /* @__PURE__ */ u("div", { class: "font-mono opacity-70", children: pinnedResultSetSnapshot.resultSetId }),
              /* @__PURE__ */ u("div", { children: [
                t("Query"),
                ":",
                " ",
                /* @__PURE__ */ u("span", { class: "font-mono", children: pinnedResultSetSnapshot.queryText || "-" })
              ] }),
              /* @__PURE__ */ u("div", { children: [
                t("Sort"),
                ": ",
                /* @__PURE__ */ u("span", { class: "font-mono", children: pinnedResultSetSnapshot.sort })
              ] })
            ] }) : null,
            isTweet && /* @__PURE__ */ u("div", { class: "flex flex-wrap sm:grid grid-cols-4 sm:gap-2 items-center sm:h-9", children: [
              /* @__PURE__ */ u("p", { class: "leading-8", children: t("Filename template:") }),
              /* @__PURE__ */ u(
                "div",
                {
                  class: "tooltip tooltip-bottom col-span-3 before:whitespace-pre-line before:max-w-max",
                  "data-tip": Object.entries(patterns).map(([key, value]) => `{${key}} - ${t(value.description)}`).reduce((acc, cur) => acc + cur + "\n", ""),
                  children: /* @__PURE__ */ u(
                    "input",
                    {
                      type: "text",
                      class: "input input-bordered input-sm w-full",
                      value: filenamePattern,
                      onChange: (e) => {
                        var _a2;
                        const value = (_a2 = e == null ? void 0 : e.target) == null ? void 0 : _a2.value;
                        setFilenamePattern(value);
                        appOptionsManager.set("filenamePattern", value);
                      }
                    }
                  )
                }
              )
            ] }),
            /* @__PURE__ */ u("div", { class: "rounded-box-half border border-base-300 bg-base-200/60 px-3 py-2 mt-2 mb-2", children: [
              /* @__PURE__ */ u("div", { class: "mb-1 flex items-center justify-between gap-2", children: [
                /* @__PURE__ */ u("p", { class: "font-semibold text-sm", children: t("Download scheduler") }),
                /* @__PURE__ */ u("span", { class: "font-mono text-[10px] opacity-60", children: t("Faster defaults are intended for bulk CDN transfer.") })
              ] }),
              /* @__PURE__ */ u("div", { class: "grid grid-cols-2 sm:grid-cols-5 gap-2", children: [
                /* @__PURE__ */ u("label", { class: "form-control", children: [
                  /* @__PURE__ */ u("span", { class: "label-text text-xs", children: t("Start delay (ms)") }),
                  /* @__PURE__ */ u(
                    "input",
                    {
                      type: "number",
                      min: "0",
                      class: "input input-bordered input-sm",
                      value: rateLimitInput,
                      onInput: (e) => setRateLimitInput(e.currentTarget.value)
                    }
                  )
                ] }),
                /* @__PURE__ */ u("label", { class: "form-control", children: [
                  /* @__PURE__ */ u("span", { class: "label-text text-xs", children: t("Global parallel") }),
                  /* @__PURE__ */ u(
                    "input",
                    {
                      type: "number",
                      min: "1",
                      max: "32",
                      class: "input input-bordered input-sm",
                      value: globalConcurrencyInput,
                      onInput: (e) => setGlobalConcurrencyInput(e.currentTarget.value)
                    }
                  )
                ] }),
                /* @__PURE__ */ u("label", { class: "form-control", children: [
                  /* @__PURE__ */ u("span", { class: "label-text text-xs", children: t("Per host") }),
                  /* @__PURE__ */ u(
                    "input",
                    {
                      type: "number",
                      min: "1",
                      max: "32",
                      class: "input input-bordered input-sm",
                      value: perHostConcurrencyInput,
                      onInput: (e) => setPerHostConcurrencyInput(e.currentTarget.value)
                    }
                  )
                ] }),
                /* @__PURE__ */ u("label", { class: "form-control", children: [
                  /* @__PURE__ */ u("span", { class: "label-text text-xs", children: t("Videos") }),
                  /* @__PURE__ */ u(
                    "input",
                    {
                      type: "number",
                      min: "1",
                      max: "16",
                      class: "input input-bordered input-sm",
                      value: videoConcurrencyInput,
                      onInput: (e) => setVideoConcurrencyInput(e.currentTarget.value)
                    }
                  )
                ] }),
                /* @__PURE__ */ u("label", { class: "form-control", children: [
                  /* @__PURE__ */ u("span", { class: "label-text text-xs", children: t("Retries") }),
                  /* @__PURE__ */ u(
                    "input",
                    {
                      type: "number",
                      min: "0",
                      max: "8",
                      class: "input input-bordered input-sm",
                      value: maxRetriesInput,
                      onInput: (e) => setMaxRetriesInput(e.currentTarget.value)
                    }
                  )
                ] })
              ] })
            ] }),
            /* @__PURE__ */ u("div", { class: "flex flex-wrap sm:grid grid-cols-4 sm:gap-2 items-center sm:h-9", children: [
              /* @__PURE__ */ u("p", { class: "leading-8 col-span-1 whitespace-nowrap sm:pl-2", children: t("Use aria2 format:") }),
              /* @__PURE__ */ u("div", { class: "col-span-1 flex items-center", children: [
                /* @__PURE__ */ u(
                  "input",
                  {
                    type: "checkbox",
                    class: "toggle toggle-primary",
                    checked: useAria2Format,
                    onChange: toggleUseAria2Format
                  }
                ),
                /* @__PURE__ */ u(
                  "a",
                  {
                    href: "https://aria2.github.io/manual/en/html/aria2c.html#input-file",
                    target: "_blank",
                    rel: "noopener noreferrer",
                    class: "tooltip tooltip-bottom before:max-w-40 ml-1",
                    "data-tip": t(
                      "Click for more information. Each URL will be on a new line, with its filename on the next line. This format is compatible with aria2."
                    ),
                    children: /* @__PURE__ */ u(IconHelp, { size: 20 })
                  }
                )
              ] })
            ] }),
            /* @__PURE__ */ u("div", { class: "flex flex-wrap sm:grid grid-cols-4 sm:gap-2 items-center sm:h-9", children: [
              /* @__PURE__ */ u("p", { class: "leading-8", children: t("Media Filter:") }),
              /* @__PURE__ */ u(
                MultiSelect,
                {
                  class: "col-span-3",
                  options: [
                    { label: t("filter.photo"), value: "photo" },
                    { label: t("filter.video"), value: "video" },
                    { label: t("filter.animated_gif"), value: "animated_gif" },
                    ...isTweet ? [{ label: t("filter.retweet"), value: "retweet" }] : []
                  ],
                  selected: filters,
                  onChange: setFilters
                }
              )
            ] }),
            /* @__PURE__ */ u("div", { class: "my-3 overflow-x-scroll", children: [
              /* @__PURE__ */ u("table", { class: "table table-xs table-zebra", children: [
                /* @__PURE__ */ u("thead", { children: /* @__PURE__ */ u("tr", { children: [
                  /* @__PURE__ */ u("th", {}),
                  /* @__PURE__ */ u("th", { children: "#" }),
                  /* @__PURE__ */ u("th", { children: t("File Name") }),
                  /* @__PURE__ */ u("th", { children: t("Media Type") }),
                  /* @__PURE__ */ u("th", { children: t("Download URL") })
                ] }) }),
                /* @__PURE__ */ u("tbody", { children: previewMediaList.map((media, index) => /* @__PURE__ */ u("tr", { children: [
                  /* @__PURE__ */ u("td", { children: taskStatusSignal.value[media.filename] ? /* @__PURE__ */ u(IconCircleCheck, { class: "text-success", size: 14 }) : /* @__PURE__ */ u(IconCircleDashed, { size: 14 }) }),
                  /* @__PURE__ */ u("th", { children: index + 1 }),
                  /* @__PURE__ */ u("td", { children: media.filename }),
                  /* @__PURE__ */ u("td", { children: t(`filter.${media.type}`) }),
                  /* @__PURE__ */ u("td", { children: /* @__PURE__ */ u(
                    "a",
                    {
                      class: "link whitespace-nowrap",
                      href: media.url,
                      target: "_blank",
                      rel: "noopener noreferrer",
                      children: media.url
                    }
                  ) })
                ] })) })
              ] }),
              mediaList.length > 250 ? /* @__PURE__ */ u("div", { class: "px-2 py-1 text-xs opacity-60", children: t("Preview limited to first 250 media items.") }) : null,
              mediaList.length > 0 ? null : /* @__PURE__ */ u("div", { class: "flex items-center justify-center h-28 w-full", children: /* @__PURE__ */ u("p", { class: "text-base-content text-opacity-50", children: t("No media selected.") }) })
            ] }),
            /* @__PURE__ */ u("div", { class: "flex flex-col mt-6", children: [
              /* @__PURE__ */ u(
                "progress",
                {
                  class: "progress progress-secondary w-full",
                  value: currentProgress / (totalProgress || 1) * 100,
                  max: "100"
                }
              ),
              /* @__PURE__ */ u("span", { class: "text-sm h-4 leading-none mt-2 text-base-content text-opacity-60", children: zipProgress ? `${t("Zipping")}: ${zipProgress}/${mediaList.length}` : `${currentProgress}/${mediaList.length}` })
            ] })
          ] }),
          /* @__PURE__ */ u("div", { class: "flex space-x-2 mt-2", children: [
            /* @__PURE__ */ u("span", { class: "flex-grow" }),
            /* @__PURE__ */ u("button", { class: "btn", onClick: onClose, children: t("Cancel") }),
            /* @__PURE__ */ u("div", { class: "join", children: [
              /* @__PURE__ */ u("button", { class: "btn join-item pr-2", onClick: () => onCopy(), children: copied ? t("Copied!") : t("Copy URLs") }),
              /* @__PURE__ */ u("button", { class: "btn join-item pl-2", onClick: () => onCopy(true), children: /* @__PURE__ */ u(IconFileDownload, {}) })
            ] }),
            /* @__PURE__ */ u(
              "button",
              {
                class: cx("btn btn-secondary", (loading || mediaList.length === 0) && "btn-disabled"),
                onClick: onExport,
                disabled: loading || mediaList.length === 0,
                children: [
                  loading && /* @__PURE__ */ u("span", { class: "loading loading-spinner" }),
                  t("Start Export")
                ]
              }
            )
          ] })
        ]
      }
    );
  }
  const CAPTURE_COUNT_SNAPSHOT_KEY = "__twe_capture_counts_v1";
  const CAPTURE_COUNT_SNAPSHOT_V2_KEY = "__twe_capture_counts_v2";
  const ACTIVE_DB_NAME_KEY = "__twe_active_db_name_v1";
  const DB_MUTATION_STORAGE_KEY = "__twe_db_mutation_v1";
  const CAPTURE_COUNT_EVENT_NAME = "twe:capture-count-updated-v1";
  const CAPTURED_RECORDS_CACHE_LIMIT = 10;
  const VIEWER_INITIAL_PAGE_SIZE = 160;
  const VIEWER_NEXT_PAGE_SIZE = 320;
  const VIEWER_WARM_PREFETCH_TARGET = 960;
  const capturedRecordsCache = /* @__PURE__ */ new Map();
  function setCapturedRecordsCacheEntry(key, value) {
    if (capturedRecordsCache.has(key)) {
      capturedRecordsCache.delete(key);
    }
    capturedRecordsCache.set(key, value);
    while (capturedRecordsCache.size > CAPTURED_RECORDS_CACHE_LIMIT) {
      const oldestKey = capturedRecordsCache.keys().next().value;
      if (!oldestKey) break;
      capturedRecordsCache.delete(oldestKey);
    }
  }
  async function readCaptureCountFromDb(dbName, extName) {
    return await new Promise((resolve) => {
      const openReq = indexedDB.open(dbName);
      openReq.onerror = () => resolve(0);
      openReq.onsuccess = () => {
        const opened = openReq.result;
        let tx;
        try {
          if (!opened.objectStoreNames.contains("captures")) {
            opened.close();
            resolve(0);
            return;
          }
          tx = opened.transaction(["captures"], "readonly");
        } catch {
          opened.close();
          resolve(0);
          return;
        }
        let req;
        try {
          const store = tx.objectStore("captures");
          if (store.indexNames.contains("extension")) {
            req = store.index("extension").count(extName);
          } else {
            req = store.count();
          }
        } catch {
          opened.close();
          resolve(0);
          return;
        }
        req.onsuccess = () => {
          opened.close();
          resolve(Number(req.result) || 0);
        };
        req.onerror = () => {
          opened.close();
          resolve(0);
        };
      };
    });
  }
  async function getCaptureCountAcrossKnownDatabases(extName) {
    const getActiveDatabaseName = () => {
      try {
        const unsafeCandidate = _unsafeWindow;
        const unsafeName = unsafeCandidate == null ? void 0 : unsafeCandidate[ACTIVE_DB_NAME_KEY];
        if (typeof unsafeName === "string" && unsafeName.trim().length > 0) {
          return unsafeName.trim();
        }
      } catch {
      }
      try {
        const directName = globalThis[ACTIVE_DB_NAME_KEY];
        if (typeof directName === "string" && directName.trim().length > 0) {
          return directName.trim();
        }
      } catch {
      }
      try {
        if (typeof localStorage !== "undefined") {
          const stored = localStorage.getItem(ACTIVE_DB_NAME_KEY);
          if (stored && stored.trim().length > 0) {
            return stored.trim();
          }
        }
      } catch {
      }
      return null;
    };
    const readSnapshot = (activeDbName2) => {
      const candidates = [];
      const collectFromV2Entry = (entry) => {
        if (!entry || typeof entry !== "object") return;
        const obj = entry;
        const count = Number(obj.count);
        if (!Number.isFinite(count)) return;
        const dbName = typeof obj.dbName === "string" ? obj.dbName : "";
        const updatedAt = Number(obj.updatedAt);
        candidates.push({
          count,
          dbName,
          updatedAt: Number.isFinite(updatedAt) ? updatedAt : 0
        });
      };
      const collectFromV1Entry = (entry) => {
        const count = Number(entry);
        if (!Number.isFinite(count)) return;
        candidates.push({ count, dbName: "", updatedAt: 0 });
      };
      const collectFromSource = (source) => {
        if (!source || typeof source !== "object") return;
        const root = source;
        const v2 = root[CAPTURE_COUNT_SNAPSHOT_V2_KEY];
        if (v2 && typeof v2 === "object") {
          collectFromV2Entry(v2[extName]);
        }
        const v1 = root[CAPTURE_COUNT_SNAPSHOT_KEY];
        if (v1 && typeof v1 === "object") {
          collectFromV1Entry(v1[extName]);
        }
      };
      try {
        collectFromSource(_unsafeWindow);
      } catch {
      }
      try {
        collectFromSource(globalThis);
      } catch {
      }
      try {
        if (typeof localStorage !== "undefined") {
          const rawV2 = localStorage.getItem(CAPTURE_COUNT_SNAPSHOT_V2_KEY);
          if (rawV2) {
            const parsed = JSON.parse(rawV2);
            collectFromV2Entry(parsed == null ? void 0 : parsed[extName]);
          }
          const rawV1 = localStorage.getItem(CAPTURE_COUNT_SNAPSHOT_KEY);
          if (rawV1) {
            const parsed = JSON.parse(rawV1);
            collectFromV1Entry(parsed == null ? void 0 : parsed[extName]);
          }
        }
      } catch {
      }
      if (!candidates.length) {
        return 0;
      }
      if (activeDbName2) {
        const scoped = candidates.filter((candidate) => candidate.dbName === activeDbName2).sort((a, b) => b.updatedAt - a.updatedAt);
        const firstScoped = scoped[0];
        if (firstScoped) {
          return firstScoped.count;
        }
      }
      candidates.sort((a, b) => {
        if (b.updatedAt !== a.updatedAt) return b.updatedAt - a.updatedAt;
        return b.count - a.count;
      });
      const first = candidates[0];
      return first ? first.count : 0;
    };
    const activeDbName = getActiveDatabaseName();
    if (typeof indexedDB === "undefined") {
      return Math.max(
        readSnapshot(activeDbName),
        Number(await dbProxy.extGetCaptureCount(extName) || 0)
      );
    }
    let names = [];
    try {
      const rows = typeof indexedDB.databases === "function" ? await indexedDB.databases() : [];
      names = Array.from(
        new Set(
          (rows || []).map((row) => row == null ? void 0 : row.name).filter(
            (name2) => !!name2 && (name2.includes("twitter-web-exporter") || name2.includes("scrollmark"))
          )
        )
      );
    } catch {
      names = [];
    }
    if (!names.length) {
      if (activeDbName) {
        const activeDbCount = await readCaptureCountFromDb(activeDbName, extName);
        return Math.max(
          activeDbCount,
          readSnapshot(activeDbName),
          Number(await dbProxy.extGetCaptureCount(extName) || 0)
        );
      }
      return Math.max(
        readSnapshot(activeDbName),
        Number(await dbProxy.extGetCaptureCount(extName) || 0)
      );
    }
    if (activeDbName && names.includes(activeDbName)) {
      const activeDbCount = await readCaptureCountFromDb(activeDbName, extName);
      return Math.max(
        activeDbCount,
        readSnapshot(activeDbName),
        Number(await dbProxy.extGetCaptureCount(extName) || 0)
      );
    }
    let best = 0;
    for (const name2 of names) {
      const count = await readCaptureCountFromDb(name2, extName);
      if (count > best) {
        best = count;
      }
    }
    return Math.max(
      best,
      readSnapshot(activeDbName),
      Number(await dbProxy.extGetCaptureCount(extName) || 0)
    );
  }
  function useCaptureCount(extName) {
    const mutationVersion = useDatabaseMutationVersion(extName);
    const [count, setCount] = hooks.useState(0);
    hooks.useEffect(() => {
      let disposed = false;
      let refreshInFlight = false;
      let refreshQueued = false;
      const refresh = async () => {
        try {
          const next = await getCaptureCountAcrossKnownDatabases(extName);
          if (!disposed) {
            setCount(next);
          }
        } catch {
        }
      };
      const scheduleRefresh = () => {
        if (disposed) {
          return;
        }
        if (refreshInFlight) {
          refreshQueued = true;
          return;
        }
        refreshInFlight = true;
        void (async () => {
          try {
            await refresh();
          } finally {
            refreshInFlight = false;
            if (refreshQueued) {
              refreshQueued = false;
              scheduleRefresh();
            }
          }
        })();
      };
      const onStorage = (event) => {
        const key = event.key;
        if (!key) return;
        if (key !== CAPTURE_COUNT_SNAPSHOT_KEY && key !== CAPTURE_COUNT_SNAPSHOT_V2_KEY && key !== ACTIVE_DB_NAME_KEY && key !== DB_MUTATION_STORAGE_KEY) {
          return;
        }
        scheduleRefresh();
      };
      const onCaptureCountEvent = (event) => {
        const detail = event.detail;
        const targetExtension = detail && typeof detail === "object" ? detail.extension : void 0;
        if (targetExtension && targetExtension !== extName) {
          return;
        }
        scheduleRefresh();
      };
      let timer = null;
      const scheduleNext = () => {
        if (disposed) return;
        const delay = typeof document !== "undefined" && document.hidden ? 6e3 : 1500;
        timer = globalThis.setTimeout(() => {
          scheduleRefresh();
          scheduleNext();
        }, delay);
      };
      scheduleRefresh();
      scheduleNext();
      if (typeof window !== "undefined") {
        window.addEventListener("storage", onStorage);
        window.addEventListener(CAPTURE_COUNT_EVENT_NAME, onCaptureCountEvent);
      }
      return () => {
        disposed = true;
        if (timer !== null) {
          globalThis.clearTimeout(timer);
        }
        if (typeof window !== "undefined") {
          window.removeEventListener("storage", onStorage);
          window.removeEventListener(CAPTURE_COUNT_EVENT_NAME, onCaptureCountEvent);
        }
      };
    }, [extName, mutationVersion]);
    return count;
  }
  function useCapturedRecords(extName, type2) {
    const mutationVersion = useDatabaseMutationVersion(extName);
    const cacheKey = `${extName}:${type2}`;
    const loadingMoreRef = hooks.useRef(false);
    const nextOffsetRef = hooks.useRef(0);
    const exhaustedRef = hooks.useRef(false);
    const recordsRef = hooks.useRef([]);
    const totalCountRef = hooks.useRef(0);
    const [state, setState] = hooks.useState(() => {
      const cached = capturedRecordsCache.get(cacheKey);
      if (cached && cached.mutationVersion === mutationVersion) {
        nextOffsetRef.current = cached.nextOffset;
        exhaustedRef.current = cached.exhausted;
        recordsRef.current = cached.records;
        totalCountRef.current = cached.totalCount;
        return {
          records: cached.records,
          loading: false,
          loadingMore: false,
          loadedCount: cached.records.length,
          totalCount: cached.totalCount,
          hasMore: !cached.exhausted
        };
      }
      return {
        records: [],
        loading: true,
        loadingMore: false,
        loadedCount: 0,
        totalCount: 0,
        hasMore: false
      };
    });
    const readPage = hooks.useCallback(
      async (offset, limit) => {
        const captures = await dbProxy.extGetCapturePage(extName, {
          type: type2,
          offset,
          limit,
          order: "newest"
        });
        const records = type2 === ExtensionType.USER ? await dbProxy.extGetCapturedUsers(extName, captures) ?? [] : await dbProxy.extGetCapturedTweets(extName, captures) ?? [];
        return {
          captures,
          records
        };
      },
      [extName, type2]
    );
    const commitState = hooks.useCallback(
      (next, nextOffset = next.loadedCount) => {
        recordsRef.current = next.records;
        totalCountRef.current = next.totalCount;
        nextOffsetRef.current = nextOffset;
        exhaustedRef.current = !next.hasMore;
        setState(next);
        setCapturedRecordsCacheEntry(cacheKey, {
          mutationVersion,
          totalCount: next.totalCount,
          nextOffset,
          exhausted: !next.hasMore,
          records: next.records
        });
      },
      [cacheKey, mutationVersion]
    );
    const loadMore = hooks.useCallback(async () => {
      if (loadingMoreRef.current || exhaustedRef.current) {
        return;
      }
      loadingMoreRef.current = true;
      const startedAt = nowMs();
      const offset = nextOffsetRef.current;
      setState((current) => ({ ...current, loadingMore: true }));
      try {
        const { captures, records: pageRecords } = await readPage(offset, VIEWER_NEXT_PAGE_SIZE);
        const existing = recordsRef.current;
        const existingIds = new Set(
          existing.map(
            (record, index) => String(record.rest_id || index)
          )
        );
        const appended = pageRecords.filter((record, index) => {
          const id2 = String(
            record.rest_id || `${offset}-${index}`
          );
          if (existingIds.has(id2)) return false;
          existingIds.add(id2);
          return true;
        });
        const nextRecords = [...existing, ...appended];
        const hasMore = captures.length >= VIEWER_NEXT_PAGE_SIZE && nextRecords.length < Math.max(totalCountRef.current, nextRecords.length);
        commitState(
          {
            records: nextRecords,
            loading: false,
            loadingMore: false,
            loadedCount: nextRecords.length,
            totalCount: Math.max(totalCountRef.current, nextRecords.length),
            hasMore
          },
          offset + captures.length
        );
        recordPerfMetric({
          kind: "viewer",
          name: "load-more",
          durationMs: nowMs() - startedAt,
          value: appended.length,
          tags: { extName, type: type2, offset, loadedCount: nextRecords.length }
        });
      } finally {
        loadingMoreRef.current = false;
        setState((current) => ({ ...current, loadingMore: false }));
      }
    }, [commitState, extName, readPage, type2]);
    const loadAll = hooks.useCallback(async () => {
      while (!exhaustedRef.current) {
        const beforeOffset = nextOffsetRef.current;
        await loadMore();
        if (nextOffsetRef.current === beforeOffset) {
          break;
        }
      }
    }, [loadMore]);
    hooks.useEffect(() => {
      let cancelled = false;
      const cached = capturedRecordsCache.get(cacheKey);
      if (cached && cached.mutationVersion === mutationVersion) {
        nextOffsetRef.current = cached.nextOffset;
        exhaustedRef.current = cached.exhausted;
        recordsRef.current = cached.records;
        totalCountRef.current = cached.totalCount;
        setState({
          records: cached.records,
          loading: false,
          loadingMore: false,
          loadedCount: cached.records.length,
          totalCount: cached.totalCount,
          hasMore: !cached.exhausted
        });
        return;
      }
      const load = async () => {
        logger.debug("useCapturedRecords page load", extName);
        const startedAt = nowMs();
        const totalCount = await dbProxy.extGetCaptureCount(extName, type2) ?? 0;
        if (cancelled) return;
        if (!totalCount) {
          commitState({
            records: [],
            loading: false,
            loadingMore: false,
            loadedCount: 0,
            totalCount: 0,
            hasMore: false
          });
          return;
        }
        setState({
          records: [],
          loading: true,
          loadingMore: false,
          loadedCount: 0,
          totalCount,
          hasMore: false
        });
        const { captures, records: firstRecords } = await readPage(0, VIEWER_INITIAL_PAGE_SIZE);
        if (cancelled) return;
        const hasMore = captures.length >= VIEWER_INITIAL_PAGE_SIZE && firstRecords.length < totalCount;
        commitState(
          {
            records: firstRecords,
            loading: false,
            loadingMore: false,
            loadedCount: firstRecords.length,
            totalCount,
            hasMore
          },
          captures.length
        );
        recordPerfMetric({
          kind: "viewer",
          name: "initial-page",
          durationMs: nowMs() - startedAt,
          value: firstRecords.length,
          tags: { extName, type: type2, totalCount, hasMore }
        });
      };
      void load();
      return () => {
        cancelled = true;
      };
    }, [cacheKey, commitState, extName, mutationVersion, readPage, type2]);
    hooks.useEffect(() => {
      if (state.loading || state.loadingMore || !state.hasMore) return;
      if (state.loadedCount >= VIEWER_WARM_PREFETCH_TARGET) return;
      const run = () => {
        if (loadingMoreRef.current || exhaustedRef.current) return;
        void loadMore();
      };
      if (typeof window !== "undefined" && "requestIdleCallback" in window && typeof window.requestIdleCallback === "function") {
        const handle2 = window.requestIdleCallback(run, { timeout: 800 });
        return () => {
          if (typeof window !== "undefined" && "cancelIdleCallback" in window && typeof window.cancelIdleCallback === "function") {
            window.cancelIdleCallback(
              handle2
            );
          }
        };
      }
      const handle = globalThis.setTimeout(run, 120);
      return () => globalThis.clearTimeout(handle);
    }, [loadMore, state.hasMore, state.loadedCount, state.loading, state.loadingMore]);
    return {
      ...state,
      loadMore,
      loadAll
    };
  }
  function useSearchDocuments(extName, type2) {
    const mutationVersion = useDatabaseMutationVersion(extName);
    const [state, setState] = hooks.useState({ documents: [], loading: true });
    const backfillKeyRef = hooks.useRef("");
    hooks.useEffect(() => {
      let cancelled = false;
      setState((current) => ({ ...current, loading: true }));
      void Promise.all([
        dbProxy.extGetSearchDocuments(extName, type2),
        dbProxy.extGetCaptureCount(extName, type2)
      ]).then(([documents, captureCount]) => {
        if (cancelled) return;
        const rows = documents ?? [];
        setState({ documents: rows, loading: false });
        const total = captureCount ?? 0;
        const backfillKey = `${extName}:${type2}:${total}:${rows.length}`;
        const toleratedGap = Math.max(50, Math.ceil(total * 0.02));
        if (total > 0 && rows.length + toleratedGap < total && backfillKeyRef.current !== backfillKey) {
          backfillKeyRef.current = backfillKey;
          void dbProxy.extBackfillSearchDocuments(extName, type2).catch((error) => {
            logger.warn("Search document backfill failed", error);
          });
        }
      }).catch(() => {
        if (cancelled) return;
        setState({ documents: [], loading: false });
      });
      return () => {
        cancelled = true;
      };
    }, [extName, mutationVersion, type2]);
    return state;
  }
  function useClearCaptures(extName) {
    return async () => {
      logger.debug("Clearing captures for extension:", extName);
      return dbProxy.extClearCaptures(extName);
    };
  }
  function getBookmarkFolderStatus(record) {
    const obj = record;
    const folderName = obj == null ? void 0 : obj.__bookmark_folder_name;
    const folderNameSource = obj == null ? void 0 : obj.__bookmark_folder_name_source;
    const folderId = obj == null ? void 0 : obj.__bookmark_folder_id;
    if (folderNameSource === "api" && typeof folderName === "string" && folderName.trim().length > 0) {
      return "api-name";
    }
    if (typeof folderId === "string" && folderId.trim().length > 0) {
      return "id-only";
    }
    return "none";
  }
  function getBookmarkFolderStatusFromDoc(document2) {
    if (document2.folder_id && document2.folder_name) return "api-name";
    if (document2.folder_id) return "id-only";
    return "none";
  }
  function TableView({ title, extension, fullscreen, onFullscreenChange }) {
    const { t } = useTranslation();
    const { name: name2, type: type2 } = extension;
    const capturedState = useCapturedRecords(name2, type2);
    const searchDocumentsState = useSearchDocuments(name2, type2);
    const records = capturedState.records;
    const clearCapturedData = useClearCaptures(name2);
    const isBookmarksModule = name2 === "BookmarksModule" && type2 === ExtensionType.TWEET;
    const [bookmarkStatus, setBookmarkStatus] = hooks.useState({
      latestStatus: "none",
      counts: {
        "api-name": 0,
        "id-only": 0,
        none: 0
      }
    });
    const [bookmarkFolderOptions, setBookmarkFolderOptions] = hooks.useState([]);
    hooks.useEffect(() => {
      if (!isBookmarksModule) {
        setBookmarkStatus({
          latestStatus: "none",
          counts: {
            "api-name": 0,
            "id-only": 0,
            none: 0
          }
        });
        setBookmarkFolderOptions([]);
        return;
      }
      let cancelled = false;
      let idleHandle = 0;
      let timeoutHandle = null;
      const recompute = () => {
        if (cancelled) return;
        const items = records ?? [];
        const documents = searchDocumentsState.documents;
        const useDocuments = documents.length >= items.length;
        const counts = {
          "api-name": 0,
          "id-only": 0,
          none: 0
        };
        const counter = /* @__PURE__ */ new Map();
        if (useDocuments) {
          for (const document2 of documents) {
            counts[getBookmarkFolderStatusFromDoc(document2)]++;
            const folderId = String(document2.folder_id || "").trim();
            if (!folderId) continue;
            const folderName = String(document2.folder_name || "").trim();
            const current = counter.get(folderId);
            if (current) {
              current.count += 1;
            } else {
              counter.set(folderId, {
                label: folderName ? `${folderName}` : `Folder ${folderId}`,
                count: 1
              });
            }
          }
        } else {
          for (const item of items) {
            counts[getBookmarkFolderStatus(item)]++;
            if (!item || typeof item !== "object") continue;
            const row = item;
            const folderId = typeof row.__bookmark_folder_id === "string" ? row.__bookmark_folder_id : "";
            if (!folderId.trim()) continue;
            const folderName = row.__bookmark_folder_name_source === "api" && typeof row.__bookmark_folder_name === "string" ? row.__bookmark_folder_name.trim() : "";
            const current = counter.get(folderId);
            if (current) {
              current.count += 1;
            } else {
              counter.set(folderId, {
                label: folderName ? `${folderName}` : `Folder ${folderId}`,
                count: 1
              });
            }
          }
        }
        const firstDocument = documents[0];
        setBookmarkStatus({
          latestStatus: useDocuments && firstDocument ? getBookmarkFolderStatusFromDoc(firstDocument) : items.length > 0 ? getBookmarkFolderStatus(items[items.length - 1]) : "none",
          counts
        });
        setBookmarkFolderOptions(
          [...counter.entries()].sort((a, b) => {
            if (b[1].count !== a[1].count) return b[1].count - a[1].count;
            return a[1].label.localeCompare(b[1].label);
          }).map(([value, meta]) => ({
            value,
            label: `${meta.label} (${meta.count})`
          }))
        );
      };
      if (typeof window !== "undefined" && "requestIdleCallback" in window) {
        idleHandle = window.requestIdleCallback(() => recompute());
      } else {
        timeoutHandle = globalThis.setTimeout(recompute, 80);
      }
      return () => {
        cancelled = true;
        if (idleHandle && typeof window !== "undefined" && "cancelIdleCallback" in window) {
          window.cancelIdleCallback(idleHandle);
        }
        if (timeoutHandle !== null) {
          globalThis.clearTimeout(timeoutHandle);
        }
      };
    }, [isBookmarksModule, records, searchDocumentsState.documents]);
    const [showExportMediaModal, toggleShowExportMediaModal] = useToggle();
    const tweetAlternateViews = [
      {
        id: "media-masonry",
        label: "Media masonry",
        icon: "grid",
        component: TweetMediaMasonry
      }
    ];
    const renderActions = (_table, context) => /* @__PURE__ */ u("div", { class: "flex items-center gap-2", children: [
      context.loading ? /* @__PURE__ */ u("span", { class: "font-mono text-[10px] opacity-60", children: [
        "loading ",
        context.loadedCount,
        "/",
        context.totalCount
      ] }) : null,
      context.loadingMore ? /* @__PURE__ */ u("span", { class: "font-mono text-[10px] opacity-60", children: [
        "loading more ",
        context.loadedCount,
        "/",
        context.totalCount
      ] }) : null,
      isBookmarksModule && /* @__PURE__ */ u(
        "button",
        {
          class: "btn btn-sm btn-accent btn-outline",
          onClick: () => {
            const history = readSearchHistory("bookmarks");
            downloadJson(
              {
                exported_at_ms: Date.now(),
                exported_at_iso: (/* @__PURE__ */ new Date()).toISOString(),
                scope: "bookmarks",
                count: history.length,
                history
              },
              `twe-bookmarks-search-history-${Date.now()}.json`
            );
          },
          title: "Export persisted bookmark search history",
          children: t("Export Search History")
        }
      ),
      isBookmarksModule && /* @__PURE__ */ u(
        "span",
        {
          class: "badge badge-outline badge-sm font-mono tooltip before:whitespace-pre-line before:max-w-40",
          "data-tip": `latest: ${bookmarkStatus.latestStatus}
api-name: ${bookmarkStatus.counts["api-name"]}
id-only: ${bookmarkStatus.counts["id-only"]}
none: ${bookmarkStatus.counts.none}`,
          children: [
            "folder metadata: ",
            bookmarkStatus.latestStatus
          ]
        }
      ),
      /* @__PURE__ */ u(
        "button",
        {
          class: "btn btn-sm btn-secondary",
          onClick: toggleShowExportMediaModal,
          disabled: context.loading,
          title: context.loading ? "Wait for records to finish loading before exporting." : void 0,
          children: t("Export Media")
        }
      )
    ] });
    if (type2 === ExtensionType.TWEET) {
      return /* @__PURE__ */ u(
        BaseTableView,
        {
          title,
          viewStateKey: `${name2}:${type2}`,
          searchHistoryScope: isBookmarksModule ? "bookmarks" : void 0,
          fullscreen,
          onFullscreenChange,
          loading: capturedState.loading,
          loadingMore: capturedState.loadingMore,
          loadedCount: capturedState.loadedCount,
          totalCount: capturedState.totalCount,
          hasMore: capturedState.hasMore,
          loadMore: capturedState.loadMore,
          loadAll: capturedState.loadAll,
          hydrateRecordsByIds: (ids) => dbProxy.extGetTweetsByIds(ids),
          records: records ?? [],
          searchDocuments: searchDocumentsState.documents,
          columns: columns$2,
          clear: clearCapturedData,
          alternateViews: tweetAlternateViews,
          bookmarkFolderOptions,
          renderActions,
          renderExtra: (_table, context) => /* @__PURE__ */ u(
            ExportMediaModal,
            {
              title,
              resultRecords: context.resultRecords,
              selectedRecords: context.selectedRecords,
              resultSetSnapshot: context.resultSetSnapshot,
              selectionMode: context.selectionMode,
              isTweet: true,
              show: showExportMediaModal,
              onClose: toggleShowExportMediaModal
            }
          )
        }
      );
    }
    return /* @__PURE__ */ u(
      BaseTableView,
      {
        title,
        viewStateKey: `${name2}:${type2}`,
        searchHistoryScope: isBookmarksModule ? "bookmarks" : void 0,
        fullscreen,
        onFullscreenChange,
        loading: capturedState.loading,
        loadingMore: capturedState.loadingMore,
        loadedCount: capturedState.loadedCount,
        totalCount: capturedState.totalCount,
        hasMore: capturedState.hasMore,
        loadMore: capturedState.loadMore,
        loadAll: capturedState.loadAll,
        hydrateRecordsByIds: (ids) => dbProxy.extGetUsersByIds(ids),
        records: records ?? [],
        searchDocuments: searchDocumentsState.documents,
        columns: columns$1,
        clear: clearCapturedData,
        bookmarkFolderOptions,
        renderActions,
        renderExtra: (_table, context) => /* @__PURE__ */ u(
          ExportMediaModal,
          {
            title,
            resultRecords: context.resultRecords,
            selectedRecords: context.selectedRecords,
            resultSetSnapshot: context.resultSetSnapshot,
            selectionMode: context.selectionMode,
            isTweet: false,
            show: showExportMediaModal,
            onClose: toggleShowExportMediaModal
          }
        )
      }
    );
  }
  function CommonModuleUI({ extension }) {
    const { t } = useTranslation();
    const [showModal, toggleShowModal] = useToggle();
    const [isViewerFullscreen, setIsViewerFullscreen] = hooks.useState(false);
    const count = useCaptureCount(extension.name);
    if (extension.type !== ExtensionType.TWEET && extension.type !== ExtensionType.USER) {
      throw new Error("Incorrect use of CommonModuleUI component.");
    }
    const presentation = getWidgetPresentation(extension);
    const titleKey = presentation.titleKey ?? extension.name.replace("Module", "");
    const title = t(titleKey);
    return /* @__PURE__ */ u(
      ExtensionPanel,
      {
        title,
        description: `${t("Captured:")} ${count}`,
        active: !!count && count > 0,
        onClick: toggleShowModal,
        indicatorColor: presentation.indicatorColor,
        panelClass: presentation.panelClass,
        children: /* @__PURE__ */ u(
          Modal,
          {
            class: isViewerFullscreen ? "h-screen max-h-screen max-w-none" : "max-w-4xl md:max-w-screen-md sm:max-w-screen-sm h-[82vh] max-h-[calc(100vh-4rem)]",
            title,
            show: showModal,
            fullscreen: isViewerFullscreen,
            onClose: () => {
              setIsViewerFullscreen(false);
              toggleShowModal();
            },
            children: /* @__PURE__ */ u(
              TableView,
              {
                title,
                extension,
                fullscreen: isViewerFullscreen,
                onFullscreenChange: setIsViewerFullscreen
              }
            )
          }
        )
      }
    );
  }
  const USER_MEDIA_EXTENSION_NAME = "UserMediaModule";
  function getUserMediaMirrorTweetIds(extName, tweets) {
    if (extName === USER_MEDIA_EXTENSION_NAME || !Array.isArray(tweets) || !tweets.length) {
      return [];
    }
    return tweets.filter((tweet) => hasOwnTweetMedia(tweet)).map((tweet) => String(tweet.rest_id || "").trim()).filter(Boolean);
  }
  const TWEET_INDEX_MODULE_NAME$1 = "TweetIndexModule";
  async function projectTweets(extName, tweets) {
    const normalizedTweets = Array.isArray(tweets) ? tweets : [];
    await dbProxy.extAddTweets(extName, normalizedTweets);
    const tweetIds = normalizedTweets.map((tweet) => String((tweet == null ? void 0 : tweet.rest_id) || "").trim()).filter(Boolean);
    if (extName !== TWEET_INDEX_MODULE_NAME$1 && tweetIds.length) {
      await dbProxy.extAddTweetCaptureIds(TWEET_INDEX_MODULE_NAME$1, tweetIds);
    }
    const userMediaTweetIds = getUserMediaMirrorTweetIds(extName, normalizedTweets);
    if (userMediaTweetIds.length) {
      await dbProxy.extAddTweetCaptureIds("UserMediaModule", userMediaTweetIds);
    }
    return { kind: "tweets", count: normalizedTweets.length };
  }
  function projectUsers(extName, users) {
    void dbProxy.extAddUsers(extName, users);
    return { kind: "users", count: Array.isArray(users) ? users.length : 0 };
  }
  async function projectUsersWithEdges(extName, users, edges) {
    const normalizedUsers = Array.isArray(users) ? users : [];
    await dbProxy.extAddUsers(extName, normalizedUsers);
    if (edges.length) {
      await dbProxy.extAddSocialEdges(extName, edges);
    }
    return { kind: "users", count: normalizedUsers.length };
  }
  function logModuleItemsReceived(moduleName, count) {
    logger.info(`${moduleName}: ${count} items received`);
  }
  function logModuleParseFailure(moduleName, req, res, err2) {
    logger.debug(req.method, req.url, res.status, res.responseText);
    logger.errorWithBanner(`${moduleName}: Failed to parse API response`, err2);
  }
  function createModuleInterceptor(spec) {
    return (req, res, ext) => {
      if (!spec.match(req, res)) {
        return;
      }
      try {
        const parsed = spec.parse(req, res, ext);
        const count = spec.count ? spec.count(parsed) : Array.isArray(parsed) ? parsed.length : 0;
        const projection = spec.project ? spec.project(ext.name, parsed, req, res) : { kind: "none", count };
        const finish = (resolvedProjection) => {
          var _a2;
          const normalizedProjection = resolvedProjection && typeof resolvedProjection === "object" ? resolvedProjection : { kind: "none", count };
          logModuleItemsReceived(spec.moduleName, count);
          (_a2 = spec.onSuccess) == null ? void 0 : _a2.call(spec, parsed, {
            ext,
            req,
            res,
            projection: normalizedProjection,
            count
          });
        };
        if (projection && typeof projection.then === "function") {
          void projection.then(
            (resolved) => finish(resolved)
          );
        } else {
          finish(projection);
        }
      } catch (err2) {
        logModuleParseFailure(
          spec.moduleName,
          req,
          res,
          err2 instanceof Error ? err2 : new Error(String(err2))
        );
      }
    };
  }
  const BOOKMARK_FOLDER_CACHE_STORAGE_KEY$1 = "twe_bookmark_folder_name_cache_v1";
  const BOOKMARK_FOLDER_BACKFILL_CANDIDATE_LIMIT = 260;
  const BOOKMARK_FOLDER_BACKFILL_SCAN_LIMIT = 1800;
  const pendingBookmarkFolderNameBackfills = /* @__PURE__ */ new Set();
  const inFlightBookmarkFolderNameBackfills = /* @__PURE__ */ new Set();
  function loadBookmarkFolderNameCache() {
    try {
      if (typeof localStorage === "undefined") return /* @__PURE__ */ new Map();
      const raw = localStorage.getItem(BOOKMARK_FOLDER_CACHE_STORAGE_KEY$1);
      if (!raw) return /* @__PURE__ */ new Map();
      const data = JSON.parse(raw);
      if (!Array.isArray(data)) return /* @__PURE__ */ new Map();
      const entries = [];
      for (const entry of data) {
        if (Array.isArray(entry) && entry.length === 2 && typeof entry[0] === "string" && typeof entry[1] === "string") {
          entries.push([entry[0], entry[1]]);
        }
      }
      return new Map(entries);
    } catch {
      return /* @__PURE__ */ new Map();
    }
  }
  function persistBookmarkFolderNameCache(cache) {
    try {
      if (typeof localStorage === "undefined") return;
      localStorage.setItem(BOOKMARK_FOLDER_CACHE_STORAGE_KEY$1, JSON.stringify([...cache.entries()]));
    } catch {
    }
  }
  function extractTweetIdsForBackfill(tweets) {
    const ids = [];
    for (const tweet of tweets) {
      if (!tweet || typeof tweet !== "object") continue;
      const restId = String(tweet.rest_id || "").trim();
      if (!restId) continue;
      ids.push(restId);
      if (ids.length >= BOOKMARK_FOLDER_BACKFILL_CANDIDATE_LIMIT) {
        break;
      }
    }
    return ids;
  }
  function queueFolderNameBackfill(folderIds) {
    for (const folderId of folderIds) {
      const normalized = String(folderId || "").trim();
      if (!normalized) continue;
      pendingBookmarkFolderNameBackfills.add(normalized);
    }
  }
  function triggerFolderNameBackfillIfNeeded(extName, folderId, tweets) {
    if (!folderId) return;
    if (!pendingBookmarkFolderNameBackfills.has(folderId)) return;
    if (inFlightBookmarkFolderNameBackfills.has(folderId)) return;
    const folderName = bookmarkFolderNameCache.get(folderId);
    if (!folderName) return;
    const candidateTweetIds = extractTweetIdsForBackfill(tweets);
    inFlightBookmarkFolderNameBackfills.add(folderId);
    void dbProxy.extBackfillRecentBookmarkFolderName(extName, folderId, folderName, {
      candidateTweetIds,
      candidateLimit: BOOKMARK_FOLDER_BACKFILL_CANDIDATE_LIMIT,
      recentCaptureScanLimit: BOOKMARK_FOLDER_BACKFILL_SCAN_LIMIT
    }).then((summary) => {
      if (((summary == null ? void 0 : summary.inspected) || 0) > 0) {
        pendingBookmarkFolderNameBackfills.delete(folderId);
      }
      if (((summary == null ? void 0 : summary.updated) || 0) > 0) {
        logger.info(
          `Bookmarks: folder-name backfill updated ${summary.updated} rows for folder ${folderId}`
        );
      }
    }).catch(() => {
    }).finally(() => {
      inFlightBookmarkFolderNameBackfills.delete(folderId);
    });
  }
  const bookmarkFolderNameCache = loadBookmarkFolderNameCache();
  function isTimelineContainer(value) {
    if (!value || typeof value !== "object") return false;
    const timeline = value.timeline;
    if (!timeline || typeof timeline !== "object") return false;
    const inst = timeline.instructions;
    return Array.isArray(inst);
  }
  function findTimelineInstructionsFromObject(value, depth = 0, seen2 = /* @__PURE__ */ new Set()) {
    var _a2;
    if (!value || typeof value !== "object") return null;
    if (depth > 5) return null;
    if (Array.isArray(value)) {
      for (const item of value) {
        const found = findTimelineInstructionsFromObject(item, depth + 1, seen2);
        if (found) return found;
      }
      return null;
    }
    if (isTimelineContainer(value)) {
      return ((_a2 = value.timeline) == null ? void 0 : _a2.instructions) || null;
    }
    const obj = value;
    if (seen2.has(obj)) return null;
    seen2.add(obj);
    for (const key of Object.keys(obj)) {
      const nested = findTimelineInstructionsFromObject(obj[key], depth + 1, seen2);
      if (nested) return nested;
    }
    return null;
  }
  function coerceFolderId(value) {
    if (typeof value === "number" && Number.isFinite(value)) {
      const normalized = String(Math.trunc(value));
      return /^\d+$/.test(normalized) ? normalized : null;
    }
    if (typeof value !== "string") {
      return null;
    }
    const trimmed = value.trim();
    return /^\d+$/.test(trimmed) ? trimmed : null;
  }
  function isFolderOrCollectionKey(key) {
    const normalized = key.toLowerCase().replace(/[^a-z0-9]/g, "");
    return /^(bookmarkcollectionid|bookmarkfolderid|bookmarkcollection|folderid|collectionid|folder)$/.test(
      normalized
    );
  }
  function findFolderId(value) {
    const seen2 = /* @__PURE__ */ new Set();
    function walk(node, currentKey) {
      const byKey = currentKey && isFolderOrCollectionKey(currentKey) ? coerceFolderId(node) : null;
      if (byKey) return byKey;
      if (!node || typeof node !== "object") {
        return null;
      }
      if (Array.isArray(node)) {
        for (const item of node) {
          const nested = walk(item);
          if (nested) return nested;
        }
        return null;
      }
      const obj = node;
      if (seen2.has(obj)) return null;
      seen2.add(obj);
      for (const [key, nested] of Object.entries(obj)) {
        const found = walk(nested, key);
        if (found) return found;
      }
      return null;
    }
    return walk(value);
  }
  function extractTimelineInstructions(json) {
    var _a2, _b2, _c, _d, _e, _f;
    const timeline = ((_b2 = (_a2 = json.data) == null ? void 0 : _a2.bookmark_timeline_v2) == null ? void 0 : _b2.timeline) || ((_d = (_c = json.data) == null ? void 0 : _c.bookmark_timeline) == null ? void 0 : _d.timeline) || ((_f = (_e = json.data) == null ? void 0 : _e.bookmark_collection_timeline) == null ? void 0 : _f.timeline) || json.timeline;
    if (timeline && Array.isArray(timeline.instructions)) {
      return timeline.instructions;
    }
    const found = findTimelineInstructionsFromObject(json.data, 0, /* @__PURE__ */ new Set());
    if (found) {
      return found;
    }
    throw new Error("Bookmarks response missing timeline instructions");
  }
  const BOOKMARK_CONTEXT_GLOBAL_KEY = "__twe_bookmark_context_v1";
  const BOOKMARK_STRICT_FOLDER_GLOBAL_KEY = "__twe_bookmark_strict_folder_id_v1";
  const BOOKMARK_STRICT_FOLDER_STORAGE_KEY = "twe_bookmark_strict_folder_id_v1";
  const BOOKMARK_STRICT_MODE_GLOBAL_KEY = "__twe_bookmark_strict_mode_v1";
  const BOOKMARK_STRICT_MODE_STORAGE_KEY = "twe_bookmark_strict_mode_v1";
  const HOOK_STATS_GLOBAL_KEY = "__twe_hook_stats_v1";
  const HOOK_RUNTIME_GLOBAL_KEY = "__twe_runtime_v1";
  function incrementBookmarkDropCounter(counterKey) {
    const incrementOn = (target) => {
      if (!target || typeof target !== "object") return;
      const map = target;
      const current = Number(map[counterKey]);
      map[counterKey] = Number.isFinite(current) ? current + 1 : 1;
    };
    try {
      const root = globalThis;
      incrementOn(root[HOOK_STATS_GLOBAL_KEY]);
      incrementOn(root[HOOK_RUNTIME_GLOBAL_KEY]);
    } catch {
    }
  }
  function parseJsonLike(value) {
    try {
      return JSON.parse(value);
    } catch {
      return null;
    }
  }
  function toFormDataBody(value) {
    try {
      return new URLSearchParams(value);
    } catch {
      return null;
    }
  }
  function parseRequestBodyForVariables(rawBody) {
    if (typeof rawBody === "undefined" || rawBody === null) return null;
    if (typeof rawBody === "string") {
      let parsed = parseJsonLike(rawBody);
      if (parsed !== null) return parsed;
      const form = toFormDataBody(rawBody);
      if (form) {
        const variables = form.get("variables");
        if (variables) {
          parsed = parseJsonLike(variables);
          if (parsed !== null) {
            return parsed;
          }
        }
        const fallbackCollection = form.get("bookmark_collection_id") ?? form.get("folder_id");
        if (fallbackCollection) {
          return { bookmark_collection_id: fallbackCollection, folder_id: fallbackCollection };
        }
      }
      return null;
    }
    if (typeof Blob !== "undefined" && rawBody instanceof Blob) {
      return null;
    }
    if (typeof FormData !== "undefined" && rawBody instanceof FormData) {
      try {
        const form = new URLSearchParams();
        for (const [name2, value] of rawBody.entries()) {
          if (typeof value === "string") {
            form.set(name2, value);
          } else {
            form.set(name2, String(value));
          }
        }
        return parseRequestBodyForVariables(form.toString());
      } catch {
        return null;
      }
    }
    return null;
  }
  function resolveFolderFromContext(rawContext) {
    if (!rawContext) return null;
    if (typeof rawContext === "string") {
      const trimmed = rawContext.trim();
      if (!trimmed) return null;
      if (/^\d+$/.test(trimmed)) return trimmed;
      const byUrl = extractFolderIdFromUrl(trimmed);
      if (byUrl) return byUrl;
      const parsed = parseJsonLike(trimmed);
      if (parsed) {
        return findFolderId(parsed);
      }
      return null;
    }
    if (typeof rawContext === "object" && rawContext !== null) {
      const obj = rawContext;
      if (typeof obj.folderId === "string" && /^\d+$/.test(obj.folderId)) {
        return obj.folderId;
      }
      if (typeof obj.folderId === "number" && Number.isFinite(obj.folderId)) {
        return String(Math.trunc(obj.folderId));
      }
      if (typeof obj.folder_id === "string" && /^\d+$/.test(obj.folder_id)) {
        return obj.folder_id;
      }
      if (typeof obj.folder_id === "number" && Number.isFinite(obj.folder_id)) {
        return String(Math.trunc(obj.folder_id));
      }
      const found = findFolderId(rawContext);
      if (found) return found;
      const pageUrl = [obj.pageUrl, obj.url, obj.location, obj.currentUrl, obj.folderUrl].find(
        (value) => typeof value === "string"
      );
      if (typeof pageUrl === "string") {
        const byUrl = extractFolderIdFromUrl(pageUrl);
        if (byUrl) return byUrl;
      }
    }
    return null;
  }
  function resolveBookmarkContextFromGlobal() {
    try {
      return globalThis[BOOKMARK_CONTEXT_GLOBAL_KEY] ?? null;
    } catch {
      return null;
    }
  }
  function resolveStrictBookmarkFolderId() {
    try {
      const g = globalThis;
      if (g[BOOKMARK_STRICT_MODE_GLOBAL_KEY] === false) {
        return null;
      }
      const fromGlobal = coerceFolderId(g[BOOKMARK_STRICT_FOLDER_GLOBAL_KEY]);
      if (fromGlobal) return fromGlobal;
    } catch {
    }
    try {
      if (typeof localStorage === "undefined") return null;
      const modeRaw = localStorage.getItem(BOOKMARK_STRICT_MODE_STORAGE_KEY);
      if (modeRaw && /^(0|false|off|no)$/i.test(modeRaw.trim())) {
        return null;
      }
      const fromStorage = coerceFolderId(localStorage.getItem(BOOKMARK_STRICT_FOLDER_STORAGE_KEY));
      if (fromStorage) return fromStorage;
    } catch {
    }
    return null;
  }
  function resolveExplicitRequestFolderId(req) {
    const fromRequestUrl = resolveFolderFromRequestVariables(req.url);
    if (fromRequestUrl) return fromRequestUrl;
    if (req.body) {
      const parsed = parseRequestBodyForVariables(req.body);
      if (parsed) {
        const fromBody = findFolderId(parsed);
        if (fromBody) return fromBody;
      }
    }
    return null;
  }
  function resolveFolderFromResponseText(responseText) {
    if (!responseText) return null;
    const parsed = parseJsonLike(responseText);
    if (!parsed) return null;
    return findFolderId(parsed);
  }
  function extractFolderIdFromUrl(url) {
    try {
      const u2 = new URL(url, "https://x.com");
      const directKeys = [
        "bookmark_collection_id",
        "bookmarkcollectionid",
        "bookmarkCollectionId",
        "folder_id",
        "folderid",
        "folderId",
        "collection_id",
        "collectionid",
        "collectionId"
      ];
      const m = u2.pathname.match(/\/bookmarks\/(\d+)/) || u2.pathname.match(/\/bookmarks\/folders\/(\d+)/) || u2.pathname.match(/\/bookmarks\/folder\/(\d+)/) || u2.pathname.match(/\/bookmark_folders\/(\d+)/);
      if (m && m[1]) return m[1];
      for (const key of directKeys) {
        const direct = u2.searchParams.get(key);
        if (direct && /^\d+$/.test(direct)) {
          return direct;
        }
      }
      for (const [k, v] of u2.searchParams.entries()) {
        if (/folder/i.test(k) && /^\d+$/.test(v)) {
          return v;
        }
      }
      return null;
    } catch {
      return null;
    }
  }
  function resolveFolderFromRequestVariables(url) {
    try {
      const u2 = new URL(url, "https://x.com");
      const raw = u2.searchParams.get("variables");
      if (!raw) return null;
      let parsed;
      try {
        parsed = JSON.parse(decodeURIComponent(raw));
      } catch {
        try {
          parsed = JSON.parse(raw);
        } catch {
          return null;
        }
      }
      const directFolderId = findFolderId(parsed);
      if (directFolderId) return directFolderId;
      if (parsed && typeof parsed === "object") {
        const obj = parsed;
        for (const key of [
          "bookmark_collection_id",
          "bookmarkcollectionid",
          "bookmarkCollectionId",
          "folder_id",
          "folderid",
          "folderId",
          "collection_id",
          "collectionid",
          "collectionId"
        ]) {
          const value = obj[key];
          const folderId = coerceFolderId(value);
          if (folderId) return folderId;
        }
      }
    } catch {
      return null;
    }
    return null;
  }
  function getBookmarkFolderContext(req) {
    const ctx = {
      folder_id: null,
      folder_name: null,
      folder_url: typeof location !== "undefined" ? location.href : ""
    };
    if (!ctx.folder_id) {
      try {
        const u2 = new URL(req.url, "https://x.com");
        const fromRequestVariables = resolveFolderFromRequestVariables(req.url);
        if (fromRequestVariables) {
          ctx.folder_id = fromRequestVariables;
        } else {
          const directFromUrl = extractFolderIdFromUrl(u2.href);
          if (directFromUrl) {
            ctx.folder_id = directFromUrl;
          } else {
            const raw = u2.searchParams.get("variables");
            if (raw) {
              let vars;
              try {
                vars = JSON.parse(decodeURIComponent(raw));
              } catch {
                vars = JSON.parse(raw);
              }
              const found = findFolderId(vars);
              if (found) {
                ctx.folder_id = found;
              }
            }
          }
        }
      } catch {
      }
    }
    if (!ctx.folder_id && req.body) {
      const parsed = parseRequestBodyForVariables(req.body);
      if (parsed) {
        const found = findFolderId(parsed);
        if (found) {
          ctx.folder_id = found;
        }
      }
    }
    if (!ctx.folder_id) {
      const contextFolderId = resolveFolderFromContext(req.bookmarkContext);
      if (contextFolderId) {
        ctx.folder_id = contextFolderId;
      }
    }
    if (!ctx.folder_id) {
      const contextFromGlobal = resolveFolderFromContext(resolveBookmarkContextFromGlobal());
      if (contextFromGlobal) {
        ctx.folder_id = contextFromGlobal;
      }
    }
    if (!ctx.folder_id && req.bookmarkContext && typeof req.bookmarkContext === "object") {
      const rawContext = req.bookmarkContext;
      const candidateFromContextUrl = [
        rawContext.folderUrl,
        rawContext.pageUrl,
        rawContext.url,
        rawContext.location,
        rawContext.currentUrl
      ].filter((value) => typeof value === "string" && value.length > 0).map((value) => extractFolderIdFromUrl(value)).find((candidate) => !!candidate);
      if (candidateFromContextUrl) {
        ctx.folder_id = candidateFromContextUrl;
      }
    }
    if (req.bookmarkContext && typeof req.bookmarkContext === "object") {
      const rawContext = req.bookmarkContext;
      if (typeof rawContext.folderUrl === "string") {
        ctx.folder_url = rawContext.folderUrl;
      } else if (typeof rawContext.pageUrl === "string") {
        ctx.folder_url = rawContext.pageUrl;
      } else if (typeof rawContext.url === "string") {
        ctx.folder_url = rawContext.url;
      } else if (typeof rawContext.location === "string") {
        ctx.folder_url = rawContext.location;
      } else if (typeof rawContext.currentUrl === "string") {
        ctx.folder_url = rawContext.currentUrl;
      }
    } else {
      const globalContext = resolveBookmarkContextFromGlobal();
      if (globalContext && typeof globalContext === "object") {
        const rawContext = globalContext;
        if (typeof rawContext.folderUrl === "string") {
          ctx.folder_url = rawContext.folderUrl;
        } else if (typeof rawContext.pageUrl === "string") {
          ctx.folder_url = rawContext.pageUrl;
        } else if (typeof rawContext.url === "string") {
          ctx.folder_url = rawContext.url;
        } else if (typeof rawContext.location === "string") {
          ctx.folder_url = rawContext.location;
        } else if (typeof rawContext.currentUrl === "string") {
          ctx.folder_url = rawContext.currentUrl;
        }
      }
    }
    if (!ctx.folder_id) {
      const pageUrl = typeof location !== "undefined" ? location.href : "";
      if (pageUrl) {
        const fromPageUrl = extractFolderIdFromUrl(pageUrl);
        if (fromPageUrl) {
          ctx.folder_id = fromPageUrl;
          ctx.folder_url = pageUrl;
        }
      }
    }
    if (!ctx.folder_id && req.responseText) {
      const found = resolveFolderFromResponseText(req.responseText);
      if (found) {
        ctx.folder_id = found;
      }
    }
    if (!ctx.folder_id) {
      const contextFromGlobal = resolveFolderFromContext(resolveBookmarkContextFromGlobal());
      if (contextFromGlobal) {
        ctx.folder_id = contextFromGlobal;
      }
    }
    if (!ctx.folder_id && typeof document !== "undefined") {
      ctx.folder_id = extractFolderIdFromUrl(document.URL);
    }
    if (!ctx.folder_id && typeof document !== "undefined") {
      const canonical = document.querySelector('link[rel="canonical"]');
      if (canonical == null ? void 0 : canonical.href) {
        ctx.folder_id = extractFolderIdFromUrl(canonical.href);
      }
    }
    if (!ctx.folder_id && typeof document !== "undefined") {
      const ogUrl = document.querySelector('meta[property="og:url"]');
      const content = (ogUrl == null ? void 0 : ogUrl.content) ?? "";
      if (content) {
        ctx.folder_id = extractFolderIdFromUrl(content);
      }
    }
    if (ctx.folder_id) {
      try {
        const route = new URL(ctx.folder_url || "", "https://x.com");
        if (/\/i\/bookmarks\/?$/.test(route.pathname)) {
          ctx.folder_url = `https://x.com/i/bookmarks/${ctx.folder_id}`;
        }
      } catch {
        if (!ctx.folder_url || /\/i\/bookmarks\/?$/.test(ctx.folder_url)) {
          ctx.folder_url = `https://x.com/i/bookmarks/${ctx.folder_id}`;
        }
      }
      const cached = bookmarkFolderNameCache.get(ctx.folder_id);
      if (cached) ctx.folder_name = cached;
    }
    return ctx;
  }
  function parseBookmarkFoldersSlice(text) {
    var _a2, _b2, _c, _d, _e;
    const changedFolderIds = /* @__PURE__ */ new Set();
    try {
      const json = JSON.parse(text);
      const items = ((_e = (_d = (_c = (_b2 = (_a2 = json.data) == null ? void 0 : _a2.viewer) == null ? void 0 : _b2.user_results) == null ? void 0 : _c.result) == null ? void 0 : _d.bookmark_collections_slice) == null ? void 0 : _e.items) ?? [];
      let changed = false;
      for (const item of items) {
        if (item.id && typeof item.name === "string") {
          const old = bookmarkFolderNameCache.get(item.id);
          if (old !== item.name) {
            bookmarkFolderNameCache.set(item.id, item.name);
            changedFolderIds.add(item.id);
            changed = true;
          }
        }
      }
      if (changed) persistBookmarkFolderNameCache(bookmarkFolderNameCache);
    } catch {
    }
    return changedFolderIds;
  }
  function isBookmarksTimelineRequest(url) {
    try {
      const path = new URL(url, "https://x.com").pathname.toLowerCase();
      const graphqlMatch = path.match(/\/graphql\/[^/]+\/([^/?#]+)/);
      const endpoint = String((graphqlMatch == null ? void 0 : graphqlMatch[1]) || "").toLowerCase();
      if (!endpoint) return false;
      return endpoint === "bookmarks" || endpoint === "bookmarktimeline" || endpoint === "bookmarkfoldertimeline" || endpoint === "bookmarkcollectiontimeline" || endpoint === "bookmarkcollectionstimeline" || endpoint === "bookmarkfoldersslice" || endpoint === "bookmarkfolderslice" || endpoint === "bookmarkcollectionslice" || endpoint === "bookmarkslice";
    } catch {
      return false;
    }
  }
  const BookmarksInterceptor = (req, res, ext) => {
    if (/\/graphql\/.+\/BookmarkFoldersSlice/.test(req.url)) {
      const text = typeof res.responseText === "string" ? res.responseText : "";
      if (text) {
        const changedFolderIds = parseBookmarkFoldersSlice(text);
        if (changedFolderIds.size > 0) {
          queueFolderNameBackfill(changedFolderIds);
        }
      }
      return;
    }
    if (!isBookmarksTimelineRequest(req.url)) {
      return;
    }
    try {
      const explicitRequestFolderId = resolveExplicitRequestFolderId(req);
      const pageFolderId = typeof location !== "undefined" ? extractFolderIdFromUrl(location.href) : null;
      if (pageFolderId && explicitRequestFolderId && explicitRequestFolderId !== pageFolderId) {
        incrementBookmarkDropCounter("bookmarkDropsCrossFolder");
        logger.debug(
          `Bookmarks: skip cross-folder request pageFolder=${pageFolderId}, requestFolder=${explicitRequestFolderId}`
        );
        return;
      }
      const folderCtx = getBookmarkFolderContext({ ...req, responseText: res.responseText });
      const strictFolderId = resolveStrictBookmarkFolderId();
      if (strictFolderId) {
        const strictMatchCandidate = explicitRequestFolderId || pageFolderId || folderCtx.folder_id || null;
        const strictMatchSource = explicitRequestFolderId ? "request" : pageFolderId ? "page" : folderCtx.folder_id ? "context" : "none";
        if (!strictMatchCandidate) {
          incrementBookmarkDropCounter("bookmarkDropsStrictNoExplicitFolder");
          logger.debug(`Bookmarks(strict): skip request without folder evidence: ${req.url}`);
          return;
        }
        if (strictMatchCandidate !== strictFolderId) {
          incrementBookmarkDropCounter("bookmarkDropsStrictFolderMismatch");
          logger.debug(
            `Bookmarks(strict): skip folder mismatch source=${strictMatchSource} candidateFolder=${strictMatchCandidate}, strictFolder=${strictFolderId}`
          );
          return;
        }
        if (!explicitRequestFolderId && strictMatchSource !== "none") {
          logger.debug(
            `Bookmarks(strict): allowing request without explicit folder id via ${strictMatchSource} context`
          );
        }
        if (folderCtx.folder_id !== strictFolderId) {
          folderCtx.folder_id = strictFolderId;
          folderCtx.folder_url = `https://x.com/i/bookmarks/${strictFolderId}`;
        }
      }
      if (!folderCtx.folder_id) {
        incrementBookmarkDropCounter("bookmarkContextUnresolved");
      }
      const newData = extractDataFromResponse(
        res,
        extractTimelineInstructions,
        (entry) => extractTimelineTweet(entry.content.itemContent)
      );
      for (const t of newData) {
        if (t && typeof t === "object") {
          const obj = t;
          if (folderCtx.folder_id) {
            obj.__bookmark_folder_id = folderCtx.folder_id;
          }
          if (folderCtx.folder_name) {
            obj.__bookmark_folder_name = folderCtx.folder_name;
          }
          if (folderCtx.folder_id) {
            obj.__bookmark_folder_name_source = folderCtx.folder_name ? "api" : "id-only";
          }
          if (folderCtx.folder_url) {
            obj.__bookmark_folder_url = folderCtx.folder_url;
          }
        }
      }
      projectTweets(ext.name, newData);
      triggerFolderNameBackfillIfNeeded(ext.name, folderCtx.folder_id, newData);
      logModuleItemsReceived(
        `Bookmarks${folderCtx.folder_id ? ` (folder: ${folderCtx.folder_name ?? folderCtx.folder_id})` : ""}`,
        newData.length
      );
    } catch (err2) {
      logModuleParseFailure("Bookmarks", req, res, err2);
    }
  };
  class BookmarksModule extends Extension {
    constructor() {
      super(...arguments);
      __publicField(this, "name", "BookmarksModule");
      __publicField(this, "type", ExtensionType.TWEET);
    }
    intercept() {
      return BookmarksInterceptor;
    }
    render() {
      return CommonModuleUI;
    }
  }
  const CommunityMembersInterceptor = createModuleInterceptor({
    moduleName: "CommunityMembers",
    match: (req) => /\/graphql\/.+\/(members|moderators)SliceTimeline_Query/.test(req.url),
    parse: (_req, res) => {
      const json = JSON.parse(res.responseText);
      const result = json.data.communityResults.result;
      return (result.members_slice ?? result.moderators_slice).items_results.map((item) => item.result).filter((user) => user.__typename === "User");
    },
    project: (extName, users) => projectUsers(extName, users)
  });
  class CommunityMembersModule extends Extension {
    constructor() {
      super(...arguments);
      __publicField(this, "name", "CommunityMembersModule");
      __publicField(this, "type", ExtensionType.USER);
    }
    intercept() {
      return CommunityMembersInterceptor;
    }
    render() {
      return CommonModuleUI;
    }
  }
  const CommunityTimelineInterceptor = createModuleInterceptor({
    moduleName: "CommunityTimeline",
    match: (req) => /\/graphql\/.+\/Community(Tweets|Media)Timeline/.test(req.url),
    parse: (_req, res) => {
      const json = JSON.parse(res.responseText);
      const result = json.data.communityResults.result;
      const timeline = result.ranked_community_timeline ?? result.community_media_timeline;
      const instructions = timeline.timeline.instructions;
      const newData = [];
      const timelineAddEntriesInstruction = instructions.find(
        (i) => i.type === "TimelineAddEntries"
      );
      const timelineAddEntriesInstructionEntries = (timelineAddEntriesInstruction == null ? void 0 : timelineAddEntriesInstruction.entries) ?? [];
      for (const entry of timelineAddEntriesInstructionEntries) {
        if (isTimelineEntryItem(entry)) {
          const tweet = extractTimelineTweet(entry.content.itemContent);
          if (tweet) {
            newData.push(tweet);
          }
        }
        if (isTimelineEntryCommunitiesGrid(entry)) {
          const tweetsInGrid = entry.content.items.map((i) => extractTimelineTweet(i.item.itemContent)).filter((t) => !!t);
          newData.push(...tweetsInGrid);
        }
      }
      const timelineAddToModuleInstruction = instructions.find(
        (i) => i.type === "TimelineAddToModule"
      );
      if (timelineAddToModuleInstruction == null ? void 0 : timelineAddToModuleInstruction.moduleItems) {
        const tweets = timelineAddToModuleInstruction.moduleItems.map((i) => extractTimelineTweet(i.item.itemContent)).filter((t) => !!t);
        newData.push(...tweets);
      }
      return newData;
    },
    project: (extName, tweets) => projectTweets(extName, tweets)
  });
  class CommunityTimelineModule extends Extension {
    constructor() {
      super(...arguments);
      __publicField(this, "name", "CommunityTimelineModule");
      __publicField(this, "type", ExtensionType.TWEET);
    }
    intercept() {
      return CommunityTimelineInterceptor;
    }
    render() {
      return CommonModuleUI;
    }
  }
  const messagesSignal = signals.signal([]);
  const conversationsCollection = /* @__PURE__ */ new Map();
  const usersCollection = /* @__PURE__ */ new Map();
  const strategies = [
    {
      test: (url) => /\/dm\/inbox_initial_state\.json/.test(url),
      parse: (json) => ({
        entries: json.inbox_initial_state.entries,
        conversations: Object.values(json.inbox_initial_state.conversations),
        users: Object.values(json.inbox_initial_state.users)
      })
    },
    {
      test: (url) => /\/dm\/inbox_timeline\/trusted\.json/.test(url),
      parse: (json) => ({
        entries: json.inbox_timeline.entries,
        conversations: Object.values(json.inbox_timeline.conversations),
        users: Object.values(json.inbox_timeline.users)
      })
    },
    {
      test: (url) => /\/dm\/conversation\/\d+-?\d+\.json/.test(url),
      parse: (json) => ({
        entries: json.conversation_timeline.entries,
        conversations: Object.values(json.conversation_timeline.conversations),
        users: Object.values(json.conversation_timeline.users)
      })
    }
  ];
  const DirectMessagesInterceptor = createModuleInterceptor({
    moduleName: "DirectMessages",
    match: (req) => strategies.some((s) => s.test(req.url)),
    parse: (req, res) => {
      const strategy = strategies.find((s) => s.test(req.url));
      if (!strategy) {
        return { messages: [], conversations: [], users: [] };
      }
      const json = JSON.parse(res.responseText);
      const { entries, conversations, users } = strategy.parse(json);
      const messages = entries.map((entry) => entry.message).filter((message) => !!message);
      return { messages, conversations, users };
    },
    count: (parsed) => parsed.messages.length,
    onSuccess: (parsed) => {
      messagesSignal.value = [...messagesSignal.value, ...parsed.messages];
      parsed.conversations.filter(Boolean).forEach((c) => conversationsCollection.set(c.conversation_id, c));
      parsed.users.filter(Boolean).forEach((user) => usersCollection.set(user.id_str, user));
    }
  });
  function getUserScreeNameFromId(id2) {
    const user = id2 ? usersCollection.get(id2) : null;
    return user ? user.screen_name : "";
  }
  function getConversationTypeFromId(id2) {
    const conversation = id2 ? conversationsCollection.get(id2) : null;
    return conversation ? conversation.type : "";
  }
  function extractMessageMedia(message) {
    var _a2, _b2, _c;
    return [
      (_a2 = message.message_data.attachment) == null ? void 0 : _a2.photo,
      (_b2 = message.message_data.attachment) == null ? void 0 : _b2.video,
      (_c = message.message_data.attachment) == null ? void 0 : _c.animated_gif
    ].filter((m) => !!m);
  }
  const columnHelper = tableCore.createColumnHelper();
  const columns = [
    columnHelper.display({
      id: "select",
      meta: { exportable: false },
      header: ({ table }) => /* @__PURE__ */ u(
        "input",
        {
          type: "checkbox",
          class: "checkbox checkbox-sm align-middle",
          checked: table.getIsAllRowsSelected(),
          indeterminate: table.getIsSomeRowsSelected(),
          onChange: table.getToggleAllRowsSelectedHandler()
        }
      ),
      cell: ({ row }) => /* @__PURE__ */ u(
        "input",
        {
          type: "checkbox",
          class: "checkbox checkbox-sm",
          checked: row.getIsSelected(),
          disabled: !row.getCanSelect(),
          indeterminate: row.getIsSomeSelected(),
          onChange: row.getToggleSelectedHandler()
        }
      )
    }),
    columnHelper.accessor("id", {
      meta: { exportKey: "id", exportHeader: "ID" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "ID" }),
      cell: (info) => /* @__PURE__ */ u("p", { class: "w-20 break-all font-mono text-xs", children: info.getValue() })
    }),
    columnHelper.accessor("time", {
      meta: {
        exportKey: "time",
        exportHeader: "Date",
        exportValue: (row) => formatDateTime(dayjs(+row.original.time), appOptionsManager.get("dateTimeFormat"))
      },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Date" }),
      cell: (info) => /* @__PURE__ */ u("p", { class: "w-24", children: formatDateTime(+info.getValue(), appOptionsManager.get("dateTimeFormat")) })
    }),
    columnHelper.accessor("message_data.text", {
      meta: {
        exportKey: "text",
        exportHeader: "Content"
      },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Content" }),
      cell: (info) => {
        var _a2;
        return /* @__PURE__ */ u("div", { children: /* @__PURE__ */ u(
          "p",
          {
            class: "w-60 whitespace-pre-wrap",
            dangerouslySetInnerHTML: {
              __html: strEntitiesToHTML(
                info.row.original.message_data.text,
                (_a2 = info.row.original.message_data.entities) == null ? void 0 : _a2.urls
              )
            }
          }
        ) });
      }
    }),
    columnHelper.accessor((row) => extractMessageMedia(row), {
      id: "media",
      meta: {
        exportKey: "media",
        exportHeader: "Media",
        exportValue: (row) => extractMessageMedia(row.original).map((media) => ({
          type: media.type,
          url: media.url,
          thumbnail: formatTwitterImage(media.media_url_https, "thumb"),
          original: getMediaOriginalUrl(media),
          ext_alt_text: media.ext_alt_text
        }))
      },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Media" }),
      cell: (info) => /* @__PURE__ */ u(
        MediaDisplayColumn,
        {
          data: info.getValue().filter((m) => !!m),
          onClick: (media) => {
            var _a2;
            return (_a2 = info.table.options.meta) == null ? void 0 : _a2.setMediaPreview(getMediaOriginalUrl(media));
          }
        }
      )
    }),
    columnHelper.accessor((row) => getUserScreeNameFromId(row.message_data.sender_id), {
      id: "sender",
      meta: { exportKey: "sender", exportHeader: "Sender" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Sender" }),
      cell: (info) => /* @__PURE__ */ u("p", { class: "whitespace-pre", children: /* @__PURE__ */ u("a", { class: "link", target: "_blank", href: `https://twitter.com/${info.getValue()}`, children: [
        "@",
        info.getValue()
      ] }) })
    }),
    columnHelper.accessor((row) => getUserScreeNameFromId(row.message_data.recipient_id), {
      id: "recipient",
      meta: { exportKey: "recipient", exportHeader: "Recipient" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Recipient" }),
      cell: (info) => /* @__PURE__ */ u("p", { class: "whitespace-pre", children: info.getValue() ? /* @__PURE__ */ u("a", { class: "link", target: "_blank", href: `https://twitter.com/${info.getValue()}`, children: [
        "@",
        info.getValue()
      ] }) : "N/A" })
    }),
    columnHelper.accessor("conversation_id", {
      meta: { exportKey: "conversation_id", exportHeader: "Conversation ID" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Conversation ID" }),
      cell: (info) => /* @__PURE__ */ u("p", { class: "w-20 break-all font-mono text-xs", children: info.getValue() })
    }),
    columnHelper.accessor((row) => getConversationTypeFromId(row.conversation_id), {
      id: "conversation_type",
      meta: { exportKey: "conversation_type", exportHeader: "Conversation Type" },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Conversation Type" }),
      cell: (info) => /* @__PURE__ */ u("p", { class: "whitespace-pre", children: info.getValue() })
    }),
    columnHelper.display({
      id: "actions",
      meta: { exportable: false },
      header: () => /* @__PURE__ */ u(Trans, { i18nKey: "Actions" }),
      cell: (info) => /* @__PURE__ */ u("div", { class: "flex flex-row items-start space-x-1", children: /* @__PURE__ */ u(
        "button",
        {
          onClick: () => {
            var _a2;
            return (_a2 = info.table.options.meta) == null ? void 0 : _a2.setRawDataPreview(info.row.original);
          },
          class: "btn btn-xs btn-neutral whitespace-nowrap",
          children: /* @__PURE__ */ u(Trans, { i18nKey: "Details" })
        }
      ) })
    })
  ];
  function DirectMessagesUI() {
    const { t } = useTranslation();
    const [showModal, toggleShowModal] = useToggle();
    const title = t("DirectMessages");
    const count = messagesSignal.value.length;
    return /* @__PURE__ */ u(
      ExtensionPanel,
      {
        title,
        description: `${t("Captured:")} ${count}`,
        active: !!count && count > 0,
        onClick: toggleShowModal,
        indicatorColor: "bg-accent",
        panelClass: "border-l-2 border-accent/50 pl-2",
        children: /* @__PURE__ */ u(
          Modal,
          {
            class: "max-w-4xl md:max-w-screen-md sm:max-w-screen-sm min-h-[512px]",
            title,
            show: showModal,
            onClose: toggleShowModal,
            children: /* @__PURE__ */ u(
              BaseTableView,
              {
                title,
                records: messagesSignal.value,
                columns,
                clear: () => messagesSignal.value = []
              }
            )
          }
        )
      }
    );
  }
  class DirectMessagesModule extends Extension {
    constructor() {
      super(...arguments);
      __publicField(this, "name", "DirectMessagesModule");
      __publicField(this, "type", ExtensionType.CUSTOM);
    }
    intercept() {
      return DirectMessagesInterceptor;
    }
    render() {
      return DirectMessagesUI;
    }
  }
  function parseRequestVariables$1(url) {
    try {
      const parsedUrl = new URL(url, "https://x.com");
      const raw = parsedUrl.searchParams.get("variables");
      if (!raw) return null;
      try {
        return JSON.parse(decodeURIComponent(raw));
      } catch {
        return JSON.parse(raw);
      }
    } catch {
      return null;
    }
  }
  function resolveSubjectFallback$1(req) {
    const variables = parseRequestVariables$1(req.url);
    const subjectUserId = String(
      (variables == null ? void 0 : variables.userId) || (variables == null ? void 0 : variables.user_id) || (variables == null ? void 0 : variables.rest_id) || (variables == null ? void 0 : variables.profileUserId) || ""
    ).trim();
    const subjectScreenName = String(
      (variables == null ? void 0 : variables.screen_name) || (variables == null ? void 0 : variables.screenName) || (variables == null ? void 0 : variables.userScreenName) || ""
    ).trim();
    return {
      rest_id: subjectUserId || void 0,
      screen_name: subjectScreenName || void 0
    };
  }
  function parseFollowers(req, res) {
    var _a2, _b2, _c, _d, _e, _f;
    const json = JSON.parse(res.responseText);
    const result = (_b2 = (_a2 = json == null ? void 0 : json.data) == null ? void 0 : _a2.user) == null ? void 0 : _b2.result;
    const instructions = (_d = (_c = result == null ? void 0 : result.timeline) == null ? void 0 : _c.timeline) == null ? void 0 : _d.instructions;
    if (!Array.isArray(instructions)) {
      throw new Error("Followers response missing timeline instructions");
    }
    const users = [];
    for (const instruction of instructions) {
      if (instruction.type === "TimelineAddEntries") {
        for (const entry of instruction.entries || []) {
          if (isTimelineEntryUser(entry)) {
            const parsed = extractTimelineUser(entry.content.itemContent);
            if (parsed) {
              users.push(parsed);
            }
            continue;
          }
          if (isTimelineEntryModule(entry)) {
            for (const item of entry.content.items || []) {
              if (item.item.itemContent.__typename !== "TimelineUser") continue;
              const parsed = extractTimelineUser(item.item.itemContent);
              if (parsed) {
                users.push(parsed);
              }
            }
          }
        }
      }
      if (instruction.type === "TimelineAddToModule") {
        for (const item of instruction.moduleItems || []) {
          if (item.item.itemContent.__typename !== "TimelineUser") continue;
          const parsed = extractTimelineUser(item.item.itemContent);
          if (parsed) {
            users.push(parsed);
          }
        }
      }
    }
    const subjectFallback = resolveSubjectFallback$1(req);
    const subject = result && result.__typename === "User" ? {
      ...result,
      rest_id: String(result.rest_id || subjectFallback.rest_id || "").trim(),
      core: {
        ...result.core,
        screen_name: String(
          ((_e = result.core) == null ? void 0 : _e.screen_name) || subjectFallback.screen_name || ""
        ).trim()
      }
    } : null;
    const subjectUserId = String((subject == null ? void 0 : subject.rest_id) || subjectFallback.rest_id || "").trim();
    const subjectScreenName = String(
      ((_f = subject == null ? void 0 : subject.core) == null ? void 0 : _f.screen_name) || subjectFallback.screen_name || ""
    ).trim();
    const edges = subjectUserId ? users.map((user) => {
      var _a3;
      return {
        id: `FollowersModule-follower-${subjectUserId}-${user.rest_id}`,
        extension: "FollowersModule",
        relation_type: "follower",
        subject_user_id: subjectUserId,
        subject_screen_name: subjectScreenName || void 0,
        related_user_id: user.rest_id,
        related_screen_name: (_a3 = user.core) == null ? void 0 : _a3.screen_name,
        observed_at: Date.now(),
        provenance_surface: "followers"
      };
    }) : [];
    return { subject, users, edges };
  }
  const FollowersInterceptor = createModuleInterceptor({
    moduleName: "Followers",
    match: (req) => /\/graphql\/.+\/(BlueVerified)*Followers/.test(req.url),
    parse: (req, res) => parseFollowers(req, res),
    count: (parsed) => parsed.users.length,
    project: (extName, parsed) => projectUsersWithEdges(extName, parsed.users, parsed.edges)
  });
  class FollowersModule extends Extension {
    constructor() {
      super(...arguments);
      __publicField(this, "name", "FollowersModule");
      __publicField(this, "type", ExtensionType.USER);
    }
    intercept() {
      return FollowersInterceptor;
    }
    render() {
      return CommonModuleUI;
    }
  }
  function parseRequestVariables(url) {
    try {
      const parsedUrl = new URL(url, "https://x.com");
      const raw = parsedUrl.searchParams.get("variables");
      if (!raw) return null;
      try {
        return JSON.parse(decodeURIComponent(raw));
      } catch {
        return JSON.parse(raw);
      }
    } catch {
      return null;
    }
  }
  function resolveSubjectFallback(req) {
    const variables = parseRequestVariables(req.url);
    const subjectUserId = String(
      (variables == null ? void 0 : variables.userId) || (variables == null ? void 0 : variables.user_id) || (variables == null ? void 0 : variables.rest_id) || (variables == null ? void 0 : variables.profileUserId) || ""
    ).trim();
    const subjectScreenName = String(
      (variables == null ? void 0 : variables.screen_name) || (variables == null ? void 0 : variables.screenName) || (variables == null ? void 0 : variables.userScreenName) || ""
    ).trim();
    return {
      rest_id: subjectUserId || void 0,
      screen_name: subjectScreenName || void 0
    };
  }
  function parseFollowing(req, res) {
    var _a2, _b2, _c, _d, _e, _f;
    const json = JSON.parse(res.responseText);
    const result = (_b2 = (_a2 = json == null ? void 0 : json.data) == null ? void 0 : _a2.user) == null ? void 0 : _b2.result;
    const instructions = (_d = (_c = result == null ? void 0 : result.timeline) == null ? void 0 : _c.timeline) == null ? void 0 : _d.instructions;
    if (!Array.isArray(instructions)) {
      throw new Error("Following response missing timeline instructions");
    }
    const users = [];
    for (const instruction of instructions) {
      if (instruction.type === "TimelineAddEntries") {
        for (const entry of instruction.entries || []) {
          if (isTimelineEntryUser(entry)) {
            const parsed = extractTimelineUser(entry.content.itemContent);
            if (parsed) {
              users.push(parsed);
            }
            continue;
          }
          if (isTimelineEntryModule(entry)) {
            for (const item of entry.content.items || []) {
              if (item.item.itemContent.__typename !== "TimelineUser") continue;
              const parsed = extractTimelineUser(item.item.itemContent);
              if (parsed) {
                users.push(parsed);
              }
            }
          }
        }
      }
      if (instruction.type === "TimelineAddToModule") {
        for (const item of instruction.moduleItems || []) {
          if (item.item.itemContent.__typename !== "TimelineUser") continue;
          const parsed = extractTimelineUser(item.item.itemContent);
          if (parsed) {
            users.push(parsed);
          }
        }
      }
    }
    const subjectFallback = resolveSubjectFallback(req);
    const subject = result && result.__typename === "User" ? {
      ...result,
      rest_id: String(result.rest_id || subjectFallback.rest_id || "").trim(),
      core: {
        ...result.core,
        screen_name: String(
          ((_e = result.core) == null ? void 0 : _e.screen_name) || subjectFallback.screen_name || ""
        ).trim()
      }
    } : null;
    const subjectUserId = String((subject == null ? void 0 : subject.rest_id) || subjectFallback.rest_id || "").trim();
    const subjectScreenName = String(
      ((_f = subject == null ? void 0 : subject.core) == null ? void 0 : _f.screen_name) || subjectFallback.screen_name || ""
    ).trim();
    const edges = subjectUserId ? users.map((user) => {
      var _a3;
      return {
        id: `FollowingModule-following-${subjectUserId}-${user.rest_id}`,
        extension: "FollowingModule",
        relation_type: "following",
        subject_user_id: subjectUserId,
        subject_screen_name: subjectScreenName || void 0,
        related_user_id: user.rest_id,
        related_screen_name: (_a3 = user.core) == null ? void 0 : _a3.screen_name,
        observed_at: Date.now(),
        provenance_surface: "following"
      };
    }) : [];
    return { subject, users, edges };
  }
  const FollowingInterceptor = createModuleInterceptor({
    moduleName: "Following",
    match: (req) => /\/graphql\/.+\/Following/.test(req.url),
    parse: (req, res) => parseFollowing(req, res),
    count: (parsed) => parsed.users.length,
    project: (extName, parsed) => projectUsersWithEdges(extName, parsed.users, parsed.edges)
  });
  class FollowingModule extends Extension {
    constructor() {
      super(...arguments);
      __publicField(this, "name", "FollowingModule");
      __publicField(this, "type", ExtensionType.USER);
    }
    intercept() {
      return FollowingInterceptor;
    }
    render() {
      return CommonModuleUI;
    }
  }
  const HomeTimelineInterceptor = createModuleInterceptor({
    moduleName: "HomeTimeline",
    match: (req) => /\/graphql\/.+\/Home(Latest)?Timeline/.test(req.url),
    parse: (_req, res) => extractDataFromResponse(
      res,
      (json) => json.data.home.home_timeline_urt.instructions,
      (entry) => extractTimelineTweet(entry.content.itemContent)
    ),
    project: (extName, tweets) => projectTweets(extName, tweets)
  });
  class HomeTimelineModule extends Extension {
    constructor() {
      super(...arguments);
      __publicField(this, "name", "HomeTimelineModule");
      __publicField(this, "type", ExtensionType.TWEET);
    }
    intercept() {
      return HomeTimelineInterceptor;
    }
    render() {
      return CommonModuleUI;
    }
  }
  function isVisibleElement(node) {
    if (!(node instanceof HTMLElement)) return false;
    const rect = node.getBoundingClientRect();
    return rect.width > 0 && rect.height > 0 && rect.bottom > 0 && rect.top < window.innerHeight;
  }
  function uniqStrings(values) {
    const seen2 = /* @__PURE__ */ new Set();
    const out = [];
    for (const value of values) {
      const normalized = String(value || "").trim();
      if (!normalized) continue;
      if (seen2.has(normalized)) continue;
      seen2.add(normalized);
      out.push(normalized);
    }
    return out;
  }
  function normalizeText(value) {
    return String(value || "").replace(/\s+/g, " ").replace(/\u00a0/g, " ").trim();
  }
  function selectTweetArticle(tweetId) {
    if (typeof document === "undefined" || !tweetId) return null;
    const anchors = [...document.querySelectorAll(`a[href*="/status/${tweetId}"]`)];
    const visibleArticle = anchors.map((anchor) => anchor.closest("article")).find((article) => isVisibleElement(article));
    if (visibleArticle instanceof HTMLElement) {
      return visibleArticle;
    }
    const firstArticle = anchors.map((anchor) => anchor.closest("article")).find(Boolean);
    return firstArticle instanceof HTMLElement ? firstArticle : null;
  }
  function extractScreenName(article, tweetId) {
    const statusAnchor = article.querySelector(
      `a[href*="/status/${tweetId}"]`
    );
    const href = (statusAnchor == null ? void 0 : statusAnchor.getAttribute("href")) || "";
    const match = href.match(/^\/([^/?#]+)\/status\//);
    return (match == null ? void 0 : match[1]) || "unknown";
  }
  function extractDisplayName(article, screenName) {
    var _a2;
    const explicit = ((_a2 = article.querySelector('[data-testid="User-Name"] span')) == null ? void 0 : _a2.textContent) || "";
    const normalized = normalizeText(explicit);
    if (normalized) return normalized.replace(/^@/, "");
    return screenName;
  }
  function extractTweetText(article) {
    const tweetText = uniqStrings(
      [...article.querySelectorAll('[data-testid="tweetText"], div[lang]')].map(
        (node) => normalizeText(node.textContent || "")
      )
    );
    if (tweetText.length) {
      return tweetText.join("\n\n");
    }
    const fallback = normalizeText(article.innerText || article.textContent || "");
    return fallback;
  }
  function toUtcStringFromDatetime(value) {
    if (!value) return (/* @__PURE__ */ new Date()).toUTCString();
    const parsed = Date.parse(value);
    if (!Number.isFinite(parsed)) return (/* @__PURE__ */ new Date()).toUTCString();
    return new Date(parsed).toUTCString();
  }
  function extractProfileImage(article) {
    const candidate = [...article.querySelectorAll("img")].find((img) => {
      const src = img.getAttribute("src") || "";
      return /profile_images/.test(src);
    });
    return (candidate == null ? void 0 : candidate.getAttribute("src")) || "";
  }
  function extractMedia(article, tweetId) {
    const mediaImages = [...article.querySelectorAll("img")].map((img) => ({
      src: img.getAttribute("src") || "",
      width: img.naturalWidth || img.clientWidth || 0,
      height: img.naturalHeight || img.clientHeight || 0,
      alt: img.getAttribute("alt") || ""
    })).filter(({ src, alt }) => {
      if (!src) return false;
      if (/profile_images/.test(src)) return false;
      if (/emoji/.test(src)) return false;
      if (!/twimg\.com\//.test(src)) return false;
      if (alt === "Image") return true;
      return /\/media\//.test(src) || /name=/.test(src);
    });
    const uniq = uniqStrings(mediaImages.map((item) => item.src));
    return uniq.slice(0, 8).map((src, index) => {
      const meta = mediaImages.find((item) => item.src === src);
      return {
        type: "photo",
        media_url_https: src,
        media_url: src,
        id_str: `${tweetId}${index}`,
        media_key: `dom:${tweetId}:${index}`,
        indices: [0, 0],
        url: src,
        display_url: src,
        expanded_url: src,
        sizes: {
          medium: { w: (meta == null ? void 0 : meta.width) || 1200, h: (meta == null ? void 0 : meta.height) || 675, resize: "fit" },
          large: { w: (meta == null ? void 0 : meta.width) || 1200, h: (meta == null ? void 0 : meta.height) || 675, resize: "fit" },
          small: { w: (meta == null ? void 0 : meta.width) || 680, h: (meta == null ? void 0 : meta.height) || 382, resize: "fit" },
          thumb: { w: 150, h: 150, resize: "crop" }
        },
        original_info: {
          width: (meta == null ? void 0 : meta.width) || 1200,
          height: (meta == null ? void 0 : meta.height) || 675
        },
        features: {}
      };
    });
  }
  function buildSyntheticUser(screenName, displayName, profileImageUrl) {
    return {
      __typename: "User",
      id: screenName || "unknown",
      rest_id: screenName || "unknown",
      affiliates_highlighted_label: null,
      has_graduated_access: false,
      is_blue_verified: false,
      profile_image_shape: "Circle",
      legacy: {
        default_profile: false,
        default_profile_image: !profileImageUrl,
        description: "",
        entities: { description: { urls: [] } },
        fast_followers_count: 0,
        favourites_count: 0,
        followers_count: 0,
        friends_count: 0,
        has_custom_timelines: false,
        is_translator: false,
        listed_count: 0,
        media_count: 0,
        normal_followers_count: 0,
        pinned_tweet_ids_str: [],
        possibly_sensitive: false,
        profile_interstitial_type: "",
        statuses_count: 0,
        translator_type: "",
        want_retweets: true,
        withheld_in_countries: []
      },
      avatar: {
        image_url: profileImageUrl
      },
      core: {
        name: displayName || screenName || "unknown",
        screen_name: screenName || "unknown",
        created_at: (/* @__PURE__ */ new Date(0)).toUTCString()
      },
      dm_permissions: { can_dm: false },
      location: { location: "" },
      media_permissions: { can_media_tag: false },
      privacy: { protected: false },
      verification: { verified: false },
      relationship_perspectives: { following: false, followed_by: false },
      twe_private_fields: {
        created_at: 0,
        updated_at: Date.now()
      }
    };
  }
  function buildSyntheticTweetFromDomSnapshot(input) {
    var _a2;
    const tweetId = String(input.tweetId || "").trim();
    if (!tweetId) return null;
    const article = selectTweetArticle(tweetId);
    if (!article) return null;
    const screenName = extractScreenName(article, tweetId);
    const displayName = extractDisplayName(article, screenName);
    const fullText = extractTweetText(article);
    const createdAtIso = ((_a2 = article.querySelector("time")) == null ? void 0 : _a2.getAttribute("datetime")) || null;
    const createdAt = toUtcStringFromDatetime(createdAtIso);
    const profileImageUrl = extractProfileImage(article);
    const media = extractMedia(article, tweetId);
    const folderId = input.bookmarkFolderId ? String(input.bookmarkFolderId) : "";
    const folderName = input.bookmarkFolderName ? String(input.bookmarkFolderName).trim() : "";
    const tweet = {
      __typename: "Tweet",
      rest_id: tweetId,
      core: {
        user_results: {
          result: buildSyntheticUser(screenName, displayName, profileImageUrl)
        }
      },
      edit_control: {
        edit_tweet_ids: [tweetId],
        editable_until_msecs: "0",
        is_edit_eligible: false,
        edits_remaining: "0"
      },
      is_translatable: false,
      views: {
        count: "0",
        state: "Enabled"
      },
      source: "dom-snapshot",
      legacy: {
        bookmark_count: 0,
        bookmarked: input.bookmarked === true,
        created_at: createdAt,
        conversation_id_str: tweetId,
        display_text_range: [0, fullText.length],
        entities: {
          media: media.length ? media : void 0,
          user_mentions: [],
          urls: [],
          hashtags: [],
          symbols: [],
          timestamps: []
        },
        extended_entities: media.length ? { media } : void 0,
        favorite_count: 0,
        favorited: false,
        full_text: fullText,
        is_quote_status: false,
        lang: "",
        possibly_sensitive: false,
        possibly_sensitive_editable: false,
        quote_count: 0,
        reply_count: 0,
        retweet_count: 0,
        retweeted: false,
        user_id_str: screenName || "unknown",
        id_str: tweetId
      },
      twe_private_fields: {
        created_at: Date.parse(createdAt) || Date.now(),
        updated_at: Date.now(),
        media_count: media.length
      },
      ...folderId ? {
        __bookmark_folder_id: folderId,
        __bookmark_folder_url: `https://x.com/i/bookmarks/${folderId}`,
        __bookmark_folder_name_source: folderName ? "api" : "id-only",
        ...folderName ? { __bookmark_folder_name: folderName } : {}
      } : {}
    };
    return tweet;
  }
  const GRAPHQL_OP_RE = /\/graphql\/[^/]+\/([^/?#]+)/i;
  const ID_VALUE_RE = /^\d{5,25}$/;
  const TWEET_ID_KEYS = /* @__PURE__ */ new Set(["tweetid", "tweetidstr", "statusid", "statusidstr", "id", "restid"]);
  const USER_ID_KEYS = /* @__PURE__ */ new Set(["userid", "targetuserid", "useridstr", "sourceuserid"]);
  const FOLDER_ID_KEYS = /* @__PURE__ */ new Set([
    "bookmarkcollectionid",
    "bookmarkfolderid",
    "bookmarkcollection",
    "bookmarkfolder",
    "folderid",
    "collectionid"
  ]);
  const BOOKMARKS_EXTENSION_NAME = "BookmarksModule";
  const LIKES_EXTENSION_NAME = "LikesModule";
  const FOLLOWING_EXTENSION_NAME = "FollowingModule";
  const BOOKMARK_FOLDER_CACHE_STORAGE_KEY = "twe_bookmark_folder_name_cache_v1";
  function normalizeKey(key) {
    return String(key || "").toLowerCase().replace(/[^a-z0-9]/g, "");
  }
  function toId(value) {
    if (typeof value === "number" && Number.isFinite(value)) {
      const normalized = String(Math.trunc(value));
      return ID_VALUE_RE.test(normalized) ? normalized : null;
    }
    const text = String(value ?? "").trim();
    if (!text) return null;
    return ID_VALUE_RE.test(text) ? text : null;
  }
  function extractOperationName(url) {
    const match = String(url || "").match(GRAPHQL_OP_RE);
    return (match == null ? void 0 : match[1]) || "";
  }
  function detectInteraction(url) {
    const path = String(url || "").toLowerCase();
    const op = extractOperationName(url).toLowerCase();
    const source = `${path} ${op}`;
    if (/(bookmarktweettofolder|addbookmarktofolder|createbookmarktofolder|bookmarktofolder)/.test(
      source
    )) {
      return { kind: "bookmark_folder_add", targetType: "folder" };
    }
    if (/(createbookmark|addbookmark|bookmarkcreate|bookmark\/entries\/add)/.test(source)) {
      return { kind: "bookmark_add", targetType: "tweet" };
    }
    if (/(deletebookmark|removebookmark|destroybookmark|bookmark\/entries\/(remove|delete|destroy))/.test(
      source
    )) {
      return { kind: "bookmark_remove", targetType: "tweet" };
    }
    if (/(favoritetweet|createfavorite|like(add|tweet)?\b)/.test(source)) {
      return { kind: "like_add", targetType: "tweet" };
    }
    if (/(unfavoritetweet|deletefavorite|destroyfavorite|unlike(tweet)?\b)/.test(source)) {
      return { kind: "like_remove", targetType: "tweet" };
    }
    if (/(followuser|createfollow|friendships\/create(\.json)?\b)/.test(source)) {
      return { kind: "follow_add", targetType: "user" };
    }
    if (/(unfollowuser|destroyfollow|friendships\/destroy(\.json)?\b)/.test(source)) {
      return { kind: "follow_remove", targetType: "user" };
    }
    if (/(createretweet|statuses\/retweet\/)/.test(source)) {
      return { kind: "retweet_add", targetType: "tweet" };
    }
    if (/(deleteretweet|unretweet|statuses\/unretweet\/)/.test(source)) {
      return { kind: "retweet_remove", targetType: "tweet" };
    }
    return null;
  }
  function safeJsonParse(input) {
    try {
      return JSON.parse(input);
    } catch {
      return null;
    }
  }
  function parseRequestBody(body) {
    if (!body) return {};
    const trimmed = body.trim();
    if (!trimmed) return {};
    if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
      const parsed = safeJsonParse(trimmed);
      if (parsed && typeof parsed === "object") {
        return parsed;
      }
      return {};
    }
    const out = {};
    try {
      const params = new URLSearchParams(trimmed);
      for (const [key, value] of params.entries()) {
        const parsedValue = value.startsWith("{") || value.startsWith("[") ? safeJsonParse(value) ?? value : value;
        if (key in out) {
          const existing = out[key];
          if (Array.isArray(existing)) {
            existing.push(parsedValue);
          } else {
            out[key] = [existing, parsedValue];
          }
        } else {
          out[key] = parsedValue;
        }
      }
    } catch {
      return {};
    }
    return out;
  }
  function collectIdsByKeys(node, matcher, out, depth = 0, seen2 = /* @__PURE__ */ new Set()) {
    if (depth > 8 || !node) return;
    if (Array.isArray(node)) {
      for (const item of node) {
        collectIdsByKeys(item, matcher, out, depth + 1, seen2);
      }
      return;
    }
    if (typeof node !== "object") {
      return;
    }
    const obj = node;
    if (seen2.has(obj)) return;
    seen2.add(obj);
    for (const [key, value] of Object.entries(obj)) {
      const normalized = normalizeKey(key);
      if (matcher(normalized)) {
        if (Array.isArray(value)) {
          for (const item of value) {
            const id2 = toId(item);
            if (id2) out.add(id2);
          }
        } else {
          const id2 = toId(value);
          if (id2) out.add(id2);
        }
      }
      collectIdsByKeys(value, matcher, out, depth + 1, seen2);
    }
  }
  function extractIds(body, url) {
    const tweetIds = /* @__PURE__ */ new Set();
    const userIds = /* @__PURE__ */ new Set();
    const folderIds = /* @__PURE__ */ new Set();
    collectIdsByKeys(body, (k) => TWEET_ID_KEYS.has(k), tweetIds);
    collectIdsByKeys(body, (k) => USER_ID_KEYS.has(k), userIds);
    collectIdsByKeys(body, (k) => FOLDER_ID_KEYS.has(k), folderIds);
    const path = String(url || "");
    const retweetPathMatch = path.match(/\/(?:retweet|unretweet)\/(\d{5,25})/i);
    if (retweetPathMatch == null ? void 0 : retweetPathMatch[1]) {
      tweetIds.add(retweetPathMatch[1]);
    }
    return {
      tweetIds: [...tweetIds],
      userIds: [...userIds],
      folderIds: [...folderIds]
    };
  }
  function pickTargets(match, ids) {
    if (match.targetType === "tweet" && ids.tweetIds.length) return ids.tweetIds;
    if (match.targetType === "user" && ids.userIds.length) return ids.userIds;
    if (match.targetType === "folder" && ids.folderIds.length) return ids.folderIds;
    if (ids.tweetIds.length) return ids.tweetIds;
    if (ids.userIds.length) return ids.userIds;
    if (ids.folderIds.length) return ids.folderIds;
    return ["unknown"];
  }
  function toSafeKeyPart(value) {
    return value.replace(/[^a-zA-Z0-9:_-]/g, "_").slice(0, 128);
  }
  function readBookmarkFolderNameFromCache(folderId) {
    if (!folderId) return null;
    try {
      if (typeof localStorage === "undefined") return null;
      const raw = localStorage.getItem(BOOKMARK_FOLDER_CACHE_STORAGE_KEY);
      if (!raw) return null;
      const entries = JSON.parse(raw);
      if (!Array.isArray(entries)) return null;
      for (const entry of entries) {
        if (Array.isArray(entry) && entry.length === 2 && String(entry[0] || "").trim() === folderId && typeof entry[1] === "string") {
          return entry[1];
        }
      }
    } catch {
      return null;
    }
    return null;
  }
  function patchTweetFlags(tweet, state) {
    const next = {
      ...tweet,
      legacy: {
        ...tweet.legacy
      }
    };
    if (typeof state.bookmarked === "boolean") {
      next.legacy.bookmarked = state.bookmarked;
    }
    if (typeof state.favorited === "boolean") {
      next.legacy.favorited = state.favorited;
    }
    if (typeof state.retweeted === "boolean") {
      next.legacy.retweeted = state.retweeted;
    }
    if (state.bookmarkFolderId !== void 0) {
      if (state.bookmarkFolderId) {
        next.__bookmark_folder_id = state.bookmarkFolderId;
        next.__bookmark_folder_url = `https://x.com/i/bookmarks/${state.bookmarkFolderId}`;
        next.__bookmark_folder_name_source = state.bookmarkFolderName ? "api" : "id-only";
        if (state.bookmarkFolderName) {
          next.__bookmark_folder_name = state.bookmarkFolderName;
        } else {
          delete next.__bookmark_folder_name;
        }
      } else {
        delete next.__bookmark_folder_id;
        delete next.__bookmark_folder_name;
        delete next.__bookmark_folder_name_source;
        delete next.__bookmark_folder_url;
      }
    }
    return next;
  }
  function patchUserFollowing(user, following) {
    return {
      ...user,
      relationship_perspectives: {
        ...user.relationship_perspectives || {},
        following
      }
    };
  }
  async function buildBookmarkHydrationTasks(tweetIds, state) {
    const ids = tweetIds.filter(Boolean);
    if (!ids.length) {
      return { hydratedIds: [], tasks: [] };
    }
    const hydratedTweets = [];
    for (const tweetId of ids) {
      const synthetic = buildSyntheticTweetFromDomSnapshot({
        tweetId,
        bookmarked: state.bookmarked,
        bookmarkFolderId: state.bookmarkFolderId,
        bookmarkFolderName: state.bookmarkFolderName
      });
      if (synthetic) {
        hydratedTweets.push(synthetic);
      }
    }
    if (!hydratedTweets.length) {
      return { hydratedIds: [], tasks: [] };
    }
    await dbProxy.extAddTweets(BOOKMARKS_EXTENSION_NAME, hydratedTweets);
    return {
      hydratedIds: hydratedTweets.map((tweet) => tweet.rest_id),
      tasks: []
    };
  }
  async function buildMirrorTasks(interaction, ids) {
    const folderId = ids.folderIds[0] ?? null;
    const folderName = readBookmarkFolderNameFromCache(folderId);
    switch (interaction.kind) {
      case "bookmark_add":
        if (!ids.tweetIds.length) return [];
        {
          const hydrated = await buildBookmarkHydrationTasks(ids.tweetIds, {
            bookmarked: true
          });
          const remainingIds = ids.tweetIds.filter(
            (tweetId) => !hydrated.hydratedIds.includes(tweetId)
          );
          return [
            ...hydrated.tasks,
            ...remainingIds.length ? [
              dbProxy.extAddTweetCaptureIds(
                BOOKMARKS_EXTENSION_NAME,
                remainingIds,
                (tweet) => patchTweetFlags(tweet, {
                  bookmarked: true
                })
              )
            ] : []
          ];
        }
      case "bookmark_folder_add":
        if (!ids.tweetIds.length) return [];
        {
          const hydrated = await buildBookmarkHydrationTasks(ids.tweetIds, {
            bookmarked: true,
            bookmarkFolderId: folderId ?? void 0,
            bookmarkFolderName: folderName
          });
          const hydratedSet = new Set(hydrated.hydratedIds);
          const remainingIds = ids.tweetIds.filter((tweetId) => !hydratedSet.has(tweetId));
          return [
            ...hydrated.tasks,
            ...remainingIds.length ? [
              dbProxy.extAddTweetCaptureIds(
                BOOKMARKS_EXTENSION_NAME,
                remainingIds,
                (tweet) => patchTweetFlags(tweet, {
                  bookmarked: true,
                  bookmarkFolderId: folderId ?? void 0,
                  bookmarkFolderName: folderName
                })
              )
            ] : []
          ];
        }
      case "bookmark_remove":
        if (!ids.tweetIds.length) return [];
        return [
          dbProxy.extRemoveTweetCaptureIds(
            BOOKMARKS_EXTENSION_NAME,
            ids.tweetIds,
            (tweet) => patchTweetFlags(tweet, {
              bookmarked: false,
              bookmarkFolderId: null
            })
          )
        ];
      case "like_add":
        if (!ids.tweetIds.length) return [];
        return [
          dbProxy.extAddTweetCaptureIds(
            LIKES_EXTENSION_NAME,
            ids.tweetIds,
            (tweet) => patchTweetFlags(tweet, {
              favorited: true
            })
          )
        ];
      case "like_remove":
        if (!ids.tweetIds.length) return [];
        return [
          dbProxy.extRemoveTweetCaptureIds(
            LIKES_EXTENSION_NAME,
            ids.tweetIds,
            (tweet) => patchTweetFlags(tweet, {
              favorited: false
            })
          )
        ];
      case "follow_add":
        if (!ids.userIds.length) return [];
        return [
          dbProxy.extAddUserCaptureIds(
            FOLLOWING_EXTENSION_NAME,
            ids.userIds,
            (user) => patchUserFollowing(user, true)
          )
        ];
      case "follow_remove":
        if (!ids.userIds.length) return [];
        return [
          dbProxy.extRemoveUserCaptureIds(
            FOLLOWING_EXTENSION_NAME,
            ids.userIds,
            (user) => patchUserFollowing(user, false)
          )
        ];
      default:
        return [];
    }
  }
  const InteractionEventsInterceptor = (req, res, ext) => {
    const interaction = detectInteraction(req.url);
    if (!interaction) return;
    if (res.status < 200 || res.status >= 300) {
      return;
    }
    const body = parseRequestBody(req.body);
    const ids = extractIds(body, req.url);
    const targets = pickTargets(interaction, ids);
    const operation = extractOperationName(req.url) || "none";
    const requestId = toSafeKeyPart(
      req.requestId || `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
    );
    const now = Date.now();
    const items = targets.map((target, index) => {
      const targetSafe = toSafeKeyPart(target || "unknown");
      const id2 = `${requestId}-${interaction.kind}-${index}`;
      const data_key = `${interaction.kind}|target:${targetSafe}|type:${interaction.targetType}|op:${toSafeKeyPart(operation)}`;
      return {
        id: id2,
        data_key,
        created_at: now
      };
    });
    const baseTasks = [dbProxy.extAddCustomCaptures(ext.name, items)];
    recordDiagnosticInteractionEvent({
      ts: now,
      extension: ext.name,
      kind: interaction.kind,
      target_type: interaction.targetType,
      operation,
      request_id: req.requestId,
      tweet_ids: ids.tweetIds,
      user_ids: ids.userIds,
      folder_ids: ids.folderIds,
      targets,
      mirror_task_count: baseTasks.length
    });
    void buildMirrorTasks(interaction, ids).then((mirrorTasks) => Promise.allSettled([...baseTasks, ...mirrorTasks])).then((results) => {
      for (const result of results) {
        if (result.status === "rejected") {
          logger.warn("InteractionEvents: failed to mirror interaction state", result.reason);
        }
      }
    });
    logger.info(`InteractionEvents: ${items.length} items received`);
    logger.debug(
      `InteractionEvents: kind=${interaction.kind} targetType=${interaction.targetType} op=${operation}`
    );
  };
  /**
   * Modified from `dexie-react-hooks` with some lines removed. The modified version
   * is specifically designed for `Observable` and `liveQuery` from Dexie.js.
   *
   * @license Apache-2.0
   * @see https://dexie.org/docs/dexie-react-hooks/useObservable()
   * @see https://github.com/dexie/Dexie.js/blob/v4.0.4/libs/dexie-react-hooks/src/useObservable.ts
   * @param observableFactory Function that returns an observable.
   * @param deps The observableFactory function will be re-executed if deps change.
   * @param defaultResult Result returned on initial render.
   * @returns The current result of the observable.
   */
  function useObservable(observableFactory, deps, defaultResult) {
    const monitor = hooks.useRef({
      hasResult: false,
      result: defaultResult,
      error: null
    });
    const [, triggerUpdate] = hooks.useReducer((x) => x + 1, 0);
    const observable = hooks.useMemo(() => {
      const observable2 = observableFactory();
      if (!observable2 || typeof observable2.subscribe !== "function") {
        throw new TypeError(
          `Observable factory given to useObservable() did not return a valid observable.`
        );
      }
      if (monitor.current.hasResult) {
        return observable2;
      }
      if (typeof observable2.hasValue !== "function" || observable2.hasValue()) {
        if (typeof observable2.getValue === "function") {
          monitor.current.result = observable2.getValue();
          monitor.current.hasResult = true;
        }
      }
      return observable2;
    }, deps);
    hooks.useEffect(() => {
      const subscription = observable.subscribe(
        (val) => {
          const state = monitor.current;
          if (state.error !== null || state.result !== val) {
            state.error = null;
            state.result = val;
            state.hasResult = true;
            triggerUpdate(1);
          }
        },
        (err2) => {
          if (monitor.current.error !== err2) {
            monitor.current.error = err2;
            triggerUpdate(1);
          }
        }
      );
      return subscription.unsubscribe.bind(subscription);
    }, deps);
    if (monitor.current.error) {
      throw monitor.current.error;
    }
    return monitor.current.result;
  }
  /**
   * A hook that subscribes to a live query and returns the current result.
   * Copied from `dexie-react-hooks` with some function overloads removed.
   *
   * @license Apache-2.0
   * @see https://dexie.org/docs/dexie-react-hooks/useLiveQuery()
   * @see https://github.com/dexie/Dexie.js/blob/v4.0.4/libs/dexie-react-hooks/src/useLiveQuery.ts
   * @see https://github.com/dexie/Dexie.js/blob/v4.0.4/src/live-query/live-query.ts
   * @param querier Function that returns a final result (Promise).
   * @param deps Variables that querier is dependent on.
   * @param defaultResult Result returned on initial render.
   * @returns The current result of the live query.
   */
  function useLiveQuery(querier, deps, defaultResult) {
    return useObservable(() => Dexie.liveQuery(querier), deps || [], defaultResult);
  }
  function sortByNewest(rows) {
    return rows.slice().sort((a, b) => Number(b.created_at || 0) - Number(a.created_at || 0));
  }
  function formatTimestamp(ms) {
    if (!Number.isFinite(ms) || ms <= 0) return "-";
    try {
      return new Date(ms).toLocaleString();
    } catch {
      return String(ms);
    }
  }
  function splitDataKey(dataKey) {
    var _a2, _b2;
    const parts = String(dataKey || "").split("|");
    const kind = parts[0] || "unknown";
    const target = ((_a2 = parts.find((part) => part.startsWith("target:"))) == null ? void 0 : _a2.slice("target:".length)) || "-";
    const operation = ((_b2 = parts.find((part) => part.startsWith("op:"))) == null ? void 0 : _b2.slice("op:".length)) || "-";
    return { kind, target, operation };
  }
  function InteractionEventsPanel({ extension }) {
    const { t } = useTranslation();
    const [showModal, toggleShowModal] = useToggle();
    const count = useCaptureCount(extension.name);
    const queryResult = useLiveQuery(() => dbProxy.extGetCaptures(extension.name), [extension.name]);
    const rows = Array.isArray(queryResult) ? queryResult : [];
    const recent = sortByNewest(rows).slice(0, 150);
    return /* @__PURE__ */ u(
      ExtensionPanel,
      {
        title: t("Interaction Events"),
        description: `${t("Captured:")} ${count}`,
        active: count > 0,
        onClick: toggleShowModal,
        indicatorColor: "bg-neutral",
        panelClass: "opacity-90",
        children: /* @__PURE__ */ u(
          Modal,
          {
            class: "max-w-4xl md:max-w-screen-md sm:max-w-screen-sm min-h-[512px]",
            title: t("Interaction Events"),
            show: showModal,
            onClose: toggleShowModal,
            children: [
              /* @__PURE__ */ u("div", { class: "text-xs text-base-content opacity-70 mb-2", children: [
                "Captures request-level actions (like/bookmark/follow/repost). Showing latest",
                " ",
                recent.length,
                " events."
              ] }),
              /* @__PURE__ */ u("div", { class: "overflow-y-auto max-h-[460px] border rounded-box-half border-base-300", children: /* @__PURE__ */ u("table", { class: "table table-xs w-full", children: [
                /* @__PURE__ */ u("thead", { children: /* @__PURE__ */ u("tr", { children: [
                  /* @__PURE__ */ u("th", { children: "Time" }),
                  /* @__PURE__ */ u("th", { children: "Action" }),
                  /* @__PURE__ */ u("th", { children: "Target" }),
                  /* @__PURE__ */ u("th", { children: "Op" })
                ] }) }),
                /* @__PURE__ */ u("tbody", { children: recent.map((row) => {
                  const parsed = splitDataKey(row.data_key);
                  return /* @__PURE__ */ u("tr", { children: [
                    /* @__PURE__ */ u("td", { class: "font-mono", children: formatTimestamp(Number(row.created_at || 0)) }),
                    /* @__PURE__ */ u("td", { class: "font-mono", children: parsed.kind }),
                    /* @__PURE__ */ u("td", { class: "font-mono break-all", children: parsed.target }),
                    /* @__PURE__ */ u("td", { class: "font-mono break-all", children: parsed.operation })
                  ] }, row.id);
                }) })
              ] }) })
            ]
          }
        )
      }
    );
  }
  class InteractionEventsModule extends Extension {
    constructor() {
      super(...arguments);
      __publicField(this, "name", "InteractionEventsModule");
      __publicField(this, "type", ExtensionType.CUSTOM);
    }
    intercept() {
      return InteractionEventsInterceptor;
    }
    render() {
      return InteractionEventsPanel;
    }
  }
  const LikesInterceptor = createModuleInterceptor({
    moduleName: "Likes",
    match: (req) => /\/graphql\/.+\/Likes/.test(req.url),
    parse: (_req, res) => extractDataFromResponse(
      res,
      (json) => json.data.user.result.timeline.timeline.instructions,
      (entry) => extractTimelineTweet(entry.content.itemContent)
    ),
    project: (extName, tweets) => projectTweets(extName, tweets)
  });
  class LikesModule extends Extension {
    constructor() {
      super(...arguments);
      __publicField(this, "name", "LikesModule");
      __publicField(this, "type", ExtensionType.TWEET);
    }
    intercept() {
      return LikesInterceptor;
    }
    render() {
      return CommonModuleUI;
    }
  }
  const ListMembersInterceptor = createModuleInterceptor({
    moduleName: "ListMembers",
    match: (req) => /\/graphql\/.+\/ListMembers/.test(req.url),
    parse: (_req, res) => extractDataFromResponse(
      res,
      (json) => json.data.list.members_timeline.timeline.instructions,
      (entry) => extractTimelineUser(entry.content.itemContent)
    ),
    project: (extName, users) => projectUsers(extName, users)
  });
  class ListMembersModule extends Extension {
    constructor() {
      super(...arguments);
      __publicField(this, "name", "ListMembersModule");
      __publicField(this, "type", ExtensionType.USER);
    }
    intercept() {
      return ListMembersInterceptor;
    }
    render() {
      return CommonModuleUI;
    }
  }
  const ListSubscribersInterceptor = createModuleInterceptor({
    moduleName: "ListSubscribers",
    match: (req) => /\/graphql\/.+\/ListSubscribers/.test(req.url),
    parse: (_req, res) => extractDataFromResponse(
      res,
      (json) => json.data.list.subscribers_timeline.timeline.instructions,
      (entry) => extractTimelineUser(entry.content.itemContent)
    ),
    project: (extName, users) => projectUsers(extName, users)
  });
  class ListSubscribersModule extends Extension {
    constructor() {
      super(...arguments);
      __publicField(this, "name", "ListSubscribersModule");
      __publicField(this, "type", ExtensionType.USER);
    }
    intercept() {
      return ListSubscribersInterceptor;
    }
    render() {
      return CommonModuleUI;
    }
  }
  const ListTimelineInterceptor = createModuleInterceptor({
    moduleName: "ListTimeline",
    match: (req) => /\/graphql\/.+\/ListLatestTweetsTimeline/.test(req.url),
    parse: (_req, res) => extractDataFromResponse(
      res,
      (json) => json.data.list.tweets_timeline.timeline.instructions,
      (entry) => extractTimelineTweet(entry.content.itemContent)
    ),
    project: (extName, tweets) => projectTweets(extName, tweets)
  });
  class ListTimelineModule extends Extension {
    constructor() {
      super(...arguments);
      __publicField(this, "name", "ListTimelineModule");
      __publicField(this, "type", ExtensionType.TWEET);
    }
    intercept() {
      return ListTimelineInterceptor;
    }
    render() {
      return CommonModuleUI;
    }
  }
  const colors = {
    info: "text-base-content",
    warn: "text-warning",
    error: "text-error"
  };
  const RAW_DAEMON_BASE_URL_STORAGE_KEY$1 = "twe_raw_capture_daemon_url_v1";
  const RAW_SEARCH_QUERY_STORAGE_KEY = "twe_raw_search_query_v1";
  const RAW_SEARCH_SORT_STORAGE_KEY = "twe_raw_search_sort_v1";
  const RAW_SEARCH_LIMIT_STORAGE_KEY = "twe_raw_search_limit_v1";
  const RAW_SEARCH_SAVED_STORAGE_KEY = "twe_raw_search_saved_v1";
  const RAW_SEARCH_RANKING_STORAGE_KEY = "twe_raw_search_ranking_v1";
  function Logs({ lines }) {
    const reversed = lines.value.slice().reverse();
    return /* @__PURE__ */ u("pre", { class: "leading-none text-xs max-h-48 bg-base-200 overflow-y-scroll m-0 px-1 py-2.5 no-scrollbar rounded-box-half", children: reversed.map((line) => /* @__PURE__ */ u("span", { class: colors[line.type], children: [
      "#",
      line.index,
      " ",
      line.line,
      "\n"
    ] }, line.index)) });
  }
  function RawCaptureHealth() {
    const [stats, setStats] = hooks.useState(() => readRawStats());
    hooks.useEffect(() => {
      const refresh = () => setStats(readRawStats());
      refresh();
      let timer = null;
      const schedule = () => {
        const delay = document.hidden ? 9e3 : 2500;
        timer = window.setTimeout(() => {
          refresh();
          schedule();
        }, delay);
      };
      schedule();
      window.addEventListener("twe:raw-event-v1", refresh);
      window.addEventListener("twe:raw-spool-state-v1", refresh);
      window.addEventListener("twe:raw-monitor-role-v1", refresh);
      return () => {
        if (timer !== null) {
          window.clearTimeout(timer);
        }
        window.removeEventListener("twe:raw-event-v1", refresh);
        window.removeEventListener("twe:raw-spool-state-v1", refresh);
        window.removeEventListener("twe:raw-monitor-role-v1", refresh);
      };
    }, []);
    return /* @__PURE__ */ u("div", { class: "text-[11px] leading-tight bg-base-200 rounded-box-half px-2 py-1.5 mb-1", children: [
      /* @__PURE__ */ u("div", { children: [
        "raw events: ",
        Number(stats.total || 0),
        " | dropped: ",
        Number(stats.dropped || 0)
      ] }),
      /* @__PURE__ */ u("div", { children: [
        "spool: ",
        Number(stats.spool_count || 0),
        " queued / ",
        Number(stats.spool_enqueued || 0),
        " enq /",
        " ",
        Number(stats.spool_flushed || 0),
        " flushed / ",
        Number(stats.spool_failed || 0),
        " failed"
      ] }),
      /* @__PURE__ */ u("div", { children: [
        "spool overflow drops: ",
        Number(stats.spool_drop_overflow || 0),
        " | unavailable:",
        " ",
        Number(stats.spool_unavailable || 0),
        " | oldest pending:",
        " ",
        Number(stats.oldest_pending_age_ms || 0),
        "ms"
      ] }),
      /* @__PURE__ */ u("div", { children: [
        "daemon: ",
        stats.daemon_online ? "online" : "offline",
        stats.daemon_last_error ? ` | last error: ${stats.daemon_last_error}` : ""
      ] }),
      /* @__PURE__ */ u("div", { children: [
        "monitor: ",
        stats.monitor_role || "unknown",
        " | leader: ",
        stats.monitor_leader_tab_id || "-",
        " | lease: ",
        Number(stats.monitor_last_heartbeat_ms || 0)
      ] }),
      /* @__PURE__ */ u("div", { children: [
        "monitor ticks route/viewport: ",
        Number(stats.monitor_ticks_route || 0),
        "/",
        Number(stats.monitor_ticks_viewport || 0),
        " | suppressed route/viewport:",
        " ",
        Number(stats.monitor_suppressed_route || 0),
        "/",
        Number(stats.monitor_suppressed_viewport || 0)
      ] })
    ] });
  }
  function DiagnosticsExport() {
    const { t } = useTranslation();
    const [busy, setBusy] = hooks.useState(false);
    const [enabled, setEnabled] = hooks.useState(() => isDiagnosticCaptureEnabled());
    hooks.useEffect(() => {
      const refresh = () => setEnabled(isDiagnosticCaptureEnabled());
      window.addEventListener(diagnosticKeys.eventName, refresh);
      return () => window.removeEventListener(diagnosticKeys.eventName, refresh);
    }, []);
    const onExport = async () => {
      if (busy) return;
      setBusy(true);
      try {
        await exportDiagnosticsBundleZip();
      } finally {
        setBusy(false);
      }
    };
    return /* @__PURE__ */ u("div", { class: "mb-1 grid min-w-0 grid-cols-2 items-center gap-2", children: [
      /* @__PURE__ */ u("label", { class: "label min-w-0 cursor-pointer gap-2 py-0", children: [
        /* @__PURE__ */ u("span", { class: "text-[11px]", children: t("Diagnostic capture") }),
        /* @__PURE__ */ u(
          "input",
          {
            type: "checkbox",
            class: "toggle toggle-xs",
            checked: enabled,
            onChange: (event) => {
              const next = event.target.checked;
              setDiagnosticCaptureEnabled(next);
              setEnabled(next);
            }
          }
        )
      ] }),
      /* @__PURE__ */ u(
        "button",
        {
          class: "btn btn-xs h-auto min-h-6 max-w-full whitespace-normal break-words text-center leading-tight",
          onClick: () => {
            clearDiagnosticBuffers();
          },
          children: t("Clear Buffers")
        }
      ),
      /* @__PURE__ */ u(
        "button",
        {
          class: "btn btn-xs col-span-2 h-auto min-h-6 max-w-full whitespace-normal break-words text-center leading-tight",
          disabled: busy,
          onClick: onExport,
          children: busy ? t("Preparing diagnostics...") : t("Export Diagnostics Bundle")
        }
      )
    ] });
  }
  const DEFAULT_SEARCH_RANKING_OVERRIDES = {
    bm25: "",
    lexical: "",
    cover_density: "",
    recency: "",
    term_match: "",
    phrase_match: "",
    cover_bigram: "",
    cover_trigram: ""
  };
  function readLocalStorageString$1(key) {
    try {
      return localStorage.getItem(key);
    } catch {
      return null;
    }
  }
  function writeLocalStorageString(key, value) {
    try {
      localStorage.setItem(key, value);
    } catch {
    }
  }
  function readSavedSearches() {
    try {
      const raw = readLocalStorageString$1(RAW_SEARCH_SAVED_STORAGE_KEY);
      if (!raw) return [];
      const parsed = JSON.parse(raw);
      if (!Array.isArray(parsed)) return [];
      const out = [];
      for (const item of parsed) {
        if (!item || typeof item !== "object") continue;
        const row = item;
        const id2 = String(row.id || "").trim();
        const name2 = String(row.name || "").trim();
        const query = String(row.query || "").trim();
        if (!id2 || !name2) continue;
        out.push({ id: id2, name: name2, query });
      }
      return out.slice(0, 30);
    } catch {
      return [];
    }
  }
  function writeSavedSearches(entries) {
    try {
      const payload = JSON.stringify(entries.slice(0, 30));
      writeLocalStorageString(RAW_SEARCH_SAVED_STORAGE_KEY, payload);
    } catch {
    }
  }
  function readSearchRankingOverrides() {
    try {
      const raw = readLocalStorageString$1(RAW_SEARCH_RANKING_STORAGE_KEY);
      if (!raw) return { ...DEFAULT_SEARCH_RANKING_OVERRIDES };
      const parsed = JSON.parse(raw);
      if (!parsed || typeof parsed !== "object") return { ...DEFAULT_SEARCH_RANKING_OVERRIDES };
      const value = parsed;
      return {
        bm25: String(value.bm25 || "").trim(),
        lexical: String(value.lexical || "").trim(),
        cover_density: String(value.cover_density || "").trim(),
        recency: String(value.recency || "").trim(),
        term_match: String(value.term_match || "").trim(),
        phrase_match: String(value.phrase_match || "").trim(),
        cover_bigram: String(value.cover_bigram || "").trim(),
        cover_trigram: String(value.cover_trigram || "").trim()
      };
    } catch {
      return { ...DEFAULT_SEARCH_RANKING_OVERRIDES };
    }
  }
  function writeSearchRankingOverrides(value) {
    try {
      writeLocalStorageString(
        RAW_SEARCH_RANKING_STORAGE_KEY,
        JSON.stringify({
          bm25: String(value.bm25 || "").trim(),
          lexical: String(value.lexical || "").trim(),
          cover_density: String(value.cover_density || "").trim(),
          recency: String(value.recency || "").trim(),
          term_match: String(value.term_match || "").trim(),
          phrase_match: String(value.phrase_match || "").trim(),
          cover_bigram: String(value.cover_bigram || "").trim(),
          cover_trigram: String(value.cover_trigram || "").trim()
        })
      );
    } catch {
    }
  }
  function normalizeRankValue(value) {
    const trimmed = String(value || "").trim();
    if (!trimmed) return "";
    const numeric = Number(trimmed);
    if (!Number.isFinite(numeric)) return "";
    return String(numeric);
  }
  function resolveSearchRankingOverrides(raw) {
    const resolved = {};
    for (const key of Object.keys(DEFAULT_SEARCH_RANKING_OVERRIDES)) {
      const normalized = normalizeRankValue(raw[key]);
      if (!normalized) continue;
      resolved[key] = Number(normalized);
    }
    return resolved;
  }
  function normalizeDaemonBaseUrl(input) {
    const trimmed = String(input || "").trim();
    if (!trimmed) return "";
    return trimmed.replace(/\/+$/g, "");
  }
  function resolveDaemonBaseUrl() {
    const optionValue = appOptionsManager.get("rawCaptureDaemonUrl", "");
    const fromOption = normalizeDaemonBaseUrl(String(optionValue || ""));
    if (fromOption) return fromOption;
    const fromStorage = normalizeDaemonBaseUrl(
      readLocalStorageString$1(RAW_DAEMON_BASE_URL_STORAGE_KEY$1) || ""
    );
    if (fromStorage) return fromStorage;
    return "http://127.0.0.1:8754";
  }
  function appendOperator(query, operator) {
    const base = String(query || "").trim();
    const next = String(operator || "").trim();
    if (!next) return base;
    if (!base) return next;
    return `${base} ${next}`;
  }
  function makeSearchId() {
    return `saved-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
  }
  function computeTopFacets(rows) {
    const makeCounts = (values) => {
      const counter = /* @__PURE__ */ new Map();
      for (const value of values) {
        const normalized = String(value || "").trim();
        if (!normalized) continue;
        counter.set(normalized, (counter.get(normalized) || 0) + 1);
      }
      return [...counter.entries()].sort((a, b) => {
        if (b[1] !== a[1]) return b[1] - a[1];
        return a[0].localeCompare(b[0]);
      }).slice(0, 6).map(([value, count]) => ({ value, count }));
    };
    return {
      authors: makeCounts(rows.map((row) => row.author_screen_name || "")),
      routes: makeCounts(rows.map((row) => row.route_type || "")),
      folders: makeCounts(rows.map((row) => row.bookmark_folder_id || ""))
    };
  }
  function buildStructuredSearchQuery(baseQuery, filters) {
    const tokens = [];
    const normalizedBase = String(baseQuery || "").trim();
    if (normalizedBase) tokens.push(normalizedBase);
    if (filters.fromUser.trim()) tokens.push(`from:${filters.fromUser.trim().replace(/^@+/, "")}`);
    if (filters.toUser.trim()) tokens.push(`to:${filters.toUser.trim().replace(/^@+/, "")}`);
    if (filters.folderIdOrName.trim())
      tokens.push(`bookmark_folder:${filters.folderIdOrName.trim()}`);
    if (filters.sourceContains.trim()) tokens.push(`source:${filters.sourceContains.trim()}`);
    if (filters.cardName.trim()) tokens.push(`card_name:${filters.cardName.trim()}`);
    if (filters.bookmarkedOnly) tokens.push("is:bookmarked");
    if (filters.likedOnly) tokens.push("is:liked");
    if (filters.mediaOnly) tokens.push("filter:media");
    if (filters.repliesOnly) tokens.push("filter:replies");
    if (filters.excludeRetweets) tokens.push("-filter:retweets");
    if (filters.verifiedOnly) tokens.push("filter:verified");
    if (filters.blueVerifiedOnly) tokens.push("filter:blue_verified");
    return tokens.join(" ").trim();
  }
  const SEARCH_OPERATOR_CHIPS = [
    "filter:replies",
    "-filter:replies",
    "filter:media",
    "-filter:retweets",
    "id:",
    "from_id:",
    "in_reply_to_id:",
    "domain:x.com",
    "is:bookmarked",
    "is:liked",
    "filter:verified",
    "filter:blue_verified",
    "source:iphone",
    "card_name:summary_large_image",
    "min_faves:100",
    "lang:en",
    "since:2026-01-01",
    "until:2026-12-31",
    "OR"
  ];
  const SEARCH_OPERATOR_AUTOCOMPLETE = [
    "AND",
    "OR",
    "NOT",
    "from:",
    "from_id:",
    "author_id:",
    "to:",
    "to_id:",
    "in_reply_to_id:",
    "id:",
    "lang:",
    "route:",
    "bookmark_folder:",
    "conversation_id:",
    "source:",
    "card_name:",
    "url:",
    "domain:",
    "filter:replies",
    "filter:retweets",
    "-filter:retweets",
    "filter:media",
    "filter:links",
    "filter:verified",
    "filter:blue_verified",
    "is:bookmarked",
    "is:liked",
    "is:verified",
    "is:blue_verified",
    "has:links",
    "has:hashtags",
    "has:mentions",
    "has:cashtags",
    "min_faves:",
    "min_likes:",
    "min_retweets:",
    "min_replies:",
    "min_bookmarks:",
    "since:",
    "until:"
  ];
  const SEARCH_EXPORT_SCHEMA_VERSION = "search.export.v1";
  function extractAutocompleteSeed(query) {
    const value = String(query || "");
    if (!value.trim()) return "";
    const parts = value.split(/\s+/g);
    return String(parts[parts.length - 1] || "").trim();
  }
  function applyAutocompleteSuggestion(query, suggestion) {
    const next = String(suggestion || "").trim();
    if (!next) return String(query || "");
    const source = String(query || "");
    if (!source.trim()) return next;
    if (/\s$/.test(source)) return `${source}${next}`;
    const replaced = source.replace(/\S+$/, next);
    return replaced.trim();
  }
  function computeAutocompleteSuggestions(query) {
    const seed = extractAutocompleteSeed(query).toLowerCase();
    if (!seed) return SEARCH_OPERATOR_AUTOCOMPLETE.slice(0, 10);
    return SEARCH_OPERATOR_AUTOCOMPLETE.filter(
      (value) => value.toLowerCase().startsWith(seed) || value.toLowerCase().includes(seed)
    ).slice(0, 10);
  }
  function escapeRegExp(value) {
    return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
  }
  function highlightMatchedText(text, terms) {
    const source = String(text);
    const normalizedTerms = [
      ...new Set(
        terms.map(
          (term) => String(term || "").trim().toLowerCase()
        )
      )
    ].filter((term) => term.length >= 2).sort((a, b) => b.length - a.length).slice(0, 24);
    if (!source || !normalizedTerms.length) return /* @__PURE__ */ u(preact.Fragment, { children: source });
    const pattern = new RegExp(
      `(${normalizedTerms.map((term) => escapeRegExp(term)).join("|")})`,
      "ig"
    );
    const chunks = source.split(pattern);
    return /* @__PURE__ */ u(preact.Fragment, { children: chunks.map((chunk, index) => {
      const lower = chunk.toLowerCase();
      const matched = normalizedTerms.includes(lower);
      if (!matched) return /* @__PURE__ */ u(preact.Fragment, { children: chunk }, `txt-${index}`);
      return /* @__PURE__ */ u("mark", { class: "bg-warning/30 px-[1px] rounded-[2px]", children: chunk }, `hl-${index}`);
    }) });
  }
  function buildRowNarrowingOperators(row) {
    const out = [];
    if (row.author_screen_name) out.push(`from:${row.author_screen_name}`);
    if (row.route_type) out.push(`route:${row.route_type}`);
    if (row.bookmark_folder_id) out.push(`bookmark_folder:${row.bookmark_folder_id}`);
    if (row.has_media) out.push("filter:media");
    if (row.has_links) out.push("has:links");
    if (row.bookmarked) out.push("is:bookmarked");
    if (row.favorited) out.push("is:liked");
    if (row.author_verified) out.push("is:verified");
    if (row.author_blue_verified) out.push("is:blue_verified");
    return [...new Set(out)].slice(0, 8);
  }
  function buildSearchExportBlob(args) {
    const now = Date.now();
    return {
      schema_version: SEARCH_EXPORT_SCHEMA_VERSION,
      generated_at_ms: now,
      generated_at_iso: new Date(now).toISOString(),
      producer: {
        name: "twitter-web-exporter.local-search",
        version: "1"
      },
      source: {
        daemon_base_url: args.daemonBaseUrl,
        page_url: typeof location !== "undefined" ? location.href : ""
      },
      request: {
        query: args.effectiveQuery,
        sort: args.sort,
        limit: args.limit,
        offset: args.offset,
        ranking: args.ranking
      },
      response: args.payload
    };
  }
  function RawRecorderSearchPanel() {
    var _a2, _b2, _c, _d, _e, _f;
    const [daemonBaseUrl, setDaemonBaseUrl] = hooks.useState(() => resolveDaemonBaseUrl());
    const [query, setQuery] = hooks.useState(
      () => readLocalStorageString$1(RAW_SEARCH_QUERY_STORAGE_KEY) || ""
    );
    const [sort, setSort] = hooks.useState(() => {
      const candidate = readLocalStorageString$1(RAW_SEARCH_SORT_STORAGE_KEY);
      if (candidate === "newest" || candidate === "oldest" || candidate === "relevance") {
        return candidate;
      }
      return "relevance";
    });
    const [limit, setLimit] = hooks.useState(() => {
      const candidate = Number(readLocalStorageString$1(RAW_SEARCH_LIMIT_STORAGE_KEY) || "50");
      if (Number.isFinite(candidate) && candidate > 0 && candidate <= 500)
        return Math.floor(candidate);
      return 50;
    });
    const [offset, setOffset] = hooks.useState(0);
    const [savedSearches, setSavedSearches] = hooks.useState(() => readSavedSearches());
    const [saveName, setSaveName] = hooks.useState("");
    const [selectedSavedId, setSelectedSavedId] = hooks.useState("");
    const [fromUser, setFromUser] = hooks.useState("");
    const [toUser, setToUser] = hooks.useState("");
    const [folderIdOrName, setFolderIdOrName] = hooks.useState("");
    const [sourceContains, setSourceContains] = hooks.useState("");
    const [cardName, setCardName] = hooks.useState("");
    const [bookmarkedOnly, setBookmarkedOnly] = hooks.useState(false);
    const [likedOnly, setLikedOnly] = hooks.useState(false);
    const [mediaOnly, setMediaOnly] = hooks.useState(false);
    const [repliesOnly, setRepliesOnly] = hooks.useState(false);
    const [excludeRetweets, setExcludeRetweets] = hooks.useState(false);
    const [verifiedOnly, setVerifiedOnly] = hooks.useState(false);
    const [blueVerifiedOnly, setBlueVerifiedOnly] = hooks.useState(false);
    const [rankBm25, setRankBm25] = hooks.useState("");
    const [rankLexical, setRankLexical] = hooks.useState("");
    const [rankCoverDensity, setRankCoverDensity] = hooks.useState("");
    const [rankRecency, setRankRecency] = hooks.useState("");
    const [rankTermMatch, setRankTermMatch] = hooks.useState("");
    const [rankPhraseMatch, setRankPhraseMatch] = hooks.useState("");
    const [rankCoverBigram, setRankCoverBigram] = hooks.useState("");
    const [rankCoverTrigram, setRankCoverTrigram] = hooks.useState("");
    const [busy, setBusy] = hooks.useState(false);
    const [error, setError] = hooks.useState("");
    const [payload, setPayload] = hooks.useState(null);
    const [lastEffectiveQuery, setLastEffectiveQuery] = hooks.useState("");
    hooks.useEffect(() => {
      const initial = readSearchRankingOverrides();
      setRankBm25(initial.bm25);
      setRankLexical(initial.lexical);
      setRankCoverDensity(initial.cover_density);
      setRankRecency(initial.recency);
      setRankTermMatch(initial.term_match);
      setRankPhraseMatch(initial.phrase_match);
      setRankCoverBigram(initial.cover_bigram);
      setRankCoverTrigram(initial.cover_trigram);
    }, []);
    hooks.useEffect(() => {
      writeLocalStorageString(RAW_SEARCH_QUERY_STORAGE_KEY, query);
    }, [query]);
    hooks.useEffect(() => {
      writeLocalStorageString(RAW_SEARCH_SORT_STORAGE_KEY, sort);
    }, [sort]);
    hooks.useEffect(() => {
      writeLocalStorageString(RAW_SEARCH_LIMIT_STORAGE_KEY, String(limit));
    }, [limit]);
    hooks.useEffect(() => {
      writeSavedSearches(savedSearches);
    }, [savedSearches]);
    hooks.useEffect(() => {
      writeSearchRankingOverrides({
        bm25: rankBm25,
        lexical: rankLexical,
        cover_density: rankCoverDensity,
        recency: rankRecency,
        term_match: rankTermMatch,
        phrase_match: rankPhraseMatch,
        cover_bigram: rankCoverBigram,
        cover_trigram: rankCoverTrigram
      });
    }, [
      rankBm25,
      rankLexical,
      rankCoverDensity,
      rankRecency,
      rankTermMatch,
      rankPhraseMatch,
      rankCoverBigram,
      rankCoverTrigram
    ]);
    const runSearch = async (nextOffset = offset) => {
      if (busy) return;
      const baseUrl = normalizeDaemonBaseUrl(daemonBaseUrl || resolveDaemonBaseUrl());
      if (!baseUrl) {
        setError("Missing recorder daemon URL.");
        return;
      }
      setBusy(true);
      setError("");
      try {
        const effectiveQuery = buildStructuredSearchQuery(query, {
          fromUser,
          toUser,
          folderIdOrName,
          sourceContains,
          cardName,
          bookmarkedOnly,
          likedOnly,
          mediaOnly,
          repliesOnly,
          excludeRetweets,
          verifiedOnly,
          blueVerifiedOnly
        });
        const params = new URLSearchParams();
        if (effectiveQuery.trim()) params.set("q", effectiveQuery.trim());
        params.set("sort", sort);
        params.set("limit", String(limit));
        params.set("offset", String(Math.max(0, nextOffset)));
        const rankingOverrides = resolveSearchRankingOverrides({
          bm25: rankBm25,
          lexical: rankLexical,
          cover_density: rankCoverDensity,
          recency: rankRecency,
          term_match: rankTermMatch,
          phrase_match: rankPhraseMatch,
          cover_bigram: rankCoverBigram,
          cover_trigram: rankCoverTrigram
        });
        for (const [key, value] of Object.entries(rankingOverrides)) {
          params.set(`rank_${key}`, String(value));
        }
        const response = await fetch(`${baseUrl}/query/search?${params.toString()}`);
        const parsed = await response.json();
        if (!response.ok || !(parsed == null ? void 0 : parsed.ok) || !parsed.query) {
          throw new Error((parsed == null ? void 0 : parsed.error) || `HTTP ${response.status}`);
        }
        setPayload(parsed.query);
        setOffset(Math.max(0, nextOffset));
        setLastEffectiveQuery(effectiveQuery);
      } catch (err2) {
        setError(err2 instanceof Error ? err2.message : String(err2));
        setPayload(null);
      } finally {
        setBusy(false);
      }
    };
    const onOpenTweet = (tweetId) => {
      const id2 = String(tweetId || "").trim();
      if (!id2) return;
      const url = `https://x.com/i/web/status/${id2}`;
      window.open(url, "_blank", "noopener,noreferrer");
    };
    const onPrevPage = () => {
      const next = Math.max(0, offset - limit);
      void runSearch(next);
    };
    const onNextPage = () => {
      const next = offset + limit;
      void runSearch(next);
    };
    const canPageForward = !!payload && offset + limit < payload.total_matches;
    const facets = computeTopFacets((payload == null ? void 0 : payload.rows) || []);
    const onSaveSearch = () => {
      const effectiveQuery = buildStructuredSearchQuery(query, {
        fromUser,
        toUser,
        folderIdOrName,
        sourceContains,
        cardName,
        bookmarkedOnly,
        likedOnly,
        mediaOnly,
        repliesOnly,
        excludeRetweets,
        verifiedOnly,
        blueVerifiedOnly
      }).trim();
      if (!effectiveQuery) return;
      const name2 = saveName.trim() || `Saved ${(/* @__PURE__ */ new Date()).toLocaleString()}`;
      setSavedSearches((current) => {
        const next = [
          { id: makeSearchId(), name: name2, query: effectiveQuery },
          ...current.filter((entry) => entry.query !== effectiveQuery)
        ];
        return next.slice(0, 30);
      });
      setSaveName("");
    };
    const onLoadSavedSearch = (id2) => {
      const found = savedSearches.find((entry) => entry.id === id2);
      if (!found) return;
      setQuery(found.query);
      setSelectedSavedId(id2);
    };
    const onDeleteSavedSearch = () => {
      const id2 = selectedSavedId.trim();
      if (!id2) return;
      setSavedSearches((current) => current.filter((entry) => entry.id !== id2));
      setSelectedSavedId("");
    };
    const onExportSearchBlob = () => {
      if (!payload) return;
      const blob2 = buildSearchExportBlob({
        daemonBaseUrl: normalizeDaemonBaseUrl(daemonBaseUrl || resolveDaemonBaseUrl()),
        effectiveQuery: lastEffectiveQuery || buildStructuredSearchQuery(query, {
          fromUser,
          toUser,
          folderIdOrName,
          sourceContains,
          cardName,
          bookmarkedOnly,
          likedOnly,
          mediaOnly,
          repliesOnly,
          excludeRetweets,
          verifiedOnly,
          blueVerifiedOnly
        }),
        payload,
        sort,
        limit,
        offset,
        ranking: resolveSearchRankingOverrides({
          bm25: rankBm25,
          lexical: rankLexical,
          cover_density: rankCoverDensity,
          recency: rankRecency,
          term_match: rankTermMatch,
          phrase_match: rankPhraseMatch,
          cover_bigram: rankCoverBigram,
          cover_trigram: rankCoverTrigram
        })
      });
      downloadJson(blob2, `twe-search-export-${Date.now()}.json`);
    };
    const autocompleteSuggestions = computeAutocompleteSuggestions(query);
    const highlightedTerms = ((_a2 = payload == null ? void 0 : payload.parsed) == null ? void 0 : _a2.positive_terms) || [];
    return /* @__PURE__ */ u("div", { class: "text-[11px] leading-tight bg-base-200 rounded-box-half px-2 py-1.5 mb-1", children: [
      /* @__PURE__ */ u("div", { class: "font-semibold mb-1", children: "Local Recorder Search" }),
      /* @__PURE__ */ u("div", { class: "mb-1", children: [
        /* @__PURE__ */ u("label", { class: "text-[10px] opacity-70", children: "Daemon URL" }),
        /* @__PURE__ */ u(
          "input",
          {
            type: "text",
            class: "input input-bordered input-xs w-full font-mono mt-0.5",
            value: daemonBaseUrl,
            onInput: (event) => {
              const value = event.target.value;
              setDaemonBaseUrl(value);
              writeLocalStorageString(RAW_DAEMON_BASE_URL_STORAGE_KEY$1, value);
              appOptionsManager.set("rawCaptureDaemonUrl", value);
            }
          }
        )
      ] }),
      /* @__PURE__ */ u("div", { class: "mb-1", children: [
        /* @__PURE__ */ u("label", { class: "text-[10px] opacity-70", children: "Query (Twitter-style operators)" }),
        /* @__PURE__ */ u(
          "textarea",
          {
            class: "textarea textarea-bordered w-full min-h-16 mt-0.5 font-mono text-[11px]",
            value: query,
            onInput: (event) => setQuery(event.target.value),
            onKeyDown: (event) => {
              if (event.key === "Enter" && (event.metaKey || event.ctrlKey)) {
                event.preventDefault();
                void runSearch(0);
              }
            }
          }
        )
      ] }),
      /* @__PURE__ */ u("div", { class: "flex flex-wrap gap-1 mb-1", children: autocompleteSuggestions.map((suggestion) => /* @__PURE__ */ u(
        "button",
        {
          class: "btn btn-ghost btn-xs h-5 min-h-0 px-1 font-mono opacity-80",
          onClick: () => setQuery((current) => applyAutocompleteSuggestion(current, suggestion)),
          children: suggestion
        },
        `suggest-${suggestion}`
      )) }),
      /* @__PURE__ */ u("details", { class: "mb-1", children: [
        /* @__PURE__ */ u("summary", { class: "cursor-pointer text-[10px] opacity-70", children: "operator quick reference" }),
        /* @__PURE__ */ u("div", { class: "text-[10px] font-mono mt-0.5 opacity-80 leading-tight", children: "boolean: `AND` `OR` `NOT` `(...)` | filters: global `AND` semantics" }),
        /* @__PURE__ */ u("div", { class: "text-[10px] font-mono opacity-80 leading-tight", children: "core: `from:` `from_id:` `to:` `to_id:` `id:` `domain:` `filter:*` `is:*` `has:*` `lang:` `since:` `until:`" }),
        /* @__PURE__ */ u("div", { class: "text-[10px] font-mono opacity-80 leading-tight", children: "ranking params: `rank_bm25` `rank_lexical` `rank_cover_density` `rank_recency`" }),
        /* @__PURE__ */ u("div", { class: "text-[10px] font-mono opacity-80 leading-tight", children: "phrase/density: `rank_term_match` `rank_phrase_match` `rank_cover_bigram` `rank_cover_trigram`" })
      ] }),
      /* @__PURE__ */ u("div", { class: "flex flex-wrap gap-1 mb-1", children: SEARCH_OPERATOR_CHIPS.map((chip) => /* @__PURE__ */ u(
        "button",
        {
          class: "btn btn-ghost btn-xs h-5 min-h-0 px-1 font-mono",
          onClick: () => setQuery((current) => appendOperator(current, chip)),
          children: chip
        },
        chip
      )) }),
      /* @__PURE__ */ u("div", { class: "flex flex-wrap items-center gap-1 mb-1", children: [
        /* @__PURE__ */ u(
          "select",
          {
            class: "select select-bordered select-xs flex-1 min-w-44",
            value: selectedSavedId,
            onChange: (event) => onLoadSavedSearch(event.target.value),
            children: [
              /* @__PURE__ */ u("option", { value: "", children: "saved searches" }),
              savedSearches.map((entry) => /* @__PURE__ */ u("option", { value: entry.id, children: entry.name }, entry.id))
            ]
          }
        ),
        /* @__PURE__ */ u(
          "input",
          {
            type: "text",
            class: "input input-bordered input-xs w-32",
            placeholder: "save name",
            value: saveName,
            onInput: (event) => setSaveName(event.target.value)
          }
        ),
        /* @__PURE__ */ u("button", { class: "btn btn-ghost btn-xs", onClick: onSaveSearch, children: "Save" }),
        /* @__PURE__ */ u(
          "button",
          {
            class: "btn btn-ghost btn-xs",
            disabled: !selectedSavedId,
            onClick: onDeleteSavedSearch,
            children: "Delete"
          }
        )
      ] }),
      /* @__PURE__ */ u("div", { class: "grid grid-cols-2 gap-1 mb-1", children: [
        /* @__PURE__ */ u(
          "input",
          {
            type: "text",
            class: "input input-bordered input-xs w-full font-mono",
            placeholder: "from user (alice)",
            value: fromUser,
            onInput: (event) => setFromUser(event.target.value)
          }
        ),
        /* @__PURE__ */ u(
          "input",
          {
            type: "text",
            class: "input input-bordered input-xs w-full font-mono",
            placeholder: "to user (bob)",
            value: toUser,
            onInput: (event) => setToUser(event.target.value)
          }
        ),
        /* @__PURE__ */ u(
          "input",
          {
            type: "text",
            class: "input input-bordered input-xs w-full font-mono col-span-2",
            placeholder: "bookmark folder id/name",
            value: folderIdOrName,
            onInput: (event) => setFolderIdOrName(event.target.value)
          }
        ),
        /* @__PURE__ */ u(
          "input",
          {
            type: "text",
            class: "input input-bordered input-xs w-full font-mono",
            placeholder: "source contains (iphone)",
            value: sourceContains,
            onInput: (event) => setSourceContains(event.target.value)
          }
        ),
        /* @__PURE__ */ u(
          "input",
          {
            type: "text",
            class: "input input-bordered input-xs w-full font-mono",
            placeholder: "card name (summary_large_image)",
            value: cardName,
            onInput: (event) => setCardName(event.target.value)
          }
        )
      ] }),
      /* @__PURE__ */ u("div", { class: "flex flex-wrap items-center gap-2 mb-1", children: [
        /* @__PURE__ */ u("label", { class: "label cursor-pointer p-0 gap-1", children: [
          /* @__PURE__ */ u(
            "input",
            {
              type: "checkbox",
              class: "checkbox checkbox-xs",
              checked: bookmarkedOnly,
              onChange: (event) => setBookmarkedOnly(event.target.checked)
            }
          ),
          /* @__PURE__ */ u("span", { class: "label-text text-[10px]", children: "bookmarked" })
        ] }),
        /* @__PURE__ */ u("label", { class: "label cursor-pointer p-0 gap-1", children: [
          /* @__PURE__ */ u(
            "input",
            {
              type: "checkbox",
              class: "checkbox checkbox-xs",
              checked: likedOnly,
              onChange: (event) => setLikedOnly(event.target.checked)
            }
          ),
          /* @__PURE__ */ u("span", { class: "label-text text-[10px]", children: "liked" })
        ] }),
        /* @__PURE__ */ u("label", { class: "label cursor-pointer p-0 gap-1", children: [
          /* @__PURE__ */ u(
            "input",
            {
              type: "checkbox",
              class: "checkbox checkbox-xs",
              checked: mediaOnly,
              onChange: (event) => setMediaOnly(event.target.checked)
            }
          ),
          /* @__PURE__ */ u("span", { class: "label-text text-[10px]", children: "media" })
        ] }),
        /* @__PURE__ */ u("label", { class: "label cursor-pointer p-0 gap-1", children: [
          /* @__PURE__ */ u(
            "input",
            {
              type: "checkbox",
              class: "checkbox checkbox-xs",
              checked: repliesOnly,
              onChange: (event) => setRepliesOnly(event.target.checked)
            }
          ),
          /* @__PURE__ */ u("span", { class: "label-text text-[10px]", children: "replies" })
        ] }),
        /* @__PURE__ */ u("label", { class: "label cursor-pointer p-0 gap-1", children: [
          /* @__PURE__ */ u(
            "input",
            {
              type: "checkbox",
              class: "checkbox checkbox-xs",
              checked: excludeRetweets,
              onChange: (event) => setExcludeRetweets(event.target.checked)
            }
          ),
          /* @__PURE__ */ u("span", { class: "label-text text-[10px]", children: "exclude retweets" })
        ] }),
        /* @__PURE__ */ u("label", { class: "label cursor-pointer p-0 gap-1", children: [
          /* @__PURE__ */ u(
            "input",
            {
              type: "checkbox",
              class: "checkbox checkbox-xs",
              checked: verifiedOnly,
              onChange: (event) => setVerifiedOnly(event.target.checked)
            }
          ),
          /* @__PURE__ */ u("span", { class: "label-text text-[10px]", children: "verified" })
        ] }),
        /* @__PURE__ */ u("label", { class: "label cursor-pointer p-0 gap-1", children: [
          /* @__PURE__ */ u(
            "input",
            {
              type: "checkbox",
              class: "checkbox checkbox-xs",
              checked: blueVerifiedOnly,
              onChange: (event) => setBlueVerifiedOnly(event.target.checked)
            }
          ),
          /* @__PURE__ */ u("span", { class: "label-text text-[10px]", children: "blue verified" })
        ] })
      ] }),
      /* @__PURE__ */ u("div", { class: "flex flex-wrap items-center gap-1 mb-1", children: [
        /* @__PURE__ */ u(
          "select",
          {
            class: "select select-bordered select-xs w-24",
            value: sort,
            onChange: (event) => setSort(event.target.value),
            children: [
              /* @__PURE__ */ u("option", { value: "relevance", children: "relevance" }),
              /* @__PURE__ */ u("option", { value: "newest", children: "newest" }),
              /* @__PURE__ */ u("option", { value: "oldest", children: "oldest" })
            ]
          }
        ),
        /* @__PURE__ */ u(
          "select",
          {
            class: "select select-bordered select-xs w-20",
            value: String(limit),
            onChange: (event) => setLimit(Number(event.target.value)),
            children: [
              /* @__PURE__ */ u("option", { value: "25", children: "25" }),
              /* @__PURE__ */ u("option", { value: "50", children: "50" }),
              /* @__PURE__ */ u("option", { value: "100", children: "100" }),
              /* @__PURE__ */ u("option", { value: "200", children: "200" })
            ]
          }
        ),
        /* @__PURE__ */ u("button", { class: "btn btn-primary btn-xs", disabled: busy, onClick: () => void runSearch(0), children: busy ? "Searching..." : "Search" }),
        /* @__PURE__ */ u("button", { class: "btn btn-ghost btn-xs", disabled: !payload, onClick: onExportSearchBlob, children: "Export Blob" }),
        /* @__PURE__ */ u("button", { class: "btn btn-ghost btn-xs", disabled: busy, onClick: onPrevPage, children: "Prev" }),
        /* @__PURE__ */ u(
          "button",
          {
            class: "btn btn-ghost btn-xs",
            disabled: busy || !canPageForward,
            onClick: onNextPage,
            children: "Next"
          }
        )
      ] }),
      /* @__PURE__ */ u("details", { class: "mb-1", children: [
        /* @__PURE__ */ u("summary", { class: "cursor-pointer text-[10px] opacity-70", children: "ranking overrides (blank = daemon defaults)" }),
        /* @__PURE__ */ u("div", { class: "grid grid-cols-2 gap-1 mt-1", children: [
          /* @__PURE__ */ u(
            "input",
            {
              type: "text",
              class: "input input-bordered input-xs w-full font-mono",
              placeholder: "bm25",
              value: rankBm25,
              onInput: (event) => setRankBm25(event.target.value)
            }
          ),
          /* @__PURE__ */ u(
            "input",
            {
              type: "text",
              class: "input input-bordered input-xs w-full font-mono",
              placeholder: "lexical",
              value: rankLexical,
              onInput: (event) => setRankLexical(event.target.value)
            }
          ),
          /* @__PURE__ */ u(
            "input",
            {
              type: "text",
              class: "input input-bordered input-xs w-full font-mono",
              placeholder: "cover_density",
              value: rankCoverDensity,
              onInput: (event) => setRankCoverDensity(event.target.value)
            }
          ),
          /* @__PURE__ */ u(
            "input",
            {
              type: "text",
              class: "input input-bordered input-xs w-full font-mono",
              placeholder: "recency",
              value: rankRecency,
              onInput: (event) => setRankRecency(event.target.value)
            }
          ),
          /* @__PURE__ */ u(
            "input",
            {
              type: "text",
              class: "input input-bordered input-xs w-full font-mono",
              placeholder: "term_match",
              value: rankTermMatch,
              onInput: (event) => setRankTermMatch(event.target.value)
            }
          ),
          /* @__PURE__ */ u(
            "input",
            {
              type: "text",
              class: "input input-bordered input-xs w-full font-mono",
              placeholder: "phrase_match",
              value: rankPhraseMatch,
              onInput: (event) => setRankPhraseMatch(event.target.value)
            }
          ),
          /* @__PURE__ */ u(
            "input",
            {
              type: "text",
              class: "input input-bordered input-xs w-full font-mono",
              placeholder: "cover_bigram",
              value: rankCoverBigram,
              onInput: (event) => setRankCoverBigram(event.target.value)
            }
          ),
          /* @__PURE__ */ u(
            "input",
            {
              type: "text",
              class: "input input-bordered input-xs w-full font-mono",
              placeholder: "cover_trigram",
              value: rankCoverTrigram,
              onInput: (event) => setRankCoverTrigram(event.target.value)
            }
          )
        ] })
      ] }),
      /* @__PURE__ */ u("div", { class: "text-[10px] opacity-70 mb-1", children: payload ? `rows ${payload.count} / total ${payload.total_matches} (offset ${payload.offset}, sort ${payload.sort})` : "No results yet." }),
      lastEffectiveQuery ? /* @__PURE__ */ u("div", { class: "text-[10px] font-mono opacity-70 mb-1", children: [
        "effective: ",
        lastEffectiveQuery
      ] }) : null,
      ((_b2 = payload == null ? void 0 : payload.parsed) == null ? void 0 : _b2.lexical_expression) ? /* @__PURE__ */ u("div", { class: "text-[10px] font-mono opacity-70 mb-1", children: [
        "parse: ",
        payload.parsed.lexical_expression,
        payload.parsed.filter_boolean_semantics ? ` | filters=${payload.parsed.filter_boolean_semantics}` : ""
      ] }) : null,
      (payload == null ? void 0 : payload.ranking) ? /* @__PURE__ */ u("div", { class: "text-[10px] font-mono opacity-70 mb-1 leading-tight", children: [
        "weights bm25=",
        Number(payload.ranking.bm25 || 0).toFixed(2),
        " lexical=",
        Number(payload.ranking.lexical || 0).toFixed(2),
        " density=",
        Number(payload.ranking.cover_density || 0).toFixed(2),
        " recency=",
        Number(payload.ranking.recency || 0).toFixed(2),
        /* @__PURE__ */ u("br", {}),
        "term=",
        Number(payload.ranking.term_match || 0).toFixed(2),
        " phrase=",
        Number(payload.ranking.phrase_match || 0).toFixed(2),
        " bigram=",
        Number(payload.ranking.cover_bigram || 0).toFixed(2),
        " trigram=",
        Number(payload.ranking.cover_trigram || 0).toFixed(2)
      ] }) : null,
      error ? /* @__PURE__ */ u("div", { class: "text-error mb-1", children: [
        "search error: ",
        error
      ] }) : null,
      ((_c = payload == null ? void 0 : payload.warning_objects) == null ? void 0 : _c.length) ? /* @__PURE__ */ u("div", { class: "text-warning mb-1 space-y-0.5", children: payload.warning_objects.map((warning2, index) => /* @__PURE__ */ u("div", { children: [
        "[",
        warning2.code || "search_warning",
        "] ",
        warning2.message || "search warning",
        warning2.token ? ` (${warning2.token})` : ""
      ] }, `warn-${index}`)) }) : ((_d = payload == null ? void 0 : payload.warnings) == null ? void 0 : _d.length) ? /* @__PURE__ */ u("div", { class: "text-warning mb-1", children: [
        "warnings: ",
        payload.warnings.join(" | ")
      ] }) : null,
      ((_e = payload == null ? void 0 : payload.rows) == null ? void 0 : _e.length) ? /* @__PURE__ */ u("div", { class: "mb-1", children: [
        /* @__PURE__ */ u("div", { class: "text-[10px] opacity-70 mb-0.5", children: "facets" }),
        /* @__PURE__ */ u("div", { class: "flex flex-wrap gap-1 mb-0.5", children: facets.authors.map((entry) => /* @__PURE__ */ u(
          "button",
          {
            class: "btn btn-ghost btn-xs h-5 min-h-0 px-1 font-mono",
            onClick: () => setQuery((current) => appendOperator(current, `from:${entry.value}`)),
            children: [
              "@",
              entry.value,
              " (",
              entry.count,
              ")"
            ]
          },
          `author-${entry.value}`
        )) }),
        /* @__PURE__ */ u("div", { class: "flex flex-wrap gap-1 mb-0.5", children: facets.routes.map((entry) => /* @__PURE__ */ u(
          "button",
          {
            class: "btn btn-ghost btn-xs h-5 min-h-0 px-1 font-mono",
            onClick: () => setQuery((current) => appendOperator(current, `route:${entry.value}`)),
            children: [
              "route:",
              entry.value,
              " (",
              entry.count,
              ")"
            ]
          },
          `route-${entry.value}`
        )) }),
        /* @__PURE__ */ u("div", { class: "flex flex-wrap gap-1", children: facets.folders.map((entry) => /* @__PURE__ */ u(
          "button",
          {
            class: "btn btn-ghost btn-xs h-5 min-h-0 px-1 font-mono",
            onClick: () => setQuery((current) => appendOperator(current, `bookmark_folder:${entry.value}`)),
            children: [
              "folder:",
              entry.value,
              " (",
              entry.count,
              ")"
            ]
          },
          `folder-${entry.value}`
        )) })
      ] }) : null,
      /* @__PURE__ */ u("div", { class: "max-h-56 overflow-y-auto space-y-1", children: ((_f = payload == null ? void 0 : payload.rows) == null ? void 0 : _f.length) ? payload.rows.map((row) => /* @__PURE__ */ u("div", { class: "bg-base-100 rounded px-1.5 py-1", children: [
        /* @__PURE__ */ u("div", { class: "flex items-center justify-between gap-1", children: [
          /* @__PURE__ */ u("div", { class: "font-mono text-[10px]", children: [
            "@",
            row.author_screen_name || "unknown",
            " · ",
            row.entity_id,
            " · ",
            row.route_type
          ] }),
          /* @__PURE__ */ u(
            "button",
            {
              class: "btn btn-ghost btn-xs h-5 min-h-0 px-1",
              onClick: () => onOpenTweet(row.entity_id),
              children: "Open"
            }
          )
        ] }),
        /* @__PURE__ */ u("div", { class: "text-[11px] whitespace-pre-line break-words", children: highlightMatchedText(
          row.text || "[no text available in snapshot]",
          highlightedTerms
        ) }),
        /* @__PURE__ */ u("div", { class: "font-mono text-[10px] opacity-70 mt-0.5", children: [
          "score=",
          Number(row.score || 0).toFixed(3),
          " | fav=",
          Number(row.favorite_count || 0),
          " ",
          "rt=",
          Number(row.retweet_count || 0),
          " rep=",
          Number(row.reply_count || 0),
          " q=",
          Number(row.quote_count || 0),
          row.bookmark_folder_id ? ` | folder=${row.bookmark_folder_id}` : "",
          row.source_text ? ` | src=${row.source_text}` : "",
          row.card_name ? ` | card=${row.card_name}` : "",
          row.author_verified ? " | verified" : "",
          row.author_blue_verified ? " | blue" : "",
          row.has_media ? " | media" : "",
          row.has_links ? " | links" : "",
          row.is_reply ? " | reply" : "",
          row.is_retweet ? " | retweet" : "",
          row.is_quote ? " | quote" : "",
          row.bookmarked ? " | bookmarked" : "",
          row.favorited ? " | liked" : ""
        ] }),
        /* @__PURE__ */ u("div", { class: "flex flex-wrap gap-1 mt-0.5", children: buildRowNarrowingOperators(row).map((operator) => /* @__PURE__ */ u(
          "button",
          {
            class: "btn btn-ghost btn-xs h-5 min-h-0 px-1 font-mono",
            onClick: () => setQuery((current) => appendOperator(current, operator)),
            children: operator
          },
          `${row.entity_id}-${operator}`
        )) })
      ] }, row.entity_id)) : /* @__PURE__ */ u("div", { class: "text-[10px] opacity-70", children: "Run a query to load rows." }) })
    ] });
  }
  function RuntimeLogsPanel() {
    return /* @__PURE__ */ u(preact.Fragment, { children: [
      /* @__PURE__ */ u("div", { class: "divider mt-0 mb-1" }),
      /* @__PURE__ */ u(DiagnosticsExport, {}),
      /* @__PURE__ */ u(RawCaptureHealth, {}),
      /* @__PURE__ */ u(Logs, { lines: logLinesSignal })
    ] });
  }
  function LocalSearchUI() {
    const { t } = useTranslation();
    const [showModal, toggleShowModal] = useToggle();
    return /* @__PURE__ */ u(
      ExtensionPanel,
      {
        title: t("Local Search"),
        description: t("Search indexed tweets with Twitter-style operators"),
        active: true,
        onClick: toggleShowModal,
        indicatorColor: "bg-neutral",
        panelClass: "opacity-90",
        children: /* @__PURE__ */ u(
          Modal,
          {
            class: "max-w-4xl md:max-w-screen-md sm:max-w-screen-sm min-h-[560px]",
            title: t("Local Recorder Search"),
            show: showModal,
            onClose: toggleShowModal,
            children: /* @__PURE__ */ u(RawRecorderSearchPanel, {})
          }
        )
      }
    );
  }
  class LocalSearchModule extends Extension {
    constructor() {
      super(...arguments);
      __publicField(this, "name", "LocalSearchModule");
      __publicField(this, "type", ExtensionType.CUSTOM);
    }
    render() {
      return LocalSearchUI;
    }
  }
  const RetweetersInterceptor = createModuleInterceptor({
    moduleName: "Retweeters",
    match: (req) => /\/graphql\/.+\/Retweeters/.test(req.url),
    parse: (_req, res) => extractDataFromResponse(
      res,
      (json) => json.data.retweeters_timeline.timeline.instructions,
      (entry) => extractTimelineUser(entry.content.itemContent)
    ),
    project: (extName, users) => projectUsers(extName, users)
  });
  class RetweetersModule extends Extension {
    constructor() {
      super(...arguments);
      __publicField(this, "name", "RetweetersModule");
      __publicField(this, "type", ExtensionType.USER);
    }
    intercept() {
      return RetweetersInterceptor;
    }
    render() {
      return CommonModuleUI;
    }
  }
  class RuntimeLogsModule extends Extension {
    constructor() {
      super(...arguments);
      __publicField(this, "name", "RuntimeLogsModule");
    }
    render() {
      return RuntimeLogsPanel;
    }
  }
  const route_policies = { "home": "public", "bookmarks": "sensitive", "tweet_detail": "public", "notifications": "sensitive", "search_top": "public", "search_latest": "public", "search_people": "public", "search_media": "public", "list": "public", "list_members": "public", "list_subscribers": "public", "community": "public", "community_members": "public", "followers": "sensitive", "following": "sensitive", "user_profile_tweets": "public", "user_profile_replies": "public", "user_profile_media": "public", "user_profile_likes": "sensitive", "messages": "dm", "unknown": "public" };
  const registry = {
    route_policies
  };
  const policyRegistry = registry;
  function normalizeRouteType(routeType) {
    const next = String(routeType || "").trim();
    return next || "unknown";
  }
  function getPolicyClass(routeType) {
    var _a2, _b2;
    const normalized = normalizeRouteType(routeType);
    const raw = ((_a2 = policyRegistry.route_policies) == null ? void 0 : _a2[normalized]) || ((_b2 = policyRegistry.route_policies) == null ? void 0 : _b2.unknown);
    if (raw === "dm" || raw === "sensitive" || raw === "public") {
      return raw;
    }
    return "public";
  }
  function shouldCaptureByPolicy(routeType, opts) {
    const cls = getPolicyClass(routeType);
    if (cls === "dm") {
      return !!opts.dmCaptureAllowed;
    }
    return true;
  }
  const RAW_SCHEMA = "twe.raw.v1";
  const RAW_EVENT_BUFFER_KEY = "__twe_raw_events_v1";
  const RAW_EVENT_EMIT_NAME = "twe:raw-event-v1";
  const RAW_CAPTURE_STATS_KEY = "__twe_raw_capture_stats_v1";
  const ROUTE_EPOCH_KEY = "__twe_route_epoch_v1";
  const RAW_CAPTURE_SESSION_KEY = "__twe_raw_capture_session_id_v1";
  const RAW_CAPTURE_TAB_KEY = "__twe_raw_capture_tab_id_v1";
  const RAW_CAPTURE_EVENT_REV = 1;
  const RAW_EVENT_BUFFER_LIMIT_DEFAULT = 48;
  const RAW_EVENT_BUFFER_LIMIT_DIAGNOSTIC = 160;
  const RESPONSE_SAMPLE_MAX_CHARS = 4096;
  const RESPONSE_SAMPLE_MAX_CHARS_DIAGNOSTIC = 32768;
  const RAW_SPOOL_DB_NAME = "twitter-web-exporter-raw-spool-v1";
  const RAW_SPOOL_STORE_NAME = "events";
  const RAW_SPOOL_MAX_ROWS = 5e3;
  const RAW_SPOOL_FLUSH_BATCH_SIZE = 50;
  const RAW_SPOOL_FLUSH_INTERVAL_MS = 2500;
  const RAW_SPOOL_MAINTENANCE_EVERY_TICKS = 8;
  const RAW_SPOOL_FLUSH_TIMER_KEY = "__twe_raw_spool_flush_timer_v1";
  const RAW_SPOOL_STATE_EVENT_NAME = "twe:raw-spool-state-v1";
  const RAW_SPOOL_DEV_CLEAR_KEY = "__twe_raw_spool_clear_v1";
  const RAW_ROUTE_MONITOR_TIMER_KEY = "__twe_raw_route_monitor_timer_v1";
  const RAW_VIEWPORT_MONITOR_TIMER_KEY = "__twe_raw_viewport_monitor_timer_v1";
  const RAW_MONITOR_COORD_TIMER_KEY = "__twe_raw_monitor_coord_timer_v1";
  const RAW_MONITOR_COORDINATION_KEY = "__twe_raw_monitor_coordination_v1";
  const RAW_MONITOR_METRICS_KEY = "__twe_raw_monitor_metrics_v1";
  const RAW_MONITOR_STORAGE_LISTENER_KEY = "__twe_raw_monitor_storage_listener_v1";
  const RAW_MONITOR_BEFOREUNLOAD_KEY = "__twe_raw_monitor_beforeunload_v1";
  const RAW_MONITOR_DEV_TICK_KEY = "__twe_raw_monitor_tick_v1";
  const RAW_MONITOR_ROLE_EVENT_NAME = "twe:raw-monitor-role-v1";
  const RAW_ROUTE_MONITOR_INTERVAL_MS = 2500;
  const RAW_VIEWPORT_MONITOR_INTERVAL_MS = 4e3;
  const RAW_VIEWPORT_SEEN_WINDOW_MS = 6e4;
  const RAW_VIEWPORT_SCAN_LIMIT = 40;
  const RAW_VIEWPORT_MIN_VISIBLE_PX = 48;
  const RAW_MONITOR_HEARTBEAT_MS = 5e3;
  const RAW_MONITOR_STALE_MS = 2e4;
  const RAW_DAEMON_BASE_URL_STORAGE_KEY = "twe_raw_capture_daemon_url_v1";
  const RAW_DAEMON_STREAM_ENABLED_STORAGE_KEY = "twe_raw_capture_stream_enabled_v1";
  const RAW_CAPTURE_ENABLED_STORAGE_KEY = "twe_raw_capture_enabled_v1";
  const RAW_CAPTURE_ENCRYPTION_READY_STORAGE_KEY = "twe_raw_capture_encryption_ready_v1";
  const RAW_CAPTURE_DM_SESSION_ARMED_UNTIL_STORAGE_KEY = "twe_raw_capture_dm_session_armed_until_ms_v1";
  const RAW_CAPTURE_DM_SESSION_DEFAULT_ARM_MS = 15 * 60 * 1e3;
  const RAW_MONITOR_LEADER_STORAGE_KEY = "twe_raw_monitor_leader_v1";
  const VOLATILE_QUERY_KEYS = /* @__PURE__ */ new Set([
    "s",
    "t",
    "cn",
    "ref_src",
    "ref_url",
    "utm_source",
    "utm_medium",
    "utm_campaign",
    "utm_term",
    "utm_content"
  ]);
  const REDACT_QUERY_KEY_REGEX = /(token|auth|authorization|cookie|csrf|sig|signature|bearer|session|oauth)/i;
  let sequence = 0;
  let lastEventHash = "";
  let spoolDbPromise = null;
  let flushInFlight = false;
  let spoolMaintenanceTick = 0;
  let spoolUnavailableUntil = 0;
  let lastSpoolStateSignature = "";
  let lastMonitorRoleSignature = "";
  let lastObservedRouteUrl = "";
  const viewportSeenAt = /* @__PURE__ */ new Map();
  function getWindowRecord() {
    return globalThis;
  }
  function clearWindowTimer(key) {
    const g = getWindowRecord();
    const timer = g[key];
    if (!(typeof timer === "number" || typeof timer === "object")) {
      return;
    }
    try {
      clearInterval(timer);
    } catch {
    }
    delete g[key];
  }
  function readMonitorMetrics() {
    const g = getWindowRecord();
    const raw = g[RAW_MONITOR_METRICS_KEY];
    const obj = raw && typeof raw === "object" ? raw : {};
    const out = {};
    for (const [key, value] of Object.entries(obj)) {
      out[key] = toNumber(value, 0);
    }
    return out;
  }
  function bumpMonitorMetric(key, by = 1) {
    if (!key) return;
    const g = getWindowRecord();
    const metrics = readMonitorMetrics();
    metrics[key] = toNumber(metrics[key], 0) + Math.max(1, toNumber(by, 1));
    g[RAW_MONITOR_METRICS_KEY] = metrics;
  }
  function toNumber(value, fallback = 0) {
    const n = Number(value);
    return Number.isFinite(n) ? n : fallback;
  }
  function hashText(text) {
    let hash = 2166136261;
    for (let i = 0; i < text.length; i += 1) {
      hash ^= text.charCodeAt(i);
      hash = Math.imul(hash, 16777619);
    }
    return `fnv1a32:${(hash >>> 0).toString(16)}`;
  }
  function makeId(prefix2) {
    sequence += 1;
    return `${prefix2}-${Date.now().toString(36)}-${sequence.toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
  }
  function ensureStableId(key, prefix2) {
    const g = getWindowRecord();
    const existing = g[key];
    if (typeof existing === "string" && existing.length > 0) {
      return existing;
    }
    const next = makeId(prefix2);
    g[key] = next;
    return next;
  }
  function sanitizeUrl(url, stripVolatile) {
    try {
      const parsed = new URL(url, location.href);
      const next = new URL(parsed.toString());
      const keys = [...next.searchParams.keys()];
      for (const key of keys) {
        if (REDACT_QUERY_KEY_REGEX.test(key)) {
          next.searchParams.delete(key);
          continue;
        }
        if (stripVolatile && VOLATILE_QUERY_KEYS.has(key.toLowerCase())) {
          next.searchParams.delete(key);
        }
      }
      const sorted = [...next.searchParams.entries()].sort(([ak, av], [bk, bv]) => {
        if (ak === bk) return av.localeCompare(bv);
        return ak.localeCompare(bk);
      });
      next.search = "";
      for (const [key, value] of sorted) {
        next.searchParams.append(key, value);
      }
      return next.toString();
    } catch {
      return url;
    }
  }
  function parseSearchMode(search) {
    try {
      const params = new URLSearchParams(search || "");
      const raw = (params.get("f") || params.get("src") || "").toLowerCase();
      if (raw.includes("live") || raw.includes("latest")) return "latest";
      if (raw.includes("user") || raw.includes("people")) return "people";
      if (raw.includes("image") || raw.includes("media")) return "media";
      if (raw.includes("top")) return "top";
    } catch {
    }
    return "top";
  }
  function deriveRouteType(pathname, search = "") {
    if (/^\/home\/?$/.test(pathname)) return "home";
    if (/^\/i\/bookmarks(\/|$)/.test(pathname)) return "bookmarks";
    if (/\/status\/\d+/.test(pathname)) return "tweet_detail";
    if (/^\/notifications(\/|$)/.test(pathname)) return "notifications";
    if (/^\/search\/?$/.test(pathname)) {
      const mode = parseSearchMode(search);
      if (mode === "latest") return "search_latest";
      if (mode === "people") return "search_people";
      if (mode === "media") return "search_media";
      return "search_top";
    }
    if (/^\/i\/lists\/\d+\/(members)(\/|$)/.test(pathname)) return "list_members";
    if (/^\/i\/lists\/\d+\/(followers|subscribers)(\/|$)/.test(pathname)) return "list_subscribers";
    if (/^\/i\/lists\//.test(pathname)) return "list";
    if (/^\/i\/communities\/\d+\/(members)(\/|$)/.test(pathname)) return "community_members";
    if (/^\/i\/communities\//.test(pathname)) return "community";
    if (/^\/[A-Za-z0-9_]+\/(followers)(\/|$)/.test(pathname)) return "followers";
    if (/^\/[A-Za-z0-9_]+\/(following)(\/|$)/.test(pathname)) return "following";
    if (/^\/[A-Za-z0-9_]+\/(with_replies)(\/|$)/.test(pathname)) return "user_profile_replies";
    if (/^\/[A-Za-z0-9_]+\/(media)(\/|$)/.test(pathname)) return "user_profile_media";
    if (/^\/[A-Za-z0-9_]+\/(likes)(\/|$)/.test(pathname)) return "user_profile_likes";
    if (/^\/messages(\/|$)/.test(pathname)) return "messages";
    if (/^\/[A-Za-z0-9_]+\/?$/.test(pathname)) return "user_profile_tweets";
    return "unknown";
  }
  function deriveRouteParams(pathname, search = "") {
    const out = {};
    const reservedProfileRoots = /* @__PURE__ */ new Set([
      "home",
      "search",
      "notifications",
      "messages",
      "explore",
      "i"
    ]);
    const tweetMatch = pathname.match(/\/status\/(\d+)/);
    if (tweetMatch == null ? void 0 : tweetMatch[1]) {
      out.tweetId = tweetMatch[1];
    }
    const bookmarkMatch = pathname.match(/\/i\/bookmarks\/(\d+)/);
    if (bookmarkMatch == null ? void 0 : bookmarkMatch[1]) {
      out.folderId = bookmarkMatch[1];
    }
    const listMatch = pathname.match(/\/i\/lists\/(\d+)/);
    if (listMatch == null ? void 0 : listMatch[1]) {
      out.listId = listMatch[1];
    }
    const communityMatch = pathname.match(/\/i\/communities\/(\d+)/);
    if (communityMatch == null ? void 0 : communityMatch[1]) {
      out.communityId = communityMatch[1];
    }
    const profileTabMatch = pathname.match(
      /^\/([A-Za-z0-9_]+)\/(with_replies|media|likes|followers|following)(\/|$)/
    );
    if (profileTabMatch == null ? void 0 : profileTabMatch[1]) {
      out.screenName = profileTabMatch[1];
    }
    if (profileTabMatch == null ? void 0 : profileTabMatch[2]) {
      out.profileTab = profileTabMatch[2];
    }
    const profileRootMatch = pathname.match(/^\/([A-Za-z0-9_]+)\/?$/);
    if ((profileRootMatch == null ? void 0 : profileRootMatch[1]) && !reservedProfileRoots.has(String(profileRootMatch[1]).toLowerCase())) {
      out.screenName = profileRootMatch[1];
    }
    if (/^\/search\/?$/.test(pathname)) {
      out.searchMode = parseSearchMode(search);
    }
    return Object.keys(out).length ? out : void 0;
  }
  function inferResponseContentType(responseText) {
    const trimmed = responseText.trim();
    if (!trimmed) return "text/plain";
    if (trimmed.startsWith("{") || trimmed.startsWith("[")) return "application/json";
    return "text/plain";
  }
  function readMonitorLeaderLease() {
    const raw = readLocalStorageString(RAW_MONITOR_LEADER_STORAGE_KEY);
    if (!raw) return null;
    try {
      const parsed = JSON.parse(raw);
      if (!parsed || typeof parsed !== "object") return null;
      const tabId = typeof parsed.tab_id === "string" ? parsed.tab_id : "";
      if (!tabId) return null;
      const heartbeatMs = toNumber(parsed.heartbeat_ms, 0);
      const acquiredMs = toNumber(parsed.acquired_ms, 0);
      return {
        tab_id: tabId,
        session_id: typeof parsed.session_id === "string" ? parsed.session_id : void 0,
        heartbeat_ms: heartbeatMs,
        acquired_ms: acquiredMs
      };
    } catch {
      return null;
    }
  }
  function writeMonitorLeaderLease(lease) {
    try {
      if (typeof localStorage === "undefined") return null;
      localStorage.setItem(RAW_MONITOR_LEADER_STORAGE_KEY, JSON.stringify(lease));
    } catch {
      return null;
    }
    return readMonitorLeaderLease();
  }
  function clearMonitorLeaderLeaseIfOwned(tabId) {
    if (!tabId) return;
    try {
      if (typeof localStorage === "undefined") return;
      const current = readMonitorLeaderLease();
      if (!current || current.tab_id !== tabId) return;
      localStorage.removeItem(RAW_MONITOR_LEADER_STORAGE_KEY);
    } catch {
    }
  }
  function patchMonitorStats(patch) {
    const stats = readStats();
    writeStats({
      ...stats,
      ...patch
    });
  }
  function publishMonitorCoordinationState(role, leaderTabId, leaseHeartbeatMs) {
    const g = getWindowRecord();
    const state = {
      role,
      tab_id: ensureStableId(RAW_CAPTURE_TAB_KEY, "tab"),
      leader_tab_id: leaderTabId || void 0,
      lease_heartbeat_ms: leaseHeartbeatMs || 0,
      updated_at_ms: Date.now()
    };
    g[RAW_MONITOR_COORDINATION_KEY] = state;
    patchMonitorStats({
      monitor_role: role,
      monitor_leader_tab_id: leaderTabId || void 0,
      monitor_last_heartbeat_ms: leaseHeartbeatMs || 0
    });
    const signature = `${state.role}|${state.tab_id}|${state.leader_tab_id || ""}|${state.lease_heartbeat_ms || 0}`;
    if (signature !== lastMonitorRoleSignature) {
      lastMonitorRoleSignature = signature;
      if (typeof window !== "undefined" && typeof window.dispatchEvent === "function") {
        try {
          window.dispatchEvent(
            new CustomEvent(RAW_MONITOR_ROLE_EVENT_NAME, {
              detail: state
            })
          );
        } catch {
        }
      }
    }
  }
  function refreshMonitorCoordination(forceHeartbeat = false) {
    const tabId = ensureStableId(RAW_CAPTURE_TAB_KEY, "tab");
    const sessionId = ensureStableId(RAW_CAPTURE_SESSION_KEY, "session");
    const now = Date.now();
    let lease = readMonitorLeaderLease();
    if (!lease) {
      const fallback = {
        tab_id: tabId,
        session_id: sessionId,
        heartbeat_ms: now,
        acquired_ms: now
      };
      const written = writeMonitorLeaderLease(fallback);
      if (!written) {
        publishMonitorCoordinationState("single", tabId, now);
        return;
      }
      lease = written;
    }
    const leaseIsStale = toNumber(lease.heartbeat_ms, 0) <= 0 || now - toNumber(lease.heartbeat_ms, 0) > RAW_MONITOR_STALE_MS;
    const weOwnLease = lease.tab_id === tabId;
    if (leaseIsStale || weOwnLease) {
      const shouldHeartbeat = forceHeartbeat || !weOwnLease || now - toNumber(lease.heartbeat_ms, 0) >= Math.floor(RAW_MONITOR_HEARTBEAT_MS / 2);
      if (shouldHeartbeat) {
        const renewed = {
          tab_id: tabId,
          session_id: sessionId,
          acquired_ms: weOwnLease ? toNumber(lease.acquired_ms, now) : now,
          heartbeat_ms: now
        };
        const written = writeMonitorLeaderLease(renewed);
        if ((written == null ? void 0 : written.tab_id) === tabId) {
          publishMonitorCoordinationState("leader", tabId, toNumber(written.heartbeat_ms, now));
          return;
        }
        lease = written || lease;
      }
    }
    const leaderTabId = typeof lease.tab_id === "string" ? lease.tab_id : "";
    publishMonitorCoordinationState("follower", leaderTabId, toNumber(lease.heartbeat_ms, 0));
  }
  function isPassiveMonitorLeader() {
    const g = getWindowRecord();
    const forced = g.__twe_raw_monitor_force_leader_v1;
    if (typeof forced === "boolean") {
      return forced;
    }
    const stateRaw = g[RAW_MONITOR_COORDINATION_KEY];
    const state = stateRaw && typeof stateRaw === "object" ? stateRaw : void 0;
    if (!state || typeof state.role !== "string") {
      refreshMonitorCoordination(true);
      const next = g[RAW_MONITOR_COORDINATION_KEY];
      const nextState = next && typeof next === "object" ? next : void 0;
      return (nextState == null ? void 0 : nextState.role) === "leader" || (nextState == null ? void 0 : nextState.role) === "single";
    }
    return state.role === "leader" || state.role === "single";
  }
  function getRuntimeModeSnapshot() {
    const g = getWindowRecord();
    const rawStats = readStats();
    const runtimeModes = g.__twe_runtime_modes_v1;
    const runtime = g.__twe_runtime_v1;
    const modesObject = runtimeModes && typeof runtimeModes === "object" ? runtimeModes : void 0;
    const runtimeObject = runtime && typeof runtime === "object" ? runtime : void 0;
    const hookRev = toNumber(runtimeObject == null ? void 0 : runtimeObject.revision, 0) || toNumber(runtimeObject == null ? void 0 : runtimeObject.rev, 0) || void 0;
    const capabilitiesRaw = runtimeObject == null ? void 0 : runtimeObject.capabilities;
    const capabilitiesObject = capabilitiesRaw && typeof capabilitiesRaw === "object" ? capabilitiesRaw : void 0;
    return {
      recorder_rev: RAW_CAPTURE_EVENT_REV,
      hook_rev: hookRev,
      modes: {
        safeMode: !!(modesObject == null ? void 0 : modesObject.safeMode),
        hookMode: typeof (modesObject == null ? void 0 : modesObject.hookMode) === "string" ? modesObject.hookMode : void 0,
        repairMode: typeof (modesObject == null ? void 0 : modesObject.repairMode) === "string" ? modesObject.repairMode : void 0
      },
      capabilities: {
        hasExportFunction: !!(capabilitiesObject == null ? void 0 : capabilitiesObject.hasExportFunction),
        hasWrappedJSObject: !!(capabilitiesObject == null ? void 0 : capabilitiesObject.hasWrappedJSObject)
      },
      spool: {
        queued: toNumber(rawStats.spool_count, 0),
        enqueued_total: toNumber(rawStats.spool_enqueued, 0),
        flushed_total: toNumber(rawStats.spool_flushed, 0),
        failed_total: toNumber(rawStats.spool_failed, 0),
        oldest_pending_age_ms: toNumber(rawStats.oldest_pending_age_ms, 0)
      },
      coordination: {
        role: rawStats.monitor_role === "leader" || rawStats.monitor_role === "follower" || rawStats.monitor_role === "single" ? rawStats.monitor_role : void 0,
        leader_tab_id: typeof rawStats.monitor_leader_tab_id === "string" ? rawStats.monitor_leader_tab_id : void 0,
        lease_heartbeat_ms: toNumber(rawStats.monitor_last_heartbeat_ms, 0) || void 0
      }
    };
  }
  function nextRouteEpoch() {
    const g = getWindowRecord();
    const next = toNumber(g[ROUTE_EPOCH_KEY], 0) + 1;
    g[ROUTE_EPOCH_KEY] = next;
    return next;
  }
  function computeLinkedEventHash(eventId, wall, seed) {
    const prevHash = lastEventHash || void 0;
    const eventHash = hashText(`${prevHash || ""}|${eventId}|${wall}|${seed}`);
    lastEventHash = eventHash;
    return {
      prevHash,
      eventHash
    };
  }
  function getAccountHint() {
    const meta = getWindowRecord().__META_DATA__;
    const metaObj = meta && typeof meta === "object" ? meta : void 0;
    const userRestId = typeof (metaObj == null ? void 0 : metaObj.userId) === "string" ? metaObj.userId : void 0;
    return userRestId ? { user_rest_id: userRestId } : void 0;
  }
  function buildEnvelope(req, res) {
    const wall = Date.now();
    const mono = typeof performance !== "undefined" ? Number(performance.now()) : 0;
    const pageUrl = typeof location !== "undefined" ? location.href : "";
    const pathname = typeof location !== "undefined" ? location.pathname : "";
    const search = typeof location !== "undefined" ? location.search : "";
    const reqBodyHash = typeof req.body === "string" && req.body ? hashText(req.body) : void 0;
    const responseText = typeof res.responseText === "string" ? res.responseText : "";
    const respHash = hashText(responseText);
    const responseSampleLimit = getResponseSampleLimit();
    const truncated = responseText.length > responseSampleLimit;
    const responseSample = truncated ? responseText.slice(0, responseSampleLimit) : responseText;
    const eventId = req.requestId && req.requestId.length > 0 ? req.requestId : makeId("evt");
    const { prevHash, eventHash } = computeLinkedEventHash(
      eventId,
      wall,
      `${req.method}|${req.url}|${res.status}|${respHash}`
    );
    return {
      schema: RAW_SCHEMA,
      event_id: eventId,
      prev_event_hash: prevHash,
      event_hash: eventHash,
      wall_time_ms: wall,
      mono_time_ms: mono,
      tz_offset_min: (/* @__PURE__ */ new Date()).getTimezoneOffset(),
      page_url: pageUrl,
      route_type: deriveRouteType(pathname, search),
      route_params: deriveRouteParams(pathname, search),
      route_epoch: nextRouteEpoch(),
      kind: "net",
      session_id: ensureStableId(RAW_CAPTURE_SESSION_KEY, "session"),
      tab_id: ensureStableId(RAW_CAPTURE_TAB_KEY, "tab"),
      account_hint: getAccountHint(),
      net: {
        transport: req.method.toUpperCase() === "GET" ? "xhr" : "fetch",
        phase: "response",
        method: req.method.toUpperCase(),
        url_raw_redacted: sanitizeUrl(req.url, false),
        url_norm: sanitizeUrl(req.url, true),
        status: res.status,
        req_body_hash: reqBodyHash,
        resp_content_type: inferResponseContentType(responseText),
        resp_body_ref: `sha:${respHash}`,
        resp_body_hash: respHash,
        resp_body_size: responseText.length,
        resp_truncated: truncated,
        resp_body_sample: responseSample
      },
      recorder: getRuntimeModeSnapshot()
    };
  }
  function getResponseSampleLimit() {
    if (isDiagnosticCaptureEnabled() || isDaemonStreamingEnabled()) {
      return RESPONSE_SAMPLE_MAX_CHARS_DIAGNOSTIC;
    }
    return RESPONSE_SAMPLE_MAX_CHARS;
  }
  function getRawEventBufferLimit() {
    if (isDiagnosticCaptureEnabled() || isDaemonStreamingEnabled()) {
      return RAW_EVENT_BUFFER_LIMIT_DIAGNOSTIC;
    }
    return RAW_EVENT_BUFFER_LIMIT_DEFAULT;
  }
  function buildRouteEnvelope(source) {
    const wall = Date.now();
    const mono = typeof performance !== "undefined" ? Number(performance.now()) : 0;
    const pageUrl = typeof location !== "undefined" ? location.href : "";
    const pathname = typeof location !== "undefined" ? location.pathname : "";
    const search = typeof location !== "undefined" ? location.search : "";
    const hash = typeof location !== "undefined" ? location.hash : "";
    const eventId = makeId("route");
    const { prevHash, eventHash } = computeLinkedEventHash(
      eventId,
      wall,
      `route|${source}|${pageUrl}|${pathname}|${search}|${hash}`
    );
    return {
      schema: RAW_SCHEMA,
      event_id: eventId,
      prev_event_hash: prevHash,
      event_hash: eventHash,
      wall_time_ms: wall,
      mono_time_ms: mono,
      tz_offset_min: (/* @__PURE__ */ new Date()).getTimezoneOffset(),
      page_url: pageUrl,
      route_type: deriveRouteType(pathname, search),
      route_params: deriveRouteParams(pathname, search),
      route_epoch: nextRouteEpoch(),
      kind: "route",
      session_id: ensureStableId(RAW_CAPTURE_SESSION_KEY, "session"),
      tab_id: ensureStableId(RAW_CAPTURE_TAB_KEY, "tab"),
      account_hint: getAccountHint(),
      route: {
        source,
        pathname,
        search,
        hash
      },
      recorder: getRuntimeModeSnapshot()
    };
  }
  function buildViewportEnvelope(tweetId, source = "dom-scan") {
    const wall = Date.now();
    const mono = typeof performance !== "undefined" ? Number(performance.now()) : 0;
    const pageUrl = typeof location !== "undefined" ? location.href : "";
    const pathname = typeof location !== "undefined" ? location.pathname : "";
    const search = typeof location !== "undefined" ? location.search : "";
    const eventId = makeId("vp");
    const { prevHash, eventHash } = computeLinkedEventHash(
      eventId,
      wall,
      `viewport|${source}|${tweetId}|${pageUrl}|${pathname}`
    );
    return {
      schema: RAW_SCHEMA,
      event_id: eventId,
      prev_event_hash: prevHash,
      event_hash: eventHash,
      wall_time_ms: wall,
      mono_time_ms: mono,
      tz_offset_min: (/* @__PURE__ */ new Date()).getTimezoneOffset(),
      page_url: pageUrl,
      route_type: deriveRouteType(pathname, search),
      route_params: deriveRouteParams(pathname, search),
      route_epoch: toNumber(getWindowRecord()[ROUTE_EPOCH_KEY], 0),
      kind: "viewport",
      session_id: ensureStableId(RAW_CAPTURE_SESSION_KEY, "session"),
      tab_id: ensureStableId(RAW_CAPTURE_TAB_KEY, "tab"),
      account_hint: getAccountHint(),
      viewport: {
        tweet_id: tweetId,
        source
      },
      recorder: getRuntimeModeSnapshot()
    };
  }
  function readStats() {
    const g = getWindowRecord();
    const current = g[RAW_CAPTURE_STATS_KEY];
    if (!current || typeof current !== "object") {
      return {
        total: 0,
        emitted: 0,
        dropped: 0,
        last_at: 0,
        spool_count: 0,
        spool_enqueued: 0,
        spool_flushed: 0,
        spool_failed: 0,
        spool_drop_overflow: 0,
        spool_unavailable: 0,
        oldest_pending_age_ms: 0,
        daemon_online: false,
        monitor_role: "single",
        monitor_leader_tab_id: void 0,
        monitor_last_heartbeat_ms: 0,
        monitor_ticks_route: 0,
        monitor_ticks_viewport: 0,
        monitor_suppressed_route: 0,
        monitor_suppressed_viewport: 0
      };
    }
    const c = current;
    return {
      total: toNumber(c.total, 0),
      emitted: toNumber(c.emitted, 0),
      dropped: toNumber(c.dropped, 0),
      last_at: toNumber(c.last_at, 0),
      last_event_id: typeof c.last_event_id === "string" ? c.last_event_id : void 0,
      last_event_hash: typeof c.last_event_hash === "string" ? c.last_event_hash : void 0,
      spool_count: toNumber(c.spool_count, 0),
      spool_enqueued: toNumber(c.spool_enqueued, 0),
      spool_flushed: toNumber(c.spool_flushed, 0),
      spool_failed: toNumber(c.spool_failed, 0),
      spool_drop_overflow: toNumber(c.spool_drop_overflow, 0),
      spool_unavailable: toNumber(c.spool_unavailable, 0),
      oldest_pending_age_ms: toNumber(c.oldest_pending_age_ms, 0),
      daemon_online: !!c.daemon_online,
      daemon_last_flush_at: toNumber(c.daemon_last_flush_at, 0) || void 0,
      daemon_last_error: typeof c.daemon_last_error === "string" ? c.daemon_last_error : void 0,
      monitor_role: c.monitor_role === "leader" || c.monitor_role === "follower" || c.monitor_role === "single" ? c.monitor_role : void 0,
      monitor_leader_tab_id: typeof c.monitor_leader_tab_id === "string" ? c.monitor_leader_tab_id : void 0,
      monitor_last_heartbeat_ms: toNumber(c.monitor_last_heartbeat_ms, 0),
      monitor_ticks_route: toNumber(c.monitor_ticks_route, 0),
      monitor_ticks_viewport: toNumber(c.monitor_ticks_viewport, 0),
      monitor_suppressed_route: toNumber(c.monitor_suppressed_route, 0),
      monitor_suppressed_viewport: toNumber(c.monitor_suppressed_viewport, 0)
    };
  }
  function writeStats(next) {
    const g = getWindowRecord();
    g[RAW_CAPTURE_STATS_KEY] = next;
    const signature = [
      toNumber(next.spool_count, 0),
      toNumber(next.spool_enqueued, 0),
      toNumber(next.spool_flushed, 0),
      toNumber(next.spool_failed, 0),
      toNumber(next.spool_drop_overflow, 0),
      toNumber(next.spool_unavailable, 0),
      toNumber(next.oldest_pending_age_ms, 0),
      next.daemon_online ? 1 : 0
    ].join("|");
    if (signature !== lastSpoolStateSignature) {
      lastSpoolStateSignature = signature;
      if (typeof window !== "undefined" && typeof window.dispatchEvent === "function") {
        try {
          window.dispatchEvent(
            new CustomEvent(RAW_SPOOL_STATE_EVENT_NAME, {
              detail: {
                stats: next
              }
            })
          );
        } catch {
        }
      }
    }
  }
  function patchStats(patch) {
    const current = readStats();
    writeStats({ ...current, ...patch });
  }
  function pushToGlobalBuffer(event) {
    const g = getWindowRecord();
    const current = g[RAW_EVENT_BUFFER_KEY];
    const events = Array.isArray(current) ? current : [];
    events.push(event);
    let dropped = 0;
    const limit = getRawEventBufferLimit();
    if (events.length > limit) {
      dropped = events.length - limit;
    }
    if (dropped > 0) {
      events.splice(0, dropped);
    }
    g[RAW_EVENT_BUFFER_KEY] = events;
    const stats = readStats();
    writeStats({
      ...stats,
      total: stats.total + 1,
      emitted: stats.emitted + 1,
      dropped: stats.dropped + dropped,
      last_at: Date.now(),
      last_event_id: event.event_id,
      last_event_hash: event.event_hash
    });
    if (typeof window !== "undefined" && typeof window.dispatchEvent === "function") {
      try {
        window.dispatchEvent(
          new CustomEvent(RAW_EVENT_EMIT_NAME, {
            detail: event
          })
        );
      } catch {
      }
    }
  }
  function emitSupplementalEvent(event) {
    pushToGlobalBuffer(event);
    if (isDaemonStreamingEnabled()) {
      void enqueueSpoolEvent(event);
      void flushSpoolToDaemon();
    }
  }
  function isSupplementalMonitoringEnabled() {
    if (!isRawCaptureEnabled()) return false;
    if (typeof document !== "undefined" && document.visibilityState === "hidden") {
      return false;
    }
    return isDaemonStreamingEnabled() || isDiagnosticCaptureEnabled() || isRawCaptureDebugEnabled();
  }
  function monitorRouteChanges() {
    if (!isSupplementalMonitoringEnabled()) return;
    if (typeof document !== "undefined" && document.visibilityState === "hidden") return;
    if (!isPassiveMonitorLeader()) {
      bumpMonitorMetric("route_suppressed_checks", 1);
      const stats = readStats();
      writeStats({
        ...stats,
        monitor_suppressed_route: toNumber(stats.monitor_suppressed_route, 0) + 1
      });
      return;
    }
    const roleStats = readStats();
    writeStats({
      ...roleStats,
      monitor_ticks_route: toNumber(roleStats.monitor_ticks_route, 0) + 1
    });
    bumpMonitorMetric("route_leader_checks", 1);
    const currentUrl = typeof location !== "undefined" ? location.href : "";
    if (!currentUrl) return;
    if (!lastObservedRouteUrl) {
      lastObservedRouteUrl = currentUrl;
      emitSupplementalEvent(buildRouteEnvelope("bootstrap"));
      bumpMonitorMetric("route_emits", 1);
      return;
    }
    if (currentUrl !== lastObservedRouteUrl) {
      lastObservedRouteUrl = currentUrl;
      emitSupplementalEvent(buildRouteEnvelope("poll"));
      bumpMonitorMetric("route_emits", 1);
    }
  }
  function parseTweetIdFromHref(href) {
    const match = href.match(/\/status\/(\d+)/);
    if (!(match == null ? void 0 : match[1])) return null;
    return match[1];
  }
  function isElementMostlyVisible(element) {
    if (!(element instanceof Element)) return false;
    const rect = element.getBoundingClientRect();
    if (rect.width <= 0 || rect.height <= 0) return false;
    if (rect.bottom < RAW_VIEWPORT_MIN_VISIBLE_PX) return false;
    if (rect.top > window.innerHeight - RAW_VIEWPORT_MIN_VISIBLE_PX) return false;
    return true;
  }
  function collectVisibleTweetIds(limit = RAW_VIEWPORT_SCAN_LIMIT) {
    if (typeof document === "undefined") return [];
    const out = [];
    const seen2 = /* @__PURE__ */ new Set();
    const candidates = document.querySelectorAll('article a[href*="/status/"]');
    for (const node of candidates) {
      if (out.length >= limit) break;
      if (!(node instanceof HTMLAnchorElement)) continue;
      if (!isElementMostlyVisible(node)) continue;
      const tweetId = parseTweetIdFromHref(node.getAttribute("href") || "");
      if (!tweetId || seen2.has(tweetId)) continue;
      seen2.add(tweetId);
      out.push(tweetId);
    }
    return out;
  }
  function cleanupViewportSeen(nowMs2) {
    for (const [tweetId, at] of viewportSeenAt.entries()) {
      if (nowMs2 - at > RAW_VIEWPORT_SEEN_WINDOW_MS) {
        viewportSeenAt.delete(tweetId);
      }
    }
  }
  function monitorViewportSightings() {
    if (!isSupplementalMonitoringEnabled()) return;
    if (typeof document !== "undefined" && document.visibilityState === "hidden") return;
    if (!isPassiveMonitorLeader()) {
      bumpMonitorMetric("viewport_suppressed_checks", 1);
      const stats = readStats();
      writeStats({
        ...stats,
        monitor_suppressed_viewport: toNumber(stats.monitor_suppressed_viewport, 0) + 1
      });
      return;
    }
    const roleStats = readStats();
    writeStats({
      ...roleStats,
      monitor_ticks_viewport: toNumber(roleStats.monitor_ticks_viewport, 0) + 1
    });
    bumpMonitorMetric("viewport_leader_checks", 1);
    const now = Date.now();
    cleanupViewportSeen(now);
    const ids = collectVisibleTweetIds();
    for (const tweetId of ids) {
      const seenAt = toNumber(viewportSeenAt.get(tweetId), 0);
      if (seenAt > 0 && now - seenAt < RAW_VIEWPORT_SEEN_WINDOW_MS) {
        continue;
      }
      viewportSeenAt.set(tweetId, now);
      emitSupplementalEvent(buildViewportEnvelope(tweetId));
      bumpMonitorMetric("viewport_emits", 1);
    }
  }
  function isRawCaptureEnabled() {
    const g = getWindowRecord();
    const globalFlag = g.__twe_raw_capture_enabled_v1;
    if (typeof globalFlag === "boolean") {
      return globalFlag;
    }
    const optionFlag = appOptionsManager.get("rawCaptureEnabled", true);
    if (typeof optionFlag === "boolean") {
      return optionFlag;
    }
    const local = readLocalStorageString(RAW_CAPTURE_ENABLED_STORAGE_KEY);
    if (!local) return true;
    return local !== "0" && local !== "false";
  }
  function isRawCaptureDebugEnabled() {
    const g = getWindowRecord();
    if (g.__twe_raw_capture_dev_utils_v1 === true) {
      return true;
    }
    return !!appOptionsManager.get("debug", false);
  }
  function isLoopbackHostname(hostname) {
    const host = hostname.toLowerCase();
    if (host === "localhost" || host === "127.0.0.1" || host === "::1") {
      return true;
    }
    return host.endsWith(".localhost");
  }
  function isInternalRecorderTraffic(url) {
    if (!url) return false;
    try {
      const parsed = new URL(url, location.href);
      const daemon = new URL(getDaemonBaseUrl(), location.href);
      if (parsed.origin === daemon.origin && /^\/(?:ingest|health|stats|query)(\/|$)/.test(parsed.pathname)) {
        return true;
      }
      return isLoopbackHostname(parsed.hostname) && /^\/(?:ingest|health|stats|query)(\/|$)/.test(parsed.pathname);
    } catch {
      return false;
    }
  }
  function isDirectMessagesCaptureEnabled() {
    const optionFlag = appOptionsManager.get("directMessagesCaptureEnabled", false);
    return optionFlag === true && isEncryptedStorageReadyForDm() && isDmSessionArmed();
  }
  function isEncryptedStorageReadyForDm() {
    const g = getWindowRecord();
    if (g.__twe_raw_capture_encryption_ready_v1 === true) {
      return true;
    }
    const optionFlag = appOptionsManager.get("rawCaptureEncryptedStorageReady", false);
    if (optionFlag === true) {
      return true;
    }
    const local = readLocalStorageString(RAW_CAPTURE_ENCRYPTION_READY_STORAGE_KEY);
    if (!local) return false;
    const value = local.trim().toLowerCase();
    return value === "1" || value === "true" || value === "yes" || value === "on";
  }
  function isDmSessionArmed() {
    const g = getWindowRecord();
    const armedUntilGlobal = toNumber(g.__twe_raw_capture_dm_session_armed_until_ms_v1, 0);
    const now = Date.now();
    if (armedUntilGlobal > now) {
      return true;
    }
    const local = readLocalStorageString(RAW_CAPTURE_DM_SESSION_ARMED_UNTIL_STORAGE_KEY);
    const armedUntilLocal = toNumber(local, 0);
    return armedUntilLocal > now;
  }
  function setDmSessionArmedUntil(armedUntilMs) {
    const g = getWindowRecord();
    g.__twe_raw_capture_dm_session_armed_until_ms_v1 = armedUntilMs;
    try {
      localStorage.setItem(RAW_CAPTURE_DM_SESSION_ARMED_UNTIL_STORAGE_KEY, String(armedUntilMs));
    } catch {
    }
  }
  function isPolicyClassEnabled(policyClass) {
    if (policyClass === "sensitive") {
      return appOptionsManager.get("rawCapturePolicySensitiveEnabled", true) !== false;
    }
    if (policyClass === "dm") {
      return appOptionsManager.get("rawCapturePolicyDmEnabled", true) !== false;
    }
    return appOptionsManager.get("rawCapturePolicyPublicEnabled", true) !== false;
  }
  function shouldBlockCaptureByRoutePolicy() {
    let routeType = "unknown";
    if (isDirectMessagesCaptureEnabled()) {
      return { blocked: false, routeType, policyClass: "dm" };
    }
    try {
      routeType = deriveRouteType(location.pathname || "/", location.search || "");
      const policyClass = getPolicyClass(routeType);
      if (!isPolicyClassEnabled(policyClass)) {
        return { blocked: true, routeType, policyClass };
      }
      const allowed = shouldCaptureByPolicy(routeType, {
        dmCaptureAllowed: isDirectMessagesCaptureEnabled()
      });
      return { blocked: !allowed, routeType, policyClass };
    } catch {
      return { blocked: false, routeType: "unknown", policyClass: "public" };
    }
  }
  function shouldCapture(req, res) {
    if (!req.url) return false;
    if (!isRawCaptureEnabled()) return false;
    if (isInternalRecorderTraffic(req.url)) return false;
    const policyDecision = shouldBlockCaptureByRoutePolicy();
    if (policyDecision.blocked) {
      const stats = readStats();
      const statsRecord = stats;
      patchStats({
        dm_policy_blocks: toNumber(statsRecord.dm_policy_blocks, 0) + 1,
        dm_policy_last_route_type: policyDecision.routeType,
        dm_policy_last_policy_class: policyDecision.policyClass
      });
      return false;
    }
    const isApiRoute = /\/graphql\/|\/api\/1\.1\//.test(req.url);
    if (!isApiRoute) return false;
    const text = String(res.responseText || "");
    if (!text) return false;
    return true;
  }
  function openSpoolDb() {
    if (Date.now() < spoolUnavailableUntil) {
      return Promise.reject(new Error("raw spool unavailable"));
    }
    if (typeof indexedDB === "undefined") {
      spoolUnavailableUntil = Date.now() + 6e4;
      patchStats({
        spool_unavailable: toNumber(readStats().spool_unavailable, 0) + 1,
        daemon_last_error: "raw-spool-indexeddb-unavailable"
      });
      return Promise.reject(new Error("indexeddb unavailable"));
    }
    if (spoolDbPromise) {
      return spoolDbPromise;
    }
    spoolDbPromise = new Promise((resolve, reject) => {
      let request;
      try {
        request = indexedDB.open(RAW_SPOOL_DB_NAME, 1);
      } catch (err2) {
        spoolDbPromise = null;
        spoolUnavailableUntil = Date.now() + 6e4;
        patchStats({
          spool_unavailable: toNumber(readStats().spool_unavailable, 0) + 1,
          daemon_last_error: `raw-spool-open-failed:${summarizeError(err2)}`
        });
        reject(err2);
        return;
      }
      request.onupgradeneeded = () => {
        const db = request.result;
        if (!db.objectStoreNames.contains(RAW_SPOOL_STORE_NAME)) {
          const store = db.createObjectStore(RAW_SPOOL_STORE_NAME, { keyPath: "event_id" });
          store.createIndex("created_at", "created_at", { unique: false });
          store.createIndex("next_retry_at", "next_retry_at", { unique: false });
          store.createIndex("wall_time_ms", "wall_time_ms", { unique: false });
        }
      };
      request.onsuccess = () => {
        spoolUnavailableUntil = 0;
        resolve(request.result);
      };
      request.onerror = () => {
        spoolDbPromise = null;
        spoolUnavailableUntil = Date.now() + 6e4;
        patchStats({
          spool_unavailable: toNumber(readStats().spool_unavailable, 0) + 1,
          daemon_last_error: `raw-spool-open-error:${summarizeError(
          request.error ?? new Error("failed to open raw spool db")
        )}`
        });
        reject(request.error ?? new Error("failed to open raw spool db"));
      };
    });
    return spoolDbPromise;
  }
  function runTx(db, mode, handler, onComplete) {
    return new Promise((resolve, reject) => {
      const tx = db.transaction([RAW_SPOOL_STORE_NAME], mode);
      const store = tx.objectStore(RAW_SPOOL_STORE_NAME);
      tx.oncomplete = () => {
        try {
          resolve(onComplete());
        } catch (err2) {
          reject(err2);
        }
      };
      tx.onerror = () => reject(tx.error ?? new Error("raw spool transaction failed"));
      tx.onabort = () => reject(tx.error ?? new Error("raw spool transaction aborted"));
      handler(store);
    });
  }
  async function spoolPut(record) {
    const db = await openSpoolDb();
    await runTx(
      db,
      "readwrite",
      (store) => {
        store.put(record);
      },
      () => void 0
    );
  }
  async function spoolCountAccurate() {
    const db = await openSpoolDb();
    return await new Promise((resolve) => {
      const tx = db.transaction([RAW_SPOOL_STORE_NAME], "readonly");
      const store = tx.objectStore(RAW_SPOOL_STORE_NAME);
      const countReq = store.count();
      countReq.onsuccess = () => resolve(toNumber(countReq.result, 0));
      countReq.onerror = () => resolve(0);
    });
  }
  async function spoolOldestCreatedAtMs() {
    const db = await openSpoolDb();
    return await new Promise((resolve) => {
      const tx = db.transaction([RAW_SPOOL_STORE_NAME], "readonly");
      const store = tx.objectStore(RAW_SPOOL_STORE_NAME);
      const index = store.index("created_at");
      const req = index.openCursor();
      req.onsuccess = () => {
        const cursor = req.result;
        if (!cursor) {
          resolve(0);
          return;
        }
        const value = cursor.value;
        resolve(toNumber(value.created_at, 0));
      };
      req.onerror = () => resolve(0);
    });
  }
  async function spoolPruneOldest(maxRows) {
    const db = await openSpoolDb();
    const currentCount = await spoolCountAccurate();
    if (currentCount <= maxRows) {
      return 0;
    }
    const toDeleteCount = currentCount - maxRows;
    return await new Promise((resolve, reject) => {
      const tx = db.transaction([RAW_SPOOL_STORE_NAME], "readwrite");
      const store = tx.objectStore(RAW_SPOOL_STORE_NAME);
      const index = store.index("created_at");
      const keys = [];
      const cursorReq = index.openCursor();
      cursorReq.onsuccess = () => {
        const cursor = cursorReq.result;
        if (!cursor || keys.length >= toDeleteCount) {
          for (const key of keys) {
            store.delete(key);
          }
          return;
        }
        keys.push(cursor.primaryKey);
        cursor.continue();
      };
      cursorReq.onerror = () => reject(cursorReq.error ?? new Error("cursor read failed"));
      tx.oncomplete = () => resolve(keys.length);
      tx.onerror = () => reject(tx.error ?? new Error("prune tx failed"));
      tx.onabort = () => reject(tx.error ?? new Error("prune tx aborted"));
    });
  }
  async function spoolListFlushBatch(nowMs2, limit) {
    const db = await openSpoolDb();
    return await new Promise((resolve, reject) => {
      const tx = db.transaction([RAW_SPOOL_STORE_NAME], "readonly");
      const store = tx.objectStore(RAW_SPOOL_STORE_NAME);
      const index = store.index("created_at");
      const out = [];
      const cursorReq = index.openCursor();
      cursorReq.onsuccess = () => {
        const cursor = cursorReq.result;
        if (!cursor || out.length >= limit) {
          resolve(out);
          return;
        }
        const value = cursor.value;
        if (toNumber(value.next_retry_at, 0) <= nowMs2) {
          out.push(value);
        }
        cursor.continue();
      };
      cursorReq.onerror = () => reject(cursorReq.error ?? new Error("list batch failed"));
      tx.onerror = () => reject(tx.error ?? new Error("list batch tx failed"));
    });
  }
  async function spoolDeleteByIds(ids) {
    if (!ids.length) return;
    const db = await openSpoolDb();
    await runTx(
      db,
      "readwrite",
      (store) => {
        for (const id2 of ids) {
          store.delete(id2);
        }
      },
      () => void 0
    );
  }
  async function spoolUpdateRecords(records) {
    if (!records.length) return;
    const db = await openSpoolDb();
    await runTx(
      db,
      "readwrite",
      (store) => {
        for (const record of records) {
          store.put(record);
        }
      },
      () => void 0
    );
  }
  async function spoolClearAll() {
    const db = await openSpoolDb();
    await runTx(
      db,
      "readwrite",
      (store) => {
        store.clear();
      },
      () => void 0
    );
    return await spoolCountAccurate();
  }
  async function refreshSpoolStats(prunedOverflow = 0) {
    try {
      const count = await spoolCountAccurate();
      const oldestCreatedAt = await spoolOldestCreatedAtMs();
      const oldestPendingAgeMs = oldestCreatedAt ? Math.max(0, Date.now() - oldestCreatedAt) : 0;
      const stats = readStats();
      writeStats({
        ...stats,
        spool_count: count,
        oldest_pending_age_ms: oldestPendingAgeMs,
        dropped: stats.dropped + Math.max(0, prunedOverflow),
        spool_drop_overflow: toNumber(stats.spool_drop_overflow, 0) + Math.max(0, prunedOverflow)
      });
    } catch {
    }
  }
  async function maintainSpoolBounds() {
    if (Date.now() < spoolUnavailableUntil) {
      patchStats({
        spool_count: 0,
        oldest_pending_age_ms: 0
      });
      return;
    }
    try {
      const pruned = await spoolPruneOldest(RAW_SPOOL_MAX_ROWS);
      await refreshSpoolStats(pruned);
    } catch {
    }
  }
  function computeRetryDelayMs(attempts) {
    const exp = Math.max(0, Math.min(6, attempts));
    return Math.min(6e4, 1e3 * 2 ** exp);
  }
  function readLocalStorageString(key) {
    try {
      if (typeof localStorage === "undefined") return null;
      return localStorage.getItem(key);
    } catch {
      return null;
    }
  }
  function isDaemonStreamingEnabled() {
    const g = getWindowRecord();
    const globalFlag = g.__twe_raw_capture_stream_enabled_v1;
    if (typeof globalFlag === "boolean") {
      return globalFlag;
    }
    const optionFlag = appOptionsManager.get("rawCaptureStreamEnabled", false);
    if (typeof optionFlag === "boolean") {
      return optionFlag;
    }
    const local = readLocalStorageString(RAW_DAEMON_STREAM_ENABLED_STORAGE_KEY);
    if (!local) return false;
    return local === "1" || local === "true";
  }
  function getDaemonBaseUrl() {
    const g = getWindowRecord();
    const globalValue = g.__twe_raw_capture_daemon_url_v1;
    const optionValue = appOptionsManager.get("rawCaptureDaemonUrl", "http://127.0.0.1:8754");
    const local = typeof globalValue === "string" && globalValue.trim().length > 0 ? globalValue.trim() : typeof optionValue === "string" && optionValue.trim().length > 0 ? optionValue.trim() : readLocalStorageString(RAW_DAEMON_BASE_URL_STORAGE_KEY) || "http://127.0.0.1:8754";
    return local.replace(/\/+$/, "");
  }
  async function enqueueSpoolEvent(event) {
    if (Date.now() < spoolUnavailableUntil) {
      patchStats({
        spool_unavailable: toNumber(readStats().spool_unavailable, 0) + 1
      });
      return;
    }
    try {
      const now = Date.now();
      const record = {
        event_id: event.event_id,
        wall_time_ms: event.wall_time_ms,
        created_at: now,
        attempts: 0,
        next_retry_at: now,
        payload: event
      };
      await spoolPut(record);
      const pruned = await spoolPruneOldest(RAW_SPOOL_MAX_ROWS);
      const stats = readStats();
      writeStats({
        ...stats,
        spool_enqueued: toNumber(stats.spool_enqueued, 0) + 1,
        dropped: stats.dropped + Math.max(0, pruned),
        spool_drop_overflow: toNumber(stats.spool_drop_overflow, 0) + Math.max(0, pruned)
      });
      await refreshSpoolStats();
    } catch (err2) {
      patchStats({
        spool_unavailable: toNumber(readStats().spool_unavailable, 0) + 1,
        daemon_last_error: `spool-enqueue-error:${summarizeError(err2)}`
      });
    }
  }
  function stopSupplementalMonitoring() {
    const g = getWindowRecord();
    clearWindowTimer(RAW_MONITOR_COORD_TIMER_KEY);
    clearWindowTimer(RAW_ROUTE_MONITOR_TIMER_KEY);
    clearWindowTimer(RAW_VIEWPORT_MONITOR_TIMER_KEY);
    viewportSeenAt.clear();
    const storageListener = g[RAW_MONITOR_STORAGE_LISTENER_KEY];
    if (typeof storageListener === "function" && typeof window !== "undefined") {
      try {
        window.removeEventListener("storage", storageListener);
      } catch {
      }
    }
    delete g[RAW_MONITOR_STORAGE_LISTENER_KEY];
    const beforeUnloadListener = g[RAW_MONITOR_BEFOREUNLOAD_KEY];
    if (typeof beforeUnloadListener === "function" && typeof window !== "undefined") {
      try {
        window.removeEventListener("beforeunload", beforeUnloadListener);
      } catch {
      }
    }
    delete g[RAW_MONITOR_BEFOREUNLOAD_KEY];
    try {
      const tabId = ensureStableId(RAW_CAPTURE_TAB_KEY, "tab");
      clearMonitorLeaderLeaseIfOwned(tabId);
    } catch {
    }
    delete g[RAW_MONITOR_COORDINATION_KEY];
    patchMonitorStats({
      monitor_role: void 0,
      monitor_leader_tab_id: void 0,
      monitor_last_heartbeat_ms: 0
    });
  }
  function syncSpoolFlushLoop() {
    if (isDaemonStreamingEnabled()) {
      const g = getWindowRecord();
      const existing = g[RAW_SPOOL_FLUSH_TIMER_KEY];
      if (!(typeof existing === "number" || typeof existing === "object")) {
        const timer = setInterval(() => {
          spoolMaintenanceTick += 1;
          if (spoolMaintenanceTick % RAW_SPOOL_MAINTENANCE_EVERY_TICKS === 0) {
            void maintainSpoolBounds();
          }
          void flushSpoolToDaemon();
        }, RAW_SPOOL_FLUSH_INTERVAL_MS);
        g[RAW_SPOOL_FLUSH_TIMER_KEY] = timer;
      }
      return;
    }
    clearWindowTimer(RAW_SPOOL_FLUSH_TIMER_KEY);
  }
  function ensureFlushLoopStarted() {
    const g = getWindowRecord();
    if (isRawCaptureDebugEnabled() && typeof g[RAW_SPOOL_DEV_CLEAR_KEY] !== "function") {
      g[RAW_SPOOL_DEV_CLEAR_KEY] = async () => {
        try {
          const remaining = await spoolClearAll();
          const stats = readStats();
          writeStats({
            ...stats,
            spool_count: remaining,
            oldest_pending_age_ms: 0
          });
          return { ok: true, remaining };
        } catch (err2) {
          const message = summarizeError(err2);
          patchStats({
            daemon_last_error: `spool-clear-error:${message}`
          });
          return { ok: false, error: message };
        }
      };
    }
    if (typeof g[RAW_MONITOR_DEV_TICK_KEY] !== "function") {
      g[RAW_MONITOR_DEV_TICK_KEY] = (mode = "both") => {
        try {
          if (mode === "both" || mode === "route") {
            monitorRouteChanges();
          }
          if (mode === "both" || mode === "viewport") {
            monitorViewportSightings();
          }
          return { ok: true };
        } catch (err2) {
          return { ok: false, error: summarizeError(err2) };
        }
      };
    }
    if (typeof g.__twe_arm_dm_capture_v1 !== "function") {
      g.__twe_arm_dm_capture_v1 = (durationMs = RAW_CAPTURE_DM_SESSION_DEFAULT_ARM_MS) => {
        const now = Date.now();
        const nextUntil = now + Math.max(3e4, toNumber(durationMs, RAW_CAPTURE_DM_SESSION_DEFAULT_ARM_MS));
        setDmSessionArmedUntil(nextUntil);
        return { ok: true, armed_until_ms: nextUntil };
      };
    }
    if (typeof g.__twe_disarm_dm_capture_v1 !== "function") {
      g.__twe_disarm_dm_capture_v1 = () => {
        setDmSessionArmedUntil(0);
        return { ok: true, armed_until_ms: 0 };
      };
    }
    if (isSupplementalMonitoringEnabled()) {
      refreshMonitorCoordination(true);
      if (typeof g[RAW_MONITOR_STORAGE_LISTENER_KEY] !== "function") {
        const onStorage = (event) => {
          if (event.key !== RAW_MONITOR_LEADER_STORAGE_KEY) return;
          try {
            refreshMonitorCoordination(true);
          } catch {
          }
        };
        g[RAW_MONITOR_STORAGE_LISTENER_KEY] = onStorage;
        if (typeof window !== "undefined" && typeof window.addEventListener === "function") {
          try {
            window.addEventListener("storage", onStorage);
          } catch {
          }
        }
      }
      if (typeof g[RAW_MONITOR_BEFOREUNLOAD_KEY] !== "function") {
        const onBeforeUnload = () => {
          try {
            const tabId = ensureStableId(RAW_CAPTURE_TAB_KEY, "tab");
            clearMonitorLeaderLeaseIfOwned(tabId);
          } catch {
          }
        };
        g[RAW_MONITOR_BEFOREUNLOAD_KEY] = onBeforeUnload;
        if (typeof window !== "undefined" && typeof window.addEventListener === "function") {
          try {
            window.addEventListener("beforeunload", onBeforeUnload);
          } catch {
          }
        }
      }
      const coordTimer = g[RAW_MONITOR_COORD_TIMER_KEY];
      if (!(typeof coordTimer === "number" || typeof coordTimer === "object")) {
        const timer = setInterval(() => {
          if (typeof document !== "undefined" && document.visibilityState === "hidden") {
            return;
          }
          try {
            refreshMonitorCoordination(true);
          } catch {
          }
        }, RAW_MONITOR_HEARTBEAT_MS);
        g[RAW_MONITOR_COORD_TIMER_KEY] = timer;
      }
      const routeTimer = g[RAW_ROUTE_MONITOR_TIMER_KEY];
      if (!(typeof routeTimer === "number" || typeof routeTimer === "object")) {
        const timer = setInterval(() => {
          try {
            monitorRouteChanges();
          } catch {
          }
        }, RAW_ROUTE_MONITOR_INTERVAL_MS);
        g[RAW_ROUTE_MONITOR_TIMER_KEY] = timer;
        try {
          monitorRouteChanges();
        } catch {
        }
      }
      const viewportTimer = g[RAW_VIEWPORT_MONITOR_TIMER_KEY];
      if (!(typeof viewportTimer === "number" || typeof viewportTimer === "object")) {
        const timer = setInterval(() => {
          try {
            monitorViewportSightings();
          } catch {
          }
        }, RAW_VIEWPORT_MONITOR_INTERVAL_MS);
        g[RAW_VIEWPORT_MONITOR_TIMER_KEY] = timer;
      }
    } else {
      stopSupplementalMonitoring();
    }
    syncSpoolFlushLoop();
  }
  function summarizeError(err2) {
    if (err2 instanceof Error) {
      return `${err2.name}: ${err2.message}`;
    }
    return String(err2);
  }
  async function sendBatchToDaemon(batch) {
    const daemonBaseUrl = getDaemonBaseUrl();
    const response = await fetch(`${daemonBaseUrl}/ingest/events`, {
      method: "POST",
      headers: {
        "content-type": "application/json"
      },
      body: JSON.stringify({
        events: batch.map((record) => record.payload)
      })
    });
    if (!response.ok) {
      throw new Error(`daemon-response-${response.status}`);
    }
    let parsed = null;
    try {
      parsed = await response.json();
    } catch {
      return batch.map((record) => record.event_id);
    }
    const accepted = parsed && typeof parsed === "object" && Array.isArray(parsed.accepted_ids) ? parsed.accepted_ids.map((value) => typeof value === "string" ? value : "").filter((value) => !!value) : null;
    if (!accepted || !accepted.length) {
      return batch.map((record) => record.event_id);
    }
    return accepted;
  }
  async function flushSpoolToDaemon() {
    if (flushInFlight) {
      return;
    }
    flushInFlight = true;
    try {
      if (Date.now() < spoolUnavailableUntil) {
        patchStats({
          spool_count: 0,
          oldest_pending_age_ms: 0,
          daemon_online: false
        });
        return;
      }
      const count = await spoolCountAccurate();
      await refreshSpoolStats();
      if (!isDaemonStreamingEnabled()) {
        patchStats({ daemon_online: false });
        return;
      }
      if (count <= 0) {
        patchStats({
          daemon_online: true,
          daemon_last_flush_at: Date.now(),
          oldest_pending_age_ms: 0
        });
        return;
      }
      const now = Date.now();
      const batch = await spoolListFlushBatch(now, RAW_SPOOL_FLUSH_BATCH_SIZE);
      if (!batch.length) {
        patchStats({ daemon_online: true });
        await refreshSpoolStats();
        return;
      }
      try {
        const acceptedIds = await sendBatchToDaemon(batch);
        const acceptedSet = new Set(acceptedIds);
        const rejected = batch.filter((record) => !acceptedSet.has(record.event_id));
        if (acceptedIds.length) {
          await spoolDeleteByIds(acceptedIds);
        }
        if (rejected.length) {
          const retryNow = Date.now();
          const next = rejected.map((record) => {
            const attempts = toNumber(record.attempts, 0) + 1;
            return {
              ...record,
              attempts,
              next_retry_at: retryNow + computeRetryDelayMs(attempts)
            };
          });
          await spoolUpdateRecords(next);
        }
        const stats = readStats();
        writeStats({
          ...stats,
          spool_flushed: toNumber(stats.spool_flushed, 0) + acceptedIds.length,
          daemon_online: true,
          daemon_last_flush_at: Date.now(),
          daemon_last_error: void 0
        });
        await refreshSpoolStats();
      } catch (err2) {
        const retryNow = Date.now();
        const next = batch.map((record) => {
          const attempts = toNumber(record.attempts, 0) + 1;
          return {
            ...record,
            attempts,
            next_retry_at: retryNow + computeRetryDelayMs(attempts)
          };
        });
        await spoolUpdateRecords(next);
        const stats = readStats();
        writeStats({
          ...stats,
          spool_failed: toNumber(stats.spool_failed, 0) + batch.length,
          daemon_online: false,
          daemon_last_error: summarizeError(err2)
        });
        await refreshSpoolStats();
      }
    } catch (err2) {
      patchStats({ daemon_last_error: `flush-error:${summarizeError(err2)}` });
    } finally {
      flushInFlight = false;
    }
  }
  const RawCaptureInterceptor = (req, res) => {
    try {
      if (!isRawCaptureEnabled()) {
        return;
      }
      const request = {
        method: typeof req.method === "string" && req.method ? req.method : "GET",
        url: typeof req.url === "string" ? req.url : "",
        body: typeof req.body === "string" ? req.body : void 0,
        requestId: typeof req.requestId === "string" ? req.requestId : void 0
      };
      const response = {
        status: Number(res.status ?? 0),
        responseText: String(res.responseText || "")
      };
      if (!shouldCapture(request, response)) {
        return;
      }
      ensureFlushLoopStarted();
      const envelope = buildEnvelope(request, response);
      pushToGlobalBuffer(envelope);
      if (isDaemonStreamingEnabled()) {
        void enqueueSpoolEvent(envelope);
        void flushSpoolToDaemon();
      }
    } catch {
    }
  };
  class RawCaptureModule extends Extension {
    constructor() {
      super(...arguments);
      __publicField(this, "name", "RawCaptureModule");
      __publicField(this, "type", ExtensionType.CUSTOM);
    }
    intercept() {
      return RawCaptureInterceptor;
    }
  }
  function getSearchTimelineVariables(req) {
    try {
      const url = new URL(req.url);
      const raw = url.searchParams.get("variables");
      if (!raw) return null;
      const parsed = JSON.parse(raw);
      return parsed && typeof parsed === "object" ? parsed : null;
    } catch {
      return null;
    }
  }
  function isQuoteTweetSearchTimelineRequest(req) {
    const variables = getSearchTimelineVariables(req);
    const rawQuery = String((variables == null ? void 0 : variables.rawQuery) || "").trim().toLowerCase();
    const querySource = String((variables == null ? void 0 : variables.querySource) || "").trim().toLowerCase();
    return rawQuery.startsWith("quoted_tweet_id:") || querySource === "tdqt";
  }
  function parseSearchTimelineResponse(res) {
    const json = JSON.parse(res.responseText);
    const instructions = json.data.search_by_raw_query.search_timeline.timeline.instructions;
    const newTweets = [];
    const newUsers = [];
    const newLists = [];
    const timelineAddEntriesInstruction = instructions.find(
      (i) => i.type === "TimelineAddEntries"
    );
    const timelineAddToModuleInstruction = instructions.find(
      (i) => i.type === "TimelineAddToModule"
    );
    const timelineAddEntriesInstructionEntries = (timelineAddEntriesInstruction == null ? void 0 : timelineAddEntriesInstruction.entries) ?? [];
    for (const entry of timelineAddEntriesInstructionEntries) {
      if (isTimelineEntryTweet(entry)) {
        const tweet = extractTimelineTweet(entry.content.itemContent);
        if (tweet) {
          newTweets.push(tweet);
        }
      }
      if (isTimelineEntrySearchGrid(entry)) {
        const tweetsInSearchGrid = entry.content.items.map((i) => extractTimelineTweet(i.item.itemContent)).filter((t) => !!t);
        newTweets.push(...tweetsInSearchGrid);
      }
      if (isTimelineEntryUser(entry)) {
        const user = extractTimelineUser(entry.content.itemContent);
        if (user) {
          newUsers.push(user);
        }
      }
      if (isTimelineEntryListSearch(entry)) {
        const lists = entry.content.items.map((i) => i.item.itemContent.list);
        newLists.push(...lists);
      }
    }
    if (timelineAddToModuleInstruction) {
      const items = timelineAddToModuleInstruction.moduleItems.map((i) => i.item.itemContent);
      const tweets = items.filter((i) => i.__typename === "TimelineTweet").map((t) => extractTimelineTweet(t)).filter((t) => !!t);
      newTweets.push(...tweets);
      const lists = items.filter((i) => i.__typename === "TimelineTwitterList").map((i) => i.list);
      newLists.push(...lists);
    }
    return { tweets: newTweets, users: newUsers, lists: newLists };
  }
  const SearchTimelineInterceptor = createModuleInterceptor({
    moduleName: "SearchTimeline",
    match: (req) => /\/graphql\/.+\/SearchTimeline/.test(req.url) && !isQuoteTweetSearchTimelineRequest(req),
    parse: (_req, res) => parseSearchTimelineResponse(res),
    count: (parsed) => parsed.tweets.length,
    project: (extName, parsed) => projectTweets(extName, parsed.tweets),
    onSuccess: (parsed) => {
      if (parsed.lists.length > 0) {
        logger.warn(
          `SearchList: ${parsed.lists.length} lists received but ignored (Reason: not implemented)`,
          parsed.lists
        );
      }
      if (parsed.users.length > 0) {
        logger.warn(
          `SearchUser: ${parsed.users.length} users received but ignored (Reason: not implemented)`,
          parsed.users
        );
      }
    }
  });
  const QuotesInterceptor = createModuleInterceptor({
    moduleName: "Quotes",
    match: (req) => /\/graphql\/.+\/SearchTimeline/.test(req.url) && isQuoteTweetSearchTimelineRequest(req),
    parse: (_req, res) => {
      const parsed = parseSearchTimelineResponse(res);
      return { tweets: parsed.tweets };
    },
    count: (parsed) => parsed.tweets.length,
    project: (extName, parsed) => projectTweets(extName, parsed.tweets),
    onSuccess: (parsed) => {
      logger.debug(`Quotes: projected ${parsed.tweets.length} quote-tweet rows`);
    }
  });
  class QuotesModule extends Extension {
    constructor() {
      super(...arguments);
      __publicField(this, "name", "QuotesModule");
      __publicField(this, "type", ExtensionType.TWEET);
    }
    intercept() {
      return QuotesInterceptor;
    }
    render() {
      return CommonModuleUI;
    }
  }
  class SearchTimelineModule extends Extension {
    constructor() {
      super(...arguments);
      __publicField(this, "name", "SearchTimelineModule");
      __publicField(this, "type", ExtensionType.TWEET);
    }
    intercept() {
      return SearchTimelineInterceptor;
    }
    render() {
      return CommonModuleUI;
    }
  }
  const TWEET_INDEX_MODULE_NAME = "TweetIndexModule";
  class TweetIndexModule extends Extension {
    constructor() {
      super(...arguments);
      __publicField(this, "name", TWEET_INDEX_MODULE_NAME);
      __publicField(this, "type", ExtensionType.TWEET);
    }
    setup() {
      void dbProxy.extBackfillTweetCapturesFromAllTweets(this.name);
    }
    render() {
      return CommonModuleUI;
    }
  }
  const TweetDetailInterceptor = createModuleInterceptor({
    moduleName: "TweetDetail",
    match: (req) => /\/graphql\/.+\/TweetDetail/.test(req.url) || /\/graphql\/.+\/ModeratedTimeline/.test(req.url),
    parse: (req, res) => {
      const isTweetDetail = /\/graphql\/.+\/TweetDetail/.test(req.url);
      const isModeratedTimeline = /\/graphql\/.+\/ModeratedTimeline/.test(req.url);
      const json = JSON.parse(res.responseText);
      let instructions = [];
      if (isTweetDetail) {
        instructions = json.data.threaded_conversation_with_injections_v2.instructions;
      } else if (isModeratedTimeline) {
        instructions = json.data.tweet.result.timeline_response.timeline.instructions;
      }
      const newData = [];
      const timelineAddEntriesInstruction = instructions.find(
        (i) => i.type === "TimelineAddEntries"
      );
      const timelineAddEntriesInstructionEntries = (timelineAddEntriesInstruction == null ? void 0 : timelineAddEntriesInstruction.entries) ?? [];
      for (const entry of timelineAddEntriesInstructionEntries) {
        if (isTimelineEntryTweet(entry)) {
          const tweet = extractTimelineTweet(entry.content.itemContent);
          if (tweet) {
            newData.push(tweet);
          }
        }
        if (isTweetDetail && isTimelineEntryConversationThread(entry)) {
          const tweetsInConversation = entry.content.items.map((i) => {
            if (i.entryId.includes("-tweet-")) {
              return extractTimelineTweet(i.item.itemContent);
            }
          });
          newData.push(...tweetsInConversation.filter((t) => !!t));
        }
      }
      const timelineAddToModuleInstruction = instructions.find(
        (i) => i.type === "TimelineAddToModule"
      );
      if (timelineAddToModuleInstruction) {
        const tweetsInConversation = timelineAddToModuleInstruction.moduleItems.map((i) => extractTimelineTweet(i.item.itemContent)).filter((t) => !!t);
        newData.push(...tweetsInConversation);
      }
      return newData;
    },
    project: (extName, tweets) => projectTweets(extName, tweets)
  });
  class TweetDetailModule extends Extension {
    constructor() {
      super(...arguments);
      __publicField(this, "name", "TweetDetailModule");
      __publicField(this, "type", ExtensionType.TWEET);
    }
    intercept() {
      return TweetDetailInterceptor;
    }
    render() {
      return CommonModuleUI;
    }
  }
  const UserDetailInterceptor = createModuleInterceptor({
    moduleName: "UserDetail",
    match: (req) => /\/graphql\/.+\/UserByScreenName/.test(req.url),
    parse: (_req, res) => {
      const json = JSON.parse(res.responseText);
      return [json.data.user.result];
    },
    project: (extName, users) => projectUsers(extName, users)
  });
  class UserDetailModule extends Extension {
    constructor() {
      super(...arguments);
      __publicField(this, "name", "UserDetailModule");
      __publicField(this, "type", ExtensionType.USER);
    }
    intercept() {
      return UserDetailInterceptor;
    }
    render() {
      return CommonModuleUI;
    }
  }
  const UserMediaInterceptor = createModuleInterceptor({
    moduleName: "UserMedia",
    match: (req) => /\/graphql\/.+\/UserMedia/.test(req.url),
    parse: (_req, res) => {
      const json = JSON.parse(res.responseText);
      const instructions = json.data.user.result.timeline.timeline.instructions;
      const newData = [];
      const timelineAddEntriesInstruction = instructions.find(
        (i) => i.type === "TimelineAddEntries"
      );
      const timelineAddEntriesInstructionEntries = (timelineAddEntriesInstruction == null ? void 0 : timelineAddEntriesInstruction.entries) ?? [];
      for (const entry of timelineAddEntriesInstructionEntries) {
        if (isTimelineEntryProfileGrid(entry)) {
          const tweetsInSearchGrid = entry.content.items.map((i) => extractTimelineTweet(i.item.itemContent)).filter((t) => !!t);
          newData.push(...tweetsInSearchGrid);
        }
      }
      const timelineAddToModuleInstruction = instructions.find(
        (i) => i.type === "TimelineAddToModule"
      );
      if (timelineAddToModuleInstruction) {
        const tweetsInProfileGrid = timelineAddToModuleInstruction.moduleItems.map((i) => extractTimelineTweet(i.item.itemContent)).filter((t) => !!t);
        newData.push(...tweetsInProfileGrid);
      }
      return newData;
    },
    project: (extName, tweets) => projectTweets(extName, tweets)
  });
  class UserMediaModule extends Extension {
    constructor() {
      super(...arguments);
      __publicField(this, "name", "UserMediaModule");
      __publicField(this, "type", ExtensionType.TWEET);
    }
    intercept() {
      return UserMediaInterceptor;
    }
    render() {
      return CommonModuleUI;
    }
  }
  const UserTweetsInterceptor = createModuleInterceptor({
    moduleName: "UserTweets",
    match: (req) => /\/graphql\/.+\/UserTweets/.test(req.url),
    parse: (_req, res) => {
      const json = JSON.parse(res.responseText);
      const instructions = json.data.user.result.timeline.timeline.instructions;
      const newData = [];
      const timelinePinEntryInstruction = instructions.find(
        (i) => i.type === "TimelinePinEntry"
      );
      if (timelinePinEntryInstruction) {
        const tweet = extractTimelineTweet(timelinePinEntryInstruction.entry.content.itemContent);
        if (tweet) {
          newData.push(tweet);
        }
      }
      const timelineAddEntriesInstruction = instructions.find(
        (i) => i.type === "TimelineAddEntries"
      );
      const timelineAddEntriesInstructionEntries = (timelineAddEntriesInstruction == null ? void 0 : timelineAddEntriesInstruction.entries) ?? [];
      for (const entry of timelineAddEntriesInstructionEntries) {
        if (isTimelineEntryTweet(entry)) {
          const tweet = extractTimelineTweet(entry.content.itemContent);
          if (tweet) {
            newData.push(tweet);
          }
        }
        if (isTimelineEntryProfileConversation(entry)) {
          const tweetsInConversation = entry.content.items.map((i) => extractTimelineTweet(i.item.itemContent)).filter((t) => !!t);
          newData.push(...tweetsInConversation);
        }
      }
      return newData;
    },
    project: (extName, tweets) => projectTweets(extName, tweets)
  });
  class UserTweetsModule extends Extension {
    constructor() {
      super(...arguments);
      __publicField(this, "name", "UserTweetsModule");
      __publicField(this, "type", ExtensionType.TWEET);
    }
    intercept() {
      return UserTweetsInterceptor;
    }
    render() {
      return CommonModuleUI;
    }
  }
  const APP_ROOT_ID = "twe-root";
  const APP_ROOT_MOUNTED_FLAG = "__twe_root_mounted_v1";
  const windowScope = globalThis;
  function isUserscriptOrigin(value) {
    const text = String(value ?? "");
    return text.includes("Twitter Web Exporter") || text.includes("Twitter%20Web%20Exporter") || text.includes("Scrollmark") || text.includes("twitter-web-exporter") || text.includes("moz-extension://");
  }
  function installUserscriptErrorGuard() {
    try {
      window.addEventListener(
        "error",
        (event) => {
          var _a2;
          try {
            const fromUserscript = isUserscriptOrigin(event.filename) || isUserscriptOrigin(event.message) || isUserscriptOrigin((_a2 = event.error) == null ? void 0 : _a2.stack);
            if (!fromUserscript) return;
            console.error(
              "[twitter-web-exporter] suppressed global error",
              event.error || event.message
            );
            event.preventDefault();
            event.stopImmediatePropagation();
          } catch {
          }
        },
        true
      );
    } catch {
    }
    try {
      window.addEventListener(
        "unhandledrejection",
        (event) => {
          try {
            const reason = event.reason;
            const fromUserscript = isUserscriptOrigin(reason == null ? void 0 : reason.stack) || isUserscriptOrigin(reason == null ? void 0 : reason.message) || isUserscriptOrigin(reason);
            if (!fromUserscript) return;
            console.error("[twitter-web-exporter] suppressed rejection", reason);
            event.preventDefault();
            event.stopImmediatePropagation();
          } catch {
          }
        },
        true
      );
    } catch {
    }
  }
  function safeAddExtension(manager, ctor) {
    try {
      manager.add(ctor);
    } catch (err2) {
      console.error("[twitter-web-exporter] Failed to add extension", ctor == null ? void 0 : ctor.name, err2);
    }
  }
  function mountApp() {
    try {
      const existingRoot = document.getElementById(APP_ROOT_ID);
      if (windowScope[APP_ROOT_MOUNTED_FLAG]) {
        if (existingRoot) {
          return;
        }
        windowScope[APP_ROOT_MOUNTED_FLAG] = false;
      }
      const root = existingRoot ?? document.createElement("div");
      if (!existingRoot) {
        root.id = APP_ROOT_ID;
        document.body.append(root);
      }
      windowScope[APP_ROOT_MOUNTED_FLAG] = true;
      preact.render(/* @__PURE__ */ u(App, {}), root);
    } catch (err2) {
      console.error("[twitter-web-exporter] mountApp failed", err2);
    }
  }
  function bootstrap() {
    installUserscriptErrorGuard();
    initializePerformanceMonitoring();
    try {
      const manager = getExtensionManager();
      safeAddExtension(manager, FollowersModule);
      safeAddExtension(manager, FollowingModule);
      safeAddExtension(manager, UserDetailModule);
      safeAddExtension(manager, ListMembersModule);
      safeAddExtension(manager, ListSubscribersModule);
      safeAddExtension(manager, CommunityMembersModule);
      safeAddExtension(manager, RetweetersModule);
      safeAddExtension(manager, HomeTimelineModule);
      safeAddExtension(manager, ListTimelineModule);
      safeAddExtension(manager, CommunityTimelineModule);
      safeAddExtension(manager, BookmarksModule);
      safeAddExtension(manager, QuotesModule);
      safeAddExtension(manager, LikesModule);
      safeAddExtension(manager, TweetIndexModule);
      safeAddExtension(manager, UserTweetsModule);
      safeAddExtension(manager, UserMediaModule);
      safeAddExtension(manager, TweetDetailModule);
      safeAddExtension(manager, SearchTimelineModule);
      safeAddExtension(manager, InteractionEventsModule);
      if (appOptionsManager.get("directMessagesCaptureEnabled", false)) {
        safeAddExtension(manager, DirectMessagesModule);
      }
      safeAddExtension(manager, LocalSearchModule);
      safeAddExtension(manager, RuntimeLogsModule);
      if (appOptionsManager.get("rawCaptureEnabled", true)) {
        safeAddExtension(manager, RawCaptureModule);
      }
      extensionManagerProxy.start();
    } catch (err2) {
      console.error("[twitter-web-exporter] bootstrap failed", err2);
      setTimeout(() => {
        try {
          const manager = getExtensionManager();
          safeAddExtension(manager, BookmarksModule);
          extensionManagerProxy.start();
        } catch (retryErr) {
          console.error("[twitter-web-exporter] bootstrap retry failed", retryErr);
        }
      }, 250);
    }
    if (document.readyState === "loading") {
      document.addEventListener("DOMContentLoaded", mountApp);
    } else {
      mountApp();
    }
  }
  bootstrap();

})(preact, preactHooks, i18next, preactSignals, dayjs, FileSaver, Dexie, DexieExportImport, TableCore);