diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 00000000..79b59ddd
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,10 @@
+version: 2
+updates:
+ - package-ecosystem: "github-actions"
+ directory: "/.github/workflows"
+ schedule:
+ interval: "monthly"
+ groups:
+ actions:
+ patterns:
+ - '*'
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
new file mode 100644
index 00000000..2274700b
--- /dev/null
+++ b/.github/workflows/deploy.yml
@@ -0,0 +1,46 @@
+name: Deploy to GitHub Pages
+
+on:
+ push:
+ branches: ["main"]
+ workflow_dispatch:
+
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v7
+
+ - name: Set up Node
+ uses: actions/setup-node@v6
+ with:
+ node-version: 20
+ cache: 'npm'
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Build
+ run: npm run build
+
+ - name: Upload artifact
+ uses: actions/upload-pages-artifact@v5
+ with:
+ path: ./dist
+
+ deploy:
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ runs-on: ubuntu-latest
+ needs: build
+ steps:
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v5
diff --git a/.gitignore b/.gitignore
index 8d06f28a..6dbbe234 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,28 @@
-summary/
-compare/
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+
+.claude/
+CLAUDE.md
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..261eeb9e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
index cda456ed..0775162f 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,27 @@
# LSTPerformanceWeb
-`python make_lstpage.py`
+A static React Single Page Application for browsing LST performance plots generated by CI. Plots are stored as tarballs in archive repositories and decompressed client-side — no backend required.
-`sh make_html.sh`
+## Development
+
+```bash
+npm install
+npm run dev
+```
+
+## Build
+
+```bash
+npm run build
+```
+
+The output in `dist/` can be served from any static file host.
+
+## How it works
+
+The sidebar fetches directory listings from two GitHub archive repositories:
+
+- **lst-performance-plots-archive-2026** (`main` branch)
+- **TrackLooper-plots-archive** (`cmssw` branch)
+
+Selecting a run downloads and decompresses its tarball in the browser. Plots are displayed as PDFs in an organized grid with category/metric navigation. The current view is encoded in the URL for deep linking.
diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644
index 00000000..ef614d25
--- /dev/null
+++ b/eslint.config.js
@@ -0,0 +1,22 @@
+import js from '@eslint/js'
+import globals from 'globals'
+import reactHooks from 'eslint-plugin-react-hooks'
+import reactRefresh from 'eslint-plugin-react-refresh'
+import tseslint from 'typescript-eslint'
+import { defineConfig, globalIgnores } from 'eslint/config'
+
+export default defineConfig([
+ globalIgnores(['dist']),
+ {
+ files: ['**/*.{ts,tsx}'],
+ extends: [
+ js.configs.recommended,
+ tseslint.configs.recommended,
+ reactHooks.configs.flat.recommended,
+ reactRefresh.configs.vite,
+ ],
+ languageOptions: {
+ globals: globals.browser,
+ },
+ },
+])
diff --git a/index.html b/index.html
new file mode 100644
index 00000000..d63300d1
--- /dev/null
+++ b/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ LST Performance Plots
+
+
+
+
+
+
diff --git a/make_html.sh b/make_html.sh
deleted file mode 100644
index 373f295d..00000000
--- a/make_html.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-
-cd summary/
-rm -f .jobs.txt
-for File in $(ls *.md); do
- echo "/home/users/phchang/local/bin/pandoc -f markdown -t html -o ${File/.md/.html} ${File}" >> .jobs.txt
-done
-xargs.sh .jobs.txt
-rm -f .jobs.txt
-cd ../
-
-cd compare/
-rm -f .jobs.txt
-for File in $(ls *.md); do
- echo "/home/users/phchang/local/bin/pandoc -f markdown -t html -o ${File/.md/.html} ${File}" >> .jobs.txt
-done
-xargs.sh .jobs.txt
-rm -f .jobs.txt
-cd ../
diff --git a/make_lstpage.py b/make_lstpage.py
deleted file mode 100644
index 21421191..00000000
--- a/make_lstpage.py
+++ /dev/null
@@ -1,254 +0,0 @@
-#!/bin/env python
-
-import os
-import glob
-import sys
-
-#___________________________________________________________________________________________________
-def write_pages_v2(directory, objecttypes):
-
- os.system("mkdir -p {directory}".format(directory=directory))
-
- ##############################################################################################################################
-
- # eff
- pdgids = [0, 11, 13, 211, 321]
- sels = ["base", "loweta", "xtr", "vtr"]
- variables = [
- "pt",
- "ptzoom",
- "ptlow",
- "ptlowzoom",
- "ptmtv",
- "ptmtvzoom",
- "eta",
- "etazoom",
- "etacoarse",
- "etacoarsezoom",
- "phi",
- "phizoom",
- "phicoarse",
- "phicoarsezoom",
- "dxy",
- "dxycoarse",
- "dxycoarsezoom",
- "dz",
- "dzcoarse",
- "dzcoarsezoom",
- "vxy",
- "vxycoarse",
- "vxycoarsezoom",
- ]
- breakdowns = ["_breakdown"]
- charges = [0, -1, 1]
-
- plotdir = "../mtv"
- plot_width = 250
- plot_large_width = 600
-
- index_md = open("{directory}/index.md".format(directory=directory), "w")
- index_md.write("# LST Performance\n\n")
-
- ##############################################################################################################################
-
- metric = "eff"
- for objecttype in objecttypes:
- index_md.write("## {}\n\n".format(objecttype))
- index_md.write("### Efficiencies\n\n")
- for breakdown in breakdowns:
- for selection in sels:
- index_md.write("#### For {selectionstr}\n\n".format(selectionstr=get_selectionstr(selection)))
- for charge in charges:
- summary_file_name = "{objecttype}_{metric}_{selection}_{charge}".format(objecttype=objecttype,metric=metric, selection=selection, charge=charge)
- summary_markdown = open("{directory}/{summary_file_name}.md".format(directory=directory, summary_file_name=summary_file_name), "w")
- TOC = {}
- SectionID = 0
- page_name = "Summary Plots of LST {objecttype} Efficiency for {selectionstr} with Charge={chargestr}".format(objecttype=objecttype, selectionstr=get_selectionstr(selection), chargestr=get_chargestr(charge))
- index_md.write("[Charge={chargestr}]({summary_file_name}.html)\n\n".format(selectionstr=get_selectionstr(selection), chargestr=get_chargestr(charge), summary_file_name=summary_file_name))
- for pdgid in pdgids:
- SectionID += 1
- pdgidstr = get_pdgidstr(pdgid)
- SectionTitle = "{SectionID} {objecttype} {pdgidstr} {metricstr}".format(SectionID=SectionID, objecttype=objecttype, pdgidstr=pdgidstr, metricstr=get_metricstr(metric))
- summary_markdown.write("\n\n## {SectionTitle}\n\n [[back to top](#top)]\n\n".format(SectionTitle=SectionTitle, SectionID=SectionID))
- TOC["#{SectionID}".format(SectionID=SectionID)] = SectionTitle
- for variable in variables:
- name = "{objecttype}_{selection}_{pdgid}_{charge}_{metric}_{variable}".format(objecttype=objecttype, selection=selection, pdgid=pdgid, charge=charge, metric=metric, variable=variable)
- html = "{name}.html".format(name=name)
- md = "{name}.md".format(name=name)
- f = open("{directory}/{md}".format(directory=directory, md=md), "w")
- f.write("# {objecttype} {metricstr} vs. {variable}\n\n[[back to main](./)]\n\n".format(objecttype=objecttype, metricstr=get_metricstr(metric), variable=variable))
- f.write("\n\n")
- f.write("## Ratio\n\n[{{ width={plot_large_width}px }}]({plotdir}/var/{name}.pdf)\n\n".format(plotdir=plotdir, name=name, plot_large_width=plot_large_width))
- if len(breakdown) != 0:
- for i in range(5):
- f.write("## Denominator {i}\n\n[{{ width={plot_large_width}px }}]({plotdir}/den/{name}_den{i}.pdf)\n\n".format(plotdir=plotdir, name=name, plot_large_width=plot_large_width, i=i))
- f.write("## Numerator {i}\n\n[{{ width={plot_large_width}px }}]({plotdir}/num/{name}_num{i}.pdf)\n\n".format(plotdir=plotdir, name=name, plot_large_width=plot_large_width, i=i))
- for i in range(4):
- f.write("## Double Ratio {i}\n\n[{{ width={plot_large_width}px }}]({plotdir}/ratio/{name}_ratio{i}.pdf)\n\n".format(plotdir=plotdir, name=name, plot_large_width=plot_large_width, i=i))
- else:
- f.write("## Numerator\n\n[{{ width={plot_large_width}px }}]({plotdir}/num/{name}_num0.pdf)\n\n".format(plotdir=plotdir, name=name, plot_large_width=plot_large_width, i=i))
- f.close()
- summary_markdown.write("[{{ width={plot_width}px }}]({html})\n".format(plotdir=plotdir, name=name, plot_width=plot_width, html=html))
-
- summary_markdown.close()
-
- # Reopen and add TOC at the top
- tmp = open("{directory}/{summary_file_name}.md".format(directory=directory, summary_file_name=summary_file_name))
- lines = tmp.readlines()
- tmp.close()
-
- header_lines = []
- header_lines.append("[[back to main](./)]\n\n")
- header_lines.append("# {page_name}\n".format(page_name=page_name))
- header_lines.append("\n")
- for key in sorted(TOC.keys()):
- header_lines.append("[{SectionTitle}]({key}) ".format(SectionTitle=TOC[key], key=key))
-
- newlines = header_lines + ["\n\n"] + lines
-
- summary_markdown = open("{directory}/{summary_file_name}.md".format(directory=directory, summary_file_name=summary_file_name), "w")
- for line in newlines:
- summary_markdown.write(line)
-
- summary_markdown.close()
-
-
- ##############################################################################################################################
-
- recometrics = ["fakerate", "duplrate"]
-
- for metric in recometrics:
-
- index_md.write("### {metricstr}\n\n".format(metricstr=get_metricstr(metric)))
-
- variables = [
- "pt",
- "ptzoom",
- "ptlow",
- "ptlowzoom",
- "ptmtv",
- "ptmtvzoom",
- "eta",
- "etazoom",
- "etacoarse",
- "etacoarsezoom",
- "phi",
- "phizoom",
- "phicoarse",
- "phicoarsezoom",
- ]
-
- for breakdown in breakdowns:
- summary_file_name = "{metric}".format(metric=metric)
- summary_markdown = open("{directory}/{summary_file_name}.md".format(directory=directory, summary_file_name=summary_file_name), "w")
- TOC = {}
- SectionID = 0
- page_name = "Summary Plots of LST {metricstr}".format(metricstr=get_metricstr(metric))
- index_md.write("[{metricstr}]({summary_file_name}.html)\n\n".format(metricstr=get_metricstr(metric), summary_file_name=summary_file_name))
- for objecttype in objecttypes:
- SectionID += 1
- pdgidstr = get_pdgidstr(pdgid)
- SectionTitle = "{SectionID} {objecttype} {metricstr}".format(SectionID=SectionID, objecttype=objecttype, metricstr=get_metricstr(metric))
- summary_markdown.write("\n\n## {SectionTitle}\n\n [[back to top](#top)]\n\n".format(SectionTitle=SectionTitle, SectionID=SectionID))
- TOC["#{SectionID}".format(SectionID=SectionID)] = SectionTitle
- for variable in variables:
- name = "{objecttype}_{metric}_{variable}".format(objecttype=objecttype, metric=metric, variable=variable)
- html = "{name}.html".format(name=name)
- md = "{name}.md".format(name=name)
- f = open("{directory}/{md}".format(directory=directory, md=md), "w")
- f.write("# {objecttype} {metricstr} vs. {variable}\n\n[[back to main](./)]\n\n".format(objecttype=objecttype, metricstr=get_metricstr(metric), variable=variable))
- f.write("\n\n")
- f.write("## Ratio\n\n[{{ width={plot_large_width}px }}]({plotdir}/var/{name}.pdf)\n\n".format(plotdir=plotdir, name=name, plot_large_width=plot_large_width))
- if len(breakdown) != 0:
- for i in range(5):
- f.write("## Denominator {i}\n\n[{{ width={plot_large_width}px }}]({plotdir}/den/{name}_den{i}.pdf)\n\n".format(plotdir=plotdir, name=name, plot_large_width=plot_large_width, i=i))
- f.write("## Numerator {i}\n\n[{{ width={plot_large_width}px }}]({plotdir}/num/{name}_num{i}.pdf)\n\n".format(plotdir=plotdir, name=name, plot_large_width=plot_large_width, i=i))
- for i in range(4):
- f.write("## Double Ratio {i}\n\n[{{ width={plot_large_width}px }}]({plotdir}/ratio/{name}_ratio{i}.pdf)\n\n".format(plotdir=plotdir, name=name, plot_large_width=plot_large_width, i=i))
- else:
- f.write("## Numerator\n\n[{{ width={plot_large_width}px }}]({plotdir}/num/{name}_num0.pdf)\n\n".format(plotdir=plotdir, name=name, plot_large_width=plot_large_width, i=i))
- f.close()
- summary_markdown.write("[{{ width={plot_width}px }}]({html})\n".format(plotdir=plotdir, name=name, plot_width=plot_width, html=html))
-
- summary_markdown.close()
-
- # Reopen and add TOC at the top
- tmp = open("{directory}/{summary_file_name}.md".format(directory=directory, summary_file_name=summary_file_name))
- lines = tmp.readlines()
- tmp.close()
-
- header_lines = []
- header_lines.append("[[back to main](./)]\n\n")
- header_lines.append("# {page_name}\n".format(page_name=page_name))
- header_lines.append("\n")
- for key in sorted(TOC.keys()):
- header_lines.append("[{SectionTitle}]({key}) ".format(SectionTitle=TOC[key], key=key))
-
- newlines = header_lines + ["\n\n"] + lines
-
- summary_markdown = open("{directory}/{summary_file_name}.md".format(directory=directory, summary_file_name=summary_file_name), "w")
- for line in newlines:
- summary_markdown.write(line)
-
- summary_markdown.close()
-
-#___________________________________________________________________________________________________
-def get_pdgidstr(pdgid):
- if pdgid == 0:
- pdgidstr = "All"
- if pdgid == 11:
- pdgidstr = "Electron"
- if pdgid == 13:
- pdgidstr = "Muon"
- if pdgid == 211:
- pdgidstr = "Pion"
- if pdgid == 321:
- pdgidstr = "Kaon"
- return pdgidstr
-
-#___________________________________________________________________________________________________
-def get_chargestr(charge):
- if charge == 0:
- chargestr = "Both"
- if charge == 1:
- chargestr = "Positive"
- if charge == -1:
- chargestr = "Negative"
- return chargestr
-
-#___________________________________________________________________________________________________
-def get_selectionstr(selection):
- if selection == "base":
- selectionstr = "|eta| < 4.5"
- if selection == "loweta":
- selectionstr = "|eta| < 2.4"
- if selection == "xtr":
- selectionstr = "1.1 < |eta| < 2.7"
- if selection == "vtr":
- selectionstr = "not (1.1 < |eta| < 2.7) and |eta| < 2.4"
- return selectionstr
-
-#___________________________________________________________________________________________________
-def get_metricstr(metric):
- if metric == "eff":
- metricstr = "Efficiency"
- if metric == "fakerate":
- metricstr = "Fake Rate"
- if metric == "duplrate":
- metricstr = "Duplicate Rate"
- return metricstr
-
-#___________________________________________________________________________________________________
-def write_footnote(ff):
- ff.write("""
-``` {=html}
-
-```
-""")
-
-if __name__ == "__main__":
-
- write_pages_v2("summary", ["TC", "pT5_lower", "pT3_lower", "T5_lower"])
- write_pages_v2("compare", ["TC", "pT5", "pT3", "T5", "pLS", "pT5_lower", "pT3_lower", "T5_lower"])
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 00000000..36819f50
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,3134 @@
+{
+ "name": "lst-performance-web",
+ "version": "0.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "lst-performance-web",
+ "version": "0.0.0",
+ "dependencies": {
+ "fflate": "^0.8.2",
+ "js-untar": "^2.0.0",
+ "react": "^19.2.5",
+ "react-dom": "^19.2.5",
+ "react-pdf": "^10.4.1"
+ },
+ "devDependencies": {
+ "@eslint/js": "^10.0.1",
+ "@types/js-untar": "^2.0.0",
+ "@types/node": "^24.12.2",
+ "@types/react": "^19.2.14",
+ "@types/react-dom": "^19.2.3",
+ "@vitejs/plugin-react": "^6.0.1",
+ "eslint": "^10.2.1",
+ "eslint-plugin-react-hooks": "^7.1.1",
+ "eslint-plugin-react-refresh": "^0.5.2",
+ "globals": "^17.5.0",
+ "typescript": "~6.0.2",
+ "typescript-eslint": "^8.58.2",
+ "vite": "^8.0.10"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
+ "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz",
+ "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz",
+ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.7",
+ "@babel/helper-compilation-targets": "^7.29.7",
+ "@babel/helper-module-transforms": "^7.29.7",
+ "@babel/helpers": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/template": "^7.29.7",
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "@jridgewell/remapping": "^2.3.5",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
+ "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
+ "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.29.7",
+ "@babel/helper-validator-option": "^7.29.7",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
+ "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
+ "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
+ "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "@babel/traverse": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+ "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+ "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
+ "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz",
+ "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
+ "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.29.7"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
+ "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz",
+ "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.7",
+ "@babel/helper-globals": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
+ "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@emnapi/core": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz",
+ "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.2.2",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz",
+ "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz",
+ "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.9.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
+ "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.23.5",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz",
+ "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^3.0.5",
+ "debug": "^4.3.1",
+ "minimatch": "^10.2.4"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ }
+ },
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz",
+ "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^1.2.1"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz",
+ "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz",
+ "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "eslint": "^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "eslint": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz",
+ "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz",
+ "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^1.2.1",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ }
+ },
+ "node_modules/@humanfs/core": {
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
+ "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/types": "^0.15.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.8",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz",
+ "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.2",
+ "@humanfs/types": "^0.15.0",
+ "@humanwhocodes/retry": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/types": {
+ "version": "0.15.0",
+ "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz",
+ "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@napi-rs/canvas": {
+ "version": "0.1.100",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.100.tgz",
+ "integrity": "sha512-xglYA6q3XO5P3BNJYxVZ1IV7DLVjp1Py6nwag88YntrS+3vKHyYcMqXVS4ZztJmwz2uGvz1FWhI/4LgbR5uQDA==",
+ "license": "MIT",
+ "optional": true,
+ "workspaces": [
+ "e2e/*"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ },
+ "optionalDependencies": {
+ "@napi-rs/canvas-android-arm64": "0.1.100",
+ "@napi-rs/canvas-darwin-arm64": "0.1.100",
+ "@napi-rs/canvas-darwin-x64": "0.1.100",
+ "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.100",
+ "@napi-rs/canvas-linux-arm64-gnu": "0.1.100",
+ "@napi-rs/canvas-linux-arm64-musl": "0.1.100",
+ "@napi-rs/canvas-linux-riscv64-gnu": "0.1.100",
+ "@napi-rs/canvas-linux-x64-gnu": "0.1.100",
+ "@napi-rs/canvas-linux-x64-musl": "0.1.100",
+ "@napi-rs/canvas-win32-arm64-msvc": "0.1.100",
+ "@napi-rs/canvas-win32-x64-msvc": "0.1.100"
+ }
+ },
+ "node_modules/@napi-rs/canvas-android-arm64": {
+ "version": "0.1.100",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.100.tgz",
+ "integrity": "sha512-hjhCKhntPv9+t4ckHymdx0phYNcVW+GKQR6Lzw2zE+pOVjOplSmtx9nNNknTjbEDLcuLZqA1y8ufKg1XfgftzQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/@napi-rs/canvas-darwin-arm64": {
+ "version": "0.1.100",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.100.tgz",
+ "integrity": "sha512-2PcswRaC7Ly645DGt88///zuFDhJxJYdKAs1uU3mfk1atYkXufgcgLfBpk6Tm12nCQBaNt1wpybuPZ4qOhTo8A==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/@napi-rs/canvas-darwin-x64": {
+ "version": "0.1.100",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.100.tgz",
+ "integrity": "sha512-ePNZtj7pNIva/siZMg+HmbeozkIjqUIYdoymH8HaA3qK7LfzFN4WMBM8G6HQ9ZC+H3+Dnn5pqtiXpgLykaPOhw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": {
+ "version": "0.1.100",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.100.tgz",
+ "integrity": "sha512-d5cDB48oWFGU8/XPhUOFAlySgb/VAu7D+s8fi55K1Pcfg8aPplHWqMgibhVLU8ky7Pyg/fuiVLz4Nf3JrSTuUA==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/@napi-rs/canvas-linux-arm64-gnu": {
+ "version": "0.1.100",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.100.tgz",
+ "integrity": "sha512-rDxgxRu69RvDlX/bh9o22DxLsGr8EqsNgotL9+RwQE1S0b0cqeatqsw6aW45mukm0B42DIAaAacKaYQ8cqS1nw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/@napi-rs/canvas-linux-arm64-musl": {
+ "version": "0.1.100",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.100.tgz",
+ "integrity": "sha512-K3mDW66N+xT2/V439u1alFANiBUjdEx2gLiNYnCmUsva5jZMxWTjafBYwTzYK+EMFMHrUoabuU+T1BIP5CgbYQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/@napi-rs/canvas-linux-riscv64-gnu": {
+ "version": "0.1.100",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.100.tgz",
+ "integrity": "sha512-mooqUBTIsccZpnoQC4NgrC1v6C1vof39etLNMnBwCY+p0gajWJvAHLGQ6g/gGyS5YrpDW+GefSN4+Cvcr08UWw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/@napi-rs/canvas-linux-x64-gnu": {
+ "version": "0.1.100",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.100.tgz",
+ "integrity": "sha512-1eCvkDCazm7FFhsT7DfGOdSaHgZVK3bt/dSBl5EWHOWmnz+I7j8tPseJqqD81NF+MH21jKUK4wQSDjN0mdhnTg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/@napi-rs/canvas-linux-x64-musl": {
+ "version": "0.1.100",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.100.tgz",
+ "integrity": "sha512-20arT6lnI19S68qNlii73TSEDbECNgzMz2EpldC1V3mZFuRkeujXkcebRk0LRJe9SEUAooYiLokfMViY8IX7yA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/@napi-rs/canvas-win32-arm64-msvc": {
+ "version": "0.1.100",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-arm64-msvc/-/canvas-win32-arm64-msvc-0.1.100.tgz",
+ "integrity": "sha512-DZFFT1wIAg37LJw37yhMRFfjATd3vTQzjZ1Yki8u2vhO6Hi5VE6BVaGQ1aaDu7xb4iMErz+9EOwjpS7xcxFeBw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/@napi-rs/canvas-win32-x64-msvc": {
+ "version": "0.1.100",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.100.tgz",
+ "integrity": "sha512-MyT1j3mHC2+Lu4pBi9mKyMJhtP6U7k7EldY7sj/uS5gJA65gTXt8MefJQXLJo5d/vZbuWmfxzkEUNc/urV3pHA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz",
+ "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@tybys/wasm-util": "^0.10.3"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ },
+ "peerDependencies": {
+ "@emnapi/core": "^1.7.1",
+ "@emnapi/runtime": "^1.7.1"
+ }
+ },
+ "node_modules/@oxc-project/types": {
+ "version": "0.137.0",
+ "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.137.0.tgz",
+ "integrity": "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/Boshen"
+ }
+ },
+ "node_modules/@rolldown/binding-android-arm64": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.3.tgz",
+ "integrity": "sha512-DT6Z3PhvioeHMvxo+xHc3KtqggrI7CCTXCmC2h/5zUlp5jVitv7XEy+9q5/7v8IolhlioawpMo8Kg0EEBy7J0g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-arm64": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.3.tgz",
+ "integrity": "sha512-0NwgwsjM7LrsuVnXMK3koTpagBNOhloc/BNjKqZjv4V5zI5r13qx69uVhRx+o5Z0yy4Hzq+lpy7TAgUG/ocvrw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-x64": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.3.tgz",
+ "integrity": "sha512-YtiBp4disu6V560loT6PjMdiRaWmVvDNrUunAalbiFx2ggeJwxdAsgZMcoGP17uyAsTwAj5V1niksxlHnVQ1Sw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-freebsd-x64": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.3.tgz",
+ "integrity": "sha512-yD3EkEdXk2LypPxnf/kSZHirarsI8gcPzc62SukhR9VJTyvV+F9Q/GxWNuCojc7sXyuVC4DxRGhdDK4X8VSsbw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.3.tgz",
+ "integrity": "sha512-c+8vieQbsD7HNAHKIA34w0GJ9FedFFuJGD+7E6vz7Q3uqAIugL5p45fhlsj4UaAsHpcmlqugBWMhA0/j7o0sIg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-gnu": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.3.tgz",
+ "integrity": "sha512-50jD0uUwLvur7Zz9LHz17kaAdTPjn5wN93hEgjvmYFRZwiR7ZJYovTd5ipyWJDAnXKvZ+wgc+/Ika6dwSF5OcA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-musl": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.3.tgz",
+ "integrity": "sha512-BO9+oPL8K9poZJBfYPsXNtYjPE5uM3qeehT3aFcW4LITOl+iSqhp0abzjR2nWBUNjIZeKXjAEWBZ64WjNoHd6w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-ppc64-gnu": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.3.tgz",
+ "integrity": "sha512-f3VpLB1vQ0Eo6ecr/6cekLnvYMFF4YBFoVGkfkvPLq1bAkbAwHYQPZKoAmG6OJyTcxxoC+AvezGx/S1obNC0Mw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-s390x-gnu": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.3.tgz",
+ "integrity": "sha512-AmurZ26Pqx/RI9N1gzEOCklkKXl927yjfXWUUS0O7Puh8ARM/Ob8qfrD3qnWksScdw6cSrW5PSHE9DyLu7+PtA==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-gnu": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.3.tgz",
+ "integrity": "sha512-JJpqs8bRGITDOdbkNKnlojzBabbOHrqjSvDr0IVsZObE1lBcPjxItUEY9eWIDbxaJ3cGrXPWGfGkIxFijg/URg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-musl": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.3.tgz",
+ "integrity": "sha512-rSJcdjPxzA/by/6/rYs+v+bXU7UjvnbUWz8MJb6kh6+knqB1dCrtHg0uu7C/4haqJvqdkYHQ5IGn+tCH9GLW/g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-openharmony-arm64": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.3.tgz",
+ "integrity": "sha512-hQ3/PYkDJICgevvyNcVrihVeqq7k1Pp3VZ9lY+dauAYUJKO+auqApvANhvR1An9BhmqYKvW2Mu1F9u4DXSMLxQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-wasm32-wasi": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.3.tgz",
+ "integrity": "sha512-Elcv/BtML9lXrV6JuKITc/grN2kYV9gjsQpW8Jfw4ioK0TOkjBjye0nnyqQNy9STNaI20lXNaQBRrD5gSgR0Yg==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "1.11.1",
+ "@emnapi/runtime": "1.11.1",
+ "@napi-rs/wasm-runtime": "^1.1.6"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-arm64-msvc": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.3.tgz",
+ "integrity": "sha512-2DrEfhluH9yhiaFApmsjsjwrSYbNcY1oFTzYSP1a535jDbV98zCFanA/96TBUd0iDFcxGmw9QRExwGCXz3U+/g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-x64-msvc": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.3.tgz",
+ "integrity": "sha512-OL4OMk7UPXOeVGGd3qo5zJyPIljf4AFgk5QAkPPS+OoLuOOozhuaQGC18MxVTnw/06q93gShAJzlwnSCY9YtqA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
+ "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.10.3",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz",
+ "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@types/esrecurse": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz",
+ "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
+ "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/js-untar": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@types/js-untar/-/js-untar-2.0.0.tgz",
+ "integrity": "sha512-rid45mAPcbPWlqLIyuIqZYE1Iyy9b7DigqYJl2uG4XOJIUmD21OMYlRyVJ5FbQjtPCmYpnRCYp6JAHiasnYymg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "24.13.2",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.2.tgz",
+ "integrity": "sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~7.18.0"
+ }
+ },
+ "node_modules/@types/react": {
+ "version": "19.2.17",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz",
+ "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.2.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^19.2.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.62.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.62.1.tgz",
+ "integrity": "sha512-4EQM77WgVNxj7OkL/5b/D/xZsw00G577+UriYTC7JF5opcF3T2AuoeY7ueLaZgSVjSgCS6yOAJB5bRGLPSJUzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.12.2",
+ "@typescript-eslint/scope-manager": "8.62.1",
+ "@typescript-eslint/type-utils": "8.62.1",
+ "@typescript-eslint/utils": "8.62.1",
+ "@typescript-eslint/visitor-keys": "8.62.1",
+ "ignore": "^7.0.5",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.62.1",
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+ "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.62.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.62.1.tgz",
+ "integrity": "sha512-sPhE4iHuJDSvoAiec+Ro8JyXw8f0ql13HFR82P99nCm9GwTEKG0KYLvDe6REk8BCXuit6vJAv/Yxg5ABaNS2rA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.62.1",
+ "@typescript-eslint/types": "8.62.1",
+ "@typescript-eslint/typescript-estree": "8.62.1",
+ "@typescript-eslint/visitor-keys": "8.62.1",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/project-service": {
+ "version": "8.62.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.62.1.tgz",
+ "integrity": "sha512-yQ3RgY5RkSBpsNS1Bx/JQEcA24FOSdfGktoyprAr5u18390UQdtVcfnEv4nIrIshNnavlVyZBKxQwT1fIAE6cg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.62.1",
+ "@typescript-eslint/types": "^8.62.1",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.62.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.62.1.tgz",
+ "integrity": "sha512-r4d249KbQ1SFdpeStvob8Ih6aPPIzfqllPVOtvhve6ZcpuVcYo5/7zUWckKpHE7StASX4kTKZTLf0WQm/wPkcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.62.1",
+ "@typescript-eslint/visitor-keys": "8.62.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.62.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.62.1.tgz",
+ "integrity": "sha512-xadytJqX9vJVQ2fdQjkcIVigwaOJNWkpjdLt6cEQ+xPnrI1fkp+/jZE/I97k9KUjqtpd25i0HeyZf3T6dutv2g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.62.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.62.1.tgz",
+ "integrity": "sha512-aXM5xlqXiTxPibXB93cLAURfT3rlizf7uMXISCXy66Isr/9hISJx3yDsKl0L7lKa51b8JpFuNKby0/O0pEm9jg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.62.1",
+ "@typescript-eslint/typescript-estree": "8.62.1",
+ "@typescript-eslint/utils": "8.62.1",
+ "debug": "^4.4.3",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.62.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.62.1.tgz",
+ "integrity": "sha512-ooCzJFaf+Hg+uG6fA3NRFGuFjlfNlDhBthbv4ZPU/0elCAFUfnyXUvf/WOpHz/jYwSmvU2GkR2LtyUfy1AxZ1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.62.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.62.1.tgz",
+ "integrity": "sha512-xMcW9oP9u7fAMXYs9A65CVmtLQe2r//oXINHfi8HV+oiqhih17sbLdhXr4540YWlgpDKQdY854OL5ZrdCiQsAA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.62.1",
+ "@typescript-eslint/tsconfig-utils": "8.62.1",
+ "@typescript-eslint/types": "8.62.1",
+ "@typescript-eslint/visitor-keys": "8.62.1",
+ "debug": "^4.4.3",
+ "minimatch": "^10.2.2",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
+ "version": "7.8.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.62.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.62.1.tgz",
+ "integrity": "sha512-sHtbPfuKNZCG+ih8SyjjucqRntSVmp8XgL5u6o9mAhiSn8ds5o/M/XdM0abweme2Tln3szOstOrZ9OXitvPh0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.9.1",
+ "@typescript-eslint/scope-manager": "8.62.1",
+ "@typescript-eslint/types": "8.62.1",
+ "@typescript-eslint/typescript-estree": "8.62.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.62.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.62.1.tgz",
+ "integrity": "sha512-4g3BLxfdTMy8iZG0MaBkadnlRrCJ74cQiFbyEVMrkwIoqdyaXXQM22cotDvrl4x28wgIZ9rEJRoM+mmhSJpJ1g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.62.1",
+ "eslint-visitor-keys": "^5.0.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.3.tgz",
+ "integrity": "sha512-vmFvco5/QuC2f9Oj+wTk0+9XeDFkHxSamwZKYc7MxYwKICfvUvlMhqKI0VuICPltGqh1neqBKDvO4kes1ya8vg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rolldown/pluginutils": "^1.0.1"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "peerDependencies": {
+ "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0",
+ "babel-plugin-react-compiler": "^1.0.0",
+ "vite": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@rolldown/plugin-babel": {
+ "optional": true
+ },
+ "babel-plugin-react-compiler": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.17.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz",
+ "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.15.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz",
+ "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.10.40",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.40.tgz",
+ "integrity": "sha512-BSSLZ9/Cjjv7Gtj5B68ZzXcXUg8iOf3fme+FCuh8rC/Go+Kmh8cox7M3A8dolou16s64QjLPOSdngh7GxXvkSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "5.0.7",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
+ "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.28.4",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.4.tgz",
+ "integrity": "sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "baseline-browser-mapping": "^2.10.38",
+ "caniuse-lite": "^1.0.30001799",
+ "electron-to-chromium": "^1.5.376",
+ "node-releases": "^2.0.48",
+ "update-browserslist-db": "^1.2.3"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001799",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz",
+ "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "devOptional": true,
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/dequal": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.381",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.381.tgz",
+ "integrity": "sha512-n9Wa6yB+vDsGuA8AKbl/0z7HbvWqt5jxIdvr1IUicd0ryPrk7/xzwqLv8D9AbbvZ6avVNtXYLTfmgFHkwkyelg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.6.0.tgz",
+ "integrity": "sha512-6lVbcqSodALYo+4ELD0heG6lFiFxnLMuLkiMi2qV8LMp54N8tE8FT1GMH+ev4Ti00nFjNze2+Su6DsV5OQW3Dg==",
+ "dev": true,
+ "license": "MIT",
+ "workspaces": [
+ "packages/*"
+ ],
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.8.0",
+ "@eslint-community/regexpp": "^4.12.2",
+ "@eslint/config-array": "^0.23.5",
+ "@eslint/config-helpers": "^0.6.0",
+ "@eslint/core": "^1.2.1",
+ "@eslint/plugin-kit": "^0.7.2",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "ajv": "^6.14.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^9.1.2",
+ "eslint-visitor-keys": "^5.0.1",
+ "espree": "^11.2.0",
+ "esquery": "^1.7.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "minimatch": "^10.2.4",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-plugin-react-hooks": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz",
+ "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.24.4",
+ "@babel/parser": "^7.24.4",
+ "hermes-parser": "^0.25.1",
+ "zod": "^3.25.0 || ^4.0.0",
+ "zod-validation-error": "^3.5.0 || ^4.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-react-refresh": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.3.tgz",
+ "integrity": "sha512-5EMmLCV98Pi4o/f/3DP/v/tNqLHMIc9I8LKClNDWhZ9JTho89/kQcitCXQBMG7sAfVRK0Ie3T2EDOzp1YXYiVA==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "eslint": "^9 || ^10"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "9.1.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz",
+ "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@types/esrecurse": "^4.3.1",
+ "@types/estree": "^1.0.8",
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree": {
+ "version": "11.2.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz",
+ "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.16.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^5.0.1"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
+ "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/fflate": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz",
+ "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==",
+ "license": "MIT"
+ },
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
+ "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "17.7.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-17.7.0.tgz",
+ "integrity": "sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/hermes-estree": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
+ "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/hermes-parser": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz",
+ "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hermes-estree": "0.25.1"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "license": "MIT"
+ },
+ "node_modules/js-untar": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/js-untar/-/js-untar-2.0.0.tgz",
+ "integrity": "sha512-7CsDLrYQMbLxDt2zl9uKaPZSdmJMvGGQ7wo9hoB3J+z/VcO2w63bXFgHVnjF1+S9wD3zAu8FBVj7EYWjTQ3Z7g==",
+ "license": "MIT"
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/lightningcss": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
+ "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-android-arm64": "1.32.0",
+ "lightningcss-darwin-arm64": "1.32.0",
+ "lightningcss-darwin-x64": "1.32.0",
+ "lightningcss-freebsd-x64": "1.32.0",
+ "lightningcss-linux-arm-gnueabihf": "1.32.0",
+ "lightningcss-linux-arm64-gnu": "1.32.0",
+ "lightningcss-linux-arm64-musl": "1.32.0",
+ "lightningcss-linux-x64-gnu": "1.32.0",
+ "lightningcss-linux-x64-musl": "1.32.0",
+ "lightningcss-win32-arm64-msvc": "1.32.0",
+ "lightningcss-win32-x64-msvc": "1.32.0"
+ }
+ },
+ "node_modules/lightningcss-android-arm64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz",
+ "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-arm64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz",
+ "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-x64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz",
+ "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-freebsd-x64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz",
+ "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm-gnueabihf": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz",
+ "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-gnu": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz",
+ "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-musl": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz",
+ "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-gnu": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz",
+ "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz",
+ "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz",
+ "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz",
+ "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/make-cancellable-promise": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/make-cancellable-promise/-/make-cancellable-promise-2.0.0.tgz",
+ "integrity": "sha512-3SEQqTpV9oqVsIWqAcmDuaNeo7yBO3tqPtqGRcKkEo0lrzD3wqbKG9mkxO65KoOgXqj+zH2phJ2LiAsdzlogSw==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/wojtekmaj/make-cancellable-promise?sponsor=1"
+ }
+ },
+ "node_modules/make-event-props": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/make-event-props/-/make-event-props-2.0.0.tgz",
+ "integrity": "sha512-G/hncXrl4Qt7mauJEXSg3AcdYzmpkIITTNl5I+rH9sog5Yw0kK6vseJjCaPfOXqOqQuPUP89Rkhfz5kPS8ijtw==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/wojtekmaj/make-event-props?sponsor=1"
+ }
+ },
+ "node_modules/merge-refs": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-refs/-/merge-refs-2.0.0.tgz",
+ "integrity": "sha512-3+B21mYK2IqUWnd2EivABLT7ueDhb0b8/dGK8LoFQPrU61YITeCMn14F7y7qZafWNZhUEKb24cJdiT5Wxs3prg==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/wojtekmaj/merge-refs?sponsor=1"
+ },
+ "peerDependencies": {
+ "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "10.2.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.5"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.15",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz",
+ "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.50",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.50.tgz",
+ "integrity": "sha512-J6l92tKHX6w8Jy5nO1Vuc01NoIiRGi/d6qBKVxh+IQ8Cr3b6HbVNfKiF8ZpFKufTwpwxMmce2W3iQZ861ZRyTg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pdfjs-dist": {
+ "version": "5.4.296",
+ "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.4.296.tgz",
+ "integrity": "sha512-DlOzet0HO7OEnmUmB6wWGJrrdvbyJKftI1bhMitK7O2N8W2gc757yyYBbINy9IDafXAV9wmKr9t7xsTaNKRG5Q==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=20.16.0 || >=22.3.0"
+ },
+ "optionalDependencies": {
+ "@napi-rs/canvas": "^0.1.80"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.16",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz",
+ "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.12",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/react": {
+ "version": "19.2.7",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz",
+ "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.2.7",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz",
+ "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.27.0"
+ },
+ "peerDependencies": {
+ "react": "^19.2.7"
+ }
+ },
+ "node_modules/react-pdf": {
+ "version": "10.4.1",
+ "resolved": "https://registry.npmjs.org/react-pdf/-/react-pdf-10.4.1.tgz",
+ "integrity": "sha512-kS/35staVCBqS29verTQJQZXw7RfsRCPO3fdJoW1KXylcv7A9dw6DZ3vJXC2w+bIBgLw5FN4pOFvKSQtkQhPfA==",
+ "license": "MIT",
+ "dependencies": {
+ "clsx": "^2.0.0",
+ "dequal": "^2.0.3",
+ "make-cancellable-promise": "^2.0.0",
+ "make-event-props": "^2.0.0",
+ "merge-refs": "^2.0.0",
+ "pdfjs-dist": "5.4.296",
+ "tiny-invariant": "^1.0.0",
+ "warning": "^4.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/wojtekmaj/react-pdf?sponsor=1"
+ },
+ "peerDependencies": {
+ "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/rolldown": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.3.tgz",
+ "integrity": "sha512-1F1eEtUBtFvcGm1HQ9TiUIUHPQG7mSAODrhIzjxoUEFuo8OcbrGLiVLkevNgj84TE4lnHvnumwFjhJO5Eu135g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@oxc-project/types": "=0.137.0",
+ "@rolldown/pluginutils": "^1.0.0"
+ },
+ "bin": {
+ "rolldown": "bin/cli.mjs"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "optionalDependencies": {
+ "@rolldown/binding-android-arm64": "1.1.3",
+ "@rolldown/binding-darwin-arm64": "1.1.3",
+ "@rolldown/binding-darwin-x64": "1.1.3",
+ "@rolldown/binding-freebsd-x64": "1.1.3",
+ "@rolldown/binding-linux-arm-gnueabihf": "1.1.3",
+ "@rolldown/binding-linux-arm64-gnu": "1.1.3",
+ "@rolldown/binding-linux-arm64-musl": "1.1.3",
+ "@rolldown/binding-linux-ppc64-gnu": "1.1.3",
+ "@rolldown/binding-linux-s390x-gnu": "1.1.3",
+ "@rolldown/binding-linux-x64-gnu": "1.1.3",
+ "@rolldown/binding-linux-x64-musl": "1.1.3",
+ "@rolldown/binding-openharmony-arm64": "1.1.3",
+ "@rolldown/binding-wasm32-wasi": "1.1.3",
+ "@rolldown/binding-win32-arm64-msvc": "1.1.3",
+ "@rolldown/binding-win32-x64-msvc": "1.1.3"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
+ "license": "MIT"
+ },
+ "node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/tiny-invariant": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
+ "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
+ "license": "MIT"
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.17",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
+ "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/ts-api-utils": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz",
+ "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "dev": true,
+ "license": "0BSD",
+ "optional": true
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
+ "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/typescript-eslint": {
+ "version": "8.62.1",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.62.1.tgz",
+ "integrity": "sha512-vymnnM5g0AKQDSAyfP12nMIBvgwgA42syg74kkuZ4x1VuTzwQKwc5h9rGxeShCjny5o+zWAb6OEoz7XLgrIkIw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/eslint-plugin": "8.62.1",
+ "@typescript-eslint/parser": "8.62.1",
+ "@typescript-eslint/typescript-estree": "8.62.1",
+ "@typescript-eslint/utils": "8.62.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "7.18.2",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
+ "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/vite": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.0.tgz",
+ "integrity": "sha512-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lightningcss": "^1.32.0",
+ "picomatch": "^4.0.4",
+ "postcss": "^8.5.15",
+ "rolldown": "~1.1.2",
+ "tinyglobby": "^0.2.17"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "@vitejs/devtools": "^0.3.0",
+ "esbuild": "^0.27.0 || ^0.28.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "@vitejs/devtools": {
+ "optional": true
+ },
+ "esbuild": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/warning": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+ "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.0.0"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/zod": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz",
+ "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/zod-validation-error": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz",
+ "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.0 || ^4.0.0"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..36f0fe36
--- /dev/null
+++ b/package.json
@@ -0,0 +1,34 @@
+{
+ "name": "lst-performance-web",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc -b && vite build",
+ "lint": "eslint .",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "fflate": "^0.8.2",
+ "js-untar": "^2.0.0",
+ "react": "^19.2.5",
+ "react-dom": "^19.2.5",
+ "react-pdf": "^10.4.1"
+ },
+ "devDependencies": {
+ "@eslint/js": "^10.0.1",
+ "@types/js-untar": "^2.0.0",
+ "@types/node": "^24.12.2",
+ "@types/react": "^19.2.14",
+ "@types/react-dom": "^19.2.3",
+ "@vitejs/plugin-react": "^6.0.1",
+ "eslint": "^10.2.1",
+ "eslint-plugin-react-hooks": "^7.1.1",
+ "eslint-plugin-react-refresh": "^0.5.2",
+ "globals": "^17.5.0",
+ "typescript": "~6.0.2",
+ "typescript-eslint": "^8.58.2",
+ "vite": "^8.0.10"
+ }
+}
diff --git a/public/logo.png b/public/logo.png
new file mode 100644
index 00000000..a2cf0c45
Binary files /dev/null and b/public/logo.png differ
diff --git a/src/App.css b/src/App.css
new file mode 100644
index 00000000..07f0cc60
--- /dev/null
+++ b/src/App.css
@@ -0,0 +1,513 @@
+:root {
+ --header-height: 60px;
+ --primary-color: #24292e;
+ --accent-color: #0366d6;
+ --bg-color: #f6f8fa;
+ --border-color: #e1e4e8;
+ --text-color: #24292e;
+}
+
+* {
+ box-sizing: border-box;
+}
+
+body, html, #root {
+ height: 100%;
+ margin: 0;
+ padding: 0;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
+ background-color: var(--bg-color);
+ color: var(--text-color);
+}
+
+.app-container {
+ display: flex;
+ flex-direction: column;
+ height: 100vh;
+}
+
+.app-header {
+ height: var(--header-height);
+ background-color: var(--primary-color);
+ color: white;
+ display: flex;
+ align-items: center;
+ padding: 0 20px;
+ flex-shrink: 0;
+}
+
+.app-logo {
+ height: 36px;
+ width: auto;
+ margin-right: 12px;
+ flex-shrink: 0;
+}
+
+.app-header h1 {
+ font-size: 1.2rem;
+ margin: 0;
+}
+
+.app-body {
+ display: flex;
+ flex: 1;
+ overflow: hidden;
+}
+
+/* Sidebar */
+.sidebar {
+ border-right: 1px solid var(--border-color);
+ background-color: white;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+}
+
+.resizer-h {
+ width: 5px;
+ cursor: col-resize;
+ background-color: var(--border-color);
+ transition: background-color 0.2s;
+ flex-shrink: 0;
+}
+
+.resizer-h:hover, .is-resizing .resizer-h {
+ background-color: var(--accent-color);
+}
+
+.is-resizing {
+ cursor: col-resize;
+ user-select: none;
+}
+
+.sidebar-header {
+ border-bottom: 1px solid var(--border-color);
+ background-color: white;
+ position: sticky;
+ top: 0;
+ z-index: 10;
+}
+
+.sidebar h2 {
+ font-size: 1rem;
+ padding: 15px 15px 10px 15px;
+ margin: 0;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.search-container {
+ padding: 0 15px 15px 15px;
+}
+
+.search-input {
+ width: 100%;
+ padding: 6px 10px;
+ border: 1px solid var(--border-color);
+ border-radius: 4px;
+ font-size: 0.85rem;
+ outline: none;
+}
+
+.search-input:focus {
+ border-color: var(--accent-color);
+ box-shadow: 0 0 0 3px rgba(3, 102, 214, 0.1);
+}
+
+.no-results {
+ padding: 15px;
+ color: #6a737d;
+ font-style: italic;
+ font-size: 0.85rem;
+ text-align: center;
+}
+
+.directory-list {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ overflow: auto;
+ flex: 1;
+}
+
+.run-item, .dir-item {
+ padding: 8px 15px;
+ cursor: pointer;
+ font-size: 0.85rem;
+ white-space: nowrap;
+ min-width: min-content;
+ display: flex;
+ align-items: center;
+}
+
+.run-item-content {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ flex: 1;
+}
+
+.run-name { flex: 1; }
+
+.run-item:hover, .dir-item:hover { background-color: #f1f8ff; }
+
+.run-item.selected {
+ background-color: #e7f3ff;
+ border-left: 4px solid var(--accent-color);
+ font-weight: bold;
+}
+
+.repo-tag {
+ font-size: 0.65rem;
+ background-color: #e1e4e8;
+ color: #586069;
+ padding: 1px 5px;
+ border-radius: 8px;
+ font-weight: normal;
+ flex-shrink: 0;
+}
+
+.run-item.selected .repo-tag {
+ background-color: var(--accent-color);
+ color: white;
+}
+
+.hw-tag {
+ font-size: 0.65rem;
+ padding: 1px 5px;
+ border-radius: 8px;
+ font-weight: normal;
+ flex-shrink: 0;
+}
+
+.hw-tag--gpu {
+ background-color: #d4edda;
+ color: #155724;
+}
+
+.hw-tag--cpu {
+ background-color: #fff3cd;
+ color: #856404;
+}
+
+.run-item.selected .hw-tag--gpu {
+ background-color: #28a745;
+ color: white;
+}
+
+.run-item.selected .hw-tag--cpu {
+ background-color: #fd7e14;
+ color: white;
+}
+
+.tree-list {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+.icon {
+ margin-right: 8px;
+ font-size: 1rem;
+ width: 1.2rem;
+ display: inline-block;
+ text-align: center;
+}
+
+.dir-item {
+ font-weight: 600;
+ color: #444;
+}
+
+/* Content area */
+.content-area {
+ flex: 1;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+}
+
+.error-message {
+ background-color: #ffeef0;
+ color: #d73a49;
+ padding: 10px 20px;
+ border-bottom: 1px solid #d73a49;
+ flex-shrink: 0;
+}
+
+.loading {
+ padding: 20px;
+ text-align: center;
+ color: #6a737d;
+}
+
+/* Plot Viewer */
+.plot-viewer {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ overflow: hidden;
+}
+
+.viewer-placeholder {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100%;
+ color: #6a737d;
+ font-style: italic;
+ font-size: 1rem;
+}
+
+/* Navigation bar */
+.nav-bar {
+ background-color: white;
+ border-bottom: 1px solid var(--border-color);
+ padding: 8px 16px;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 16px;
+ align-items: center;
+ flex-shrink: 0;
+}
+
+.nav-group {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+}
+
+.nav-label {
+ font-size: 0.8rem;
+ font-weight: 600;
+ color: #586069;
+ white-space: nowrap;
+}
+
+.nav-tabs {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 2px;
+}
+
+.nav-tab {
+ padding: 4px 10px;
+ border: 1px solid var(--border-color);
+ border-radius: 4px;
+ background: var(--bg-color);
+ cursor: pointer;
+ font-size: 0.8rem;
+ color: var(--text-color);
+ white-space: nowrap;
+ transition: background-color 0.15s, color 0.15s;
+}
+
+.nav-tab:hover { background-color: #e7f3ff; }
+
+.nav-tab.active {
+ background-color: var(--accent-color);
+ color: white;
+ border-color: var(--accent-color);
+}
+
+.nav-select {
+ padding: 4px 8px;
+ border: 1px solid var(--border-color);
+ border-radius: 4px;
+ background: white;
+ font-size: 0.8rem;
+ color: var(--text-color);
+ cursor: pointer;
+ outline: none;
+}
+
+.nav-select:focus { border-color: var(--accent-color); }
+
+/* Plot grid */
+.plot-grid-container {
+ flex: 1;
+ overflow-y: auto;
+ padding: 16px;
+}
+
+.plot-section {
+ margin-bottom: 24px;
+}
+
+.plot-section-header {
+ font-size: 1rem;
+ font-weight: 600;
+ color: var(--primary-color);
+ margin: 0 0 10px 0;
+ padding-bottom: 6px;
+ border-bottom: 2px solid var(--border-color);
+}
+
+.plot-section-grid {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+}
+
+/* Individual thumbnail */
+.plot-thumbnail {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ cursor: pointer;
+ border: 2px solid var(--border-color);
+ border-radius: 4px;
+ padding: 4px;
+ background: white;
+ transition: border-color 0.15s, box-shadow 0.15s;
+ width: 210px;
+}
+
+.plot-thumbnail:hover {
+ border-color: var(--accent-color);
+ box-shadow: 0 2px 8px rgba(3, 102, 214, 0.15);
+}
+
+.plot-thumbnail.selected {
+ border-color: var(--accent-color);
+ box-shadow: 0 0 0 3px rgba(3, 102, 214, 0.2);
+}
+
+.thumbnail-pdf {
+ width: 200px;
+ height: 150px;
+ overflow: hidden;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background-color: #f8f8f8;
+}
+
+.thumbnail-pdf .react-pdf__Page canvas {
+ max-width: 200px !important;
+ height: auto !important;
+}
+
+.thumbnail-placeholder {
+ width: 200px;
+ height: 150px;
+ background: linear-gradient(135deg, #f0f0f0 25%, #e8e8e8 50%, #f0f0f0 75%);
+ background-size: 400% 400%;
+ animation: shimmer 1.5s infinite;
+}
+
+@keyframes shimmer {
+ 0% { background-position: 100% 50%; }
+ 100% { background-position: 0% 50%; }
+}
+
+.thumbnail-label {
+ margin-top: 4px;
+ font-size: 0.72rem;
+ color: #444;
+ text-align: center;
+ max-width: 200px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+/* Plot detail */
+.plot-detail-container {
+ flex: 1;
+ overflow-y: auto;
+ padding: 16px;
+}
+
+.plot-detail-back {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ margin-bottom: 16px;
+ cursor: pointer;
+ color: var(--accent-color);
+ font-size: 0.9rem;
+ font-weight: 600;
+ background: none;
+ border: none;
+ padding: 0;
+}
+
+.plot-detail-back:hover { text-decoration: underline; }
+
+.plot-detail-title {
+ font-size: 1.1rem;
+ font-weight: 600;
+ margin: 0 0 16px 0;
+ color: var(--primary-color);
+}
+
+.plot-detail-section {
+ margin-bottom: 20px;
+}
+
+.plot-detail-section-label {
+ font-size: 0.85rem;
+ font-weight: 600;
+ color: #586069;
+ margin: 0 0 8px 0;
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+}
+
+.plot-detail-pdf {
+ border: 1px solid var(--border-color);
+ border-radius: 4px;
+ overflow: hidden;
+ display: inline-block;
+ background: white;
+}
+
+.plot-detail-pdf .react-pdf__Page {
+ margin: 0;
+}
+
+.react-pdf__Page {
+ margin-bottom: 0;
+}
+
+/* Non-MTV fallback */
+.flat-file-list {
+ flex: 1;
+ overflow-y: auto;
+ padding: 16px;
+}
+
+.flat-file-group {
+ margin-bottom: 16px;
+}
+
+.flat-file-group-name {
+ font-size: 0.9rem;
+ font-weight: 600;
+ color: var(--primary-color);
+ margin: 0 0 8px 0;
+ padding-bottom: 4px;
+ border-bottom: 1px solid var(--border-color);
+}
+
+.flat-file-item {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 6px 0;
+ cursor: pointer;
+ font-size: 0.85rem;
+ color: var(--accent-color);
+ border-bottom: 1px solid #f0f0f0;
+}
+
+.flat-file-item:hover { text-decoration: underline; }
+
+.flat-pdf-display {
+ margin-top: 12px;
+ border: 1px solid var(--border-color);
+ border-radius: 4px;
+ overflow: hidden;
+}
diff --git a/src/App.tsx b/src/App.tsx
new file mode 100644
index 00000000..7f29e713
--- /dev/null
+++ b/src/App.tsx
@@ -0,0 +1,123 @@
+import { useEffect, useState } from 'react';
+import Sidebar from './components/Sidebar';
+import PlotViewer from './components/PlotViewer';
+import { fetchDirectories, fetchTarball } from './api';
+import type { GitHubContent } from './api';
+import { decompressTarGz, revokeFileUrls } from './decompress';
+import type { DecompressedFile } from './decompress';
+import './App.css';
+
+const REPOS = [
+ { name: 'lst-performance-plots-archive-2026', branch: 'main' },
+ { name: 'TrackLooper-plots-archive', branch: 'cmssw' },
+];
+
+function App() {
+ const [directories, setDirectories] = useState([]);
+ const [selectedDir, setSelectedDir] = useState(null);
+ const [plotFiles, setPlotFiles] = useState([]);
+ const [isLoadingDirs, setIsLoadingDirs] = useState(true);
+ const [isExtracting, setIsExtracting] = useState(false);
+ const [error, setError] = useState(null);
+ const [sidebarWidth, setSidebarWidth] = useState(300);
+ const [isResizing, setIsResizing] = useState(false);
+
+ useEffect(() => {
+ async function initApp() {
+ try {
+ const allDirsResults = await Promise.all(REPOS.map(repo => fetchDirectories(repo.name)));
+ const allDirs = allDirsResults.flat();
+ setDirectories(allDirs);
+
+ const params = new URLSearchParams(window.location.search);
+ const runName = params.get('run');
+ if (runName) {
+ const matchedDir = allDirs.find(d => d.name === runName);
+ if (matchedDir) handleDirSelect(matchedDir);
+ }
+ } catch (err) {
+ setError('Failed to load directories from GitHub.');
+ console.error(err);
+ } finally {
+ setIsLoadingDirs(false);
+ }
+ }
+ initApp();
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ const handleMouseDown = (e: React.MouseEvent) => {
+ e.preventDefault();
+ setIsResizing(true);
+ };
+
+ useEffect(() => {
+ const handleMouseMove = (e: MouseEvent) => {
+ if (!isResizing) return;
+ setSidebarWidth(Math.max(150, Math.min(600, e.clientX)));
+ };
+ const handleMouseUp = () => setIsResizing(false);
+
+ if (isResizing) {
+ window.addEventListener('mousemove', handleMouseMove);
+ window.addEventListener('mouseup', handleMouseUp);
+ }
+ return () => {
+ window.removeEventListener('mousemove', handleMouseMove);
+ window.removeEventListener('mouseup', handleMouseUp);
+ };
+ }, [isResizing]);
+
+ const handleDirSelect = async (dir: GitHubContent) => {
+ setSelectedDir(prev => {
+ if (prev?.path === dir.path && prev?.repo === dir.repo) return prev;
+ return dir;
+ });
+
+ const url = new URL(window.location.href);
+ url.searchParams.set('run', dir.name);
+ window.history.pushState({}, '', url);
+
+ setPlotFiles(prev => { revokeFileUrls(prev); return []; });
+ setIsExtracting(true);
+ setError(null);
+
+ try {
+ const repoInfo = REPOS.find(r => r.name === dir.repo);
+ const buffer = await fetchTarball(dir.repo, dir.path, repoInfo?.branch ?? 'main');
+ const files = await decompressTarGz(buffer);
+ setPlotFiles(files);
+ } catch (err) {
+ setError(`Failed to process plots for ${dir.name}.`);
+ console.error(err);
+ } finally {
+ setIsExtracting(false);
+ }
+ };
+
+ return (
+
+
+
+ LST Performance Web
+
+
+
+
+
+
+ );
+}
+
+export default App;
diff --git a/src/api.ts b/src/api.ts
new file mode 100644
index 00000000..6887befa
--- /dev/null
+++ b/src/api.ts
@@ -0,0 +1,32 @@
+const REPO_OWNER = 'SegmentLinking';
+const BASE_API_URL = `https://api.github.com/repos/${REPO_OWNER}`;
+const RAW_URL = `https://raw.githubusercontent.com/${REPO_OWNER}`;
+
+export interface GitHubContent {
+ name: string;
+ path: string;
+ type: 'dir' | 'file';
+ download_url: string | null;
+ repo: string;
+}
+
+export async function fetchDirectories(repoName: string): Promise {
+ const response = await fetch(`${BASE_API_URL}/${repoName}/contents`);
+ if (!response.ok) {
+ throw new Error(`Failed to fetch directories for ${repoName}: ${response.statusText}`);
+ }
+ const data: GitHubContent[] = await response.json();
+ return data
+ .filter(item => item.type === 'dir')
+ .map(item => ({ ...item, repo: repoName }))
+ .reverse();
+}
+
+export async function fetchTarball(repoName: string, dirPath: string, branch: string = 'main'): Promise {
+ const url = `${RAW_URL}/${repoName}/${branch}/${dirPath}/plots.tar.gz`;
+ const response = await fetch(url);
+ if (!response.ok) {
+ throw new Error(`Failed to fetch tarball from ${repoName} (${branch}): ${response.statusText}`);
+ }
+ return await response.arrayBuffer();
+}
diff --git a/src/components/CmssPlotViewer.tsx b/src/components/CmssPlotViewer.tsx
new file mode 100644
index 00000000..3901e0d0
--- /dev/null
+++ b/src/components/CmssPlotViewer.tsx
@@ -0,0 +1,163 @@
+import React, { useMemo, useState, useEffect } from 'react';
+import { Document, Page } from 'react-pdf';
+import 'react-pdf/dist/Page/AnnotationLayer.css';
+import 'react-pdf/dist/Page/TextLayer.css';
+import type { DecompressedFile } from '../decompress';
+import type { CmssPlotFile } from '../plotLayout';
+import {
+ parseCmssFiles,
+ formatCategoryName,
+ formatCmssStemLabel,
+ CMSS_GROUP_LABELS,
+} from '../plotLayout';
+import PlotThumbnail from './PlotThumbnail';
+import FlatPdfViewer from './FlatPdfViewer';
+
+interface CmssPlotViewerProps {
+ files: DecompressedFile[];
+}
+
+const CmssPlotViewer: React.FC = ({ files }) => {
+ const parsed = useMemo(() => parseCmssFiles(files), [files]);
+
+ const [category, setCategory] = useState('');
+ const [group, setGroup] = useState('');
+ const [detail, setDetail] = useState(null);
+
+ // Initialise from URL params when parsed data changes, falling back to defaults
+ useEffect(() => {
+ const params = new URLSearchParams(window.location.search);
+ const first = parsed.categories[0] ?? '';
+ const urlCat = params.get('cat') ?? '';
+ const resolvedCat = parsed.categories.includes(urlCat) ? urlCat : first;
+ setCategory(resolvedCat);
+
+ const groups = parsed.groupsForCategory.get(resolvedCat) ?? [];
+ const urlGrp = params.get('grp') ?? '';
+ const resolvedGrp = groups.includes(urlGrp) ? urlGrp : (groups[0] ?? '');
+ setGroup(resolvedGrp);
+
+ const urlPlot = params.get('plot') ?? '';
+ if (urlPlot) {
+ const key = `${resolvedCat}\x00${resolvedGrp}`;
+ const catFiles = parsed.filesForCategoryGroup.get(key) ?? [];
+ setDetail(catFiles.find(f => f.stem === urlPlot) ?? null);
+ } else {
+ setDetail(null);
+ }
+ }, [parsed]);
+
+ // Sync nav state → URL
+ useEffect(() => {
+ if (!category) return;
+ const url = new URL(window.location.href);
+ url.searchParams.set('cat', category);
+ url.searchParams.set('grp', group);
+ detail
+ ? url.searchParams.set('plot', detail.stem)
+ : url.searchParams.delete('plot');
+ url.searchParams.delete('ds');
+ url.searchParams.delete('obj');
+ url.searchParams.delete('metric');
+ url.searchParams.delete('sel');
+ url.searchParams.delete('charge');
+ window.history.replaceState({}, '', url);
+ }, [category, group, detail]);
+
+ if (parsed.categories.length === 0) {
+ return ;
+ }
+
+ const groups = parsed.groupsForCategory.get(category) ?? [];
+ const currentFiles = parsed.filesForCategoryGroup.get(`${category}\x00${group}`) ?? [];
+
+ const handleCategoryChange = (cat: string) => {
+ setCategory(cat);
+ setGroup(parsed.groupsForCategory.get(cat)?.[0] ?? '');
+ setDetail(null);
+ };
+
+ if (detail) {
+ return (
+
+
setDetail(null)}>
+ ← Back to grid
+
+
{detail.stem}
+
+
+
+ );
+ }
+
+ return (
+
+
+ {parsed.categories.length > 0 && (
+
+
Category:
+
+ {parsed.categories.map(cat => (
+ handleCategoryChange(cat)}
+ >
+ {formatCategoryName(cat)}
+
+ ))}
+
+
+ )}
+
+ {groups.length > 0 && (
+
+
Group:
+
+ {groups.map(g => (
+ { setGroup(g); setDetail(null); }}
+ >
+ {CMSS_GROUP_LABELS[g] ?? g}
+
+ ))}
+
+
+ )}
+
+
+
+
+
+ {currentFiles.map(f => (
+
setDetail(f)}
+ />
+ ))}
+
+
+
+
+ );
+};
+
+export default CmssPlotViewer;
diff --git a/src/components/FlatPdfViewer.tsx b/src/components/FlatPdfViewer.tsx
new file mode 100644
index 00000000..70ffbe05
--- /dev/null
+++ b/src/components/FlatPdfViewer.tsx
@@ -0,0 +1,98 @@
+import React, { useMemo, useState, useEffect, useRef } from 'react';
+import { Document, Page } from 'react-pdf';
+import 'react-pdf/dist/Page/AnnotationLayer.css';
+import 'react-pdf/dist/Page/TextLayer.css';
+import type { DecompressedFile } from '../decompress';
+
+interface FlatPdfViewerProps {
+ files: DecompressedFile[];
+}
+
+const FlatPdfViewer: React.FC = ({ files }) => {
+ const [selected, setSelected] = useState(null);
+ const [listWidth, setListWidth] = useState(280);
+ const [isResizing, setIsResizing] = useState(false);
+ const containerRef = useRef(null);
+
+ const handleMouseDown = (e: React.MouseEvent) => {
+ e.preventDefault();
+ setIsResizing(true);
+ };
+
+ useEffect(() => {
+ const handleMouseMove = (e: MouseEvent) => {
+ if (!isResizing || !containerRef.current) return;
+ const left = containerRef.current.getBoundingClientRect().left;
+ setListWidth(Math.max(150, Math.min(600, e.clientX - left)));
+ };
+ const handleMouseUp = () => setIsResizing(false);
+ if (isResizing) {
+ window.addEventListener('mousemove', handleMouseMove);
+ window.addEventListener('mouseup', handleMouseUp);
+ }
+ return () => {
+ window.removeEventListener('mousemove', handleMouseMove);
+ window.removeEventListener('mouseup', handleMouseUp);
+ };
+ }, [isResizing]);
+
+ const groups = useMemo(() => {
+ const map = new Map();
+ for (const f of files) {
+ const parts = f.name.split('/');
+ const dir = parts.length > 1 ? parts.slice(0, -1).join('/') : '/';
+ if (!map.has(dir)) map.set(dir, []);
+ map.get(dir)!.push(f);
+ }
+ return map;
+ }, [files]);
+
+ return (
+
+
+
+ {[...groups.entries()].map(([dir, groupFiles]) => (
+
+
{dir}
+ {groupFiles.map(f => {
+ const name = f.name.split('/').at(-1) ?? f.name;
+ return (
+
setSelected(f)}
+ >
+ {name}
+
+ );
+ })}
+
+ ))}
+
+
+
+
+
+ {selected ? (
+ Loading…
}
+ >
+
+
+ ) : (
+
+ Select a plot from the list.
+
+ )}
+
+
+ );
+};
+
+export default FlatPdfViewer;
diff --git a/src/components/PlotDetail.tsx b/src/components/PlotDetail.tsx
new file mode 100644
index 00000000..1b2ac09d
--- /dev/null
+++ b/src/components/PlotDetail.tsx
@@ -0,0 +1,68 @@
+import React from 'react';
+import { Document, Page } from 'react-pdf';
+import 'react-pdf/dist/Page/AnnotationLayer.css';
+import 'react-pdf/dist/Page/TextLayer.css';
+import type { PlotInfo, ParsedDataset } from '../plotLayout';
+import { formatMetric, formatVariable, PDGID_LABELS, SELECTION_LABELS, CHARGE_LABELS } from '../plotLayout';
+
+interface PlotDetailProps {
+ mainPlot: PlotInfo;
+ dataset: ParsedDataset;
+ onBack: () => void;
+}
+
+function sectionLabel(breakdown: PlotInfo): string {
+ if (breakdown.breakdownType === 'den') return `Denominator ${breakdown.breakdownIndex}`;
+ if (breakdown.breakdownType === 'num') return `Numerator ${breakdown.breakdownIndex}`;
+ if (breakdown.breakdownType === 'ratio') return `Double Ratio ${breakdown.breakdownIndex}`;
+ return 'Breakdown';
+}
+
+function makePlotTitle(plot: PlotInfo): string {
+ const metric = formatMetric(plot.metric);
+ const variable = formatVariable(plot.variable);
+ if (plot.metric === 'eff') {
+ const pdgLabel = plot.pdgid !== undefined ? (PDGID_LABELS[plot.pdgid] ?? String(plot.pdgid)) : '';
+ const selLabel = plot.selection ? (SELECTION_LABELS[plot.selection] ?? plot.selection) : '';
+ const chLabel = plot.charge !== undefined ? (CHARGE_LABELS[plot.charge] ?? String(plot.charge)) : '';
+ return `${plot.objectType} ${metric} — ${variable} | ${pdgLabel}, ${selLabel}, charge: ${chLabel}`;
+ }
+ return `${plot.objectType} ${metric} — ${variable}`;
+}
+
+const SinglePdf: React.FC<{ url: string; label: string }> = ({ url, label }) => (
+
+
{label}
+
+ Loading…
}>
+
+
+
+
+);
+
+const PlotDetail: React.FC = ({ mainPlot, dataset, onBack }) => {
+ const breakdownList = dataset.breakdowns.get(mainPlot.stem) ?? [];
+
+ return (
+
+
+ ← Back to summary
+
+
{makePlotTitle(mainPlot)}
+
+
+
+ {breakdownList.map(bd => (
+
+ ))}
+
+ );
+};
+
+export default PlotDetail;
diff --git a/src/components/PlotGrid.tsx b/src/components/PlotGrid.tsx
new file mode 100644
index 00000000..0e4d3964
--- /dev/null
+++ b/src/components/PlotGrid.tsx
@@ -0,0 +1,99 @@
+import React from 'react';
+import PlotThumbnail from './PlotThumbnail';
+import type { ParsedDataset, PlotInfo } from '../plotLayout';
+import {
+ formatVariable,
+ PDGID_LABELS,
+ makeNavKey,
+ getEffStem,
+ getRecoStem,
+} from '../plotLayout';
+
+interface PlotGridProps {
+ dataset: ParsedDataset;
+ objectType: string;
+ metric: string;
+ selection: string;
+ charge: number;
+ selectedStem: string | null;
+ onSelect: (plot: PlotInfo) => void;
+}
+
+const PlotGrid: React.FC = ({
+ dataset,
+ objectType,
+ metric,
+ selection,
+ charge,
+ selectedStem,
+ onSelect,
+}) => {
+ if (metric === 'eff') {
+ const pdgidKey = makeNavKey(objectType, metric, selection, charge);
+ const pdgids = dataset.pdgidsForObjMetricSelCharge.get(pdgidKey) ?? [];
+
+ const varKey = (pdgid: number) => makeNavKey(objectType, metric, selection, charge, pdgid);
+
+ return (
+
+ {pdgids.map(pdgid => {
+ const variables = dataset.variablesForNav.get(varKey(pdgid)) ?? [];
+ return (
+
+ {pdgids.length > 1 && (
+
+ {PDGID_LABELS[pdgid] ?? String(pdgid)}
+
+ )}
+
+ {variables.map(variable => {
+ const stem = getEffStem(objectType, selection, pdgid, charge, variable);
+ const plot = dataset.plotsByKey.get(stem);
+ if (!plot) return null;
+ return (
+
onSelect(plot)}
+ />
+ );
+ })}
+
+
+ );
+ })}
+
+ );
+ }
+
+ // Reco metrics: flat grid of variables
+ const vKey = makeNavKey(objectType, metric);
+ const variables = dataset.variablesForNav.get(vKey) ?? [];
+
+ return (
+
+
+
+ {variables.map(variable => {
+ const stem = getRecoStem(objectType, metric, variable);
+ const plot = dataset.plotsByKey.get(stem);
+ if (!plot) return null;
+ return (
+
onSelect(plot)}
+ />
+ );
+ })}
+
+
+
+ );
+};
+
+export default PlotGrid;
diff --git a/src/components/PlotThumbnail.tsx b/src/components/PlotThumbnail.tsx
new file mode 100644
index 00000000..82655e08
--- /dev/null
+++ b/src/components/PlotThumbnail.tsx
@@ -0,0 +1,60 @@
+import React, { useEffect, useRef, useState } from 'react';
+import { Document, Page } from 'react-pdf';
+import 'react-pdf/dist/Page/AnnotationLayer.css';
+import 'react-pdf/dist/Page/TextLayer.css';
+
+interface PlotThumbnailProps {
+ url: string;
+ label: string;
+ isSelected: boolean;
+ onClick: () => void;
+}
+
+const PlotThumbnail: React.FC = ({ url, label, isSelected, onClick }) => {
+ const [shouldRender, setShouldRender] = useState(false);
+ const containerRef = useRef(null);
+
+ useEffect(() => {
+ const el = containerRef.current;
+ if (!el) return;
+ const observer = new IntersectionObserver(
+ ([entry]) => {
+ if (entry.isIntersecting) {
+ setShouldRender(true);
+ observer.disconnect();
+ }
+ },
+ { threshold: 0, rootMargin: '300px' },
+ );
+ observer.observe(el);
+ return () => observer.disconnect();
+ }, []);
+
+ return (
+
+
+ {shouldRender ? (
+
+
+
+ ) : (
+
+ )}
+
+
{label}
+
+ );
+};
+
+export default PlotThumbnail;
diff --git a/src/components/PlotViewer.tsx b/src/components/PlotViewer.tsx
new file mode 100644
index 00000000..989bdb82
--- /dev/null
+++ b/src/components/PlotViewer.tsx
@@ -0,0 +1,312 @@
+import React, { useMemo, useState, useEffect } from 'react';
+import type { DecompressedFile } from '../decompress';
+import type { PlotInfo } from '../plotLayout';
+import {
+ parseFiles,
+ formatMetric,
+ formatDatasetName,
+ SELECTION_LABELS,
+ CHARGE_LABELS,
+ makeNavKey,
+} from '../plotLayout';
+import PlotGrid from './PlotGrid';
+import PlotDetail from './PlotDetail';
+import CmssPlotViewer from './CmssPlotViewer';
+import FlatPdfViewer from './FlatPdfViewer';
+
+interface PlotViewerProps {
+ files: DecompressedFile[];
+ isExtracting: boolean;
+}
+
+const PlotViewer: React.FC = ({ files, isExtracting }) => {
+ const parsedFiles = useMemo(() => parseFiles(files), [files]);
+ const isMtvRun = parsedFiles.isMtvRun;
+
+ const [datasetName, setDatasetName] = useState('');
+ const [objectType, setObjectType] = useState('');
+ const [metric, setMetric] = useState('');
+ const [selection, setSelection] = useState('');
+ const [charge, setCharge] = useState(0);
+ const [detailPlot, setDetailPlot] = useState(null);
+
+ // Initialise nav state from URL params when files change, falling back to defaults
+ useEffect(() => {
+ if (!isMtvRun || parsedFiles.datasets.size === 0) {
+ setDatasetName('');
+ setObjectType('');
+ setMetric('');
+ setSelection('');
+ setCharge(0);
+ setDetailPlot(null);
+ return;
+ }
+
+ const params = new URLSearchParams(window.location.search);
+ const firstDs = parsedFiles.datasets.keys().next().value as string;
+ const urlDs = params.get('ds') ?? '';
+ const resolvedDs = parsedFiles.datasets.has(urlDs) ? urlDs : firstDs;
+ setDatasetName(resolvedDs);
+
+ const ds = parsedFiles.datasets.get(resolvedDs)!;
+
+ const firstObj = ds.objectTypes[0] ?? '';
+ const urlObj = params.get('obj') ?? '';
+ const resolvedObj = ds.objectTypes.includes(urlObj) ? urlObj : firstObj;
+ setObjectType(resolvedObj);
+
+ const metrics = ds.metricsForObj.get(resolvedObj) ?? [];
+ const firstMetric = metrics[0] ?? '';
+ const urlMetric = params.get('metric') ?? '';
+ const resolvedMetric = metrics.includes(urlMetric) ? urlMetric : firstMetric;
+ setMetric(resolvedMetric);
+
+ if (resolvedMetric === 'eff') {
+ const sels = ds.selectionsForObjMetric.get(makeNavKey(resolvedObj, 'eff')) ?? [];
+ const firstSel = sels[0] ?? '';
+ const urlSel = params.get('sel') ?? '';
+ const resolvedSel = sels.includes(urlSel) ? urlSel : firstSel;
+ setSelection(resolvedSel);
+
+ const chargeOpts = ds.chargesForObjMetricSel.get(makeNavKey(resolvedObj, 'eff', resolvedSel)) ?? [];
+ const firstCharge = chargeOpts[0] ?? 0;
+ const urlCharge = parseInt(params.get('charge') ?? '');
+ setCharge(chargeOpts.includes(urlCharge) ? urlCharge : firstCharge);
+ } else {
+ setSelection('');
+ setCharge(0);
+ }
+
+ const urlPlot = params.get('plot') ?? '';
+ setDetailPlot(urlPlot ? (ds.plotsByKey.get(urlPlot) ?? null) : null);
+ }, [parsedFiles]); // eslint-disable-line react-hooks/exhaustive-deps
+
+ // Sync nav state → URL
+ useEffect(() => {
+ if (!isMtvRun || !datasetName) return;
+ const url = new URL(window.location.href);
+ url.searchParams.set('ds', datasetName);
+ url.searchParams.set('obj', objectType);
+ url.searchParams.set('metric', metric);
+ if (metric === 'eff' && selection) {
+ url.searchParams.set('sel', selection);
+ url.searchParams.set('charge', String(charge));
+ } else {
+ url.searchParams.delete('sel');
+ url.searchParams.delete('charge');
+ }
+ detailPlot
+ ? url.searchParams.set('plot', detailPlot.stem)
+ : url.searchParams.delete('plot');
+ url.searchParams.delete('cat');
+ url.searchParams.delete('grp');
+ window.history.replaceState({}, '', url);
+ }, [isMtvRun, datasetName, objectType, metric, selection, charge, detailPlot]); // eslint-disable-line react-hooks/exhaustive-deps
+
+ function resetNavForDataset(dsName: string) {
+ const ds = parsedFiles.datasets.get(dsName);
+ if (!ds) return;
+
+ const firstObj = ds.objectTypes[0] ?? '';
+ setObjectType(firstObj);
+
+ const metrics = ds.metricsForObj.get(firstObj) ?? [];
+ const firstMetric = metrics[0] ?? '';
+ setMetric(firstMetric);
+
+ if (firstMetric === 'eff') {
+ const sels = ds.selectionsForObjMetric.get(makeNavKey(firstObj, 'eff')) ?? [];
+ const firstSel = sels[0] ?? '';
+ setSelection(firstSel);
+ const charges = ds.chargesForObjMetricSel.get(makeNavKey(firstObj, 'eff', firstSel)) ?? [];
+ setCharge(charges[0] ?? 0);
+ } else {
+ setSelection('');
+ setCharge(0);
+ }
+ }
+
+ const handleDatasetChange = (name: string) => {
+ setDatasetName(name);
+ resetNavForDataset(name);
+ setDetailPlot(null);
+ };
+
+ const handleObjectTypeChange = (obj: string) => {
+ const ds = parsedFiles.datasets.get(datasetName);
+ if (!ds) return;
+ setObjectType(obj);
+ const metrics = ds.metricsForObj.get(obj) ?? [];
+ const firstMetric = metrics[0] ?? '';
+ setMetric(firstMetric);
+ if (firstMetric === 'eff') {
+ const sels = ds.selectionsForObjMetric.get(makeNavKey(obj, 'eff')) ?? [];
+ const firstSel = sels[0] ?? '';
+ setSelection(firstSel);
+ const charges = ds.chargesForObjMetricSel.get(makeNavKey(obj, 'eff', firstSel)) ?? [];
+ setCharge(charges[0] ?? 0);
+ }
+ setDetailPlot(null);
+ };
+
+ const handleMetricChange = (m: string) => {
+ const ds = parsedFiles.datasets.get(datasetName);
+ if (!ds) return;
+ setMetric(m);
+ if (m === 'eff') {
+ const sels = ds.selectionsForObjMetric.get(makeNavKey(objectType, 'eff')) ?? [];
+ const firstSel = sels[0] ?? '';
+ setSelection(firstSel);
+ const charges = ds.chargesForObjMetricSel.get(makeNavKey(objectType, 'eff', firstSel)) ?? [];
+ setCharge(charges[0] ?? 0);
+ }
+ setDetailPlot(null);
+ };
+
+ const handleSelectionChange = (sel: string) => {
+ const ds = parsedFiles.datasets.get(datasetName);
+ if (!ds) return;
+ setSelection(sel);
+ const charges = ds.chargesForObjMetricSel.get(makeNavKey(objectType, metric, sel)) ?? [];
+ setCharge(charges[0] ?? 0);
+ setDetailPlot(null);
+ };
+
+ if (isExtracting) {
+ return Decompressing plots…
;
+ }
+
+ if (files.length === 0) {
+ return Select a CI run from the sidebar to view plots.
;
+ }
+
+ if (!parsedFiles.isMtvRun) {
+ return ;
+ }
+
+ const datasetNames = [...parsedFiles.datasets.keys()];
+ const currentDataset = parsedFiles.datasets.get(datasetName);
+ if (!currentDataset) return ;
+
+ const objectTypes = currentDataset.objectTypes;
+ const metrics = currentDataset.metricsForObj.get(objectType) ?? [];
+ const selections = metric === 'eff'
+ ? (currentDataset.selectionsForObjMetric.get(makeNavKey(objectType, metric)) ?? [])
+ : [];
+ const charges = metric === 'eff' && selection
+ ? (currentDataset.chargesForObjMetricSel.get(makeNavKey(objectType, metric, selection)) ?? [])
+ : [];
+
+ if (detailPlot) {
+ return (
+ setDetailPlot(null)}
+ />
+ );
+ }
+
+ return (
+
+
+ {datasetNames.length > 1 && (
+
+
Dataset:
+
+ {datasetNames.map(name => (
+ handleDatasetChange(name)}
+ >
+ {formatDatasetName(name)}
+
+ ))}
+
+
+ )}
+
+ {objectTypes.length > 1 && (
+
+
Object:
+
+ {objectTypes.map(obj => (
+ handleObjectTypeChange(obj)}
+ >
+ {obj}
+
+ ))}
+
+
+ )}
+
+ {metrics.length > 0 && (
+
+
Metric:
+
+ {metrics.map(m => (
+ handleMetricChange(m)}
+ >
+ {formatMetric(m)}
+
+ ))}
+
+
+ )}
+
+ {metric === 'eff' && selections.length > 1 && (
+
+ Selection:
+ handleSelectionChange(e.target.value)}
+ >
+ {selections.map(sel => (
+
+ {SELECTION_LABELS[sel] ?? sel}
+
+ ))}
+
+
+ )}
+
+ {metric === 'eff' && charges.length > 1 && (
+
+
Charge:
+
+ {charges.map(ch => (
+ { setCharge(ch); setDetailPlot(null); }}
+ >
+ {CHARGE_LABELS[ch] ?? String(ch)}
+
+ ))}
+
+
+ )}
+
+
+
+
+ );
+};
+
+export default PlotViewer;
diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx
new file mode 100644
index 00000000..013b4e93
--- /dev/null
+++ b/src/components/Sidebar.tsx
@@ -0,0 +1,184 @@
+import React, { useState, useMemo, useEffect } from 'react';
+import type { GitHubContent } from '../api';
+
+interface SidebarProps {
+ directories: GitHubContent[];
+ selectedDir: string | null;
+ onSelect: (dir: GitHubContent) => void;
+ isLoading: boolean;
+}
+
+interface RunTreeNode {
+ name: string;
+ type: 'pr' | 'commit' | 'run';
+ children: Record;
+ dir?: GitHubContent;
+}
+
+function getHardwareTag(dirName: string): 'GPU' | 'CPU' | null {
+ if (dirName.endsWith('_gpu')) return 'GPU';
+ if (dirName.endsWith('_cpu')) return 'CPU';
+ return null;
+}
+
+const RunTreeItem: React.FC<{
+ node: RunTreeNode;
+ level: number;
+ selectedDir: string | null;
+ onSelect: (dir: GitHubContent) => void;
+ searchTerm: string;
+}> = ({ node, level, selectedDir, onSelect, searchTerm }) => {
+ const [isOpen, setIsOpen] = useState(false);
+
+ useEffect(() => {
+ if (searchTerm) setIsOpen(true);
+ }, [searchTerm]);
+
+ const hasSelectedChild = useMemo(() => {
+ const check = (n: RunTreeNode): boolean => {
+ if (n.dir?.path === selectedDir) return true;
+ return Object.values(n.children).some(check);
+ };
+ return check(node);
+ }, [node, selectedDir]);
+
+ useEffect(() => {
+ if (hasSelectedChild) setIsOpen(true);
+ }, [hasSelectedChild]);
+
+ if (node.name === 'root') {
+ const children = Object.values(node.children).sort((a, b) => {
+ const aNum = parseInt(a.name.replace('PR', '')) || 0;
+ const bNum = parseInt(b.name.replace('PR', '')) || 0;
+ return bNum - aNum;
+ });
+ return (
+
+ {children.map(child => (
+
+ ))}
+
+ );
+ }
+
+ if (node.type === 'run') {
+ return (
+ onSelect(node.dir!)}
+ style={{ paddingLeft: `${level * 12 + 15}px` }}
+ title={`${node.dir?.name} (${node.dir?.repo})`}
+ >
+
+ 🚀
+ {node.name.replace(/_(gpu|cpu)$/, '')}
+ {getHardwareTag(node.dir?.name ?? '') && (
+
+ {getHardwareTag(node.dir?.name ?? '')}
+
+ )}
+ {node.dir?.repo.includes('2026') ? '2026' : 'Legacy'}
+
+
+ );
+ }
+
+ const children = Object.values(node.children).sort((a, b) => a.name.localeCompare(b.name));
+ if (children.length === 0) return null;
+
+ return (
+ <>
+ setIsOpen(!isOpen)}
+ style={{ paddingLeft: `${level * 12 + 15}px` }}
+ >
+ {isOpen ? '📂' : '📁'} {node.name}
+
+ {isOpen && (
+
+ {children.map(child => (
+
+ ))}
+
+ )}
+ >
+ );
+};
+
+const Sidebar: React.FC = ({ directories, selectedDir, onSelect, isLoading }) => {
+ const [searchTerm, setSearchTerm] = useState('');
+
+ const runTree = useMemo(() => {
+ const root: RunTreeNode = { name: 'root', type: 'pr', children: {} };
+ const filtered = directories.filter(dir =>
+ dir.name.toLowerCase().includes(searchTerm.toLowerCase())
+ );
+ filtered.forEach(dir => {
+ const parts = dir.name.split('_');
+ const prName = parts[0] || 'Unknown';
+ const commitHash = parts[1] || 'NoHash';
+ const rest = parts.slice(2).join('_') || 'default';
+
+ if (!root.children[prName]) {
+ root.children[prName] = { name: prName, type: 'pr', children: {} };
+ }
+ const prNode = root.children[prName];
+ if (!prNode.children[commitHash]) {
+ prNode.children[commitHash] = { name: commitHash, type: 'commit', children: {} };
+ }
+ const commitNode = prNode.children[commitHash];
+ commitNode.children[rest] = { name: rest, type: 'run', children: {}, dir };
+ });
+ return root;
+ }, [directories, searchTerm]);
+
+ return (
+
+ );
+};
+
+export default Sidebar;
diff --git a/src/decompress.ts b/src/decompress.ts
new file mode 100644
index 00000000..96a1ea69
--- /dev/null
+++ b/src/decompress.ts
@@ -0,0 +1,22 @@
+import { gunzipSync } from 'fflate';
+import untar from 'js-untar';
+
+export interface DecompressedFile {
+ name: string;
+ url: string;
+}
+
+export async function decompressTarGz(buffer: ArrayBuffer): Promise {
+ const decompressed = gunzipSync(new Uint8Array(buffer));
+ const files = await untar(decompressed.buffer as ArrayBuffer);
+ return files
+ .filter(file => file.name.endsWith('.pdf'))
+ .map(file => ({
+ name: file.name,
+ url: URL.createObjectURL((file as unknown as { blob: Blob }).blob),
+ }));
+}
+
+export function revokeFileUrls(files: DecompressedFile[]) {
+ files.forEach(file => URL.revokeObjectURL(file.url));
+}
diff --git a/src/index.css b/src/index.css
new file mode 100644
index 00000000..7ddbbf02
--- /dev/null
+++ b/src/index.css
@@ -0,0 +1,29 @@
+:root {
+ font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
+ line-height: 1.5;
+ font-weight: 400;
+
+ color-scheme: light;
+ color: #24292e;
+ background-color: #f6f8fa;
+
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+* {
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+ display: flex;
+ min-width: 320px;
+ min-height: 100vh;
+}
+
+#root {
+ width: 100%;
+}
diff --git a/src/main.tsx b/src/main.tsx
new file mode 100644
index 00000000..ee68d4f5
--- /dev/null
+++ b/src/main.tsx
@@ -0,0 +1,13 @@
+import { StrictMode } from 'react'
+import { createRoot } from 'react-dom/client'
+import { pdfjs } from 'react-pdf'
+import './index.css'
+import App from './App.tsx'
+
+pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`
+
+createRoot(document.getElementById('root')!).render(
+
+
+ ,
+)
diff --git a/src/plotLayout.ts b/src/plotLayout.ts
new file mode 100644
index 00000000..b43dc54e
--- /dev/null
+++ b/src/plotLayout.ts
@@ -0,0 +1,429 @@
+export interface PlotInfo {
+ url: string;
+ stem: string; // filename without .pdf
+ objectType: string;
+ metric: string;
+ variable: string;
+ selection?: string; // only for metric='eff'
+ pdgid?: number; // only for metric='eff'
+ charge?: number; // only for metric='eff'
+ breakdownType?: 'den' | 'num' | 'ratio';
+ breakdownIndex?: number;
+}
+
+// Per-dataset parsed data (one for each validation_plots_* / comparison_plots_* dir)
+export interface ParsedDataset {
+ plotsByKey: Map;
+ breakdowns: Map;
+ objectTypes: string[];
+ metricsForObj: Map;
+ selectionsForObjMetric: Map;
+ chargesForObjMetricSel: Map;
+ pdgidsForObjMetricSelCharge: Map;
+ variablesForNav: Map;
+}
+
+export interface ParsedFiles {
+ isMtvRun: boolean;
+ // keyed by directory name, e.g. "validation_plots_8da008-PU200"
+ datasets: Map;
+}
+
+const SELECTION_SET = new Set(['base', 'loweta', 'xtr', 'vtr']);
+
+export const METRIC_LABELS: Record = {
+ eff: 'Efficiency',
+ fakerate: 'Fake Rate',
+ duplrate: 'Duplicate Rate',
+ fakeorduplrate: 'Fake or Dup. Rate',
+ avgOTlen: 'Avg. OT Length',
+};
+
+export const SELECTION_LABELS: Record = {
+ base: '|η| < 4.5',
+ loweta: '|η| < 2.4',
+ xtr: '1.1 < |η| < 2.7',
+ vtr: 'barrel non-transition',
+};
+
+export const PDGID_LABELS: Record = {
+ 0: 'All',
+ 11: 'Electron',
+ 13: 'Muon',
+ 211: 'Pion',
+ 321: 'Kaon',
+};
+
+export const CHARGE_LABELS: Record = {
+ 0: 'Both',
+ 1: 'Positive+',
+ [-1]: 'Negative−',
+};
+
+const VARIABLE_ORDER: string[] = [
+ 'pt', 'ptzoom', 'ptlow', 'ptlowzoom', 'ptmtv', 'ptmtvzoom',
+ 'eta', 'etazoom', 'etacoarse', 'etacoarsezoom',
+ 'phi', 'phizoom', 'phicoarse', 'phicoarsezoom',
+ 'dxy', 'dxyzoom', 'dxycoarse', 'dxycoarsezoom',
+ 'dz', 'dzzoom', 'dzcoarse', 'dzcoarsezoom',
+ 'vxy', 'vxyzoom', 'vxycoarse', 'vxycoarsezoom',
+];
+
+const VARIABLE_ORDER_IDX = new Map(VARIABLE_ORDER.map((v, i) => [v, i]));
+
+const VARIABLE_LABELS: Record = {
+ pt: 'pT', ptzoom: 'pT zoom', ptlow: 'pT low', ptlowzoom: 'pT low zoom',
+ ptmtv: 'pT (MTV)', ptmtvzoom: 'pT (MTV) zoom',
+ eta: 'η', etazoom: 'η zoom', etacoarse: 'η coarse', etacoarsezoom: 'η coarse zoom',
+ phi: 'φ', phizoom: 'φ zoom', phicoarse: 'φ coarse', phicoarsezoom: 'φ coarse zoom',
+ dxy: 'dxy', dxyzoom: 'dxy zoom', dxycoarse: 'dxy coarse', dxycoarsezoom: 'dxy coarse zoom',
+ dz: 'dz', dzzoom: 'dz zoom', dzcoarse: 'dz coarse', dzcoarsezoom: 'dz coarse zoom',
+ vxy: 'vxy', vxyzoom: 'vxy zoom', vxycoarse: 'vxy coarse', vxycoarsezoom: 'vxy coarse zoom',
+};
+
+export function formatVariable(v: string): string {
+ return VARIABLE_LABELS[v] ?? v;
+}
+
+export function formatMetric(m: string): string {
+ return METRIC_LABELS[m] ?? m;
+}
+
+export function formatDatasetName(name: string): string {
+ if (name.startsWith('validation_plots_')) {
+ return 'Validation: ' + name.slice('validation_plots_'.length);
+ }
+ if (name.startsWith('comparison_plots_')) {
+ const rest = name.slice('comparison_plots_'.length);
+ // rest is like "8da008-PU200_8da008-PU200"; the two runs are _ separated
+ const idx = rest.indexOf('_');
+ if (idx !== -1) {
+ return 'Comparison: ' + rest.slice(0, idx) + ' vs ' + rest.slice(idx + 1);
+ }
+ return 'Comparison: ' + rest;
+ }
+ return name;
+}
+
+export function sortVariables(vars: string[]): string[] {
+ return [...vars].sort((a, b) => {
+ const ia = VARIABLE_ORDER_IDX.get(a) ?? 999;
+ const ib = VARIABLE_ORDER_IDX.get(b) ?? 999;
+ if (ia !== ib) return ia - ib;
+ return a.localeCompare(b);
+ });
+}
+
+const SELECTION_ORDER = ['base', 'loweta', 'xtr', 'vtr'];
+const PDGID_ORDER = [0, 11, 13, 211, 321];
+const CHARGE_ORDER = [0, 1, -1];
+
+function parseStem(stem: string): Omit | null {
+ const parts = stem.split('_');
+ if (parts.length < 2) return null;
+ const objectType = parts[0];
+
+ // Efficiency: objectType_selection_pdgid_charge_eff_variable
+ if (parts.length >= 6 && SELECTION_SET.has(parts[1]) && parts[4] === 'eff') {
+ const selection = parts[1];
+ const pdgid = parseInt(parts[2]);
+ const charge = parseInt(parts[3]);
+ const variable = parts.slice(5).join('_');
+ if (isNaN(pdgid) || isNaN(charge)) return null;
+ return { objectType, metric: 'eff', variable, selection, pdgid, charge };
+ }
+
+ // Reco metric: objectType_metric_variable
+ if (parts.length >= 3) {
+ const metric = parts[1];
+ const variable = parts.slice(2).join('_');
+ return { objectType, metric, variable };
+ }
+
+ return null;
+}
+
+export function makeNavKey(...parts: (string | number)[]): string {
+ return parts.join('\x00');
+}
+
+function makeEmptyDataset(): {
+ plotsByKey: Map;
+ breakdowns: Map;
+ objTypesSet: Set;
+ metricsSet: Map>;
+ selectionsSet: Map>;
+ chargesSet: Map>;
+ pdgidsSet: Map>;
+ variablesSet: Map>;
+} {
+ return {
+ plotsByKey: new Map(),
+ breakdowns: new Map(),
+ objTypesSet: new Set(),
+ metricsSet: new Map(),
+ selectionsSet: new Map(),
+ chargesSet: new Map(),
+ pdgidsSet: new Map(),
+ variablesSet: new Map(),
+ };
+}
+
+export function parseFiles(decompressedFiles: { name: string; url: string }[]): ParsedFiles {
+ // Each entry in rawDatasets corresponds to one *_plots_* directory
+ const rawDatasets = new Map>();
+
+ let isMtvRun = false;
+
+ for (const file of decompressedFiles) {
+ // Capture the immediate parent of mtv/ as the dataset name
+ const mtvMatch = file.name.match(/\/([^/]+)\/mtv\/(var|den|num|ratio)\/(.+)\.pdf$/);
+ if (!mtvMatch) continue;
+ isMtvRun = true;
+
+ const datasetName = mtvMatch[1];
+ const category = mtvMatch[2] as 'var' | 'den' | 'num' | 'ratio';
+ const filename = mtvMatch[3];
+
+ if (!rawDatasets.has(datasetName)) rawDatasets.set(datasetName, makeEmptyDataset());
+ const ds = rawDatasets.get(datasetName)!;
+
+ // Check for breakdown suffix: _den0, _num0, _ratio0, etc.
+ const breakdownMatch = filename.match(/^(.+)_(den|num|ratio)(\d+)$/);
+ if (breakdownMatch) {
+ const mainStem = breakdownMatch[1];
+ const breakdownType = breakdownMatch[2] as 'den' | 'num' | 'ratio';
+ const breakdownIndex = parseInt(breakdownMatch[3]);
+ const parsed = parseStem(mainStem);
+ if (!parsed) continue;
+
+ const info: PlotInfo = { url: file.url, stem: filename, breakdownType, breakdownIndex, ...parsed };
+ if (!ds.breakdowns.has(mainStem)) ds.breakdowns.set(mainStem, []);
+ ds.breakdowns.get(mainStem)!.push(info);
+ continue;
+ }
+
+ if (category !== 'var') continue;
+
+ const parsed = parseStem(filename);
+ if (!parsed) continue;
+
+ const info: PlotInfo = { url: file.url, stem: filename, ...parsed };
+ ds.plotsByKey.set(filename, info);
+
+ const { objectType, metric, selection, pdgid, charge, variable } = parsed;
+ ds.objTypesSet.add(objectType);
+
+ if (!ds.metricsSet.has(objectType)) ds.metricsSet.set(objectType, new Set());
+ ds.metricsSet.get(objectType)!.add(metric);
+
+ if (metric === 'eff' && selection !== undefined && pdgid !== undefined && charge !== undefined) {
+ const selKey = makeNavKey(objectType, metric);
+ if (!ds.selectionsSet.has(selKey)) ds.selectionsSet.set(selKey, new Set());
+ ds.selectionsSet.get(selKey)!.add(selection);
+
+ const chKey = makeNavKey(objectType, metric, selection);
+ if (!ds.chargesSet.has(chKey)) ds.chargesSet.set(chKey, new Set());
+ ds.chargesSet.get(chKey)!.add(charge);
+
+ const pgKey = makeNavKey(objectType, metric, selection, charge);
+ if (!ds.pdgidsSet.has(pgKey)) ds.pdgidsSet.set(pgKey, new Set());
+ ds.pdgidsSet.get(pgKey)!.add(pdgid);
+
+ const vKey = makeNavKey(objectType, metric, selection, charge, pdgid);
+ if (!ds.variablesSet.has(vKey)) ds.variablesSet.set(vKey, new Set());
+ ds.variablesSet.get(vKey)!.add(variable);
+ } else if (metric !== 'eff') {
+ const vKey = makeNavKey(objectType, metric);
+ if (!ds.variablesSet.has(vKey)) ds.variablesSet.set(vKey, new Set());
+ ds.variablesSet.get(vKey)!.add(variable);
+ }
+ }
+
+ const BREAKDOWN_ORDER = { den: 0, num: 1, ratio: 2 };
+
+ // Sort dataset names: validation first, then comparison, then others alphabetically
+ const datasetOrder = (name: string) =>
+ name.startsWith('validation_plots_') ? 0 : name.startsWith('comparison_plots_') ? 1 : 2;
+
+ const sortedNames = [...rawDatasets.keys()].sort((a, b) => {
+ const da = datasetOrder(a), db = datasetOrder(b);
+ if (da !== db) return da - db;
+ return a.localeCompare(b);
+ });
+
+ const datasets = new Map();
+
+ for (const name of sortedNames) {
+ const ds = rawDatasets.get(name)!;
+
+ ds.breakdowns.forEach(list => {
+ list.sort((a, b) => {
+ const ta = BREAKDOWN_ORDER[a.breakdownType!] ?? 3;
+ const tb = BREAKDOWN_ORDER[b.breakdownType!] ?? 3;
+ if (ta !== tb) return ta - tb;
+ return (a.breakdownIndex ?? 0) - (b.breakdownIndex ?? 0);
+ });
+ });
+
+ const objectTypes = [...ds.objTypesSet].sort();
+
+ const metricsForObj = new Map();
+ ds.metricsSet.forEach((v, k) => {
+ const order = ['eff', 'fakerate', 'duplrate', 'fakeorduplrate', 'avgOTlen'];
+ const ordered = order.filter(m => v.has(m));
+ const remaining = [...v].filter(m => !order.includes(m)).sort();
+ metricsForObj.set(k, [...ordered, ...remaining]);
+ });
+
+ const selectionsForObjMetric = new Map();
+ ds.selectionsSet.forEach((v, k) => {
+ selectionsForObjMetric.set(k, SELECTION_ORDER.filter(s => v.has(s)));
+ });
+
+ const chargesForObjMetricSel = new Map();
+ ds.chargesSet.forEach((v, k) => {
+ chargesForObjMetricSel.set(k, CHARGE_ORDER.filter(c => v.has(c)));
+ });
+
+ const pdgidsForObjMetricSelCharge = new Map();
+ ds.pdgidsSet.forEach((v, k) => {
+ pdgidsForObjMetricSelCharge.set(k, PDGID_ORDER.filter(p => v.has(p)));
+ });
+
+ const variablesForNav = new Map();
+ ds.variablesSet.forEach((v, k) => {
+ variablesForNav.set(k, sortVariables([...v]));
+ });
+
+ datasets.set(name, {
+ plotsByKey: ds.plotsByKey,
+ breakdowns: ds.breakdowns,
+ objectTypes,
+ metricsForObj,
+ selectionsForObjMetric,
+ chargesForObjMetricSel,
+ pdgidsForObjMetricSelCharge,
+ variablesForNav,
+ });
+ }
+
+ return { isMtvRun, datasets };
+}
+
+export function getEffStem(objectType: string, selection: string, pdgid: number, charge: number, variable: string): string {
+ return `${objectType}_${selection}_${pdgid}_${charge}_eff_${variable}`;
+}
+
+export function getRecoStem(objectType: string, metric: string, variable: string): string {
+ return `${objectType}_${metric}_${variable}`;
+}
+
+// ── CMSSW run parsing ────────────────────────────────────────────────────────
+
+export interface CmssPlotFile {
+ url: string;
+ stem: string;
+ category: string;
+ group: string;
+}
+
+export interface ParsedCmssFiles {
+ categories: string[];
+ groupsForCategory: Map;
+ filesForCategoryGroup: Map;
+}
+
+// Order matters: longer/more-specific prefixes must come before their prefixes.
+const CMSS_GROUP_PREFIXES: [string, string][] = [
+ ['effandfake', 'Efficiency & Fake Rate'],
+ ['dupandfake', 'Duplicate & Fake Rate'],
+ ['distsim', 'Sim Distributions'],
+ ['dist', 'Distributions'],
+ ['resolutions', 'Resolutions'],
+ ['residual', 'Residuals'],
+ ['pvassociation', 'PV Association'],
+ ['hits', 'Hits'],
+];
+
+export const CMSS_GROUP_LABELS: Record = {
+ ...Object.fromEntries(CMSS_GROUP_PREFIXES),
+ other: 'Other',
+};
+
+const CMSS_GROUP_ORDER = [
+ 'effandfake', 'dupandfake', 'dist', 'distsim',
+ 'resolutions', 'residual', 'pvassociation', 'hits', 'other',
+];
+
+function detectCmssGroup(stem: string): string {
+ const lower = stem.toLowerCase();
+ for (const [prefix] of CMSS_GROUP_PREFIXES) {
+ if (lower.startsWith(prefix)) return prefix;
+ }
+ return 'other';
+}
+
+const CATEGORY_LABELS: Record = {
+ plots_ootb: 'OOTB',
+ plots_building_highPtTripletStep: 'High-pT Triplet Step',
+};
+
+export function formatCategoryName(cat: string): string {
+ if (CATEGORY_LABELS[cat]) return CATEGORY_LABELS[cat];
+ const name = cat.replace(/^plots_/, '').replace(/^building_/, '');
+ const spaced = name.replace(/([A-Z])/g, ' $1').trim();
+ return spaced.charAt(0).toUpperCase() + spaced.slice(1);
+}
+
+export function formatCmssStemLabel(stem: string, group: string): string {
+ const prefix = group === 'other' ? '' : group;
+ let label = stem;
+ if (prefix && stem.toLowerCase().startsWith(prefix.toLowerCase())) {
+ label = stem.slice(prefix.length);
+ }
+ if (!label) return stem;
+ label = label.charAt(0).toUpperCase() + label.slice(1);
+ return label.replace(/([A-Z][a-z])/g, ' $1').trim();
+}
+
+export function parseCmssFiles(decompressedFiles: { name: string; url: string }[]): ParsedCmssFiles {
+ const categoriesSet = new Set();
+ const groupsForCategorySet = new Map>();
+ const filesForCategoryGroup = new Map();
+
+ for (const file of decompressedFiles) {
+ if (file.name.includes('/mtv/')) continue;
+ const match = file.name.match(/\/([^/]+)\/([^/]+)\.pdf$/);
+ if (!match) continue;
+
+ const category = match[1];
+ const stem = match[2];
+ const group = detectCmssGroup(stem);
+
+ categoriesSet.add(category);
+ if (!groupsForCategorySet.has(category)) groupsForCategorySet.set(category, new Set());
+ groupsForCategorySet.get(category)!.add(group);
+
+ const key = `${category}\x00${group}`;
+ if (!filesForCategoryGroup.has(key)) filesForCategoryGroup.set(key, []);
+ filesForCategoryGroup.get(key)!.push({ url: file.url, stem, category, group });
+ }
+
+ const sortCat = (cat: string) => {
+ if (cat === 'plots_ootb') return 0;
+ if (cat.startsWith('plots_building_')) return 2;
+ return 1;
+ };
+ const categories = [...categoriesSet].sort((a, b) => {
+ const d = sortCat(a) - sortCat(b);
+ return d !== 0 ? d : a.localeCompare(b);
+ });
+
+ const groupsForCategory = new Map();
+ groupsForCategorySet.forEach((v, k) => {
+ groupsForCategory.set(k, CMSS_GROUP_ORDER.filter(g => v.has(g)));
+ });
+
+ return { categories, groupsForCategory, filesForCategoryGroup };
+}
diff --git a/tsconfig.app.json b/tsconfig.app.json
new file mode 100644
index 00000000..7f42e5f7
--- /dev/null
+++ b/tsconfig.app.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
+ "target": "es2023",
+ "lib": ["ES2023", "DOM"],
+ "module": "esnext",
+ "types": ["vite/client"],
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "jsx": "react-jsx",
+
+ /* Linting */
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true
+ },
+ "include": ["src"]
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 00000000..1ffef600
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "files": [],
+ "references": [
+ { "path": "./tsconfig.app.json" },
+ { "path": "./tsconfig.node.json" }
+ ]
+}
diff --git a/tsconfig.node.json b/tsconfig.node.json
new file mode 100644
index 00000000..d3c52ea6
--- /dev/null
+++ b/tsconfig.node.json
@@ -0,0 +1,24 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
+ "target": "es2023",
+ "lib": ["ES2023"],
+ "module": "esnext",
+ "types": ["node"],
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+
+ /* Linting */
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 00000000..d40796b4
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,8 @@
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+
+// https://vite.dev/config/
+export default defineConfig({
+ plugins: [react()],
+ base: './',
+})