Compare commits

...

6 Commits

Author SHA1 Message Date
914005e00e feat(libraries): add export (JSON download) and import (JSON restore) for library symbols 2026-07-04 16:07:13 -03:00
75637bc265 fix(mutations): auto-dismiss suggestion cards with fade-out after approve/reject
Cards now fade out and disappear 1.5s after being approved or rejected.
Added resolvedIds tracking in AIChatPanel to prevent cards from
reappearing in previousMutationSuggestions after resolution.
onRenameApplied now called on reject too (was only on approve).
2026-07-04 15:41:03 -03:00
87df739575 fix(chat): remove hardcoded default model from session creation 2026-07-04 15:40:58 -03:00
17fd065773 chore: add .env to .gitignore 2026-07-04 15:40:54 -03:00
6c53789db7 chore(deps): fix npm audit vulnerabilities
Bump transitive dependencies (esbuild, form-data, js-yaml, orval) via
npm audit fix to resolve 4 advisories (1 high, 2 moderate, 1 low).
2026-07-04 14:50:43 -03:00
e027e9cbe0 feat(theme): add light theme with theme toggle and fix workspace dialog labels
- Add ThemeProvider with localStorage persistence and default dark theme
- Add ThemeToggle (sun/moon) to the header's right corner
- Restructure index.css: light palette in :root, dark palette in .dark
- Apply stored theme in index.html to avoid flash on load
- Fix mislabeled "Create/Edit Home" text in WorkspaceDialog to "Workspace"
2026-07-04 14:22:40 -03:00
26 changed files with 1116 additions and 362 deletions

1
.gitignore vendored
View File

