@zhangferry-dev/tokendash 1.2.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ /*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-duration:initial}}}@layer theme{:root,:host{--font-sans:"Geist", "Geist Fallback", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;--font-mono:"Geist Mono", "Geist Mono Fallback", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-200:oklch(88.5% .062 18.334);--color-red-600:oklch(57.7% .245 27.325);--color-orange-600:oklch(64.6% .222 41.116);--color-amber-50:oklch(98.7% .022 95.277);--color-amber-200:oklch(92.4% .12 95.746);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-600:oklch(66.6% .179 58.318);--color-amber-700:oklch(55.5% .163 48.998);--color-emerald-50:oklch(97.9% .021 166.113);--color-emerald-100:oklch(95% .052 163.051);--color-emerald-200:oklch(90.5% .093 164.15);--color-emerald-400:oklch(76.5% .177 163.223);--color-emerald-500:oklch(69.6% .17 162.48);--color-emerald-600:oklch(59.6% .145 163.225);--color-emerald-700:oklch(50.8% .118 165.612);--color-indigo-50:oklch(96.2% .018 272.314);--color-indigo-500:oklch(58.5% .233 277.117);--color-indigo-600:oklch(51.1% .262 276.966);--color-stone-50:oklch(98.5% .001 106.423);--color-stone-100:oklch(97% .001 106.424);--color-stone-200:oklch(92.3% .003 48.717);--color-stone-400:oklch(70.9% .01 56.259);--color-stone-500:oklch(55.3% .013 58.071);--color-stone-600:oklch(44.4% .011 73.639);--color-stone-700:oklch(37.4% .01 67.558);--color-stone-800:oklch(26.8% .007 34.298);--color-stone-900:oklch(21.6% .006 56.043);--color-white:#fff;--spacing:.25rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height: 1.2 ;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--font-weight-extrabold:800;--font-weight-black:900;--tracking-tighter:-.05em;--tracking-tight:-.025em;--tracking-wide:.025em;--tracking-wider:.05em;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--animate-spin:spin 1s linear infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.visible{visibility:visible}.absolute{position:absolute}.relative{position:relative}.static{position:static}.start{inset-inline-start:var(--spacing)}.-top-1\.5{top:calc(var(--spacing) * -1.5)}.top-full{top:100%}.bottom-full{bottom:100%}.left-1\/2{left:50%}.z-20{z-index:20}.z-50{z-index:50}.mx-auto{margin-inline:auto}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-2\.5{margin-top:calc(var(--spacing) * 2.5)}.mr-2{margin-right:calc(var(--spacing) * 2)}.mb-0\.5{margin-bottom:calc(var(--spacing) * .5)}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-2\.5{margin-bottom:calc(var(--spacing) * 2.5)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-5{margin-bottom:calc(var(--spacing) * 5)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.mb-8{margin-bottom:calc(var(--spacing) * 8)}.ml-10{margin-left:calc(var(--spacing) * 10)}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-block{display:inline-block}.table{display:table}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-3{height:calc(var(--spacing) * 3)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-6{height:calc(var(--spacing) * 6)}.h-8{height:calc(var(--spacing) * 8)}.h-10{height:calc(var(--spacing) * 10)}.h-20{height:calc(var(--spacing) * 20)}.h-48{height:calc(var(--spacing) * 48)}.h-64{height:calc(var(--spacing) * 64)}.h-72{height:calc(var(--spacing) * 72)}.h-\[22px\]{height:22px}.h-\[44px\]{height:44px}.min-h-0{min-height:calc(var(--spacing) * 0)}.min-h-dvh{min-height:100dvh}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-3{width:calc(var(--spacing) * 3)}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-4{width:calc(var(--spacing) * 4)}.w-6{width:calc(var(--spacing) * 6)}.w-8{width:calc(var(--spacing) * 8)}.w-20{width:calc(var(--spacing) * 20)}.w-48{width:calc(var(--spacing) * 48)}.w-72{width:calc(var(--spacing) * 72)}.w-\[320px\]{width:320px}.w-fit{width:fit-content}.w-full{width:100%}.w-px{width:1px}.max-w-\[200px\]{max-width:200px}.max-w-\[220px\]{max-width:220px}.max-w-\[1440px\]{max-width:1440px}.flex-1{flex:1}.shrink-0{flex-shrink:0}.-translate-x-1\/2{--tw-translate-x: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.rotate-45{rotate:45deg}.animate-spin{animation:var(--animate-spin)}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-around{justify-content:space-around}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}.gap-6{gap:calc(var(--spacing) * 6)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-x-auto{overflow-x:auto}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-\[3px\]{border-radius:3px}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-amber-200\/60{border-color:#fee68599}@supports (color:color-mix(in lab,red,red)){.border-amber-200\/60{border-color:color-mix(in oklab,var(--color-amber-200) 60%,transparent)}}.border-emerald-100\/50{border-color:#d0fae580}@supports (color:color-mix(in lab,red,red)){.border-emerald-100\/50{border-color:color-mix(in oklab,var(--color-emerald-100) 50%,transparent)}}.border-red-200\/60{border-color:#ffcaca99}@supports (color:color-mix(in lab,red,red)){.border-red-200\/60{border-color:color-mix(in oklab,var(--color-red-200) 60%,transparent)}}.border-stone-100{border-color:var(--color-stone-100)}.border-stone-200{border-color:var(--color-stone-200)}.border-stone-200\/40{border-color:#e7e5e466}@supports (color:color-mix(in lab,red,red)){.border-stone-200\/40{border-color:color-mix(in oklab,var(--color-stone-200) 40%,transparent)}}.border-stone-200\/50{border-color:#e7e5e480}@supports (color:color-mix(in lab,red,red)){.border-stone-200\/50{border-color:color-mix(in oklab,var(--color-stone-200) 50%,transparent)}}.border-stone-200\/60{border-color:#e7e5e499}@supports (color:color-mix(in lab,red,red)){.border-stone-200\/60{border-color:color-mix(in oklab,var(--color-stone-200) 60%,transparent)}}.bg-amber-50{background-color:var(--color-amber-50)}.bg-emerald-50\/50{background-color:#ecfdf580}@supports (color:color-mix(in lab,red,red)){.bg-emerald-50\/50{background-color:color-mix(in oklab,var(--color-emerald-50) 50%,transparent)}}.bg-emerald-200\/50{background-color:#a4f4cf80}@supports (color:color-mix(in lab,red,red)){.bg-emerald-200\/50{background-color:color-mix(in oklab,var(--color-emerald-200) 50%,transparent)}}.bg-indigo-50{background-color:var(--color-indigo-50)}.bg-indigo-50\/50{background-color:#eef2ff80}@supports (color:color-mix(in lab,red,red)){.bg-indigo-50\/50{background-color:color-mix(in oklab,var(--color-indigo-50) 50%,transparent)}}.bg-red-50{background-color:var(--color-red-50)}.bg-stone-50{background-color:var(--color-stone-50)}.bg-stone-50\/40{background-color:#fafaf966}@supports (color:color-mix(in lab,red,red)){.bg-stone-50\/40{background-color:color-mix(in oklab,var(--color-stone-50) 40%,transparent)}}.bg-stone-100{background-color:var(--color-stone-100)}.bg-stone-200\/50{background-color:#e7e5e480}@supports (color:color-mix(in lab,red,red)){.bg-stone-200\/50{background-color:color-mix(in oklab,var(--color-stone-200) 50%,transparent)}}.bg-stone-200\/60{background-color:#e7e5e499}@supports (color:color-mix(in lab,red,red)){.bg-stone-200\/60{background-color:color-mix(in oklab,var(--color-stone-200) 60%,transparent)}}.bg-stone-800{background-color:var(--color-stone-800)}.bg-stone-900{background-color:var(--color-stone-900)}.bg-white{background-color:var(--color-white)}.bg-\[radial-gradient\(ellipse_at_top\,\#eef2ff_0\%\,\#faf9f7_35\%\,\#faf9f7_100\%\)\]{background-image:radial-gradient(at top,#eef2ff,#faf9f7 35%,#faf9f7)}.p-0\.5{padding:calc(var(--spacing) * .5)}.p-1{padding:calc(var(--spacing) * 1)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-10{padding-block:calc(var(--spacing) * 10)}.pt-0\.5{padding-top:calc(var(--spacing) * .5)}.pt-1{padding-top:calc(var(--spacing) * 1)}.pt-2\.5{padding-top:calc(var(--spacing) * 2.5)}.pb-0\.5{padding-bottom:calc(var(--spacing) * .5)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[9px\]{font-size:9px}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.text-\[14px\]{font-size:14px}.text-\[15px\]{font-size:15px}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-black{--tw-font-weight:var(--font-weight-black);font-weight:var(--font-weight-black)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-extrabold{--tw-font-weight:var(--font-weight-extrabold);font-weight:var(--font-weight-extrabold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-tighter{--tw-tracking:var(--tracking-tighter);letter-spacing:var(--tracking-tighter)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.whitespace-nowrap{white-space:nowrap}.text-amber-400{color:var(--color-amber-400)}.text-amber-600{color:var(--color-amber-600)}.text-amber-700{color:var(--color-amber-700)}.text-emerald-500{color:var(--color-emerald-500)}.text-emerald-600{color:var(--color-emerald-600)}.text-emerald-600\/70{color:#009767b3}@supports (color:color-mix(in lab,red,red)){.text-emerald-600\/70{color:color-mix(in oklab,var(--color-emerald-600) 70%,transparent)}}.text-emerald-700\/80{color:#007956cc}@supports (color:color-mix(in lab,red,red)){.text-emerald-700\/80{color:color-mix(in oklab,var(--color-emerald-700) 80%,transparent)}}.text-indigo-500\/70{color:#625fffb3}@supports (color:color-mix(in lab,red,red)){.text-indigo-500\/70{color:color-mix(in oklab,var(--color-indigo-500) 70%,transparent)}}.text-indigo-600{color:var(--color-indigo-600)}.text-orange-600{color:var(--color-orange-600)}.text-red-600{color:var(--color-red-600)}.text-stone-400{color:var(--color-stone-400)}.text-stone-500{color:var(--color-stone-500)}.text-stone-600{color:var(--color-stone-600)}.text-stone-700{color:var(--color-stone-700)}.text-stone-800{color:var(--color-stone-800)}.text-stone-900{color:var(--color-stone-900)}.text-white{color:var(--color-white)}.uppercase{text-transform:uppercase}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.opacity-0{opacity:0}.opacity-25{opacity:.25}.opacity-75{opacity:.75}.shadow-\[0_1px_3px_rgba\(0\,0\,0\,0\.1\)\]{--tw-shadow:0 1px 3px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-\[0_1px_3px_rgba\(120\,113\,108\,0\.06\)\]{--tw-shadow:0 1px 3px var(--tw-shadow-color,#78716c0f);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-\[0_8px_30px_rgba\(120\,113\,108\,0\.12\)\]{--tw-shadow:0 8px 30px var(--tw-shadow-color,#78716c1f);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-\[0_8px_30px_rgba\(120\,113\,108\,0\.15\)\]{--tw-shadow:0 8px 30px var(--tw-shadow-color,#78716c26);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-inner{--tw-shadow:inset 0 2px 4px 0 var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-stone-900\/5{--tw-ring-color:#1c19170d}@supports (color:color-mix(in lab,red,red)){.ring-stone-900\/5{--tw-ring-color:color-mix(in oklab, var(--color-stone-900) 5%, transparent)}}.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,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-shadow{transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-200{--tw-duration:.2s;transition-duration:.2s}.outline-none{--tw-outline-style:none;outline-style:none}@media(hover:hover){.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}.hover\:z-10:hover{z-index:10}.hover\:bg-indigo-50:hover{background-color:var(--color-indigo-50)}.hover\:bg-stone-50:hover{background-color:var(--color-stone-50)}.hover\:bg-stone-50\/60:hover{background-color:#fafaf999}@supports (color:color-mix(in lab,red,red)){.hover\:bg-stone-50\/60:hover{background-color:color-mix(in oklab,var(--color-stone-50) 60%,transparent)}}.hover\:bg-stone-200\/50:hover{background-color:#e7e5e480}@supports (color:color-mix(in lab,red,red)){.hover\:bg-stone-200\/50:hover{background-color:color-mix(in oklab,var(--color-stone-200) 50%,transparent)}}.hover\:text-indigo-600:hover{color:var(--color-indigo-600)}.hover\:text-stone-800:hover{color:var(--color-stone-800)}.hover\:shadow-\[0_4px_12px_rgba\(120\,113\,108\,0\.09\)\]:hover{--tw-shadow:0 4px 12px var(--tw-shadow-color,#78716c17);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:ring-2:hover{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:ring-emerald-400:hover{--tw-ring-color:var(--color-emerald-400)}.hover\:ring-offset-1:hover{--tw-ring-offset-width:1px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}}.focus\:border-indigo-500:focus{border-color:var(--color-indigo-500)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-indigo-500\/20:focus{--tw-ring-color:#625fff33}@supports (color:color-mix(in lab,red,red)){.focus\:ring-indigo-500\/20:focus{--tw-ring-color:color-mix(in oklab, var(--color-indigo-500) 20%, transparent)}}@media(min-width:40rem){.sm\:block{display:block}}@media(min-width:48rem){.md\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.md\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:items-center{align-items:center}}@media(min-width:64rem){.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}}html,body{background-color:#faf9f7!important}@font-face{font-family:Geist;src:url(https://cdn.jsdelivr.net/npm/geist@1.3.1/dist/fonts/geist-sans/Geist-Variable.woff2)format("woff2");font-weight:100 900;font-display:swap;font-style:normal}@font-face{font-family:Geist Mono;src:url(https://cdn.jsdelivr.net/npm/geist@1.3.1/dist/fonts/geist-mono/GeistMono-Variable.woff2)format("woff2");font-weight:100 900;font-display:swap;font-style:normal}body:before{content:"";z-index:9999;pointer-events:none;opacity:.015;background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E");background-repeat:repeat;background-size:200px 200px;position:fixed;top:0;right:0;bottom:0;left:0}.recharts-cartesian-grid-horizontal line,.recharts-cartesian-grid-vertical line{stroke:#e7e5e4}.recharts-text{fill:#78716c;font-family:var(--font-sans);font-size:11px}.recharts-legend-item-text{color:#57534e;font-size:12px}.recharts-tooltip-wrapper{outline:none}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background:#c8c4be;border-radius:3px}::-webkit-scrollbar-thumb:hover{background:#a8a29e}@keyframes shimmer{0%{background-position:-200% 0}to{background-position:200% 0}}.skeleton{background:linear-gradient(90deg,#e7e5e4 25%,#d6d3d1,#e7e5e4 75%) 0 0/200% 100%;animation:1.5s ease-in-out infinite shimmer}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}
@@ -5,8 +5,8 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>TokenDash</title>
7
7
  <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><text y='28' font-size='28'>⚡</text></svg>" />
8
- <script type="module" crossorigin src="/assets/index-DohuMiQc.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-92lvfG3S.css">
8
+ <script type="module" crossorigin src="/assets/index-D-RErhSy.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-x7K7fQX4.css">
10
10
  </head>
11
11
  <body class="antialiased" style="background:#faf9f7">
12
12
  <div id="root"></div>
@@ -1,6 +1,8 @@
1
1
  export declare function isClaudeCodeAvailable(): boolean;
2
2
  export declare function isCodexAvailable(): boolean;
3
+ export declare function isOpencodeAvailable(): boolean;
3
4
  export declare function detectAvailableAgents(): {
4
5
  claude: boolean;
5
6
  codex: boolean;
7
+ opencode: boolean;
6
8
  };
@@ -17,9 +17,13 @@ export function isClaudeCodeAvailable() {
17
17
  export function isCodexAvailable() {
18
18
  return existsSync(CODEX_SESSIONS_DIR);
19
19
  }
20
+ export function isOpencodeAvailable() {
21
+ return existsSync(join(homedir(), '.local', 'share', 'opencode', 'opencode.db'));
22
+ }
20
23
  export function detectAvailableAgents() {
21
24
  return {
22
25
  claude: isClaudeCodeAvailable(),
23
26
  codex: isCodexAvailable(),
27
+ opencode: isOpencodeAvailable(),
24
28
  };
25
29
  }
@@ -1,5 +1,7 @@
1
1
  import express from 'express';
2
2
  import { readFileSync } from 'node:fs';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { dirname, join } from 'node:path';
3
5
  import { registerApiRoutes } from './routes/api.js';
4
6
  import { detectAvailableAgents } from './agentDetection.js';
5
7
  import open from 'open';
@@ -10,7 +12,9 @@ const CLI_USAGE = [
10
12
  ' tokendash --port <number> [--no-open]',
11
13
  ].join('\n');
12
14
  function getPackageVersion() {
13
- const packageJson = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf8'));
15
+ const __filename = fileURLToPath(import.meta.url);
16
+ const __dirname = dirname(__filename);
17
+ const packageJson = JSON.parse(readFileSync(join(__dirname, '..', '..', 'package.json'), 'utf8'));
14
18
  return packageJson.version ?? 'unknown';
15
19
  }
16
20
  function exitWithCliError(message) {
@@ -133,8 +137,9 @@ async function main() {
133
137
  const isProduction = import.meta.url.includes('dist/');
134
138
  if (isProduction) {
135
139
  // Serve static files from client build
136
- const clientPath = new URL('../client', import.meta.url).pathname;
137
- const clientIndexPath = new URL('../client/index.html', import.meta.url).pathname;
140
+ const __dirname = dirname(fileURLToPath(import.meta.url));
141
+ const clientPath = join(__dirname, '..', 'client');
142
+ const clientIndexPath = join(clientPath, 'index.html');
138
143
  app.use(express.static(clientPath));
139
144
  // SPA fallback
140
145
  app.use('{*path}', (_req, res) => {
@@ -0,0 +1,22 @@
1
+ import type { DailyResponse, ProjectsResponse, BlocksResponse } from '../shared/types.js';
2
+ export declare function isOpencodeAccessible(): boolean;
3
+ interface OpenCodeTokenEvent {
4
+ timestampMs: number;
5
+ inputTokens: number;
6
+ outputTokens: number;
7
+ cacheReadTokens: number;
8
+ cacheWriteTokens: number;
9
+ totalTokens: number;
10
+ cost: number;
11
+ model: string;
12
+ project: string;
13
+ }
14
+ export declare function parseAllOpenCodeEvents(project?: string | null): OpenCodeTokenEvent[];
15
+ export interface OpenCodeAggregateOptions {
16
+ project?: string | null;
17
+ timezone?: string;
18
+ }
19
+ export declare function getDailyResponse(options?: OpenCodeAggregateOptions): DailyResponse;
20
+ export declare function getProjectsResponse(options?: OpenCodeAggregateOptions): ProjectsResponse;
21
+ export declare function getBlocksResponse(options?: OpenCodeAggregateOptions): BlocksResponse;
22
+ export {};
@@ -0,0 +1,292 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { execSync } from 'node:child_process';
3
+ import { join } from 'node:path';
4
+ import { homedir } from 'node:os';
5
+ // ---------------------------------------------------------------------------
6
+ // OpenCode SQLite format
7
+ //
8
+ // Database: ~/.local/share/opencode/opencode.db
9
+ // Table: message
10
+ // Column: data (JSON) with structure:
11
+ // {
12
+ // "role": "assistant",
13
+ // "time": { "created": <ms>, "completed": <ms> },
14
+ // "modelID": "glm-4.7",
15
+ // "providerID": "zhipuai-coding-plan",
16
+ // "tokens": { "input": N, "output": N, "reasoning": N, "cache": { "read": N, "write": N } },
17
+ // "cost": N,
18
+ // "path": { "cwd": "/path/to/project" }
19
+ // }
20
+ // ---------------------------------------------------------------------------
21
+ const OPENCODE_DB = join(homedir(), '.local', 'share', 'opencode', 'opencode.db');
22
+ // ---------------------------------------------------------------------------
23
+ // Detection
24
+ // ---------------------------------------------------------------------------
25
+ export function isOpencodeAccessible() {
26
+ return existsSync(OPENCODE_DB);
27
+ }
28
+ // ---------------------------------------------------------------------------
29
+ // SQLite query helper
30
+ // ---------------------------------------------------------------------------
31
+ function queryOpenCodeDB(sql) {
32
+ return execSync(`sqlite3 -json "${OPENCODE_DB}" "${sql}"`, {
33
+ encoding: 'utf-8',
34
+ maxBuffer: 50 * 1024 * 1024,
35
+ timeout: 10000,
36
+ });
37
+ }
38
+ export function parseAllOpenCodeEvents(project) {
39
+ let sql = `SELECT data FROM message WHERE json_extract(data, '$.role') = 'assistant'`;
40
+ if (project) {
41
+ sql += ` AND json_extract(data, '$.path.cwd') = '${project.replace(/'/g, "''")}'`;
42
+ }
43
+ let raw;
44
+ try {
45
+ raw = queryOpenCodeDB(sql);
46
+ }
47
+ catch {
48
+ return [];
49
+ }
50
+ let rows;
51
+ try {
52
+ rows = JSON.parse(raw);
53
+ }
54
+ catch {
55
+ return [];
56
+ }
57
+ const events = [];
58
+ for (const row of rows) {
59
+ let data;
60
+ try {
61
+ data = JSON.parse(row.data);
62
+ }
63
+ catch {
64
+ continue;
65
+ }
66
+ const tokens = data.tokens || {};
67
+ const cache = tokens.cache || {};
68
+ const time = data.time || {};
69
+ const path = data.path || {};
70
+ const input = Number(tokens.input ?? 0);
71
+ const output = Number(tokens.output ?? 0);
72
+ const cacheRead = Number(cache.read ?? 0);
73
+ const cacheWrite = Number(cache.write ?? 0);
74
+ if (input === 0 && output === 0 && cacheRead === 0 && cacheWrite === 0)
75
+ continue;
76
+ events.push({
77
+ timestampMs: Number(time.created ?? 0),
78
+ inputTokens: Math.max(0, input),
79
+ outputTokens: Math.max(0, output),
80
+ cacheReadTokens: Math.max(0, cacheRead),
81
+ cacheWriteTokens: Math.max(0, cacheWrite),
82
+ totalTokens: Math.max(0, input + output + cacheRead),
83
+ cost: Math.max(0, Number(data.cost ?? 0)),
84
+ model: String(data.modelID ?? 'unknown'),
85
+ project: String(path.cwd ?? ''),
86
+ });
87
+ }
88
+ return events;
89
+ }
90
+ // ---------------------------------------------------------------------------
91
+ // Date / timezone helpers (same logic as openclawParser)
92
+ // ---------------------------------------------------------------------------
93
+ const TZ_OFFSETS = {
94
+ 'Asia/Shanghai': 8,
95
+ 'Asia/Tokyo': 9,
96
+ 'America/New_York': -5,
97
+ 'America/Los_Angeles': -8,
98
+ 'Europe/London': 0,
99
+ 'UTC': 0,
100
+ };
101
+ function getTzOffsetHours(tz) {
102
+ return TZ_OFFSETS[tz] ?? 8;
103
+ }
104
+ function msToLocalDate(ms, tz) {
105
+ return new Date(ms + getTzOffsetHours(tz) * 3_600_000);
106
+ }
107
+ function getDateKey(ms, tz) {
108
+ return msToLocalDate(ms, tz).toISOString().slice(0, 10);
109
+ }
110
+ function getHourKey(ms, tz) {
111
+ const d = msToLocalDate(ms, tz);
112
+ return d.toISOString().slice(0, 13).replace('T', ' ') + ':00';
113
+ }
114
+ // ---------------------------------------------------------------------------
115
+ // Aggregation helpers
116
+ // ---------------------------------------------------------------------------
117
+ function emptyAcc() {
118
+ return { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, totalTokens: 0, cost: 0 };
119
+ }
120
+ function addEvent(acc, ev) {
121
+ acc.inputTokens += ev.inputTokens;
122
+ acc.outputTokens += ev.outputTokens;
123
+ acc.cacheReadTokens += ev.cacheReadTokens;
124
+ acc.cacheWriteTokens += ev.cacheWriteTokens;
125
+ acc.totalTokens += ev.totalTokens;
126
+ acc.cost += ev.cost;
127
+ }
128
+ export function getDailyResponse(options) {
129
+ const events = parseAllOpenCodeEvents(options?.project);
130
+ const tz = options?.timezone || 'Asia/Shanghai';
131
+ const grouped = new Map();
132
+ for (const ev of events) {
133
+ const key = getDateKey(ev.timestampMs, tz);
134
+ if (!grouped.has(key))
135
+ grouped.set(key, { totals: emptyAcc(), models: new Map() });
136
+ const g = grouped.get(key);
137
+ addEvent(g.totals, ev);
138
+ if (!g.models.has(ev.model)) {
139
+ g.models.set(ev.model, { inputTokens: 0, outputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0, cost: 0 });
140
+ }
141
+ const m = g.models.get(ev.model);
142
+ m.inputTokens += ev.inputTokens;
143
+ m.outputTokens += ev.outputTokens;
144
+ m.cacheCreationTokens += ev.cacheWriteTokens;
145
+ m.cacheReadTokens += ev.cacheReadTokens;
146
+ m.cost += ev.cost;
147
+ }
148
+ const totalsAcc = emptyAcc();
149
+ const daily = [];
150
+ for (const [date, g] of grouped) {
151
+ mergeAcc(totalsAcc, g.totals);
152
+ const modelList = [...g.models.keys()];
153
+ daily.push({
154
+ date,
155
+ inputTokens: g.totals.inputTokens,
156
+ outputTokens: g.totals.outputTokens,
157
+ cacheCreationTokens: g.totals.cacheWriteTokens,
158
+ cacheReadTokens: g.totals.cacheReadTokens,
159
+ totalTokens: g.totals.totalTokens,
160
+ totalCost: g.totals.cost,
161
+ modelsUsed: modelList,
162
+ modelBreakdowns: modelList.map(name => {
163
+ const m = g.models.get(name);
164
+ return {
165
+ modelName: name,
166
+ inputTokens: m.inputTokens,
167
+ outputTokens: m.outputTokens,
168
+ cacheCreationTokens: m.cacheCreationTokens,
169
+ cacheReadTokens: m.cacheReadTokens,
170
+ cost: m.cost,
171
+ };
172
+ }),
173
+ });
174
+ }
175
+ daily.sort((a, b) => a.date.localeCompare(b.date));
176
+ return {
177
+ daily,
178
+ totals: {
179
+ inputTokens: totalsAcc.inputTokens,
180
+ outputTokens: totalsAcc.outputTokens,
181
+ cacheCreationTokens: totalsAcc.cacheWriteTokens,
182
+ cacheReadTokens: totalsAcc.cacheReadTokens,
183
+ totalTokens: totalsAcc.totalTokens,
184
+ totalCost: totalsAcc.cost,
185
+ },
186
+ };
187
+ }
188
+ function mergeAcc(a, b) {
189
+ a.inputTokens += b.inputTokens;
190
+ a.outputTokens += b.outputTokens;
191
+ a.cacheReadTokens += b.cacheReadTokens;
192
+ a.cacheWriteTokens += b.cacheWriteTokens;
193
+ a.totalTokens += b.totalTokens;
194
+ a.cost += b.cost;
195
+ }
196
+ export function getProjectsResponse(options) {
197
+ const events = parseAllOpenCodeEvents();
198
+ const tz = options?.timezone || 'Asia/Shanghai';
199
+ const projects = {};
200
+ for (const ev of events) {
201
+ const projectName = ev.project || 'unknown';
202
+ const dayKey = getDateKey(ev.timestampMs, tz);
203
+ if (!projects[projectName])
204
+ projects[projectName] = [];
205
+ // Find or create entry for this date in this project
206
+ let dayEntry = projects[projectName].find(d => d.date === dayKey);
207
+ if (!dayEntry) {
208
+ dayEntry = {
209
+ date: dayKey,
210
+ inputTokens: 0,
211
+ outputTokens: 0,
212
+ cacheCreationTokens: 0,
213
+ cacheReadTokens: 0,
214
+ totalTokens: 0,
215
+ totalCost: 0,
216
+ modelsUsed: [],
217
+ modelBreakdowns: [],
218
+ };
219
+ projects[projectName].push(dayEntry);
220
+ }
221
+ dayEntry.inputTokens += ev.inputTokens;
222
+ dayEntry.outputTokens += ev.outputTokens;
223
+ dayEntry.cacheCreationTokens += ev.cacheWriteTokens;
224
+ dayEntry.cacheReadTokens += ev.cacheReadTokens;
225
+ dayEntry.totalTokens += ev.totalTokens;
226
+ dayEntry.totalCost += ev.cost;
227
+ if (!dayEntry.modelsUsed.includes(ev.model)) {
228
+ dayEntry.modelsUsed.push(ev.model);
229
+ }
230
+ // Update or add model breakdown
231
+ let breakdown = dayEntry.modelBreakdowns.find(b => b.modelName === ev.model);
232
+ if (!breakdown) {
233
+ breakdown = {
234
+ modelName: ev.model,
235
+ inputTokens: 0,
236
+ outputTokens: 0,
237
+ cacheCreationTokens: 0,
238
+ cacheReadTokens: 0,
239
+ cost: 0,
240
+ };
241
+ dayEntry.modelBreakdowns.push(breakdown);
242
+ }
243
+ breakdown.inputTokens += ev.inputTokens;
244
+ breakdown.outputTokens += ev.outputTokens;
245
+ breakdown.cacheCreationTokens += ev.cacheWriteTokens;
246
+ breakdown.cacheReadTokens += ev.cacheReadTokens;
247
+ breakdown.cost += ev.cost;
248
+ }
249
+ for (const key of Object.keys(projects)) {
250
+ projects[key].sort((a, b) => a.date.localeCompare(b.date));
251
+ }
252
+ return { projects };
253
+ }
254
+ export function getBlocksResponse(options) {
255
+ const events = parseAllOpenCodeEvents(options?.project);
256
+ const tz = options?.timezone || 'Asia/Shanghai';
257
+ const grouped = new Map();
258
+ for (const ev of events) {
259
+ const key = getHourKey(ev.timestampMs, tz);
260
+ if (!grouped.has(key))
261
+ grouped.set(key, { acc: emptyAcc(), models: new Set() });
262
+ addEvent(grouped.get(key).acc, ev);
263
+ grouped.get(key).models.add(ev.model);
264
+ }
265
+ const blocks = [];
266
+ let idx = 0;
267
+ for (const [hourKey, { acc, models }] of grouped) {
268
+ const [datePart, timePart] = hourKey.split(' ');
269
+ const hour = timePart.split(':')[0];
270
+ blocks.push({
271
+ id: `opencode-hour-${idx}`,
272
+ startTime: `${datePart}T${hour}:00:00`,
273
+ endTime: `${datePart}T${hour}:59:59`,
274
+ actualEndTime: null,
275
+ isActive: false,
276
+ isGap: false,
277
+ entries: acc.totalTokens > 0 ? 1 : 0,
278
+ tokenCounts: {
279
+ inputTokens: acc.inputTokens,
280
+ outputTokens: acc.outputTokens,
281
+ cacheCreationInputTokens: acc.cacheWriteTokens,
282
+ cacheReadInputTokens: acc.cacheReadTokens,
283
+ },
284
+ totalTokens: acc.totalTokens,
285
+ costUSD: acc.cost,
286
+ models: [...models],
287
+ });
288
+ idx++;
289
+ }
290
+ blocks.sort((a, b) => a.startTime.localeCompare(b.startTime));
291
+ return { blocks };
292
+ }
@@ -10,7 +10,7 @@ const EMPTY_ANALYTICS = {
10
10
  export async function getAnalytics(req, res) {
11
11
  const agent = req.query.agent || 'claude';
12
12
  const project = req.query.project || undefined;
13
- if (agent === 'codex') {
13
+ if (agent === 'codex' || agent === 'opencode') {
14
14
  res.json(EMPTY_ANALYTICS);
15
15
  return;
16
16
  }
@@ -6,6 +6,7 @@ import { getBlocks } from './blocks.js';
6
6
  import { getAnalytics } from './analytics.js';
7
7
  import { detectAvailableAgents } from '../agentDetection.js';
8
8
  import { isOpenClawAccessible } from '../openclawParser.js';
9
+ import { isOpencodeAccessible } from '../opencodeParser.js';
9
10
  function getAgents(_req, res) {
10
11
  try {
11
12
  const agents = detectAvailableAgents();
@@ -16,6 +17,8 @@ function getAgents(_req, res) {
16
17
  available.push('codex');
17
18
  if (isOpenClawAccessible())
18
19
  available.push('openclaw');
20
+ if (isOpencodeAccessible())
21
+ available.push('opencode');
19
22
  res.json({ available, default: available[0] || null });
20
23
  }
21
24
  catch (error) {
@@ -2,6 +2,7 @@ import { cache } from '../cache.js';
2
2
  import { validateBlocks } from '../../shared/schemas.js';
3
3
  import { getBlocksResponse as getCodexBlocksResponse } from '../codexParser.js';
4
4
  import { getBlocksResponse as getOpenClawBlocksResponse } from '../openclawParser.js';
5
+ import { getBlocksResponse as getOpencodeBlocksResponse } from '../opencodeParser.js';
5
6
  import { getBlocksResponse as getClaudeBlocksResponse } from '../claudeJsonlParser.js';
6
7
  export async function getBlocks(req, res) {
7
8
  const agent = req.query.agent || 'claude';
@@ -37,6 +38,9 @@ function fetchBlocksData(agent, project) {
37
38
  if (agent === 'openclaw') {
38
39
  return validateBlocks(getOpenClawBlocksResponse({ project: project || null }));
39
40
  }
41
+ else if (agent === 'opencode') {
42
+ return validateBlocks(getOpencodeBlocksResponse({ project: project || null }));
43
+ }
40
44
  else if (agent === 'codex') {
41
45
  return validateBlocks(getCodexBlocksResponse({ project: project || null }));
42
46
  }
@@ -2,6 +2,7 @@ import { cache } from '../cache.js';
2
2
  import { validateDaily } from '../../shared/schemas.js';
3
3
  import { getDailyResponse as getCodexDailyResponse } from '../codexParser.js';
4
4
  import { getDailyResponse as getOpenClawDailyResponse } from '../openclawParser.js';
5
+ import { getDailyResponse as getOpencodeDailyResponse } from '../opencodeParser.js';
5
6
  import { getDailyResponse as getClaudeDailyResponse } from '../claudeJsonlParser.js';
6
7
  export async function getDaily(req, res) {
7
8
  const agent = req.query.agent || 'claude';
@@ -39,6 +40,9 @@ function fetchDailyData(agent) {
39
40
  else if (agent === 'openclaw') {
40
41
  return Promise.resolve(validateDaily(getOpenClawDailyResponse()));
41
42
  }
43
+ else if (agent === 'opencode') {
44
+ return Promise.resolve(validateDaily(getOpencodeDailyResponse()));
45
+ }
42
46
  else {
43
47
  // Claude Code: parse JSONL directly (fast, no CLI)
44
48
  return Promise.resolve(validateDaily(getClaudeDailyResponse()));
@@ -2,6 +2,7 @@ import { cache } from '../cache.js';
2
2
  import { validateProjects } from '../../shared/schemas.js';
3
3
  import { getProjectsResponse as getCodexProjectsResponse } from '../codexParser.js';
4
4
  import { getProjectsResponse as getOpenClawProjectsResponse } from '../openclawParser.js';
5
+ import { getProjectsResponse as getOpencodeProjectsResponse } from '../opencodeParser.js';
5
6
  import { getProjectsResponse as getClaudeProjectsResponse } from '../claudeJsonlParser.js';
6
7
  export async function getProjects(req, res) {
7
8
  const agent = req.query.agent || 'claude';
@@ -39,6 +40,9 @@ function fetchProjectsData(agent) {
39
40
  else if (agent === 'openclaw') {
40
41
  return validateProjects(getOpenClawProjectsResponse());
41
42
  }
43
+ else if (agent === 'opencode') {
44
+ return validateProjects(getOpencodeProjectsResponse());
45
+ }
42
46
  else {
43
47
  // Claude Code: parse JSONL directly (fast, no CLI)
44
48
  return validateProjects(getClaudeProjectsResponse());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhangferry-dev/tokendash",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
4
4
  "type": "module",
5
5
  "description": "Token Usage Analytics Dashboard",
6
6
  "publishConfig": {
@@ -25,7 +25,8 @@
25
25
  "test": "vitest run",
26
26
  "test:watch": "vitest",
27
27
  "test:e2e": "playwright test",
28
- "prepack": "npm run build"
28
+ "prepack": "npm run build",
29
+ "publish": "npm publish --access public --registry https://registry.npmjs.org"
29
30
  },
30
31
  "dependencies": {
31
32
  "express": "^5.1.0",
@@ -37,15 +38,17 @@
37
38
  },
38
39
  "devDependencies": {
39
40
  "@playwright/test": "^1.59.1",
41
+ "@tailwindcss/vite": "^4.1.4",
40
42
  "@types/express": "^5.0.2",
41
43
  "@types/node": "^22.15.2",
42
44
  "@types/react": "^19.1.2",
43
45
  "@types/react-dom": "^19.1.2",
44
46
  "@vitejs/plugin-react": "^4.4.1",
45
47
  "concurrently": "^9.1.2",
48
+ "tailwindcss": "^4.1.4",
46
49
  "tsx": "^4.19.3",
47
50
  "typescript": "^5.8.3",
48
51
  "vite": "^6.3.3",
49
52
  "vitest": "^4.1.4"
50
53
  }
51
- }
54
+ }
@@ -1 +0,0 @@
1
- /*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-duration:initial}}}@layer theme{:root,:host{--font-sans:"Geist", "Geist Fallback", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;--font-mono:"Geist Mono", "Geist Mono Fallback", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-200:oklch(88.5% .062 18.334);--color-red-600:oklch(57.7% .245 27.325);--color-orange-600:oklch(64.6% .222 41.116);--color-amber-50:oklch(98.7% .022 95.277);--color-amber-200:oklch(92.4% .12 95.746);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-700:oklch(55.5% .163 48.998);--color-emerald-50:oklch(97.9% .021 166.113);--color-emerald-100:oklch(95% .052 163.051);--color-emerald-200:oklch(90.5% .093 164.15);--color-emerald-400:oklch(76.5% .177 163.223);--color-emerald-500:oklch(69.6% .17 162.48);--color-emerald-600:oklch(59.6% .145 163.225);--color-emerald-700:oklch(50.8% .118 165.612);--color-indigo-50:oklch(96.2% .018 272.314);--color-indigo-500:oklch(58.5% .233 277.117);--color-indigo-600:oklch(51.1% .262 276.966);--color-stone-50:oklch(98.5% .001 106.423);--color-stone-100:oklch(97% .001 106.424);--color-stone-200:oklch(92.3% .003 48.717);--color-stone-400:oklch(70.9% .01 56.259);--color-stone-500:oklch(55.3% .013 58.071);--color-stone-600:oklch(44.4% .011 73.639);--color-stone-700:oklch(37.4% .01 67.558);--color-stone-800:oklch(26.8% .007 34.298);--color-stone-900:oklch(21.6% .006 56.043);--color-white:#fff;--spacing:.25rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height: 1.2 ;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--font-weight-extrabold:800;--font-weight-black:900;--tracking-tighter:-.05em;--tracking-tight:-.025em;--tracking-wide:.025em;--tracking-wider:.05em;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--animate-spin:spin 1s linear infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.start{inset-inline-start:var(--spacing)}.-top-1\.5{top:calc(var(--spacing) * -1.5)}.top-full{top:100%}.bottom-full{bottom:100%}.left-1\/2{left:50%}.z-20{z-index:20}.z-50{z-index:50}.mx-auto{margin-inline:auto}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-2\.5{margin-top:calc(var(--spacing) * 2.5)}.mr-2{margin-right:calc(var(--spacing) * 2)}.mb-0\.5{margin-bottom:calc(var(--spacing) * .5)}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-2\.5{margin-bottom:calc(var(--spacing) * 2.5)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-5{margin-bottom:calc(var(--spacing) * 5)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.mb-8{margin-bottom:calc(var(--spacing) * 8)}.ml-10{margin-left:calc(var(--spacing) * 10)}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-block{display:inline-block}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-3{height:calc(var(--spacing) * 3)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-6{height:calc(var(--spacing) * 6)}.h-8{height:calc(var(--spacing) * 8)}.h-10{height:calc(var(--spacing) * 10)}.h-20{height:calc(var(--spacing) * 20)}.h-48{height:calc(var(--spacing) * 48)}.h-64{height:calc(var(--spacing) * 64)}.h-72{height:calc(var(--spacing) * 72)}.h-\[22px\]{height:22px}.min-h-0{min-height:calc(var(--spacing) * 0)}.min-h-dvh{min-height:100dvh}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-3{width:calc(var(--spacing) * 3)}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-4{width:calc(var(--spacing) * 4)}.w-6{width:calc(var(--spacing) * 6)}.w-8{width:calc(var(--spacing) * 8)}.w-20{width:calc(var(--spacing) * 20)}.w-48{width:calc(var(--spacing) * 48)}.w-72{width:calc(var(--spacing) * 72)}.w-\[320px\]{width:320px}.w-fit{width:fit-content}.w-full{width:100%}.w-px{width:1px}.max-w-\[200px\]{max-width:200px}.max-w-\[220px\]{max-width:220px}.max-w-\[1440px\]{max-width:1440px}.flex-1{flex:1}.shrink-0{flex-shrink:0}.-translate-x-1\/2{--tw-translate-x: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.rotate-45{rotate:45deg}.animate-spin{animation:var(--animate-spin)}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-around{justify-content:space-around}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}.gap-6{gap:calc(var(--spacing) * 6)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-x-auto{overflow-x:auto}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-\[3px\]{border-radius:3px}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-amber-200\/60{border-color:#fee68599}@supports (color:color-mix(in lab,red,red)){.border-amber-200\/60{border-color:color-mix(in oklab,var(--color-amber-200) 60%,transparent)}}.border-emerald-100\/50{border-color:#d0fae580}@supports (color:color-mix(in lab,red,red)){.border-emerald-100\/50{border-color:color-mix(in oklab,var(--color-emerald-100) 50%,transparent)}}.border-red-200\/60{border-color:#ffcaca99}@supports (color:color-mix(in lab,red,red)){.border-red-200\/60{border-color:color-mix(in oklab,var(--color-red-200) 60%,transparent)}}.border-stone-100{border-color:var(--color-stone-100)}.border-stone-200{border-color:var(--color-stone-200)}.border-stone-200\/40{border-color:#e7e5e466}@supports (color:color-mix(in lab,red,red)){.border-stone-200\/40{border-color:color-mix(in oklab,var(--color-stone-200) 40%,transparent)}}.border-stone-200\/50{border-color:#e7e5e480}@supports (color:color-mix(in lab,red,red)){.border-stone-200\/50{border-color:color-mix(in oklab,var(--color-stone-200) 50%,transparent)}}.border-stone-200\/60{border-color:#e7e5e499}@supports (color:color-mix(in lab,red,red)){.border-stone-200\/60{border-color:color-mix(in oklab,var(--color-stone-200) 60%,transparent)}}.bg-amber-50{background-color:var(--color-amber-50)}.bg-emerald-50\/50{background-color:#ecfdf580}@supports (color:color-mix(in lab,red,red)){.bg-emerald-50\/50{background-color:color-mix(in oklab,var(--color-emerald-50) 50%,transparent)}}.bg-emerald-200\/50{background-color:#a4f4cf80}@supports (color:color-mix(in lab,red,red)){.bg-emerald-200\/50{background-color:color-mix(in oklab,var(--color-emerald-200) 50%,transparent)}}.bg-indigo-50{background-color:var(--color-indigo-50)}.bg-indigo-50\/50{background-color:#eef2ff80}@supports (color:color-mix(in lab,red,red)){.bg-indigo-50\/50{background-color:color-mix(in oklab,var(--color-indigo-50) 50%,transparent)}}.bg-red-50{background-color:var(--color-red-50)}.bg-stone-50{background-color:var(--color-stone-50)}.bg-stone-50\/40{background-color:#fafaf966}@supports (color:color-mix(in lab,red,red)){.bg-stone-50\/40{background-color:color-mix(in oklab,var(--color-stone-50) 40%,transparent)}}.bg-stone-100{background-color:var(--color-stone-100)}.bg-stone-200\/50{background-color:#e7e5e480}@supports (color:color-mix(in lab,red,red)){.bg-stone-200\/50{background-color:color-mix(in oklab,var(--color-stone-200) 50%,transparent)}}.bg-stone-200\/60{background-color:#e7e5e499}@supports (color:color-mix(in lab,red,red)){.bg-stone-200\/60{background-color:color-mix(in oklab,var(--color-stone-200) 60%,transparent)}}.bg-stone-800{background-color:var(--color-stone-800)}.bg-stone-900{background-color:var(--color-stone-900)}.bg-white{background-color:var(--color-white)}.bg-\[radial-gradient\(ellipse_at_top\,\#eef2ff_0\%\,\#faf9f7_35\%\,\#faf9f7_100\%\)\]{background-image:radial-gradient(at top,#eef2ff,#faf9f7 35%,#faf9f7)}.p-0\.5{padding:calc(var(--spacing) * .5)}.p-1{padding:calc(var(--spacing) * 1)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-10{padding-block:calc(var(--spacing) * 10)}.pt-0\.5{padding-top:calc(var(--spacing) * .5)}.pt-1{padding-top:calc(var(--spacing) * 1)}.pt-2\.5{padding-top:calc(var(--spacing) * 2.5)}.pb-0\.5{padding-bottom:calc(var(--spacing) * .5)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.text-\[14px\]{font-size:14px}.text-\[15px\]{font-size:15px}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-black{--tw-font-weight:var(--font-weight-black);font-weight:var(--font-weight-black)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-extrabold{--tw-font-weight:var(--font-weight-extrabold);font-weight:var(--font-weight-extrabold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-tighter{--tw-tracking:var(--tracking-tighter);letter-spacing:var(--tracking-tighter)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.whitespace-nowrap{white-space:nowrap}.text-amber-400{color:var(--color-amber-400)}.text-amber-700{color:var(--color-amber-700)}.text-emerald-500{color:var(--color-emerald-500)}.text-emerald-600{color:var(--color-emerald-600)}.text-emerald-600\/70{color:#009767b3}@supports (color:color-mix(in lab,red,red)){.text-emerald-600\/70{color:color-mix(in oklab,var(--color-emerald-600) 70%,transparent)}}.text-emerald-700\/80{color:#007956cc}@supports (color:color-mix(in lab,red,red)){.text-emerald-700\/80{color:color-mix(in oklab,var(--color-emerald-700) 80%,transparent)}}.text-indigo-500\/70{color:#625fffb3}@supports (color:color-mix(in lab,red,red)){.text-indigo-500\/70{color:color-mix(in oklab,var(--color-indigo-500) 70%,transparent)}}.text-indigo-600{color:var(--color-indigo-600)}.text-orange-600{color:var(--color-orange-600)}.text-red-600{color:var(--color-red-600)}.text-stone-400{color:var(--color-stone-400)}.text-stone-500{color:var(--color-stone-500)}.text-stone-600{color:var(--color-stone-600)}.text-stone-700{color:var(--color-stone-700)}.text-stone-800{color:var(--color-stone-800)}.text-stone-900{color:var(--color-stone-900)}.text-white{color:var(--color-white)}.uppercase{text-transform:uppercase}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.opacity-0{opacity:0}.opacity-25{opacity:.25}.opacity-75{opacity:.75}.shadow-\[0_1px_3px_rgba\(0\,0\,0\,0\.1\)\]{--tw-shadow:0 1px 3px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-\[0_1px_3px_rgba\(120\,113\,108\,0\.06\)\]{--tw-shadow:0 1px 3px var(--tw-shadow-color,#78716c0f);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-\[0_8px_30px_rgba\(120\,113\,108\,0\.12\)\]{--tw-shadow:0 8px 30px var(--tw-shadow-color,#78716c1f);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-\[0_8px_30px_rgba\(120\,113\,108\,0\.15\)\]{--tw-shadow:0 8px 30px var(--tw-shadow-color,#78716c26);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-inner{--tw-shadow:inset 0 2px 4px 0 var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-stone-900\/5{--tw-ring-color:#1c19170d}@supports (color:color-mix(in lab,red,red)){.ring-stone-900\/5{--tw-ring-color:color-mix(in oklab, var(--color-stone-900) 5%, transparent)}}.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,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-shadow{transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-200{--tw-duration:.2s;transition-duration:.2s}.outline-none{--tw-outline-style:none;outline-style:none}@media(hover:hover){.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}.hover\:z-10:hover{z-index:10}.hover\:bg-indigo-50:hover{background-color:var(--color-indigo-50)}.hover\:bg-stone-50:hover{background-color:var(--color-stone-50)}.hover\:bg-stone-50\/60:hover{background-color:#fafaf999}@supports (color:color-mix(in lab,red,red)){.hover\:bg-stone-50\/60:hover{background-color:color-mix(in oklab,var(--color-stone-50) 60%,transparent)}}.hover\:bg-stone-200\/50:hover{background-color:#e7e5e480}@supports (color:color-mix(in lab,red,red)){.hover\:bg-stone-200\/50:hover{background-color:color-mix(in oklab,var(--color-stone-200) 50%,transparent)}}.hover\:text-indigo-600:hover{color:var(--color-indigo-600)}.hover\:text-stone-800:hover{color:var(--color-stone-800)}.hover\:shadow-\[0_4px_12px_rgba\(120\,113\,108\,0\.09\)\]:hover{--tw-shadow:0 4px 12px var(--tw-shadow-color,#78716c17);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:ring-2:hover{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:ring-emerald-400:hover{--tw-ring-color:var(--color-emerald-400)}.hover\:ring-offset-1:hover{--tw-ring-offset-width:1px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}}.focus\:border-indigo-500:focus{border-color:var(--color-indigo-500)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-indigo-500\/20:focus{--tw-ring-color:#625fff33}@supports (color:color-mix(in lab,red,red)){.focus\:ring-indigo-500\/20:focus{--tw-ring-color:color-mix(in oklab, var(--color-indigo-500) 20%, transparent)}}@media(min-width:40rem){.sm\:block{display:block}}@media(min-width:48rem){.md\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.md\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:items-center{align-items:center}}@media(min-width:64rem){.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}}html,body{background-color:#faf9f7!important}@font-face{font-family:Geist;src:url(https://cdn.jsdelivr.net/npm/geist@1.3.1/dist/fonts/geist-sans/Geist-Variable.woff2)format("woff2");font-weight:100 900;font-display:swap;font-style:normal}@font-face{font-family:Geist Mono;src:url(https://cdn.jsdelivr.net/npm/geist@1.3.1/dist/fonts/geist-mono/GeistMono-Variable.woff2)format("woff2");font-weight:100 900;font-display:swap;font-style:normal}body:before{content:"";z-index:9999;pointer-events:none;opacity:.015;background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E");background-repeat:repeat;background-size:200px 200px;position:fixed;top:0;right:0;bottom:0;left:0}.recharts-cartesian-grid-horizontal line,.recharts-cartesian-grid-vertical line{stroke:#e7e5e4}.recharts-text{fill:#78716c;font-family:var(--font-sans);font-size:11px}.recharts-legend-item-text{color:#57534e;font-size:12px}.recharts-tooltip-wrapper{outline:none}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background:#c8c4be;border-radius:3px}::-webkit-scrollbar-thumb:hover{background:#a8a29e}@keyframes shimmer{0%{background-position:-200% 0}to{background-position:200% 0}}.skeleton{background:linear-gradient(90deg,#e7e5e4 25%,#d6d3d1,#e7e5e4 75%) 0 0/200% 100%;animation:1.5s ease-in-out infinite shimmer}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}