@cryptiklemur/lattice 1.4.0 → 1.5.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.
package/bun.lock CHANGED
@@ -34,6 +34,7 @@
34
34
  "react": "^19",
35
35
  "react-dom": "^19",
36
36
  "react-markdown": "^10.1.0",
37
+ "recharts": "2.15.3",
37
38
  "remark-gfm": "^4.0.1",
38
39
  "shiki": "^4.0.2",
39
40
  "tailwindcss": "^4.2.2",
@@ -489,6 +490,24 @@
489
490
 
490
491
  "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
491
492
 
493
+ "@types/d3-array": ["@types/d3-array@3.2.2", "", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="],
494
+
495
+ "@types/d3-color": ["@types/d3-color@3.1.3", "", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="],
496
+
497
+ "@types/d3-ease": ["@types/d3-ease@3.0.2", "", {}, "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="],
498
+
499
+ "@types/d3-interpolate": ["@types/d3-interpolate@3.0.4", "", { "dependencies": { "@types/d3-color": "*" } }, "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA=="],
500
+
501
+ "@types/d3-path": ["@types/d3-path@3.1.1", "", {}, "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="],
502
+
503
+ "@types/d3-scale": ["@types/d3-scale@4.0.9", "", { "dependencies": { "@types/d3-time": "*" } }, "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw=="],
504
+
505
+ "@types/d3-shape": ["@types/d3-shape@3.1.8", "", { "dependencies": { "@types/d3-path": "*" } }, "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w=="],
506
+
507
+ "@types/d3-time": ["@types/d3-time@3.0.4", "", {}, "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="],
508
+
509
+ "@types/d3-timer": ["@types/d3-timer@3.0.2", "", {}, "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="],
510
+
492
511
  "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
493
512
 
494
513
  "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
@@ -627,6 +646,8 @@
627
646
 
628
647
  "cliui": ["cliui@9.0.1", "", { "dependencies": { "string-width": "^7.2.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w=="],
629
648
 
649
+ "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
650
+
630
651
  "color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],
631
652
 
632
653
  "color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
@@ -671,6 +692,28 @@
671
692
 
672
693
  "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
673
694
 
695
+ "d3-array": ["d3-array@3.2.4", "", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="],
696
+
697
+ "d3-color": ["d3-color@3.1.0", "", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="],
698
+
699
+ "d3-ease": ["d3-ease@3.0.1", "", {}, "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="],
700
+
701
+ "d3-format": ["d3-format@3.1.2", "", {}, "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg=="],
702
+
703
+ "d3-interpolate": ["d3-interpolate@3.0.1", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="],
704
+
705
+ "d3-path": ["d3-path@3.1.0", "", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="],
706
+
707
+ "d3-scale": ["d3-scale@4.0.2", "", { "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", "d3-interpolate": "1.2.0 - 3", "d3-time": "2.1.1 - 3", "d3-time-format": "2 - 4" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="],
708
+
709
+ "d3-shape": ["d3-shape@3.2.0", "", { "dependencies": { "d3-path": "^3.1.0" } }, "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA=="],
710
+
711
+ "d3-time": ["d3-time@3.1.0", "", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="],
712
+
713
+ "d3-time-format": ["d3-time-format@4.1.0", "", { "dependencies": { "d3-time": "1 - 3" } }, "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg=="],
714
+
715
+ "d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="],
716
+
674
717
  "daisyui": ["daisyui@5.5.19", "", {}, "sha512-pbFAkl1VCEh/MPCeclKL61I/MqRIFFhNU7yiXoDDRapXN4/qNCoMxeCCswyxEEhqL5eiTTfwHvucFtOE71C9sA=="],
675
718
 
676
719
  "data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="],
@@ -683,6 +726,8 @@
683
726
 
684
727
  "decamelize": ["decamelize@1.2.0", "", {}, "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="],
685
728
 
729
+ "decimal.js-light": ["decimal.js-light@2.5.1", "", {}, "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="],
730
+
686
731
  "decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="],
687
732
 
688
733
  "deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="],
@@ -705,6 +750,8 @@
705
750
 
706
751
  "dns-packet": ["dns-packet@5.6.1", "", { "dependencies": { "@leichtgewicht/ip-codec": "^2.0.1" } }, "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw=="],
707
752
 
753
+ "dom-helpers": ["dom-helpers@5.2.1", "", { "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA=="],
754
+
708
755
  "dot-prop": ["dot-prop@5.3.0", "", { "dependencies": { "is-obj": "^2.0.0" } }, "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q=="],
709
756
 
710
757
  "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
@@ -751,6 +798,8 @@
751
798
 
752
799
  "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
753
800
 
801
+ "eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="],
802
+
754
803
  "execa": ["execa@9.6.1", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.6", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.1", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.2.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.1.1" } }, "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA=="],
755
804
 
756
805
  "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
@@ -759,6 +808,8 @@
759
808
 
760
809
  "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
761
810
 
811
+ "fast-equals": ["fast-equals@5.4.0", "", {}, "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw=="],
812
+
762
813
  "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
763
814
 
764
815
  "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="],
@@ -881,6 +932,8 @@
881
932
 
882
933
  "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="],
883
934
 
935
+ "internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="],
936
+
884
937
  "into-stream": ["into-stream@7.0.0", "", { "dependencies": { "from2": "^2.3.0", "p-is-promise": "^3.0.0" } }, "sha512-2dYz766i9HprMBasCMvHMuazJ7u4WzhJwo5kb3iPSiW/iRYV6uPari3zHoqZlnuaR7V1bEiNMxikhp37rdBXbw=="],
885
938
 
886
939
  "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="],
@@ -1045,6 +1098,8 @@
1045
1098
 
1046
1099
  "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="],
1047
1100
 
1101
+ "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
1102
+
1048
1103
  "lru-cache": ["lru-cache@11.2.7", "", {}, "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA=="],
1049
1104
 
1050
1105
  "lucide-react": ["lucide-react@0.577.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-4LjoFv2eEPwYDPg/CUdBJQSDfPyzXCRrVW1X7jrx/trgxnxkHFjnVZINbzvzxjN70dxychOfg+FTYwBiS3pQ5A=="],
@@ -1273,6 +1328,8 @@
1273
1328
 
1274
1329
  "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="],
1275
1330
 
1331
+ "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
1332
+
1276
1333
  "property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="],
1277
1334
 
1278
1335
  "proto-list": ["proto-list@1.2.4", "", {}, "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA=="],
@@ -1289,14 +1346,24 @@
1289
1346
 
1290
1347
  "react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="],