@ -11,6 +11,7 @@ node_modules
dist
dist-ssr
*.local
.env
# Editor directories and files
.vscode/*

View File

@ -5,6 +5,17 @@
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Decompile.AI</title>
<script>
(function () {
try {
var stored = localStorage.getItem("decompile-ai-theme");
var theme = stored === "light" || stored === "dark" ? stored : "dark";
document.documentElement.classList.add(theme);
} catch (e) {
document.documentElement.classList.add("dark");
}
})();
</script>
</head>
<body>
<div id="root"></div>

531
package-lock.json generated
View File

@ -92,7 +92,6 @@
"integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.29.7",
"@babel/generator": "^7.29.7",
@ -312,12 +311,12 @@
}
},
"node_modules/@commander-js/extra-typings": {
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/@commander-js/extra-typings/-/extra-typings-14.0.0.tgz",
"integrity": "sha512-hIn0ncNaJRLkZrxBIp5AsW/eXEHNKYQBh0aPdoUqNgD+Io3NIykQqpKFyKcuasZhicGaEZJX/JBSIkZ4e5x8Dg==",
"version": "15.0.0",
"resolved": "https://registry.npmjs.org/@commander-js/extra-typings/-/extra-typings-15.0.0.tgz",
"integrity": "sha512-yeJlba62xqmkgELUsn7356MEnzLLu/fw2x4lofFqGnXh6YysRdEs2BaLeLtg1+KU0AXvMeqQvTTp+3hBEBK+EA==",
"license": "MIT",
"peerDependencies": {
"commander": "~14.0.0"
"commander": "~15.0.0"
}
},
"node_modules/@dagrejs/dagre": {
@ -335,6 +334,27 @@
"integrity": "sha512-IvcV6FduIIAmLwnH+yun+QtV36SC7mERqa86aClNqmMN09WhmPPYU8ckHrZBozErf+UvHPWOTJYaGYiIcs0DgA==",
"license": "MIT"
},
"node_modules/@emnapi/core": {
"version": "1.11.2",
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.2.tgz",
"integrity": "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==",
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/wasi-threads": "1.2.2",
"tslib": "^2.4.0"
}
},
"node_modules/@emnapi/runtime": {
"version": "1.11.2",
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz",
"integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==",
"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",
@ -346,9 +366,9 @@
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz",
"integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz",
"integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==",
"cpu": [
"ppc64"
],
@ -362,9 +382,9 @@
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz",
"integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz",
"integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==",
"cpu": [
"arm"
],
@ -378,9 +398,9 @@
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz",
"integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz",
"integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==",
"cpu": [
"arm64"
],
@ -394,9 +414,9 @@
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz",
"integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz",
"integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==",
"cpu": [
"x64"
],
@ -410,9 +430,9 @@
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz",
"integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz",
"integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==",
"cpu": [
"arm64"
],
@ -426,9 +446,9 @@
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz",
"integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz",
"integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==",
"cpu": [
"x64"
],
@ -442,9 +462,9 @@
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz",
"integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz",
"integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==",
"cpu": [
"arm64"
],
@ -458,9 +478,9 @@
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz",
"integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz",
"integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==",
"cpu": [
"x64"
],
@ -474,9 +494,9 @@
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz",
"integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz",
"integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==",
"cpu": [
"arm"
],
@ -490,9 +510,9 @@
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz",
"integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz",
"integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==",
"cpu": [
"arm64"
],
@ -506,9 +526,9 @@
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz",
"integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz",
"integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==",
"cpu": [
"ia32"
],
@ -522,9 +542,9 @@
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz",
"integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz",
"integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==",
"cpu": [
"loong64"
],
@ -538,9 +558,9 @@
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz",
"integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz",
"integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==",
"cpu": [
"mips64el"
],
@ -554,9 +574,9 @@
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz",
"integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz",
"integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==",
"cpu": [
"ppc64"
],
@ -570,9 +590,9 @@
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz",
"integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz",
"integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==",
"cpu": [
"riscv64"
],
@ -586,9 +606,9 @@
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz",
"integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz",
"integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==",
"cpu": [
"s390x"
],
@ -602,9 +622,9 @@
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz",
"integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz",
"integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==",
"cpu": [
"x64"
],
@ -618,9 +638,9 @@
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz",
"integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz",
"integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==",
"cpu": [
"arm64"
],
@ -634,9 +654,9 @@
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz",
"integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz",
"integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==",
"cpu": [
"x64"
],
@ -650,9 +670,9 @@
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz",
"integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz",
"integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==",
"cpu": [
"arm64"
],
@ -666,9 +686,9 @@
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz",
"integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz",
"integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==",
"cpu": [
"x64"
],
@ -682,9 +702,9 @@
}
},
"node_modules/@esbuild/openharmony-arm64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz",
"integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz",
"integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==",
"cpu": [
"arm64"
],
@ -698,9 +718,9 @@
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz",
"integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz",
"integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==",
"cpu": [
"x64"
],
@ -714,9 +734,9 @@
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz",
"integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz",
"integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==",
"cpu": [
"arm64"
],
@ -730,9 +750,9 @@
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz",
"integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz",
"integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==",
"cpu": [
"ia32"
],
@ -746,9 +766,9 @@
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz",
"integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz",
"integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==",
"cpu": [
"x64"
],
@ -1088,27 +1108,27 @@
}
},
"node_modules/@orval/angular": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/@orval/angular/-/angular-8.15.0.tgz",
"integrity": "sha512-aFArKav6FuZ7ZTm1GorkRLJKtHza3TSy04qcuNnb84qlJ83DHAhxoDVyBXQUmSYtwbc7ckzTveMofiA5v13wnw==",
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/@orval/angular/-/angular-8.20.0.tgz",
"integrity": "sha512-MavI1m1bqw/p2xNsPYoJ2hvk1fo898OpeVTpLRFO3bckjfpAFyKE+wyi89Ces33qFC9NTedEwMKvt0nQresBgw==",
"license": "MIT",
"dependencies": {
"@orval/core": "8.15.0"
"@orval/core": "8.20.0"
}
},
"node_modules/@orval/axios": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/@orval/axios/-/axios-8.15.0.tgz",
"integrity": "sha512-jdsEPvmxlmbQ9YVsLWmzxphN+x+xTYst6WmnnEfz/QMicOEzy1RzWp9YzmpHUT+EKs6pdYWrwagxph74R3qV9A==",
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/@orval/axios/-/axios-8.20.0.tgz",
"integrity": "sha512-YPEAVHGCJ+8LoKCFNQjcJPFpIzyAP+B6QjqkrYZ4i/L00JkVaqy0kRCb9q0+G/LtmPGoGfl/RB3lPlEeApaGdA==",
"license": "MIT",
"dependencies": {
"@orval/core": "8.15.0"
"@orval/core": "8.20.0"
}
},
"node_modules/@orval/core": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/@orval/core/-/core-8.15.0.tgz",
"integrity": "sha512-YcUZQWnCU/0Dzf1aISQ+XY7ghL248Ctfv++2Qyr4nUuZ2UWG3bdO1FAdDUGULYTDAgfb5B3iiJIYsxR0iDKk5A==",
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/@orval/core/-/core-8.20.0.tgz",
"integrity": "sha512-PYZlOzDmD5O+GlnamrvVkX4jdo8B3LuprlmLT7tct8wknwiKf/1SDxT8pR9cv3gcx8IlCQA+7zPSp41uX2LFkQ==",
"license": "MIT",
"dependencies": {
"@scalar/openapi-types": "0.8.0",
@ -1133,35 +1153,33 @@
}
},
"node_modules/@orval/effect": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/@orval/effect/-/effect-8.15.0.tgz",
"integrity": "sha512-gF2P1tbe08KbBuEaLHtwliabs599qYvypzy8Aj6j+aRY4EiSCRNZYs1U7lQY52jZrvwqv2L0llQl4qkelkOMoA==",
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/@orval/effect/-/effect-8.20.0.tgz",
"integrity": "sha512-BPquvm7bhxIp8hJ/DHaugADjldhqZUvnHXF2caC520/Kx7/OWbvWYjHWQtZNgeNW3gLmB1OvOtt52q3IKhV2Jw==",
"license": "MIT",
"dependencies": {
"@orval/core": "8.15.0",
"@orval/core": "8.20.0",
"remeda": "^2.33.6"
}
},
"node_modules/@orval/fetch": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/@orval/fetch/-/fetch-8.15.0.tgz",
"integrity": "sha512-EyTOblRIo6DtP80F1RzfgLIbsQRu0as8sbpU0Kd46Zj248SeWE/5if4cfrjFR1lzSL+9pDBaHlA2JA7BNu08Rg==",
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/@orval/fetch/-/fetch-8.20.0.tgz",
"integrity": "sha512-u2GDK6Fjcmlht8LpD7cNorLEifL96Du20zP1gqOce6Zc+Cd/nS4KMaLrYvf74nvjU8TbG8iIsxp0uN0+Y3iWRQ==",
"license": "MIT",
"dependencies": {
"@orval/core": "8.15.0",
"@scalar/openapi-types": "0.8.0"
"@orval/core": "8.20.0"
}
},
"node_modules/@orval/hono": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/@orval/hono/-/hono-8.15.0.tgz",
"integrity": "sha512-UhYn0Z+P/Fo3HmxIB5RcJnnhdTtSge7ewhenauJCooMF1yrQjlPyHsKIxmjolZjZIKae2igsZekz1tCVtKi26g==",
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/@orval/hono/-/hono-8.20.0.tgz",
"integrity": "sha512-/9VoD5NmM+DuL+YUs7wTpJ22KwhmCSybvxTNeGv6oIFLTD5C7JJzM3ewlC9dvUNT6K30sjRBx784WMUChRlWdQ==",
"license": "MIT",
"dependencies": {
"@orval/core": "8.15.0",
"@orval/zod": "8.15.0",
"fs-extra": "^11.3.2",
"remeda": "^2.33.6"
"@orval/core": "8.20.0",
"@orval/zod": "8.20.0",
"fs-extra": "^11.3.2"
},
"peerDependencies": {
"typescript": ">=5"
@ -1173,64 +1191,63 @@
}
},
"node_modules/@orval/mcp": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/@orval/mcp/-/mcp-8.15.0.tgz",
"integrity": "sha512-VwpBhIR/rD7o8XGs4bIOnXwAjpRrJTusBBUG50VOgAtPA88cM8WDPMND3Zt7Wr8iP8xZmzRBw105n9qhoQ5xUg==",
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/@orval/mcp/-/mcp-8.20.0.tgz",
"integrity": "sha512-8EuZdlscZiS89AHwaXjMJl8lbmlWJBpFkna3UJGQSw4pbGg08FOh9HbXvxiN5Lr/G9cCcnwIQ2gk3TmPfH3MGA==",
"license": "MIT",
"dependencies": {
"@orval/core": "8.15.0",
"@orval/fetch": "8.15.0",
"@orval/zod": "8.15.0"
"@orval/core": "8.20.0",
"@orval/fetch": "8.20.0",
"@orval/zod": "8.20.0"
}
},
"node_modules/@orval/mock": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/@orval/mock/-/mock-8.15.0.tgz",
"integrity": "sha512-PdIvn/y7u6ktoVEWLm9XrMdx4KPlsv0ww9Fl6H+ibL3yB1xX3JWe/e1YKlA1aDyGyBDTyDAdTWNJHoAtKnqEug==",
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/@orval/mock/-/mock-8.20.0.tgz",
"integrity": "sha512-6hnDtAFdP64SHuKlr1aOqps3RXPs4FUMUh3TWXrXvOf0s2HmWzYZOecNqowlswFFKLRWrh5fF5l1Nr0Q9zai7g==",
"license": "MIT",
"dependencies": {
"@orval/core": "8.15.0",
"@orval/core": "8.20.0",
"remeda": "^2.33.6"
}
},
"node_modules/@orval/query": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/@orval/query/-/query-8.15.0.tgz",
"integrity": "sha512-NmMiObzXCHFVj0fIDVT9xpBknmEXNl+N+1vpPnQc8H2XQ+XpbMMaf6MJKXriFV2QhiZ7myjLomofQEi1impwzg==",
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/@orval/query/-/query-8.20.0.tgz",
"integrity": "sha512-PTExKtAUtaYfkS6+xvgSvrIWxxmiOHLuCZwYnVc5U7c6Nthw+sS4dF3zFiOd80zX2vFSadxdveOxTFDks7AXIw==",
"license": "MIT",
"dependencies": {
"@orval/core": "8.15.0",
"@orval/fetch": "8.15.0",
"@orval/core": "8.20.0",
"@orval/fetch": "8.20.0",
"remeda": "^2.33.6"
}
},
"node_modules/@orval/solid-start": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/@orval/solid-start/-/solid-start-8.15.0.tgz",
"integrity": "sha512-2lPvOp6WBJDEF51aZmuXZagUBmckrkyNFrlpJCAceCJaDhUT86H5f+6p1wdVgjsH672Hud7oHWoqlIf5XQyABg==",
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/@orval/solid-start/-/solid-start-8.20.0.tgz",
"integrity": "sha512-xwH+VwN02JmpE5gemwSVw/jwsWzPUkaxkBm10Pa6Yr7Eu+tUtmot4upJAmQsLXq9ep+EMM97gtVuMbmT69SAUg==",
"license": "MIT",
"dependencies": {
"@orval/core": "8.15.0",
"@scalar/openapi-types": "0.8.0"
"@orval/core": "8.20.0"
}
},
"node_modules/@orval/swr": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/@orval/swr/-/swr-8.15.0.tgz",
"integrity": "sha512-BEu6lQJzZdlVDLd2xppsrSj29EgHVD6owHhaldyIiz1HMezO895aNCsqoFbMK8rYgB4x+XemR40Wr+8ivNvP9A==",
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/@orval/swr/-/swr-8.20.0.tgz",
"integrity": "sha512-92TFUIgllvdEY+34tcsB8BzGM6CaxYW8/P47xDDODvsksjHV0OcpkR9Hh1setzjJu7WpAyAGLsL0Sy71REfLug==",
"license": "MIT",
"dependencies": {
"@orval/core": "8.15.0",
"@orval/fetch": "8.15.0"
"@orval/core": "8.20.0",
"@orval/fetch": "8.20.0"
}
},
"node_modules/@orval/zod": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/@orval/zod/-/zod-8.15.0.tgz",
"integrity": "sha512-5970pTfgsqUtygNxoLVlL6dKAv31UogAkBCRfmgQPSzr4sMXKya2WhLl1ERSNYMr9A+iGBWVR5ZmYtPGhAaVDg==",
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/@orval/zod/-/zod-8.20.0.tgz",
"integrity": "sha512-6/bC1i4MGcMwksEPURs8sR7QDU3ymFV0fKvTEttvdhacwLahz58DZZtXLYauVVEZV1cMSGLUX5jE4axmO3S+6w==",
"license": "MIT",
"dependencies": {
"@orval/core": "8.15.0",
"@orval/core": "8.20.0",
"remeda": "^2.33.6"
}
},
@ -3637,21 +3654,21 @@
"license": "MIT"
},
"node_modules/@scalar/helpers": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@scalar/helpers/-/helpers-0.8.1.tgz",
"integrity": "sha512-yuiuBCadP5bjAnIv23QvifVN/NaMi9xBF6b8Wdk4QOzwzLPJmp699MAdf33J0A5i2qKcvnu32iz/VkEJmQRe5g==",
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/@scalar/helpers/-/helpers-0.9.0.tgz",
"integrity": "sha512-M34CLRCttqC1bXthI/QSzQj0s5C6nrU2PFWf/vOT3RpycbiGDGQbqR+5RfFzpOIQvRqbHfNdcRbeiZBw+vCbkQ==",
"license": "MIT",
"engines": {
"node": ">=22"
}
},
"node_modules/@scalar/json-magic": {
"version": "0.12.15",
"resolved": "https://registry.npmjs.org/@scalar/json-magic/-/json-magic-0.12.15.tgz",
"integrity": "sha512-ZYgdYZ0jSZXQeyhG2lJ20FjzvKsaDRXk4bPguF/Ytl2nGBh9a6RIIr9NvVy4zAD67a/ahm+xipXlfoR1KtB5fg==",
"version": "0.12.17",
"resolved": "https://registry.npmjs.org/@scalar/json-magic/-/json-magic-0.12.17.tgz",
"integrity": "sha512-Vw2nrUDIjhvMP6vxFtkiiFlabJ6SyTtfn1BsOxgnr1hIB+/rkngMguiDzl5em21VjyfFGIoADia+QWKM2hdcdA==",
"license": "MIT",
"dependencies": {
"@scalar/helpers": "0.8.1",
"@scalar/helpers": "0.9.0",
"pathe": "^2.0.3",
"yaml": "^2.8.3"
},
@ -3660,13 +3677,13 @@
}
},
"node_modules/@scalar/openapi-parser": {
"version": "0.28.6",
"resolved": "https://registry.npmjs.org/@scalar/openapi-parser/-/openapi-parser-0.28.6.tgz",
"integrity": "sha512-gCl4r+rpmO+CKfwmmwI+GJVFhuNut7e6beUcD/xazanH4epkLT7V9ZgMv9YKbUBE73fkrOv6NJympeEGiU8aUw==",
"version": "0.28.8",
"resolved": "https://registry.npmjs.org/@scalar/openapi-parser/-/openapi-parser-0.28.8.tgz",
"integrity": "sha512-BO98D3TLfKNL80UnE1sIAmI6DQRmOaH0AHnlAooTn1sQjNbRDaeHyRO53EEjo7HjFEdHeQtiRzoXmMB4jvt8AA==",
"license": "MIT",
"dependencies": {
"@scalar/helpers": "0.8.1",
"@scalar/json-magic": "0.12.15",
"@scalar/helpers": "0.9.0",
"@scalar/json-magic": "0.12.17",
"@scalar/openapi-types": "0.9.1",
"@scalar/openapi-upgrader": "0.2.9",
"ajv": "^8.17.1",
@ -3694,7 +3711,6 @@
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
"integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==",
"license": "MIT",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@ -4179,7 +4195,6 @@
"integrity": "sha512-5vtOqGQr4NJKeEzV441FcOi2MeG9UTWq9LqVLGneDdu4vlX17H8kQ2PA2UmNwCUGPVDj4oBjNhS7ReVEIWJJrg==",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"undici-types": "~7.18.0"
}
@ -4195,7 +4210,6 @@
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.16.tgz",
"integrity": "sha512-esJiCAnl0kfpNdE69f3So4WJUXy95dLZydX0KwK46riIHDzHM7O9Vtf9xCHW0PXIqvgqNrswl522kA/5yx+F4w==",
"license": "MIT",
"peer": true,
"dependencies": {
"csstype": "^3.2.2"
}
@ -4206,7 +4220,6 @@
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
"devOptional": true,
"license": "MIT",
"peer": true,
"peerDependencies": {
"@types/react": "^19.2.0"
}
@ -4272,7 +4285,6 @@
"integrity": "sha512-A0M6ua6H252bVjPvvtSgl2QA4+ET9S5Mtkb2GDyTxIhH/C4qDItT7RQNO5PhMC6NXGYXOR9dIalcDDgBKT7oFA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.60.1",
"@typescript-eslint/types": "8.60.1",
@ -4508,7 +4520,6 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@ -4594,24 +4605,6 @@
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"license": "MIT"
},
"node_modules/ansi-colors": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
"integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@ -4712,7 +4705,6 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.10.12",
"caniuse-lite": "^1.0.30001782",
@ -4870,13 +4862,12 @@
}
},
"node_modules/commander": {
"version": "14.0.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz",
"integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==",
"version": "15.0.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz",
"integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=20"
"node": ">=22.12.0"
}
},
"node_modules/compare-versions": {
@ -5052,19 +5043,6 @@
"node": ">=10.13.0"
}
},
"node_modules/enquirer": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz",
"integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==",
"license": "MIT",
"dependencies": {
"ansi-colors": "^4.1.1",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8.6"
}
},
"node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
@ -5123,9 +5101,9 @@
}
},
"node_modules/esbuild": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz",
"integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz",
"integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==",
"hasInstallScript": true,
"license": "MIT",
"bin": {
@ -5135,32 +5113,32 @@
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.28.0",
"@esbuild/android-arm": "0.28.0",
"@esbuild/android-arm64": "0.28.0",
"@esbuild/android-x64": "0.28.0",
"@esbuild/darwin-arm64": "0.28.0",
"@esbuild/darwin-x64": "0.28.0",
"@esbuild/freebsd-arm64": "0.28.0",
"@esbuild/freebsd-x64": "0.28.0",
"@esbuild/linux-arm": "0.28.0",
"@esbuild/linux-arm64": "0.28.0",
"@esbuild/linux-ia32": "0.28.0",
"@esbuild/linux-loong64": "0.28.0",
"@esbuild/linux-mips64el": "0.28.0",
"@esbuild/linux-ppc64": "0.28.0",
"@esbuild/linux-riscv64": "0.28.0",
"@esbuild/linux-s390x": "0.28.0",
"@esbuild/linux-x64": "0.28.0",
"@esbuild/netbsd-arm64": "0.28.0",
"@esbuild/netbsd-x64": "0.28.0",
"@esbuild/openbsd-arm64": "0.28.0",
"@esbuild/openbsd-x64": "0.28.0",
"@esbuild/openharmony-arm64": "0.28.0",
"@esbuild/sunos-x64": "0.28.0",
"@esbuild/win32-arm64": "0.28.0",
"@esbuild/win32-ia32": "0.28.0",
"@esbuild/win32-x64": "0.28.0"
"@esbuild/aix-ppc64": "0.28.1",
"@esbuild/android-arm": "0.28.1",
"@esbuild/android-arm64": "0.28.1",
"@esbuild/android-x64": "0.28.1",
"@esbuild/darwin-arm64": "0.28.1",
"@esbuild/darwin-x64": "0.28.1",
"@esbuild/freebsd-arm64": "0.28.1",
"@esbuild/freebsd-x64": "0.28.1",
"@esbuild/linux-arm": "0.28.1",
"@esbuild/linux-arm64": "0.28.1",
"@esbuild/linux-ia32": "0.28.1",
"@esbuild/linux-loong64": "0.28.1",
"@esbuild/linux-mips64el": "0.28.1",
"@esbuild/linux-ppc64": "0.28.1",
"@esbuild/linux-riscv64": "0.28.1",
"@esbuild/linux-s390x": "0.28.1",
"@esbuild/linux-x64": "0.28.1",
"@esbuild/netbsd-arm64": "0.28.1",
"@esbuild/netbsd-x64": "0.28.1",
"@esbuild/openbsd-arm64": "0.28.1",
"@esbuild/openbsd-x64": "0.28.1",
"@esbuild/openharmony-arm64": "0.28.1",
"@esbuild/sunos-x64": "0.28.1",
"@esbuild/win32-arm64": "0.28.1",
"@esbuild/win32-ia32": "0.28.1",
"@esbuild/win32-x64": "0.28.1"
}
},
"node_modules/escalade": {
@ -5192,7 +5170,6 @@
"integrity": "sha512-AyIKhnOBuOAdueD7RB3xB+YeAWScb9jHsJBgH2Hcde8InP5JYhqrRR6iTMHyTEwgENK54Cp44e4v8BwNhsuHuw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.2",
@ -5249,7 +5226,6 @@
"integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"eslint-config-prettier": "bin/cli.js"
},
@ -5486,9 +5462,9 @@
"license": "MIT"
},
"node_modules/fast-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz",
"integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==",
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.3.tgz",
"integrity": "sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==",
"funding": [
{
"type": "github",
@ -5618,16 +5594,16 @@
}
},
"node_modules/form-data": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz",
"integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
"hasown": "^2.0.4",
"mime-types": "^2.1.35"
},
"engines": {
"node": ">= 6"
@ -5642,9 +5618,9 @@
}
},
"node_modules/fs-extra": {
"version": "11.3.5",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.5.tgz",
"integrity": "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==",
"version": "11.3.6",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.6.tgz",
"integrity": "sha512-w8ZNZr2mKIc7qeNaQ9AVPT1+iFaI+Avd4xudVOvdDJ8VytREi1Ft5Ih7hd9jjehod8vAM5GMsfQ/TpPf4EyoEA==",
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.0",
@ -6131,9 +6107,19 @@
"license": "MIT"
},
"node_modules/js-yaml": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
"integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/puzrin"
},
{
"type": "github",
"url": "https://github.com/sponsors/nodeca"
}
],
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
@ -7623,36 +7609,35 @@
}
},
"node_modules/orval": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/orval/-/orval-8.15.0.tgz",
"integrity": "sha512-o4Sl2VLCP0Yu5wv5ubN477Fze8QDXSY0vwjB0U9z4Ew1YJrIAX/6D5hG2PsD8KIoZtLCgrDFj5t/otqmNJtBdw==",
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/orval/-/orval-8.20.0.tgz",
"integrity": "sha512-iL7aA/seKMxw1Ga7sJTqgT4+LXsE3JcPiYclJtjSTlPa9kai8cO39zX2U+6M1JeVftmjs/BDE2r7quE2nZqfjg==",
"license": "MIT",
"dependencies": {
"@commander-js/extra-typings": "^14.0.0",
"@orval/angular": "8.15.0",
"@orval/axios": "8.15.0",
"@orval/core": "8.15.0",
"@orval/effect": "8.15.0",
"@orval/fetch": "8.15.0",
"@orval/hono": "8.15.0",
"@orval/mcp": "8.15.0",
"@orval/mock": "8.15.0",
"@orval/query": "8.15.0",
"@orval/solid-start": "8.15.0",
"@orval/swr": "8.15.0",
"@orval/zod": "8.15.0",
"@scalar/json-magic": "^0.12.8",
"@scalar/openapi-parser": "^0.28.5",
"@commander-js/extra-typings": "^15.0.0",
"@orval/angular": "8.20.0",
"@orval/axios": "8.20.0",
"@orval/core": "8.20.0",
"@orval/effect": "8.20.0",
"@orval/fetch": "8.20.0",
"@orval/hono": "8.20.0",
"@orval/mcp": "8.20.0",
"@orval/mock": "8.20.0",
"@orval/query": "8.20.0",
"@orval/solid-start": "8.20.0",
"@orval/swr": "8.20.0",
"@orval/zod": "8.20.0",
"@scalar/json-magic": "^0.12.16",
"@scalar/openapi-parser": "^0.28.7",
"@scalar/openapi-types": "0.8.0",
"chokidar": "^5.0.0",
"commander": "^14.0.2",
"enquirer": "^2.4.1",
"commander": "^15.0.0",
"execa": "^9.6.1",
"find-up": "8.0.0",
"fs-extra": "^11.3.2",
"get-tsconfig": "^4.14.0",
"jiti": "^2.6.1",
"js-yaml": "4.1.1",
"js-yaml": "4.2.0",
"remeda": "^2.33.6",
"string-argv": "^0.3.2",
"typedoc": "^0.28.19",
@ -7852,7 +7837,6 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@ -7904,7 +7888,6 @@
"integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==",
"devOptional": true,
"license": "MIT",
"peer": true,
"bin": {
"prettier": "bin/prettier.cjs"
},
@ -8074,7 +8057,6 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz",
"integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@ -8084,7 +8066,6 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz",
"integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"scheduler": "^0.27.0"
},
@ -8351,9 +8332,9 @@
}
},
"node_modules/remeda": {
"version": "2.37.0",
"resolved": "https://registry.npmjs.org/remeda/-/remeda-2.37.0.tgz",
"integrity": "sha512-wN6BXWua0t4o7vDamqc27J3VRxnokG9cDezsFN2nOnt2JD/IkJQHTYqM6UvmEctAZETAoviwEFQZJO3kZ4Ohew==",
"version": "2.39.0",
"resolved": "https://registry.npmjs.org/remeda/-/remeda-2.39.0.tgz",
"integrity": "sha512-3Ki8dU1o3OVu4dwIQ2Pj+yiuP7OnEbmWAGmJ3yDRqopily5jsj8NWzPvbS89H85d6UdONKEcUnrfuHY6jN9vyw==",
"license": "MIT",
"engines": {
"node": ">=18.0.0"
@ -8520,18 +8501,6 @@
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-final-newline": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz",
@ -8680,7 +8649,6 @@
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.28.19.tgz",
"integrity": "sha512-wKh+lhdmMFivMlc6vRRcMGXeGEHGU2g8a2CkPTJjJlwRf1iXbimWIPcFolCqe4E0d/FRtGszpIrsp3WLpDB8Pw==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"@gerrit0/mini-shiki": "^3.23.0",
"lunr": "^2.3.9",
@ -8728,7 +8696,6 @@
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@ -9011,7 +8978,6 @@
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz",
"integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==",
"license": "MIT",
"peer": true,
"dependencies": {
"lightningcss": "^1.32.0",
"picomatch": "^4.0.4",
@ -9162,7 +9128,6 @@
"integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==",
"dev": true,
"license": "MIT",
"peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}

View File

@ -1,5 +1,5 @@
/**
* Generated by orval v8.15.0 🍺
* Generated by orval v8.20.0 🍺
* Do not edit manually.
* OpenAPI definition
* OpenAPI spec version: v0
@ -33,6 +33,21 @@ import { customInstance } from "../../api";
type SecondParameter<T extends (...args: never) => unknown> = Parameters<T>[1];
const withQueryKey = <T extends object, K>(query: T, queryKey: K): T & { queryKey: K } => {
const result = { queryKey } as T & { queryKey: K };
for (const key of Object.keys(query)) {
// The explicit queryKey always wins, matching the previous
// `{ ...query, queryKey }` spread where it was set last.
if (key === "queryKey") continue;
Object.defineProperty(result, key, {
enumerable: true,
configurable: true,
get: () => (query as Record<string, unknown>)[key],
});
}
return result;
};
/**
* Retrieve all static analyses performed on a binary.
* @summary List static analyses for a binary
@ -140,7 +155,7 @@ export function useListAnalyses<TData = Awaited<ReturnType<typeof listAnalyses>>
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}
/**
@ -250,7 +265,7 @@ export function useGetAnalysis<TData = Awaited<ReturnType<typeof getAnalysis>>,
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}
/**
@ -376,7 +391,7 @@ export function useListStructures<
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}
/**
@ -486,7 +501,7 @@ export function useListStrings<TData = Awaited<ReturnType<typeof listStrings>>,
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}
/**
@ -596,7 +611,7 @@ export function useListSegments<TData = Awaited<ReturnType<typeof listSegments>>
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}
/**
@ -706,7 +721,7 @@ export function useListImports<TData = Awaited<ReturnType<typeof listImports>>,
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}
/**
@ -832,7 +847,7 @@ export function useListGlobalVars<
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}
/**
@ -965,7 +980,7 @@ export function useListFunctions<
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}
/**
@ -1075,5 +1090,5 @@ export function useListEnums<TData = Awaited<ReturnType<typeof listEnums>>, TErr
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}

View File

@ -1,5 +1,5 @@
/**
* Generated by orval v8.15.0 🍺
* Generated by orval v8.20.0 🍺
* Do not edit manually.
* OpenAPI definition
* OpenAPI spec version: v0
@ -84,6 +84,23 @@ export interface LibraryResponse {
createdAt?: string;
}
export interface SymbolEntry {
moduleName?: string;
className?: string;
symbolName?: string;
ordinal?: number;
symbolType?: string;
parameters?: string;
category?: string;
}
export interface Library {
name?: string;
description?: string;
aliases?: string[];
symbols?: SymbolEntry[];
}
export type JobResponseType = (typeof JobResponseType)[keyof typeof JobResponseType];
export const JobResponseType = {
@ -160,8 +177,8 @@ export interface PageableObject {
paged?: boolean;
pageNumber?: number;
pageSize?: number;
unpaged?: boolean;
sort?: SortObject;
unpaged?: boolean;
}
export interface PageLibrarySymbolResponse {
@ -171,10 +188,10 @@ export interface PageLibrarySymbolResponse {
content?: LibrarySymbolResponse[];
number?: number;
pageable?: PageableObject;
numberOfElements?: number;
sort?: SortObject;
first?: boolean;
last?: boolean;
sort?: SortObject;
numberOfElements?: number;
empty?: boolean;
}
@ -357,10 +374,10 @@ export interface PageFunctionSummaryResponse {
content?: FunctionSummaryResponse[];
number?: number;
pageable?: PageableObject;
numberOfElements?: number;
sort?: SortObject;
first?: boolean;
last?: boolean;
sort?: SortObject;
numberOfElements?: number;
empty?: boolean;
}

View File

@ -1,5 +1,5 @@
/**
* Generated by orval v8.15.0 🍺
* Generated by orval v8.20.0 🍺
* Do not edit manually.
* OpenAPI definition
* OpenAPI spec version: v0
@ -35,6 +35,21 @@ import { customInstance } from "../../api";
type SecondParameter<T extends (...args: never) => unknown> = Parameters<T>[1];
const withQueryKey = <T extends object, K>(query: T, queryKey: K): T & { queryKey: K } => {
const result = { queryKey } as T & { queryKey: K };
for (const key of Object.keys(query)) {
// The explicit queryKey always wins, matching the previous
// `{ ...query, queryKey }` spread where it was set last.
if (key === "queryKey") continue;
Object.defineProperty(result, key, {
enumerable: true,
configurable: true,
get: () => (query as Record<string, unknown>)[key],
});
}
return result;
};
/**
* Retrieve all binaries belonging to a project, optionally filtered by filename.
* @summary List binaries of a project
@ -153,7 +168,7 @@ export function useGetBinaries<TData = Awaited<ReturnType<typeof getBinaries>>,
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}
/**
@ -370,7 +385,7 @@ export function useGetDependencies<
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}
/**
@ -651,7 +666,7 @@ export function useGetBinary<TData = Awaited<ReturnType<typeof getBinary>>, TErr
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}
/**
@ -852,7 +867,7 @@ export function useDownloadBinary<
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}
/**

View File

@ -1,5 +1,5 @@
/**
* Generated by orval v8.15.0 🍺
* Generated by orval v8.20.0 🍺
* Do not edit manually.
* OpenAPI definition
* OpenAPI spec version: v0
@ -26,6 +26,21 @@ import { customInstance } from "../../api";
type SecondParameter<T extends (...args: never) => unknown> = Parameters<T>[1];
const withQueryKey = <T extends object, K>(query: T, queryKey: K): T & { queryKey: K } => {
const result = { queryKey } as T & { queryKey: K };
for (const key of Object.keys(query)) {
// The explicit queryKey always wins, matching the previous
// `{ ...query, queryKey }` spread where it was set last.
if (key === "queryKey") continue;
Object.defineProperty(result, key, {
enumerable: true,
configurable: true,
get: () => (query as Record<string, unknown>)[key],
});
}
return result;
};
/**
* @summary List chat sessions for a binary
*/
@ -136,7 +151,7 @@ export function useListSessions<TData = Awaited<ReturnType<typeof listSessions>>
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}
/**
@ -324,7 +339,7 @@ export function useGetSession<TData = Awaited<ReturnType<typeof getSession>>, TE
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}
/**
@ -510,5 +525,5 @@ export function useGetMessages<TData = Awaited<ReturnType<typeof getMessages>>,
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}

View File

@ -1,5 +1,5 @@
/**
* Generated by orval v8.15.0 🍺
* Generated by orval v8.20.0 🍺
* Do not edit manually.
* OpenAPI definition
* OpenAPI spec version: v0
@ -23,6 +23,21 @@ import { customInstance } from "../../api";
type SecondParameter<T extends (...args: never) => unknown> = Parameters<T>[1];
const withQueryKey = <T extends object, K>(query: T, queryKey: K): T & { queryKey: K } => {
const result = { queryKey } as T & { queryKey: K };
for (const key of Object.keys(query)) {
// The explicit queryKey always wins, matching the previous
// `{ ...query, queryKey }` spread where it was set last.
if (key === "queryKey") continue;
Object.defineProperty(result, key, {
enumerable: true,
configurable: true,
get: () => (query as Record<string, unknown>)[key],
});
}
return result;
};
/**
* Retrieve all registered static analysis engines with their availability status.
* @summary List available analysis engines
@ -116,5 +131,5 @@ export function useListEngines<TData = Awaited<ReturnType<typeof listEngines>>,
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}

View File

@ -1,5 +1,5 @@
/**
* Generated by orval v8.15.0 🍺
* Generated by orval v8.20.0 🍺
* Do not edit manually.
* OpenAPI definition
* OpenAPI spec version: v0
@ -23,6 +23,21 @@ import { customInstance } from "../../api";
type SecondParameter<T extends (...args: never) => unknown> = Parameters<T>[1];
const withQueryKey = <T extends object, K>(query: T, queryKey: K): T & { queryKey: K } => {
const result = { queryKey } as T & { queryKey: K };
for (const key of Object.keys(query)) {
// The explicit queryKey always wins, matching the previous
// `{ ...query, queryKey }` spread where it was set last.
if (key === "queryKey") continue;
Object.defineProperty(result, key, {
enumerable: true,
configurable: true,
get: () => (query as Record<string, unknown>)[key],
});
}
return result;
};
/**
* Retrieve full details of a function including assembly, decompiled code, and xref counts.
* @summary Get function details
@ -130,7 +145,7 @@ export function useGetFunction<TData = Awaited<ReturnType<typeof getFunction>>,
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}
/**
@ -240,7 +255,7 @@ export function useGetCallers<TData = Awaited<ReturnType<typeof getCallers>>, TE
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}
/**
@ -350,5 +365,5 @@ export function useGetCallees<TData = Awaited<ReturnType<typeof getCallees>>, TE
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}

View File

@ -1,5 +1,5 @@
/**
* Generated by orval v8.15.0 🍺
* Generated by orval v8.20.0 🍺
* Do not edit manually.
* OpenAPI definition
* OpenAPI spec version: v0
@ -26,6 +26,21 @@ import { customInstance } from "../../api";
type SecondParameter<T extends (...args: never) => unknown> = Parameters<T>[1];
const withQueryKey = <T extends object, K>(query: T, queryKey: K): T & { queryKey: K } => {
const result = { queryKey } as T & { queryKey: K };
for (const key of Object.keys(query)) {
// The explicit queryKey always wins, matching the previous
// `{ ...query, queryKey }` spread where it was set last.
if (key === "queryKey") continue;
Object.defineProperty(result, key, {
enumerable: true,
configurable: true,
get: () => (query as Record<string, unknown>)[key],
});
}
return result;
};
/**
* Cancel a job that is enqueued, started, or in progress. Returns 409 if the job is already in a final state.
* @summary Cancel a job
@ -200,7 +215,7 @@ export function useGetJobs<TData = Awaited<ReturnType<typeof getJobs>>, TError =
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}
/**
@ -307,5 +322,5 @@ export function useGetJob<TData = Awaited<ReturnType<typeof getJob>>, TError = u
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}

View File

@ -1,5 +1,5 @@
/**
* Generated by orval v8.15.0 🍺
* Generated by orval v8.20.0 🍺
* Do not edit manually.
* OpenAPI definition
* OpenAPI spec version: v0
@ -21,6 +21,7 @@ import type {
} from "@tanstack/react-query";
import type {
Library,
LibraryResponse,
ListLibrarySymbolsParams,
PageLibrarySymbolResponse,
@ -34,6 +35,21 @@ import { customInstance } from "../../api";
type SecondParameter<T extends (...args: never) => unknown> = Parameters<T>[1];
const withQueryKey = <T extends object, K>(query: T, queryKey: K): T & { queryKey: K } => {
const result = { queryKey } as T & { queryKey: K };
for (const key of Object.keys(query)) {
// The explicit queryKey always wins, matching the previous
// `{ ...query, queryKey }` spread where it was set last.
if (key === "queryKey") continue;
Object.defineProperty(result, key, {
enumerable: true,
configurable: true,
get: () => (query as Record<string, unknown>)[key],
});
}
return result;
};
/**
* Parse and store symbols from a library dump file (e.g., TurboDump output).
* @summary Upload a library symbol dump
@ -184,6 +200,86 @@ export const useResolveAllSymbols = <TError = unknown, TContext = unknown>(
): UseMutationResult<Awaited<ReturnType<typeof resolveAllSymbols>>, TError, void, TContext> => {
return useMutation(getResolveAllSymbolsMutationOptions(options), queryClient);
};
/**
* Create a new library from a previously exported JSON backup. The new library gets its own IDs existing libraries are not affected.
* @summary Import a library from JSON export
*/
export const importLibrary = (
library: Library,
options?: SecondParameter<typeof customInstance>,
signal?: AbortSignal
) => {
return customInstance<LibraryResponse>(
{
url: `/libraries/import`,
method: "POST",
headers: { "Content-Type": "application/json" },
data: library,
signal,
},
options
);
};
export const getImportLibraryMutationOptions = <TError = unknown, TContext = unknown>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof importLibrary>>,
TError,
{ data: Library },
TContext
>;
request?: SecondParameter<typeof customInstance>;
}): UseMutationOptions<
Awaited<ReturnType<typeof importLibrary>>,
TError,
{ data: Library },
TContext
> => {
const mutationKey = ["importLibrary"];
const { mutation: mutationOptions, request: requestOptions } = options
? options.mutation && "mutationKey" in options.mutation && options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey }, request: undefined };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof importLibrary>>,
{ data: Library }
> = (props) => {
const { data } = props ?? {};
return importLibrary(data, requestOptions);
};
return { mutationFn, ...mutationOptions };
};
export type ImportLibraryMutationResult = NonNullable<Awaited<ReturnType<typeof importLibrary>>>;
export type ImportLibraryMutationBody = Library;
export type ImportLibraryMutationError = unknown;
/**
* @summary Import a library from JSON export
*/
export const useImportLibrary = <TError = unknown, TContext = unknown>(
options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof importLibrary>>,
TError,
{ data: Library },
TContext
>;
request?: SecondParameter<typeof customInstance>;
},
queryClient?: QueryClient
): UseMutationResult<
Awaited<ReturnType<typeof importLibrary>>,
TError,
{ data: Library },
TContext
> => {
return useMutation(getImportLibraryMutationOptions(options), queryClient);
};
/**
* Cross-reference imports from a specific analysis against all loaded library symbol databases.
* @summary Resolve symbols for an analysis
@ -367,7 +463,7 @@ export function useListLibraries<
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}
/**
@ -477,7 +573,7 @@ export function useGetLibrary<TData = Awaited<ReturnType<typeof getLibrary>>, TE
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}
/**
@ -691,7 +787,129 @@ export function useListLibrarySymbols<
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}
/**
* Download the complete library (name, description, aliases, and all symbols) as a JSON file for backup or transfer.
* @summary Export a library as JSON
*/
export const exportLibrary = (
libraryId: string,
options?: SecondParameter<typeof customInstance>,
signal?: AbortSignal
) => {
return customInstance<Library>(
{ url: `/libraries/${encodeURIComponent(String(libraryId))}/export`, method: "GET", signal },
options
);
};
export const getExportLibraryQueryKey = (libraryId: string) => {
return [`/libraries/${libraryId}/export`] as const;
};
export const getExportLibraryQueryOptions = <
TData = Awaited<ReturnType<typeof exportLibrary>>,
TError = unknown,
>(
libraryId: string,
options?: {
query?: Partial<UseQueryOptions<Awaited<ReturnType<typeof exportLibrary>>, TError, TData>>;
request?: SecondParameter<typeof customInstance>;
}
) => {
const { query: queryOptions, request: requestOptions } = options ?? {};
const queryKey = queryOptions?.queryKey ?? getExportLibraryQueryKey(libraryId);
const queryFn: QueryFunction<Awaited<ReturnType<typeof exportLibrary>>> = ({ signal }) =>
exportLibrary(libraryId, requestOptions, signal);
return {
queryKey,
queryFn,
enabled: libraryId !== null && libraryId !== undefined,
...queryOptions,
} as UseQueryOptions<Awaited<ReturnType<typeof exportLibrary>>, TError, TData> & {
queryKey: DataTag<QueryKey, TData, TError>;
};
};
export type ExportLibraryQueryResult = NonNullable<Awaited<ReturnType<typeof exportLibrary>>>;
export type ExportLibraryQueryError = unknown;
export function useExportLibrary<
TData = Awaited<ReturnType<typeof exportLibrary>>,
TError = unknown,
>(
libraryId: string,
options: {
query: Partial<UseQueryOptions<Awaited<ReturnType<typeof exportLibrary>>, TError, TData>> &
Pick<
DefinedInitialDataOptions<
Awaited<ReturnType<typeof exportLibrary>>,
TError,
Awaited<ReturnType<typeof exportLibrary>>
>,
"initialData"
>;
request?: SecondParameter<typeof customInstance>;
},
queryClient?: QueryClient
): DefinedUseQueryResult<TData, TError> & { queryKey: DataTag<QueryKey, TData, TError> };
export function useExportLibrary<
TData = Awaited<ReturnType<typeof exportLibrary>>,
TError = unknown,
>(
libraryId: string,
options?: {
query?: Partial<UseQueryOptions<Awaited<ReturnType<typeof exportLibrary>>, TError, TData>> &
Pick<
UndefinedInitialDataOptions<
Awaited<ReturnType<typeof exportLibrary>>,
TError,
Awaited<ReturnType<typeof exportLibrary>>
>,
"initialData"
>;
request?: SecondParameter<typeof customInstance>;
},
queryClient?: QueryClient
): UseQueryResult<TData, TError> & { queryKey: DataTag<QueryKey, TData, TError> };
export function useExportLibrary<
TData = Awaited<ReturnType<typeof exportLibrary>>,
TError = unknown,
>(
libraryId: string,
options?: {
query?: Partial<UseQueryOptions<Awaited<ReturnType<typeof exportLibrary>>, TError, TData>>;
request?: SecondParameter<typeof customInstance>;
},
queryClient?: QueryClient
): UseQueryResult<TData, TError> & { queryKey: DataTag<QueryKey, TData, TError> };
/**
* @summary Export a library as JSON
*/
export function useExportLibrary<
TData = Awaited<ReturnType<typeof exportLibrary>>,
TError = unknown,
>(
libraryId: string,
options?: {
query?: Partial<UseQueryOptions<Awaited<ReturnType<typeof exportLibrary>>, TError, TData>>;
request?: SecondParameter<typeof customInstance>;
},
queryClient?: QueryClient
): UseQueryResult<TData, TError> & { queryKey: DataTag<QueryKey, TData, TError> } {
const queryOptions = getExportLibraryQueryOptions(libraryId, options);
const query = useQuery(queryOptions, queryClient) as UseQueryResult<TData, TError> & {
queryKey: DataTag<QueryKey, TData, TError>;
};
return withQueryKey(query, queryOptions.queryKey);
}
/**
@ -787,5 +1005,5 @@ export function useListParsers<TData = Awaited<ReturnType<typeof listParsers>>,
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}

View File

@ -1,5 +1,5 @@
/**
* Generated by orval v8.15.0 🍺
* Generated by orval v8.20.0 🍺
* Do not edit manually.
* OpenAPI definition
* OpenAPI spec version: v0
@ -26,6 +26,21 @@ import { customInstance } from "../../api";
type SecondParameter<T extends (...args: never) => unknown> = Parameters<T>[1];
const withQueryKey = <T extends object, K>(query: T, queryKey: K): T & { queryKey: K } => {
const result = { queryKey } as T & { queryKey: K };
for (const key of Object.keys(query)) {
// The explicit queryKey always wins, matching the previous
// `{ ...query, queryKey }` spread where it was set last.
if (key === "queryKey") continue;
Object.defineProperty(result, key, {
enumerable: true,
configurable: true,
get: () => (query as Record<string, unknown>)[key],
});
}
return result;
};
/**
* Reject the suggested mutation without applying it.
* @summary Reject a mutation suggestion
@ -306,5 +321,5 @@ export function useListMutations<
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}

View File

@ -1,5 +1,5 @@
/**
* Generated by orval v8.15.0 🍺
* Generated by orval v8.20.0 🍺
* Do not edit manually.
* OpenAPI definition
* OpenAPI spec version: v0
@ -26,6 +26,21 @@ import { customInstance } from "../../api";
type SecondParameter<T extends (...args: never) => unknown> = Parameters<T>[1];
const withQueryKey = <T extends object, K>(query: T, queryKey: K): T & { queryKey: K } => {
const result = { queryKey } as T & { queryKey: K };
for (const key of Object.keys(query)) {
// The explicit queryKey always wins, matching the previous
// `{ ...query, queryKey }` spread where it was set last.
if (key === "queryKey") continue;
Object.defineProperty(result, key, {
enumerable: true,
configurable: true,
get: () => (query as Record<string, unknown>)[key],
});
}
return result;
};
/**
* Retrieve a single project by ID.
* @summary Get a project
@ -133,7 +148,7 @@ export function useGetProject<TData = Awaited<ReturnType<typeof getProject>>, TE
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}
/**
@ -409,7 +424,7 @@ export function useGetProjects<TData = Awaited<ReturnType<typeof getProjects>>,
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}
/**

View File

@ -1,5 +1,5 @@
/**
* Generated by orval v8.15.0 🍺
* Generated by orval v8.20.0 🍺
* Do not edit manually.
* OpenAPI definition
* OpenAPI spec version: v0
@ -26,6 +26,21 @@ import { customInstance } from "../../api";
type SecondParameter<T extends (...args: never) => unknown> = Parameters<T>[1];
const withQueryKey = <T extends object, K>(query: T, queryKey: K): T & { queryKey: K } => {
const result = { queryKey } as T & { queryKey: K };
for (const key of Object.keys(query)) {
// The explicit queryKey always wins, matching the previous
// `{ ...query, queryKey }` spread where it was set last.
if (key === "queryKey") continue;
Object.defineProperty(result, key, {
enumerable: true,
configurable: true,
get: () => (query as Record<string, unknown>)[key],
});
}
return result;
};
/**
* Retrieve a single workspace by its unique identifier.
* @summary Get a workspace by ID
@ -133,7 +148,7 @@ export function useGetWorkspace<TData = Awaited<ReturnType<typeof getWorkspace>>
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}
/**
@ -411,7 +426,7 @@ export function useGetWorkspaces<
queryKey: DataTag<QueryKey, TData, TError>;
};
return { ...query, queryKey: queryOptions.queryKey };
return withQueryKey(query, queryOptions.queryKey);
}
/**

View File

@ -0,0 +1,25 @@
import { Moon, Sun } from "lucide-react";
import { Button } from "@/components/ui/button.tsx";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip.tsx";
import { useTheme } from "@/providers/ThemeProvider.tsx";
export const ThemeToggle = () => {
const { theme, toggleTheme } = useTheme();
const isDark = theme === "dark";
return (
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={toggleTheme}
aria-label={isDark ? "Switch to light theme" : "Switch to dark theme"}
>
{isDark ? <Sun className="h-5 w-5" /> : <Moon className="h-5 w-5" />}
</Button>
</TooltipTrigger>
<TooltipContent>{isDark ? "Light mode" : "Dark mode"}</TooltipContent>
</Tooltip>
);
};

View File

@ -1,6 +1,7 @@
import { Activity, Binary, FolderOpen, BookOpen } from "lucide-react";
import { Link, NavLink } from "react-router";
import { cn } from "@/lib/utils.ts";
import { ThemeToggle } from "@/components/ThemeToggle.tsx";
const navItems = [
{ href: "/", label: "Workspaces", icon: FolderOpen },
@ -45,6 +46,11 @@ export const Header = () => {
);
})}
</nav>
{/* Right actions */}
<div className="ml-auto flex items-center gap-1">
<ThemeToggle />
</div>
</header>
);
};

