From 63cc18e788e4c8e0a9d27b59439fcf0e5820c3d2 Mon Sep 17 00:00:00 2001 From: Armen Rohalov Date: Fri, 17 Apr 2026 23:33:00 +0300 Subject: [PATCH] Enhance annotations: save/download, fallback classes, photo mode icons Add local annotation save fallback, PNG+txt download with drawn boxes, shared classColors helper, photo mode icon toggles, and react-dropzone / react-icons dependencies. --- .gitignore | 3 +- dist/assets/index--amdfC0Y.css | 1 - dist/assets/index-B-KLvAXK.js | 164 +++++++++++++++ dist/assets/index-Du68yxJU.css | 1 + dist/assets/index-o2ENlayJ.js | 63 ------ dist/index.html | 26 +-- package.json | 2 + src/components/DetectionClasses.tsx | 61 ++++-- src/features/annotations/AnnotationsPage.tsx | 199 +++++++++++++++++- .../annotations/AnnotationsSidebar.tsx | 40 ++-- src/features/annotations/CanvasEditor.tsx | 91 ++++++-- src/features/annotations/MediaList.tsx | 162 +++++++++++--- src/features/annotations/VideoPlayer.tsx | 157 +++++++++----- src/features/annotations/classColors.ts | 24 +++ tsconfig.tsbuildinfo | 6 +- 15 files changed, 782 insertions(+), 218 deletions(-) delete mode 100644 dist/assets/index--amdfC0Y.css create mode 100644 dist/assets/index-B-KLvAXK.js create mode 100644 dist/assets/index-Du68yxJU.css delete mode 100644 dist/assets/index-o2ENlayJ.js create mode 100644 src/features/annotations/classColors.ts diff --git a/.gitignore b/.gitignore index 37d292f..4058496 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .idea - +.claude +.superpowers # dependencies /node_modules /.pnp diff --git a/dist/assets/index--amdfC0Y.css b/dist/assets/index--amdfC0Y.css deleted file mode 100644 index c32a8c6..0000000 --- a/dist/assets/index--amdfC0Y.css +++ /dev/null @@ -1 +0,0 @@ -.leaflet-pane,.leaflet-tile,.leaflet-marker-icon,.leaflet-marker-shadow,.leaflet-tile-container,.leaflet-pane>svg,.leaflet-pane>canvas,.leaflet-zoom-box,.leaflet-image-layer,.leaflet-layer{position:absolute;left:0;top:0}.leaflet-container{overflow:hidden}.leaflet-tile,.leaflet-marker-icon,.leaflet-marker-shadow{-webkit-user-select:none;-moz-user-select:none;user-select:none;-webkit-user-drag:none}.leaflet-tile::selection{background:transparent}.leaflet-safari .leaflet-tile{image-rendering:-webkit-optimize-contrast}.leaflet-safari .leaflet-tile-container{width:1600px;height:1600px;-webkit-transform-origin:0 0}.leaflet-marker-icon,.leaflet-marker-shadow{display:block}.leaflet-container .leaflet-overlay-pane svg{max-width:none!important;max-height:none!important}.leaflet-container .leaflet-marker-pane img,.leaflet-container .leaflet-shadow-pane img,.leaflet-container .leaflet-tile-pane img,.leaflet-container img.leaflet-image-layer,.leaflet-container .leaflet-tile{max-width:none!important;max-height:none!important;width:auto;padding:0}.leaflet-container img.leaflet-tile{mix-blend-mode:plus-lighter}.leaflet-container.leaflet-touch-zoom{-ms-touch-action:pan-x pan-y;touch-action:pan-x pan-y}.leaflet-container.leaflet-touch-drag{-ms-touch-action:pinch-zoom;touch-action:none;touch-action:pinch-zoom}.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom{-ms-touch-action:none;touch-action:none}.leaflet-container{-webkit-tap-highlight-color:transparent}.leaflet-container a{-webkit-tap-highlight-color:rgba(51,181,229,.4)}.leaflet-tile{filter:inherit;visibility:hidden}.leaflet-tile-loaded{visibility:inherit}.leaflet-zoom-box{width:0;height:0;-moz-box-sizing:border-box;box-sizing:border-box;z-index:800}.leaflet-overlay-pane svg{-moz-user-select:none}.leaflet-pane{z-index:400}.leaflet-tile-pane{z-index:200}.leaflet-overlay-pane{z-index:400}.leaflet-shadow-pane{z-index:500}.leaflet-marker-pane{z-index:600}.leaflet-tooltip-pane{z-index:650}.leaflet-popup-pane{z-index:700}.leaflet-map-pane canvas{z-index:100}.leaflet-map-pane svg{z-index:200}.leaflet-vml-shape{width:1px;height:1px}.lvml{behavior:url(#default#VML);display:inline-block;position:absolute}.leaflet-control{position:relative;z-index:800;pointer-events:visiblePainted;pointer-events:auto}.leaflet-top,.leaflet-bottom{position:absolute;z-index:1000;pointer-events:none}.leaflet-top{top:0}.leaflet-right{right:0}.leaflet-bottom{bottom:0}.leaflet-left{left:0}.leaflet-control{float:left;clear:both}.leaflet-right .leaflet-control{float:right}.leaflet-top .leaflet-control{margin-top:10px}.leaflet-bottom .leaflet-control{margin-bottom:10px}.leaflet-left .leaflet-control{margin-left:10px}.leaflet-right .leaflet-control{margin-right:10px}.leaflet-fade-anim .leaflet-popup{opacity:0;-webkit-transition:opacity .2s linear;-moz-transition:opacity .2s linear;transition:opacity .2s linear}.leaflet-fade-anim .leaflet-map-pane .leaflet-popup{opacity:1}.leaflet-zoom-animated{-webkit-transform-origin:0 0;-ms-transform-origin:0 0;transform-origin:0 0}svg.leaflet-zoom-animated{will-change:transform}.leaflet-zoom-anim .leaflet-zoom-animated{-webkit-transition:-webkit-transform .25s cubic-bezier(0,0,.25,1);-moz-transition:-moz-transform .25s cubic-bezier(0,0,.25,1);transition:transform .25s cubic-bezier(0,0,.25,1)}.leaflet-zoom-anim .leaflet-tile,.leaflet-pan-anim .leaflet-tile{-webkit-transition:none;-moz-transition:none;transition:none}.leaflet-zoom-anim .leaflet-zoom-hide{visibility:hidden}.leaflet-interactive{cursor:pointer}.leaflet-grab{cursor:-webkit-grab;cursor:-moz-grab;cursor:grab}.leaflet-crosshair,.leaflet-crosshair .leaflet-interactive{cursor:crosshair}.leaflet-popup-pane,.leaflet-control{cursor:auto}.leaflet-dragging .leaflet-grab,.leaflet-dragging .leaflet-grab .leaflet-interactive,.leaflet-dragging .leaflet-marker-draggable{cursor:move;cursor:-webkit-grabbing;cursor:-moz-grabbing;cursor:grabbing}.leaflet-marker-icon,.leaflet-marker-shadow,.leaflet-image-layer,.leaflet-pane>svg path,.leaflet-tile-container{pointer-events:none}.leaflet-marker-icon.leaflet-interactive,.leaflet-image-layer.leaflet-interactive,.leaflet-pane>svg path.leaflet-interactive,svg.leaflet-image-layer.leaflet-interactive path{pointer-events:visiblePainted;pointer-events:auto}.leaflet-container{background:#ddd;outline-offset:1px}.leaflet-container a{color:#0078a8}.leaflet-zoom-box{border:2px dotted #38f;background:#ffffff80}.leaflet-container{font-family:Helvetica Neue,Arial,Helvetica,sans-serif;font-size:12px;font-size:.75rem;line-height:1.5}.leaflet-bar{box-shadow:0 1px 5px #000000a6;border-radius:4px}.leaflet-bar a{background-color:#fff;border-bottom:1px solid #ccc;width:26px;height:26px;line-height:26px;display:block;text-align:center;text-decoration:none;color:#000}.leaflet-bar a,.leaflet-control-layers-toggle{background-position:50% 50%;background-repeat:no-repeat;display:block}.leaflet-bar a:hover,.leaflet-bar a:focus{background-color:#f4f4f4}.leaflet-bar a:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.leaflet-bar a:last-child{border-bottom-left-radius:4px;border-bottom-right-radius:4px;border-bottom:none}.leaflet-bar a.leaflet-disabled{cursor:default;background-color:#f4f4f4;color:#bbb}.leaflet-touch .leaflet-bar a{width:30px;height:30px;line-height:30px}.leaflet-touch .leaflet-bar a:first-child{border-top-left-radius:2px;border-top-right-radius:2px}.leaflet-touch .leaflet-bar a:last-child{border-bottom-left-radius:2px;border-bottom-right-radius:2px}.leaflet-control-zoom-in,.leaflet-control-zoom-out{font:700 18px Lucida Console,Monaco,monospace;text-indent:1px}.leaflet-touch .leaflet-control-zoom-in,.leaflet-touch .leaflet-control-zoom-out{font-size:22px}.leaflet-control-layers{box-shadow:0 1px 5px #0006;background:#fff;border-radius:5px}.leaflet-control-layers-toggle{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAQAAAADQ4RFAAACf0lEQVR4AY1UM3gkARTePdvdoTxXKc+qTl3aU5U6b2Kbkz3Gtq3Zw6ziLGNPzrYx7946Tr6/ee/XeCQ4D3ykPtL5tHno4n0d/h3+xfuWHGLX81cn7r0iTNzjr7LrlxCqPtkbTQEHeqOrTy4Yyt3VCi/IOB0v7rVC7q45Q3Gr5K6jt+3Gl5nCoDD4MtO+j96Wu8atmhGqcNGHObuf8OM/x3AMx38+4Z2sPqzCxRFK2aF2e5Jol56XTLyggAMTL56XOMoS1W4pOyjUcGGQdZxU6qRh7B9Zp+PfpOFlqt0zyDZckPi1ttmIp03jX8gyJ8a/PG2yutpS/Vol7peZIbZcKBAEEheEIAgFbDkz5H6Zrkm2hVWGiXKiF4Ycw0RWKdtC16Q7qe3X4iOMxruonzegJzWaXFrU9utOSsLUmrc0YjeWYjCW4PDMADElpJSSQ0vQvA1Tm6/JlKnqFs1EGyZiFCqnRZTEJJJiKRYzVYzJck2Rm6P4iH+cmSY0YzimYa8l0EtTODFWhcMIMVqdsI2uiTvKmTisIDHJ3od5GILVhBCarCfVRmo4uTjkhrhzkiBV7SsaqS+TzrzM1qpGGUFt28pIySQHR6h7F6KSwGWm97ay+Z+ZqMcEjEWebE7wxCSQwpkhJqoZA5ivCdZDjJepuJ9IQjGGUmuXJdBFUygxVqVsxFsLMbDe8ZbDYVCGKxs+W080max1hFCarCfV+C1KATwcnvE9gRRuMP2prdbWGowm1KB1y+zwMMENkM755cJ2yPDtqhTI6ED1M/82yIDtC/4j4BijjeObflpO9I9MwXTCsSX8jWAFeHr05WoLTJ5G8IQVS/7vwR6ohirYM7f6HzYpogfS3R2OAAAAAElFTkSuQmCC);width:36px;height:36px}.leaflet-retina .leaflet-control-layers-toggle{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADQAAAA0CAQAAABvcdNgAAAEsklEQVR4AWL4TydIhpZK1kpWOlg0w3ZXP6D2soBtG42jeI6ZmQTHzAxiTbSJsYLjO9HhP+WOmcuhciVnmHVQcJnp7DFvScowZorad/+V/fVzMdMT2g9Cv9guXGv/7pYOrXh2U+RRR3dSd9JRx6bIFc/ekqHI29JC6pJ5ZEh1yWkhkbcFeSjxgx3L2m1cb1C7bceyxA+CNjT/Ifff+/kDk2u/w/33/IeCMOSaWZ4glosqT3DNnNZQ7Cs58/3Ce5HL78iZH/vKVIaYlqzfdLu8Vi7dnvUbEza5Idt36tquZFldl6N5Z/POLof0XLK61mZCmJSWjVF9tEjUluu74IUXvgttuVIHE7YxSkaYhJZam7yiM9Pv82JYfl9nptxZaxMJE4YSPty+vF0+Y2up9d3wwijfjZbabqm/3bZ9ecKHsiGmRflnn1MW4pjHf9oLufyn2z3y1D6n8g8TZhxyzipLNPnAUpsOiuWimg52psrTZYnOWYNDTMuWBWa0tJb4rgq1UvmutpaYEbZlwU3CLJm/ayYjHW5/h7xWLn9Hh1vepDkyf7dE7MtT5LR4e7yYpHrkhOUpEfssBLq2pPhAqoSWKUkk7EDqkmK6RrCEzqDjhNDWNE+XSMvkJRDWlZTmCW0l0PHQGRZY5t1L83kT0Y3l2SItk5JAWHl2dCOBm+fPu3fo5/3v61RMCO9Jx2EEYYhb0rmNQMX/vm7gqOEJLcXTGw3CAuRNeyaPWwjR8PRqKQ1PDA/dpv+on9Shox52WFnx0KY8onHayrJzm87i5h9xGw/tfkev0jGsQizqezUKjk12hBMKJ4kbCqGPVNXudyyrShovGw5CgxsRICxF6aRmSjlBnHRzg7Gx8fKqEubI2rahQYdR1YgDIRQO7JvQyD52hoIQx0mxa0ODtW2Iozn1le2iIRdzwWewedyZzewidueOGqlsn1MvcnQpuVwLGG3/IR1hIKxCjelIDZ8ldqWz25jWAsnldEnK0Zxro19TGVb2ffIZEsIO89EIEDvKMPrzmBOQcKQ+rroye6NgRRxqR4U8EAkz0CL6uSGOm6KQCdWjvjRiSP1BPalCRS5iQYiEIvxuBMJEWgzSoHADcVMuN7IuqqTeyUPq22qFimFtxDyBBJEwNyt6TM88blFHao/6tWWhuuOM4SAK4EI4QmFHA+SEyWlp4EQoJ13cYGzMu7yszEIBOm2rVmHUNqwAIQabISNMRstmdhNWcFLsSm+0tjJH1MdRxO5Nx0WDMhCtgD6OKgZeljJqJKc9po8juskR9XN0Y1lZ3mWjLR9JCO1jRDMd0fpYC2VnvjBSEFg7wBENc0R9HFlb0xvF1+TBEpF68d+DHR6IOWVv2BECtxo46hOFUBd/APU57WIoEwJhIi2CdpyZX0m93BZicktMj1AS9dClteUFAUNUIEygRZCtik5zSxI9MubTBH1GOiHsiLJ3OCoSZkILa9PxiN0EbvhsAo8tdAf9Seepd36lGWHmtNANTv5Jd0z4QYyeo/UEJqxKRpg5LZx6btLPsOaEmdMyxYdlc8LMaJnikDlhclqmPiQnTEpLUIZEwkRagjYkEibQErwhkTAKCLQEbUgkzJQWc/0PstHHcfEdQ+UAAAAASUVORK5CYII=);background-size:26px 26px}.leaflet-touch .leaflet-control-layers-toggle{width:44px;height:44px}.leaflet-control-layers .leaflet-control-layers-list,.leaflet-control-layers-expanded .leaflet-control-layers-toggle{display:none}.leaflet-control-layers-expanded .leaflet-control-layers-list{display:block;position:relative}.leaflet-control-layers-expanded{padding:6px 10px 6px 6px;color:#333;background:#fff}.leaflet-control-layers-scrollbar{overflow-y:scroll;overflow-x:hidden;padding-right:5px}.leaflet-control-layers-selector{margin-top:2px;position:relative;top:1px}.leaflet-control-layers label{display:block;font-size:13px;font-size:1.08333em}.leaflet-control-layers-separator{height:0;border-top:1px solid #ddd;margin:5px -10px 5px -6px}.leaflet-default-icon-path{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAApCAYAAADAk4LOAAAFgUlEQVR4Aa1XA5BjWRTN2oW17d3YaZtr2962HUzbDNpjszW24mRt28p47v7zq/bXZtrp/lWnXr337j3nPCe85NcypgSFdugCpW5YoDAMRaIMqRi6aKq5E3YqDQO3qAwjVWrD8Ncq/RBpykd8oZUb/kaJutow8r1aP9II0WmLKLIsJyv1w/kqw9Ch2MYdB++12Onxee/QMwvf4/Dk/Lfp/i4nxTXtOoQ4pW5Aj7wpici1A9erdAN2OH64x8OSP9j3Ft3b7aWkTg/Fm91siTra0f9on5sQr9INejH6CUUUpavjFNq1B+Oadhxmnfa8RfEmN8VNAsQhPqF55xHkMzz3jSmChWU6f7/XZKNH+9+hBLOHYozuKQPxyMPUKkrX/K0uWnfFaJGS1QPRtZsOPtr3NsW0uyh6NNCOkU3Yz+bXbT3I8G3xE5EXLXtCXbbqwCO9zPQYPRTZ5vIDXD7U+w7rFDEoUUf7ibHIR4y6bLVPXrz8JVZEql13trxwue/uDivd3fkWRbS6/IA2bID4uk0UpF1N8qLlbBlXs4Ee7HLTfV1j54APvODnSfOWBqtKVvjgLKzF5YdEk5ewRkGlK0i33Eofffc7HT56jD7/6U+qH3Cx7SBLNntH5YIPvODnyfIXZYRVDPqgHtLs5ABHD3YzLuespb7t79FY34DjMwrVrcTuwlT55YMPvOBnRrJ4VXTdNnYug5ucHLBjEpt30701A3Ts+HEa73u6dT3FNWwflY86eMHPk+Yu+i6pzUpRrW7SNDg5JHR4KapmM5Wv2E8Tfcb1HoqqHMHU+uWDD7zg54mz5/2BSnizi9T1Dg4QQXLToGNCkb6tb1NU+QAlGr1++eADrzhn/u8Q2YZhQVlZ5+CAOtqfbhmaUCS1ezNFVm2imDbPmPng5wmz+gwh+oHDce0eUtQ6OGDIyR0uUhUsoO3vfDmmgOezH0mZN59x7MBi++WDL1g/eEiU3avlidO671bkLfwbw5XV2P8Pzo0ydy4t2/0eu33xYSOMOD8hTf4CrBtGMSoXfPLchX+J0ruSePw3LZeK0juPJbYzrhkH0io7B3k164hiGvawhOKMLkrQLyVpZg8rHFW7E2uHOL888IBPlNZ1FPzstSJM694fWr6RwpvcJK60+0HCILTBzZLFNdtAzJaohze60T8qBzyh5ZuOg5e7uwQppofEmf2++DYvmySqGBuKaicF1blQjhuHdvCIMvp8whTTfZzI7RldpwtSzL+F1+wkdZ2TBOW2gIF88PBTzD/gpeREAMEbxnJcaJHNHrpzji0gQCS6hdkEeYt9DF/2qPcEC8RM28Hwmr3sdNyht00byAut2k3gufWNtgtOEOFGUwcXWNDbdNbpgBGxEvKkOQsxivJx33iow0Vw5S6SVTrpVq11ysA2Rp7gTfPfktc6zhtXBBC+adRLshf6sG2RfHPZ5EAc4sVZ83yCN00Fk/4kggu40ZTvIEm5g24qtU4KjBrx/BTTH8ifVASAG7gKrnWxJDcU7x8X6Ecczhm3o6YicvsLXWfh3Ch1W0k8x0nXF+0fFxgt4phz8QvypiwCCFKMqXCnqXExjq10beH+UUA7+nG6mdG/Pu0f3LgFcGrl2s0kNNjpmoJ9o4B29CMO8dMT4Q5ox8uitF6fqsrJOr8qnwNbRzv6hSnG5wP+64C7h9lp30hKNtKdWjtdkbuPA19nJ7Tz3zR/ibgARbhb4AlhavcBebmTHcFl2fvYEnW0ox9xMxKBS8btJ+KiEbq9zA4RthQXDhPa0T9TEe69gWupwc6uBUphquXgf+/FrIjweHQS4/pduMe5ERUMHUd9xv8ZR98CxkS4F2n3EUrUZ10EYNw7BWm9x1GiPssi3GgiGRDKWRYZfXlON+dfNbM+GgIwYdwAAAAASUVORK5CYII=)}.leaflet-container .leaflet-control-attribution{background:#fff;background:#fffc;margin:0}.leaflet-control-attribution,.leaflet-control-scale-line{padding:0 5px;color:#333;line-height:1.4}.leaflet-control-attribution a{text-decoration:none}.leaflet-control-attribution a:hover,.leaflet-control-attribution a:focus{text-decoration:underline}.leaflet-attribution-flag{display:inline!important;vertical-align:baseline!important;width:1em;height:.6669em}.leaflet-left .leaflet-control-scale{margin-left:5px}.leaflet-bottom .leaflet-control-scale{margin-bottom:5px}.leaflet-control-scale-line{border:2px solid #777;border-top:none;line-height:1.1;padding:2px 5px 1px;white-space:nowrap;-moz-box-sizing:border-box;box-sizing:border-box;background:#fffc;text-shadow:1px 1px #fff}.leaflet-control-scale-line:not(:first-child){border-top:2px solid #777;border-bottom:none;margin-top:-2px}.leaflet-control-scale-line:not(:first-child):not(:last-child){border-bottom:2px solid #777}.leaflet-touch .leaflet-control-attribution,.leaflet-touch .leaflet-control-layers,.leaflet-touch .leaflet-bar{box-shadow:none}.leaflet-touch .leaflet-control-layers,.leaflet-touch .leaflet-bar{border:2px solid rgba(0,0,0,.2);background-clip:padding-box}.leaflet-popup{position:absolute;text-align:center;margin-bottom:20px}.leaflet-popup-content-wrapper{padding:1px;text-align:left;border-radius:12px}.leaflet-popup-content{margin:13px 24px 13px 20px;line-height:1.3;font-size:13px;font-size:1.08333em;min-height:1px}.leaflet-popup-content p{margin:1.3em 0}.leaflet-popup-tip-container{width:40px;height:20px;position:absolute;left:50%;margin-top:-1px;margin-left:-20px;overflow:hidden;pointer-events:none}.leaflet-popup-tip{width:17px;height:17px;padding:1px;margin:-10px auto 0;pointer-events:auto;-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg)}.leaflet-popup-content-wrapper,.leaflet-popup-tip{background:#fff;color:#333;box-shadow:0 3px 14px #0006}.leaflet-container a.leaflet-popup-close-button{position:absolute;top:0;right:0;border:none;text-align:center;width:24px;height:24px;font:16px/24px Tahoma,Verdana,sans-serif;color:#757575;text-decoration:none;background:transparent}.leaflet-container a.leaflet-popup-close-button:hover,.leaflet-container a.leaflet-popup-close-button:focus{color:#585858}.leaflet-popup-scrolled{overflow:auto}.leaflet-oldie .leaflet-popup-content-wrapper{-ms-zoom:1}.leaflet-oldie .leaflet-popup-tip{width:24px;margin:0 auto;-ms-filter:"progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";filter:progid:DXImageTransform.Microsoft.Matrix(M11=.70710678,M12=.70710678,M21=-.70710678,M22=.70710678)}.leaflet-oldie .leaflet-control-zoom,.leaflet-oldie .leaflet-control-layers,.leaflet-oldie .leaflet-popup-content-wrapper,.leaflet-oldie .leaflet-popup-tip{border:1px solid #999}.leaflet-div-icon{background:#fff;border:1px solid #666}.leaflet-tooltip{position:absolute;padding:6px;background-color:#fff;border:1px solid #fff;border-radius:3px;color:#222;white-space:nowrap;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;pointer-events:none;box-shadow:0 1px 3px #0006}.leaflet-tooltip.leaflet-interactive{cursor:pointer;pointer-events:auto}.leaflet-tooltip-top:before,.leaflet-tooltip-bottom:before,.leaflet-tooltip-left:before,.leaflet-tooltip-right:before{position:absolute;pointer-events:none;border:6px solid transparent;background:transparent;content:""}.leaflet-tooltip-bottom{margin-top:6px}.leaflet-tooltip-top{margin-top:-6px}.leaflet-tooltip-bottom:before,.leaflet-tooltip-top:before{left:50%;margin-left:-6px}.leaflet-tooltip-top:before{bottom:0;margin-bottom:-12px;border-top-color:#fff}.leaflet-tooltip-bottom:before{top:0;margin-top:-12px;margin-left:-6px;border-bottom-color:#fff}.leaflet-tooltip-left{margin-left:-6px}.leaflet-tooltip-right{margin-left:6px}.leaflet-tooltip-left:before,.leaflet-tooltip-right:before{top:50%;margin-top:-6px}.leaflet-tooltip-left:before{right:0;margin-right:-12px;border-left-color:#fff}.leaflet-tooltip-right:before{left:0;margin-left:-12px;border-right-color:#fff}@media print{.leaflet-control{-webkit-print-color-adjust:exact;print-color-adjust:exact}}/*! tailwindcss v4.2.1 | 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-space-y-reverse:0;--tw-border-style:solid;--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}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-600:oklch(57.7% .245 27.325);--color-orange-600:oklch(64.6% .222 41.116);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-sm:24rem;--container-md:28rem;--container-2xl:42rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--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);--font-weight-semibold:600;--font-weight-bold:700;--tracking-wider:.05em;--tracking-widest:.1em;--radius-lg:.5rem;--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);--color-az-bg:#1e1e1e;--color-az-panel:#2b2b2b;--color-az-header:#343a40;--color-az-border:#495057;--color-az-muted:#6c757d;--color-az-text:#adb5bd;--color-az-orange:#fd7e14;--color-az-blue:#228be6;--color-az-red:#fa5252;--color-az-green:#40c057}}@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{.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.inset-0{inset:calc(var(--spacing) * 0)}.start{inset-inline-start:var(--spacing)}.top-full{top:100%}.right-0{right:calc(var(--spacing) * 0)}.bottom-0{bottom:calc(var(--spacing) * 0)}.left-0{left:calc(var(--spacing) * 0)}.z-50{z-index:50}.z-\[100\]{z-index:100}.container{width:100%}@media(min-width:40rem){.container{max-width:40rem}}@media(min-width:48rem){.container{max-width:48rem}}@media(min-width:64rem){.container{max-width:64rem}}@media(min-width:80rem){.container{max-width:80rem}}@media(min-width:96rem){.container{max-width:96rem}}.mx-1{margin-inline:calc(var(--spacing) * 1)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-5{margin-top:calc(var(--spacing) * 5)}.mb-0\.5{margin-bottom:calc(var(--spacing) * .5)}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.ml-2{margin-left:calc(var(--spacing) * 2)}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-block{display:inline-block}.h-1{height:calc(var(--spacing) * 1)}.h-2\.5{height:calc(var(--spacing) * 2.5)}.h-3{height:calc(var(--spacing) * 3)}.h-4{height:calc(var(--spacing) * 4)}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-10{height:calc(var(--spacing) * 10)}.h-32{height:calc(var(--spacing) * 32)}.h-full{height:100%}.h-screen{height:100vh}.max-h-48{max-height:calc(var(--spacing) * 48)}.max-h-60{max-height:calc(var(--spacing) * 60)}.max-h-80{max-height:calc(var(--spacing) * 80)}.max-h-\[50vh\]{max-height:50vh}.max-h-\[80vh\]{max-height:80vh}.w-1{width:calc(var(--spacing) * 1)}.w-2\.5{width:calc(var(--spacing) * 2.5)}.w-3{width:calc(var(--spacing) * 3)}.w-6{width:calc(var(--spacing) * 6)}.w-8{width:calc(var(--spacing) * 8)}.w-12{width:calc(var(--spacing) * 12)}.w-40{width:calc(var(--spacing) * 40)}.w-64{width:calc(var(--spacing) * 64)}.w-80{width:calc(var(--spacing) * 80)}.w-96{width:calc(var(--spacing) * 96)}.w-\[280px\]{width:280px}.w-\[300px\]{width:300px}.w-\[340px\]{width:340px}.w-\[400px\]{width:400px}.w-\[500px\]{width:500px}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.max-w-md{max-width:var(--container-md)}.max-w-sm{max-width:var(--container-sm)}.min-w-\[160px\]{min-width:160px}.flex-1{flex:1}.shrink-0{flex-shrink:0}.animate-spin{animation:var(--animate-spin)}.cursor-col-resize{cursor:col-resize}.cursor-crosshair{cursor:crosshair}.cursor-pointer{cursor:pointer}.resize{resize:both}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-around{justify-content:space-around}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.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-6{gap:calc(var(--spacing) * 6)}:where(.space-y-0\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * .5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * .5) * calc(1 - var(--tw-space-y-reverse)))}: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)))}:where(.space-y-1\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}.self-end{align-self:flex-end}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-b-2{border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-az-border{border-color:var(--color-az-border)}.border-az-orange{border-color:var(--color-az-orange)}.border-white{border-color:var(--color-white)}.border-t-transparent{border-top-color:#0000}.bg-\[\#1e1e1e\]{background-color:#1e1e1e}.bg-az-bg{background-color:var(--color-az-bg)}.bg-az-bg\/50{background-color:#1e1e1e80}@supports (color:color-mix(in lab,red,red)){.bg-az-bg\/50{background-color:color-mix(in oklab,var(--color-az-bg) 50%,transparent)}}.bg-az-blue{background-color:var(--color-az-blue)}.bg-az-blue\/20{background-color:#228be633}@supports (color:color-mix(in lab,red,red)){.bg-az-blue\/20{background-color:color-mix(in oklab,var(--color-az-blue) 20%,transparent)}}.bg-az-border{background-color:var(--color-az-border)}.bg-az-green{background-color:var(--color-az-green)}.bg-az-green\/20{background-color:#40c05733}@supports (color:color-mix(in lab,red,red)){.bg-az-green\/20{background-color:color-mix(in oklab,var(--color-az-green) 20%,transparent)}}.bg-az-header{background-color:var(--color-az-header)}.bg-az-muted\/20{background-color:#6c757d33}@supports (color:color-mix(in lab,red,red)){.bg-az-muted\/20{background-color:color-mix(in oklab,var(--color-az-muted) 20%,transparent)}}.bg-az-orange{background-color:var(--color-az-orange)}.bg-az-panel{background-color:var(--color-az-panel)}.bg-az-red{background-color:var(--color-az-red)}.bg-black{background-color:var(--color-black)}.bg-black\/60{background-color:#0009}@supports (color:color-mix(in lab,red,red)){.bg-black\/60{background-color:color-mix(in oklab,var(--color-black) 60%,transparent)}}.bg-transparent{background-color:#0000}.object-contain{object-fit:contain}.object-cover{object-fit:cover}.p-1\.5{padding:calc(var(--spacing) * 1.5)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-6{padding:calc(var(--spacing) * 6)}.px-0\.5{padding-inline:calc(var(--spacing) * .5)}.px-1{padding-inline:calc(var(--spacing) * 1)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-3{padding-inline:calc(var(--spacing) * 3)}.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-3{padding-block:calc(var(--spacing) * 3)}.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-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}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.text-\[\#adb5bd\]{color:#adb5bd}.text-az-blue{color:var(--color-az-blue)}.text-az-green{color:var(--color-az-green)}.text-az-muted{color:var(--color-az-muted)}.text-az-orange{color:var(--color-az-orange)}.text-az-red{color:var(--color-az-red)}.text-az-text{color:var(--color-az-text)}.text-white{color:var(--color-white)}.accent-az-orange{accent-color:var(--color-az-orange)}.shadow{--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)}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);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-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px 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-2{--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)}.ring-az-orange{--tw-ring-color:var(--color-az-orange)}.ring-az-red{--tw-ring-color:var(--color-az-red)}.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))}.outline-none{--tw-outline-style:none;outline-style:none}.ring-inset{--tw-ring-inset:inset}@media(hover:hover){.hover\:border-az-muted:hover{border-color:var(--color-az-muted)}.hover\:bg-az-bg:hover{background-color:var(--color-az-bg)}.hover\:bg-az-muted:hover{background-color:var(--color-az-muted)}.hover\:bg-az-orange:hover{background-color:var(--color-az-orange)}.hover\:bg-orange-600:hover{background-color:var(--color-orange-600)}.hover\:bg-red-600:hover{background-color:var(--color-red-600)}.hover\:text-az-orange:hover{color:var(--color-az-orange)}.hover\:text-az-red:hover{color:var(--color-az-red)}.hover\:text-white:hover{color:var(--color-white)}}.focus\:border-az-orange:focus{border-color:var(--color-az-orange)}.disabled\:opacity-30:disabled{opacity:.3}.disabled\:opacity-50:disabled{opacity:.5}@media(min-width:40rem){.sm\:block{display:block}.sm\:flex{display:flex}.sm\:hidden{display:none}}}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:var(--color-az-bg)}::-webkit-scrollbar-thumb{background:var(--color-az-border);border-radius:3px}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@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:"";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:"";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:"";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}@keyframes spin{to{transform:rotate(360deg)}} diff --git a/dist/assets/index-B-KLvAXK.js b/dist/assets/index-B-KLvAXK.js new file mode 100644 index 0000000..d7ed51b --- /dev/null +++ b/dist/assets/index-B-KLvAXK.js @@ -0,0 +1,164 @@ +var iM=Object.defineProperty;var aM=(t,e,n)=>e in t?iM(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var Lt=(t,e,n)=>aM(t,typeof e!="symbol"?e+"":e,n);function rM(t,e){for(var n=0;ni[a]})}}}return Object.freeze(Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}))}(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const a of document.querySelectorAll('link[rel="modulepreload"]'))i(a);new MutationObserver(a=>{for(const s of a)if(s.type==="childList")for(const l of s.addedNodes)l.tagName==="LINK"&&l.rel==="modulepreload"&&i(l)}).observe(document,{childList:!0,subtree:!0});function n(a){const s={};return a.integrity&&(s.integrity=a.integrity),a.referrerPolicy&&(s.referrerPolicy=a.referrerPolicy),a.crossOrigin==="use-credentials"?s.credentials="include":a.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function i(a){if(a.ep)return;a.ep=!0;const s=n(a);fetch(a.href,s)}})();function Sd(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var uv={exports:{}},hu={};/** + * @license React + * react-jsx-runtime.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var e1;function sM(){if(e1)return hu;e1=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.fragment");function n(i,a,s){var l=null;if(s!==void 0&&(l=""+s),a.key!==void 0&&(l=""+a.key),"key"in a){s={};for(var d in a)d!=="key"&&(s[d]=a[d])}else s=a;return a=s.ref,{$$typeof:t,type:i,key:l,ref:a!==void 0?a:null,props:s}}return hu.Fragment=e,hu.jsx=n,hu.jsxs=n,hu}var n1;function oM(){return n1||(n1=1,uv.exports=sM()),uv.exports}var T=oM(),dv={exports:{}},Qt={};/** + * @license React + * react.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var i1;function lM(){if(i1)return Qt;i1=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.portal"),n=Symbol.for("react.fragment"),i=Symbol.for("react.strict_mode"),a=Symbol.for("react.profiler"),s=Symbol.for("react.consumer"),l=Symbol.for("react.context"),d=Symbol.for("react.forward_ref"),f=Symbol.for("react.suspense"),p=Symbol.for("react.memo"),g=Symbol.for("react.lazy"),v=Symbol.for("react.activity"),y=Symbol.iterator;function _(F){return F===null||typeof F!="object"?null:(F=y&&F[y]||F["@@iterator"],typeof F=="function"?F:null)}var w={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},C=Object.assign,A={};function k(F,K,at){this.props=F,this.context=K,this.refs=A,this.updater=at||w}k.prototype.isReactComponent={},k.prototype.setState=function(F,K){if(typeof F!="object"&&typeof F!="function"&&F!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,F,K,"setState")},k.prototype.forceUpdate=function(F){this.updater.enqueueForceUpdate(this,F,"forceUpdate")};function z(){}z.prototype=k.prototype;function j(F,K,at){this.props=F,this.context=K,this.refs=A,this.updater=at||w}var P=j.prototype=new z;P.constructor=j,C(P,k.prototype),P.isPureReactComponent=!0;var H=Array.isArray;function V(){}var U={H:null,A:null,T:null,S:null},Z=Object.prototype.hasOwnProperty;function Q(F,K,at){var mt=at.ref;return{$$typeof:t,type:F,key:K,ref:mt!==void 0?mt:null,props:at}}function et(F,K){return Q(F.type,K,F.props)}function X(F){return typeof F=="object"&&F!==null&&F.$$typeof===t}function R(F){var K={"=":"=0",":":"=2"};return"$"+F.replace(/[=:]/g,function(at){return K[at]})}var dt=/\/+/g;function it(F,K){return typeof F=="object"&&F!==null&&F.key!=null?R(""+F.key):K.toString(36)}function ut(F){switch(F.status){case"fulfilled":return F.value;case"rejected":throw F.reason;default:switch(typeof F.status=="string"?F.then(V,V):(F.status="pending",F.then(function(K){F.status==="pending"&&(F.status="fulfilled",F.value=K)},function(K){F.status==="pending"&&(F.status="rejected",F.reason=K)})),F.status){case"fulfilled":return F.value;case"rejected":throw F.reason}}throw F}function I(F,K,at,mt,pt){var yt=typeof F;(yt==="undefined"||yt==="boolean")&&(F=null);var ft=!1;if(F===null)ft=!0;else switch(yt){case"bigint":case"string":case"number":ft=!0;break;case"object":switch(F.$$typeof){case t:case e:ft=!0;break;case g:return ft=F._init,I(ft(F._payload),K,at,mt,pt)}}if(ft)return pt=pt(F),ft=mt===""?"."+it(F,0):mt,H(pt)?(at="",ft!=null&&(at=ft.replace(dt,"$&/")+"/"),I(pt,K,at,"",function(Mt){return Mt})):pt!=null&&(X(pt)&&(pt=et(pt,at+(pt.key==null||F&&F.key===pt.key?"":(""+pt.key).replace(dt,"$&/")+"/")+ft)),K.push(pt)),1;ft=0;var Ht=mt===""?".":mt+":";if(H(F))for(var Tt=0;Tt>>1,ot=I[nt];if(0>>1;nta(at,q))mta(pt,at)?(I[nt]=pt,I[mt]=q,nt=mt):(I[nt]=at,I[K]=q,nt=K);else if(mta(pt,q))I[nt]=pt,I[mt]=q,nt=mt;else break t}}return $}function a(I,$){var q=I.sortIndex-$.sortIndex;return q!==0?q:I.id-$.id}if(t.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var s=performance;t.unstable_now=function(){return s.now()}}else{var l=Date,d=l.now();t.unstable_now=function(){return l.now()-d}}var f=[],p=[],g=1,v=null,y=3,_=!1,w=!1,C=!1,A=!1,k=typeof setTimeout=="function"?setTimeout:null,z=typeof clearTimeout=="function"?clearTimeout:null,j=typeof setImmediate<"u"?setImmediate:null;function P(I){for(var $=n(p);$!==null;){if($.callback===null)i(p);else if($.startTime<=I)i(p),$.sortIndex=$.expirationTime,e(f,$);else break;$=n(p)}}function H(I){if(C=!1,P(I),!w)if(n(f)!==null)w=!0,V||(V=!0,R());else{var $=n(p);$!==null&&ut(H,$.startTime-I)}}var V=!1,U=-1,Z=5,Q=-1;function et(){return A?!0:!(t.unstable_now()-QI&&et());){var nt=v.callback;if(typeof nt=="function"){v.callback=null,y=v.priorityLevel;var ot=nt(v.expirationTime<=I);if(I=t.unstable_now(),typeof ot=="function"){v.callback=ot,P(I),$=!0;break e}v===n(f)&&i(f),P(I)}else i(f);v=n(f)}if(v!==null)$=!0;else{var F=n(p);F!==null&&ut(H,F.startTime-I),$=!1}}break t}finally{v=null,y=q,_=!1}$=void 0}}finally{$?R():V=!1}}}var R;if(typeof j=="function")R=function(){j(X)};else if(typeof MessageChannel<"u"){var dt=new MessageChannel,it=dt.port2;dt.port1.onmessage=X,R=function(){it.postMessage(null)}}else R=function(){k(X,0)};function ut(I,$){U=k(function(){I(t.unstable_now())},$)}t.unstable_IdlePriority=5,t.unstable_ImmediatePriority=1,t.unstable_LowPriority=4,t.unstable_NormalPriority=3,t.unstable_Profiling=null,t.unstable_UserBlockingPriority=2,t.unstable_cancelCallback=function(I){I.callback=null},t.unstable_forceFrameRate=function(I){0>I||125nt?(I.sortIndex=q,e(p,I),n(f)===null&&I===n(p)&&(C?(z(U),U=-1):C=!0,ut(H,q-nt))):(I.sortIndex=ot,e(f,I),w||_||(w=!0,V||(V=!0,R()))),I},t.unstable_shouldYield=et,t.unstable_wrapCallback=function(I){var $=y;return function(){var q=y;y=$;try{return I.apply(this,arguments)}finally{y=q}}}})(pv)),pv}var o1;function uM(){return o1||(o1=1,hv.exports=cM()),hv.exports}var mv={exports:{}},Yn={};/** + * @license React + * react-dom.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var l1;function dM(){if(l1)return Yn;l1=1;var t=Op();function e(f){var p="https://react.dev/errors/"+f;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(t)}catch(e){console.error(e)}}return t(),mv.exports=dM(),mv.exports}/** + * @license React + * react-dom-client.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var u1;function fM(){if(u1)return pu;u1=1;var t=uM(),e=Op(),n=Qw();function i(r){var o="https://react.dev/errors/"+r;if(1ot||(r.current=nt[ot],nt[ot]=null,ot--)}function at(r,o){ot++,nt[ot]=r.current,r.current=o}var mt=F(null),pt=F(null),yt=F(null),ft=F(null);function Ht(r,o){switch(at(yt,o),at(pt,r),at(mt,null),o.nodeType){case 9:case 11:r=(r=o.documentElement)&&(r=r.namespaceURI)?C_(r):0;break;default:if(r=o.tagName,o=o.namespaceURI)o=C_(o),r=E_(o,r);else switch(r){case"svg":r=1;break;case"math":r=2;break;default:r=0}}K(mt),at(mt,r)}function Tt(){K(mt),K(pt),K(yt)}function Mt(r){r.memoizedState!==null&&at(ft,r);var o=mt.current,u=E_(o,r.type);o!==u&&(at(pt,r),at(mt,u))}function Ee(r){pt.current===r&&(K(mt),K(pt)),ft.current===r&&(K(ft),cu._currentValue=q)}var Ut,ye;function ge(r){if(Ut===void 0)try{throw Error()}catch(u){var o=u.stack.trim().match(/\n( *(at )?)/);Ut=o&&o[1]||"",ye=-1)":-1x||W[m]!==st[x]){var vt=` +`+W[m].replace(" at new "," at ");return r.displayName&&vt.includes("")&&(vt=vt.replace("",r.displayName)),vt}while(1<=m&&0<=x);break}}}finally{ve=!1,Error.prepareStackTrace=u}return(u=r?r.displayName||r.name:"")?ge(u):""}function En(r,o){switch(r.tag){case 26:case 27:case 5:return ge(r.type);case 16:return ge("Lazy");case 13:return r.child!==o&&o!==null?ge("Suspense Fallback"):ge("Suspense");case 19:return ge("SuspenseList");case 0:case 15:return Cn(r.type,!1);case 11:return Cn(r.type.render,!1);case 1:return Cn(r.type,!0);case 31:return ge("Activity");default:return""}}function nn(r){try{var o="",u=null;do o+=En(r,u),u=r,r=r.return;while(r);return o}catch(m){return` +Error generating stack: `+m.message+` +`+m.stack}}var mn=Object.prototype.hasOwnProperty,kn=t.unstable_scheduleCallback,gn=t.unstable_cancelCallback,wi=t.unstable_shouldYield,Rt=t.unstable_requestPaint,At=t.unstable_now,wt=t.unstable_getCurrentPriorityLevel,Et=t.unstable_ImmediatePriority,zt=t.unstable_UserBlockingPriority,Yt=t.unstable_NormalPriority,re=t.unstable_LowPriority,gt=t.unstable_IdlePriority,Ct=t.log,Ot=t.unstable_setDisableYieldValue,jt=null,Gt=null;function Bt(r){if(typeof Ct=="function"&&Ot(r),Gt&&typeof Gt.setStrictMode=="function")try{Gt.setStrictMode(jt,r)}catch{}}var Vt=Math.clz32?Math.clz32:Wn,ae=Math.log,An=Math.LN2;function Wn(r){return r>>>=0,r===0?32:31-(ae(r)/An|0)|0}var ra=256,Rs=262144,Ps=4194304;function Xa(r){var o=r&42;if(o!==0)return o;switch(r&-r){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return r&261888;case 262144:case 524288:case 1048576:case 2097152:return r&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return r&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return r}}function Vo(r,o,u){var m=r.pendingLanes;if(m===0)return 0;var x=0,S=r.suspendedLanes,M=r.pingedLanes;r=r.warmLanes;var B=m&134217727;return B!==0?(m=B&~S,m!==0?x=Xa(m):(M&=B,M!==0?x=Xa(M):u||(u=B&~r,u!==0&&(x=Xa(u))))):(B=m&~S,B!==0?x=Xa(B):M!==0?x=Xa(M):u||(u=m&~r,u!==0&&(x=Xa(u)))),x===0?0:o!==0&&o!==x&&(o&S)===0&&(S=x&-x,u=o&-o,S>=u||S===32&&(u&4194048)!==0)?o:x}function Ns(r,o){return(r.pendingLanes&~(r.suspendedLanes&~r.pingedLanes)&o)===0}function vm(r,o){switch(r){case 1:case 2:case 4:case 8:case 64:return o+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return o+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function zd(){var r=Ps;return Ps<<=1,(Ps&62914560)===0&&(Ps=4194304),r}function dc(r){for(var o=[],u=0;31>u;u++)o.push(r);return o}function Ka(r,o){r.pendingLanes|=o,o!==268435456&&(r.suspendedLanes=0,r.pingedLanes=0,r.warmLanes=0)}function bm(r,o,u,m,x,S){var M=r.pendingLanes;r.pendingLanes=u,r.suspendedLanes=0,r.pingedLanes=0,r.warmLanes=0,r.expiredLanes&=u,r.entangledLanes&=u,r.errorRecoveryDisabledLanes&=u,r.shellSuspendCounter=0;var B=r.entanglements,W=r.expirationTimes,st=r.hiddenUpdates;for(u=M&~u;0"u")return null;try{return r.activeElement||r.body}catch{return r.body}}var $o=/[\n"\\]/g;function Ge(r){return r.replace($o,function(o){return"\\"+o.charCodeAt(0).toString(16)+" "})}function bc(r,o,u,m,x,S,M,B){r.name="",M!=null&&typeof M!="function"&&typeof M!="symbol"&&typeof M!="boolean"?r.type=M:r.removeAttribute("type"),o!=null?M==="number"?(o===0&&r.value===""||r.value!=o)&&(r.value=""+Tn(o)):r.value!==""+Tn(o)&&(r.value=""+Tn(o)):M!=="submit"&&M!=="reset"||r.removeAttribute("value"),o!=null?oa(r,M,Tn(o)):u!=null?oa(r,M,Tn(u)):m!=null&&r.removeAttribute("value"),x==null&&S!=null&&(r.defaultChecked=!!S),x!=null&&(r.checked=x&&typeof x!="function"&&typeof x!="symbol"),B!=null&&typeof B!="function"&&typeof B!="symbol"&&typeof B!="boolean"?r.name=""+Tn(B):r.removeAttribute("name")}function Fs(r,o,u,m,x,S,M,B){if(S!=null&&typeof S!="function"&&typeof S!="symbol"&&typeof S!="boolean"&&(r.type=S),o!=null||u!=null){if(!(S!=="submit"&&S!=="reset"||o!=null)){Kt(r);return}u=u!=null?""+Tn(u):"",o=o!=null?""+Tn(o):u,B||o===r.value||(r.value=o),r.defaultValue=o}m=m??x,m=typeof m!="function"&&typeof m!="symbol"&&!!m,r.checked=B?r.checked:!!m,r.defaultChecked=!!m,M!=null&&typeof M!="function"&&typeof M!="symbol"&&typeof M!="boolean"&&(r.name=M),Kt(r)}function oa(r,o,u){o==="number"&&Pr(r.ownerDocument)===r||r.defaultValue===""+u||(r.defaultValue=""+u)}function He(r,o,u,m){if(r=r.options,o){o={};for(var x=0;x"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Vs=!1;if(Ei)try{var Ai={};Object.defineProperty(Ai,"passive",{get:function(){Vs=!0}}),window.addEventListener("test",Ai,Ai),window.removeEventListener("test",Ai,Ai)}catch{Vs=!1}var Hi=null,ir=null,an=null;function Ea(){if(an)return an;var r,o=ir,u=o.length,m,x="value"in Hi?Hi.value:Hi.textContent,S=x.length;for(r=0;r=Hr),Qo=" ",Kd=!1;function Qd(r,o){switch(r){case"keyup":return ui.indexOf(o.keyCode)!==-1;case"keydown":return o.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Oc(r){return r=r.detail,typeof r=="object"&&"data"in r?r.data:null}var Ur=!1;function Ti(r,o){switch(r){case"compositionend":return Oc(o);case"keypress":return o.which!==32?null:(Kd=!0,Qo);case"textInput":return r=o.data,r===Qo&&Kd?null:r;default:return null}}function Vr(r,o){if(Ur)return r==="compositionend"||!Xo&&Qd(r,o)?(r=Ea(),an=ir=Hi=null,Ur=!1,r):null;switch(r){case"paste":return null;case"keypress":if(!(o.ctrlKey||o.altKey||o.metaKey)||o.ctrlKey&&o.altKey){if(o.char&&1=o)return{node:u,offset:o-r};r=m}t:{for(;u;){if(u.nextSibling){u=u.nextSibling;break t}u=u.parentNode}u=void 0}u=tl(u)}}function el(r,o){return r&&o?r===o?!0:r&&r.nodeType===3?!1:o&&o.nodeType===3?el(r,o.parentNode):"contains"in r?r.contains(o):r.compareDocumentPosition?!!(r.compareDocumentPosition(o)&16):!1:!1}function Js(r){r=r!=null&&r.ownerDocument!=null&&r.ownerDocument.defaultView!=null?r.ownerDocument.defaultView:window;for(var o=Pr(r.document);o instanceof r.HTMLIFrameElement;){try{var u=typeof o.contentWindow.location.href=="string"}catch{u=!1}if(u)r=o.contentWindow;else break;o=Pr(r.document)}return o}function Ta(r){var o=r&&r.nodeName&&r.nodeName.toLowerCase();return o&&(o==="input"&&(r.type==="text"||r.type==="search"||r.type==="tel"||r.type==="url"||r.type==="password")||o==="textarea"||r.contentEditable==="true")}var nl=Ei&&"documentMode"in document&&11>=document.documentMode,Da=null,il=null,to=null,Zr=!1;function af(r,o,u){var m=u.window===u?u.document:u.nodeType===9?u:u.ownerDocument;Zr||Da==null||Da!==Pr(m)||(m=Da,"selectionStart"in m&&Ta(m)?m={start:m.selectionStart,end:m.selectionEnd}:(m=(m.ownerDocument&&m.ownerDocument.defaultView||window).getSelection(),m={anchorNode:m.anchorNode,anchorOffset:m.anchorOffset,focusNode:m.focusNode,focusOffset:m.focusOffset}),to&&Wr(to,m)||(to=m,m=$f(il,"onSelect"),0>=M,x-=M,_t=1<<32-Vt(o)+x|u<ne?(he=Nt,Nt=null):he=Nt.sibling;var we=ct(J,Nt,rt[ne],xt);if(we===null){Nt===null&&(Nt=he);break}r&&Nt&&we.alternate===null&&o(J,Nt),Y=S(we,Y,ne),Se===null?Wt=we:Se.sibling=we,Se=we,Nt=he}if(ne===rt.length)return u(J,Nt),oe&&Ft(J,ne),Wt;if(Nt===null){for(;nene?(he=Nt,Nt=null):he=Nt.sibling;var ms=ct(J,Nt,we.value,xt);if(ms===null){Nt===null&&(Nt=he);break}r&&Nt&&ms.alternate===null&&o(J,Nt),Y=S(ms,Y,ne),Se===null?Wt=ms:Se.sibling=ms,Se=ms,Nt=he}if(we.done)return u(J,Nt),oe&&Ft(J,ne),Wt;if(Nt===null){for(;!we.done;ne++,we=rt.next())we=St(J,we.value,xt),we!==null&&(Y=S(we,Y,ne),Se===null?Wt=we:Se.sibling=we,Se=we);return oe&&Ft(J,ne),Wt}for(Nt=m(Nt);!we.done;ne++,we=rt.next())we=ht(Nt,J,ne,we.value,xt),we!==null&&(r&&we.alternate!==null&&Nt.delete(we.key===null?ne:we.key),Y=S(we,Y,ne),Se===null?Wt=we:Se.sibling=we,Se=we);return r&&Nt.forEach(function(nM){return o(J,nM)}),oe&&Ft(J,ne),Wt}function Ie(J,Y,rt,xt){if(typeof rt=="object"&&rt!==null&&rt.type===C&&rt.key===null&&(rt=rt.props.children),typeof rt=="object"&&rt!==null){switch(rt.$$typeof){case _:t:{for(var Wt=rt.key;Y!==null;){if(Y.key===Wt){if(Wt=rt.type,Wt===C){if(Y.tag===7){u(J,Y.sibling),xt=x(Y,rt.props.children),xt.return=J,J=xt;break t}}else if(Y.elementType===Wt||typeof Wt=="object"&&Wt!==null&&Wt.$$typeof===Z&&fo(Wt)===Y.type){u(J,Y.sibling),xt=x(Y,rt.props),Fc(xt,rt),xt.return=J,J=xt;break t}u(J,Y);break}else o(J,Y);Y=Y.sibling}rt.type===C?(xt=sr(rt.props.children,J.mode,xt,rt.key),xt.return=J,J=xt):(xt=so(rt.type,rt.key,rt.props,null,J.mode,xt),Fc(xt,rt),xt.return=J,J=xt)}return M(J);case w:t:{for(Wt=rt.key;Y!==null;){if(Y.key===Wt)if(Y.tag===4&&Y.stateNode.containerInfo===rt.containerInfo&&Y.stateNode.implementation===rt.implementation){u(J,Y.sibling),xt=x(Y,rt.children||[]),xt.return=J,J=xt;break t}else{u(J,Y);break}else o(J,Y);Y=Y.sibling}xt=Rc(rt,J.mode,xt),xt.return=J,J=xt}return M(J);case Z:return rt=fo(rt),Ie(J,Y,rt,xt)}if(ut(rt))return Pt(J,Y,rt,xt);if(R(rt)){if(Wt=R(rt),typeof Wt!="function")throw Error(i(150));return rt=Wt.call(rt),Zt(J,Y,rt,xt)}if(typeof rt.then=="function")return Ie(J,Y,pf(rt),xt);if(rt.$$typeof===j)return Ie(J,Y,uf(J,rt),xt);mf(J,rt)}return typeof rt=="string"&&rt!==""||typeof rt=="number"||typeof rt=="bigint"?(rt=""+rt,Y!==null&&Y.tag===6?(u(J,Y.sibling),xt=x(Y,rt),xt.return=J,J=xt):(u(J,Y),xt=ol(rt,J.mode,xt),xt.return=J,J=xt),M(J)):u(J,Y)}return function(J,Y,rt,xt){try{Ic=0;var Wt=Ie(J,Y,rt,xt);return hl=null,Wt}catch(Nt){if(Nt===fl||Nt===ff)throw Nt;var Se=qn(29,Nt,null,J.mode);return Se.lanes=xt,Se.return=J,Se}finally{}}}var po=mx(!0),gx=mx(!1),Qr=!1;function Fm(r){r.updateQueue={baseState:r.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function Hm(r,o){r=r.updateQueue,o.updateQueue===r&&(o.updateQueue={baseState:r.baseState,firstBaseUpdate:r.firstBaseUpdate,lastBaseUpdate:r.lastBaseUpdate,shared:r.shared,callbacks:null})}function Jr(r){return{lane:r,tag:0,payload:null,callback:null,next:null}}function ts(r,o,u){var m=r.updateQueue;if(m===null)return null;if(m=m.shared,(Ae&2)!==0){var x=m.pending;return x===null?o.next=o:(o.next=x.next,x.next=o),m.pending=o,o=ro(r),Xr(r,null,u),o}return ka(r,m,o,u),ro(r)}function Hc(r,o,u){if(o=o.updateQueue,o!==null&&(o=o.shared,(u&4194048)!==0)){var m=o.lanes;m&=r.pendingLanes,u|=m,o.lanes=u,Pd(r,u)}}function Um(r,o){var u=r.updateQueue,m=r.alternate;if(m!==null&&(m=m.updateQueue,u===m)){var x=null,S=null;if(u=u.firstBaseUpdate,u!==null){do{var M={lane:u.lane,tag:u.tag,payload:u.payload,callback:null,next:null};S===null?x=S=M:S=S.next=M,u=u.next}while(u!==null);S===null?x=S=o:S=S.next=o}else x=S=o;u={baseState:m.baseState,firstBaseUpdate:x,lastBaseUpdate:S,shared:m.shared,callbacks:m.callbacks},r.updateQueue=u;return}r=u.lastBaseUpdate,r===null?u.firstBaseUpdate=o:r.next=o,u.lastBaseUpdate=o}var Vm=!1;function Uc(){if(Vm){var r=dl;if(r!==null)throw r}}function Vc(r,o,u,m){Vm=!1;var x=r.updateQueue;Qr=!1;var S=x.firstBaseUpdate,M=x.lastBaseUpdate,B=x.shared.pending;if(B!==null){x.shared.pending=null;var W=B,st=W.next;W.next=null,M===null?S=st:M.next=st,M=W;var vt=r.alternate;vt!==null&&(vt=vt.updateQueue,B=vt.lastBaseUpdate,B!==M&&(B===null?vt.firstBaseUpdate=st:B.next=st,vt.lastBaseUpdate=W))}if(S!==null){var St=x.baseState;M=0,vt=st=W=null,B=S;do{var ct=B.lane&-536870913,ht=ct!==B.lane;if(ht?(fe&ct)===ct:(m&ct)===ct){ct!==0&&ct===ul&&(Vm=!0),vt!==null&&(vt=vt.next={lane:0,tag:B.tag,payload:B.payload,callback:null,next:null});t:{var Pt=r,Zt=B;ct=o;var Ie=u;switch(Zt.tag){case 1:if(Pt=Zt.payload,typeof Pt=="function"){St=Pt.call(Ie,St,ct);break t}St=Pt;break t;case 3:Pt.flags=Pt.flags&-65537|128;case 0:if(Pt=Zt.payload,ct=typeof Pt=="function"?Pt.call(Ie,St,ct):Pt,ct==null)break t;St=v({},St,ct);break t;case 2:Qr=!0}}ct=B.callback,ct!==null&&(r.flags|=64,ht&&(r.flags|=8192),ht=x.callbacks,ht===null?x.callbacks=[ct]:ht.push(ct))}else ht={lane:ct,tag:B.tag,payload:B.payload,callback:B.callback,next:null},vt===null?(st=vt=ht,W=St):vt=vt.next=ht,M|=ct;if(B=B.next,B===null){if(B=x.shared.pending,B===null)break;ht=B,B=ht.next,ht.next=null,x.lastBaseUpdate=ht,x.shared.pending=null}}while(!0);vt===null&&(W=St),x.baseState=W,x.firstBaseUpdate=st,x.lastBaseUpdate=vt,S===null&&(x.shared.lanes=0),rs|=M,r.lanes=M,r.memoizedState=St}}function vx(r,o){if(typeof r!="function")throw Error(i(191,r));r.call(o)}function bx(r,o){var u=r.callbacks;if(u!==null)for(r.callbacks=null,r=0;rS?S:8;var M=I.T,B={};I.T=B,lg(r,!1,o,u);try{var W=x(),st=I.S;if(st!==null&&st(B,W),W!==null&&typeof W=="object"&&typeof W.then=="function"){var vt=GT(W,m);Wc(r,o,vt,Li(r))}else Wc(r,o,m,Li(r))}catch(St){Wc(r,o,{then:function(){},status:"rejected",reason:St},Li())}finally{$.p=S,M!==null&&B.types!==null&&(M.types=B.types),I.T=M}}function KT(){}function sg(r,o,u,m){if(r.tag!==5)throw Error(i(476));var x=Xx(r).queue;Yx(r,x,o,q,u===null?KT:function(){return Kx(r),u(m)})}function Xx(r){var o=r.memoizedState;if(o!==null)return o;o={memoizedState:q,baseState:q,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:ur,lastRenderedState:q},next:null};var u={};return o.next={memoizedState:u,baseState:u,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:ur,lastRenderedState:u},next:null},r.memoizedState=o,r=r.alternate,r!==null&&(r.memoizedState=o),o}function Kx(r){var o=Xx(r);o.next===null&&(o=r.alternate.memoizedState),Wc(r,o.next.queue,{},Li())}function og(){return Fn(cu)}function Qx(){return fn().memoizedState}function Jx(){return fn().memoizedState}function QT(r){for(var o=r.return;o!==null;){switch(o.tag){case 24:case 3:var u=Li();r=Jr(u);var m=ts(o,r,u);m!==null&&(bi(m,o,u),Hc(m,o,u)),o={cache:Nm()},r.payload=o;return}o=o.return}}function JT(r,o,u){var m=Li();u={lane:m,revertLane:0,gesture:null,action:u,hasEagerState:!1,eagerState:null,next:null},Ef(r)?e0(o,u):(u=kc(r,o,u,m),u!==null&&(bi(u,r,m),n0(u,o,m)))}function t0(r,o,u){var m=Li();Wc(r,o,u,m)}function Wc(r,o,u,m){var x={lane:m,revertLane:0,gesture:null,action:u,hasEagerState:!1,eagerState:null,next:null};if(Ef(r))e0(o,x);else{var S=r.alternate;if(r.lanes===0&&(S===null||S.lanes===0)&&(S=o.lastRenderedReducer,S!==null))try{var M=o.lastRenderedState,B=S(M,u);if(x.hasEagerState=!0,x.eagerState=B,zn(B,M))return ka(r,o,x,0),Ue===null&&ao(),!1}catch{}finally{}if(u=kc(r,o,x,m),u!==null)return bi(u,r,m),n0(u,o,m),!0}return!1}function lg(r,o,u,m){if(m={lane:2,revertLane:Fg(),gesture:null,action:m,hasEagerState:!1,eagerState:null,next:null},Ef(r)){if(o)throw Error(i(479))}else o=kc(r,u,m,2),o!==null&&bi(o,r,2)}function Ef(r){var o=r.alternate;return r===te||o!==null&&o===te}function e0(r,o){ml=bf=!0;var u=r.pending;u===null?o.next=o:(o.next=u.next,u.next=o),r.pending=o}function n0(r,o,u){if((u&4194048)!==0){var m=o.lanes;m&=r.pendingLanes,u|=m,o.lanes=u,Pd(r,u)}}var Zc={readContext:Fn,use:_f,useCallback:sn,useContext:sn,useEffect:sn,useImperativeHandle:sn,useLayoutEffect:sn,useInsertionEffect:sn,useMemo:sn,useReducer:sn,useRef:sn,useState:sn,useDebugValue:sn,useDeferredValue:sn,useTransition:sn,useSyncExternalStore:sn,useId:sn,useHostTransitionStatus:sn,useFormState:sn,useActionState:sn,useOptimistic:sn,useMemoCache:sn,useCacheRefresh:sn};Zc.useEffectEvent=sn;var i0={readContext:Fn,use:_f,useCallback:function(r,o){return ai().memoizedState=[r,o===void 0?null:o],r},useContext:Fn,useEffect:Fx,useImperativeHandle:function(r,o,u){u=u!=null?u.concat([r]):null,wf(4194308,4,$x.bind(null,o,r),u)},useLayoutEffect:function(r,o){return wf(4194308,4,r,o)},useInsertionEffect:function(r,o){wf(4,2,r,o)},useMemo:function(r,o){var u=ai();o=o===void 0?null:o;var m=r();if(mo){Bt(!0);try{r()}finally{Bt(!1)}}return u.memoizedState=[m,o],m},useReducer:function(r,o,u){var m=ai();if(u!==void 0){var x=u(o);if(mo){Bt(!0);try{u(o)}finally{Bt(!1)}}}else x=o;return m.memoizedState=m.baseState=x,r={pending:null,lanes:0,dispatch:null,lastRenderedReducer:r,lastRenderedState:x},m.queue=r,r=r.dispatch=JT.bind(null,te,r),[m.memoizedState,r]},useRef:function(r){var o=ai();return r={current:r},o.memoizedState=r},useState:function(r){r=eg(r);var o=r.queue,u=t0.bind(null,te,o);return o.dispatch=u,[r.memoizedState,u]},useDebugValue:ag,useDeferredValue:function(r,o){var u=ai();return rg(u,r,o)},useTransition:function(){var r=eg(!1);return r=Yx.bind(null,te,r.queue,!0,!1),ai().memoizedState=r,[!1,r]},useSyncExternalStore:function(r,o,u){var m=te,x=ai();if(oe){if(u===void 0)throw Error(i(407));u=u()}else{if(u=o(),Ue===null)throw Error(i(349));(fe&127)!==0||Cx(m,o,u)}x.memoizedState=u;var S={value:u,getSnapshot:o};return x.queue=S,Fx(Ax.bind(null,m,S,r),[r]),m.flags|=2048,vl(9,{destroy:void 0},Ex.bind(null,m,S,u,o),null),u},useId:function(){var r=ai(),o=Ue.identifierPrefix;if(oe){var u=kt,m=_t;u=(m&~(1<<32-Vt(m)-1)).toString(32)+u,o="_"+o+"R_"+u,u=yf++,0<\/script>",S=S.removeChild(S.firstChild);break;case"select":S=typeof m.is=="string"?M.createElement("select",{is:m.is}):M.createElement("select"),m.multiple?S.multiple=!0:m.size&&(S.size=m.size);break;default:S=typeof m.is=="string"?M.createElement(x,{is:m.is}):M.createElement(x)}}S[vn]=o,S[Ln]=m;t:for(M=o.child;M!==null;){if(M.tag===5||M.tag===6)S.appendChild(M.stateNode);else if(M.tag!==4&&M.tag!==27&&M.child!==null){M.child.return=M,M=M.child;continue}if(M===o)break t;for(;M.sibling===null;){if(M.return===null||M.return===o)break t;M=M.return}M.sibling.return=M.return,M=M.sibling}o.stateNode=S;t:switch(Un(S,x,m),x){case"button":case"input":case"select":case"textarea":m=!!m.autoFocus;break t;case"img":m=!0;break t;default:m=!1}m&&fr(o)}}return We(o),Sg(o,o.type,r===null?null:r.memoizedProps,o.pendingProps,u),null;case 6:if(r&&o.stateNode!=null)r.memoizedProps!==m&&fr(o);else{if(typeof m!="string"&&o.stateNode===null)throw Error(i(166));if(r=yt.current,fa(o)){if(r=o.stateNode,u=o.memoizedProps,m=null,x=xe,x!==null)switch(x.tag){case 27:case 5:m=x.memoizedProps}r[vn]=o,r=!!(r.nodeValue===u||m!==null&&m.suppressHydrationWarning===!0||S_(r.nodeValue,u)),r||za(o,!0)}else r=Gf(r).createTextNode(m),r[vn]=o,o.stateNode=r}return We(o),null;case 31:if(u=o.memoizedState,r===null||r.memoizedState!==null){if(m=fa(o),u!==null){if(r===null){if(!m)throw Error(i(318));if(r=o.memoizedState,r=r!==null?r.dehydrated:null,!r)throw Error(i(557));r[vn]=o}else or(),(o.flags&128)===0&&(o.memoizedState=null),o.flags|=4;We(o),r=!1}else u=Nc(),r!==null&&r.memoizedState!==null&&(r.memoizedState.hydrationErrors=u),r=!0;if(!r)return o.flags&256?(Mi(o),o):(Mi(o),null);if((o.flags&128)!==0)throw Error(i(558))}return We(o),null;case 13:if(m=o.memoizedState,r===null||r.memoizedState!==null&&r.memoizedState.dehydrated!==null){if(x=fa(o),m!==null&&m.dehydrated!==null){if(r===null){if(!x)throw Error(i(318));if(x=o.memoizedState,x=x!==null?x.dehydrated:null,!x)throw Error(i(317));x[vn]=o}else or(),(o.flags&128)===0&&(o.memoizedState=null),o.flags|=4;We(o),x=!1}else x=Nc(),r!==null&&r.memoizedState!==null&&(r.memoizedState.hydrationErrors=x),x=!0;if(!x)return o.flags&256?(Mi(o),o):(Mi(o),null)}return Mi(o),(o.flags&128)!==0?(o.lanes=u,o):(u=m!==null,r=r!==null&&r.memoizedState!==null,u&&(m=o.child,x=null,m.alternate!==null&&m.alternate.memoizedState!==null&&m.alternate.memoizedState.cachePool!==null&&(x=m.alternate.memoizedState.cachePool.pool),S=null,m.memoizedState!==null&&m.memoizedState.cachePool!==null&&(S=m.memoizedState.cachePool.pool),S!==x&&(m.flags|=2048)),u!==r&&u&&(o.child.flags|=8192),Of(o,o.updateQueue),We(o),null);case 4:return Tt(),r===null&&$g(o.stateNode.containerInfo),We(o),null;case 10:return lr(o.type),We(o),null;case 19:if(K(dn),m=o.memoizedState,m===null)return We(o),null;if(x=(o.flags&128)!==0,S=m.rendering,S===null)if(x)Yc(m,!1);else{if(on!==0||r!==null&&(r.flags&128)!==0)for(r=o.child;r!==null;){if(S=vf(r),S!==null){for(o.flags|=128,Yc(m,!1),r=S.updateQueue,o.updateQueue=r,Of(o,r),o.subtreeFlags=0,r=u,u=o.child;u!==null;)Lc(u,r),u=u.sibling;return at(dn,dn.current&1|2),oe&&Ft(o,m.treeForkCount),o.child}r=r.sibling}m.tail!==null&&At()>Pf&&(o.flags|=128,x=!0,Yc(m,!1),o.lanes=4194304)}else{if(!x)if(r=vf(S),r!==null){if(o.flags|=128,x=!0,r=r.updateQueue,o.updateQueue=r,Of(o,r),Yc(m,!0),m.tail===null&&m.tailMode==="hidden"&&!S.alternate&&!oe)return We(o),null}else 2*At()-m.renderingStartTime>Pf&&u!==536870912&&(o.flags|=128,x=!0,Yc(m,!1),o.lanes=4194304);m.isBackwards?(S.sibling=o.child,o.child=S):(r=m.last,r!==null?r.sibling=S:o.child=S,m.last=S)}return m.tail!==null?(r=m.tail,m.rendering=r,m.tail=r.sibling,m.renderingStartTime=At(),r.sibling=null,u=dn.current,at(dn,x?u&1|2:u&1),oe&&Ft(o,m.treeForkCount),r):(We(o),null);case 22:case 23:return Mi(o),Gm(),m=o.memoizedState!==null,r!==null?r.memoizedState!==null!==m&&(o.flags|=8192):m&&(o.flags|=8192),m?(u&536870912)!==0&&(o.flags&128)===0&&(We(o),o.subtreeFlags&6&&(o.flags|=8192)):We(o),u=o.updateQueue,u!==null&&Of(o,u.retryQueue),u=null,r!==null&&r.memoizedState!==null&&r.memoizedState.cachePool!==null&&(u=r.memoizedState.cachePool.pool),m=null,o.memoizedState!==null&&o.memoizedState.cachePool!==null&&(m=o.memoizedState.cachePool.pool),m!==u&&(o.flags|=2048),r!==null&&K(uo),null;case 24:return u=null,r!==null&&(u=r.memoizedState.cache),o.memoizedState.cache!==u&&(o.flags|=2048),lr(yn),We(o),null;case 25:return null;case 30:return null}throw Error(i(156,o.tag))}function aD(r,o){switch(rn(o),o.tag){case 1:return r=o.flags,r&65536?(o.flags=r&-65537|128,o):null;case 3:return lr(yn),Tt(),r=o.flags,(r&65536)!==0&&(r&128)===0?(o.flags=r&-65537|128,o):null;case 26:case 27:case 5:return Ee(o),null;case 31:if(o.memoizedState!==null){if(Mi(o),o.alternate===null)throw Error(i(340));or()}return r=o.flags,r&65536?(o.flags=r&-65537|128,o):null;case 13:if(Mi(o),r=o.memoizedState,r!==null&&r.dehydrated!==null){if(o.alternate===null)throw Error(i(340));or()}return r=o.flags,r&65536?(o.flags=r&-65537|128,o):null;case 19:return K(dn),null;case 4:return Tt(),null;case 10:return lr(o.type),null;case 22:case 23:return Mi(o),Gm(),r!==null&&K(uo),r=o.flags,r&65536?(o.flags=r&-65537|128,o):null;case 24:return lr(yn),null;case 25:return null;default:return null}}function T0(r,o){switch(rn(o),o.tag){case 3:lr(yn),Tt();break;case 26:case 27:case 5:Ee(o);break;case 4:Tt();break;case 31:o.memoizedState!==null&&Mi(o);break;case 13:Mi(o);break;case 19:K(dn);break;case 10:lr(o.type);break;case 22:case 23:Mi(o),Gm(),r!==null&&K(uo);break;case 24:lr(yn)}}function Xc(r,o){try{var u=o.updateQueue,m=u!==null?u.lastEffect:null;if(m!==null){var x=m.next;u=x;do{if((u.tag&r)===r){m=void 0;var S=u.create,M=u.inst;m=S(),M.destroy=m}u=u.next}while(u!==x)}}catch(B){Le(o,o.return,B)}}function is(r,o,u){try{var m=o.updateQueue,x=m!==null?m.lastEffect:null;if(x!==null){var S=x.next;m=S;do{if((m.tag&r)===r){var M=m.inst,B=M.destroy;if(B!==void 0){M.destroy=void 0,x=o;var W=u,st=B;try{st()}catch(vt){Le(x,W,vt)}}}m=m.next}while(m!==S)}}catch(vt){Le(o,o.return,vt)}}function D0(r){var o=r.updateQueue;if(o!==null){var u=r.stateNode;try{bx(o,u)}catch(m){Le(r,r.return,m)}}}function M0(r,o,u){u.props=go(r.type,r.memoizedProps),u.state=r.memoizedState;try{u.componentWillUnmount()}catch(m){Le(r,o,m)}}function Kc(r,o){try{var u=r.ref;if(u!==null){switch(r.tag){case 26:case 27:case 5:var m=r.stateNode;break;case 30:m=r.stateNode;break;default:m=r.stateNode}typeof u=="function"?r.refCleanup=u(m):u.current=m}}catch(x){Le(r,o,x)}}function Pa(r,o){var u=r.ref,m=r.refCleanup;if(u!==null)if(typeof m=="function")try{m()}catch(x){Le(r,o,x)}finally{r.refCleanup=null,r=r.alternate,r!=null&&(r.refCleanup=null)}else if(typeof u=="function")try{u(null)}catch(x){Le(r,o,x)}else u.current=null}function O0(r){var o=r.type,u=r.memoizedProps,m=r.stateNode;try{t:switch(o){case"button":case"input":case"select":case"textarea":u.autoFocus&&m.focus();break t;case"img":u.src?m.src=u.src:u.srcSet&&(m.srcset=u.srcSet)}}catch(x){Le(r,r.return,x)}}function wg(r,o,u){try{var m=r.stateNode;AD(m,r.type,u,o),m[Ln]=o}catch(x){Le(r,r.return,x)}}function k0(r){return r.tag===5||r.tag===3||r.tag===26||r.tag===27&&us(r.type)||r.tag===4}function Cg(r){t:for(;;){for(;r.sibling===null;){if(r.return===null||k0(r.return))return null;r=r.return}for(r.sibling.return=r.return,r=r.sibling;r.tag!==5&&r.tag!==6&&r.tag!==18;){if(r.tag===27&&us(r.type)||r.flags&2||r.child===null||r.tag===4)continue t;r.child.return=r,r=r.child}if(!(r.flags&2))return r.stateNode}}function Eg(r,o,u){var m=r.tag;if(m===5||m===6)r=r.stateNode,o?(u.nodeType===9?u.body:u.nodeName==="HTML"?u.ownerDocument.body:u).insertBefore(r,o):(o=u.nodeType===9?u.body:u.nodeName==="HTML"?u.ownerDocument.body:u,o.appendChild(r),u=u._reactRootContainer,u!=null||o.onclick!==null||(o.onclick=ci));else if(m!==4&&(m===27&&us(r.type)&&(u=r.stateNode,o=null),r=r.child,r!==null))for(Eg(r,o,u),r=r.sibling;r!==null;)Eg(r,o,u),r=r.sibling}function kf(r,o,u){var m=r.tag;if(m===5||m===6)r=r.stateNode,o?u.insertBefore(r,o):u.appendChild(r);else if(m!==4&&(m===27&&us(r.type)&&(u=r.stateNode),r=r.child,r!==null))for(kf(r,o,u),r=r.sibling;r!==null;)kf(r,o,u),r=r.sibling}function L0(r){var o=r.stateNode,u=r.memoizedProps;try{for(var m=r.type,x=o.attributes;x.length;)o.removeAttributeNode(x[0]);Un(o,m,u),o[vn]=r,o[Ln]=u}catch(S){Le(r,r.return,S)}}var hr=!1,Sn=!1,Ag=!1,z0=typeof WeakSet=="function"?WeakSet:Set,Pn=null;function rD(r,o){if(r=r.containerInfo,Zg=Qf,r=Js(r),Ta(r)){if("selectionStart"in r)var u={start:r.selectionStart,end:r.selectionEnd};else t:{u=(u=r.ownerDocument)&&u.defaultView||window;var m=u.getSelection&&u.getSelection();if(m&&m.rangeCount!==0){u=m.anchorNode;var x=m.anchorOffset,S=m.focusNode;m=m.focusOffset;try{u.nodeType,S.nodeType}catch{u=null;break t}var M=0,B=-1,W=-1,st=0,vt=0,St=r,ct=null;e:for(;;){for(var ht;St!==u||x!==0&&St.nodeType!==3||(B=M+x),St!==S||m!==0&&St.nodeType!==3||(W=M+m),St.nodeType===3&&(M+=St.nodeValue.length),(ht=St.firstChild)!==null;)ct=St,St=ht;for(;;){if(St===r)break e;if(ct===u&&++st===x&&(B=M),ct===S&&++vt===m&&(W=M),(ht=St.nextSibling)!==null)break;St=ct,ct=St.parentNode}St=ht}u=B===-1||W===-1?null:{start:B,end:W}}else u=null}u=u||{start:0,end:0}}else u=null;for(qg={focusedElem:r,selectionRange:u},Qf=!1,Pn=o;Pn!==null;)if(o=Pn,r=o.child,(o.subtreeFlags&1028)!==0&&r!==null)r.return=o,Pn=r;else for(;Pn!==null;){switch(o=Pn,S=o.alternate,r=o.flags,o.tag){case 0:if((r&4)!==0&&(r=o.updateQueue,r=r!==null?r.events:null,r!==null))for(u=0;u title"))),Un(S,m,u),S[vn]=r,bn(S),m=S;break t;case"link":var M=I_("link","href",x).get(m+(u.href||""));if(M){for(var B=0;BIe&&(M=Ie,Ie=Zt,Zt=M);var J=Qs(B,Zt),Y=Qs(B,Ie);if(J&&Y&&(ht.rangeCount!==1||ht.anchorNode!==J.node||ht.anchorOffset!==J.offset||ht.focusNode!==Y.node||ht.focusOffset!==Y.offset)){var rt=St.createRange();rt.setStart(J.node,J.offset),ht.removeAllRanges(),Zt>Ie?(ht.addRange(rt),ht.extend(Y.node,Y.offset)):(rt.setEnd(Y.node,Y.offset),ht.addRange(rt))}}}}for(St=[],ht=B;ht=ht.parentNode;)ht.nodeType===1&&St.push({element:ht,left:ht.scrollLeft,top:ht.scrollTop});for(typeof B.focus=="function"&&B.focus(),B=0;Bu?32:u,I.T=null,u=zg,zg=null;var S=os,M=br;if(Mn=0,Sl=os=null,br=0,(Ae&6)!==0)throw Error(i(331));var B=Ae;if(Ae|=4,$0(S.current),H0(S,S.current,M,u),Ae=B,iu(0,!1),Gt&&typeof Gt.onPostCommitFiberRoot=="function")try{Gt.onPostCommitFiberRoot(jt,S)}catch{}return!0}finally{$.p=x,I.T=m,l_(r,o)}}function u_(r,o,u){o=h(u,o),o=fg(r.stateNode,o,2),r=ts(r,o,2),r!==null&&(Ka(r,2),Na(r))}function Le(r,o,u){if(r.tag===3)u_(r,r,u);else for(;o!==null;){if(o.tag===3){u_(o,r,u);break}else if(o.tag===1){var m=o.stateNode;if(typeof o.type.getDerivedStateFromError=="function"||typeof m.componentDidCatch=="function"&&(ss===null||!ss.has(m))){r=h(u,r),u=d0(2),m=ts(o,u,2),m!==null&&(f0(u,m,o,r),Ka(m,2),Na(m));break}}o=o.return}}function jg(r,o,u){var m=r.pingCache;if(m===null){m=r.pingCache=new lD;var x=new Set;m.set(o,x)}else x=m.get(o),x===void 0&&(x=new Set,m.set(o,x));x.has(u)||(Mg=!0,x.add(u),r=hD.bind(null,r,o,u),o.then(r,r))}function hD(r,o,u){var m=r.pingCache;m!==null&&m.delete(o),r.pingedLanes|=r.suspendedLanes&u,r.warmLanes&=~u,Ue===r&&(fe&u)===u&&(on===4||on===3&&(fe&62914560)===fe&&300>At()-Rf?(Ae&2)===0&&wl(r,0):Og|=u,_l===fe&&(_l=0)),Na(r)}function d_(r,o){o===0&&(o=zd()),r=Wi(r,o),r!==null&&(Ka(r,o),Na(r))}function pD(r){var o=r.memoizedState,u=0;o!==null&&(u=o.retryLane),d_(r,u)}function mD(r,o){var u=0;switch(r.tag){case 31:case 13:var m=r.stateNode,x=r.memoizedState;x!==null&&(u=x.retryLane);break;case 19:m=r.stateNode;break;case 22:m=r.stateNode._retryCache;break;default:throw Error(i(314))}m!==null&&m.delete(o),d_(r,u)}function gD(r,o){return kn(r,o)}var Hf=null,El=null,Bg=!1,Uf=!1,Ig=!1,cs=0;function Na(r){r!==El&&r.next===null&&(El===null?Hf=El=r:El=El.next=r),Uf=!0,Bg||(Bg=!0,bD())}function iu(r,o){if(!Ig&&Uf){Ig=!0;do for(var u=!1,m=Hf;m!==null;){if(r!==0){var x=m.pendingLanes;if(x===0)var S=0;else{var M=m.suspendedLanes,B=m.pingedLanes;S=(1<<31-Vt(42|r)+1)-1,S&=x&~(M&~B),S=S&201326741?S&201326741|1:S?S|2:0}S!==0&&(u=!0,m_(m,S))}else S=fe,S=Vo(m,m===Ue?S:0,m.cancelPendingCommit!==null||m.timeoutHandle!==-1),(S&3)===0||Ns(m,S)||(u=!0,m_(m,S));m=m.next}while(u);Ig=!1}}function vD(){f_()}function f_(){Uf=Bg=!1;var r=0;cs!==0&&DD()&&(r=cs);for(var o=At(),u=null,m=Hf;m!==null;){var x=m.next,S=h_(m,o);S===0?(m.next=null,u===null?Hf=x:u.next=x,x===null&&(El=u)):(u=m,(r!==0||(S&3)!==0)&&(Uf=!0)),m=x}Mn!==0&&Mn!==5||iu(r),cs!==0&&(cs=0)}function h_(r,o){for(var u=r.suspendedLanes,m=r.pingedLanes,x=r.expirationTimes,S=r.pendingLanes&-62914561;0B)break;var vt=W.transferSize,St=W.initiatorType;vt&&w_(St)&&(W=W.responseEnd,M+=vt*(W"u"?null:document;function P_(r,o,u){var m=Al;if(m&&typeof o=="string"&&o){var x=Ge(o);x='link[rel="'+r+'"][href="'+x+'"]',typeof u=="string"&&(x+='[crossorigin="'+u+'"]'),R_.has(x)||(R_.add(x),r={rel:r,crossOrigin:u,href:o},m.querySelector(x)===null&&(o=m.createElement("link"),Un(o,"link",r),bn(o),m.head.appendChild(o)))}}function jD(r){yr.D(r),P_("dns-prefetch",r,null)}function BD(r,o){yr.C(r,o),P_("preconnect",r,o)}function ID(r,o,u){yr.L(r,o,u);var m=Al;if(m&&r&&o){var x='link[rel="preload"][as="'+Ge(o)+'"]';o==="image"&&u&&u.imageSrcSet?(x+='[imagesrcset="'+Ge(u.imageSrcSet)+'"]',typeof u.imageSizes=="string"&&(x+='[imagesizes="'+Ge(u.imageSizes)+'"]')):x+='[href="'+Ge(r)+'"]';var S=x;switch(o){case"style":S=Tl(r);break;case"script":S=Dl(r)}Xi.has(S)||(r=v({rel:"preload",href:o==="image"&&u&&u.imageSrcSet?void 0:r,as:o},u),Xi.set(S,r),m.querySelector(x)!==null||o==="style"&&m.querySelector(ou(S))||o==="script"&&m.querySelector(lu(S))||(o=m.createElement("link"),Un(o,"link",r),bn(o),m.head.appendChild(o)))}}function FD(r,o){yr.m(r,o);var u=Al;if(u&&r){var m=o&&typeof o.as=="string"?o.as:"script",x='link[rel="modulepreload"][as="'+Ge(m)+'"][href="'+Ge(r)+'"]',S=x;switch(m){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":S=Dl(r)}if(!Xi.has(S)&&(r=v({rel:"modulepreload",href:r},o),Xi.set(S,r),u.querySelector(x)===null)){switch(m){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(u.querySelector(lu(S)))return}m=u.createElement("link"),Un(m,"link",r),bn(m),u.head.appendChild(m)}}}function HD(r,o,u){yr.S(r,o,u);var m=Al;if(m&&r){var x=Rr(m).hoistableStyles,S=Tl(r);o=o||"default";var M=x.get(S);if(!M){var B={loading:0,preload:null};if(M=m.querySelector(ou(S)))B.loading=5;else{r=v({rel:"stylesheet",href:r,"data-precedence":o},u),(u=Xi.get(S))&&ev(r,u);var W=M=m.createElement("link");bn(W),Un(W,"link",r),W._p=new Promise(function(st,vt){W.onload=st,W.onerror=vt}),W.addEventListener("load",function(){B.loading|=1}),W.addEventListener("error",function(){B.loading|=2}),B.loading|=4,Zf(M,o,m)}M={type:"stylesheet",instance:M,count:1,state:B},x.set(S,M)}}}function UD(r,o){yr.X(r,o);var u=Al;if(u&&r){var m=Rr(u).hoistableScripts,x=Dl(r),S=m.get(x);S||(S=u.querySelector(lu(x)),S||(r=v({src:r,async:!0},o),(o=Xi.get(x))&&nv(r,o),S=u.createElement("script"),bn(S),Un(S,"link",r),u.head.appendChild(S)),S={type:"script",instance:S,count:1,state:null},m.set(x,S))}}function VD(r,o){yr.M(r,o);var u=Al;if(u&&r){var m=Rr(u).hoistableScripts,x=Dl(r),S=m.get(x);S||(S=u.querySelector(lu(x)),S||(r=v({src:r,async:!0,type:"module"},o),(o=Xi.get(x))&&nv(r,o),S=u.createElement("script"),bn(S),Un(S,"link",r),u.head.appendChild(S)),S={type:"script",instance:S,count:1,state:null},m.set(x,S))}}function N_(r,o,u,m){var x=(x=yt.current)?Wf(x):null;if(!x)throw Error(i(446));switch(r){case"meta":case"title":return null;case"style":return typeof u.precedence=="string"&&typeof u.href=="string"?(o=Tl(u.href),u=Rr(x).hoistableStyles,m=u.get(o),m||(m={type:"style",instance:null,count:0,state:null},u.set(o,m)),m):{type:"void",instance:null,count:0,state:null};case"link":if(u.rel==="stylesheet"&&typeof u.href=="string"&&typeof u.precedence=="string"){r=Tl(u.href);var S=Rr(x).hoistableStyles,M=S.get(r);if(M||(x=x.ownerDocument||x,M={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},S.set(r,M),(S=x.querySelector(ou(r)))&&!S._p&&(M.instance=S,M.state.loading=5),Xi.has(r)||(u={rel:"preload",as:"style",href:u.href,crossOrigin:u.crossOrigin,integrity:u.integrity,media:u.media,hrefLang:u.hrefLang,referrerPolicy:u.referrerPolicy},Xi.set(r,u),S||$D(x,r,u,M.state))),o&&m===null)throw Error(i(528,""));return M}if(o&&m!==null)throw Error(i(529,""));return null;case"script":return o=u.async,u=u.src,typeof u=="string"&&o&&typeof o!="function"&&typeof o!="symbol"?(o=Dl(u),u=Rr(x).hoistableScripts,m=u.get(o),m||(m={type:"script",instance:null,count:0,state:null},u.set(o,m)),m):{type:"void",instance:null,count:0,state:null};default:throw Error(i(444,r))}}function Tl(r){return'href="'+Ge(r)+'"'}function ou(r){return'link[rel="stylesheet"]['+r+"]"}function j_(r){return v({},r,{"data-precedence":r.precedence,precedence:null})}function $D(r,o,u,m){r.querySelector('link[rel="preload"][as="style"]['+o+"]")?m.loading=1:(o=r.createElement("link"),m.preload=o,o.addEventListener("load",function(){return m.loading|=1}),o.addEventListener("error",function(){return m.loading|=2}),Un(o,"link",u),bn(o),r.head.appendChild(o))}function Dl(r){return'[src="'+Ge(r)+'"]'}function lu(r){return"script[async]"+r}function B_(r,o,u){if(o.count++,o.instance===null)switch(o.type){case"style":var m=r.querySelector('style[data-href~="'+Ge(u.href)+'"]');if(m)return o.instance=m,bn(m),m;var x=v({},u,{"data-href":u.href,"data-precedence":u.precedence,href:null,precedence:null});return m=(r.ownerDocument||r).createElement("style"),bn(m),Un(m,"style",x),Zf(m,u.precedence,r),o.instance=m;case"stylesheet":x=Tl(u.href);var S=r.querySelector(ou(x));if(S)return o.state.loading|=4,o.instance=S,bn(S),S;m=j_(u),(x=Xi.get(x))&&ev(m,x),S=(r.ownerDocument||r).createElement("link"),bn(S);var M=S;return M._p=new Promise(function(B,W){M.onload=B,M.onerror=W}),Un(S,"link",m),o.state.loading|=4,Zf(S,u.precedence,r),o.instance=S;case"script":return S=Dl(u.src),(x=r.querySelector(lu(S)))?(o.instance=x,bn(x),x):(m=u,(x=Xi.get(S))&&(m=v({},u),nv(m,x)),r=r.ownerDocument||r,x=r.createElement("script"),bn(x),Un(x,"link",m),r.head.appendChild(x),o.instance=x);case"void":return null;default:throw Error(i(443,o.type))}else o.type==="stylesheet"&&(o.state.loading&4)===0&&(m=o.instance,o.state.loading|=4,Zf(m,u.precedence,r));return o.instance}function Zf(r,o,u){for(var m=u.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),x=m.length?m[m.length-1]:null,S=x,M=0;M title"):null)}function GD(r,o,u){if(u===1||o.itemProp!=null)return!1;switch(r){case"meta":case"title":return!0;case"style":if(typeof o.precedence!="string"||typeof o.href!="string"||o.href==="")break;return!0;case"link":if(typeof o.rel!="string"||typeof o.href!="string"||o.href===""||o.onLoad||o.onError)break;switch(o.rel){case"stylesheet":return r=o.disabled,typeof o.precedence=="string"&&r==null;default:return!0}case"script":if(o.async&&typeof o.async!="function"&&typeof o.async!="symbol"&&!o.onLoad&&!o.onError&&o.src&&typeof o.src=="string")return!0}return!1}function H_(r){return!(r.type==="stylesheet"&&(r.state.loading&3)===0)}function WD(r,o,u,m){if(u.type==="stylesheet"&&(typeof m.media!="string"||matchMedia(m.media).matches!==!1)&&(u.state.loading&4)===0){if(u.instance===null){var x=Tl(m.href),S=o.querySelector(ou(x));if(S){o=S._p,o!==null&&typeof o=="object"&&typeof o.then=="function"&&(r.count++,r=Yf.bind(r),o.then(r,r)),u.state.loading|=4,u.instance=S,bn(S);return}S=o.ownerDocument||o,m=j_(m),(x=Xi.get(x))&&ev(m,x),S=S.createElement("link"),bn(S);var M=S;M._p=new Promise(function(B,W){M.onload=B,M.onerror=W}),Un(S,"link",m),u.instance=S}r.stylesheets===null&&(r.stylesheets=new Map),r.stylesheets.set(u,o),(o=u.state.preload)&&(u.state.loading&3)===0&&(r.count++,u=Yf.bind(r),o.addEventListener("load",u),o.addEventListener("error",u))}}var iv=0;function ZD(r,o){return r.stylesheets&&r.count===0&&Kf(r,r.stylesheets),0iv?50:800)+o);return r.unsuspend=u,function(){r.unsuspend=null,clearTimeout(m),clearTimeout(x)}}:null}function Yf(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)Kf(this,this.stylesheets);else if(this.unsuspend){var r=this.unsuspend;this.unsuspend=null,r()}}}var Xf=null;function Kf(r,o){r.stylesheets=null,r.unsuspend!==null&&(r.count++,Xf=new Map,o.forEach(qD,r),Xf=null,Yf.call(r))}function qD(r,o){if(!(o.state.loading&4)){var u=Xf.get(r);if(u)var m=u.get(null);else{u=new Map,Xf.set(r,u);for(var x=r.querySelectorAll("link[data-precedence],style[data-precedence]"),S=0;S"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(t)}catch(e){console.error(e)}}return t(),fv.exports=fM(),fv.exports}var pM=hM();/** + * react-router v7.14.1 + * + * Copyright (c) Remix Software Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE.md file in the root directory of this source tree. + * + * @license MIT + */var f1="popstate";function h1(t){return typeof t=="object"&&t!=null&&"pathname"in t&&"search"in t&&"hash"in t&&"state"in t&&"key"in t}function mM(t={}){function e(i,a){var p;let s=(p=a.state)==null?void 0:p.masked,{pathname:l,search:d,hash:f}=s||i.location;return sb("",{pathname:l,search:d,hash:f},a.state&&a.state.usr||null,a.state&&a.state.key||"default",s?{pathname:i.location.pathname,search:i.location.search,hash:i.location.hash}:void 0)}function n(i,a){return typeof a=="string"?a:Yu(a)}return vM(e,n,null,t)}function tn(t,e){if(t===!1||t===null||typeof t>"u")throw new Error(e)}function xa(t,e){if(!t){typeof console<"u"&&console.warn(e);try{throw new Error(e)}catch{}}}function gM(){return Math.random().toString(36).substring(2,10)}function p1(t,e){return{usr:t.state,key:t.key,idx:e,masked:t.unstable_mask?{pathname:t.pathname,search:t.search,hash:t.hash}:void 0}}function sb(t,e,n=null,i,a){return{pathname:typeof t=="string"?t:t.pathname,search:"",hash:"",...typeof e=="string"?ec(e):e,state:n,key:e&&e.key||i||gM(),unstable_mask:a}}function Yu({pathname:t="/",search:e="",hash:n=""}){return e&&e!=="?"&&(t+=e.charAt(0)==="?"?e:"?"+e),n&&n!=="#"&&(t+=n.charAt(0)==="#"?n:"#"+n),t}function ec(t){let e={};if(t){let n=t.indexOf("#");n>=0&&(e.hash=t.substring(n),t=t.substring(0,n));let i=t.indexOf("?");i>=0&&(e.search=t.substring(i),t=t.substring(0,i)),t&&(e.pathname=t)}return e}function vM(t,e,n,i={}){let{window:a=document.defaultView,v5Compat:s=!1}=i,l=a.history,d="POP",f=null,p=g();p==null&&(p=0,l.replaceState({...l.state,idx:p},""));function g(){return(l.state||{idx:null}).idx}function v(){d="POP";let A=g(),k=A==null?null:A-p;p=A,f&&f({action:d,location:C.location,delta:k})}function y(A,k){d="PUSH";let z=h1(A)?A:sb(C.location,A,k);p=g()+1;let j=p1(z,p),P=C.createHref(z.unstable_mask||z);try{l.pushState(j,"",P)}catch(H){if(H instanceof DOMException&&H.name==="DataCloneError")throw H;a.location.assign(P)}s&&f&&f({action:d,location:C.location,delta:1})}function _(A,k){d="REPLACE";let z=h1(A)?A:sb(C.location,A,k);p=g();let j=p1(z,p),P=C.createHref(z.unstable_mask||z);l.replaceState(j,"",P),s&&f&&f({action:d,location:C.location,delta:0})}function w(A){return bM(A)}let C={get action(){return d},get location(){return t(a,l)},listen(A){if(f)throw new Error("A history only accepts one active listener");return a.addEventListener(f1,v),f=A,()=>{a.removeEventListener(f1,v),f=null}},createHref(A){return e(a,A)},createURL:w,encodeLocation(A){let k=w(A);return{pathname:k.pathname,search:k.search,hash:k.hash}},push:y,replace:_,go(A){return l.go(A)}};return C}function bM(t,e=!1){let n="http://localhost";typeof window<"u"&&(n=window.location.origin!=="null"?window.location.origin:window.location.href),tn(n,"No window.location.(origin|href) available to create URL");let i=typeof t=="string"?t:Yu(t);return i=i.replace(/ $/,"%20"),!e&&i.startsWith("//")&&(i=n+i),new URL(i,n)}function Jw(t,e,n="/"){return yM(t,e,n,!1)}function yM(t,e,n,i){let a=typeof e=="string"?ec(e):e,s=kr(a.pathname||"/",n);if(s==null)return null;let l=tC(t);xM(l);let d=null;for(let f=0;d==null&&f{let g={relativePath:p===void 0?l.path||"":p,caseSensitive:l.caseSensitive===!0,childrenIndex:d,route:l};if(g.relativePath.startsWith("/")){if(!g.relativePath.startsWith(i)&&f)return;tn(g.relativePath.startsWith(i),`Absolute route path "${g.relativePath}" nested under path "${i}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),g.relativePath=g.relativePath.slice(i.length)}let v=ba([i,g.relativePath]),y=n.concat(g);l.children&&l.children.length>0&&(tn(l.index!==!0,`Index routes must not have child routes. Please remove all child routes from route path "${v}".`),tC(l.children,e,y,v,f)),!(l.path==null&&!l.index)&&e.push({path:v,score:TM(v,l.index),routesMeta:y})};return t.forEach((l,d)=>{var f;if(l.path===""||!((f=l.path)!=null&&f.includes("?")))s(l,d);else for(let p of eC(l.path))s(l,d,!0,p)}),e}function eC(t){let e=t.split("/");if(e.length===0)return[];let[n,...i]=e,a=n.endsWith("?"),s=n.replace(/\?$/,"");if(i.length===0)return a?[s,""]:[s];let l=eC(i.join("/")),d=[];return d.push(...l.map(f=>f===""?s:[s,f].join("/"))),a&&d.push(...l),d.map(f=>t.startsWith("/")&&f===""?"/":f)}function xM(t){t.sort((e,n)=>e.score!==n.score?n.score-e.score:DM(e.routesMeta.map(i=>i.childrenIndex),n.routesMeta.map(i=>i.childrenIndex)))}var _M=/^:[\w-]+$/,SM=3,wM=2,CM=1,EM=10,AM=-2,m1=t=>t==="*";function TM(t,e){let n=t.split("/"),i=n.length;return n.some(m1)&&(i+=AM),e&&(i+=wM),n.filter(a=>!m1(a)).reduce((a,s)=>a+(_M.test(s)?SM:s===""?CM:EM),i)}function DM(t,e){return t.length===e.length&&t.slice(0,-1).every((i,a)=>i===e[a])?t[t.length-1]-e[e.length-1]:0}function MM(t,e,n=!1){let{routesMeta:i}=t,a={},s="/",l=[];for(let d=0;d{if(g==="*"){let w=d[y]||"";l=s.slice(0,s.length-w.length).replace(/(.)\/+$/,"$1")}const _=d[y];return v&&!_?p[g]=void 0:p[g]=(_||"").replace(/%2F/g,"/"),p},{}),pathname:s,pathnameBase:l,pattern:t}}function OM(t,e=!1,n=!0){xa(t==="*"||!t.endsWith("*")||t.endsWith("/*"),`Route path "${t}" will be treated as if it were "${t.replace(/\*$/,"/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${t.replace(/\*$/,"/*")}".`);let i=[],a="^"+t.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(l,d,f,p,g)=>{if(i.push({paramName:d,isOptional:f!=null}),f){let v=g.charAt(p+l.length);return v&&v!=="/"?"/([^\\/]*)":"(?:/([^\\/]*))?"}return"/([^\\/]+)"}).replace(/\/([\w-]+)\?(\/|$)/g,"(/$1)?$2");return t.endsWith("*")?(i.push({paramName:"*"}),a+=t==="*"||t==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):n?a+="\\/*$":t!==""&&t!=="/"&&(a+="(?:(?=\\/|$))"),[new RegExp(a,e?void 0:"i"),i]}function kM(t){try{return t.split("/").map(e=>decodeURIComponent(e).replace(/\//g,"%2F")).join("/")}catch(e){return xa(!1,`The URL path "${t}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${e}).`),t}}function kr(t,e){if(e==="/")return t;if(!t.toLowerCase().startsWith(e.toLowerCase()))return null;let n=e.endsWith("/")?e.length-1:e.length,i=t.charAt(n);return i&&i!=="/"?null:t.slice(n)||"/"}var LM=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;function zM(t,e="/"){let{pathname:n,search:i="",hash:a=""}=typeof t=="string"?ec(t):t,s;return n?(n=nC(n),n.startsWith("/")?s=g1(n.substring(1),"/"):s=g1(n,e)):s=e,{pathname:s,search:NM(i),hash:jM(a)}}function g1(t,e){let n=Qh(e).split("/");return t.split("/").forEach(a=>{a===".."?n.length>1&&n.pop():a!=="."&&n.push(a)}),n.length>1?n.join("/"):"/"}function gv(t,e,n,i){return`Cannot include a '${t}' character in a manually specified \`to.${e}\` field [${JSON.stringify(i)}]. Please separate it out to the \`to.${n}\` field. Alternatively you may provide the full path as a string in and the router will parse it for you.`}function RM(t){return t.filter((e,n)=>n===0||e.route.path&&e.route.path.length>0)}function Kb(t){let e=RM(t);return e.map((n,i)=>i===e.length-1?n.pathname:n.pathnameBase)}function kp(t,e,n,i=!1){let a;typeof t=="string"?a=ec(t):(a={...t},tn(!a.pathname||!a.pathname.includes("?"),gv("?","pathname","search",a)),tn(!a.pathname||!a.pathname.includes("#"),gv("#","pathname","hash",a)),tn(!a.search||!a.search.includes("#"),gv("#","search","hash",a)));let s=t===""||a.pathname==="",l=s?"/":a.pathname,d;if(l==null)d=n;else{let v=e.length-1;if(!i&&l.startsWith("..")){let y=l.split("/");for(;y[0]==="..";)y.shift(),v-=1;a.pathname=y.join("/")}d=v>=0?e[v]:"/"}let f=zM(a,d),p=l&&l!=="/"&&l.endsWith("/"),g=(s||l===".")&&n.endsWith("/");return!f.pathname.endsWith("/")&&(p||g)&&(f.pathname+="/"),f}var nC=t=>t.replace(/\/\/+/g,"/"),ba=t=>nC(t.join("/")),Qh=t=>t.replace(/\/+$/,""),PM=t=>Qh(t).replace(/^\/*/,"/"),NM=t=>!t||t==="?"?"":t.startsWith("?")?t:"?"+t,jM=t=>!t||t==="#"?"":t.startsWith("#")?t:"#"+t,BM=class{constructor(t,e,n,i=!1){this.status=t,this.statusText=e||"",this.internal=i,n instanceof Error?(this.data=n.toString(),this.error=n):this.data=n}};function IM(t){return t!=null&&typeof t.status=="number"&&typeof t.statusText=="string"&&typeof t.internal=="boolean"&&"data"in t}function FM(t){let e=t.map(n=>n.route.path).filter(Boolean);return ba(e)||"/"}var iC=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u";function aC(t,e){let n=t;if(typeof n!="string"||!LM.test(n))return{absoluteURL:void 0,isExternal:!1,to:n};let i=n,a=!1;if(iC)try{let s=new URL(window.location.href),l=n.startsWith("//")?new URL(s.protocol+n):new URL(n),d=kr(l.pathname,e);l.origin===s.origin&&d!=null?n=d+l.search+l.hash:a=!0}catch{xa(!1,` contains an invalid URL which will probably break when clicked - please update to a valid URL path.`)}return{absoluteURL:i,isExternal:a,to:n}}Object.getOwnPropertyNames(Object.prototype).sort().join("\0");var rC=["POST","PUT","PATCH","DELETE"];new Set(rC);var HM=["GET",...rC];new Set(HM);var nc=O.createContext(null);nc.displayName="DataRouter";var Lp=O.createContext(null);Lp.displayName="DataRouterState";var sC=O.createContext(!1);function UM(){return O.useContext(sC)}var oC=O.createContext({isTransitioning:!1});oC.displayName="ViewTransition";var VM=O.createContext(new Map);VM.displayName="Fetchers";var $M=O.createContext(null);$M.displayName="Await";var Ii=O.createContext(null);Ii.displayName="Navigation";var wd=O.createContext(null);wd.displayName="Location";var qa=O.createContext({outlet:null,matches:[],isDataRoute:!1});qa.displayName="Route";var Qb=O.createContext(null);Qb.displayName="RouteError";var lC="REACT_ROUTER_ERROR",GM="REDIRECT",WM="ROUTE_ERROR_RESPONSE";function ZM(t){if(t.startsWith(`${lC}:${GM}:{`))try{let e=JSON.parse(t.slice(28));if(typeof e=="object"&&e&&typeof e.status=="number"&&typeof e.statusText=="string"&&typeof e.location=="string"&&typeof e.reloadDocument=="boolean"&&typeof e.replace=="boolean")return e}catch{}}function qM(t){if(t.startsWith(`${lC}:${WM}:{`))try{let e=JSON.parse(t.slice(40));if(typeof e=="object"&&e&&typeof e.status=="number"&&typeof e.statusText=="string")return new BM(e.status,e.statusText,e.data)}catch{}}function YM(t,{relative:e}={}){tn(ic(),"useHref() may be used only in the context of a component.");let{basename:n,navigator:i}=O.useContext(Ii),{hash:a,pathname:s,search:l}=Cd(t,{relative:e}),d=s;return n!=="/"&&(d=s==="/"?n:ba([n,s])),i.createHref({pathname:d,search:l,hash:a})}function ic(){return O.useContext(wd)!=null}function Ya(){return tn(ic(),"useLocation() may be used only in the context of a component."),O.useContext(wd).location}var cC="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function uC(t){O.useContext(Ii).static||O.useLayoutEffect(t)}function Jb(){let{isDataRoute:t}=O.useContext(qa);return t?lO():XM()}function XM(){tn(ic(),"useNavigate() may be used only in the context of a component.");let t=O.useContext(nc),{basename:e,navigator:n}=O.useContext(Ii),{matches:i}=O.useContext(qa),{pathname:a}=Ya(),s=JSON.stringify(Kb(i)),l=O.useRef(!1);return uC(()=>{l.current=!0}),O.useCallback((f,p={})=>{if(xa(l.current,cC),!l.current)return;if(typeof f=="number"){n.go(f);return}let g=kp(f,JSON.parse(s),a,p.relative==="path");t==null&&e!=="/"&&(g.pathname=g.pathname==="/"?e:ba([e,g.pathname])),(p.replace?n.replace:n.push)(g,p.state,p)},[e,n,s,a,t])}O.createContext(null);function Cd(t,{relative:e}={}){let{matches:n}=O.useContext(qa),{pathname:i}=Ya(),a=JSON.stringify(Kb(n));return O.useMemo(()=>kp(t,JSON.parse(a),i,e==="path"),[t,a,i,e])}function KM(t,e){return dC(t,e)}function dC(t,e,n){var A;tn(ic(),"useRoutes() may be used only in the context of a component.");let{navigator:i}=O.useContext(Ii),{matches:a}=O.useContext(qa),s=a[a.length-1],l=s?s.params:{},d=s?s.pathname:"/",f=s?s.pathnameBase:"/",p=s&&s.route;{let k=p&&p.path||"";hC(d,!p||k.endsWith("*")||k.endsWith("*?"),`You rendered descendant (or called \`useRoutes()\`) at "${d}" (under ) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render. + +Please change the parent to .`)}let g=Ya(),v;if(e){let k=typeof e=="string"?ec(e):e;tn(f==="/"||((A=k.pathname)==null?void 0:A.startsWith(f)),`When overriding the location using \`\` or \`useRoutes(routes, location)\`, the location pathname must begin with the portion of the URL pathname that was matched by all parent routes. The current pathname base is "${f}" but pathname "${k.pathname}" was given in the \`location\` prop.`),v=k}else v=g;let y=v.pathname||"/",_=y;if(f!=="/"){let k=f.replace(/^\//,"").split("/");_="/"+y.replace(/^\//,"").split("/").slice(k.length).join("/")}let w=Jw(t,{pathname:_});xa(p||w!=null,`No routes matched location "${v.pathname}${v.search}${v.hash}" `),xa(w==null||w[w.length-1].route.element!==void 0||w[w.length-1].route.Component!==void 0||w[w.length-1].route.lazy!==void 0,`Matched leaf route at location "${v.pathname}${v.search}${v.hash}" does not have an element or Component. This means it will render an with a null value by default resulting in an "empty" page.`);let C=nO(w&&w.map(k=>Object.assign({},k,{params:Object.assign({},l,k.params),pathname:ba([f,i.encodeLocation?i.encodeLocation(k.pathname.replace(/%/g,"%25").replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:k.pathname]),pathnameBase:k.pathnameBase==="/"?f:ba([f,i.encodeLocation?i.encodeLocation(k.pathnameBase.replace(/%/g,"%25").replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:k.pathnameBase])})),a,n);return e&&C?O.createElement(wd.Provider,{value:{location:{pathname:"/",search:"",hash:"",state:null,key:"default",unstable_mask:void 0,...v},navigationType:"POP"}},C):C}function QM(){let t=oO(),e=IM(t)?`${t.status} ${t.statusText}`:t instanceof Error?t.message:JSON.stringify(t),n=t instanceof Error?t.stack:null,i="rgba(200,200,200, 0.5)",a={padding:"0.5rem",backgroundColor:i},s={padding:"2px 4px",backgroundColor:i},l=null;return console.error("Error handled by React Router default ErrorBoundary:",t),l=O.createElement(O.Fragment,null,O.createElement("p",null,"💿 Hey developer 👋"),O.createElement("p",null,"You can provide a way better UX than this when your app throws errors by providing your own ",O.createElement("code",{style:s},"ErrorBoundary")," or"," ",O.createElement("code",{style:s},"errorElement")," prop on your route.")),O.createElement(O.Fragment,null,O.createElement("h2",null,"Unexpected Application Error!"),O.createElement("h3",{style:{fontStyle:"italic"}},e),n?O.createElement("pre",{style:a},n):null,l)}var JM=O.createElement(QM,null),fC=class extends O.Component{constructor(t){super(t),this.state={location:t.location,revalidation:t.revalidation,error:t.error}}static getDerivedStateFromError(t){return{error:t}}static getDerivedStateFromProps(t,e){return e.location!==t.location||e.revalidation!=="idle"&&t.revalidation==="idle"?{error:t.error,location:t.location,revalidation:t.revalidation}:{error:t.error!==void 0?t.error:e.error,location:e.location,revalidation:t.revalidation||e.revalidation}}componentDidCatch(t,e){this.props.onError?this.props.onError(t,e):console.error("React Router caught the following error during render",t)}render(){let t=this.state.error;if(this.context&&typeof t=="object"&&t&&"digest"in t&&typeof t.digest=="string"){const n=qM(t.digest);n&&(t=n)}let e=t!==void 0?O.createElement(qa.Provider,{value:this.props.routeContext},O.createElement(Qb.Provider,{value:t,children:this.props.component})):this.props.children;return this.context?O.createElement(tO,{error:t},e):e}};fC.contextType=sC;var vv=new WeakMap;function tO({children:t,error:e}){let{basename:n}=O.useContext(Ii);if(typeof e=="object"&&e&&"digest"in e&&typeof e.digest=="string"){let i=ZM(e.digest);if(i){let a=vv.get(e);if(a)throw a;let s=aC(i.location,n);if(iC&&!vv.get(e))if(s.isExternal||i.reloadDocument)window.location.href=s.absoluteURL||s.to;else{const l=Promise.resolve().then(()=>window.__reactRouterDataRouter.navigate(s.to,{replace:i.replace}));throw vv.set(e,l),l}return O.createElement("meta",{httpEquiv:"refresh",content:`0;url=${s.absoluteURL||s.to}`})}}return t}function eO({routeContext:t,match:e,children:n}){let i=O.useContext(nc);return i&&i.static&&i.staticContext&&(e.route.errorElement||e.route.ErrorBoundary)&&(i.staticContext._deepestRenderedBoundaryId=e.route.id),O.createElement(qa.Provider,{value:t},n)}function nO(t,e=[],n){let i=n==null?void 0:n.state;if(t==null){if(!i)return null;if(i.errors)t=i.matches;else if(e.length===0&&!i.initialized&&i.matches.length>0)t=i.matches;else return null}let a=t,s=i==null?void 0:i.errors;if(s!=null){let g=a.findIndex(v=>v.route.id&&(s==null?void 0:s[v.route.id])!==void 0);tn(g>=0,`Could not find a matching route for errors on route IDs: ${Object.keys(s).join(",")}`),a=a.slice(0,Math.min(a.length,g+1))}let l=!1,d=-1;if(n&&i){l=i.renderFallback;for(let g=0;g=0?a=a.slice(0,d+1):a=[a[0]];break}}}}let f=n==null?void 0:n.onError,p=i&&f?(g,v)=>{var y,_;f(g,{location:i.location,params:((_=(y=i.matches)==null?void 0:y[0])==null?void 0:_.params)??{},unstable_pattern:FM(i.matches),errorInfo:v})}:void 0;return a.reduceRight((g,v,y)=>{let _,w=!1,C=null,A=null;i&&(_=s&&v.route.id?s[v.route.id]:void 0,C=v.route.errorElement||JM,l&&(d<0&&y===0?(hC("route-fallback",!1,"No `HydrateFallback` element provided to render during initial hydration"),w=!0,A=null):d===y&&(w=!0,A=v.route.hydrateFallbackElement||null)));let k=e.concat(a.slice(0,y+1)),z=()=>{let j;return _?j=C:w?j=A:v.route.Component?j=O.createElement(v.route.Component,null):v.route.element?j=v.route.element:j=g,O.createElement(eO,{match:v,routeContext:{outlet:g,matches:k,isDataRoute:i!=null},children:j})};return i&&(v.route.ErrorBoundary||v.route.errorElement||y===0)?O.createElement(fC,{location:i.location,revalidation:i.revalidation,component:C,error:_,children:z(),routeContext:{outlet:null,matches:k,isDataRoute:!0},onError:p}):z()},null)}function ty(t){return`${t} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function iO(t){let e=O.useContext(nc);return tn(e,ty(t)),e}function aO(t){let e=O.useContext(Lp);return tn(e,ty(t)),e}function rO(t){let e=O.useContext(qa);return tn(e,ty(t)),e}function ey(t){let e=rO(t),n=e.matches[e.matches.length-1];return tn(n.route.id,`${t} can only be used on routes that contain a unique "id"`),n.route.id}function sO(){return ey("useRouteId")}function oO(){var i;let t=O.useContext(Qb),e=aO("useRouteError"),n=ey("useRouteError");return t!==void 0?t:(i=e.errors)==null?void 0:i[n]}function lO(){let{router:t}=iO("useNavigate"),e=ey("useNavigate"),n=O.useRef(!1);return uC(()=>{n.current=!0}),O.useCallback(async(a,s={})=>{xa(n.current,cC),n.current&&(typeof a=="number"?await t.navigate(a):await t.navigate(a,{fromRouteId:e,...s}))},[t,e])}var v1={};function hC(t,e,n){!e&&!v1[t]&&(v1[t]=!0,xa(!1,n))}O.memo(cO);function cO({routes:t,future:e,state:n,isStatic:i,onError:a}){return dC(t,void 0,{state:n,isStatic:i,onError:a})}function b1({to:t,replace:e,state:n,relative:i}){tn(ic()," may be used only in the context of a component.");let{static:a}=O.useContext(Ii);xa(!a," must not be used on the initial render in a . This is a no-op, but you should modify your code so the is only ever rendered in response to some user interaction or state change.");let{matches:s}=O.useContext(qa),{pathname:l}=Ya(),d=Jb(),f=kp(t,Kb(s),l,i==="path"),p=JSON.stringify(f);return O.useEffect(()=>{d(JSON.parse(p),{replace:e,state:n,relative:i})},[d,p,i,e,n]),null}function Sr(t){tn(!1,"A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .")}function uO({basename:t="/",children:e=null,location:n,navigationType:i="POP",navigator:a,static:s=!1,unstable_useTransitions:l}){tn(!ic(),"You cannot render a inside another . You should never have more than one in your app.");let d=t.replace(/^\/*/,"/"),f=O.useMemo(()=>({basename:d,navigator:a,static:s,unstable_useTransitions:l,future:{}}),[d,a,s,l]);typeof n=="string"&&(n=ec(n));let{pathname:p="/",search:g="",hash:v="",state:y=null,key:_="default",unstable_mask:w}=n,C=O.useMemo(()=>{let A=kr(p,d);return A==null?null:{location:{pathname:A,search:g,hash:v,state:y,key:_,unstable_mask:w},navigationType:i}},[d,p,g,v,y,_,i,w]);return xa(C!=null,` is not able to match the URL "${p}${g}${v}" because it does not start with the basename, so the won't render anything.`),C==null?null:O.createElement(Ii.Provider,{value:f},O.createElement(wd.Provider,{children:e,value:C}))}function y1({children:t,location:e}){return KM(ob(t),e)}function ob(t,e=[]){let n=[];return O.Children.forEach(t,(i,a)=>{if(!O.isValidElement(i))return;let s=[...e,a];if(i.type===O.Fragment){n.push.apply(n,ob(i.props.children,s));return}tn(i.type===Sr,`[${typeof i.type=="string"?i.type:i.type.name}] is not a component. All component children of must be a or `),tn(!i.props.index||!i.props.children,"An index route cannot have child routes.");let l={id:i.props.id||s.join("-"),caseSensitive:i.props.caseSensitive,element:i.props.element,Component:i.props.Component,index:i.props.index,path:i.props.path,middleware:i.props.middleware,loader:i.props.loader,action:i.props.action,hydrateFallbackElement:i.props.hydrateFallbackElement,HydrateFallback:i.props.HydrateFallback,errorElement:i.props.errorElement,ErrorBoundary:i.props.ErrorBoundary,hasErrorBoundary:i.props.hasErrorBoundary===!0||i.props.ErrorBoundary!=null||i.props.errorElement!=null,shouldRevalidate:i.props.shouldRevalidate,handle:i.props.handle,lazy:i.props.lazy};i.props.children&&(l.children=ob(i.props.children,s)),n.push(l)}),n}var Lh="get",zh="application/x-www-form-urlencoded";function zp(t){return typeof HTMLElement<"u"&&t instanceof HTMLElement}function dO(t){return zp(t)&&t.tagName.toLowerCase()==="button"}function fO(t){return zp(t)&&t.tagName.toLowerCase()==="form"}function hO(t){return zp(t)&&t.tagName.toLowerCase()==="input"}function pO(t){return!!(t.metaKey||t.altKey||t.ctrlKey||t.shiftKey)}function mO(t,e){return t.button===0&&(!e||e==="_self")&&!pO(t)}var rh=null;function gO(){if(rh===null)try{new FormData(document.createElement("form"),0),rh=!1}catch{rh=!0}return rh}var vO=new Set(["application/x-www-form-urlencoded","multipart/form-data","text/plain"]);function bv(t){return t!=null&&!vO.has(t)?(xa(!1,`"${t}" is not a valid \`encType\` for \`
\`/\`\` and will default to "${zh}"`),null):t}function bO(t,e){let n,i,a,s,l;if(fO(t)){let d=t.getAttribute("action");i=d?kr(d,e):null,n=t.getAttribute("method")||Lh,a=bv(t.getAttribute("enctype"))||zh,s=new FormData(t)}else if(dO(t)||hO(t)&&(t.type==="submit"||t.type==="image")){let d=t.form;if(d==null)throw new Error('Cannot submit a - ))} - -
+
{classes.filter(c => c.photoMode === photoMode).map((c, i) => ( ))}
+
{t('annotations.photoMode')}
+
+ {modes.map(m => ( + + ))} +
) } diff --git a/src/features/annotations/AnnotationsPage.tsx b/src/features/annotations/AnnotationsPage.tsx index c04fc02..9c8d9e8 100644 --- a/src/features/annotations/AnnotationsPage.tsx +++ b/src/features/annotations/AnnotationsPage.tsx @@ -1,10 +1,13 @@ -import { useState, useCallback } from 'react' +import { useState, useCallback, useEffect, useRef } from 'react' import { useResizablePanel } from '../../hooks/useResizablePanel' +import { api } from '../../api/client' import MediaList from './MediaList' -import VideoPlayer from './VideoPlayer' -import CanvasEditor from './CanvasEditor' +import VideoPlayer, { type VideoPlayerHandle } from './VideoPlayer' +import CanvasEditor, { type CanvasEditorHandle } from './CanvasEditor' import AnnotationsSidebar from './AnnotationsSidebar' import DetectionClasses from '../../components/DetectionClasses' +import { AnnotationSource, AnnotationStatus, MediaType } from '../../types' +import { getClassColor, getClassNameFallback, getPhotoModeSuffix } from './classColors' import type { Media, AnnotationListItem, Detection } from '../../types' export default function AnnotationsPage() { @@ -17,17 +20,158 @@ export default function AnnotationsPage() { const [detections, setDetections] = useState([]) const leftPanel = useResizablePanel(250, 200, 400) const rightPanel = useResizablePanel(200, 150, 350) + const videoPlayerRef = useRef(null) + const canvasRef = useRef(null) + + useEffect(() => { + setDetections([]) + setSelectedAnnotation(null) + setCurrentTime(0) + }, [selectedMedia]) + + const handleSave = useCallback(async () => { + if (!selectedMedia || !detections.length) return + const time = selectedMedia.mediaType === MediaType.Video ? formatTicks(currentTime) : null + const body = { mediaId: selectedMedia.id, time, detections } + + if (!selectedMedia.path.startsWith('blob:')) { + try { + await api.post('/api/annotations/annotations', body) + const res = await api.get<{ items: AnnotationListItem[] }>( + `/api/annotations/annotations?mediaId=${selectedMedia.id}&pageSize=1000`, + ) + setAnnotations(res.items) + return + } catch { + // fall through to local save + } + } + + const local: AnnotationListItem = { + id: `local-${crypto.randomUUID()}`, + mediaId: selectedMedia.id, + time, + createdDate: new Date().toISOString(), + userId: 'local', + source: AnnotationSource.Manual, + status: AnnotationStatus.Created, + isSplit: false, + splitTile: null, + detections: [...detections], + } + setAnnotations(prev => [...prev, local]) + }, [selectedMedia, detections, currentTime]) + + const handleDownload = useCallback(async (ann: AnnotationListItem) => { + if (!selectedMedia) return + + const txt = ann.detections + .map(d => `${d.classNum} ${d.centerX.toFixed(6)} ${d.centerY.toFixed(6)} ${d.width.toFixed(6)} ${d.height.toFixed(6)}`) + .join('\n') + const stem = `annotation_${new Date().toISOString().replace(/[:.]/g, '-')}` + + const txtBlob = new Blob([txt], { type: 'text/plain' }) + const txtUrl = URL.createObjectURL(txtBlob) + const txtA = document.createElement('a') + txtA.href = txtUrl + txtA.download = `${stem}.txt` + txtA.click() + URL.revokeObjectURL(txtUrl) + + // Build the image: video frame or image with rectangles drawn + const videoEl = videoPlayerRef.current?.getVideoElement() ?? null + let w = 0, h = 0 + const canvas = document.createElement('canvas') + if (videoEl && videoEl.videoWidth) { + w = videoEl.videoWidth + h = videoEl.videoHeight + canvas.width = w + canvas.height = h + const ctx = canvas.getContext('2d') + if (ctx) ctx.drawImage(videoEl, 0, 0, w, h) + } else if (!selectedMedia.path) { + return + } else { + const img = new Image() + img.crossOrigin = 'anonymous' + img.src = selectedMedia.path.startsWith('blob:') + ? selectedMedia.path + : `/api/annotations/media/${selectedMedia.id}/file` + await new Promise(res => { img.onload = res; img.onerror = res }) + w = img.naturalWidth + h = img.naturalHeight + canvas.width = w + canvas.height = h + const ctx = canvas.getContext('2d') + if (ctx) ctx.drawImage(img, 0, 0, w, h) + } + + if (w && h) { + const ctx = canvas.getContext('2d') + if (ctx) { + ctx.lineWidth = 2 + ctx.font = '14px sans-serif' + const labelH = 17 + const padX = 4 + for (const d of ann.detections) { + const bx = (d.centerX - d.width / 2) * w + const by = (d.centerY - d.height / 2) * h + const bw = d.width * w + const bh = d.height * h + const color = getClassColor(d.classNum) + + ctx.strokeStyle = color + ctx.strokeRect(bx, by, bw, bh) + + const name = d.label || getClassNameFallback(d.classNum) + const modeSuffix = getPhotoModeSuffix(d.classNum) + const confSuffix = d.confidence < 0.995 ? ` ${(d.confidence * 100).toFixed(0)}%` : '' + const label = `${name}${modeSuffix}${confSuffix}` + + const metrics = ctx.measureText(label) + const labelW = metrics.width + padX * 2 + ctx.fillStyle = color + ctx.fillRect(bx, Math.max(0, by - labelH), labelW, labelH) + ctx.fillStyle = '#000' + ctx.fillText(label, bx + padX, Math.max(13, by - 4)) + } + } + canvas.toBlob(blob => { + if (!blob) return + const url = URL.createObjectURL(blob) + const a = document.createElement('a') + a.href = url + a.download = `${stem}.png` + a.click() + URL.revokeObjectURL(url) + }, 'image/png') + } + }, [selectedMedia]) const handleAnnotationSelect = useCallback((ann: AnnotationListItem) => { setSelectedAnnotation(ann) setDetections(ann.detections) + if (ann.time) { + const parts = ann.time.split(':').map(Number) + const seconds = (parts[0] || 0) * 3600 + (parts[1] || 0) * 60 + (parts[2] || 0) + videoPlayerRef.current?.seek(seconds) + setCurrentTime(seconds) + } }, []) const handleDetectionsChange = useCallback((dets: Detection[]) => { setDetections(dets) }, []) - const isVideo = selectedMedia?.mediaType === 2 + const isVideo = selectedMedia?.mediaType === MediaType.Video + + function formatTicks(seconds: number): string { + const h = Math.floor(seconds / 3600) + const m = Math.floor((seconds % 3600) / 60) + const s = Math.floor(seconds % 60) + const ms = Math.floor((seconds - Math.floor(seconds)) * 1000) + return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}.${String(ms).padStart(3, '0')}` + } return (
@@ -48,16 +192,54 @@ export default function AnnotationsPage() {
{/* Center - video/canvas */} -
+
+ {selectedMedia && ( +
+ + + + {detections.length} detection{detections.length !== 1 ? 's' : ''} +
+ )} {selectedMedia && isVideo && ( + > + + )} - {selectedMedia && ( + {selectedMedia && !isVideo && (
diff --git a/src/features/annotations/AnnotationsSidebar.tsx b/src/features/annotations/AnnotationsSidebar.tsx index 624acb3..dec40f1 100644 --- a/src/features/annotations/AnnotationsSidebar.tsx +++ b/src/features/annotations/AnnotationsSidebar.tsx @@ -1,7 +1,9 @@ import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' +import { FaDownload } from 'react-icons/fa' import { api } from '../../api/client' import { createSSE } from '../../api/sse' +import { getClassColor } from './classColors' import type { Media, AnnotationListItem, PaginatedResponse } from '../../types' interface Props { @@ -10,9 +12,10 @@ interface Props { selectedAnnotation: AnnotationListItem | null onSelect: (ann: AnnotationListItem) => void onAnnotationsUpdate: (anns: AnnotationListItem[]) => void + onDownload?: (ann: AnnotationListItem) => void } -export default function AnnotationsSidebar({ media, annotations, selectedAnnotation, onSelect, onAnnotationsUpdate }: Props) { +export default function AnnotationsSidebar({ media, annotations, selectedAnnotation, onSelect, onAnnotationsUpdate, onDownload }: Props) { const { t } = useTranslation() const [detecting, setDetecting] = useState(false) const [detectLog, setDetectLog] = useState([]) @@ -45,29 +48,32 @@ export default function AnnotationsSidebar({ media, annotations, selectedAnnotat const stops = ann.detections.map((d, i) => { const pct = (i / Math.max(ann.detections.length - 1, 1)) * 100 const alpha = Math.min(1, d.confidence) - return `${d.label ? getClassColor(d.classNum) : '#888'}${Math.round(alpha * 40).toString(16).padStart(2, '0')} ${pct}%` + return `${getClassColor(d.classNum)}${Math.round(alpha * 40).toString(16).padStart(2, '0')} ${pct}%` }) return `linear-gradient(to right, ${stops.join(', ')})` } - const classColors: Record = {} - const getClassColor = (classNum: number) => { - if (classColors[classNum]) return classColors[classNum] - const colors = ['#FF0000', '#00FF00', '#0000FF', '#FFFF00', '#FF00FF', '#00FFFF', '#188021', '#800000', '#008000', '#000080'] - return colors[classNum % colors.length] - } - return (
-
+
{t('annotations.title')} - +
+ + +
diff --git a/src/features/annotations/CanvasEditor.tsx b/src/features/annotations/CanvasEditor.tsx index 284980d..649e6a3 100644 --- a/src/features/annotations/CanvasEditor.tsx +++ b/src/features/annotations/CanvasEditor.tsx @@ -1,5 +1,7 @@ -import { useRef, useEffect, useState, useCallback } from 'react' +import { useRef, useEffect, useState, useCallback, forwardRef, useImperativeHandle } from 'react' +import { MediaType } from '../../types' import type { Media, AnnotationListItem, Detection, Affiliation, CombatReadiness } from '../../types' +import { getClassColor, getPhotoModeSuffix, getClassNameFallback } from './classColors' interface Props { media: Media @@ -11,6 +13,12 @@ interface Props { annotations: AnnotationListItem[] } +export interface CanvasEditorHandle { + deleteSelected: () => void + deleteAll: () => void + hasSelection: () => boolean +} + interface DragState { type: 'draw' | 'move' | 'resize' startX: number @@ -28,7 +36,10 @@ const AFFILIATION_COLORS: Record = { 2: '#fa5252', } -export default function CanvasEditor({ media, annotation, detections, onDetectionsChange, selectedClassNum, currentTime, annotations }: Props) { +const CanvasEditor = forwardRef(function CanvasEditor( + { media, annotation, detections, onDetectionsChange, selectedClassNum, currentTime, annotations }, + ref, +) { const canvasRef = useRef(null) const containerRef = useRef(null) const imgRef = useRef(null) @@ -39,11 +50,35 @@ export default function CanvasEditor({ media, annotation, detections, onDetectio const [drawRect, setDrawRect] = useState<{ x: number; y: number; w: number; h: number } | null>(null) const [imgSize, setImgSize] = useState({ w: 0, h: 0 }) + useImperativeHandle(ref, () => ({ + deleteSelected() { + if (selected.size === 0) return + onDetectionsChange(detections.filter((_, i) => !selected.has(i))) + setSelected(new Set()) + }, + deleteAll() { + onDetectionsChange([]) + setSelected(new Set()) + }, + hasSelection() { + return selected.size > 0 + }, + }), [selected, detections, onDetectionsChange]) + + const isVideo = media.mediaType === MediaType.Video + const loadImage = useCallback(() => { + if (isVideo) { + // Use natural size based on container; no image load + imgRef.current = null + return + } const img = new Image() img.crossOrigin = 'anonymous' - if (annotation) { + if (annotation && !media.path.startsWith('blob:')) { img.src = `/api/annotations/annotations/${annotation.id}/image` + } else if (media.path.startsWith('blob:')) { + img.src = media.path } else { img.src = `/api/annotations/media/${media.id}/file` } @@ -51,10 +86,22 @@ export default function CanvasEditor({ media, annotation, detections, onDetectio imgRef.current = img setImgSize({ w: img.naturalWidth, h: img.naturalHeight }) } - }, [media, annotation]) + }, [media, annotation, isVideo]) useEffect(() => { loadImage() }, [loadImage]) + useEffect(() => { + if (!isVideo || !containerRef.current) return + const update = () => { + const c = containerRef.current + if (c) setImgSize({ w: c.clientWidth, h: c.clientHeight }) + } + update() + const ro = new ResizeObserver(update) + ro.observe(containerRef.current) + return () => ro.disconnect() + }, [isVideo]) + const toCanvas = useCallback((nx: number, ny: number) => ({ x: nx * imgSize.w * zoom + pan.x, y: ny * imgSize.h * zoom + pan.y, @@ -68,7 +115,8 @@ export default function CanvasEditor({ media, annotation, detections, onDetectio const draw = useCallback(() => { const canvas = canvasRef.current const ctx = canvas?.getContext('2d') - if (!canvas || !ctx || !imgRef.current) return + if (!canvas || !ctx) return + if (!isVideo && !imgRef.current) return const container = containerRef.current if (container) { @@ -78,7 +126,9 @@ export default function CanvasEditor({ media, annotation, detections, onDetectio ctx.clearRect(0, 0, canvas.width, canvas.height) ctx.save() - ctx.drawImage(imgRef.current, pan.x, pan.y, imgSize.w * zoom, imgSize.h * zoom) + if (!isVideo && imgRef.current) { + ctx.drawImage(imgRef.current, pan.x, pan.y, imgSize.w * zoom, imgSize.h * zoom) + } const timeWindowDets = getTimeWindowDetections() const allDets = [...detections, ...timeWindowDets] @@ -90,7 +140,7 @@ export default function CanvasEditor({ media, annotation, detections, onDetectio const w = det.width * imgSize.w * zoom const h = det.height * imgSize.h * zoom - const color = AFFILIATION_COLORS[det.affiliation] || '#FFD700' + const color = getClassColor(det.classNum) ctx.strokeStyle = color ctx.lineWidth = isSelected ? 2 : 1 ctx.strokeRect(cx, cy, w, h) @@ -100,12 +150,20 @@ export default function CanvasEditor({ media, annotation, detections, onDetectio ctx.fillRect(cx, cy, w, h) ctx.globalAlpha = 1 - const label = det.confidence < 0.995 - ? `${det.label} ${(det.confidence * 100).toFixed(0)}%` - : det.label - ctx.fillStyle = color + const name = det.label || getClassNameFallback(det.classNum) + const modeSuffix = getPhotoModeSuffix(det.classNum) + const confSuffix = det.confidence < 0.995 ? ` ${(det.confidence * 100).toFixed(0)}%` : '' + const label = `${name}${modeSuffix}${confSuffix}` + ctx.font = '11px sans-serif' - ctx.fillText(label, cx + 2, cy - 3) + const metrics = ctx.measureText(label) + const padX = 3 + const labelH = 14 + const labelW = metrics.width + padX * 2 + ctx.fillStyle = color + ctx.fillRect(cx, cy - labelH, labelW, labelH) + ctx.fillStyle = '#000' + ctx.fillText(label, cx + padX, cy - 3) if (det.combatReadiness === 1) { ctx.fillStyle = '#40c057' @@ -150,7 +208,8 @@ export default function CanvasEditor({ media, annotation, detections, onDetectio }, [draw]) const getTimeWindowDetections = (): Detection[] => { - if (media.mediaType !== 2) return [] + if (media.mediaType !== MediaType.Video) return [] + if (annotation) return [] const timeTicks = currentTime * 10_000_000 return annotations .filter(a => { @@ -332,7 +391,7 @@ export default function CanvasEditor({ media, annotation, detections, onDetectio }, [detections, selected, onDetectionsChange]) return ( -
+
) -} +}) + +export default CanvasEditor diff --git a/src/features/annotations/MediaList.tsx b/src/features/annotations/MediaList.tsx index 08a6a0e..b25ffdc 100644 --- a/src/features/annotations/MediaList.tsx +++ b/src/features/annotations/MediaList.tsx @@ -1,9 +1,11 @@ -import { useState, useEffect, useCallback } from 'react' +import { useState, useEffect, useCallback, useRef } from 'react' import { useTranslation } from 'react-i18next' +import { useDropzone } from 'react-dropzone' import { useFlight } from '../../components/FlightContext' import { api } from '../../api/client' import { useDebounce } from '../../hooks/useDebounce' import ConfirmDialog from '../../components/ConfirmDialog' +import { MediaType } from '../../types' import type { Media, PaginatedResponse, AnnotationListItem } from '../../types' interface Props { @@ -19,7 +21,7 @@ export default function MediaList({ selectedMedia, onSelect, onAnnotationsLoaded const [filter, setFilter] = useState('') const debouncedFilter = useDebounce(filter, 300) const [deleteId, setDeleteId] = useState(null) - const [dragging, setDragging] = useState(false) + const folderInputRef = useRef(null) const fetchMedia = useCallback(async () => { const params = new URLSearchParams({ pageSize: '1000' }) @@ -27,57 +29,124 @@ export default function MediaList({ selectedMedia, onSelect, onAnnotationsLoaded if (debouncedFilter) params.set('name', debouncedFilter) try { const res = await api.get>(`/api/annotations/media?${params}`) - setMedia(res.items) - } catch {} + setMedia(prev => { + // Keep local-only (blob URL) entries, merge with backend entries + const local = prev.filter(m => m.path.startsWith('blob:')) + return [...local, ...res.items] + }) + } catch { + // backend unavailable — keep local-only entries + } }, [selectedFlight, debouncedFilter]) useEffect(() => { fetchMedia() }, [fetchMedia]) + useEffect(() => { + return () => { + for (const m of media) if (m.path.startsWith('blob:')) URL.revokeObjectURL(m.path) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + const handleSelect = async (m: Media) => { onSelect(m) + if (m.path.startsWith('blob:')) { + onAnnotationsLoaded([]) + return + } try { const res = await api.get>( `/api/annotations/annotations?mediaId=${m.id}&pageSize=1000` ) onAnnotationsLoaded(res.items) - } catch {} + } catch { + onAnnotationsLoaded([]) + } } const handleDelete = async () => { if (!deleteId) return - await api.delete(`/api/annotations/media/${deleteId}`) + const target = media.find(m => m.id === deleteId) + if (target?.path.startsWith('blob:')) { + URL.revokeObjectURL(target.path) + setMedia(prev => prev.filter(m => m.id !== deleteId)) + setDeleteId(null) + return + } + try { await api.delete(`/api/annotations/media/${deleteId}`) } catch {} setDeleteId(null) fetchMedia() } - const handleDrop = async (e: React.DragEvent) => { - e.preventDefault() - setDragging(false) - if (!selectedFlight || !e.dataTransfer.files.length) return - const form = new FormData() - form.append('waypointId', '') - for (const file of e.dataTransfer.files) form.append('files', file) - await api.upload('/api/annotations/media/batch', form) - fetchMedia() - } + const uploadFiles = useCallback(async (files: File[] | FileList) => { + if (!files.length) return + const arr = Array.from(files) - const handleFileUpload = async (e: React.ChangeEvent) => { - if (!e.target.files?.length) return - const form = new FormData() - form.append('waypointId', '') - for (const file of e.target.files) form.append('files', file) - await api.upload('/api/annotations/media/batch', form) - fetchMedia() + // Try backend first + if (selectedFlight) { + try { + const form = new FormData() + form.append('waypointId', '') + for (const file of arr) form.append('files', file) + await api.upload('/api/annotations/media/batch', form) + fetchMedia() + return + } catch { + // fall through to local mode + } + } + + // Local mode: add blob URL entries to state + const videoExts = /\.(mp4|mov|webm|mkv|avi|m4v|ogg|ogv)$/i + const imageExts = /\.(jpe?g|png|webp|gif|bmp|tiff?)$/i + const accepted: File[] = [] + const rejected: string[] = [] + for (const file of arr) { + const isVideo = file.type.startsWith('video/') || videoExts.test(file.name) + const isImage = file.type.startsWith('image/') || imageExts.test(file.name) + if (isVideo || isImage) accepted.push(file) + else rejected.push(file.name) + } + if (rejected.length) { + alert(`Unsupported file type (video/image only):\n${rejected.join('\n')}`) + } + + const localItems: Media[] = accepted.map(file => { + const isVideo = file.type.startsWith('video/') || videoExts.test(file.name) + return { + id: `local-${crypto.randomUUID()}`, + name: file.name, + path: URL.createObjectURL(file), + mediaType: isVideo ? MediaType.Video : MediaType.Image, + mediaStatus: 0, + duration: null, + annotationCount: 0, + waypointId: null, + userId: 'local', + } + }) + setMedia(prev => [...localItems, ...prev]) + }, [selectedFlight, fetchMedia]) + + const { getRootProps, getInputProps, isDragActive } = useDropzone({ + onDrop: uploadFiles, + multiple: true, + noClick: true, + noKeyboard: true, + }) + + const handleFolderInput = (e: React.ChangeEvent) => { + if (e.target.files?.length) uploadFiles(e.target.files) e.target.value = '' } return (
{ e.preventDefault(); setDragging(true) }} - onDragLeave={() => setDragging(false)} - onDrop={handleDrop} + {...getRootProps({ + className: `flex-1 flex flex-col overflow-hidden ${isDragActive ? 'ring-2 ring-az-orange ring-inset' : ''}`, + })} > +
-
+
+ + +
- {media.map(m => ( + {media.filter(m => m.name.toLowerCase().includes(filter.toLowerCase())).map(m => (
handleSelect(m)} @@ -100,8 +196,8 @@ export default function MediaList({ selectedMedia, onSelect, onAnnotationsLoaded selectedMedia?.id === m.id ? 'bg-az-bg text-white' : '' } ${m.annotationCount > 0 ? 'bg-az-bg/50' : ''} text-az-text hover:bg-az-bg`} > - - {m.mediaType === 2 ? 'V' : 'P'} + + {m.mediaType === MediaType.Video ? 'V' : 'P'} {m.name} {m.duration && {m.duration}} diff --git a/src/features/annotations/VideoPlayer.tsx b/src/features/annotations/VideoPlayer.tsx index e525b29..6210bef 100644 --- a/src/features/annotations/VideoPlayer.tsx +++ b/src/features/annotations/VideoPlayer.tsx @@ -1,25 +1,44 @@ -import { useRef, useState, useCallback, useEffect } from 'react' -import { useTranslation } from 'react-i18next' -import { api } from '../../api/client' -import { getToken } from '../../api/client' +import { useRef, useState, useCallback, useEffect, forwardRef, useImperativeHandle } from 'react' +import { FaPlay, FaPause, FaStop, FaStepBackward, FaStepForward, FaVolumeMute, FaVolumeUp } from 'react-icons/fa' import type { Media } from '../../types' interface Props { media: Media onTimeUpdate: (time: number) => void - selectedClassNum: number + children?: React.ReactNode } -export default function VideoPlayer({ media, onTimeUpdate, selectedClassNum }: Props) { - const { t } = useTranslation() +const STEP_BTN_CLASS = 'w-9 h-8 flex items-center justify-center bg-az-bg rounded hover:bg-az-border text-az-text text-xs font-mono' +const ICON_BTN_CLASS = 'w-10 h-10 flex items-center justify-center bg-az-bg rounded hover:bg-az-border text-white' + +export interface VideoPlayerHandle { + seek: (seconds: number) => void + getVideoElement: () => HTMLVideoElement | null +} + +const VideoPlayer = forwardRef(function VideoPlayer({ media, onTimeUpdate, children }, ref) { const videoRef = useRef(null) + + useImperativeHandle(ref, () => ({ + seek(seconds: number) { + if (videoRef.current) { + videoRef.current.currentTime = seconds + setCurrentTime(seconds) + } + }, + getVideoElement() { + return videoRef.current + }, + })) + const [error, setError] = useState(null) const [playing, setPlaying] = useState(false) const [currentTime, setCurrentTime] = useState(0) const [duration, setDuration] = useState(0) const [muted, setMuted] = useState(false) - const token = getToken() - const videoUrl = `/api/annotations/media/${media.id}/file` + const videoUrl = media.path.startsWith('blob:') + ? media.path + : `/api/annotations/media/${media.id}/file` const stepFrames = useCallback((count: number) => { const video = videoRef.current @@ -64,48 +83,88 @@ export default function VideoPlayer({ media, onTimeUpdate, selectedClassNum }: P } return ( -
-