1291
1348
 
1349
+ "react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
1350
+
1292
1351
  "react-markdown": ["react-markdown@10.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "html-url-attributes": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" }, "peerDependencies": { "@types/react": ">=18", "react": ">=18" } }, "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ=="],
1293
1352
 
1353
+ "react-smooth": ["react-smooth@4.0.4", "", { "dependencies": { "fast-equals": "^5.0.1", "prop-types": "^15.8.1", "react-transition-group": "^4.4.5" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q=="],
1354
+
1355
+ "react-transition-group": ["react-transition-group@4.4.5", "", { "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", "loose-envify": "^1.4.0", "prop-types": "^15.6.2" }, "peerDependencies": { "react": ">=16.6.0", "react-dom": ">=16.6.0" } }, "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g=="],
1356
+
1294
1357
  "read-package-up": ["read-package-up@11.0.0", "", { "dependencies": { "find-up-simple": "^1.0.0", "read-pkg": "^9.0.0", "type-fest": "^4.6.0" } }, "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ=="],
1295
1358
 
1296
1359
  "read-pkg": ["read-pkg@10.1.0", "", { "dependencies": { "@types/normalize-package-data": "^2.4.4", "normalize-package-data": "^8.0.0", "parse-json": "^8.3.0", "type-fest": "^5.4.4", "unicorn-magic": "^0.4.0" } }, "sha512-I8g2lArQiP78ll51UeMZojewtYgIRCKCWqZEgOO8c/uefTI+XDXvCSXu3+YNUaTNvZzobrL5+SqHjBrByRRTdg=="],
1297
1360
 
1298
1361
  "readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
1299
1362
 
1363
+ "recharts": ["recharts@2.15.3", "", { "dependencies": { "clsx": "^2.0.0", "eventemitter3": "^4.0.1", "lodash": "^4.17.21", "react-is": "^18.3.1", "react-smooth": "^4.0.4", "recharts-scale": "^0.4.4", "tiny-invariant": "^1.3.1", "victory-vendor": "^36.6.8" }, "peerDependencies": { "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-EdOPzTwcFSuqtvkDoaM5ws/Km1+WTAO2eizL7rqiG0V2UVhTnz0m7J2i0CjVPUCdEkZImaWvXLbZDS2H5t6GFQ=="],
1364
+
1365
+ "recharts-scale": ["recharts-scale@0.4.5", "", { "dependencies": { "decimal.js-light": "^2.4.1" } }, "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w=="],
1366
+
1300
1367
  "reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="],
1301
1368
 
1302
1369
  "regenerate": ["regenerate@1.4.2", "", {}, "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A=="],
@@ -1565,6 +1632,8 @@
1565
1632
 
1566
1633
  "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="],
1567
1634
 
1635
+ "victory-vendor": ["victory-vendor@36.9.2", "", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ=="],
1636
+
1568
1637
  "vite": ["vite@8.0.1", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.3", "postcss": "^8.5.8", "rolldown": "1.0.0-rc.10", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", "esbuild": "^0.27.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-wt+Z2qIhfFt85uiyRt5LPU4oVEJBXj8hZNWKeqFG4gRG/0RaRGJ7njQCwzFVjO+v4+Ipmf5CY7VdmZRAYYBPHw=="],
1569
1638
 
1570
1639
  "vite-plugin-pwa": ["vite-plugin-pwa@1.2.0", "", { "dependencies": { "debug": "^4.3.6", "pretty-bytes": "^6.1.1", "tinyglobby": "^0.2.10", "workbox-build": "^7.4.0", "workbox-window": "^7.4.0" }, "peerDependencies": { "@vite-pwa/assets-generator": "^1.0.0", "vite": "^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" }, "optionalPeers": ["@vite-pwa/assets-generator"] }, "sha512-a2xld+SJshT9Lgcv8Ji4+srFJL4k/1bVbd1x06JIkvecpQkwkvCncD1+gSzcdm3s+owWLpMJerG3aN5jupJEVw=="],
@@ -1999,6 +2068,8 @@
1999
2068
 
2000
2069
  "pkg-conf/find-up": ["find-up@2.1.0", "", { "dependencies": { "locate-path": "^2.0.0" } }, "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ=="],
2001
2070
 
2071
+ "prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
2072
+
2002
2073
  "qrcode/yargs": ["yargs@15.4.1", "", { "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", "find-up": "^4.1.0", "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", "yargs-parser": "^18.1.2" } }, "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A=="],
2003
2074
 
2004
2075
  "read-package-up/read-pkg": ["read-pkg@9.0.1", "", { "dependencies": { "@types/normalize-package-data": "^2.4.3", "normalize-package-data": "^6.0.0", "parse-json": "^8.0.0", "type-fest": "^4.6.0", "unicorn-magic": "^0.1.0" } }, "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA=="],
@@ -27,6 +27,7 @@
27
27
  "react": "^19",
28
28
  "react-dom": "^19",
29
29
  "react-markdown": "^10.1.0",
30
+ "recharts": "2.15.3",
30
31
  "remark-gfm": "^4.0.1",
31
32
  "shiki": "^4.0.2",
32
33
  "tailwindcss": "^4.2.2"
@@ -0,0 +1,61 @@
1
+ import { useAnalytics } from "../../hooks/useAnalytics";
2
+ import { PeriodSelector } from "./PeriodSelector";
3
+ import { ChartCard } from "./ChartCard";
4
+ import { CostAreaChart } from "./charts/CostAreaChart";
5
+ import { CumulativeCostChart } from "./charts/CumulativeCostChart";
6
+ import { CostDonutChart } from "./charts/CostDonutChart";
7
+ import { CostDistributionChart } from "./charts/CostDistributionChart";
8
+ import { SessionBubbleChart } from "./charts/SessionBubbleChart";
9
+
10
+ export function AnalyticsView() {
11
+ var analytics = useAnalytics();
12
+
13
+ return (
14
+ <div className="flex flex-col h-full overflow-hidden bg-base-100 bg-lattice-grid">
15
+ <div className="flex items-center justify-between px-6 py-4 border-b border-base-300 flex-shrink-0">
16
+ <h1 className="text-[16px] font-mono font-bold text-base-content">Analytics</h1>
17
+ <PeriodSelector value={analytics.period} onChange={analytics.setPeriod} />
18
+ </div>
19
+
20
+ <div className="flex-1 overflow-y-auto px-6 py-4">
21
+ {analytics.loading && !analytics.data && (
22
+ <div className="text-center text-base-content/30 py-20 font-mono text-[13px]">Loading analytics...</div>
23
+ )}
24
+
25
+ {analytics.error && (
26
+ <div className="text-center text-error/60 py-20 font-mono text-[13px]">{analytics.error}</div>
27
+ )}
28
+
29
+ {analytics.data && (
30
+ <div className="flex flex-col gap-4 max-w-[1200px] mx-auto pb-8">
31
+ <ChartCard title="Cost Over Time">
32
+ <CostAreaChart data={analytics.data.costOverTime} />
33
+ </ChartCard>
34
+
35
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
36
+ <ChartCard title="Cost Breakdown">
37
+ <CostDonutChart modelUsage={analytics.data.modelUsage} totalCost={analytics.data.totalCost} />
38
+ </ChartCard>
39
+ <ChartCard title="Cumulative Cost">
40
+ <CumulativeCostChart data={analytics.data.cumulativeCost} />
41
+ </ChartCard>
42
+ </div>
43
+
44
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
45
+ <ChartCard title="Cost Distribution">
46
+ <CostDistributionChart data={analytics.data.costDistribution} />
47
+ </ChartCard>
48
+ <ChartCard title="Session Costs">
49
+ <SessionBubbleChart data={analytics.data.sessionBubbles} />
50
+ </ChartCard>
51
+ </div>
52
+ </div>
53
+ )}
54
+
55
+ {!analytics.loading && !analytics.error && !analytics.data && (
56
+ <div className="text-center text-base-content/30 py-20 font-mono text-[13px]">No analytics data yet</div>
57
+ )}
58
+ </div>
59
+ </div>
60
+ );
61
+ }
@@ -0,0 +1,22 @@
1
+ import type { ReactNode } from "react";
2
+
3
+ interface ChartCardProps {
4
+ title: string;
5
+ children: ReactNode;
6
+ className?: string;
7
+ action?: ReactNode;
8
+ }
9
+
10
+ export function ChartCard({ title, children, className, action }: ChartCardProps) {
11
+ return (
12
+ <div className={["rounded-xl border border-base-content/8 bg-base-300/50 p-4", className].filter(Boolean).join(" ")}>
13
+ <div className="flex items-center justify-between mb-4">
14
+ <span className="text-[10px] font-mono font-bold uppercase tracking-widest text-base-content/35">
15
+ {title}
16
+ </span>
17
+ {action && <div>{action}</div>}
18
+ </div>
19
+ {children}
20
+ </div>
21
+ );
22
+ }
@@ -0,0 +1,42 @@
1
+ type Period = "24h" | "7d" | "30d" | "90d" | "all";
2
+
3
+ var PERIODS: Array<{ value: Period; label: string }> = [
4
+ { value: "24h", label: "24h" },
5
+ { value: "7d", label: "7d" },
6
+ { value: "30d", label: "30d" },
7
+ { value: "90d", label: "90d" },
8
+ { value: "all", label: "All" },
9
+ ];
10
+
11
+ interface PeriodSelectorProps {
12
+ value: Period;
13
+ onChange: (period: Period) => void;
14
+ }
15
+
16
+ export function PeriodSelector({ value, onChange }: PeriodSelectorProps) {
17
+ return (
18
+ <div role="radiogroup" aria-label="Time period" className="flex items-center gap-1">
19
+ {PERIODS.map(function (period) {
20
+ var isActive = period.value === value;
21
+ return (
22
+ <button
23
+ key={period.value}
24
+ role="radio"
25
+ aria-checked={isActive}
26
+ onClick={function () { onChange(period.value); }}
27
+ className={[
28
+ "px-2.5 py-1 rounded-md border text-[10px] font-mono font-bold uppercase tracking-widest transition-colors",
29
+ isActive
30
+ ? "bg-primary/15 text-primary border-primary/30"
31
+ : "text-base-content/35 border-base-content/8 hover:text-base-content/60 hover:border-base-content/20",
32
+ ].join(" ")}
33
+ >
34
+ {period.label}
35
+ </button>
36
+ );
37
+ })}
38
+ </div>
39
+ );
40
+ }
41
+
42
+ export type { Period };
@@ -0,0 +1,99 @@
1
+ import { LineChart, Line, ResponsiveContainer } from "recharts";
2
+ import { useAnalytics } from "../../hooks/useAnalytics";
3
+
4
+ function formatTokens(n: number): string {
5
+ if (n >= 1_000_000) return (n / 1_000_000).toFixed(1) + "M";
6
+ if (n >= 1_000) return Math.round(n / 1_000) + "k";
7
+ return String(n);
8
+ }
9
+
10
+ interface SparklineProps {
11
+ data: Array<{ v: number }>;
12
+ stroke: string;
13
+ }
14
+
15
+ function Sparkline({ data, stroke }: SparklineProps) {
16
+ return (
17
+ <ResponsiveContainer width={60} height={20}>
18
+ <LineChart data={data} margin={{ top: 2, right: 2, left: 2, bottom: 2 }}>
19
+ <Line
20
+ type="monotone"
21
+ dataKey="v"
22
+ stroke={stroke}
23
+ strokeWidth={1.5}
24
+ dot={false}
25
+ isAnimationActive={false}
26
+ />
27
+ </LineChart>
28
+ </ResponsiveContainer>
29
+ );
30
+ }
31
+
32
+ export function QuickStats() {
33
+ var analytics = useAnalytics();
34
+
35
+ if (!analytics.data) {
36
+ return (
37
+ <div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
38
+ {[0, 1, 2, 3].map(function (i) {
39
+ return (
40
+ <div key={i} className="bg-base-content/[0.03] border border-base-content/8 rounded-xl p-3.5 animate-pulse">
41
+ <div className="h-2.5 w-16 bg-base-content/10 rounded mb-3" />
42
+ <div className="h-6 w-12 bg-base-content/10 rounded" />
43
+ </div>
44
+ );
45
+ })}
46
+ </div>
47
+ );
48
+ }
49
+
50
+ var d = analytics.data;
51
+
52
+ var costSparkData = d.costOverTime.slice(-7).map(function (e) { return { v: e.total }; });
53
+ var sessionsSparkData = d.sessionsOverTime.slice(-7).map(function (e) { return { v: e.count }; });
54
+ var tokensSparkData = d.tokensOverTime.slice(-7).map(function (e) { return { v: e.input + e.output }; });
55
+
56
+ var totalTokens = d.totalTokens.input + d.totalTokens.output;
57
+ var cacheHitPct = Math.round(d.cacheHitRate * 100);
58
+
59
+ return (
60
+ <div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
61
+ <div className="bg-base-content/[0.03] border border-base-content/8 rounded-xl p-3.5">
62
+ <div className="flex items-center justify-between mb-1">
63
+ <span className="text-[10px] font-mono font-semibold uppercase tracking-wider text-base-content/35">Cost</span>
64
+ {costSparkData.length > 1 && <Sparkline data={costSparkData} stroke="oklch(55% 0.25 280)" />}
65
+ </div>
66
+ <div className="text-[22px] font-mono text-base-content/85">${d.totalCost.toFixed(2)}</div>
67
+ </div>
68
+
69
+ <div className="bg-base-content/[0.03] border border-base-content/8 rounded-xl p-3.5">
70
+ <div className="flex items-center justify-between mb-1">
71
+ <span className="text-[10px] font-mono font-semibold uppercase tracking-wider text-base-content/35">Sessions</span>
72
+ {sessionsSparkData.length > 1 && <Sparkline data={sessionsSparkData} stroke="#22c55e" />}
73
+ </div>
74
+ <div className="text-[22px] font-mono text-base-content/85">{d.totalSessions}</div>
75
+ </div>
76
+
77
+ <div className="bg-base-content/[0.03] border border-base-content/8 rounded-xl p-3.5">
78
+ <div className="flex items-center justify-between mb-1">
79
+ <span className="text-[10px] font-mono font-semibold uppercase tracking-wider text-base-content/35">Tokens</span>
80
+ {tokensSparkData.length > 1 && <Sparkline data={tokensSparkData} stroke="#f59e0b" />}
81
+ </div>
82
+ <div className="text-[22px] font-mono text-base-content/85">{formatTokens(totalTokens)}</div>
83
+ </div>
84
+
85
+ <div className="bg-base-content/[0.03] border border-base-content/8 rounded-xl p-3.5">
86
+ <div className="mb-1">
87
+ <span className="text-[10px] font-mono font-semibold uppercase tracking-wider text-base-content/35">Cache Hit</span>
88
+ </div>
89
+ <div className="text-[22px] font-mono text-base-content/85 mb-2">{cacheHitPct}%</div>
90
+ <div className="w-full h-1 rounded-full bg-base-content/10 overflow-hidden">
91
+ <div
92
+ className="h-full rounded-full bg-primary transition-all duration-300"
93
+ style={{ width: cacheHitPct + "%" }}
94
+ />
95
+ </div>
96
+ </div>
97
+ </div>
98
+ );
99
+ }
@@ -0,0 +1,83 @@
1
+ import {
2
+ AreaChart,
3
+ Area,
4
+ XAxis,
5
+ YAxis,
6
+ CartesianGrid,
7
+ Tooltip,
8
+ ResponsiveContainer,
9
+ } from "recharts";
10
+
11
+ var TICK_STYLE = {
12
+ fontSize: 10,
13
+ fontFamily: "var(--font-mono)",
14
+ fill: "oklch(0.9 0.02 280 / 0.3)",
15
+ };
16
+
17
+ var GRID_COLOR = "oklch(0.9 0.02 280 / 0.06)";
18
+
19
+ interface CostAreaDatum {
20
+ date: string;
21
+ total: number;
22
+ opus: number;
23
+ sonnet: number;
24
+ haiku: number;
25
+ other: number;
26
+ }
27
+
28
+ interface CostAreaChartProps {
29
+ data: CostAreaDatum[];
30
+ }
31
+
32
+ function CustomTooltip({ active, payload, label }: { active?: boolean; payload?: Array<{ name: string; value: number; color: string }>; label?: string }) {
33
+ if (!active || !payload || payload.length === 0) return null;
34
+ return (
35
+ <div className="rounded-lg border border-base-content/8 bg-base-200 px-3 py-2 shadow-lg">
36
+ <p className="text-[10px] font-mono text-base-content/50 mb-1">{label}</p>
37
+ {payload.map(function (entry) {
38
+ return (
39
+ <div key={entry.name} className="flex items-center gap-2 text-[11px] font-mono">
40
+ <span className="inline-block w-2 h-2 rounded-full" style={{ background: entry.color }} />
41
+ <span className="text-base-content/60 capitalize">{entry.name}</span>
42
+ <span className="text-base-content ml-auto pl-4">${entry.value.toFixed(4)}</span>
43
+ </div>
44
+ );
45
+ })}
46
+ </div>
47
+ );
48
+ }
49
+
50
+ export function CostAreaChart({ data }: CostAreaChartProps) {
51
+ return (
52
+ <ResponsiveContainer width="100%" height={200}>
53
+ <AreaChart data={data} margin={{ top: 4, right: 4, left: -20, bottom: 0 }}>
54
+ <defs>
55
+ <linearGradient id="opusGrad" x1="0" y1="0" x2="0" y2="1">
56
+ <stop offset="5%" stopColor="#a855f7" stopOpacity={0.4} />
57
+ <stop offset="95%" stopColor="#a855f7" stopOpacity={0.05} />
58
+ </linearGradient>
59
+ <linearGradient id="sonnetGrad" x1="0" y1="0" x2="0" y2="1">
60
+ <stop offset="5%" stopColor="oklch(55% 0.25 280)" stopOpacity={0.4} />
61
+ <stop offset="95%" stopColor="oklch(55% 0.25 280)" stopOpacity={0.05} />
62
+ </linearGradient>
63
+ <linearGradient id="haikuGrad" x1="0" y1="0" x2="0" y2="1">
64
+ <stop offset="5%" stopColor="#22c55e" stopOpacity={0.4} />
65
+ <stop offset="95%" stopColor="#22c55e" stopOpacity={0.05} />
66
+ </linearGradient>
67
+ <linearGradient id="otherGrad" x1="0" y1="0" x2="0" y2="1">
68
+ <stop offset="5%" stopColor="#f59e0b" stopOpacity={0.4} />
69
+ <stop offset="95%" stopColor="#f59e0b" stopOpacity={0.05} />
70
+ </linearGradient>
71
+ </defs>
72
+ <CartesianGrid strokeDasharray="3 3" stroke={GRID_COLOR} vertical={false} />
73
+ <XAxis dataKey="date" tick={TICK_STYLE} axisLine={false} tickLine={false} />
74
+ <YAxis tick={TICK_STYLE} axisLine={false} tickLine={false} tickFormatter={function (v) { return "$" + v.toFixed(2); }} />
75
+ <Tooltip content={<CustomTooltip />} />
76
+ <Area type="monotone" dataKey="opus" stackId="1" stroke="#a855f7" fill="url(#opusGrad)" strokeWidth={1.5} />
77
+ <Area type="monotone" dataKey="sonnet" stackId="1" stroke="oklch(55% 0.25 280)" fill="url(#sonnetGrad)" strokeWidth={1.5} />
78
+ <Area type="monotone" dataKey="haiku" stackId="1" stroke="#22c55e" fill="url(#haikuGrad)" strokeWidth={1.5} />
79
+ <Area type="monotone" dataKey="other" stackId="1" stroke="#f59e0b" fill="url(#otherGrad)" strokeWidth={1.5} />
80
+ </AreaChart>
81
+ </ResponsiveContainer>
82
+ );
83
+ }
@@ -0,0 +1,62 @@
1
+ import {
2
+ AreaChart,
3
+ Area,
4
+ XAxis,
5
+ YAxis,
6
+ CartesianGrid,
7
+ Tooltip,
8
+ ResponsiveContainer,
9
+ } from "recharts";
10
+
11
+ var TICK_STYLE = {
12
+ fontSize: 10,
13
+ fontFamily: "var(--font-mono)",
14
+ fill: "oklch(0.9 0.02 280 / 0.3)",
15
+ };
16
+
17
+ var GRID_COLOR = "oklch(0.9 0.02 280 / 0.06)";
18
+
19
+ interface DistributionDatum {
20
+ bucket: string;
21
+ count: number;
22
+ }
23
+
24
+ interface CostDistributionChartProps {
25
+ data: DistributionDatum[];
26
+ }
27
+
28
+ function CustomTooltip({ active, payload, label }: { active?: boolean; payload?: Array<{ value: number }>; label?: string }) {
29
+ if (!active || !payload || payload.length === 0) return null;
30
+ return (
31
+ <div className="rounded-lg border border-base-content/8 bg-base-200 px-3 py-2 shadow-lg">
32
+ <p className="text-[10px] font-mono text-base-content/50 mb-1">{label}</p>
33
+ <p className="text-[11px] font-mono text-base-content">{payload[0].value} sessions</p>
34
+ </div>
35
+ );
36
+ }
37
+
38
+ export function CostDistributionChart({ data }: CostDistributionChartProps) {
39
+ return (
40
+ <ResponsiveContainer width="100%" height={200}>
41
+ <AreaChart data={data} margin={{ top: 4, right: 4, left: -20, bottom: 0 }}>
42
+ <defs>
43
+ <linearGradient id="distGrad" x1="0" y1="0" x2="0" y2="1">
44
+ <stop offset="5%" stopColor="oklch(55% 0.25 280)" stopOpacity={0.35} />
45
+ <stop offset="95%" stopColor="oklch(55% 0.25 280)" stopOpacity={0.02} />
46
+ </linearGradient>
47
+ </defs>
48
+ <CartesianGrid strokeDasharray="3 3" stroke={GRID_COLOR} vertical={false} />
49
+ <XAxis dataKey="bucket" tick={TICK_STYLE} axisLine={false} tickLine={false} />
50
+ <YAxis tick={TICK_STYLE} axisLine={false} tickLine={false} allowDecimals={false} />
51
+ <Tooltip content={<CustomTooltip />} />
52
+ <Area
53
+ type="monotone"
54
+ dataKey="count"
55
+ stroke="oklch(55% 0.25 280)"
56
+ fill="url(#distGrad)"
57
+ strokeWidth={2}
58
+ />
59
+ </AreaChart>
60
+ </ResponsiveContainer>
61
+ );
62
+ }