// ==UserScript==
// @name Twitter Web Exporter
// @namespace https://github.com/prinsss
// @version 1.0.10
// @author prin <[email protected]>
// @description Export tweets, bookmarks, lists and much more from Twitter(X) web app.
// @license MIT
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABmklEQVR4Ae3XA4wcARSA4dq2bUQ1g9pRbVtBzai2otpug9pxUttn2753/3m9Ozq/5NsdvvfGM6VKoshE8/ORFbAMbxCGWHzDHjS2sXxPlM0eKYclGoq3w1eIHVGYikaYg6e4ZppgAgQrVBSvDw+IEylIhSAATUyTHIYgFdsUNnAGosAfDMccLMtOchli4g7quFC8FhIhCsRD8Bk1sxMdgVjwxRyUdtDABIgKH9DQNNEkiB1fMB9VbDSwEKLQJ1S1TFQRXhAHYnADy9ETdTEeotAze7tzNJIhCiRBFLpnq/hmzMR65UkVO2WrgaOQPLLW3u6XPDLAVgOl8R5isEhUtHcSdkEoxEBXnN3ZuuMbxCDDnTVQF52xBcEQHX1BaWcNtDLwMpzg6tNtN0RnD5U8XsviGkQnYWih9CWjNBbDHaJBMsZqec8rjV54B1EoFXO0Fh+DrxCFEjBTTdFy6IvNGu4Hf9FXSdGheAUvjZdgLPajqtp3+jl4jVSIAgHYjRZ6fWC0wSpcwScEQZCMUPzEfezEYJQrVRKFOdIAZGq1QBG8EiYAAAAASUVORK5CYII=
// @homepage https://github.com/prinsss/twitter-web-exporter
// @homepageURL https://github.com/prinsss/twitter-web-exporter
// @supportURL https://github.com/prinsss/twitter-web-exporter/issues
// @match *://twitter.com/*
// @match *://x.com/*
// @require https://cdn.jsdelivr.net/npm/[email protected]/dayjs.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
// @grant GM_addStyle
// @grant unsafeWindow
// @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{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 body{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;color:inherit;margin:0;padding:0}#twe-root button,#twe-root select{text-transform:none}#twe-root button,#twe-root [type=button],#twe-root [type=reset],#twe-root [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]{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{color-scheme:light;--in: .7206 .191 231.6;--su: 64.8% .15 160;--wa: .8471 .199 83.87;--er: .7176 .221 22.18;--pc: .152344 .017892 200.026556;--sc: .15787 .020249 356.29965;--ac: .158762 .029206 78.618794;--nc: .847148 .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: .76172 .089459 200.026556;--s: .789351 .101246 356.29965;--a: .793811 .146032 78.618794;--n: .235742 .066235 313.189598;--b1: .977882 .00418 56.375637;--b2: .939822 .007638 61.449292;--b3: .915861 .006811 53.440502;--bc: .235742 .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: .268053 .020556 277.508664;--b3: .247877 .019009 277.508664;--pc: .150922 .036614 346.812432;--sc: .148405 .029709 301.883095;--ac: .166785 .024826 66.558491;--nc: .878891 .006515 275.524078;--inc: .176526 .018676 212.846491;--suc: .174199 .043903 148.024881;--wac: .191068 .026849 112.757109;--erc: .136441 .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: .754611 .18307 346.812432;--s: .742023 .148546 301.883095;--a: .833927 .124132 66.558491;--n: .394456 .032576 275.524078;--b1: .288229 .022103 277.508664;--bc: .977477 .007913 106.545019;--in: .88263 .09338 212.846491;--su: .870995 .219516 148.024881;--wa: .955338 .134246 112.757109;--er: .682204 .206328 24.430965}}#twe-root [data-theme=cupcake]{color-scheme:light;--in: .7206 .191 231.6;--su: 64.8% .15 160;--wa: .8471 .199 83.87;--er: .7176 .221 22.18;--pc: .152344 .017892 200.026556;--sc: .15787 .020249 356.29965;--ac: .158762 .029206 78.618794;--nc: .847148 .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: .76172 .089459 200.026556;--s: .789351 .101246 356.29965;--a: .793811 .146032 78.618794;--n: .235742 .066235 313.189598;--b1: .977882 .00418 56.375637;--b2: .939822 .007638 61.449292;--b3: .915861 .006811 53.440502;--bc: .235742 .066235 313.189598;--rounded-btn: 30.4px;--tab-border: 2px;--tab-radius: 11.2px}#twe-root [data-theme=dark]{color-scheme:dark;--in: .7206 .191 231.6;--su: 64.8% .15 160;--wa: .8471 .199 83.87;--er: .7176 .221 22.18;--pc: .13138 .0392 275.75;--sc: .1496 .052 342.55;--ac: .14902 .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: .6569 .196 275.75;--s: .748 .26 342.55;--a: .7451 .167 183.61;--n: .313815 .021108 254.139175;--nc: .746477 .0216 264.435964;--b1: .253267 .015896 252.417568;--b2: .232607 .013807 253.100675;--b3: .211484 .01165 254.087939;--bc: .746477 .0216 264.435964}#twe-root [data-theme=emerald]{color-scheme:light;--b2: .93 0 0;--b3: .86 0 0;--in: .7206 .191 231.6;--su: 64.8% .15 160;--wa: .8471 .199 83.87;--er: .7176 .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: .766626 .135433 153.450024;--pc: .333872 .040618 162.240129;--s: .613028 .202368 261.294233;--sc: 1 0 0;--a: .727725 .149783 33.200363;--ac: 0 0 0;--n: .355192 .032071 262.988584;--nc: .984625 .001706 247.838921;--b1: 1 0 0;--bc: .355192 .032071 262.988584;--animation-btn: 0;--animation-input: 0;--btn-focus-scale: 1}#twe-root [data-theme=cyberpunk]{color-scheme:light;--b2: .878943 .16647 104.32;--b3: .812786 .15394 104.32;--in: .7206 .191 231.6;--su: 64.8% .15 160;--wa: .8471 .199 83.87;--er: .7176 .221 22.18;--bc: .18902 .0358 104.32;--pc: .14844 .0418 6.35;--sc: .16666 .0368 204.72;--ac: .14372 .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: .7422 .209 6.35;--s: .8333 .184 204.72;--a: .7186 .2176 310.43;--n: .2304 .065 269.31;--nc: .9451 .179 104.32;--b1: .9451 .179 104.32;--rounded-box: 0;--rounded-btn: 0;--rounded-badge: 0;--tab-radius: 0}#twe-root [data-theme=valentine]{color-scheme:light;--b2: .880567 .024834 337.06289;--b3: .814288 .022964 337.06289;--pc: .137239 .030755 15.066527;--sc: .143942 .029258 293.189609;--ac: .142537 .014961 197.828857;--inc: .90923 .043042 262.880917;--suc: .12541 .033982 149.213788;--wac: .133168 .031484 58.31834;--erc: .14614 .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: .686197 .153774 15.066527;--s: .71971 .14629 293.189609;--a: .712685 .074804 197.828857;--n: .546053 .143342 358.004839;--nc: .902701 .037202 336.955191;--b1: .946846 .026703 337.06289;--bc: .373085 .081131 4.606426;--in: .54615 .215208 262.880917;--su: .627052 .169912 149.213788;--wa: .66584 .157422 58.31834;--er: .7307 .207 27.33;--rounded-btn: 30.4px;--tab-radius: 11.2px}#twe-root [data-theme=lofi]{color-scheme:light;--inc: .15908 .0206 205.9;--suc: .18026 .0306 164.14;--wac: .17674 .027 79.94;--erc: .15732 .03 28.47;--border-btn: 1px;--tab-border: 1px;--p: .159066 0 0;--pc: 1 0 0;--s: .21455 .001566 17.278957;--sc: 1 0 0;--a: .268618 0 0;--ac: 1 0 0;--n: 0 0 0;--nc: 1 0 0;--b1: 1 0 0;--b2: .961151 0 0;--b3: .92268 .001082 17.17934;--bc: 0 0 0;--in: .7954 .103 205.9;--su: .9013 .153 164.14;--wa: .8837 .135 79.94;--er: .7866 .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: .268053 .020556 277.508664;--b3: .247877 .019009 277.508664;--pc: .150922 .036614 346.812432;--sc: .148405 .029709 301.883095;--ac: .166785 .024826 66.558491;--nc: .878891 .006515 275.524078;--inc: .176526 .018676 212.846491;--suc: .174199 .043903 148.024881;--wac: .191068 .026849 112.757109;--erc: .136441 .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: .754611 .18307 346.812432;--s: .742023 .148546 301.883095;--a: .833927 .124132 66.558491;--n: .394456 .032576 275.524078;--b1: .288229 .022103 277.508664;--bc: .977477 .007913 106.545019;--in: .88263 .09338 212.846491;--su: .870995 .219516 148.024881;--wa: .955338 .134246 112.757109;--er: .682204 .206328 24.430965}#twe-root [data-theme=cmyk]{color-scheme:light;--b2: .93 0 0;--b3: .86 0 0;--bc: .2 0 0;--pc: .143544 .02666 239.443325;--sc: .128953 .040552 359.339283;--ac: .188458 .037948 105.306968;--nc: .843557 0 0;--inc: .136952 .0189 217.284104;--suc: .893898 .032505 321.406278;--wac: .142473 .031969 52.023412;--erc: .124027 .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: .717722 .133298 239.443325;--s: .644766 .202758 359.339283;--a: .942289 .189741 105.306968;--n: .217787 0 0;--b1: 1 0 0;--in: .684759 .094499 217.284104;--su: .46949 .162524 321.406278;--wa: .712364 .159843 52.023412;--er: .620133 .208385 28.717543}#twe-root [data-theme=business]{color-scheme:dark;--b2: .226487 0 0;--b3: .20944 0 0;--bc: .848707 0 0;--pc: .883407 .019811 251.473931;--sc: .128185 .005481 229.389418;--ac: .134542 .033545 35.791525;--nc: .854882 .00265 253.041249;--inc: .125233 .028702 240.033697;--suc: .140454 .018919 156.59611;--wac: .154965 .023141 81.519177;--erc: .903221 .029356 29.674507;--animation-btn: .25s;--animation-input: .2s;--btn-focus-scale: .95;--border-btn: 1px;--tab-border: 1px;--tab-radius: 8px;--p: .417036 .099057 251.473931;--s: .640924 .027405 229.389418;--a: .67271 .167726 35.791525;--n: .27441 .01325 253.041249;--b1: .243535 0 0;--in: .626163 .143511 240.033697;--su: .702268 .094594 156.59611;--wa: .774824 .115704 81.519177;--er: .516105 .14678 29.674507;--rounded-box: 4px;--rounded-btn: 2px;--rounded-badge: 2px}#twe-root [data-theme=winter]{color-scheme:light;--pc: .91372 .051 257.57;--sc: .885103 .03222 282.339433;--ac: .11988 .038303 335.171434;--nc: .839233 .012704 257.651965;--inc: .176255 .017178 214.515264;--suc: .160988 .015404 197.823719;--wac: .178345 .009167 71.47031;--erc: .146185 .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: .5686 .255 257.57;--s: .425516 .161098 282.339433;--a: .599398 .191515 335.171434;--n: .196166 .063518 257.651965;--b1: 1 0 0;--b2: .974663 .011947 259.822565;--b3: .932686 .016223 262.751375;--bc: .418869 .053885 255.824911;--in: .881275 .085888 214.515264;--su: .804941 .077019 197.823719;--wa: .891725 .045833 71.47031;--er: .730926 .110185 20.076293}#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: }#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: }#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.placeholder>div{display:flex;align-items:center;justify-content:center}@media (hover:hover){#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):not(.menu-title):not(details):active,#twe-root .menu li>*:not(ul):not(.menu-title):not(details).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 .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);animation:button-pop var(--animation-btn, .25s) ease-out;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))}@media (hover: hover){#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.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-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 .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 .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 .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 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 .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,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-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}#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 .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-left:16px;padding-right: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 .\\!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 .timeline{position:relative;display:flex}#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%;border-width:0px}#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;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 .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}#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-neutral{--btn-color: var(--fallback-n)}}@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: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-neutral{--btn-color: var(--n)}}#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-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.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.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.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:checked,#twe-root .checkbox[checked=true],#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:disabled{cursor:not-allowed;border-color:transparent;--tw-bg-opacity: 1;background-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)));opacity:.2}@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 .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: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-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-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-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::-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-item:focus{isolation:isolate}#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='%23000' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E.spinner_V8m1%7Btransform-origin:center;animation:spinner_zKoa 2s linear infinite%7D.spinner_V8m1 circle%7Bstroke-linecap:round;animation:spinner_YpZS 1.5s ease-out infinite%7D%40keyframes spinner_zKoa%7B100%25%7Btransform:rotate(360deg)%7D%7D%40keyframes spinner_YpZS%7B0%25%7Bstroke-dasharray:0 150;stroke-dashoffset:0%7D47.5%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-16%7D95%25%2C100%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-59%7D%7D%3C%2Fstyle%3E%3Cg class='spinner_V8m1'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3'%3E%3C%2Fcircle%3E%3C%2Fg%3E%3C%2Fsvg%3E");mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='%23000' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E.spinner_V8m1%7Btransform-origin:center;animation:spinner_zKoa 2s linear infinite%7D.spinner_V8m1 circle%7Bstroke-linecap:round;animation:spinner_YpZS 1.5s ease-out infinite%7D%40keyframes spinner_zKoa%7B100%25%7Btransform:rotate(360deg)%7D%7D%40keyframes spinner_YpZS%7B0%25%7Bstroke-dasharray:0 150;stroke-dashoffset:0%7D47.5%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-16%7D95%25%2C100%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-59%7D%7D%3C%2Fstyle%3E%3Cg class='spinner_V8m1'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3'%3E%3C%2Fcircle%3E%3C%2Fg%3E%3C%2Fsvg%3E")}#twe-root .loading-spinner{-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='%23000' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E.spinner_V8m1%7Btransform-origin:center;animation:spinner_zKoa 2s linear infinite%7D.spinner_V8m1 circle%7Bstroke-linecap:round;animation:spinner_YpZS 1.5s ease-out infinite%7D%40keyframes spinner_zKoa%7B100%25%7Btransform:rotate(360deg)%7D%7D%40keyframes spinner_YpZS%7B0%25%7Bstroke-dasharray:0 150;stroke-dashoffset:0%7D47.5%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-16%7D95%25%2C100%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-59%7D%7D%3C%2Fstyle%3E%3Cg class='spinner_V8m1'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3'%3E%3C%2Fcircle%3E%3C%2Fg%3E%3C%2Fsvg%3E");mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='%23000' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E.spinner_V8m1%7Btransform-origin:center;animation:spinner_zKoa 2s linear infinite%7D.spinner_V8m1 circle%7Bstroke-linecap:round;animation:spinner_YpZS 1.5s ease-out infinite%7D%40keyframes spinner_zKoa%7B100%25%7Btransform:rotate(360deg)%7D%7D%40keyframes spinner_YpZS%7B0%25%7Bstroke-dasharray:0 150;stroke-dashoffset:0%7D47.5%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-16%7D95%25%2C100%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-59%7D%7D%3C%2Fstyle%3E%3Cg class='spinner_V8m1'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3'%3E%3C%2Fcircle%3E%3C%2Fg%3E%3C%2Fsvg%3E")}#twe-root .menu li>*:not(ul):not(.menu-title):not(details):active,#twe-root .menu li>*:not(ul):not(.menu-title):not(details).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 .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);--tw-bg-opacity: 1;background-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)))}#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);--tw-bg-opacity: 1;background-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)))}#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%}}@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}}@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)));--tw-text-opacity: .2}#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 :is([dir=rtl] .\\!table){text-align:right!important}#twe-root :is([dir=rtl] .table){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,tbody) :where(tr:not(:last-child)),#twe-root .table :where(thead,tbody) :where(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,tbody) :where(tr:not(:last-child)),#twe-root .\\!table :where(thead,tbody) :where(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,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 .timeline hr{height:4px}#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);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);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);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);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[checked=true],#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[checked=true],#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[checked=true],#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[checked=true],#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: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 .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-sm{height:20px;width:20px}#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 .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 .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 .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-horizontal>:where(*:not(:first-child)){margin-top:0;margin-bottom:0;margin-inline-start:-1px}#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 .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,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-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;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 .static{position:static}#twe-root .fixed{position:fixed}#twe-root .absolute{position:absolute}#twe-root .relative{position:relative}#twe-root .bottom-0{bottom:0}#twe-root .bottom-0\\.5{bottom:2px}#twe-root .left-0{left:0}#twe-root .left-0\\.5{left:2px}#twe-root .left-8{left:32px}#twe-root .left-\\[-20px\\]{left:-20px}#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 .m-0{margin:0}#twe-root .my-3{margin-top:12px;margin-bottom:12px}#twe-root .my-\\[2px\\]{margin-top:2px;margin-bottom:2px}#twe-root .mb-0{margin-bottom:0}#twe-root .mb-1{margin-bottom:4px}#twe-root .mb-2{margin-bottom:8px}#twe-root .ml-0{margin-left:0}#twe-root .ml-0\\.5{margin-left:2px}#twe-root .ml-1{margin-left:4px}#twe-root .ml-4{margin-left:16px}#twe-root .mr-2{margin-right:8px}#twe-root .mr-3{margin-right:12px}#twe-root .mt-0{margin-top:0}#twe-root .mt-2{margin-top:8px}#twe-root .mt-3{margin-top:12px}#twe-root .mt-6{margin-top:24px}#twe-root .block{display: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 .contents{display:contents}#twe-root .h-12{height:48px}#twe-root .h-2{height:8px}#twe-root .h-28{height:112px}#twe-root .h-4{height:16px}#twe-root .h-52{height:208px}#twe-root .h-6{height:24px}#twe-root .h-8{height:32px}#twe-root .h-9{height:36px}#twe-root .h-full{height:100%}#twe-root .max-h-48{max-height:192px}#twe-root .max-h-\\[400px\\]{max-height:400px}#twe-root .max-h-\\[500px\\]{max-height:500px}#twe-root .w-1\\/2{width:50%}#twe-root .w-12{width:48px}#twe-root .w-20{width:80px}#twe-root .w-24{width:96px}#twe-root .w-32{width:128px}#twe-root .w-36{width:144px}#twe-root .w-4{width:16px}#twe-root .w-48{width:192px}#twe-root .w-52{width:208px}#twe-root .w-60{width:240px}#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 .max-w-4xl{max-width:896px}#twe-root .max-w-full{max-width:100%}#twe-root .max-w-lg{max-width:512px}#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-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 .flex-row{flex-direction:row}#twe-root .flex-col{flex-direction:column}#twe-root .items-start{align-items:flex-start}#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-2{gap:8px}#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 .overflow-hidden{overflow:hidden}#twe-root .overflow-scroll{overflow:scroll}#twe-root .overflow-x-scroll{overflow-x:scroll}#twe-root .overflow-y-scroll{overflow-y:scroll}#twe-root .overscroll-none{overscroll-behavior:none}#twe-root .truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}#twe-root .whitespace-nowrap{white-space:nowrap}#twe-root .whitespace-pre{white-space:pre}#twe-root .whitespace-pre-wrap{white-space:pre-wrap}#twe-root .break-all{word-break:break-all}#twe-root .rounded{border-radius:4px}#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 .border{border-width:1px}#twe-root .border-solid{border-style:solid}#twe-root .border-neutral-content{--tw-border-opacity: 1;border-color:var(--fallback-nc,oklch(var(--nc)/var(--tw-border-opacity)))}#twe-root .border-opacity-50{--tw-border-opacity: .5}#twe-root .bg-base-100{--tw-bg-opacity: 1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)))}#twe-root .bg-base-200{--tw-bg-opacity: 1;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)))}#twe-root .bg-base-300{--tw-bg-opacity: 1;background-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity)))}#twe-root .bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity))}#twe-root .bg-primary{--tw-bg-opacity: 1;background-color:var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity)))}#twe-root .bg-secondary{--tw-bg-opacity: 1;background-color:var(--fallback-s,oklch(var(--s)/var(--tw-bg-opacity)))}#twe-root .bg-transparent{background-color:transparent}#twe-root .bg-opacity-30{--tw-bg-opacity: .3}#twe-root .fill-base-content{fill:var(--fallback-bc,oklch(var(--bc)/1))}#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}#twe-root .p-2{padding:8px}#twe-root .p-3{padding:12px}#twe-root .px-0{padding-left:0;padding-right:0}#twe-root .px-0\\.5{padding-left:2px;padding-right:2px}#twe-root .px-1{padding-left:4px;padding-right:4px}#twe-root .px-2{padding-left:8px;padding-right:8px}#twe-root .px-4{padding-left:16px;padding-right:16px}#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 .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-base{font-size:16px;line-height:24px}#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 .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 .text-base-content{--tw-text-opacity: 1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)))}#twe-root .text-error{--tw-text-opacity: 1;color:var(--fallback-er,oklch(var(--er)/var(--tw-text-opacity)))}#twe-root .text-success{--tw-text-opacity: 1;color:var(--fallback-su,oklch(var(--su)/var(--tw-text-opacity)))}#twe-root .text-warning{--tw-text-opacity: 1;color:var(--fallback-wa,oklch(var(--wa)/var(--tw-text-opacity)))}#twe-root .text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}#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-75{opacity:.75}#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)}#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-200{transition-duration:.2s}#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-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)))}#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\\:opacity-90{opacity:.9}@media (min-width: 640px){#twe-root .sm\\:max-w-screen-sm{max-width:640px}}@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 .\\[\\&\\>path\\]\\:stroke-0>path{stroke-width:0} `);
(function (preact, hooks, signals, dayjs, 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);
return value;
};
var defaultAttributes = {
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"
};
var __defProp2 = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp2(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp2(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __objRest = (source, exclude) => {
var target = {};
for (var prop in source)
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
target[prop] = source[prop];
if (source != null && __getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(source)) {
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
target[prop] = source[prop];
}
return target;
};
var createPreactComponent = (iconName, iconNamePascal, iconNode) => {
const Component2 = (_a) => {
var _b = _a, { color = "currentColor", size = 24, stroke = 2, children } = _b, rest = __objRest(_b, ["color", "size", "stroke", "children"]);
return preact.h(
"svg",
__spreadValues(__spreadProps(__spreadValues({}, defaultAttributes), {
width: size,
height: size,
stroke: color,
"stroke-width": stroke,
class: `tabler-icon tabler-icon-${iconName}`
}), rest),
[...iconNode.map(([tag, attrs]) => preact.h(tag, attrs)), ...preact.toChildArray(children)]
);
};
Component2.displayName = `${iconNamePascal}`;
return Component2;
};
var IconArrowUpRight = createPreactComponent("arrow-up-right", "IconArrowUpRight", [
["path", { d: "M17 7l-10 10", key: "svg-0" }],
["path", { d: "M8 7l9 0l0 9", key: "svg-1" }]
]);
var IconBrandGithubFilled = createPreactComponent(
"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",
fill: "currentColor",
key: "svg-0",
strokeWidth: "0"
}
]
]
);
var IconChevronLeftPipe = createPreactComponent(
"chevron-left-pipe",
"IconChevronLeftPipe",
[
["path", { d: "M7 6v12", key: "svg-0" }],
["path", { d: "M18 6l-6 6l6 6", key: "svg-1" }]
]
);
var IconChevronLeft = createPreactComponent("chevron-left", "IconChevronLeft", [
["path", { d: "M15 6l-6 6l6 6", key: "svg-0" }]
]);
var IconChevronRightPipe = createPreactComponent(
"chevron-right-pipe",
"IconChevronRightPipe",
[
["path", { d: "M6 6l6 6l-6 6", key: "svg-0" }],
["path", { d: "M17 5v13", key: "svg-1" }]
]
);
var IconChevronRight = createPreactComponent("chevron-right", "IconChevronRight", [
["path", { d: "M9 6l6 6l-6 6", key: "svg-0" }]
]);
var IconCircleCheck = createPreactComponent("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" }]
]);
var IconCircleDashed = createPreactComponent("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" }]
]);
var IconExclamationCircle = createPreactComponent(
"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" }]
]
);
var IconHelp = createPreactComponent("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" }
]
]);
var IconInfoCircle = createPreactComponent("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" }]
]);
var IconLink = createPreactComponent("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"
}
]
]);
var IconSearch = createPreactComponent("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" }]
]);
var IconSettings = createPreactComponent("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" }]
]);
var IconSortAscending = createPreactComponent("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" }]
]);
var IconSortDescending = createPreactComponent("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" }]
]);
var IconX = createPreactComponent("x", "IconX", [
["path", { d: "M18 6l-12 12", key: "svg-0" }],
["path", { d: "M6 6l12 12", key: "svg-1" }]
]);
const logLinesSignal = signals.signal([]);
class Logger {
constructor() {
__publicField(this, "index", 0);
__publicField(this, "buffer", []);
__publicField(this, "bufferTimer", null);
}
info(line, ...args) {
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, err, ...args) {
this.error(`${msg} (Message: ${(err == null ? void 0 : err.message) ?? "none"})
This may be a problem caused by Twitter updates.
Please file an issue on GitHub:
https://github.com/prinsss/twitter-web-exporter/issues`, ...args);
}
debug(...args) {
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() {
logLinesSignal.value = [...logLinesSignal.value, ...this.buffer];
this.buffer = [];
}
}
const logger = new Logger();
var f = 0;
function u(e, t, n, o, i, u2) {
var a, c, p = {};
for (c in 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, __d: void 0, __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;
}
class ErrorBoundary extends preact.Component {
constructor() {
super(...arguments);
__publicField(this, "state", {
error: null
});
}
static getDerivedStateFromError(err) {
return {
error: err.message
};
}
componentDidCatch(err) {
logger.error(err.message, err);
this.setState({
error: err.message
});
}
render() {
if (this.state.error) {
return u("div", {
class: "alert alert-error p-2",
children: [u(IconExclamationCircle, {}), u("div", {
children: [u("h3", {
class: "font-bold leading-normal",
children: "Something went wrong."
}), u("p", {
class: "text-xs",
children: ["Error: ", this.state.error]
})]
})]
});
}
return this.props.children;
}
}
function safeJSONParse(text) {
try {
return JSON.parse(text);
} catch (e) {
logger.error(e.message);
return null;
}
}
function useSignal(value) {
return hooks.useMemo(() => signals.signal(value), []);
}
function useSignalState(value) {
const signal2 = useSignal(value);
const updateSignal = (newValue) => {
signal2.value = newValue;
};
return [signal2.value, updateSignal, signal2];
}
function useToggle(defaultValue = false) {
const signal2 = 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 xssFilter(str) {
return str.replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
}
function strEntitiesToHTML(str, urls) {
let temp = str;
if (!(urls == null ? void 0 : urls.length)) {
return temp;
}
for (const {
url,
display_url,
expanded_url
} of urls) {
temp = temp.replaceAll(url, `<a class="link" target="_blank" href="${xssFilter(expanded_url)}">${xssFilter(display_url)}</a>`);
}
return temp;
}
function parseTwitterDateTime(str) {
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 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")}`;
}
function ExtensionPanel({
title,
description: description2,
children,
onClick,
active,
indicatorColor = "bg-secondary"
}) {
return u("section", {
class: "module-panel",
children: [u("div", {
class: "h-12 flex items-center justify-start",
children: [u("div", {
class: "relative flex h-4 w-4 mr-3 shrink-0",
children: [active && u("span", {
class: cx("animate-ping absolute inline-flex h-full w-full rounded-full opacity-75", indicatorColor)
}), u("span", {
class: cx("relative inline-flex rounded-full h-4 w-4", indicatorColor)
})]
}), u("div", {
class: "flex flex-col flex-grow",
children: [u("p", {
class: "text-base m-0 font-medium leading-none",
children: title
}), u("p", {
class: "text-sm text-base-content leading-5 text-opacity-70 m-0",
children: description2
})]
}), u("button", {
class: "btn btn-sm p-0 w-9 h-9",
onClick,
children: u(IconArrowUpRight, {})
})]
}), children]
});
}
function Modal({
show,
onClose,
title,
children,
class: className = "max-w-4xl md:max-w-screen-md sm:max-w-screen-sm"
}) {
if (!show) {
return u("dialog", {
class: "modal"
});
}
return u("dialog", {
class: "modal modal-open",
open: true,
children: [u("div", {
class: cx("modal-box p-3 flex flex-col", className),
children: [u("header", {
class: "flex items-center h-9 mb-2",
children: [u("div", {
onClick: onClose,
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: u(IconX, {})
}), u("h2", {
class: "leading-none text-xl m-0 font-semibold",
children: title
})]
}), u(ErrorBoundary, {
children
})]
}), u("form", {
method: "dialog",
class: "modal-backdrop",
children: u("div", {
onClick: onClose
})
})]
});
}
function SearchArea({
defaultValue,
onChange
}) {
const inputRef = hooks.useRef(null);
return u("div", {
class: "join justify-end my-[2px] w-full max-w-xs absolute top-3 right-3",
children: [u("input", {
ref: inputRef,
type: "text",
class: "input input-bordered input-sm join-item",
placeholder: "Search...",
defaultValue,
onKeyDown: (e) => {
var _a;
if (e.key === "Enter") {
onChange(((_a = inputRef.current) == null ? void 0 : _a.value) ?? "");
}
}
}), u("button", {
class: "btn btn-sm join-item",
onClick: () => {
var _a;
return onChange(((_a = inputRef.current) == null ? void 0 : _a.value) ?? "");
},
children: u(IconSearch, {
size: 20
})
})]
});
}
/**
* @license
* Credit: https://icooon-mono.com/12776-%e7%8c%ab%e3%81%ae%e7%84%a1%e6%96%99%e3%82%a2%e3%82%a4%e3%82%b3%e3%83%b32/
*/
const CatIcon = () => u("svg", {
xmlns: "http://www.w3.org/2000/svg",
viewBox: "0 0 512 512",
class: "w-full h-full select-none",
children: u("g", {
children: u("path", {
d: "M461.814,197.514c-2.999-11.335-14.624-18.093-25.958-15.094c-1.866,0.553-13.477,3.649-26.042,14.341 c-6.234,5.349-12.633,12.751-17.361,22.454c-4.748,9.69-7.685,21.577-7.657,35.033c0.013,16.345,4.133,34.895,13.442,56.257 c6.282,14.403,9.144,29.697,9.144,44.846c0.062,25.627-8.438,50.756-21.121,68.283c-6.296,8.777-13.546,15.606-20.816,20.022 c-2.986,1.81-5.943,3.131-8.888,4.181l0.989-5.854c-0.055-17.03-4.05-34.84-13.021-50.528 c-28.356-49.643-66.223-134.741-66.223-134.741l-1.527-4.879c29.47-7.796,58.579-23.408,73.148-54.985 c38.931-84.344-41.08-142.73-41.08-142.73s-25.958-56.222-38.924-54.06c-12.978,2.164-41.094,38.931-41.094,38.931h-23.788h-23.788 c0,0-28.108-36.767-41.08-38.931c-12.979-2.163-38.924,54.06-38.924,54.06s-80.018,58.386-41.087,142.73 c13.822,29.953,40.741,45.572,68.634,53.748l-2.951,9.662c0,0-31.908,81.552-60.279,131.195C37.198,441.092,58.478,512,97.477,512 c29.47,0,79.14,0,101.692,0c7.292,0,11.763,0,11.763,0c22.544,0,72.222,0,101.691,0c12.654,0,23.38-7.547,31.204-19.324 c15.826-0.013,30.81-4.872,43.707-12.758c19.455-11.915,34.708-30.32,45.434-51.896c10.685-21.618,16.856-46.636,16.878-72.672 c0-20.484-3.885-41.619-12.682-61.813c-7.561-17.34-9.918-30.216-9.904-39.29c0.028-7.526,1.5-12.544,3.359-16.414 c1.417-2.889,3.124-5.17,4.983-7.091c2.771-2.868,5.964-4.879,8.349-6.054c1.182-0.595,2.135-0.968,2.674-1.162l0.449-0.152 l-0.007-0.028C458.179,220.189,464.779,208.724,461.814,197.514z"
})
})
});
var _unsafeWindow = /* @__PURE__ */ (() => typeof unsafeWindow != "undefined" ? unsafeWindow : void 0)();
const name = "twitter-web-exporter";
const description = "Export tweets, bookmarks, lists and much more from Twitter(X) web app.";
const version = "1.0.10";
const author = "prin <[email protected]>";
const license = "MIT";
const homepage = "https://github.com/prinsss/twitter-web-exporter";
const bugs = "https://github.com/prinsss/twitter-web-exporter/issues";
const type = "module";
const scripts = {
dev: "vite",
build: "tsc && vite build",
prepare: "husky install",
lint: "eslint --ignore-pattern dist --ext .ts,.tsx .",
commitlint: "commitlint --edit",
changelog: "git-cliff -o CHANGELOG.md",
preview: "vite preview"
};
const dependencies = {
"@preact/signals": "1.2.2",
"@preact/signals-core": "1.5.1",
"@tabler/icons-preact": "2.44.0",
"@tanstack/table-core": "8.11.2",
dayjs: "1.11.10",
"file-saver": "2.0.5",
preact: "10.19.3"
};
const devDependencies = {
"@commitlint/cli": "^18.4.4",
"@commitlint/config-conventional": "^18.4.4",
"@preact/preset-vite": "^2.7.0",
"@types/file-saver": "^2.0.7",
"@types/node": "^20",
"@typescript-eslint/eslint-plugin": "^6.17.0",
"@typescript-eslint/parser": "^6.17.0",
autoprefixer: "^10.4.16",
daisyui: "^4.4.24",
eslint: "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.2",
"git-cliff": "^1.4.0",
husky: "^8.0.3",
"postcss-prefix-selector": "^1.16.0",
"postcss-rem-to-pixel-next": "^5.0.3",
tailwindcss: "^3.4.0",
typescript: "^5.3.3",
vite: "^5.0.10",
"vite-plugin-monkey": "^3.5.1"
};
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: [],
dateTimeFormat: "YYYY-MM-DD HH:mm:ss Z",
version: packageJson.version
};
const THEMES = ["system", "cupcake", "dark", "emerald", "cyberpunk", "valentine", "lofi", "dracula", "cmyk", "business", "winter"];
const LOCAL_STORAGE_KEY = packageJson.name;
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.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 xhrOpen = _unsafeWindow.XMLHttpRequest.prototype.open;
class ExtensionManager {
constructor() {
__publicField(this, "extensions", /* @__PURE__ */ new Map());
__publicField(this, "disabledExtensions", /* @__PURE__ */ new Set());
__publicField(this, "debugEnabled", false);
/**
* Signal for subscribing to extension changes.
*/
__publicField(this, "signal", new signals.Signal(1));
this.installHttpHooks();
this.disabledExtensions = new Set(appOptionsManager.get("disabledExtensions", []));
if (appOptionsManager.get("debug")) {
this.debugEnabled = true;
logger.info("Debug mode enabled");
}
}
/**
* 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);
this.extensions.set(instance.name, instance);
} catch (err) {
logger.error(`Failed to register extension: ${ctor.name}`, err);
}
}
/**
* 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);
ext.enabled = true;
ext.setup();
logger.debug(`Enabled extension: ${name2}`);
this.signal.value++;
} catch (err) {
logger.error(`Failed to enable extension: ${name2}`, err);
}
}
disable(name2) {
try {
this.disabledExtensions.add(name2);
appOptionsManager.set("disabledExtensions", [...this.disabledExtensions]);
const ext = this.extensions.get(name2);
ext.enabled = false;
ext.dispose();
logger.debug(`Disabled extension: ${name2}`);
this.signal.value++;
} catch (err) {
logger.error(`Failed to disable extension: ${name2}`, err);
}
}
getExtensions() {
return [...this.extensions.values()];
}
/**
* 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() {
const manager = this;
_unsafeWindow.XMLHttpRequest.prototype.open = function(method, url) {
if (manager.debugEnabled) {
logger.debug(`XHR initialized`, {
method,
url
});
}
const interceptors = manager.getExtensions().filter((ext) => ext.enabled).map((ext) => ext.intercept()).filter((int) => typeof int === "function");
this.addEventListener("load", () => {
if (manager.debugEnabled) {
logger.debug(`XHR finished`, {
method,
url,
interceptors
});
}
interceptors.forEach((func) => {
func({
method,
url
}, this);
});
});
xhrOpen.apply(this, arguments);
};
logger.info("Hooked into XMLHttpRequest");
}
}
class Extension {
constructor(manager) {
__publicField(this, "name", "");
__publicField(this, "enabled", true);
__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;
}
}
const extensionManager = new ExtensionManager();
function Settings() {
const currentTheme = useSignal(appOptionsManager.get("theme"));
const [showSettings, toggleSettings] = useToggle(false);
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"
};
return u(preact.Fragment, {
children: [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: u(IconSettings, {})
}), u(Modal, {
title: "Settings",
show: showSettings,
onClose: toggleSettings,
class: "max-w-lg",
children: [u("p", {
class: styles.subtitle,
children: "General"
}), u("div", {
class: cx(styles.block, "flex-col"),
children: [u("label", {
class: styles.item,
children: [u("span", {
class: "label-text",
children: "Theme"
}), u("select", {
class: "select select-xs",
onChange: (e) => {
var _a;
currentTheme.value = ((_a = e.target) == null ? void 0 : _a.value) ?? DEFAULT_APP_OPTIONS.theme;
appOptionsManager.set("theme", currentTheme.value);
},
children: THEMES.map((theme) => u("option", {
value: theme,
selected: currentTheme.value === theme,
children: capitalizeFirstLetter(theme)
}, theme))
})]
}), u("label", {
class: styles.item,
children: [u("span", {
class: "label-text",
children: "Debug"
}), u("input", {
type: "checkbox",
class: "toggle toggle-primary",
checked: appOptionsManager.get("debug"),
onChange: (e) => {
var _a;
appOptionsManager.set("debug", (_a = e.target) == null ? void 0 : _a.checked);
}
})]
}), u("label", {
class: styles.item,
children: [u("div", {
class: "flex items-center",
children: [u("span", {
class: "label-text",
children: "Date Time Format"
}), u("a", {
href: "https://day.js.org/docs/en/display/format",
target: "_blank",
rel: "noopener noreferrer",
class: "tooltip tooltip-bottom ml-0.5 before:whitespace-pre-line before:max-w-max",
"data-tip": "Click for more information.\nThis will take effect on both\n previewer and exported files.",
children: u(IconHelp, {
size: 20
})
})]
}), u("input", {
type: "text",
class: "input input-bordered input-xs w-48",
value: appOptionsManager.get("dateTimeFormat"),
onChange: (e) => {
var _a;
appOptionsManager.set("dateTimeFormat", (_a = e.target) == null ? void 0 : _a.value);
}
})]
})]
}), u("p", {
class: styles.subtitle,
children: "Modules"
}), u("div", {
class: cx(styles.block, "flex-col"),
children: extensionManager.getExtensions().map((extension) => u("label", {
class: styles.item,
children: [u("span", {
children: extension.name
}), u("input", {
type: "checkbox",
class: "toggle toggle-secondary",
checked: extension.enabled,
onChange: () => {
extension.enabled ? extensionManager.disable(extension.name) : extensionManager.enable(extension.name);
}
})]
}, extension.name))
}), u("p", {
class: styles.subtitle,
children: "About"
}), u("div", {
class: styles.block,
children: [u("span", {
class: "label-text",
children: ["Version ", packageJson.version]
}), u("a", {
class: "btn btn-xs btn-ghost",
target: "_blank",
href: packageJson.homepage,
children: [u(IconBrandGithubFilled, {
class: "[&>path]:stroke-0"
}), "GitHub"]
})]
})]
})]
});
}
function App() {
const extensions = useSignal([]);
const currentTheme = useSignal(appOptionsManager.get("theme"));
const showControlPanel = useSignal(appOptionsManager.get("showControlPanel"));
const toggleControlPanel = () => {
showControlPanel.value = !showControlPanel.value;
appOptionsManager.set("showControlPanel", showControlPanel.value);
};
hooks.useEffect(() => {
extensionManager.signal.subscribe(() => {
extensions.value = extensionManager.getExtensions();
});
appOptionsManager.signal.subscribe(() => {
currentTheme.value = appOptionsManager.get("theme");
});
logger.debug("App useEffect executed");
}, []);
return u(preact.Fragment, {
children: [u("div", {
onClick: toggleControlPanel,
"data-theme": currentTheme.value,
class: "group w-12 h-12 fixed top-[60%] left-[-20px] cursor-pointer bg-transparent fill-base-content",
children: 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: u(CatIcon, {})
})
}), u("section", {
"data-theme": currentTheme.value,
class: cx("card card-compact bg-base-100 fixed border shadow-xl w-80 leading-loose text-base-content px-4 py-3 rounded-box border-solid border-neutral-content border-opacity-50 left-8 top-8 transition-transform duration-500", showControlPanel.value ? "translate-x-0 transform-none" : "translate-x-[-500px]"),
children: [u("header", {
class: "flex items-center h-9",
children: [u("h2", {
class: "font-semibold leading-none text-xl m-0 flex-grow",
children: "Web Exporter (α)"
}), u(ErrorBoundary, {
children: u(Settings, {})
}), u("div", {
onClick: toggleControlPanel,
class: "w-9 h-9 cursor-pointer flex justify-center items-center transition-colors duration-200 rounded-full hover:bg-base-200",
children: u(IconX, {})
})]
}), u("p", {
class: "text-sm text-base-content text-opacity-70 mb-1 leading-none",
children: "Refresh or clear to start new captures."
}), u("div", {
class: "divider mt-0 mb-0"
}), u("main", {
children: extensions.value.map((ext) => {
const Component2 = ext.render();
if (ext.enabled && Component2) {
return u(ErrorBoundary, {
children: u(Component2, {}, ext.name)
});
}
return null;
})
})]
})]
});
}
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");
const blob = new Blob(prependBOM ? [new Uint8Array([239, 187, 191]), content] : [content], {
type: "text/plain;charset=utf-8"
});
const url = URL.createObjectURL(blob);
link.href = url;
link.download = filename;
link.click();
URL.revokeObjectURL(url);
}
async function exportData(data, format, filename) {
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);
break;
case EXPORT_FORMAT.CSV:
prependBOM = true;
content = await csvExporter(data);
break;
}
saveFile(filename, content, prependBOM);
} catch (err) {
logger.errorWithBanner("Failed to export file", err);
}
}
async function jsonExporter(data) {
return JSON.stringify(data, void 0, " ");
}
async function htmlExporter(data) {
const table = document.createElement("table");
const thead = document.createElement("thead");
const tbody = document.createElement("tbody");
const headers = Object.keys(data[0] ?? {});
const headerRow = document.createElement("tr");
for (const header of headers) {
const th = document.createElement("th");
th.textContent = header;
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 header of headers) {
const td = document.createElement("td");
const value = row[header];
if (header === "profile_image_url" || header === "profile_banner_url") {
const img = document.createElement("img");
img.src = value;
img.width = 50;
td.innerHTML = "";
td.appendChild(img);
} else if (header === "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;
const link = document.createElement("a");
link.href = media.original;
link.target = "_blank";
link.style.marginRight = "0.5em";
link.appendChild(img);
td.appendChild(link);
}
}
} else if (header === "full_text" || header === "description") {
const p = document.createElement("p");
p.innerHTML = value;
p.style.whiteSpace = "pre-wrap";
p.style.maxWidth = "640px";
td.appendChild(p);
} else if (header === "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);
td.appendChild(details);
} else if (header === "url") {
const link = document.createElement("a");
link.href = value;
link.target = "_blank";
link.textContent = value;
td.appendChild(link);
} else {
td.textContent = typeof value === "string" ? value : JSON.stringify(row[header]);
}
tr.appendChild(td);
}
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;
}
function ExportDataModal({
title,
table,
show,
onClose
}) {
const [selectedFormat, setSelectedFormat] = useSignalState(EXPORT_FORMAT.JSON);
const [loading, setLoading] = useSignalState(false);
const [includeMetadata, toggleIncludeMetadata] = useToggle(false);
const [currentProgress, setCurrentProgress] = useSignalState(0);
const [totalProgress, setTotalProgress] = useSignalState(0);
const selectedRows = table.getSelectedRowModel().rows;
const onExport = async () => {
setLoading(true);
setTotalProgress(selectedRows.length);
const allRecords = [];
for (const row of selectedRows) {
const allCells = row.getAllCells();
const record = {};
for (const cell of allCells) {
const value = cell.getValue();
const meta = cell.column.columnDef.meta;
if ((meta == null ? void 0 : meta.exportable) === false) {
continue;
}
let exportValue = (meta == null ? void 0 : meta.exportValue) ? meta.exportValue(row) : value;
if (exportValue === void 0) {
exportValue = null;
}
record[(meta == null ? void 0 : meta.exportKey) || cell.column.id] = exportValue;
}
if (includeMetadata) {
record.metadata = row.original;
}
allRecords.push(record);
setCurrentProgress(allRecords.length);
}
await exportData(allRecords, selectedFormat, `twitter-${title}-${Date.now()}.${selectedFormat.toLowerCase()}`);
setLoading(false);
};
return u(Modal, {
class: "max-w-sm md:max-w-screen-sm sm:max-w-screen-sm",
title: `${title} Data`,
show,
onClose,
children: [u("div", {
class: "px-4 text-base",
children: [u("p", {
class: "text-base-content text-opacity-60 mb-2 leading-5 text-sm",
children: "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."
}), u("div", {
class: "flex items-center",
children: [u("p", {
class: "mr-2 leading-8",
children: "Data length:"
}), u("span", {
class: "font-mono leading-6 h-6 bg-base-200 px-2 rounded-md",
children: selectedRows.length
})]
}), u("div", {
class: "flex items-center",
children: [u("p", {
class: "mr-2 leading-8",
children: "Include all metadata:"
}), u("input", {
type: "checkbox",
class: "checkbox checkbox-sm",
checked: includeMetadata,
onChange: toggleIncludeMetadata
})]
}), u("div", {
class: "flex",
children: [u("p", {
class: "mr-2 leading-8",
children: "Export as:"
}), u("select", {
class: "select select-bordered select-sm w-32",
onChange: (e) => {
setSelectedFormat(e.target.value);
},
children: Object.values(EXPORT_FORMAT).map((type2) => u("option", {
selected: type2 === selectedFormat,
children: type2
}, type2))
})]
}), selectedRows.length > 0 ? null : u("div", {
class: "flex items-center justify-center h-28 w-full",
children: u("p", {
class: "text-base-content text-opacity-50",
children: "No data selected."
})
}), u("div", {
class: "flex flex-col mt-6",
children: [u("progress", {
class: "progress progress-primary w-full",
value: currentProgress / (totalProgress || 1) * 100,
max: "100"
}), u("span", {
class: "text-sm leading-none mt-2 text-base-content text-opacity-60",
children: `${currentProgress}/${selectedRows.length}`
})]
})]
}), u("div", {
class: "flex space-x-2",
children: [u("span", {
class: "flex-grow"
}), u("button", {
class: "btn",
onClick: onClose,
children: "Cancel"
}), u("button", {
class: cx("btn btn-primary", loading && "btn-disabled"),
onClick: onExport,
children: [loading && u("span", {
class: "loading loading-spinner"
}), "Start Export"]
})]
})]
});
}
var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
var FileSaver_min = { exports: {} };
(function(module, exports) {
(function(a, b) {
b();
})(commonjsGlobal, function() {
function b(a2, b2) {
return "undefined" == typeof b2 ? b2 = { autoBom: false } : "object" != typeof b2 && (console.warn("Deprecated: Expected third argument to be a object"), b2 = { autoBom: !b2 }), b2.autoBom && /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(a2.type) ? new Blob(["\uFEFF", a2], { type: a2.type }) : a2;
}
function c(a2, b2, c2) {
var d2 = new XMLHttpRequest();
d2.open("GET", a2), d2.responseType = "blob", d2.onload = function() {
g(d2.response, b2, c2);
}, d2.onerror = function() {
console.error("could not download file");
}, d2.send();
}
function d(a2) {
var b2 = new XMLHttpRequest();
b2.open("HEAD", a2, false);
try {
b2.send();
} catch (a3) {
}
return 200 <= b2.status && 299 >= b2.status;
}
function e(a2) {
try {
a2.dispatchEvent(new MouseEvent("click"));
} catch (c2) {
var b2 = document.createEvent("MouseEvents");
b2.initMouseEvent("click", true, true, window, 0, 0, 0, 80, 20, false, false, false, false, 0, null), a2.dispatchEvent(b2);
}
}
var f2 = "object" == typeof window && window.window === window ? window : "object" == typeof self && self.self === self ? self : "object" == typeof commonjsGlobal && commonjsGlobal.global === commonjsGlobal ? commonjsGlobal : void 0, a = f2.navigator && /Macintosh/.test(navigator.userAgent) && /AppleWebKit/.test(navigator.userAgent) && !/Safari/.test(navigator.userAgent), g = f2.saveAs || ("object" != typeof window || window !== f2 ? function() {
} : "download" in HTMLAnchorElement.prototype && !a ? function(b2, g2, h2) {
var i = f2.URL || f2.webkitURL, j = document.createElement("a");
g2 = g2 || b2.name || "download", j.download = g2, j.rel = "noopener", "string" == typeof b2 ? (j.href = b2, j.origin === location.origin ? e(j) : d(j.href) ? c(b2, g2, h2) : e(j, j.target = "_blank")) : (j.href = i.createObjectURL(b2), setTimeout(function() {
i.revokeObjectURL(j.href);
}, 4e4), setTimeout(function() {
e(j);
}, 0));
} : "msSaveOrOpenBlob" in navigator ? function(f3, g2, h2) {
if (g2 = g2 || f3.name || "download", "string" != typeof f3)
navigator.msSaveOrOpenBlob(b(f3, h2), g2);
else if (d(f3))
c(f3, g2, h2);
else {
var i = document.createElement("a");
i.href = f3, i.target = "_blank", setTimeout(function() {
e(i);
});
}
} : function(b2, d2, e2, g2) {
if (g2 = g2 || open("", "_blank"), g2 && (g2.document.title = g2.document.body.innerText = "downloading..."), "string" == typeof b2)
return c(b2, d2, e2);
var h2 = "application/octet-stream" === b2.type, i = /constructor/i.test(f2.HTMLElement) || f2.safari, j = /CriOS\/[\d]+/.test(navigator.userAgent);
if ((j || h2 && i || a) && "undefined" != typeof FileReader) {
var k = new FileReader();
k.onloadend = function() {
var a2 = k.result;
a2 = j ? a2 : a2.replace(/^data:[^;]*;/, "data:attachment/file;"), g2 ? g2.location.href = a2 : location = a2, g2 = null;
}, k.readAsDataURL(b2);
} else {
var l = f2.URL || f2.webkitURL, m = l.createObjectURL(b2);
g2 ? g2.location = m : location.href = m, g2 = null, setTimeout(function() {
l.revokeObjectURL(m);
}, 4e4);
}
});
f2.saveAs = g.saveAs = g, module.exports = g;
});
})(FileSaver_min);
var FileSaver_minExports = FileSaver_min.exports;
/**
* This file zip-stream.ts is copied from StreamSaver.js.
*
* @see https://github.com/jimmywarting/StreamSaver.js/blob/master/examples/zip-stream.js
* @license MIT
*/
class Crc32 {
constructor() {
this.crc = -1;
}
append(data) {
var crc = this.crc | 0;
var table = this.table;
for (var offset = 0, len = data.length | 0; offset < len; offset++) {
crc = crc >>> 8 ^ table[(crc ^ data[offset]) & 255];
}
this.crc = crc;
}
get() {
return ~this.crc;
}
}
Crc32.prototype.table = (() => {
var i;
var j;
var t;
var table = [];
for (i = 0; i < 256; i++) {
t = i;
for (j = 0; j < 8; j++) {
t = t & 1 ? t >>> 1 ^ 3988292384 : t >>> 1;
}
table[i] = t;
}
return table;
})();
const getDataHelper = (byteLength) => {
var uint8 = new Uint8Array(byteLength);
return {
array: uint8,
view: new DataView(uint8.buffer)
};
};
const pump = (zipObj) => zipObj.reader.read().then((chunk) => {
if (chunk.done)
return zipObj.writeFooter();
const outputData = chunk.value;
zipObj.crc.append(outputData);
zipObj.uncompressedLength += outputData.length;
zipObj.compressedLength += outputData.length;
zipObj.ctrl.enqueue(outputData);
});
function createWriter(underlyingSource) {
const files = /* @__PURE__ */ Object.create(null);
const filenames = [];
const encoder = new TextEncoder();
let offset = 0;
let activeZipIndex = 0;
let ctrl;
let activeZipObject, closed;
function next() {
activeZipIndex++;
activeZipObject = files[filenames[activeZipIndex]];
if (activeZipObject)
processNextChunk();
else if (closed)
closeZip();
}
var zipWriter = {
enqueue(fileLike) {
if (closed)
throw new TypeError("Cannot enqueue a chunk into a readable stream that is closed or has been requested to be closed");
let name2 = fileLike.name.trim();
const date = new Date(typeof fileLike.lastModified === "undefined" ? Date.now() : fileLike.lastModified);
if (fileLike.directory && !name2.endsWith("/"))
name2 += "/";
if (files[name2])
throw new Error("File already exists.");
const nameBuf = encoder.encode(name2);
filenames.push(name2);
const zipObject = files[name2] = {
level: 0,
ctrl,
directory: !!fileLike.directory,
nameBuf,
comment: encoder.encode(fileLike.comment || ""),
compressedLength: 0,
uncompressedLength: 0,
writeHeader() {
var header = getDataHelper(26);
var data = getDataHelper(30 + nameBuf.length);
zipObject.offset = offset;
zipObject.header = header;
if (zipObject.level !== 0 && !zipObject.directory) {
header.view.setUint16(4, 2048);
}
header.view.setUint32(0, 335546376);
header.view.setUint16(6, (date.getHours() << 6 | date.getMinutes()) << 5 | date.getSeconds() / 2, true);
header.view.setUint16(8, (date.getFullYear() - 1980 << 4 | date.getMonth() + 1) << 5 | date.getDate(), true);
header.view.setUint16(22, nameBuf.length, true);
data.view.setUint32(0, 1347093252);
data.array.set(header.array, 4);
data.array.set(nameBuf, 30);
offset += data.array.length;
ctrl.enqueue(data.array);
},
writeFooter() {
var footer = getDataHelper(16);
footer.view.setUint32(0, 1347094280);
if (zipObject.crc) {
zipObject.header.view.setUint32(10, zipObject.crc.get(), true);
zipObject.header.view.setUint32(14, zipObject.compressedLength, true);
zipObject.header.view.setUint32(18, zipObject.uncompressedLength, true);
footer.view.setUint32(4, zipObject.crc.get(), true);
footer.view.setUint32(8, zipObject.compressedLength, true);
footer.view.setUint32(12, zipObject.uncompressedLength, true);
}
ctrl.enqueue(footer.array);
offset += zipObject.compressedLength + 16;
next();
},
fileLike
};
if (!activeZipObject) {
activeZipObject = zipObject;
processNextChunk();
}
},
close() {
if (closed)
throw new TypeError("Cannot close a readable stream that has already been requested to be closed");
if (!activeZipObject)
closeZip();
closed = true;
}
};
function closeZip() {
var length = 0;
var index = 0;
var indexFilename, file;
for (indexFilename = 0; indexFilename < filenames.length; indexFilename++) {
file = files[filenames[indexFilename]];
length += 46 + file.nameBuf.length + file.comment.length;
}
const data = getDataHelper(length + 22);
for (indexFilename = 0; indexFilename < filenames.length; indexFilename++) {
file = files[filenames[indexFilename]];
data.view.setUint32(index, 1347092738);
data.view.setUint16(index + 4, 5120);
data.array.set(file.header.array, index + 6);
data.view.setUint16(index + 32, file.comment.length, true);
if (file.directory) {
data.view.setUint8(index + 38, 16);
}
data.view.setUint32(index + 42, file.offset, true);
data.array.set(file.nameBuf, index + 46);
data.array.set(file.comment, index + 46 + file.nameBuf.length);
index += 46 + file.nameBuf.length + file.comment.length;
}
data.view.setUint32(index, 1347093766);
data.view.setUint16(index + 8, filenames.length, true);
data.view.setUint16(index + 10, filenames.length, true);
data.view.setUint32(index + 12, length, true);
data.view.setUint32(index + 16, offset, true);
ctrl.enqueue(data.array);
ctrl.close();
}
function processNextChunk() {
if (!activeZipObject)
return;
if (activeZipObject.directory)
return activeZipObject.writeFooter(activeZipObject.writeHeader());
if (activeZipObject.reader)
return pump(activeZipObject);
if (activeZipObject.fileLike.stream) {
activeZipObject.crc = new Crc32();
activeZipObject.reader = activeZipObject.fileLike.stream().getReader();
activeZipObject.writeHeader();
} else
next();
}
return new ReadableStream({
start: (c) => {
ctrl = c;
underlyingSource.start && Promise.resolve(underlyingSource.start(zipWriter));
},
pull() {
return processNextChunk() || underlyingSource.pull && Promise.resolve(underlyingSource.pull(zipWriter));
}
});
}
async function zipStreamDownload(zipFilename, files, onProgress, rateLimit = 1e3) {
let current = 0;
const total = files.length;
const fileIterator = files.values();
const readableZipStream = createWriter({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async pull(ctrl) {
const fileInfo = fileIterator.next();
if (fileInfo.done) {
ctrl.close();
} else {
const {
filename,
url
} = fileInfo.value;
const start = Date.now();
logger.debug(`Start downloading ${filename} from ${url}`);
return fetch(url).then((res) => {
ctrl.enqueue({
name: filename,
stream: () => res.body
});
onProgress == null ? void 0 : onProgress(++current, total, fileInfo.value);
logger.debug(`Finished downloading ${filename} in ${Date.now() - start}ms`);
}).then(() => {
return new Promise((resolve) => setTimeout(resolve, rateLimit));
});
}
}
});
const chunks = [];
const writableOutputStream = new WritableStream({
write(chunk) {
chunks.push(chunk);
},
close() {
logger.info("Zip stream closed.");
}
});
logger.info(`Exporting to ZIP file: ${zipFilename}`);
await readableZipStream.pipeTo(writableOutputStream);
const arrayBuffer = await new Blob(chunks).arrayBuffer();
const blob = new Blob([arrayBuffer]);
FileSaver_minExports.saveAs(blob, zipFilename);
}
function extractDataFromResponse(response, extractInstructionsFromJson, extractDataFromTimelineEntry) {
const json = JSON.parse(response.responseText);
const instructions = extractInstructionsFromJson(json);
const timelineAddEntriesInstruction = instructions.find((i) => i.type === "TimelineAddEntries");
const newData = [];
for (const entry of timelineAddEntriesInstruction.entries) {
if (isTimelineEntryItem(entry)) {
const data = extractDataFromTimelineEntry(entry);
if (data) {
newData.push(data);
}
}
}
return newData;
}
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 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 extractTweetUnion(tweet) {
var _a, _b;
try {
if (tweet.__typename === "Tweet") {
return tweet;
}
if (tweet.__typename === "TweetWithVisibilityResults") {
return tweet.tweet;
}
if (tweet.__typename === "TweetTombstone") {
logger.warn(`TweetTombstone received (Reason: ${(_b = (_a = tweet.tombstone) == null ? void 0 : _a.text) == null ? void 0 : _b.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 (err) {
logger.debug(tweet);
logger.errorWithBanner("Failed to extract tweet", err);
}
return null;
}
function extractRetweetedTweet(tweet) {
var _a;
if ((_a = tweet.legacy.retweeted_status_result) == null ? void 0 : _a.result) {
return extractTweetUnion(tweet.legacy.retweeted_status_result.result);
}
return null;
}
function extractQuotedTweet(tweet) {
var _a;
if ((_a = tweet.quoted_status_result) == null ? void 0 : _a.result) {
return extractTweetUnion(tweet.quoted_status_result.result);
}
return null;
}
function extractTweetUserScreenName(tweet) {
return tweet.core.user_results.result.legacy.screen_name;
}
function extractTweetMedia(tweet) {
var _a;
const realTweet = extractRetweetedTweet(tweet) ?? tweet;
if ((_a = realTweet.legacy.extended_entities) == null ? void 0 : _a.media) {
return realTweet.legacy.extended_entities.media;
}
return realTweet.legacy.entities.media ?? [];
}
function extractTweetFullText(tweet) {
var _a;
return ((_a = tweet.note_tweet) == null ? void 0 : _a.note_tweet_results.result.text) ?? tweet.legacy.full_text;
}
function getMediaIndex(tweet, media) {
const key = media.media_key;
return extractTweetMedia(tweet).findIndex((value) => value.media_key === key);
}
function getMediaOriginalUrl(media) {
var _a;
if (media.type === "video" || media.type === "animated_gif") {
const variants = ((_a = media.video_info) == null ? void 0 : _a.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 formatTwitterImage(imgUrl, name2 = "medium") {
const regex = /^(https?:\/\/pbs\.twimg\.com\/media\/.+)\.(\w+)$/;
const match = imgUrl.match(regex);
if (!match) {
return `${imgUrl}?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) {
return `https://twitter.com/${extractTweetUserScreenName(tweet)}/status/${tweet.legacy.id_str}`;
}
function getUserURL(user) {
return `https://twitter.com/${user.legacy.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}`;
}
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.legacy.screen_name
},
name: {
description: "The profile name of tweet author",
extractor: (tweet) => tweet.core.user_results.result.legacy.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) => parseTwitterDateTime(tweet.legacy.created_at).format("YYYYMMDD")
},
time: {
description: "The post time in HHmmss format",
extractor: (tweet) => parseTwitterDateTime(tweet.legacy.created_at).format("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_FILENAME_PATTERN = "{screen_name}_{id}_{type}_{num}_{date}.{ext}";
const FILENAME_PATTERN_TOOLTIP = Object.entries(patterns).map(([key, value]) => `{${key}} - ${value.description}`).reduce((acc, cur) => acc + cur + "\n", "");
function extractMedia(data, includeRetweets, filenamePattern) {
const gallery = /* @__PURE__ */ new Map();
for (const item of data) {
if (item.__typename === "Tweet") {
if (!includeRetweets && item.legacy.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,
url: getMediaOriginalUrl(media)
};
});
for (const media of tweetMedia) {
gallery.set(media.filename, media);
}
}
if (item.__typename === "User") {
if (item.legacy.profile_image_url_https) {
const ext = getFileExtensionFromUrl(item.legacy.profile_image_url_https);
const filename = `${item.legacy.screen_name}_profile_image.${ext}`;
gallery.set(filename, {
filename,
url: getProfileImageOriginalUrl(item.legacy.profile_image_url_https)
});
}
if (item.legacy.profile_banner_url) {
const ext = getFileExtensionFromUrl(item.legacy.profile_banner_url);
const filename = `${item.legacy.screen_name}_profile_banner.${ext}`;
gallery.set(filename, {
filename,
url: item.legacy.profile_banner_url
});
}
}
}
return Array.from(gallery.values());
}
function ExportMediaModal({
title,
table,
isTweet,
show,
onClose
}) {
const [loading, setLoading] = useSignalState(false);
const [copied, setCopied] = useSignalState(false);
const [rateLimit, setRateLimit] = useSignalState(1e3);
const [filenamePattern, setFilenamePattern] = useSignalState(DEFAULT_FILENAME_PATTERN);
const [includeRetweets, toggleIncludeRetweets] = useToggle(true);
const [currentProgress, setCurrentProgress] = useSignalState(0);
const [totalProgress, setTotalProgress] = useSignalState(0);
const taskStatusSignal = useSignal({});
const mediaList = extractMedia(table.getSelectedRowModel().rows.map((row) => row.original), includeRetweets, filenamePattern);
const onProgress = (current, total, value) => {
setCurrentProgress(current);
setTotalProgress(total);
if (value == null ? void 0 : value.filename) {
const updated = {
...taskStatusSignal.value,
[value.filename]: 100
};
taskStatusSignal.value = updated;
}
};
const onExport = async () => {
try {
setLoading(true);
await zipStreamDownload(`twitter-${title}-${Date.now()}-media.zip`, mediaList, onProgress);
setLoading(false);
} catch (err) {
setLoading(false);
logger.error("Failed to export media. Open DevTools for more details.", err);
}
};
const onCopy = () => {
const text = mediaList.map((media) => `${media.url}
out=${media.filename}`).join("\n");
try {
navigator.clipboard.writeText(text);
setCopied(true);
setTimeout(() => setCopied(false), 2e3);
} catch (err) {
logger.error("Failed to copy media URLs. Open DevTools for more details.", err);
}
};
return u(Modal, {
class: "max-w-sm md:max-w-screen-sm sm:max-w-screen-sm",
title: `${title} Media`,
show,
onClose,
children: [u("div", {
class: "px-4 text-base overflow-y-scroll overscroll-none",
children: [u("p", {
class: "text-base-content text-opacity-60 leading-5 text-sm",
children: "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)."
}), u("div", {
role: "alert",
class: "alert text-sm py-2 mt-2 mb-2",
children: [u(IconInfoCircle, {
size: 24
}), u("span", {
children: "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."
})]
}), isTweet && u("div", {
class: "flex items-center h-9",
children: [u("div", {
class: "mr-2 leading-8 flex items-center",
children: [u("span", {
children: "Filename template "
}), u("div", {
class: "tooltip tooltip-bottom ml-0.5 before:whitespace-pre-line before:max-w-max",
"data-tip": FILENAME_PATTERN_TOOLTIP,
children: u(IconHelp, {
size: 20
})
}), u("span", {
children: ":"
})]
}), u("input", {
type: "text",
class: "input input-bordered input-sm flex-grow",
value: filenamePattern,
onChange: (e) => {
var _a;
setFilenamePattern((_a = e == null ? void 0 : e.target) == null ? void 0 : _a.value);
}
})]
}), u("div", {
class: "flex h-9 justify-between",
children: [u("div", {
class: "flex items-center h-9 w-1/2",
children: [u("p", {
class: "mr-2 leading-8",
children: "Rate limit (ms):"
}), u("input", {
type: "number",
class: "input input-bordered input-sm w-32",
value: rateLimit,
onChange: (e) => {
var _a;
const value = parseInt((_a = e == null ? void 0 : e.target) == null ? void 0 : _a.value);
setRateLimit(value || 0);
}
})]
}), u("div", {
class: "flex items-center h-9 w-1/2",
children: [u("p", {
class: "mr-2 leading-8",
children: "Include retweets:"
}), u("input", {
type: "checkbox",
class: "checkbox checkbox-sm",
checked: includeRetweets,
onChange: toggleIncludeRetweets
})]
})]
}), u("div", {
class: "my-3 overflow-x-scroll",
children: [u("table", {
class: "table table-xs table-zebra",
children: [u("thead", {
children: u("tr", {
children: [u("th", {}), u("th", {
children: "#"
}), u("th", {
children: "File Name"
}), u("th", {
children: "URL"
})]
})
}), u("tbody", {
children: mediaList.map((media, index) => u("tr", {
children: [u("td", {
children: taskStatusSignal.value[media.filename] ? u(IconCircleCheck, {
class: "text-success",
size: 14
}) : u(IconCircleDashed, {
size: 14
})
}), u("th", {
children: index + 1
}), u("td", {
children: media.filename
}), u("td", {
children: u("a", {
class: "link whitespace-nowrap",
href: media.url,
target: "_blank",
rel: "noopener noreferrer",
children: media.url
})
})]
}))
})]
}), mediaList.length > 0 ? null : u("div", {
class: "flex items-center justify-center h-28 w-full",
children: u("p", {
class: "text-base-content text-opacity-50",
children: "No media selected."
})
})]
}), u("div", {
class: "flex flex-col mt-6",
children: [u("progress", {
class: "progress progress-secondary w-full",
value: currentProgress / (totalProgress || 1) * 100,
max: "100"
}), u("span", {
class: "text-sm h-2 leading-none mt-2 text-base-content text-opacity-60",
children: `${currentProgress}/${mediaList.length}`
})]
})]
}), u("div", {
class: "flex space-x-2 mt-2",
children: [u("span", {
class: "flex-grow"
}), u("button", {
class: "btn",
onClick: onClose,
children: "Cancel"
}), u("button", {
class: "btn",
onClick: onCopy,
children: copied ? "Copied!" : "Copy URLs"
}), u("button", {
class: cx("btn btn-secondary", loading && "btn-disabled"),
onClick: onExport,
children: [loading && u("span", {
class: "loading loading-spinner"
}), "Start Export"]
})]
})]
});
}
function flexRender(Comp, props) {
return !Comp ? null : isComponent(Comp) ? 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 _a;
setState(updater);
(_a = options2.onStateChange) == null ? void 0 : _a.call(options2, updater);
}
}));
return tableRef.current;
}
const columnHelper$1 = tableCore.createColumnHelper();
const rtSourceAccessor = (row) => {
const source = extractRetweetedTweet(row);
return source ? extractTweetUserScreenName(source) : null;
};
const quoteSourceAccessor = (row) => {
const source = extractQuotedTweet(row);
return source ? extractTweetUserScreenName(source) : null;
};
const columns$1 = [columnHelper$1.display({
id: "select",
meta: {
exportable: false
},
header: ({
table
}) => u("input", {
type: "checkbox",
class: "checkbox checkbox-sm align-middle",
checked: table.getIsAllRowsSelected(),
indeterminate: table.getIsSomeRowsSelected(),
onChange: table.getToggleAllRowsSelectedHandler()
}),
cell: ({
row
}) => u("input", {
type: "checkbox",
class: "checkbox checkbox-sm",
checked: row.getIsSelected(),
disabled: !row.getCanSelect(),
indeterminate: row.getIsSomeSelected(),
onChange: row.getToggleSelectedHandler()
})
}), columnHelper$1.accessor("rest_id", {
meta: {
exportKey: "id",
exportHeader: "ID"
},
header: () => u("span", {
children: "ID"
}),
cell: (info) => u("p", {
class: "w-20 break-all font-mono text-xs",
children: info.getValue()
})
}), columnHelper$1.accessor((row) => +parseTwitterDateTime(row.legacy.created_at), {
id: "created_at",
meta: {
exportKey: "created_at",
exportHeader: "Date",
exportValue: (row) => formatDateTime(parseTwitterDateTime(row.original.legacy.created_at), appOptionsManager.get("dateTimeFormat"))
},
header: () => u("span", {
children: "Date"
}),
cell: (info) => u("p", {
class: "w-24",
children: u("a", {
class: "link",
target: "_blank",
href: getTweetURL(info.row.original),
children: formatDateTime(info.getValue(), appOptionsManager.get("dateTimeFormat"))
})
})
}), columnHelper$1.accessor("legacy.full_text", {
meta: {
exportKey: "full_text",
exportHeader: "Content",
exportValue: (row) => extractTweetFullText(row.original)
},
header: () => u("span", {
children: "Content"
}),
cell: (info) => u("div", {
children: [u("p", {
class: "w-60 whitespace-pre-wrap",
dangerouslySetInnerHTML: {
__html: strEntitiesToHTML(info.row.original.legacy.full_text, [...info.row.original.legacy.entities.urls, ...info.row.original.legacy.entities.media ?? []])
}
}), info.row.original.note_tweet && u("button", {
class: "link",
onClick: () => {
var _a;
return (_a = info.table.options.meta) == null ? void 0 : _a.setRawDataPreview(extractTweetFullText(info.row.original));
},
children: [">>", " Show Full Text"]
})]
})
}), columnHelper$1.accessor((row) => extractTweetMedia(row).length, {
id: "media",
meta: {
exportKey: "media",
exportHeader: "Media",
exportValue: (row) => extractTweetMedia(row.original).map((media) => ({
type: media.type,
url: media.url,
thumbnail: formatTwitterImage(media.media_url_https, "thumb"),
original: getMediaOriginalUrl(media)
}))
},
header: () => u("span", {
children: "Media"
}),
cell: (info) => u("div", {
class: "flex flex-row items-start space-x-1 w-max",
children: [extractTweetMedia(info.row.original).map((media) => {
var _a;
return u("div", {
class: "flex-shrink-0 block cursor-pointer relative w-12 h-12 rounded bg-base-300 overflow-hidden",
onClick: () => {
var _a2;
return (_a2 = info.table.options.meta) == null ? void 0 : _a2.setMediaPreview(getMediaOriginalUrl(media));
},
children: [u("img", {
class: "w-full h-full object-cover",
src: formatTwitterImage(media.media_url_https, "thumb")
}), media.type !== "photo" && 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((_a = media.video_info) == null ? void 0 : _a.duration_millis) : "GIF"
})]
}, media.media_key);
}), extractTweetMedia(info.row.original).length ? null : "N/A"]
})
}), columnHelper$1.accessor("core.user_results.result.legacy.screen_name", {
meta: {
exportKey: "screen_name",
exportHeader: "Screen Name"
},
header: () => u("span", {
children: "Screen Name"
}),
cell: (info) => u("p", {
class: "whitespace-pre",
children: u("a", {
class: "link",
target: "_blank",
href: getUserURL(info.row.original.core.user_results.result),
children: ["@", info.getValue()]
})
})
}), columnHelper$1.accessor("core.user_results.result.legacy.name", {
meta: {
exportKey: "name",
exportHeader: "Profile Name"
},
header: () => u("span", {
children: "Profile Name"
}),
cell: (info) => u("p", {
class: "w-32",
children: info.getValue()
})
}), columnHelper$1.accessor("core.user_results.result.legacy.profile_image_url_https", {
meta: {
exportKey: "profile_image_url",
exportHeader: "Profile Image"
},
header: () => u("span", {
children: "Profile Image"
}),
cell: (info) => u("div", {
class: "cursor-pointer",
onClick: () => {
var _a;
return (_a = info.table.options.meta) == null ? void 0 : _a.setMediaPreview(getProfileImageOriginalUrl(info.getValue()));
},
children: u("img", {
class: "w-12 h-12 rounded",
src: info.getValue()
})
})
}), columnHelper$1.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: () => u("span", {
children: "Replying To"
}),
cell: (info) => u("p", {
class: "whitespace-pre",
children: info.row.original.legacy.in_reply_to_status_id_str ? u("a", {
class: "link",
target: "_blank",
href: getInReplyToTweetURL(info.row.original),
children: ["@", info.getValue()]
}) : "N/A"
})
}), columnHelper$1.accessor(rtSourceAccessor, {
id: "rt_source",
meta: {
exportKey: "retweeted_status",
exportHeader: "RT Source",
exportValue: (row) => {
var _a;
return (_a = extractRetweetedTweet(row.original)) == null ? void 0 : _a.rest_id;
}
},
header: () => u("span", {
children: "RT Source"
}),
cell: (info) => {
const source = extractRetweetedTweet(info.row.original);
return u("p", {
class: "whitespace-pre",
children: source ? u("a", {
class: "link",
target: "_blank",
href: getTweetURL(source),
children: ["@", info.getValue()]
}) : "N/A"
});
}
}), columnHelper$1.accessor(quoteSourceAccessor, {
id: "quote_source",
meta: {
exportKey: "quoted_status",
exportHeader: "Quote Source",
exportValue: (row) => {
var _a;
return (_a = extractQuotedTweet(row.original)) == null ? void 0 : _a.rest_id;
}
},
header: () => u("span", {
children: "Quote Source"
}),
cell: (info) => {
const source = extractQuotedTweet(info.row.original);
return u("p", {
class: "whitespace-pre",
children: source ? u("a", {
class: "link",
target: "_blank",
href: getTweetURL(source),
children: ["@", info.getValue()]
}) : "N/A"
});
}
}), columnHelper$1.accessor("legacy.favorite_count", {
meta: {
exportKey: "favorite_count",
exportHeader: "Favorites"
},
header: () => u("span", {
children: "Favorites"
}),
cell: (info) => u("p", {
children: info.getValue()
})
}), columnHelper$1.accessor("legacy.retweet_count", {
meta: {
exportKey: "retweet_count",
exportHeader: "Retweets"
},
header: () => u("span", {
children: "Retweets"
}),
cell: (info) => u("p", {
children: info.getValue()
})
}), columnHelper$1.accessor("legacy.bookmark_count", {
meta: {
exportKey: "bookmark_count",
exportHeader: "Bookmarks"
},
header: () => u("span", {
children: "Bookmarks"
}),
cell: (info) => u("p", {
children: info.getValue()
})
}), columnHelper$1.accessor("legacy.quote_count", {
meta: {
exportKey: "quote_count",
exportHeader: "Quotes"
},
header: () => u("span", {
children: "Quotes"
}),
cell: (info) => u("p", {
children: info.getValue()
})
}), columnHelper$1.accessor("legacy.reply_count", {
meta: {
exportKey: "reply_count",
exportHeader: "Replies"
},
header: () => u("span", {
children: "Replies"
}),
cell: (info) => u("p", {
children: info.getValue()
})
}), columnHelper$1.accessor("views.count", {
meta: {
exportKey: "views_count",
exportHeader: "Views",
exportValue: (row) => typeof row.original.views.count === "undefined" ? null : +row.original.views.count
},
header: () => u("span", {
children: "Views"
}),
cell: (info) => u("p", {
children: info.getValue() ?? "N/A"
})
}), columnHelper$1.accessor("legacy.favorited", {
meta: {
exportKey: "favorited",
exportHeader: "Favorited"
},
header: () => u("span", {
children: "Favorited"
}),
cell: (info) => u("p", {
children: info.getValue() ? "YES" : "NO"
})
}), columnHelper$1.accessor("legacy.retweeted", {
meta: {
exportKey: "retweeted",
exportHeader: "Retweeted"
},
header: () => u("span", {
children: "Retweeted"
}),
cell: (info) => u("p", {
children: info.getValue() ? "YES" : "NO"
})
}), columnHelper$1.accessor("legacy.bookmarked", {
meta: {
exportKey: "bookmarked",
exportHeader: "Bookmarked"
},
header: () => u("span", {
children: "Bookmarked"
}),
cell: (info) => u("p", {
children: info.getValue() ? "YES" : "NO"
})
}), columnHelper$1.display({
id: "url",
meta: {
exportKey: "url",
exportHeader: "URL",
exportValue: (row) => getTweetURL(row.original)
},
header: () => u("span", {
children: "URL"
}),
cell: (info) => u("a", {
href: getTweetURL(info.row.original),
target: "_blank",
children: u(IconLink, {})
})
}), columnHelper$1.display({
id: "actions",
meta: {
exportable: false
},
header: () => u("span", {
children: "Actions"
}),
cell: (info) => u("div", {
class: "flex flex-row items-start space-x-1",
children: u("button", {
onClick: () => {
var _a;
return (_a = info.table.options.meta) == null ? void 0 : _a.setRawDataPreview(info.row.original);
},
class: "btn btn-xs btn-neutral whitespace-nowrap",
children: "Details"
})
})
})];
const columnHelper = tableCore.createColumnHelper();
const columns = [columnHelper.display({
id: "select",
meta: {
exportable: false
},
header: ({
table
}) => u("input", {
type: "checkbox",
class: "checkbox checkbox-sm align-middle",
checked: table.getIsAllRowsSelected(),
indeterminate: table.getIsSomeRowsSelected(),
onChange: table.getToggleAllRowsSelectedHandler()
}),
cell: ({
row
}) => u("input", {
type: "checkbox",
class: "checkbox checkbox-sm",
checked: row.getIsSelected(),
disabled: !row.getCanSelect(),
indeterminate: row.getIsSomeSelected(),
onChange: row.getToggleSelectedHandler()
})
}), columnHelper.accessor("rest_id", {
meta: {
exportKey: "id",
exportHeader: "ID"
},
header: () => u("span", {
children: "ID"
}),
cell: (info) => u("p", {
class: "w-20 break-all font-mono text-xs",
children: info.getValue()
})
}), columnHelper.accessor("legacy.screen_name", {
meta: {
exportKey: "screen_name",
exportHeader: "Screen Name"
},
header: () => u("span", {
children: "Screen Name"
}),
cell: (info) => u("p", {
class: "whitespace-pre",
children: u("a", {
class: "link",
target: "_blank",
href: getUserURL(info.row.original),
children: ["@", info.getValue()]
})
})
}), columnHelper.accessor("legacy.name", {
meta: {
exportKey: "name",
exportHeader: "Profile Name"
},
header: () => u("span", {
children: "Profile Name"
}),
cell: (info) => u("p", {
class: "w-32",
children: info.getValue()
})
}), columnHelper.accessor("legacy.description", {
meta: {
exportKey: "description",
exportHeader: "Description"
},
header: () => u("span", {
children: "Description"
}),
cell: (info) => u("p", {
class: "w-52",
dangerouslySetInnerHTML: {
__html: strEntitiesToHTML(info.row.original.legacy.description, info.row.original.legacy.entities.description.urls)
}
})
}), columnHelper.accessor("legacy.profile_image_url_https", {
meta: {
exportKey: "profile_image_url",
exportHeader: "Profile Image"
},
header: () => u("span", {
children: "Profile Image"
}),
cell: (info) => u("div", {
class: "cursor-pointer",
onClick: () => {
var _a;
return (_a = info.table.options.meta) == null ? void 0 : _a.setMediaPreview(getProfileImageOriginalUrl(info.getValue()));
},
children: u("img", {
class: "w-12 h-12 rounded",
src: info.getValue()
})
})
}), columnHelper.accessor("legacy.profile_banner_url", {
meta: {
exportKey: "profile_banner_url",
exportHeader: "Profile Banner"
},
header: () => u("span", {
children: "Profile Banner"
}),
cell: (info) => u("div", {
class: "cursor-pointer w-36 h-12",
onClick: () => {
var _a;
return (_a = info.table.options.meta) == null ? void 0 : _a.setMediaPreview(info.getValue() ?? "");
},
children: info.getValue() ? u("img", {
class: "w-auto h-12 rounded",
src: `${info.getValue()}/600x200`
}) : u("span", {
class: "leading-[48px]",
children: "N/A"
})
})
}), columnHelper.accessor("legacy.followers_count", {
meta: {
exportKey: "followers_count",
exportHeader: "Followers"
},
header: () => u("span", {
children: "Followers"
}),
cell: (info) => u("p", {
children: info.getValue()
})
}), columnHelper.accessor("legacy.statuses_count", {
meta: {
exportKey: "statuses_count",
exportHeader: "Statuses"
},
header: () => u("span", {
children: "Statuses"
}),
cell: (info) => u("p", {
children: info.getValue()
})
}), columnHelper.accessor("legacy.favourites_count", {
meta: {
exportKey: "favourites_count",
exportHeader: "Favourites"
},
header: () => u("span", {
children: "Favourites"
}),
cell: (info) => u("p", {
children: info.getValue()
})
}), columnHelper.accessor("legacy.listed_count", {
meta: {
exportKey: "listed_count",
exportHeader: "Listed"
},
header: () => u("span", {
children: "Listed"
}),
cell: (info) => u("p", {
children: info.getValue()
})
}), columnHelper.accessor("legacy.verified_type", {
meta: {
exportKey: "verified_type",
exportHeader: "Verified Type"
},
header: () => u("span", {
children: "Verified Type"
}),
cell: (info) => u("p", {
children: info.getValue() ?? "N/A"
})
}), columnHelper.accessor("is_blue_verified", {
meta: {
exportKey: "is_blue_verified",
exportHeader: "Blue Verified"
},
header: () => u("span", {
children: "Blue Verified"
}),
cell: (info) => u("p", {
children: info.getValue() ? "YES" : "NO"
})
}), columnHelper.accessor("legacy.following", {
meta: {
exportKey: "following",
exportHeader: "Following"
},
header: () => u("span", {
children: "Following"
}),
cell: (info) => u("p", {
children: info.getValue() ? "YES" : "NO"
})
}), columnHelper.accessor("legacy.followed_by", {
meta: {
exportKey: "followed_by",
exportHeader: "Follows You"
},
header: () => u("span", {
children: "Follows You"
}),
cell: (info) => u("p", {
children: info.getValue() ? "YES" : "NO"
})
}), columnHelper.accessor((row) => +parseTwitterDateTime(row.legacy.created_at), {
id: "created_at",
meta: {
exportKey: "created_at",
exportHeader: "Created At",
exportValue: (row) => formatDateTime(parseTwitterDateTime(row.original.legacy.created_at), appOptionsManager.get("dateTimeFormat"))
},
header: () => u("span", {
children: "Created At"
}),
cell: (info) => u("p", {
class: "w-24",
children: formatDateTime(info.getValue(), appOptionsManager.get("dateTimeFormat"))
})
}), columnHelper.display({
id: "url",
meta: {
exportKey: "url",
exportHeader: "URL",
exportValue: (row) => getUserURL(row.original)
},
header: () => u("span", {
children: "URL"
}),
cell: (info) => u("a", {
href: getUserURL(info.row.original),
target: "_blank",
children: u(IconLink, {})
})
})];
const Pagination = ({
table
}) => {
const state = table.getState().pagination;
return u("div", {
className: "mt-3 flex items-center gap-2",
children: [u("span", {
children: "Rows per page:"
}), u("select", {
value: state.pageSize,
onChange: (e) => {
table.setPageSize(Number(e.target.value));
},
className: "select select-sm select-bordered",
children: [10, 20, 50, 100].map((pageSize) => u("option", {
value: pageSize,
children: pageSize
}, pageSize))
}), u("span", {
class: "flex-grow"
}), u("span", {
children: [state.pageSize * state.pageIndex + 1, " - ", Math.min(state.pageSize * (state.pageIndex + 1), table.getFilteredRowModel().rows.length), " of ", table.getFilteredRowModel().rows.length, " items"]
}), u("input", {
defaultValue: (state.pageIndex + 1).toString(),
type: "number",
onInput: (e) => {
const value = e.target.value;
table.setPageIndex(value ? Number(value) - 1 : 0);
},
className: "input input-bordered w-20 input-sm text-center"
}), u("div", {
class: "join",
children: [u("button", {
className: "join-item btn btn-sm",
onClick: () => table.setPageIndex(0),
disabled: !table.getCanPreviousPage(),
children: u(IconChevronLeftPipe, {
size: 20
})
}), u("button", {
className: "join-item btn btn-sm",
onClick: () => table.previousPage(),
disabled: !table.getCanPreviousPage(),
children: u(IconChevronLeft, {
size: 20
})
}), u("button", {
className: "join-item btn btn-sm",
onClick: () => table.nextPage(),
disabled: !table.getCanNextPage(),
children: u(IconChevronRight, {
size: 20
})
}), u("button", {
className: "join-item btn btn-sm",
onClick: () => table.setPageIndex(table.getPageCount() - 1),
disabled: !table.getCanNextPage(),
children: u(IconChevronRightPipe, {
size: 20
})
})]
})]
});
};
function TableView({
title,
recordsSignal,
isTweet
}) {
const data = recordsSignal.value;
const [showExportDataModal, toggleShowExportDataModal] = useToggle();
const [showExportMediaModal, toggleShowExportMediaModal] = useToggle();
const [mediaPreview, setMediaPreview] = useSignalState("");
const [rawDataPreview, setRawDataPreview] = useSignalState(null);
const table = useReactTable({
data,
columns: isTweet ? columns$1 : columns,
getCoreRowModel: tableCore.getCoreRowModel(),
getFilteredRowModel: tableCore.getFilteredRowModel(),
getSortedRowModel: tableCore.getSortedRowModel(),
getPaginationRowModel: tableCore.getPaginationRowModel(),
meta: {
mediaPreview,
setMediaPreview: (url) => setMediaPreview(url),
rawDataPreview,
setRawDataPreview: (data2) => setRawDataPreview(data2)
}
});
hooks.useEffect(() => {
if (!table.getIsSomeRowsSelected()) {
table.toggleAllRowsSelected(true);
}
}, [table]);
return u(preact.Fragment, {
children: [u(SearchArea, {
defaultValue: table.getState().globalFilter,
onChange: table.setGlobalFilter
}), u("main", {
class: "max-w-full grow overflow-scroll bg-base-200 overscroll-none",
children: [u("table", {
class: "table table-pin-rows table-border-bc table-padding-sm",
children: [u("thead", {
children: table.getHeaderGroups().map((headerGroup) => u("tr", {
children: headerGroup.headers.map((header) => 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" && u(IconSortAscending, {
size: 15,
class: "inline align-top ml-1"
}), header.column.getIsSorted() === "desc" && u(IconSortDescending, {
size: 15,
class: "inline align-top ml-1"
})]
}, header.id))
}, headerGroup.id))
}), u("tbody", {
children: table.getRowModel().rows.map((row) => u("tr", {
children: row.getVisibleCells().map((cell) => u("td", {
children: flexRender(cell.column.columnDef.cell, cell.getContext())
}, cell.id))
}, row.id))
})]
}), table.getRowModel().rows.length > 0 ? null : u("div", {
class: "flex items-center justify-center h-52 w-full",
children: u("p", {
class: "text-base-content text-opacity-50",
children: "No data available."
})
})]
}), u(Pagination, {
table
}), u("div", {
class: "flex mt-3 space-x-2",
children: [u("button", {
class: "btn btn-neutral btn-ghost",
onClick: () => {
recordsSignal.value = [];
},
children: "Clear"
}), u("span", {
class: "flex-grow"
}), u("button", {
class: "btn btn-secondary",
onClick: toggleShowExportMediaModal,
children: "Export Media"
}), u("button", {
class: "btn btn-primary",
onClick: toggleShowExportDataModal,
children: "Export Data"
})]
}), u(ExportDataModal, {
title,
table,
show: showExportDataModal,
onClose: toggleShowExportDataModal
}), u(ExportMediaModal, {
title,
table,
isTweet,
show: showExportMediaModal,
onClose: toggleShowExportMediaModal
}), u(Modal, {
title: "JSON View",
class: "max-w-xl",
show: !!rawDataPreview,
onClose: () => setRawDataPreview(null),
children: u("main", {
class: "max-w-full max-h-[500px] overflow-scroll overscroll-none",
children: typeof rawDataPreview === "string" ? u("p", {
class: "whitespace-pre-wrap",
children: rawDataPreview
}) : u("pre", {
class: "text-xs leading-none",
children: JSON.stringify(rawDataPreview, null, 2)
})
})
}), u(Modal, {
title: "Media View",
class: "max-w-xl",
show: !!mediaPreview,
onClose: () => setMediaPreview(""),
children: u("main", {
class: "max-w-full",
children: mediaPreview.includes(".mp4") ? u("video", {
controls: true,
class: "w-full max-h-[400px] object-contain",
src: mediaPreview
}) : u("img", {
class: "w-full max-h-[400px] object-contain",
src: mediaPreview
})
})
})]
});
}
function ModuleUI({
title,
recordsSignal,
isTweet
}) {
const [showModal, toggleShowModal] = useToggle();
return u(ExtensionPanel, {
title,
description: `Captured: ${recordsSignal.value.length}`,
active: recordsSignal.value.length > 0,
onClick: toggleShowModal,
indicatorColor: isTweet ? "bg-primary" : "bg-secondary",
children: u(Modal, {
title,
show: showModal,
onClose: toggleShowModal,
children: u(TableView, {
title,
show: showModal,
recordsSignal,
isTweet
})
})
});
}
const bookmarksSignal = signals.signal([]);
const BookmarksInterceptor = (req, res) => {
if (!/\/graphql\/.+\/Bookmarks/.test(req.url)) {
return;
}
try {
const newData = extractDataFromResponse(res, (json) => json.data.bookmark_timeline_v2.timeline.instructions, (entry) => extractTimelineTweet(entry.content.itemContent));
bookmarksSignal.value = [...bookmarksSignal.value, ...newData];
logger.info(`Bookmarks: ${newData.length} items received`);
} catch (err) {
logger.debug(req.method, req.url, res.status, res.responseText);
logger.errorWithBanner("Bookmarks: Failed to parse API response", err);
}
};
function BookmarksPanel() {
return u(ModuleUI, {
title: "Bookmarks",
recordsSignal: bookmarksSignal,
isTweet: true
});
}
class BookmarksModule extends Extension {
constructor() {
super(...arguments);
__publicField(this, "name", "BookmarksModule");
}
intercept() {
return BookmarksInterceptor;
}
render() {
return BookmarksPanel;
}
}
const followersSignal = signals.signal([]);
const FollowersInterceptor = (req, res) => {
if (!/\/graphql\/.+\/(BlueVerified)*Followers/.test(req.url)) {
return;
}
try {
const newData = extractDataFromResponse(res, (json) => json.data.user.result.timeline.timeline.instructions, (entry) => entry.content.itemContent.user_results.result);
followersSignal.value = [...followersSignal.value, ...newData];
logger.info(`Followers: ${newData.length} items received`);
} catch (err) {
logger.debug(req.method, req.url, res.status, res.responseText);
logger.errorWithBanner("Followers: Failed to parse API response", err);
}
};
function FollowersPanel() {
return u(ModuleUI, {
title: "Followers",
recordsSignal: followersSignal
});
}
class FollowersModule extends Extension {
constructor() {
super(...arguments);
__publicField(this, "name", "FollowersModule");
}
intercept() {
return FollowersInterceptor;
}
render() {
return FollowersPanel;
}
}
const followingSignal = signals.signal([]);
const FollowingInterceptor = (req, res) => {
if (!/\/graphql\/.+\/Following/.test(req.url)) {
return;
}
try {
const newData = extractDataFromResponse(res, (json) => json.data.user.result.timeline.timeline.instructions, (entry) => entry.content.itemContent.user_results.result);
followingSignal.value = [...followingSignal.value, ...newData];
logger.info(`Following: ${newData.length} items received`);
} catch (err) {
logger.debug(req.method, req.url, res.status, res.responseText);
logger.errorWithBanner("Following: Failed to parse API response", err);
}
};
function FollowingPanel() {
return u(ModuleUI, {
title: "Following",
recordsSignal: followingSignal
});
}
class FollowingModule extends Extension {
constructor() {
super(...arguments);
__publicField(this, "name", "FollowingModule");
}
intercept() {
return FollowingInterceptor;
}
render() {
return FollowingPanel;
}
}
const likesSignal = signals.signal([]);
const LikesInterceptor = (req, res) => {
if (!/\/graphql\/.+\/Likes/.test(req.url)) {
return;
}
try {
const newData = extractDataFromResponse(res, (json) => json.data.user.result.timeline_v2.timeline.instructions, (entry) => extractTimelineTweet(entry.content.itemContent));
likesSignal.value = [...likesSignal.value, ...newData];
logger.info(`Likes: ${newData.length} items received`);
} catch (err) {
logger.debug(req.method, req.url, res.status, res.responseText);
logger.errorWithBanner("Likes: Failed to parse API response", err);
}
};
function LikesPanel() {
return u(ModuleUI, {
title: "Likes",
recordsSignal: likesSignal,
isTweet: true
});
}
class LikesModule extends Extension {
constructor() {
super(...arguments);
__publicField(this, "name", "LikesModule");
}
intercept() {
return LikesInterceptor;
}
render() {
return LikesPanel;
}
}
const listMembersSignal = signals.signal([]);
const ListMembersInterceptor = (req, res) => {
if (!/\/graphql\/.+\/ListMembers/.test(req.url)) {
return;
}
try {
const newData = extractDataFromResponse(res, (json) => json.data.list.members_timeline.timeline.instructions, (entry) => entry.content.itemContent.user_results.result);
listMembersSignal.value = [...listMembersSignal.value, ...newData];
logger.info(`ListMembers: ${newData.length} items received`);
} catch (err) {
logger.debug(req.method, req.url, res.status, res.responseText);
logger.errorWithBanner("ListMembers: Failed to parse API response", err);
}
};
function ListMembersPanel() {
return u(ModuleUI, {
title: "ListMembers",
recordsSignal: listMembersSignal
});
}
class ListMembersModule extends Extension {
constructor() {
super(...arguments);
__publicField(this, "name", "ListMembersModule");
}
intercept() {
return ListMembersInterceptor;
}
render() {
return ListMembersPanel;
}
}
const listSubscribersSignal = signals.signal([]);
const ListSubscribersInterceptor = (req, res) => {
if (!/\/graphql\/.+\/ListSubscribers/.test(req.url)) {
return;
}
try {
const newData = extractDataFromResponse(res, (json) => json.data.list.subscribers_timeline.timeline.instructions, (entry) => entry.content.itemContent.user_results.result);
listSubscribersSignal.value = [...listSubscribersSignal.value, ...newData];
logger.info(`ListSubscribers: ${newData.length} items received`);
} catch (err) {
logger.debug(req.method, req.url, res.status, res.responseText);
logger.errorWithBanner("ListSubscribers: Failed to parse API response", err);
}
};
function ListSubscribersPanel() {
return u(ModuleUI, {
title: "ListSubscribers",
recordsSignal: listSubscribersSignal
});
}
class ListSubscribersModule extends Extension {
constructor() {
super(...arguments);
__publicField(this, "name", "ListSubscribersModule");
}
intercept() {
return ListSubscribersInterceptor;
}
render() {
return ListSubscribersPanel;
}
}
const colors = {
info: "text-base-content",
warn: "text-warning",
error: "text-error"
};
function Logs({
lines
}) {
const reversed = lines.value.slice().reverse();
return 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) => u("span", {
class: colors[line.type],
children: ["#", line.index, " ", line.line, "\n"]
}, line.index))
});
}
function RuntimeLogsPanel() {
return u(preact.Fragment, {
children: [u("div", {
class: "divider mt-0 mb-1"
}), u(Logs, {
lines: logLinesSignal
})]
});
}
class RuntimeLogsModule extends Extension {
constructor() {
super(...arguments);
__publicField(this, "name", "RuntimeLogsModule");
}
render() {
return RuntimeLogsPanel;
}
}
const searchTimelineSignal = signals.signal([]);
const SearchTimelineInterceptor = (req, res) => {
if (!/\/graphql\/.+\/SearchTimeline/.test(req.url)) {
return;
}
try {
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");
for (const entry of timelineAddEntriesInstruction.entries) {
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 = entry.content.itemContent.user_results.result;
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);
}
searchTimelineSignal.value = [...searchTimelineSignal.value, ...newTweets];
logger.info(`SearchTimeline: ${newTweets.length} items received`);
if (newLists.length > 0) {
logger.warn(`SearchList: ${newLists.length} lists received but ignored (Reason: not implemented)`, newLists);
}
if (newUsers.length > 0) {
logger.warn(`SearchUser: ${newUsers.length} users received but ignored (Reason: not implemented)`, newUsers);
}
} catch (err) {
logger.debug(req.method, req.url, res.status, res.responseText);
logger.errorWithBanner("SearchTimeline: Failed to parse API response", err);
}
};
function SearchTimelinePanel() {
return u(ModuleUI, {
title: "SearchTimeline",
recordsSignal: searchTimelineSignal,
isTweet: true
});
}
class SearchTimelineModule extends Extension {
constructor() {
super(...arguments);
__publicField(this, "name", "SearchTimelineModule");
}
intercept() {
return SearchTimelineInterceptor;
}
render() {
return SearchTimelinePanel;
}
}
const tweetDetailSignal = signals.signal([]);
const TweetDetailInterceptor = (req, res) => {
if (!/\/graphql\/.+\/TweetDetail/.test(req.url)) {
return;
}
try {
const json = JSON.parse(res.responseText);
const instructions = json.data.threaded_conversation_with_injections_v2.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 (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);
}
tweetDetailSignal.value = [...tweetDetailSignal.value, ...newData];
logger.info(`TweetDetail: ${newData.length} items received`);
} catch (err) {
logger.debug(req.method, req.url, res.status, res.responseText);
logger.errorWithBanner("TweetDetail: Failed to parse API response", err);
}
};
function TweetDetailPanel() {
return u(ModuleUI, {
title: "TweetDetail",
recordsSignal: tweetDetailSignal,
isTweet: true
});
}
class TweetDetailModule extends Extension {
constructor() {
super(...arguments);
__publicField(this, "name", "TweetDetailModule");
}
intercept() {
return TweetDetailInterceptor;
}
render() {
return TweetDetailPanel;
}
}
const userMediaSignal = signals.signal([]);
const UserMediaInterceptor = (req, res) => {
if (!/\/graphql\/.+\/UserMedia/.test(req.url)) {
return;
}
try {
const json = JSON.parse(res.responseText);
const instructions = json.data.user.result.timeline_v2.timeline.instructions;
const newData = [];
const timelineAddEntriesInstruction = instructions.find((i) => i.type === "TimelineAddEntries");
for (const entry of timelineAddEntriesInstruction.entries) {
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);
}
userMediaSignal.value = [...userMediaSignal.value, ...newData];
logger.info(`UserMedia: ${newData.length} items received`);
} catch (err) {
logger.debug(req.method, req.url, res.status, res.responseText);
logger.errorWithBanner("UserMedia: Failed to parse API response", err);
}
};
function UserMediaPanel() {
return u(ModuleUI, {
title: "UserMedia",
recordsSignal: userMediaSignal,
isTweet: true
});
}
class UserMediaModule extends Extension {
constructor() {
super(...arguments);
__publicField(this, "name", "UserMediaModule");
}
intercept() {
return UserMediaInterceptor;
}
render() {
return UserMediaPanel;
}
}
const userTweetsSignal = signals.signal([]);
const UserTweetsInterceptor = (req, res) => {
if (!/\/graphql\/.+\/UserTweets/.test(req.url)) {
return;
}
try {
const json = JSON.parse(res.responseText);
const instructions = json.data.user.result.timeline_v2.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");
for (const entry of timelineAddEntriesInstruction.entries) {
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);
}
}
userTweetsSignal.value = [...userTweetsSignal.value, ...newData];
logger.info(`UserTweets: ${newData.length} items received`);
} catch (err) {
logger.debug(req.method, req.url, res.status, res.responseText);
logger.errorWithBanner("UserTweets: Failed to parse API response", err);
}
};
function UserTweetsPanel() {
return u(ModuleUI, {
title: "UserTweets",
recordsSignal: userTweetsSignal,
isTweet: true
});
}
class UserTweetsModule extends Extension {
constructor() {
super(...arguments);
__publicField(this, "name", "UserTweetsModule");
}
intercept() {
return UserTweetsInterceptor;
}
render() {
return UserTweetsPanel;
}
}
extensionManager.add(FollowersModule);
extensionManager.add(FollowingModule);
extensionManager.add(ListMembersModule);
extensionManager.add(ListSubscribersModule);
extensionManager.add(BookmarksModule);
extensionManager.add(LikesModule);
extensionManager.add(UserTweetsModule);
extensionManager.add(UserMediaModule);
extensionManager.add(TweetDetailModule);
extensionManager.add(SearchTimelineModule);
extensionManager.add(RuntimeLogsModule);
extensionManager.start();
function mountApp() {
const root = document.createElement("div");
root.id = "twe-root";
document.body.append(root);
preact.render(u(App, {}), root);
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", mountApp);
} else {
mountApp();
}
})(preact, preactHooks, preactSignals, dayjs, TableCore);