View File

@ -1,4 +1,4 @@
import { useState, useRef, useEffect, useMemo } from "react";
import { useState, useRef, useEffect, useMemo, useCallback } from "react";
import { formatDistanceToNow } from "date-fns";
import { Send, Bot, User, Loader2, Sparkles, Plus, Trash2 } from "lucide-react";
import ReactMarkdown from "react-markdown";
@ -139,6 +139,16 @@ export function AIChatPanel({
const analyzingMessage = analyzingMessages[analyzingIdx];
const [resolvedIds, setResolvedIds] = useState<Set<string>>(new Set());
const handleResolved = useCallback((mutationId: string) => {
setResolvedIds((prev) => {
const next = new Set(prev);
next.add(mutationId);
return next;
});
}, []);
const { data: pendingSuggestions } = useListMutations(
binary?.id ?? "",
{ status: "PENDING", targetType: "FUNCTION" },
@ -343,6 +353,7 @@ export function AIChatPanel({
if (item.kind === "suggestion") {
const s = item.data;
if (!s.id || !s.targetId || !s.oldValue || !s.newValue) return null;
if (resolvedIds.has(s.id)) return null;
return (
<MutationSuggestionCard
key={s.id}
@ -353,6 +364,7 @@ export function AIChatPanel({
oldValue={s.oldValue}
newValue={s.newValue}
onRenameApplied={onRenameApplied}
onResolved={handleResolved}
/>
);
}
@ -415,6 +427,7 @@ export function AIChatPanel({
.filter(
(s, i, arr) =>
!pendingIds.has(s.mutationId) &&
!resolvedIds.has(s.mutationId) &&
arr.findIndex((x) => x.mutationId === s.mutationId) === i
)
.map((s) => (
@ -427,6 +440,7 @@ export function AIChatPanel({
oldValue={s.oldValue}
newValue={s.newValue}
onRenameApplied={onRenameApplied}
onResolved={handleResolved}
/>
))}
{isStreaming && (
@ -463,18 +477,21 @@ export function AIChatPanel({
<span>{toolStatus}</span>
</div>
)}
{mutationSuggestions.map((s) => (
<MutationSuggestionCard
key={s.mutationId}
mutationId={s.mutationId}
targetType={s.targetType}
targetId={s.targetId}
fieldName={s.fieldName}
oldValue={s.oldValue}
newValue={s.newValue}
onRenameApplied={onRenameApplied}
/>
))}
{mutationSuggestions
.filter((s) => !resolvedIds.has(s.mutationId))
.map((s) => (
<MutationSuggestionCard
key={s.mutationId}
mutationId={s.mutationId}
targetType={s.targetType}
targetId={s.targetId}
fieldName={s.fieldName}
oldValue={s.oldValue}
newValue={s.newValue}
onRenameApplied={onRenameApplied}
onResolved={handleResolved}
/>
))}
</>
)}
<div ref={messagesEndRef} />

View File

@ -1,4 +1,4 @@
import { useState, useMemo } from "react";
import { useState, useMemo, useEffect, useRef } from "react";
import { useQueryClient } from "@tanstack/react-query";
import {
Check,
@ -26,6 +26,7 @@ interface MutationSuggestionCardProps {
oldValue: string;
newValue: string;
onRenameApplied?: () => void;
onResolved?: (mutationId: string) => void;
}
function getLabel(targetType: string, fieldName: string): string {
@ -125,12 +126,31 @@ export function MutationSuggestionCard({
oldValue,
newValue,
onRenameApplied,
onResolved,
}: MutationSuggestionCardProps) {
const queryClient = useQueryClient();
const [status, setStatus] = useState<SuggestionStatus>("PENDING");
const [exiting, setExiting] = useState(false);
const [dismissed, setDismissed] = useState(false);
const dismissTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const invalidationKey = getInvalidationKey(targetType, targetId);
const resolve = () => {
if (dismissed) return;
setExiting(true);
dismissTimerRef.current = setTimeout(() => {
setDismissed(true);
onResolved?.(mutationId);
}, 300);
};
useEffect(() => {
return () => {
if (dismissTimerRef.current) clearTimeout(dismissTimerRef.current);
};
}, []);
const approveMutation = useApproveMutation({
mutation: {
onSuccess: () => {
@ -139,6 +159,7 @@ export function MutationSuggestionCard({
queryClient.invalidateQueries({ queryKey: [invalidationKey] });
}
onRenameApplied?.();
dismissTimerRef.current = setTimeout(resolve, 1500);
},
},
});
@ -147,6 +168,8 @@ export function MutationSuggestionCard({
mutation: {
onSuccess: () => {
setStatus("REJECTED");
onRenameApplied?.();
dismissTimerRef.current = setTimeout(resolve, 1500);
},
},
});
@ -182,12 +205,15 @@ export function MutationSuggestionCard({
return null;
}, [fieldName, oldValue, newValue]);
if (dismissed) return null;
return (
<div
className={cn(
"border-l-primary/60 bg-muted/60 my-3 rounded-lg border-l-[3px] px-3 py-2.5 text-sm",
"border-l-primary/60 bg-muted/60 my-3 rounded-lg border-l-[3px] px-3 py-2.5 text-sm transition-opacity duration-300",
isApproved && "bg-muted/40 border-l-(--success)",
isRejected && "border-l-muted-foreground/40 bg-muted/30"
isRejected && "border-l-muted-foreground/40 bg-muted/30",
exiting && "pointer-events-none opacity-0"
)}
>
<div className="mb-2 flex items-center gap-2">

View File

@ -150,7 +150,7 @@ export function useAIChat() {
binaryId,
params: {
projectId: binary?.projectId,
model: model ?? "gemma4:12b",
...(model ? { model } : {}),
},
});
},

View File

@ -43,11 +43,11 @@ export const WorkspaceDialog = ({
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent>
<DialogHeader>
<DialogTitle>{isEditing ? "Edit Home" : "Create Home"}</DialogTitle>
<DialogTitle>{isEditing ? "Edit Workspace" : "Create Workspace"}</DialogTitle>
<DialogDescription>
{isEditing
? "Update your home details."
: "Create a new home to organize your reverse engineering projects."}
? "Update your workspace details."
: "Create a new workspace to organize your reverse engineering projects."}
</DialogDescription>
</DialogHeader>
<div className="grid gap-4 py-4">
@ -76,7 +76,7 @@ export const WorkspaceDialog = ({
Cancel
</Button>
<Button onClick={handleSave} disabled={!name.trim()}>
{isEditing ? "Save Changes" : "Create Home"}
{isEditing ? "Save Changes" : "Create Workspace"}
</Button>
</DialogFooter>
</DialogContent>

View File

@ -0,0 +1,166 @@
import { useState, useRef } from "react";
import { Upload, Loader2, BookOpen, Hash, Tag } from "lucide-react";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog.tsx";
import { Button } from "@/components/ui/button.tsx";
import { Badge } from "@/components/ui/badge.tsx";
import type { Library } from "@/api/generated/api.schemas.ts";
const MAX_FILE_SIZE = 50 * 1024 * 1024;
interface ImportLibraryDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
isImporting: boolean;
onImport: (data: Library) => void;
}
export const ImportLibraryDialog = ({
open,
onOpenChange,
isImporting,
onImport,
}: ImportLibraryDialogProps) => {
const fileInputRef = useRef<HTMLInputElement>(null);
const [parsedData, setParsedData] = useState<Library | null>(null);
const [parseError, setParseError] = useState<string | null>(null);
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0] ?? null;
setParseError(null);
setParsedData(null);
if (!file) return;
if (file.size > MAX_FILE_SIZE) {
setParseError("File must be under 50MB");
return;
}
if (!file.name.endsWith(".json")) {
setParseError("Only .json files are accepted");
return;
}
const reader = new FileReader();
reader.onload = () => {
try {
const json = JSON.parse(reader.result as string);
if (!json.name || typeof json.name !== "string" || json.name.trim() === "") {
setParseError('Invalid format: "name" field is required');
return;
}
setParsedData(json);
} catch {
setParseError("Invalid JSON file");
}
};
reader.onerror = () => {
setParseError("Failed to read file");
};
reader.readAsText(file);
};
const handleImport = () => {
if (!parsedData) return;
onImport(parsedData);
};
const handleClose = () => {
if (!isImporting) {
setParsedData(null);
setParseError(null);
if (fileInputRef.current) {
fileInputRef.current.value = "";
}
}
onOpenChange(false);
};
const symbolCount = parsedData?.symbols?.length ?? 0;
const aliasCount = parsedData?.aliases?.length ?? 0;
return (
<Dialog open={open} onOpenChange={handleClose}>
<DialogContent className="sm:max-w-lg">
<DialogHeader>
<DialogTitle>Import Library</DialogTitle>
<DialogDescription>
Restore a library from a previously exported JSON backup.
</DialogDescription>
</DialogHeader>
<div className="space-y-5">
<div className="space-y-2">
<label htmlFor="import-file" className="text-sm font-medium">
JSON backup file *
</label>
<input
ref={fileInputRef}
id="import-file"
type="file"
accept=".json"
onChange={handleFileChange}
disabled={isImporting}
className="file:bg-primary file:text-primary-foreground hover:file:bg-primary/90 block w-full text-sm file:mr-3 file:cursor-pointer file:rounded-md file:border-0 file:px-3 file:py-1.5 file:text-sm file:font-medium"
/>
{parseError && <p className="text-destructive text-xs">{parseError}</p>}
</div>
{parsedData && (
<div className="bg-muted/50 space-y-3 rounded-md p-3">
<div className="flex items-center gap-2">
<BookOpen className="text-muted-foreground h-4 w-4" />
<span className="truncate font-medium">{parsedData.name}</span>
</div>
{parsedData.description && (
<p className="text-muted-foreground line-clamp-2 text-xs">
{parsedData.description}
</p>
)}
<div className="flex items-center gap-2">
<Badge variant="secondary" className="text-xs">
<Hash className="mr-1 h-3 w-3" />
{symbolCount} symbols
</Badge>
{aliasCount > 0 && (
<Badge variant="outline" className="text-xs">
<Tag className="mr-1 h-3 w-3" />
{aliasCount} aliases
</Badge>
)}
</div>
</div>
)}
<DialogFooter>
<Button variant="outline" onClick={handleClose} disabled={isImporting}>
Cancel
</Button>
<Button onClick={handleImport} disabled={isImporting || !parsedData}>
{isImporting ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Importing...
</>
) : (
<>
<Upload className="mr-2 h-4 w-4" />
Import
</>
)}
</Button>
</DialogFooter>
</div>
</DialogContent>
</Dialog>
);
};

View File

@ -1,4 +1,4 @@
import { Eye, Trash2, BookOpen } from "lucide-react";
import { Eye, Trash2, BookOpen, Download } from "lucide-react";
import { Card, CardContent, CardFooter } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
@ -9,9 +9,10 @@ interface LibraryCardProps {
library: LibraryResponse;
onDelete: (library: LibraryResponse) => void;
onViewSymbols: (library: LibraryResponse) => void;
onExport: (library: LibraryResponse) => void;
}
export const LibraryCard = ({ library, onDelete, onViewSymbols }: LibraryCardProps) => {
export const LibraryCard = ({ library, onDelete, onViewSymbols, onExport }: LibraryCardProps) => {
return (
<Card className="flex flex-col justify-between gap-2 p-4">
<CardContent className="space-y-2 p-0">
@ -45,6 +46,9 @@ export const LibraryCard = ({ library, onDelete, onViewSymbols }: LibraryCardPro
<Button variant="ghost" size="sm" onClick={() => onViewSymbols(library)} className="h-8">
<Eye className="h-3.5 w-3.5" />
</Button>
<Button variant="ghost" size="sm" onClick={() => onExport(library)} className="h-8">
<Download className="h-3.5 w-3.5" />
</Button>
<Button
variant="ghost"
size="sm"

View File

@ -53,7 +53,54 @@
}
:root {
/* Dark theme as default - IDE style for RE platform */
/* Light theme - clean IDE style for RE platform */
--background: oklch(0.98 0.005 260);
--foreground: oklch(0.2 0.02 260);
--card: oklch(1 0 0);
--card-foreground: oklch(0.2 0.02 260);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.2 0.02 260);
--primary: oklch(0.58 0.14 180);
--primary-foreground: oklch(0.98 0.005 260);
--secondary: oklch(0.95 0.01 260);
--secondary-foreground: oklch(0.2 0.02 260);
--muted: oklch(0.96 0.01 260);
--muted-foreground: oklch(0.5 0.02 260);
--accent: oklch(0.58 0.14 180);
--accent-foreground: oklch(0.98 0.005 260);
--destructive: oklch(0.58 0.22 25);
--border: oklch(0.9 0.01 260);
--input: oklch(0.9 0.01 260);
--ring: oklch(0.58 0.14 180);
--chart-1: oklch(0.58 0.14 180);
--chart-2: oklch(0.6 0.15 140);
--chart-3: oklch(0.55 0.15 280);
--chart-4: oklch(0.65 0.13 60);
--chart-5: oklch(0.5 0.18 330);
--radius: 0.5rem;
--sidebar: oklch(0.97 0.005 260);
--sidebar-foreground: oklch(0.2 0.02 260);
--sidebar-primary: oklch(0.58 0.14 180);
--sidebar-primary-foreground: oklch(0.98 0.005 260);
--sidebar-accent: oklch(0.95 0.01 260);
--sidebar-accent-foreground: oklch(0.2 0.02 260);
--sidebar-border: oklch(0.9 0.01 260);
--sidebar-ring: oklch(0.58 0.14 180);
/* Custom tokens for RE platform */
--code-bg: oklch(0.97 0.005 260);
--code-line-number: oklch(0.6 0.01 260);
--code-keyword: oklch(0.5 0.16 280);
--code-string: oklch(0.5 0.13 140);
--code-comment: oklch(0.6 0.05 140);
--code-function: oklch(0.55 0.15 60);
--code-address: oklch(0.5 0.12 180);
--success: oklch(0.55 0.16 145);
--warning: oklch(0.62 0.15 75);
}
.dark {
/* Dark theme - IDE style for RE platform */
--background: oklch(0.12 0.01 260);
--foreground: oklch(0.93 0.01 260);
--card: oklch(0.16 0.01 260);
@ -77,7 +124,6 @@
--chart-3: oklch(0.6 0.15 280);
--chart-4: oklch(0.75 0.12 60);
--chart-5: oklch(0.55 0.18 330);
--radius: 0.5rem;
--sidebar: oklch(0.14 0.01 260);
--sidebar-foreground: oklch(0.93 0.01 260);
--sidebar-primary: oklch(0.65 0.18 180);

View File

@ -1,17 +1,22 @@
import { Plus, BookOpen, Wand2 } from "lucide-react";
import { useState } from "react";
import { Plus, BookOpen, Wand2, FileUp } from "lucide-react";
import { Button } from "@/components/ui/button.tsx";
import { EmptyState } from "@/components/EmptyState.tsx";
import { ConfirmDialog } from "@/components/ConfirmDialog.tsx";
import { useLibrariesPage } from "@/features/libraries/hooks/useLibrariesPage.ts";
import { LibraryCard } from "@/features/libraries/LibraryCard.tsx";
import { UploadLibraryDialog } from "@/features/libraries/UploadLibraryDialog.tsx";
import { ImportLibraryDialog } from "@/features/libraries/ImportLibraryDialog.tsx";
import { LibrarySymbolsDialog } from "@/features/libraries/LibrarySymbolsDialog.tsx";
import {
useUploadLibrary,
useImportLibrary,
getListLibrariesQueryKey,
} from "@/api/generated/library-symbols/library-symbols.ts";
import { Api } from "@/api/api.ts";
import { useQueryClient } from "@tanstack/react-query";
import { toast } from "sonner";
import type { LibraryResponse, Library } from "@/api/generated/api.schemas.ts";
const Libraries = () => {
const queryClient = useQueryClient();
@ -31,6 +36,45 @@ const Libraries = () => {
handleViewSymbols,
} = useLibrariesPage();
const [importDialogOpen, setImportDialogOpen] = useState(false);
const handleExport = async (library: LibraryResponse) => {
if (!library.id) return;
try {
const response = await Api.get(`/libraries/${encodeURIComponent(library.id)}/export`, {
responseType: "blob",
});
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement("a");
link.href = url;
const filename = (library.name ?? "library").replace(/[^a-zA-Z0-9._-]/g, "_");
link.setAttribute("download", `${filename}.json`);
document.body.appendChild(link);
link.click();
link.remove();
window.URL.revokeObjectURL(url);
} catch {
toast.error("Failed to export library");
}
};
const importMutation = useImportLibrary({
mutation: {
onSuccess: (data) => {
queryClient.invalidateQueries({ queryKey: getListLibrariesQueryKey() });
toast.success(`${data.name} imported — ${data.symbolCount} symbols, ${data.aliasCount} aliases`);
setImportDialogOpen(false);
},
onError: () => {
toast.error("Failed to import library. Make sure the file is a valid export.");
},
},
});
const handleImport = (data: Library) => {
importMutation.mutate({ data });
};
const uploadMutation = useUploadLibrary({
mutation: {
onSuccess: (data) => {
@ -69,6 +113,14 @@ const Libraries = () => {
</p>
</div>
<div className="flex items-center gap-2">
<Button
variant="outline"
size="sm"
onClick={() => setImportDialogOpen(true)}
>
<FileUp className="mr-1.5 h-4 w-4" />
Import Library
</Button>
{libraries.length > 0 && (
<Button
variant="outline"
@ -107,6 +159,7 @@ const Libraries = () => {
library={lib}
onDelete={setDeleteTarget}
onViewSymbols={handleViewSymbols}
onExport={handleExport}
/>
))}
</div>
@ -119,6 +172,13 @@ const Libraries = () => {
onUpload={handleUpload}
/>
<ImportLibraryDialog
open={importDialogOpen}
onOpenChange={setImportDialogOpen}
isImporting={importMutation.isPending}
onImport={handleImport}
/>
<ConfirmDialog
open={!!deleteTarget}
onOpenChange={(open) => {

View File

@ -2,6 +2,7 @@ import type { ReactNode } from "react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { Toaster } from "sonner";
import { TooltipProvider } from "@/components/ui/tooltip";
import { ThemeProvider } from "@/providers/ThemeProvider.tsx";
interface ProvidersProps {
children: ReactNode;
@ -9,11 +10,13 @@ interface ProvidersProps {
export const Providers = ({ children }: ProvidersProps) => {
return (
<QueryClientProvider client={new QueryClient()}>
<TooltipProvider delayDuration={1000}>
{children}
<Toaster position="bottom-right" />
</TooltipProvider>
</QueryClientProvider>
<ThemeProvider>
<QueryClientProvider client={new QueryClient()}>
<TooltipProvider delayDuration={1000}>
{children}
<Toaster position="bottom-right" />
</TooltipProvider>
</QueryClientProvider>
</ThemeProvider>
);
};

View File

@ -0,0 +1,54 @@
import { createContext, useContext, useEffect, useState, type ReactNode } from "react";
export type Theme = "light" | "dark";
interface ThemeContextValue {
theme: Theme;
setTheme: (theme: Theme) => void;
toggleTheme: () => void;
}
const STORAGE_KEY = "decompile-ai-theme";
const DEFAULT_THEME: Theme = "dark";
const ThemeContext = createContext<ThemeContextValue | undefined>(undefined);
const getInitialTheme = (): Theme => {
if (typeof window === "undefined") return DEFAULT_THEME;
const stored = window.localStorage.getItem(STORAGE_KEY);
if (stored === "light" || stored === "dark") return stored;
return DEFAULT_THEME;
};
interface ThemeProviderProps {
children: ReactNode;
}
export const ThemeProvider = ({ children }: ThemeProviderProps) => {
const [theme, setThemeState] = useState<Theme>(getInitialTheme);
useEffect(() => {
const root = document.documentElement;
root.classList.remove("light", "dark");
root.classList.add(theme);
window.localStorage.setItem(STORAGE_KEY, theme);
}, [theme]);
const setTheme = (next: Theme) => setThemeState(next);
const toggleTheme = () => setThemeState((prev) => (prev === "dark" ? "light" : "dark"));
return (
<ThemeContext.Provider value={{ theme, setTheme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
};
// eslint-disable-next-line react-refresh/only-export-components
export const useTheme = () => {
const context = useContext(ThemeContext);
if (!context) {
throw new Error("useTheme must be used within a ThemeProvider");
}
return context;
};