diff --git a/package-lock.json b/package-lock.json index fb54c4d..65ba092 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,15 +8,13 @@ "name": "sentence-structure-diagram-app", "version": "0.1.0", "workspaces": [ + "packages/sentence-structure-data", + "packages/sentence-structure-diagram-notation", + "packages/sentence-structure-diagram", + "packages/sentence-structure-data-from-stanza", "packages/backend", "packages/frontend", - "packages/sentence-structure-data", - "packages/sentence-structure-diagram-data", - "packages/sentence-structure-diagram-svg", - "packages/evaluation", - "packages/sentence-structure-diagram-configurations", - "packages/sentence-structure-tree", - "packages/sentence-structure-diagram-tree" + "packages/evaluation" ], "devDependencies": { "prettier": "^3.7.4" @@ -463,7 +461,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -480,7 +477,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -497,7 +493,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -514,7 +509,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -531,7 +525,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -548,7 +541,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -565,7 +557,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -582,7 +573,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -599,7 +589,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -616,7 +605,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -633,7 +621,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -650,7 +637,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -667,7 +653,6 @@ "cpu": [ "mips64el" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -684,7 +669,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -701,7 +685,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -718,7 +701,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -735,7 +717,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -752,7 +733,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -769,7 +749,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -786,7 +765,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -803,7 +781,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -820,7 +797,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -837,7 +813,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -854,7 +829,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -871,7 +845,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -888,7 +861,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1458,7 +1430,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1472,7 +1443,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1486,7 +1456,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1500,7 +1469,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1514,7 +1482,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1528,7 +1495,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1542,7 +1508,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1556,7 +1521,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1570,7 +1534,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1584,7 +1547,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1598,7 +1560,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1612,7 +1573,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1626,7 +1586,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1640,7 +1599,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1654,7 +1612,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1668,7 +1625,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1682,7 +1638,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1696,7 +1651,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1710,7 +1664,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1724,7 +1677,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1738,7 +1690,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1752,7 +1703,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1775,6 +1725,14 @@ "resolved": "packages/sentence-structure-data", "link": true }, + "node_modules/@sentence-structure-diagram-app/sentence-structure-data-from-stanza": { + "resolved": "packages/sentence-structure-data-from-stanza", + "link": true + }, + "node_modules/@sentence-structure-diagram-app/sentence-structure-diagram": { + "resolved": "packages/sentence-structure-diagram", + "link": true + }, "node_modules/@sentence-structure-diagram-app/sentence-structure-diagram-configurations": { "resolved": "packages/sentence-structure-diagram-configurations", "link": true @@ -1783,16 +1741,12 @@ "resolved": "packages/sentence-structure-diagram-data", "link": true }, - "node_modules/@sentence-structure-diagram-app/sentence-structure-diagram-svg": { - "resolved": "packages/sentence-structure-diagram-svg", - "link": true - }, - "node_modules/@sentence-structure-diagram-app/sentence-structure-diagram-tree": { - "resolved": "packages/sentence-structure-diagram-tree", + "node_modules/@sentence-structure-diagram-app/sentence-structure-diagram-notation": { + "resolved": "packages/sentence-structure-diagram-notation", "link": true }, - "node_modules/@sentence-structure-diagram-app/sentence-structure-tree": { - "resolved": "packages/sentence-structure-tree", + "node_modules/@sentence-structure-diagram-app/sentence-structure-diagram-svg": { + "resolved": "packages/sentence-structure-diagram-svg", "link": true }, "node_modules/@tanstack/query-core": { @@ -1943,7 +1897,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, "license": "MIT" }, "node_modules/@types/express": { @@ -1989,7 +1942,7 @@ "version": "24.10.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.4.tgz", "integrity": "sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "undici-types": "~7.16.0" @@ -2449,6 +2402,31 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -2516,6 +2494,18 @@ "node": "*" } }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/body-parser": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz", @@ -2551,6 +2541,18 @@ "concat-map": "0.0.1" } }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/browserslist": { "version": "4.28.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", @@ -2676,6 +2678,42 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -2703,20 +2741,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, - "node_modules/compromise": { - "version": "14.14.4", - "resolved": "https://registry.npmjs.org/compromise/-/compromise-14.14.4.tgz", - "integrity": "sha512-QdbJwronwxeqb7a5KFK/+Y5YieZ4PE1f7ai0vU58Pp4jih+soDCBMuKVbhDEPQ+6+vI3vSiG4UAAjTAXLJw1Qw==", - "license": "MIT", - "dependencies": { - "efrt": "2.7.0", - "grad-school": "0.0.5", - "suffix-thumb": "5.0.2" - }, - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2915,15 +2939,6 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, - "node_modules/efrt": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/efrt/-/efrt-2.7.0.tgz", - "integrity": "sha512-/RInbCy1d4P6Zdfa+TMVsf/ufZVotat5hCw3QXmWtjU+3pFEOvOQ7ibo3aIxyCJw2leIeAMjmPj+1SLJiCpdrQ==", - "license": "MIT", - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/electron-to-chromium": { "version": "1.5.267", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", @@ -2989,7 +3004,7 @@ "version": "0.27.1", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.1.tgz", "integrity": "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==", - "dev": true, + "devOptional": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -3340,7 +3355,6 @@ "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" @@ -3390,6 +3404,18 @@ "node": ">=16.0.0" } }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/finalhandler": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", @@ -3505,7 +3531,6 @@ "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, @@ -3605,7 +3630,7 @@ "version": "4.13.0", "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "resolve-pkg-maps": "^1.0.0" @@ -3723,15 +3748,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/grad-school": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/grad-school/-/grad-school-0.0.5.tgz", - "integrity": "sha512-rXunEHF9M9EkMydTBux7+IryYXEZinRk6g8OBOGDBzo/qWJjhTxy86i5q7lQYpCLHN8Sqv1XX3OIOc7ka2gtvQ==", - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/gtoken": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-8.0.0.tgz", @@ -3917,6 +3933,18 @@ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "license": "MIT" }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-core-module": { "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", @@ -3936,7 +3964,6 @@ "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" @@ -3955,7 +3982,6 @@ "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" @@ -3964,6 +3990,15 @@ "node": ">=0.10.0" } }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/is-promise": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", @@ -4254,7 +4289,6 @@ "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, "funding": [ { "type": "github", @@ -4330,6 +4364,15 @@ "dev": true, "license": "MIT" }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -4443,6 +4486,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-map": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", @@ -4564,7 +4619,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -4577,7 +4631,6 @@ "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "dev": true, "funding": [ { "type": "opencollective", @@ -4759,6 +4812,30 @@ "react-dom": ">=16.6.0" } }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/resolve": { "version": "1.22.11", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", @@ -4792,7 +4869,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, + "devOptional": true, "license": "MIT", "funding": { "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" @@ -4817,7 +4894,6 @@ "version": "4.53.3", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", - "dev": true, "license": "MIT", "dependencies": { "@types/estree": "1.0.8" @@ -5074,7 +5150,6 @@ "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" @@ -5216,12 +5291,6 @@ "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", "license": "MIT" }, - "node_modules/suffix-thumb": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/suffix-thumb/-/suffix-thumb-5.0.2.tgz", - "integrity": "sha512-I5PWXAFKx3FYnI9a+dQMWNqTxoRt6vdBdb0O+BJ1sxXCWtSoQCusc13E58f+9p4MYx/qCnEMkD5jac6K2j3dgA==", - "license": "MIT" - }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -5251,7 +5320,6 @@ "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -5264,6 +5332,18 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -5290,7 +5370,7 @@ "version": "4.21.0", "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "esbuild": "~0.27.0", @@ -5374,7 +5454,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/unpipe": { @@ -5440,7 +5520,6 @@ "version": "7.2.7", "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.7.tgz", "integrity": "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==", - "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", @@ -5511,6 +5590,28 @@ } } }, + "node_modules/vite-plugin-static-copy": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-3.2.0.tgz", + "integrity": "sha512-g2k9z8B/1Bx7D4wnFjPLx9dyYGrqWMLTpwTtPHhcU+ElNZP2O4+4OsyaficiDClus0dzVhdGvoGFYMJxoXZ12Q==", + "license": "MIT", + "dependencies": { + "chokidar": "^3.6.0", + "p-map": "^7.0.4", + "picocolors": "^1.1.1", + "tinyglobby": "^0.2.15" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/sapphi-red" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, "node_modules/vite/node_modules/@esbuild/aix-ppc64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", @@ -5518,7 +5619,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5535,7 +5635,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5552,7 +5651,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5569,7 +5667,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5586,7 +5683,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5603,7 +5699,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5620,7 +5715,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5637,7 +5731,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5654,7 +5747,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5671,7 +5763,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5688,7 +5779,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5705,7 +5795,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5722,7 +5811,6 @@ "cpu": [ "mips64el" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5739,7 +5827,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5756,7 +5843,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5773,7 +5859,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5790,7 +5875,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5807,7 +5891,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5824,7 +5907,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5841,7 +5923,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5858,7 +5939,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5875,7 +5955,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5892,7 +5971,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5909,7 +5987,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5926,7 +6003,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5943,7 +6019,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5957,7 +6032,6 @@ "version": "0.25.12", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", - "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -6165,9 +6239,9 @@ } }, "node_modules/zod": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.2.1.tgz", - "integrity": "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.5.tgz", + "integrity": "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -6234,12 +6308,14 @@ "@sentence-structure-diagram-app/backend": "^0.1.0", "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0", "@sentence-structure-diagram-app/sentence-structure-diagram-data": "^0.1.0", + "@sentence-structure-diagram-app/sentence-structure-diagram-notation": "^0.1.0", "@sentence-structure-diagram-app/sentence-structure-diagram-svg": "^0.1.0", "@tanstack/react-query": "^5.90.12", "@trpc/client": "^11.7.2", "@trpc/tanstack-react-query": "^11.7.2", "react": "^19.2.0", - "react-dom": "^19.2.0" + "react-dom": "^19.2.0", + "vite-plugin-static-copy": "^3.2.0" }, "devDependencies": { "@eslint/js": "^9.39.1", @@ -6260,21 +6336,79 @@ "name": "@sentence-structure-diagram-app/sentence-structure-data", "version": "0.1.0", "dependencies": { - "compromise": "^14.14.4", - "fast-xml-parser": "^5.3.2", - "zod": "^4.1.13" + "fast-xml-parser": "^5.3.3", + "zod": "^4.3.5" + }, + "devDependencies": { + "typescript": "^5.9.3" + } + }, + "packages/sentence-structure-data-from-stanza": { + "name": "@sentence-structure-diagram-app/sentence-structure-data-from-stanza", + "version": "0.1.0", + "dependencies": { + "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0", + "zod": "^4.3.5" + }, + "devDependencies": { + "typescript": "^5.9.3" + } + }, + "packages/sentence-structure-data-parser": { + "name": "@sentence-structure-diagram-app/sentence-structure-data-parser", + "version": "0.1.0", + "extraneous": true, + "dependencies": { + "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0", + "pyodide": "^0.29.3", + "zod": "^4.3.5" + }, + "devDependencies": { + "typescript": "^5.9.3" + } + }, + "packages/sentence-structure-data-parser-spacy": { + "name": "@sentence-structure-diagram-app/sentence-structure-data-parser-spacy", + "version": "0.1.0", + "extraneous": true, + "dependencies": { + "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0", + "pyodide": "^0.29.3" + }, + "devDependencies": { + "typescript": "^5.9.3" + } + }, + "packages/sentence-structure-data-tree": { + "name": "@sentence-structure-diagram-app/sentence-structure-data-tree", + "version": "0.1.0", + "extraneous": true, + "dependencies": { + "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0" + }, + "devDependencies": { + "typescript": "^5.9.3" + } + }, + "packages/sentence-structure-diagram": { + "name": "@sentence-structure-diagram-app/sentence-structure-diagram", + "version": "0.1.0", + "dependencies": { + "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0", + "@sentence-structure-diagram-app/sentence-structure-diagram-notation": "^0.1.0", + "zod": "^4.3.5" }, "devDependencies": { "typescript": "^5.9.3" } }, "packages/sentence-structure-diagram-configurations": { - "name": "@sentence-structure-diagram-app/sentence-structure-diagram-configurations", + "name": "@sentence-structure-diagram-app/sentence-structure-diagram-notation", "version": "0.1.0", "dependencies": { "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0", "fast-xml-parser": "^5.3.3", - "zod": "^4.2.1" + "zod": "^4.3.5" }, "devDependencies": { "typescript": "^5.9.3" @@ -6285,8 +6419,7 @@ "version": "0.1.0", "dependencies": { "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0", - "@sentence-structure-diagram-app/sentence-structure-diagram-configurations": "^0.1.0", - "@sentence-structure-diagram-app/sentence-structure-diagram-tree": "^0.1.0", + "@sentence-structure-diagram-app/sentence-structure-diagram-notation": "^0.1.0", "fast-xml-parser": "^5.3.2", "zod": "^4.1.13" }, @@ -6294,13 +6427,24 @@ "typescript": "^5.9.3" } }, + "packages/sentence-structure-diagram-notation": { + "name": "@sentence-structure-diagram-app/sentence-structure-diagram-notation", + "version": "0.1.0", + "dependencies": { + "fast-xml-parser": "^5.3.3", + "zod": "^4.3.5" + }, + "devDependencies": { + "typescript": "^5.9.3" + } + }, "packages/sentence-structure-diagram-svg": { "name": "@sentence-structure-diagram-app/sentence-structure-diagram-svg", "version": "0.1.0", "dependencies": { "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0", - "@sentence-structure-diagram-app/sentence-structure-diagram-configurations": "^0.1.0", "@sentence-structure-diagram-app/sentence-structure-diagram-data": "^0.1.0", + "@sentence-structure-diagram-app/sentence-structure-diagram-notation": "^0.1.0", "prettier": "^3.7.4", "react": "^19.2.3", "react-dom": "^19.2.3" @@ -6312,21 +6456,20 @@ "packages/sentence-structure-diagram-tree": { "name": "@sentence-structure-diagram-app/sentence-structure-diagram-tree", "version": "0.1.0", + "extraneous": true, "dependencies": { "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0", - "@sentence-structure-diagram-app/sentence-structure-diagram-configurations": "^0.1.0", - "@sentence-structure-diagram-app/sentence-structure-tree": "^0.1.0" + "@sentence-structure-diagram-app/sentence-structure-data-tree": "^0.1.0", + "@sentence-structure-diagram-app/sentence-structure-diagram-configurations": "^0.1.0" }, "devDependencies": { "typescript": "^5.9.3" } }, - "packages/sentence-structure-tree": { - "name": "@sentence-structure-diagram-app/sentence-structure-tree", + "packages/sentence-structure-parser-spacy": { + "name": "@sentence-structure-diagram-app/sentence-structure-parser-spacy", "version": "0.1.0", - "dependencies": { - "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0" - }, + "extraneous": true, "devDependencies": { "typescript": "^5.9.3" } diff --git a/package.json b/package.json index 70c57cc..bec9956 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "type": "module", "scripts": { - "build:packages": "npm run build --workspace=packages/sentence-structure-data && npm run build --workspace=packages/sentence-structure-tree && npm run build --workspace=packages/sentence-structure-diagram-configurations && npm run build --workspace=packages/sentence-structure-diagram-tree && npm run build --workspace=packages/sentence-structure-diagram-data && npm run build --workspace=packages/sentence-structure-diagram-svg", + "build:packages": "npm run build --workspace=packages/sentence-structure-data && npm run build --workspace=packages/sentence-structure-diagram-notation && npm run build --workspace=packages/sentence-structure-diagram && npm run build --workspace=packages/sentence-structure-data-from-stanza", "build": "npm run build:packages && npm run build --workspace=packages/evaluation && npm run build --workspace=packages/backend && npm run build --workspace=packages/frontend", "format": "prettier --write .", "format:check": "prettier --check .", @@ -13,14 +13,12 @@ "prettier": "^3.7.4" }, "workspaces": [ + "packages/sentence-structure-data", + "packages/sentence-structure-diagram-notation", + "packages/sentence-structure-diagram", + "packages/sentence-structure-data-from-stanza", "packages/backend", "packages/frontend", - "packages/sentence-structure-data", - "packages/sentence-structure-diagram-data", - "packages/sentence-structure-diagram-svg", - "packages/evaluation", - "packages/sentence-structure-diagram-configurations", - "packages/sentence-structure-tree", - "packages/sentence-structure-diagram-tree" + "packages/evaluation" ] } diff --git a/packages/evaluation/calculate-metrics.ts b/packages/evaluation/calculate-metrics.ts index ff35a7a..d0c43e2 100644 --- a/packages/evaluation/calculate-metrics.ts +++ b/packages/evaluation/calculate-metrics.ts @@ -9,8 +9,8 @@ function isSameRange(range1: Range, range2: Range): boolean { range1.startWordIndex === range2.startWordIndex && range1.endWordIndex === range2.endWordIndex && range1.type === range2.type && - (range1.type === "relation" || - (range2.type !== "relation" && + (range1.type === "modification-element" || + (range2.type !== "modification-element" && range1.sentenceElementName === range2.sentenceElementName)) ); } diff --git a/packages/frontend/package.json b/packages/frontend/package.json index d8f1750..cbb2096 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -18,12 +18,14 @@ "@sentence-structure-diagram-app/backend": "^0.1.0", "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0", "@sentence-structure-diagram-app/sentence-structure-diagram-data": "^0.1.0", + "@sentence-structure-diagram-app/sentence-structure-diagram-notation": "^0.1.0", "@sentence-structure-diagram-app/sentence-structure-diagram-svg": "^0.1.0", "@tanstack/react-query": "^5.90.12", "@trpc/client": "^11.7.2", "@trpc/tanstack-react-query": "^11.7.2", "react": "^19.2.0", - "react-dom": "^19.2.0" + "react-dom": "^19.2.0", + "vite-plugin-static-copy": "^3.2.0" }, "devDependencies": { "@eslint/js": "^9.39.1", diff --git a/packages/frontend/src/pages/components/SentenceStructureEditor.tsx b/packages/frontend/src/pages/components/SentenceStructureEditor.tsx index 7ebc1d6..5c7177a 100644 --- a/packages/frontend/src/pages/components/SentenceStructureEditor.tsx +++ b/packages/frontend/src/pages/components/SentenceStructureEditor.tsx @@ -349,7 +349,7 @@ export default function SentenceStructureEditor() { ? sentenceElementRangeTypeToAllowedSentenceElementNameOptionsMap[ activeRange.type ] - : activeRange.kind === "sentence-structure" + : activeRange.kind === "sentence-constituent" ? sentenceStructureRangeTypeToAllowedSentenceElementNameOptionsMap[ activeRange.type ] @@ -527,8 +527,8 @@ export default function SentenceStructureEditor() { { ( { - "coordinating conjunction": "等位接続詞", - "correlative conjunction": "相関接続詞", + coordinator: "等位接続詞", + correlative: "相関接続詞", conjunct: "並列要素", } satisfies Record< CoordinationChildType, diff --git a/packages/frontend/src/pages/contexts/InteractionStateProvider.tsx b/packages/frontend/src/pages/contexts/InteractionStateProvider.tsx index e4b8051..4869bbf 100644 --- a/packages/frontend/src/pages/contexts/InteractionStateProvider.tsx +++ b/packages/frontend/src/pages/contexts/InteractionStateProvider.tsx @@ -16,7 +16,6 @@ import { findRangeByStartAndEndWordIndex, updateSentenceElementName, type CoordinationChildType, - type RangeType, type SentenceElementName, type SentenceElementRangeType, type SentenceStructureData, @@ -40,7 +39,6 @@ type InteractionState = } | { type: "range-selected"; - rangeType: RangeType; rangeId: string; } | { @@ -227,7 +225,6 @@ function interactionStateReducer( success: true, interactionState: { type: "range-selected", - rangeType: matchedRange.type, rangeId: matchedRange.id, }, sentenceStructureData, @@ -322,8 +319,8 @@ function interactionStateReducer( sentenceStructureData, }; default: { - const _exhaustiveCheck: never = interactionState; - return _exhaustiveCheck; + interactionState satisfies never; + throw new Error("Unreachable"); } } case "MOUSE_DOWN_ON_WORD": @@ -380,8 +377,8 @@ function interactionStateReducer( sentenceStructureData, }; default: { - const _exhaustiveCheck: never = interactionState; - return _exhaustiveCheck; + interactionState satisfies never; + throw new Error("Unreachable"); } } case "MOUSE_ENTER_ON_WORD": @@ -447,8 +444,8 @@ function interactionStateReducer( sentenceStructureData, }; default: { - const _exhaustiveCheck: never = interactionState; - return _exhaustiveCheck; + interactionState satisfies never; + throw new Error("Unreachable"); } } case "MOUSE_UP_ON_WORD": @@ -478,7 +475,6 @@ function interactionStateReducer( success: true, interactionState: { type: "range-selected", - rangeType: matchedRange.type, rangeId: matchedRange.id, }, sentenceStructureData, @@ -573,8 +569,8 @@ function interactionStateReducer( sentenceStructureData, }; default: { - const _exhaustiveCheck: never = interactionState; - return _exhaustiveCheck; + interactionState satisfies never; + throw new Error("Unreachable"); } } case "CREATE_SENTENCE_ELEMENT_RANGE": { @@ -590,7 +586,6 @@ function interactionStateReducer( success: true, interactionState: { type: "range-selected", - rangeType: action.payload.rangeType, rangeId: result.data.rangeId, }, sentenceStructureData: result.data.newSentenceStructureData, @@ -612,7 +607,6 @@ function interactionStateReducer( success: true, interactionState: { type: "range-selected", - rangeType: action.payload.rangeType, rangeId: result.data.rangeId, }, sentenceStructureData: result.data.newSentenceStructureData, @@ -631,7 +625,6 @@ function interactionStateReducer( success: true, interactionState: { type: "range-selected", - rangeType: matchedRange.type, rangeId: action.payload.rangeId, }, sentenceStructureData, @@ -643,12 +636,13 @@ function interactionStateReducer( return { success: true, interactionState, - sentenceStructureData: updateSentenceElementName< - typeof interactionState.rangeType - >(sentenceStructureData, { - rangeId: interactionState.rangeId, - sentenceElementName: action.payload.sentenceElementName, - }), + sentenceStructureData: updateSentenceElementName( + sentenceStructureData, + { + rangeId: interactionState.rangeId, + sentenceElementName: action.payload.sentenceElementName, + }, + ), }; case "DELETE_RANGE": if (interactionState.type !== "range-selected") @@ -702,7 +696,6 @@ function interactionStateReducer( success: true, interactionState: { type: "range-selected", - rangeType: fromRange.type, rangeId: fromRange.id, }, sentenceStructureData, @@ -773,8 +766,8 @@ function interactionStateReducer( }; } default: { - const _exhaustiveCheck: never = interactionState; - return _exhaustiveCheck; + interactionState satisfies never; + throw new Error("Unreachable"); } } case "CREATE_COORDINATION_CHILD": @@ -851,8 +844,8 @@ function interactionStateReducer( }), }; default: { - const _exhaustiveCheck: never = action; - return _exhaustiveCheck; + action satisfies never; + throw new Error("Unreachable"); } } } diff --git a/packages/frontend/src/pages/utils/measure-text-width.ts b/packages/frontend/src/pages/utils/measure-text-width.ts index 18b4f8c..c24f49f 100644 --- a/packages/frontend/src/pages/utils/measure-text-width.ts +++ b/packages/frontend/src/pages/utils/measure-text-width.ts @@ -7,3 +7,13 @@ export function measureTextWidth(text: string): number { const metrics = context.measureText(text); return metrics.width; } + +export function measureTextDescent(text: string): number { + const canvas = document.createElement("canvas"); + const context = canvas.getContext("2d"); + if (!context) return 0; + context.font = "16px system-ui"; + + const metrics = context.measureText(text); + return metrics.actualBoundingBoxDescent; +} diff --git a/packages/frontend/vite.config.ts b/packages/frontend/vite.config.ts index 9313147..7355ce9 100644 --- a/packages/frontend/vite.config.ts +++ b/packages/frontend/vite.config.ts @@ -1,8 +1,52 @@ +import { dirname, join } from "path"; +import { fileURLToPath } from "url"; import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; +import { viteStaticCopy } from "vite-plugin-static-copy"; + +const PYODIDE_EXCLUDE = [ + "!**/*.{md,html}", + "!**/*.d.ts", + "!**/*.whl", + "!**/node_modules", +]; + +function viteStaticCopyPyodide() { + const pyodideDir = dirname(fileURLToPath(import.meta.resolve("pyodide"))); + return viteStaticCopy({ + targets: [ + { + src: [join(pyodideDir, "*")].concat(PYODIDE_EXCLUDE), + dest: "assets", + }, + ], + }); +} + +function viteStaticCopySentenceStructureDataParserWheels() { + const sentenceStructureDataParserWheelsDir = dirname( + fileURLToPath( + import.meta + .resolve("@sentence-structure-diagram-app/sentence-structure-data-parser/browser"), + ), + ); + return viteStaticCopy({ + targets: [ + { + src: [join(sentenceStructureDataParserWheelsDir, "wheels/*")], + dest: "assets", + }, + ], + }); +} // https://vite.dev/config/ export default defineConfig({ - plugins: [react()], + plugins: [ + react(), + viteStaticCopyPyodide(), + viteStaticCopySentenceStructureDataParserWheels(), + ], + optimizeDeps: { exclude: ["pyodide"] }, base: process.env.BASE_URL || "/", }); diff --git a/packages/sentence-structure-diagram-configurations/.gitignore b/packages/sentence-structure-data-from-stanza/.gitignore similarity index 100% rename from packages/sentence-structure-diagram-configurations/.gitignore rename to packages/sentence-structure-data-from-stanza/.gitignore diff --git a/packages/sentence-structure-data-from-stanza/create.ts b/packages/sentence-structure-data-from-stanza/create.ts new file mode 100644 index 0000000..c3775ac --- /dev/null +++ b/packages/sentence-structure-data-from-stanza/create.ts @@ -0,0 +1,1919 @@ +import { + addCoordination, + addModification, + addSentenceStructureElement, + coreSentenceElementAllowedSentenceElementNameOptions, + createSentenceStructureDocumentFromWords, + sentenceConstituentTypeToAllowedSentenceElementNameOptionsMap, + type CoordinationPartType, + type SentenceStructureDocument, +} from "@sentence-structure-diagram-app/sentence-structure-data"; +import type { + StanzaDependencyLabel, + StanzaDocument, + StanzaParseTree, + StanzaSentence, + StanzaWord, +} from "./types.js"; + +function getConstituencyLeafNodes( + constituencyNode: StanzaParseTree, +): StanzaParseTree[] { + if (constituencyNode.children.length === 0) { + return [constituencyNode]; + } + return constituencyNode.children.flatMap((child) => + getConstituencyLeafNodes(child), + ); +} + +function getCorrespondingConstituencyLeafNode( + sentence: StanzaSentence, + dependencyWord: StanzaWord, +): StanzaParseTree { + const dependencyWords = sentence.tokens.flatMap((token) => token.words); + const constituencyLeafNodes = getConstituencyLeafNodes(sentence.constituency); + const correspondingConstituencyLeafNode = constituencyLeafNodes.at( + dependencyWords.indexOf(dependencyWord), + ); + if (!correspondingConstituencyLeafNode) { + throw new Error("Corresponding constituency leaf node not found"); + } + return correspondingConstituencyLeafNode; +} + +function getConstituencyParentNode( + sentence: StanzaSentence, + constituencyNode: StanzaParseTree, +): StanzaParseTree | null { + if (sentence.constituency === constituencyNode) { + return null; + } + + function findConstituencyParentNode( + currentConstituencyNode: StanzaParseTree, + ): StanzaParseTree | null { + if (currentConstituencyNode.children.includes(constituencyNode)) { + return currentConstituencyNode; + } + + for (const constituencyChildNode of currentConstituencyNode.children) { + const constituencyParentNode = findConstituencyParentNode( + constituencyChildNode, + ); + if (constituencyParentNode) { + return constituencyParentNode; + } + } + + return null; + } + + const constituencyParentNode = findConstituencyParentNode( + sentence.constituency, + ); + if (!constituencyParentNode) { + throw new Error("Constituency parent node not found"); + } + + return constituencyParentNode; +} + +function getConstituencyDirectChildNodeOnPathToLeafNode( + constituencyNode: StanzaParseTree, + targetLeafNode: StanzaParseTree, +): StanzaParseTree { + const constituencyDirectChildNodeOnPathToLeafNode = + constituencyNode.children.find((childNode) => + getConstituencyLeafNodes(childNode).includes(targetLeafNode), + ); + if (!constituencyDirectChildNodeOnPathToLeafNode) { + throw new Error( + "Constituency direct child node on path to leaf node not found", + ); + } + + return constituencyDirectChildNodeOnPathToLeafNode; +} + +function getConstituencyNodeSpan( + sentence: StanzaSentence, + constituencyNode: StanzaParseTree, +): { startWordIndex: number; endWordIndex: number } { + const sentenceLeafNodes = getConstituencyLeafNodes(sentence.constituency); + const subtreeLeafNodes = getConstituencyLeafNodes(constituencyNode); + + const subtreeStartLeafNode = subtreeLeafNodes.at(0); + const subtreeEndLeafNode = subtreeLeafNodes.at(-1); + if (!subtreeStartLeafNode || !subtreeEndLeafNode) { + throw new Error("Constituency node has no leaf nodes"); + } + + const startWordIndex = sentenceLeafNodes.indexOf(subtreeStartLeafNode); + const endWordIndex = sentenceLeafNodes.indexOf(subtreeEndLeafNode); + if (startWordIndex === -1 || endWordIndex === -1) { + throw new Error("Constituency node is not in the tree"); + } + + return { startWordIndex, endWordIndex }; +} + +function isCoordinationNode( + sentence: StanzaSentence, + constituencyNode: StanzaParseTree, +): boolean { + const dependencyWords = sentence.tokens.flatMap((token) => token.words); + const childNodeSpans = constituencyNode.children.map((childNode) => + getConstituencyNodeSpan(sentence, childNode), + ); + return dependencyWords.some((dependentWord, dependentWordIndex) => { + if (dependentWord.deprel !== "conj") { + return false; + } + + const dependencyHeadWordIndex = dependencyWords.findIndex( + (candidateDependencyWord) => + candidateDependencyWord.id === dependentWord.head, + ); + if (dependencyHeadWordIndex === -1) { + throw new Error("Dependency head word not found"); + } + + const dependentWordChildNodeSpan = childNodeSpans.find( + ({ startWordIndex, endWordIndex }) => + startWordIndex <= dependentWordIndex && + dependentWordIndex <= endWordIndex, + ); + const dependencyHeadWordChildNodeSpan = childNodeSpans.find( + ({ startWordIndex, endWordIndex }) => + startWordIndex <= dependencyHeadWordIndex && + dependencyHeadWordIndex <= endWordIndex, + ); + if (!dependentWordChildNodeSpan || !dependencyHeadWordChildNodeSpan) { + return false; + } + + return dependentWordChildNodeSpan !== dependencyHeadWordChildNodeSpan; + }); +} + +function getCoreNominalSpan( + sentence: StanzaSentence, + dependencyHeadWord: StanzaWord, +): { startWordIndex: number; endWordIndex: number } | null { + try { + const correspondingConstituencyHeadLeafNode = + getCorrespondingConstituencyLeafNode(sentence, dependencyHeadWord); + + function ascendToCandidateCoreNominalNode( + constituencyNode: StanzaParseTree, + ): StanzaParseTree { + if ( + constituencyNode.label === "NP" || + constituencyNode.label === "WHNP" + ) { + return constituencyNode; + } + + const constituencyParentNode = getConstituencyParentNode( + sentence, + constituencyNode, + ); + if (!constituencyParentNode) { + throw new Error("Candidate core nominal node not found"); + } + + return ascendToCandidateCoreNominalNode(constituencyParentNode); + } + + function descendToCoreNominalNode( + constituencyNode: StanzaParseTree, + ): StanzaParseTree { + if (isCoordinationNode(sentence, constituencyNode)) { + return descendToCoreNominalNode( + getConstituencyDirectChildNodeOnPathToLeafNode( + constituencyNode, + correspondingConstituencyHeadLeafNode, + ), + ); + } + + return constituencyNode; + } + + const candidateCoreNominalNode = ascendToCandidateCoreNominalNode( + correspondingConstituencyHeadLeafNode, + ); + const coreNominalNode = descendToCoreNominalNode(candidateCoreNominalNode); + return getConstituencyNodeSpan(sentence, coreNominalNode); + } catch (error) { + console.error(error); + return null; + } +} + +function getCoreVerbComplexSpan( + sentence: StanzaSentence, + predicateCarrierWord: StanzaWord, +): { startWordIndex: number; endWordIndex: number } | null { + try { + const dependencyWords = sentence.tokens.flatMap((token) => token.words); + const coreVerbComplexWords = (() => { + if (predicateCarrierWord.deprel === "cop") { + const copHeadWord = dependencyWords.find( + (dependencyWord) => dependencyWord.id === predicateCarrierWord.head, + ); + if (!copHeadWord) { + throw new Error("Cop head word not found"); + } + + const predicateCarrierWordIndex = + dependencyWords.indexOf(predicateCarrierWord); + if (predicateCarrierWordIndex === -1) { + throw new Error("Predicate carrier word not found"); + } + + const previousCopDependentWordIndex = dependencyWords.findLastIndex( + (dependencyWord, dependencyWordIndex) => + dependencyWord.deprel === "cop" && + dependencyWord.head === copHeadWord.id && + dependencyWordIndex < predicateCarrierWordIndex, + ); + + return dependencyWords + .slice( + previousCopDependentWordIndex + 1, + predicateCarrierWordIndex + 1, + ) + .filter( + (dependencyWord) => + dependencyWord.id === predicateCarrierWord.id || + (dependencyWord.head === copHeadWord.id && + (dependencyWord.deprel === "aux" || + dependencyWord.deprel === "aux:pass")), + ); + } + return dependencyWords.filter( + (dependencyWord) => + dependencyWord.id === predicateCarrierWord.id || + (dependencyWord.head === predicateCarrierWord.id && + (dependencyWord.deprel === "aux" || + dependencyWord.deprel === "aux:pass")), + ); + })(); + + if (coreVerbComplexWords.length === 0) { + throw new Error("Core verb complex words not found"); + } + + return { + startWordIndex: dependencyWords.indexOf(coreVerbComplexWords.at(0)!), + endWordIndex: dependencyWords.indexOf(coreVerbComplexWords.at(-1)!), + }; + } catch (error) { + console.error(error); + return null; + } +} + +function getCoreAdjectivalSpan( + sentence: StanzaSentence, + dependencyHeadWord: StanzaWord, +): { startWordIndex: number; endWordIndex: number } | null { + const correspondingConstituencyHeadLeafNode = + getCorrespondingConstituencyLeafNode(sentence, dependencyHeadWord); + + function ascendToCoreAdjectivalNode( + constituencyNode: StanzaParseTree, + ): StanzaParseTree | null { + if (constituencyNode.label === "WHADJP") { + return constituencyNode; + } + + const constituencyParentNode = getConstituencyParentNode( + sentence, + constituencyNode, + ); + if (!constituencyParentNode) { + return null; + } + + return ascendToCoreAdjectivalNode(constituencyParentNode); + } + + const coreAdjectivalNode = ascendToCoreAdjectivalNode( + correspondingConstituencyHeadLeafNode, + ); + + if (coreAdjectivalNode) { + return getConstituencyNodeSpan(sentence, coreAdjectivalNode); + } + + const dependencyWords = sentence.tokens.flatMap((token) => token.words); + const dependencyHeadWordIndex = dependencyWords.findIndex( + (dependencyWord) => dependencyWord.id === dependencyHeadWord.id, + ); + if (dependencyHeadWordIndex === -1) { + throw new Error("Dependency head word not found"); + } + return { + startWordIndex: dependencyHeadWordIndex, + endWordIndex: dependencyHeadWordIndex, + }; +} + +function getPhraseSpan( + sentence: StanzaSentence, + dependencyHeadWord: StanzaWord, +): { startWordIndex: number; endWordIndex: number } | null { + try { + const correspondingConstituencyHeadLeafNode = + getCorrespondingConstituencyLeafNode(sentence, dependencyHeadWord); + + function ascendToCandidatePhraseNode( + constituencyNode: StanzaParseTree, + ): StanzaParseTree { + const constituencyParentNode = getConstituencyParentNode( + sentence, + constituencyNode, + ); + + if (constituencyNode.label === "S") { + if ( + constituencyParentNode?.label === "SBAR" || + constituencyParentNode?.label === "PP" + ) { + return constituencyParentNode; + } + return constituencyNode; + } + + if ( + constituencyNode.label === "VP" && + constituencyParentNode?.label === "NP" + ) { + return constituencyNode; + } + + if (constituencyNode.label === "NP") { + return constituencyNode; + } + + if (!constituencyParentNode) { + throw new Error("Candidate phrase node not found"); + } + + return ascendToCandidatePhraseNode(constituencyParentNode); + } + + function descendToPhraseNode( + constituencyNode: StanzaParseTree, + ): StanzaParseTree { + if (isCoordinationNode(sentence, constituencyNode)) { + return descendToPhraseNode( + getConstituencyDirectChildNodeOnPathToLeafNode( + constituencyNode, + correspondingConstituencyHeadLeafNode, + ), + ); + } + + if (constituencyNode.children.length === 1) { + return descendToPhraseNode(constituencyNode.children.at(0)!); + } + + return constituencyNode; + } + + const candidatePhraseNode = ascendToCandidatePhraseNode( + correspondingConstituencyHeadLeafNode, + ); + const phraseNode = descendToPhraseNode(candidatePhraseNode); + return getConstituencyNodeSpan(sentence, phraseNode); + } catch (error) { + console.error(error); + return null; + } +} + +function getClauseSpan( + sentence: StanzaSentence, + dependencyHeadWord: StanzaWord, +): { startWordIndex: number; endWordIndex: number } | null { + try { + const correspondingConstituencyHeadLeafNode = + getCorrespondingConstituencyLeafNode(sentence, dependencyHeadWord); + + function ascendToClauseNode( + constituencyNode: StanzaParseTree, + ): StanzaParseTree { + const constituencyParentNode = getConstituencyParentNode( + sentence, + constituencyNode, + ); + if (!constituencyParentNode) { + throw new Error("Clause node not found"); + } + + if ( + constituencyNode.label === "S" || + constituencyNode.label === "SQ" || + constituencyNode.label === "SINV" + ) { + if ( + (constituencyParentNode.label === "S" || + constituencyParentNode.label === "SQ" || + constituencyParentNode.label === "SINV") && + isCoordinationNode(sentence, constituencyParentNode) + ) { + return ascendToClauseNode(constituencyParentNode); + } + if ( + constituencyParentNode.label === "SBAR" || + constituencyParentNode.label === "SBARQ" + ) { + return constituencyParentNode; + } + return constituencyNode; + } + + return ascendToClauseNode(constituencyParentNode); + } + + const clauseNode = ascendToClauseNode( + correspondingConstituencyHeadLeafNode, + ); + return getConstituencyNodeSpan(sentence, clauseNode); + } catch (error) { + console.error(error); + return null; + } +} + +function getAdverbialPhraseSpan( + sentence: StanzaSentence, + dependencyHeadWord: StanzaWord, +): { startWordIndex: number; endWordIndex: number } | null { + try { + const dependencyWords = sentence.tokens.flatMap((token) => token.words); + const correspondingConstituencyHeadLeafNode = + getCorrespondingConstituencyLeafNode(sentence, dependencyHeadWord); + + function ascendToAdverbialPhraseNode( + constituencyNode: StanzaParseTree, + ): StanzaParseTree { + const constituencyNodeSpan = getConstituencyNodeSpan( + sentence, + constituencyNode, + ); + const hasCrossingFixedDependency = dependencyWords.some( + (dependentWord, dependentWordIndex) => { + if (dependentWord.head === 0 || dependentWord.deprel !== "fixed") { + return false; + } + + const dependencyHeadWordIndex = dependencyWords.findIndex( + (dependencyWord) => dependencyWord.id === dependentWord.head, + ); + if (dependencyHeadWordIndex === -1) { + throw new Error("Dependency head word not found"); + } + + const isDependentWordInsideConstituencyNodeSpan = + constituencyNodeSpan.startWordIndex <= dependentWordIndex && + dependentWordIndex <= constituencyNodeSpan.endWordIndex; + const isHeadwordInsideConstituencyNodeSpan = + constituencyNodeSpan.startWordIndex <= dependencyHeadWordIndex && + dependencyHeadWordIndex <= constituencyNodeSpan.endWordIndex; + + return ( + isDependentWordInsideConstituencyNodeSpan !== + isHeadwordInsideConstituencyNodeSpan + ); + }, + ); + + if ( + (constituencyNode.label === "PP" || + constituencyNode.label === "ADVP" || + constituencyNode.label === "PRT" || + constituencyNode.label === "WHPP" || + constituencyNode.label === "WHADVP") && + !hasCrossingFixedDependency + ) { + return constituencyNode; + } + + const constituencyParentNode = getConstituencyParentNode( + sentence, + constituencyNode, + ); + if (!constituencyParentNode) { + throw new Error("Adverbial phrase node not found"); + } + + return ascendToAdverbialPhraseNode(constituencyParentNode); + } + + const adverbialPhraseNode = ascendToAdverbialPhraseNode( + correspondingConstituencyHeadLeafNode, + ); + return getConstituencyNodeSpan(sentence, adverbialPhraseNode); + } catch (error) { + console.error(error); + return null; + } +} + +function getResolvedDependencyLabel( + sentence: StanzaSentence, + dependentWord: StanzaWord, +): Exclude { + const dependencyWords = sentence.tokens.flatMap((token) => token.words); + + if (dependentWord.deprel === "conj") { + const dependencyHeadWord = dependencyWords.find( + (dependencyWord) => dependencyWord.id === dependentWord.head, + ); + if (!dependencyHeadWord) { + throw new Error("Dependency head word not found"); + } + return getResolvedDependencyLabel(sentence, dependencyHeadWord); + } + + return dependentWord.deprel; +} + +function getResolvedDependencyHeadWord( + sentence: StanzaSentence, + dependentWord: StanzaWord, +): StanzaWord | null { + const dependencyWords = sentence.tokens.flatMap((token) => token.words); + + if (dependentWord.head === 0) { + return null; + } + + const dependencyHeadWord = dependencyWords.find( + (dependencyWord) => dependencyWord.id === dependentWord.head, + ); + if (!dependencyHeadWord) { + throw new Error("Dependency head word not found"); + } + + if (dependentWord.deprel === "conj") { + return getResolvedDependencyHeadWord(sentence, dependencyHeadWord); + } + + return dependencyHeadWord; +} + +function getDependencySpanConnectorWord( + sentence: StanzaSentence, + span: { startWordIndex: number; endWordIndex: number }, +): StanzaWord | null { + const dependencyWords = sentence.tokens.flatMap((token) => token.words); + const connectorWords = dependencyWords.filter( + (dependencyWord, dependencyWordIndex) => { + if (dependencyWord.head === 0) { + return false; + } + + const dependencyHeadWordIndex = dependencyWords.findIndex( + (candidateDependencyWord) => + candidateDependencyWord.id === dependencyWord.head, + ); + if (dependencyHeadWordIndex === -1) { + throw new Error("Dependency head word not found"); + } + + return ( + span.startWordIndex <= dependencyWordIndex && + dependencyWordIndex <= span.endWordIndex && + (dependencyHeadWordIndex < span.startWordIndex || + span.endWordIndex < dependencyHeadWordIndex) + ); + }, + ); + + if (connectorWords.length !== 1) { + return null; + } + + return connectorWords.at(0)!; +} + +function getPredicateFiniteness( + sentence: StanzaSentence, + predicateCarrierWord: StanzaWord, +): "non-finite" | "finite" | null { + const dependencyWords = sentence.tokens.flatMap((token) => token.words); + + const coreVerbComplexWords = (() => { + const coreVerbComplexSpan = getCoreVerbComplexSpan( + sentence, + predicateCarrierWord, + ); + if (!coreVerbComplexSpan) { + return []; + } + return dependencyWords.slice( + coreVerbComplexSpan.startWordIndex, + coreVerbComplexSpan.endWordIndex + 1, + ); + })(); + if (coreVerbComplexWords.length === 0) { + return null; + } + + if ( + coreVerbComplexWords.every( + (coreVerbComplexWord) => + !coreVerbComplexWord.feats?.includes("VerbForm=Fin"), + ) && + coreVerbComplexWords.some( + (coreVerbComplexWord) => + coreVerbComplexWord.feats?.includes("VerbForm=Inf") || + coreVerbComplexWord.feats?.includes("VerbForm=Part") || + coreVerbComplexWord.feats?.includes("VerbForm=Ger"), + ) + ) { + return "non-finite"; + } + + if ( + coreVerbComplexWords.some((coreVerbComplexWord) => + coreVerbComplexWord.feats?.includes("VerbForm=Fin"), + ) + ) { + return "finite"; + } + + return null; +} + +type PredicateConstituent = { + type: "phrase" | "clause"; + predicateCarrierWord: StanzaWord; + connectorWord: StanzaWord | null; + span: { startWordIndex: number; endWordIndex: number }; +}; + +function getPredicateConstituents( + sentence: StanzaSentence, +): PredicateConstituent[] { + const dependencyWords = sentence.tokens.flatMap((token) => token.words); + + function isPredicateCarrierWord(dependencyWord: StanzaWord): boolean { + return ( + dependencyWord.deprel === "cop" || + ((dependencyWord.upos === "VERB" || dependencyWord.upos === "AUX") && + dependencyWord.deprel !== "aux" && + dependencyWord.deprel !== "aux:pass") + ); + } + + return dependencyWords.flatMap((predicateCarrierWord) => { + if (!isPredicateCarrierWord(predicateCarrierWord)) { + return []; + } + + const predicateConstituent = (() => { + const predicateFiniteness = getPredicateFiniteness( + sentence, + predicateCarrierWord, + ); + switch (predicateFiniteness) { + case "non-finite": { + const phraseSpan = getPhraseSpan(sentence, predicateCarrierWord); + if (!phraseSpan) { + return null; + } + return { + type: "phrase" as const, + predicateCarrierWord, + connectorWord: getDependencySpanConnectorWord(sentence, phraseSpan), + span: phraseSpan, + }; + } + case "finite": { + const clauseSpan = getClauseSpan(sentence, predicateCarrierWord); + if (!clauseSpan) { + return null; + } + return { + type: "clause" as const, + predicateCarrierWord, + connectorWord: getDependencySpanConnectorWord(sentence, clauseSpan), + span: clauseSpan, + }; + } + case null: { + return null; + } + default: { + predicateFiniteness satisfies never; + throw new Error("Unreachable"); + } + } + })(); + if (!predicateConstituent) { + return []; + } + + return [predicateConstituent]; + }); +} + +function getSentenceStructureElements(sentence: StanzaSentence): ( + | { + kind: "core-sentence-element"; + sentenceElementName: (typeof coreSentenceElementAllowedSentenceElementNameOptions)[number]; + span: { startWordIndex: number; endWordIndex: number }; + } + | { + kind: "sentence-constituent"; + type: "phrase"; + usage: "nominal" | "adjectival" | "adverbial"; + sentenceElementName: (typeof sentenceConstituentTypeToAllowedSentenceElementNameOptionsMap)["phrase"][number]; + span: { startWordIndex: number; endWordIndex: number }; + } + | { + kind: "sentence-constituent"; + type: "clause"; + usage: "nominal" | "adjectival" | "adverbial"; + sentenceElementName: (typeof sentenceConstituentTypeToAllowedSentenceElementNameOptionsMap)["clause"][number]; + span: { startWordIndex: number; endWordIndex: number }; + } + | { + kind: "sentence-constituent"; + type: "adverbial-phrase"; + sentenceElementName: (typeof sentenceConstituentTypeToAllowedSentenceElementNameOptionsMap)["adverbial-phrase"][number]; + span: { startWordIndex: number; endWordIndex: number }; + } +)[] { + const dependencyWords = sentence.tokens.flatMap((token) => token.words); + const predicateConstituents = getPredicateConstituents(sentence); + + const sentenceStructureElements: ReturnType< + typeof getSentenceStructureElements + > = []; + + for (const dependencyWord of dependencyWords) { + const resolvedDependencyLabel = getResolvedDependencyLabel( + sentence, + dependencyWord, + ); + const predicateConstituentForDependencyWord = + predicateConstituents.find( + (predicateConstituent) => + predicateConstituent.connectorWord?.id === dependencyWord.id, + ) ?? null; + + if ( + (dependencyWord.upos === "VERB" || dependencyWord.upos === "AUX") && + resolvedDependencyLabel === "root" + ) { + const coreVerbComplexSpan = getCoreVerbComplexSpan( + sentence, + dependencyWord, + ); + if (coreVerbComplexSpan) { + sentenceStructureElements.push({ + kind: "core-sentence-element", + sentenceElementName: "V", + span: coreVerbComplexSpan, + }); + } else { + console.error("Core verb complex span not found"); + } + } + + switch (resolvedDependencyLabel) { + case "cop": { + const currentCopulaWord = dependencyWord; + const currentCopulaWordIndex = dependencyWords.findIndex( + (candidateDependencyWord) => + candidateDependencyWord.id === currentCopulaWord.id, + ); + if (currentCopulaWordIndex === -1) { + throw new Error("Current copula word not found"); + } + const copHeadWord = dependencyWords.find( + (candidateDependencyWord) => + candidateDependencyWord.id === currentCopulaWord.head, + ); + if (!copHeadWord) { + throw new Error("Dependency head word not found"); + } + const copulaWordsForCopHead = dependencyWords.filter( + (candidateDependencyWord) => + candidateDependencyWord.head === copHeadWord.id && + candidateDependencyWord.deprel === "cop", + ); + + if (getResolvedDependencyLabel(sentence, copHeadWord) === "root") { + const coreVerbComplexSpan = getCoreVerbComplexSpan( + sentence, + currentCopulaWord, + ); + if (coreVerbComplexSpan) { + sentenceStructureElements.push({ + kind: "core-sentence-element", + sentenceElementName: "V", + span: coreVerbComplexSpan, + }); + } else { + console.error("Core verb complex span not found"); + } + } + + const copComplementHeadWords = + currentCopulaWord.id === copulaWordsForCopHead.at(-1)!.id + ? dependencyWords.filter( + (candidateDependencyWord) => + candidateDependencyWord.id === copHeadWord.id || + (candidateDependencyWord.head === copHeadWord.id && + candidateDependencyWord.deprel === "conj"), + ) + : [copHeadWord]; + for (const copComplementHeadWord of copComplementHeadWords) { + const followingCopulaWord = + dependencyWords.find( + (candidateDependencyWord, candidateDependencyWordIndex) => + candidateDependencyWord.head === copComplementHeadWord.id && + candidateDependencyWord.deprel === "cop" && + currentCopulaWordIndex < candidateDependencyWordIndex, + ) ?? null; + if (followingCopulaWord) { + const followingCopPredicateConstituent = + predicateConstituents.find( + (predicateConstituent) => + predicateConstituent.predicateCarrierWord.id === + followingCopulaWord.id, + ) ?? null; + if (!followingCopPredicateConstituent) { + console.error("Predicate constituent type not found"); + continue; + } + + sentenceStructureElements.push({ + kind: "sentence-constituent", + type: followingCopPredicateConstituent.type, + usage: "nominal", + sentenceElementName: "C", + span: followingCopPredicateConstituent.span, + }); + + const coreVerbComplexSpan = getCoreVerbComplexSpan( + sentence, + followingCopPredicateConstituent.predicateCarrierWord, + ); + if (!coreVerbComplexSpan) { + console.error("Core verb complex span not found"); + continue; + } + sentenceStructureElements.push({ + kind: "core-sentence-element", + sentenceElementName: "V", + span: coreVerbComplexSpan, + }); + continue; + } + + switch (copComplementHeadWord.upos) { + case "ADJ": { + const coreAdjectivalSpan = getCoreAdjectivalSpan( + sentence, + copComplementHeadWord, + ); + if (!coreAdjectivalSpan) { + console.error("Core adjectival span not found"); + break; + } + sentenceStructureElements.push({ + kind: "core-sentence-element", + sentenceElementName: "C", + span: coreAdjectivalSpan, + }); + break; + } + case "NOUN": + case "PROPN": + case "PRON": + case "NUM": + case "SYM": + case "INTJ": + case "X": { + if ( + dependencyWords.some( + (dependentWord) => + dependentWord.head === copComplementHeadWord.id && + dependentWord.deprel === "case", + ) + ) { + const adverbialPhraseSpan = getAdverbialPhraseSpan( + sentence, + copComplementHeadWord, + ); + if (!adverbialPhraseSpan) { + console.error("Adverbial phrase span not found"); + break; + } + sentenceStructureElements.push({ + kind: "sentence-constituent", + type: "adverbial-phrase", + sentenceElementName: "M", + span: adverbialPhraseSpan, + }); + break; + } + + const coreNominalSpan = getCoreNominalSpan( + sentence, + copComplementHeadWord, + ); + if (!coreNominalSpan) { + console.error("Core nominal span not found"); + break; + } + sentenceStructureElements.push({ + kind: "core-sentence-element", + sentenceElementName: "C", + span: coreNominalSpan, + }); + break; + } + case "ADV": + case "ADP": { + const adverbialPhraseSpan = getAdverbialPhraseSpan( + sentence, + copComplementHeadWord, + ); + if (!adverbialPhraseSpan) { + console.error("Adverbial phrase span not found"); + break; + } + sentenceStructureElements.push({ + kind: "sentence-constituent", + type: "adverbial-phrase", + sentenceElementName: "M", + span: adverbialPhraseSpan, + }); + break; + } + case "VERB": { + const copComplementHeadWordPredicateConstituent = + predicateConstituents.find( + (predicateConstituent) => + predicateConstituent.predicateCarrierWord.id === + copComplementHeadWord.id, + ) ?? null; + if (!copComplementHeadWordPredicateConstituent) { + console.error("Predicate constituent type not found"); + break; + } + + sentenceStructureElements.push({ + kind: "sentence-constituent", + type: copComplementHeadWordPredicateConstituent.type, + usage: "nominal", + sentenceElementName: "C", + span: copComplementHeadWordPredicateConstituent.span, + }); + + const coreVerbComplexSpan = getCoreVerbComplexSpan( + sentence, + copComplementHeadWordPredicateConstituent.predicateCarrierWord, + ); + if (!coreVerbComplexSpan) { + console.error("Core verb complex span not found"); + break; + } + sentenceStructureElements.push({ + kind: "core-sentence-element", + sentenceElementName: "V", + span: coreVerbComplexSpan, + }); + break; + } + case "AUX": + case "CCONJ": + case "DET": + case "PART": + case "PUNCT": + case "SCONJ": { + console.error("Unexpected cop head UPOS"); + break; + } + default: { + copComplementHeadWord.upos satisfies never; + throw new Error("Unreachable"); + } + } + } + break; + } + case "nsubj": + case "nsubj:pass": + case "nsubj:outer": { + const coreNominalSpan = getCoreNominalSpan(sentence, dependencyWord); + if (!coreNominalSpan) { + console.error("Core nominal span not found"); + break; + } + sentenceStructureElements.push({ + kind: "core-sentence-element", + sentenceElementName: "S", + span: coreNominalSpan, + }); + break; + } + case "csubj": + case "csubj:pass": + case "csubj:outer": { + if (!predicateConstituentForDependencyWord) { + console.error("Predicate constituent type not found"); + break; + } + + sentenceStructureElements.push({ + kind: "sentence-constituent", + type: predicateConstituentForDependencyWord.type, + usage: "nominal", + sentenceElementName: "S", + span: predicateConstituentForDependencyWord.span, + }); + + const coreVerbComplexSpan = getCoreVerbComplexSpan( + sentence, + predicateConstituentForDependencyWord.predicateCarrierWord, + ); + if (!coreVerbComplexSpan) { + console.error("Core verb complex span not found"); + break; + } + sentenceStructureElements.push({ + kind: "core-sentence-element", + sentenceElementName: "V", + span: coreVerbComplexSpan, + }); + break; + } + case "obj": + case "iobj": { + const coreNominalSpan = getCoreNominalSpan(sentence, dependencyWord); + if (!coreNominalSpan) { + console.error("Core nominal span not found"); + break; + } + sentenceStructureElements.push({ + kind: "core-sentence-element", + sentenceElementName: "O", + span: coreNominalSpan, + }); + break; + } + case "ccomp": { + if (!predicateConstituentForDependencyWord) { + console.error("Predicate constituent type not found"); + break; + } + + sentenceStructureElements.push({ + kind: "sentence-constituent", + type: predicateConstituentForDependencyWord.type, + usage: "nominal", + sentenceElementName: "O", + span: predicateConstituentForDependencyWord.span, + }); + + const coreVerbComplexSpan = getCoreVerbComplexSpan( + sentence, + predicateConstituentForDependencyWord.predicateCarrierWord, + ); + if (!coreVerbComplexSpan) { + console.error("Core verb complex span not found"); + break; + } + sentenceStructureElements.push({ + kind: "core-sentence-element", + sentenceElementName: "V", + span: coreVerbComplexSpan, + }); + break; + } + case "xcomp": { + if (predicateConstituentForDependencyWord) { + sentenceStructureElements.push({ + kind: "sentence-constituent", + type: predicateConstituentForDependencyWord.type, + usage: "nominal", + sentenceElementName: "C", + span: predicateConstituentForDependencyWord.span, + }); + + const coreVerbComplexSpan = getCoreVerbComplexSpan( + sentence, + predicateConstituentForDependencyWord.predicateCarrierWord, + ); + if (!coreVerbComplexSpan) { + console.error("Core verb complex span not found"); + break; + } + sentenceStructureElements.push({ + kind: "core-sentence-element", + sentenceElementName: "V", + span: coreVerbComplexSpan, + }); + break; + } + + switch (dependencyWord.upos) { + case "ADJ": { + const coreAdjectivalSpan = getCoreAdjectivalSpan( + sentence, + dependencyWord, + ); + if (!coreAdjectivalSpan) { + console.error("Core adjectival span not found"); + break; + } + sentenceStructureElements.push({ + kind: "core-sentence-element", + sentenceElementName: "C", + span: coreAdjectivalSpan, + }); + break; + } + case "NOUN": + case "PROPN": + case "PRON": + case "NUM": + case "SYM": + case "INTJ": + case "X": { + const coreNominalSpan = getCoreNominalSpan( + sentence, + dependencyWord, + ); + if (!coreNominalSpan) { + console.error("Core nominal span not found"); + break; + } + sentenceStructureElements.push({ + kind: "core-sentence-element", + sentenceElementName: "C", + span: coreNominalSpan, + }); + break; + } + case "ADP": + case "ADV": + case "AUX": + case "CCONJ": + case "DET": + case "PART": + case "PUNCT": + case "SCONJ": + case "VERB": { + console.error("Unexpected xcomp dependent UPOS"); + break; + } + default: { + dependencyWord.upos satisfies never; + throw new Error("Unreachable"); + } + } + break; + } + case "advmod": + case "obl": + case "obl:agent": + case "nmod": + case "compound:prt": { + const adverbialPhraseSpan = getAdverbialPhraseSpan( + sentence, + dependencyWord, + ); + if (!adverbialPhraseSpan) { + console.error("Adverbial phrase span not found"); + break; + } + sentenceStructureElements.push({ + kind: "sentence-constituent", + type: "adverbial-phrase", + sentenceElementName: "M", + span: adverbialPhraseSpan, + }); + break; + } + case "obl:tmod": + case "obl:npmod": + case "obl:unmarked": { + const coreNominalSpan = getCoreNominalSpan(sentence, dependencyWord); + if (!coreNominalSpan) { + console.error("Core nominal span not found"); + break; + } + sentenceStructureElements.push({ + kind: "sentence-constituent", + type: "adverbial-phrase", + sentenceElementName: "M", + span: coreNominalSpan, + }); + break; + } + case "expl": { + if (dependencyWord.text.toLowerCase() === "there") { + sentenceStructureElements.push({ + kind: "sentence-constituent", + type: "adverbial-phrase", + sentenceElementName: "M", + span: { + startWordIndex: dependencyWords.findIndex( + (candidateDependencyWord) => + candidateDependencyWord.id === dependencyWord.id, + ), + endWordIndex: dependencyWords.findIndex( + (candidateDependencyWord) => + candidateDependencyWord.id === dependencyWord.id, + ), + }, + }); + } else if (dependencyWord.text.toLowerCase() === "it") { + const dependencyHeadWord = getResolvedDependencyHeadWord( + sentence, + dependencyWord, + ); + if (!dependencyHeadWord) { + throw new Error("Dependency head word not found"); + } + + const hasOtherNominalSubject = dependencyWords.some( + (candidateDependencyWord) => + candidateDependencyWord.id !== dependencyWord.id && + candidateDependencyWord.head === dependencyHeadWord.id && + (candidateDependencyWord.deprel === "nsubj" || + candidateDependencyWord.deprel === "nsubj:pass" || + candidateDependencyWord.deprel === "nsubj:outer"), + ); + sentenceStructureElements.push({ + kind: "core-sentence-element", + sentenceElementName: hasOtherNominalSubject ? "O" : "S", + span: { + startWordIndex: dependencyWords.findIndex( + (candidateDependencyWord) => + candidateDependencyWord.id === dependencyWord.id, + ), + endWordIndex: dependencyWords.findIndex( + (candidateDependencyWord) => + candidateDependencyWord.id === dependencyWord.id, + ), + }, + }); + } else { + console.error("Unexpected expletive word"); + } + break; + } + case "acl": + case "acl:relcl": { + if (!predicateConstituentForDependencyWord) { + console.error("Predicate constituent type not found"); + break; + } + + sentenceStructureElements.push({ + kind: "sentence-constituent", + type: predicateConstituentForDependencyWord.type, + usage: "adjectival", + sentenceElementName: "M", + span: predicateConstituentForDependencyWord.span, + }); + + const coreVerbComplexSpan = getCoreVerbComplexSpan( + sentence, + predicateConstituentForDependencyWord.predicateCarrierWord, + ); + if (!coreVerbComplexSpan) { + console.error("Core verb complex span not found"); + break; + } + sentenceStructureElements.push({ + kind: "core-sentence-element", + sentenceElementName: "V", + span: coreVerbComplexSpan, + }); + break; + } + case "advcl": + case "advcl:relcl": { + if (!predicateConstituentForDependencyWord) { + console.error("Predicate constituent type not found"); + break; + } + + sentenceStructureElements.push({ + kind: "sentence-constituent", + type: predicateConstituentForDependencyWord.type, + usage: "adverbial", + sentenceElementName: "M", + span: predicateConstituentForDependencyWord.span, + }); + + const coreVerbComplexSpan = getCoreVerbComplexSpan( + sentence, + predicateConstituentForDependencyWord.predicateCarrierWord, + ); + if (!coreVerbComplexSpan) { + console.error("Core verb complex span not found"); + break; + } + sentenceStructureElements.push({ + kind: "core-sentence-element", + sentenceElementName: "V", + span: coreVerbComplexSpan, + }); + break; + } + case "root": + case "amod": + case "appos": + case "aux": + case "aux:pass": + case "case": + case "cc": + case "cc:preconj": + case "compound": + case "dep": + case "det": + case "det:predet": + case "discourse": + case "dislocated": + case "fixed": + case "flat": + case "goeswith": + case "list": + case "mark": + case "nmod:desc": + case "nmod:poss": + case "nmod:unmarked": + case "nummod": + case "orphan": + case "parataxis": + case "punct": + case "reparandum": + case "vocative": { + break; + } + default: { + resolvedDependencyLabel satisfies never; + throw new Error("Unreachable"); + } + } + } + + return sentenceStructureElements; +} + +function getCoordinations(sentence: StanzaSentence): { + type: CoordinationPartType; + span: { startWordIndex: number; endWordIndex: number }; +}[][] { + const dependencyWords = sentence.tokens.flatMap((token) => token.words); + + function getCoordinationParts( + sentence: StanzaSentence, + constituencyHeadNode: StanzaParseTree, + ): ReturnType[number] { + const childNodeSpans = constituencyHeadNode.children.map((childNode) => + getConstituencyNodeSpan(sentence, childNode), + ); + + function isCoordinationMarker(constituencyNode: StanzaParseTree): boolean { + return ( + constituencyNode.label === "CC" || constituencyNode.label === "CONJP" + ); + } + + function isConjunct(constituencyNode: StanzaParseTree): boolean { + const constituencyNodeSpan = childNodeSpans.find( + ({ startWordIndex, endWordIndex }) => + startWordIndex === + getConstituencyNodeSpan(sentence, constituencyNode) + .startWordIndex && + endWordIndex === + getConstituencyNodeSpan(sentence, constituencyNode).endWordIndex, + ); + if (!constituencyNodeSpan) { + throw new Error("Constituency node span not found"); + } + + return dependencyWords.some((dependentWord, dependentWordIndex) => { + if (dependentWord.deprel !== "conj") { + return false; + } + + const dependencyHeadWordIndex = dependencyWords.findIndex( + (candidateDependencyWord) => + candidateDependencyWord.id === dependentWord.head, + ); + if (dependencyHeadWordIndex === -1) { + throw new Error("Dependency head word not found"); + } + + const dependentWordChildNodeSpan = childNodeSpans.find( + ({ startWordIndex, endWordIndex }) => + startWordIndex <= dependentWordIndex && + dependentWordIndex <= endWordIndex, + ); + const dependencyHeadWordChildNodeSpan = childNodeSpans.find( + ({ startWordIndex, endWordIndex }) => + startWordIndex <= dependencyHeadWordIndex && + dependencyHeadWordIndex <= endWordIndex, + ); + if (!dependentWordChildNodeSpan || !dependencyHeadWordChildNodeSpan) { + return false; + } + + return ( + (dependentWordChildNodeSpan === constituencyNodeSpan || + dependencyHeadWordChildNodeSpan === constituencyNodeSpan) && + dependentWordChildNodeSpan !== dependencyHeadWordChildNodeSpan + ); + }); + } + + const isCorrelativeCoordination = (() => { + return dependencyWords.some((dependentWord, dependentWordIndex) => { + if (dependentWord.deprel !== "cc:preconj") { + return false; + } + + const dependencyHeadWordIndex = dependencyWords.findIndex( + (candidateDependencyWord) => + candidateDependencyWord.id === dependentWord.head, + ); + if (dependencyHeadWordIndex === -1) { + throw new Error("Dependency head word not found"); + } + + const dependentWordChildNodeSpan = childNodeSpans.find( + ({ startWordIndex, endWordIndex }) => + startWordIndex <= dependentWordIndex && + dependentWordIndex <= endWordIndex, + ); + const dependencyHeadWordChildNodeSpan = childNodeSpans.find( + ({ startWordIndex, endWordIndex }) => + startWordIndex <= dependencyHeadWordIndex && + dependencyHeadWordIndex <= endWordIndex, + ); + if (!dependentWordChildNodeSpan || !dependencyHeadWordChildNodeSpan) { + return false; + } + + return dependentWordChildNodeSpan !== dependencyHeadWordChildNodeSpan; + }); + })(); + + const firstCoordinationNodeIndex = constituencyHeadNode.children.findIndex( + (childNode) => isCoordinationMarker(childNode) || isConjunct(childNode), + ); + if (firstCoordinationNodeIndex === -1) { + throw new Error("Coordination part not found"); + } + const coordinationPartSpans: ReturnType = []; + for (const [childNodeIndex, childNode] of constituencyHeadNode.children + .slice(firstCoordinationNodeIndex) + .entries()) { + const childNodeSpan = childNodeSpans.at( + childNodeIndex + firstCoordinationNodeIndex, + ); + if (!childNodeSpan) { + throw new Error("Child node span not found"); + } + if (isCoordinationMarker(childNode) || isConjunct(childNode)) { + coordinationPartSpans.push({ + type: isCoordinationMarker(childNode) + ? isCorrelativeCoordination + ? "correlative" + : "coordinator" + : "conjunct", + span: { + startWordIndex: childNodeSpan.startWordIndex, + endWordIndex: childNodeSpan.endWordIndex, + }, + }); + } else if ( + coordinationPartSpans.at(-1)!.type === "correlative" || + coordinationPartSpans.at(-1)!.type === "coordinator" || + childNode.label === "," || + childNode.label === "." + ) { + coordinationPartSpans.at(-1)!.span.endWordIndex = + childNodeSpan.endWordIndex; + } else { + break; + } + } + + return coordinationPartSpans; + } + + function collectCoordinations( + sentence: StanzaSentence, + constituencyNode: StanzaParseTree, + ): ReturnType { + const childCoordinations = constituencyNode.children.flatMap((childNode) => + collectCoordinations(sentence, childNode), + ); + + if (isCoordinationNode(sentence, constituencyNode)) { + return [ + ...childCoordinations, + getCoordinationParts(sentence, constituencyNode), + ]; + } else { + return childCoordinations; + } + } + + return collectCoordinations(sentence, sentence.constituency); +} + +function getModifications(sentence: StanzaSentence): { + modifierWordIndex: number; + modifiedWordIndex: number; +}[] { + const dependencyWords = sentence.tokens.flatMap((token) => token.words); + const coordinations = getCoordinations(sentence); + + function getSharedModificationCoordination( + modifierWordIndex: number, + modifiedWordIndex: number, + ): ReturnType[number] | null { + return ( + coordinations + .filter((coordination) => + coordination.some( + (coordinationPart) => + coordinationPart.type === "conjunct" && + coordinationPart.span.startWordIndex <= modifiedWordIndex && + modifiedWordIndex <= coordinationPart.span.endWordIndex, + ), + ) + .filter( + (coordination) => + modifierWordIndex < coordination.at(0)!.span.startWordIndex || + coordination.at(-1)!.span.endWordIndex < modifierWordIndex, + ) + .toSorted( + (a, b) => + b.at(-1)!.span.endWordIndex - + b.at(0)!.span.startWordIndex - + (a.at(-1)!.span.endWordIndex - a.at(0)!.span.startWordIndex), + ) + .at(0) ?? null + ); + } + + return dependencyWords.flatMap((modifierWord) => { + const resolvedDependencyLabel = getResolvedDependencyLabel( + sentence, + modifierWord, + ); + if ( + !( + resolvedDependencyLabel === "acl" || + resolvedDependencyLabel === "acl:relcl" + ) + ) { + return []; + } + + const modifierWordIndex = dependencyWords.findIndex( + (dependencyWord) => dependencyWord.id === modifierWord.id, + ); + if (modifierWordIndex === -1) { + throw new Error("Modifier word not found"); + } + + const modifiedWord = getResolvedDependencyHeadWord(sentence, modifierWord); + if (!modifiedWord) { + throw new Error("Modified word not found"); + } + const modifiedWordIndex = dependencyWords.findIndex( + (dependencyWord) => dependencyWord.id === modifiedWord.id, + ); + if (modifiedWordIndex === -1) { + throw new Error("Modified word not found"); + } + + const sharedModificationCoordination = getSharedModificationCoordination( + modifierWordIndex, + modifiedWordIndex, + ); + if (!sharedModificationCoordination) { + return [{ modifierWordIndex, modifiedWordIndex }]; + } + + return sharedModificationCoordination + .filter((coordinationPart) => coordinationPart.type === "conjunct") + .flatMap((conjunct) => { + const modifiedWord = getDependencySpanConnectorWord( + sentence, + conjunct.span, + ); + if (!modifiedWord) { + console.error("Modified word not found"); + return []; + } + const modifiedWordIndex = dependencyWords.findIndex( + (dependencyWord) => dependencyWord.id === modifiedWord.id, + ); + if (modifiedWordIndex === -1) { + throw new Error("Modified word not found"); + } + + return [{ modifierWordIndex, modifiedWordIndex }]; + }); + }); +} + +export function createSentenceStructureDocumentFromStanza( + stanzaDocument: StanzaDocument, +): SentenceStructureDocument { + const sentenceStructureDocumentFromWords = + createSentenceStructureDocumentFromWords( + stanzaDocument.sentences.map((sentence) => ({ + words: sentence.tokens.flatMap((token) => + token.words.map((word, index) => ({ + text: word.text, + whitespaceAfter: + index === token.words.length - 1 ? token.spaces_after : "", + })), + ), + })), + ); + + try { + for (const sentence of stanzaDocument.sentences) { + const dependencyWords = sentence.tokens.flatMap((token) => token.words); + const constituencyLeafNodes = getConstituencyLeafNodes( + sentence.constituency, + ); + if ( + dependencyWords.length !== constituencyLeafNodes.length || + !dependencyWords.every( + (dependencyWord, index) => + dependencyWord.text === constituencyLeafNodes.at(index)?.label, + ) + ) { + throw new Error( + "Dependency words and constituency leaf nodes do not match", + ); + } + } + } catch (error) { + console.error(error); + return sentenceStructureDocumentFromWords; + } + + const sentenceStructureDocumentWithSentenceStructureElements = + stanzaDocument.sentences.reduce( + (sentenceStructureDocument, stanzaSentence, sentenceIndex) => { + const sentenceId = + sentenceStructureDocument.sentences.at(sentenceIndex)!.id; + const sentenceStructureElements = + getSentenceStructureElements(stanzaSentence); + + return sentenceStructureElements.reduce( + (sentenceStructureDocument, sentenceStructureElement) => { + const startWordId = sentenceStructureDocument.sentences + .at(sentenceIndex)! + .words.at(sentenceStructureElement.span.startWordIndex)!.id; + const endWordId = sentenceStructureDocument.sentences + .at(sentenceIndex)! + .words.at(sentenceStructureElement.span.endWordIndex)!.id; + + switch (sentenceStructureElement.kind) { + case "core-sentence-element": { + const result = addSentenceStructureElement( + sentenceStructureDocument, + { + sentenceId, + kind: sentenceStructureElement.kind, + sentenceElementName: + sentenceStructureElement.sentenceElementName, + startWordId, + endWordId, + }, + ); + if (!result.success) { + console.error(result.message); + return sentenceStructureDocument; + } + return result.data.newSentenceStructureDocument; + } + case "sentence-constituent": { + switch (sentenceStructureElement.type) { + case "phrase": + case "clause": { + const result = addSentenceStructureElement( + sentenceStructureDocument, + { + sentenceId, + kind: sentenceStructureElement.kind, + type: sentenceStructureElement.type, + usage: sentenceStructureElement.usage, + sentenceElementName: + sentenceStructureElement.sentenceElementName, + startWordId, + endWordId, + }, + ); + if (!result.success) { + console.error(result.message); + return sentenceStructureDocument; + } + return result.data.newSentenceStructureDocument; + } + case "adverbial-phrase": { + const result = addSentenceStructureElement( + sentenceStructureDocument, + { + sentenceId, + kind: sentenceStructureElement.kind, + type: sentenceStructureElement.type, + sentenceElementName: + sentenceStructureElement.sentenceElementName, + startWordId, + endWordId, + }, + ); + if (!result.success) { + console.error(result.message); + return sentenceStructureDocument; + } + return result.data.newSentenceStructureDocument; + } + default: + sentenceStructureElement satisfies never; + throw new Error("Unreachable"); + } + } + default: + sentenceStructureElement satisfies never; + throw new Error("Unreachable"); + } + }, + sentenceStructureDocument, + ); + }, + sentenceStructureDocumentFromWords, + ); + + const sentenceStructureDocumentWithCoordinations = + stanzaDocument.sentences.reduce( + (sentenceStructureDocument, stanzaSentence, sentenceIndex) => { + return getCoordinations(stanzaSentence).reduce( + (sentenceStructureDocument, coordination) => { + const result = addCoordination(sentenceStructureDocument, { + sentenceId: + sentenceStructureDocument.sentences.at(sentenceIndex)!.id, + coordinationParts: coordination.map((coordinationPart) => ({ + type: coordinationPart.type, + startWordId: sentenceStructureDocument.sentences + .at(sentenceIndex)! + .words.at(coordinationPart.span.startWordIndex)!.id, + endWordId: sentenceStructureDocument.sentences + .at(sentenceIndex)! + .words.at(coordinationPart.span.endWordIndex)!.id, + })), + }); + if (!result.success) { + console.error(result.message); + return sentenceStructureDocument; + } + return result.data.newSentenceStructureDocument; + }, + sentenceStructureDocument, + ); + }, + sentenceStructureDocumentWithSentenceStructureElements, + ); + + const sentenceStructureDocumentWithModifications = + stanzaDocument.sentences.reduce( + (sentenceStructureDocument, stanzaSentence, sentenceIndex) => { + const sentenceId = + sentenceStructureDocument.sentences.at(sentenceIndex)!.id; + const dependencyWords = stanzaSentence.tokens.flatMap( + (token) => token.words, + ); + + const modifications = getModifications(stanzaSentence); + + return modifications.reduce( + (sentenceStructureDocument, modification) => { + function getMaximalSentenceStructureElementSpan( + includedWordIndex: number, + excludedWordIndex: number, + ): { + startWordId: string; + endWordId: string; + } | null { + const sentence = + sentenceStructureDocument.sentences.at(sentenceIndex)!; + + const sentenceStructureElement = + sentence.sentenceStructureElements.find( + (sentenceStructureElement) => { + const startWordIndex = sentence.words.findIndex( + (word) => + word.id === sentenceStructureElement.startWordId, + ); + const endWordIndex = sentence.words.findIndex( + (word) => word.id === sentenceStructureElement.endWordId, + ); + if (startWordIndex === -1 || endWordIndex === -1) { + throw new Error( + "Sentence structure element word not found", + ); + } + + return ( + startWordIndex <= includedWordIndex && + includedWordIndex <= endWordIndex && + (excludedWordIndex < startWordIndex || + endWordIndex < excludedWordIndex) + ); + }, + ); + if (!sentenceStructureElement) { + return null; + } + + return { + startWordId: sentenceStructureElement.startWordId, + endWordId: sentenceStructureElement.endWordId, + }; + } + + const modifierSentenceStructureElement = + getMaximalSentenceStructureElementSpan( + modification.modifierWordIndex, + modification.modifiedWordIndex, + ); + if (!modifierSentenceStructureElement) { + console.error("Modifier sentence structure element not found"); + return sentenceStructureDocument; + } + + const modifiedWord = dependencyWords.at( + modification.modifiedWordIndex, + ); + if (!modifiedWord) { + throw new Error("Modified word not found"); + } + const coreNominalSpan = getCoreNominalSpan( + stanzaSentence, + modifiedWord, + ) ?? { + startWordIndex: modification.modifiedWordIndex, + endWordIndex: modification.modifiedWordIndex, + }; + + const modifiedSentenceStructureElement = + getMaximalSentenceStructureElementSpan( + modification.modifiedWordIndex, + modification.modifierWordIndex, + ) ?? { + startWordId: sentenceStructureDocument.sentences + .at(sentenceIndex)! + .words.at(coreNominalSpan.startWordIndex)!.id, + endWordId: sentenceStructureDocument.sentences + .at(sentenceIndex)! + .words.at(coreNominalSpan.endWordIndex)!.id, + }; + + const result = addModification(sentenceStructureDocument, { + sentenceId, + modifierSentenceStructureElement, + modifiedSentenceStructureElement, + }); + if (!result.success) { + console.error(result.message); + return sentenceStructureDocument; + } + return result.data.newSentenceStructureDocument; + }, + sentenceStructureDocument, + ); + }, + sentenceStructureDocumentWithCoordinations, + ); + + return sentenceStructureDocumentWithModifications; +} diff --git a/packages/sentence-structure-data-from-stanza/index.ts b/packages/sentence-structure-data-from-stanza/index.ts new file mode 100644 index 0000000..c6b4652 --- /dev/null +++ b/packages/sentence-structure-data-from-stanza/index.ts @@ -0,0 +1 @@ +export { createSentenceStructureDocumentFromStanza } from "./create.js"; diff --git a/packages/sentence-structure-tree/package.json b/packages/sentence-structure-data-from-stanza/package.json similarity index 74% rename from packages/sentence-structure-tree/package.json rename to packages/sentence-structure-data-from-stanza/package.json index ab018cf..28febcb 100644 --- a/packages/sentence-structure-tree/package.json +++ b/packages/sentence-structure-data-from-stanza/package.json @@ -1,5 +1,5 @@ { - "name": "@sentence-structure-diagram-app/sentence-structure-tree", + "name": "@sentence-structure-diagram-app/sentence-structure-data-from-stanza", "version": "0.1.0", "type": "module", "main": "./dist/index.js", @@ -9,7 +9,8 @@ "clean": "rm -r dist" }, "dependencies": { - "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0" + "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0", + "zod": "^4.3.5" }, "devDependencies": { "typescript": "^5.9.3" diff --git a/packages/sentence-structure-diagram-configurations/tsconfig.json b/packages/sentence-structure-data-from-stanza/tsconfig.json similarity index 100% rename from packages/sentence-structure-diagram-configurations/tsconfig.json rename to packages/sentence-structure-data-from-stanza/tsconfig.json diff --git a/packages/sentence-structure-data-from-stanza/types.ts b/packages/sentence-structure-data-from-stanza/types.ts new file mode 100644 index 0000000..4ea769e --- /dev/null +++ b/packages/sentence-structure-data-from-stanza/types.ts @@ -0,0 +1,130 @@ +export type StanzaDependencyLabel = + | "acl" + | "acl:relcl" + | "advcl" + | "advcl:relcl" + | "advmod" + | "amod" + | "appos" + | "aux" + | "aux:pass" + | "case" + | "cc" + | "cc:preconj" + | "ccomp" + | "compound" + | "compound:prt" + | "conj" + | "cop" + | "csubj" + | "csubj:outer" + | "csubj:pass" + | "dep" + | "det" + | "det:predet" + | "discourse" + | "dislocated" + | "expl" + | "fixed" + | "flat" + | "goeswith" + | "iobj" + | "list" + | "mark" + | "nmod" + | "nmod:desc" + | "nmod:poss" + | "nmod:unmarked" + | "nsubj" + | "nsubj:outer" + | "nsubj:pass" + | "nummod" + | "obj" + | "obl" + | "obl:agent" + | "obl:npmod" + | "obl:tmod" + | "obl:unmarked" + | "orphan" + | "parataxis" + | "punct" + | "reparandum" + | "root" + | "vocative" + | "xcomp"; + +export type StanzaWord = { + id: number; + text: string; + lemma: string; + upos: + | "ADJ" + | "ADP" + | "ADV" + | "AUX" + | "CCONJ" + | "DET" + | "INTJ" + | "NOUN" + | "NUM" + | "PART" + | "PRON" + | "PROPN" + | "PUNCT" + | "SCONJ" + | "SYM" + | "VERB" + | "X"; + xpos: string; + feats: string | null; + head: number; + deprel: StanzaDependencyLabel; +}; + +type StanzaToken = { + text: string; + words: StanzaWord[]; + spaces_after: string; + spaces_before: string; +}; + +export type StanzaParseTree = { + label: + | string + | "ADJP" + | "ADVP" + | "CONJP" + | "FRAG" + | "INTJ" + | "LST" + | "NAC" + | "NML" + | "NP" + | "PP" + | "PRN" + | "PRT" + | "QP" + | "ROOT" + | "RRC" + | "S" + | "SBAR" + | "SBARQ" + | "SINV" + | "SQ" + | "UCP" + | "VP" + | "WHADJP" + | "WHADVP" + | "WHNP" + | "WHPP" + | "X"; + children: StanzaParseTree[]; +}; + +export type StanzaSentence = { + text: string; + tokens: StanzaToken[]; + constituency: StanzaParseTree; +}; + +export type StanzaDocument = { text: string; sentences: StanzaSentence[] }; diff --git a/packages/sentence-structure-diagram-data/.gitignore b/packages/sentence-structure-data-parser-by-spacy/.gitignore similarity index 100% rename from packages/sentence-structure-diagram-data/.gitignore rename to packages/sentence-structure-data-parser-by-spacy/.gitignore diff --git a/packages/sentence-structure-data-parser-by-spacy/create-parser.ts b/packages/sentence-structure-data-parser-by-spacy/create-parser.ts new file mode 100644 index 0000000..f9b7a3c --- /dev/null +++ b/packages/sentence-structure-data-parser-by-spacy/create-parser.ts @@ -0,0 +1,21 @@ +import type { SentenceStructureData } from "@sentence-structure-diagram-app/sentence-structure-data"; +import { createSpacyParser } from "./spacy-parser.js"; +import { spacyParseResultToSentenceStructureData } from "./spacy-parse-result-to-sentence-structure-data.js"; + +type SentenceStructureDataParser = { + parse: (text: string) => SentenceStructureData; +}; + +export async function createSentenceStructureDataParser( + resolveWheelURL: (wheelFileName: string) => URL, + options?: { + indexURL?: string; + }, +): Promise { + const dependencyParser = await createSpacyParser(resolveWheelURL, options); + + return { + parse: (text: string) => + spacyParseResultToSentenceStructureData(dependencyParser.parse(text)), + }; +} diff --git a/packages/sentence-structure-data-parser-by-spacy/index.browser.ts b/packages/sentence-structure-data-parser-by-spacy/index.browser.ts new file mode 100644 index 0000000..4ef42b0 --- /dev/null +++ b/packages/sentence-structure-data-parser-by-spacy/index.browser.ts @@ -0,0 +1,10 @@ +import { createSentenceStructureDataParser as _createSentenceStructureDataParser } from "./create-parser.js"; + +export async function createSentenceStructureDataParser(indexURL: string) { + return _createSentenceStructureDataParser( + (wheelFileName: string) => new URL(wheelFileName, indexURL), + { + indexURL, + }, + ); +} diff --git a/packages/sentence-structure-data-parser-by-spacy/index.node.ts b/packages/sentence-structure-data-parser-by-spacy/index.node.ts new file mode 100644 index 0000000..cac176b --- /dev/null +++ b/packages/sentence-structure-data-parser-by-spacy/index.node.ts @@ -0,0 +1,8 @@ +import { createSentenceStructureDataParser as _createSentenceStructureDataParser } from "./create-parser.js"; + +export async function createSentenceStructureDataParser() { + return _createSentenceStructureDataParser( + (wheelFileName: string) => + new URL(wheelFileName, new URL("./wheels/", import.meta.url)), + ); +} diff --git a/packages/sentence-structure-data-parser-by-spacy/package.json b/packages/sentence-structure-data-parser-by-spacy/package.json new file mode 100644 index 0000000..27e53ba --- /dev/null +++ b/packages/sentence-structure-data-parser-by-spacy/package.json @@ -0,0 +1,26 @@ +{ + "name": "@sentence-structure-diagram-app/sentence-structure-data-parser", + "version": "0.1.0", + "type": "module", + "exports": { + "./browser": { + "default": "./dist/index.browser.js" + }, + "./node": { + "default": "./dist/index.node.js" + } + }, + "scripts": { + "build": "tsc && cp -r wheels dist", + "build:watch": "tsc --watch", + "clean": "rm -r dist" + }, + "dependencies": { + "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0", + "pyodide": "^0.29.3", + "zod": "^4.3.5" + }, + "devDependencies": { + "typescript": "^5.9.3" + } +} diff --git a/packages/sentence-structure-data-parser-by-spacy/spacy-parse-result-to-sentence-structure-data.ts b/packages/sentence-structure-data-parser-by-spacy/spacy-parse-result-to-sentence-structure-data.ts new file mode 100644 index 0000000..e3651ff --- /dev/null +++ b/packages/sentence-structure-data-parser-by-spacy/spacy-parse-result-to-sentence-structure-data.ts @@ -0,0 +1,341 @@ +import { + createSentenceStructureDataFromWords, + createSentenceStructureElement, + type SentenceStructureData, +} from "@sentence-structure-diagram-app/sentence-structure-data"; +import type { SpacyParseResult, Token } from "./spacy-parser.js"; + +function isFiniteVerb(token: Token): boolean { + return token.verbForm === "Fin"; +} + +function isNonFiniteVerb(token: Token): boolean { + return !!(token.verbForm && ["Inf", "Part", "Ger"].includes(token.verbForm)); +} + +function getChildren( + token: Token, + spacyParseResult: SpacyParseResult, +): Token[] { + return spacyParseResult.tokens.filter( + (token) => token.headTokenIndex === token.index, + ); +} + +export function spacyParseResultToSentenceStructureData( + spacyParseResult: SpacyParseResult, +): SentenceStructureData { + return [ + (sentenceStructureData: SentenceStructureData) => + spacyParseResult.tokens.reduce((sentenceStructureData, token) => { + if ( + spacyParseResult.nounChunks.some( + (nounChunk) => + nounChunk.startIndex <= token.index && + token.index < nounChunk.endIndex && + nounChunk.startIndex <= token.headTokenIndex && + token.headTokenIndex < nounChunk.endIndex, + ) + ) { + return sentenceStructureData; + } + + if (token.pos === "VERB") { + const verbComplexTokens = spacyParseResult.tokens.filter( + (candidateToken) => + candidateToken.index === token.index || + (candidateToken.headTokenIndex === token.index && + ["aux", "auxpass", "neg"].includes( + candidateToken.dependencyLabel, + )), + ); + const result = createSentenceStructureElement(sentenceStructureData, { + kind: "core-sentence-element", + startWordIndex: Math.min( + ...verbComplexTokens.map( + (verbComplexToken) => verbComplexToken.index, + ), + ), + endWordIndex: Math.max( + ...verbComplexTokens.map( + (verbComplexToken) => verbComplexToken.index, + ), + ), + sentenceElementName: "V", + }); + if (!result.success) + throw new Error( + "Failed to create sentence structure data from dependency parse result.", + ); + return result.data.newSentenceStructureData; + } + + switch (token.dependencyLabel) { + case "csubj": + case "csubjpass": + case "nsubj": + case "nsubjpass": { + if (isNonFiniteVerb(token)) { + const result = createSentenceStructureElement( + sentenceStructureData, + { + kind: "sentence-constituent", + type: "phrase", + usage: "nominal", + startWordIndex: token.subtreeIndices.at(0)!, + endWordIndex: token.subtreeIndices.at(-1)!, + sentenceElementName: "S", + }, + ); + if (!result.success) + throw new Error( + "Failed to create sentence structure data from dependency parse result.", + ); + return result.data.newSentenceStructureData; + } + if (isFiniteVerb(token)) { + const result = createSentenceStructureElement( + sentenceStructureData, + { + kind: "sentence-constituent", + type: "clause", + usage: "nominal", + startWordIndex: token.subtreeIndices.at(0)!, + endWordIndex: token.subtreeIndices.at(-1)!, + sentenceElementName: "S", + }, + ); + if (!result.success) + throw new Error( + "Failed to create sentence structure data from dependency parse result.", + ); + return result.data.newSentenceStructureData; + } + const result = createSentenceStructureElement( + sentenceStructureData, + { + kind: "core-sentence-element", + startWordIndex: token.subtreeIndices.at(0)!, + endWordIndex: token.subtreeIndices.at(-1)!, + sentenceElementName: "S", + }, + ); + if (!result.success) + throw new Error( + "Failed to create sentence structure data from dependency parse result.", + ); + return result.data.newSentenceStructureData; + } + case "ccomp": + case "dative": + case "dobj": + // TODO: fix later + case "xcomp": { + if (isNonFiniteVerb(token)) { + const result = createSentenceStructureElement( + sentenceStructureData, + { + kind: "sentence-constituent", + type: "phrase", + usage: "nominal", + startWordIndex: token.subtreeIndices.at(0)!, + endWordIndex: token.subtreeIndices.at(-1)!, + sentenceElementName: "O", + }, + ); + if (!result.success) + throw new Error( + "Failed to create sentence structure data from dependency parse result.", + ); + return result.data.newSentenceStructureData; + } + if (isFiniteVerb(token)) { + const result = createSentenceStructureElement( + sentenceStructureData, + { + kind: "sentence-constituent", + type: "clause", + usage: "nominal", + startWordIndex: token.subtreeIndices.at(0)!, + endWordIndex: token.subtreeIndices.at(-1)!, + sentenceElementName: "O", + }, + ); + if (!result.success) + throw new Error( + "Failed to create sentence structure data from dependency parse result.", + ); + return result.data.newSentenceStructureData; + } + const result = createSentenceStructureElement( + sentenceStructureData, + { + kind: "core-sentence-element", + startWordIndex: token.subtreeIndices.at(0)!, + endWordIndex: token.subtreeIndices.at(-1)!, + sentenceElementName: "O", + }, + ); + if (!result.success) + throw new Error( + "Failed to create sentence structure data from dependency parse result.", + ); + return result.data.newSentenceStructureData; + } + case "acomp": + case "attr": + case "oprd": { + if (isNonFiniteVerb(token)) { + const result = createSentenceStructureElement( + sentenceStructureData, + { + kind: "sentence-constituent", + type: "phrase", + usage: "nominal", + startWordIndex: token.subtreeIndices.at(0)!, + endWordIndex: token.subtreeIndices.at(-1)!, + sentenceElementName: "C", + }, + ); + if (!result.success) + throw new Error( + "Failed to create sentence structure data from dependency parse result.", + ); + return result.data.newSentenceStructureData; + } + if (isFiniteVerb(token)) { + const result = createSentenceStructureElement( + sentenceStructureData, + { + kind: "sentence-constituent", + type: "clause", + usage: "nominal", + startWordIndex: token.subtreeIndices.at(0)!, + endWordIndex: token.subtreeIndices.at(-1)!, + sentenceElementName: "C", + }, + ); + if (!result.success) + throw new Error( + "Failed to create sentence structure data from dependency parse result.", + ); + return result.data.newSentenceStructureData; + } + const result = createSentenceStructureElement( + sentenceStructureData, + { + kind: "core-sentence-element", + startWordIndex: token.subtreeIndices.at(0)!, + endWordIndex: token.subtreeIndices.at(-1)!, + sentenceElementName: "C", + }, + ); + if (!result.success) + throw new Error( + "Failed to create sentence structure data from dependency parse result.", + ); + return result.data.newSentenceStructureData; + } + case "acl": + case "advcl": + case "relcl": { + if (isNonFiniteVerb(token)) { + const result = createSentenceStructureElement( + sentenceStructureData, + { + kind: "sentence-constituent", + type: "phrase", + usage: "adverbial", + startWordIndex: token.subtreeIndices.at(0)!, + endWordIndex: token.subtreeIndices.at(-1)!, + sentenceElementName: "M", + }, + ); + if (!result.success) + throw new Error( + "Failed to create sentence structure data from dependency parse result.", + ); + return result.data.newSentenceStructureData; + } + if (isFiniteVerb(token)) { + const result = createSentenceStructureElement( + sentenceStructureData, + { + kind: "sentence-constituent", + type: "clause", + usage: "adverbial", + startWordIndex: token.subtreeIndices.at(0)!, + endWordIndex: token.subtreeIndices.at(-1)!, + sentenceElementName: "M", + }, + ); + if (!result.success) + throw new Error( + "Failed to create sentence structure data from dependency parse result.", + ); + return result.data.newSentenceStructureData; + } + throw new Error( + "Failed to create sentence structure data from dependency parse result.", + ); + } + case "advmod": // TODO: fix later + case "agent": + case "expl": + case "npadvmod": + case "prep": + case "prt": { + const result = createSentenceStructureElement( + sentenceStructureData, + { + kind: "sentence-constituent", + type: "adverbial-phrase", + startWordIndex: token.subtreeIndices.at(0)!, + endWordIndex: token.subtreeIndices.at(-1)!, + sentenceElementName: "M", + }, + ); + if (!result.success) + throw new Error( + "Failed to create sentence structure data from dependency parse result.", + ); + return result.data.newSentenceStructureData; + } + case "ROOT": + case "amod": + case "appos": // TODO: fix later + case "aux": + case "auxpass": + case "case": + case "cc": // TODO: fix later + case "compound": + case "conj": // TODO: fix later + case "dep": + case "det": + case "intj": + case "mark": + case "meta": + case "neg": + case "nmod": + case "nummod": + case "parataxis": // TODO: fix later + case "pcomp": + case "pobj": + case "poss": + case "preconj": // TODO: fix later + case "predet": + case "punct": + case "quantmod": + return sentenceStructureData; + default: + token.dependencyLabel satisfies never; + throw new Error("Unreachable"); + } + }, sentenceStructureData), + ].reduce( + (sentenceStructureData, f) => f(sentenceStructureData), + createSentenceStructureDataFromWords({ + words: spacyParseResult.tokens.map((token) => token.text), + }), + ); +} diff --git a/packages/sentence-structure-data-parser-by-spacy/spacy-parser.ts b/packages/sentence-structure-data-parser-by-spacy/spacy-parser.ts new file mode 100644 index 0000000..4a5978c --- /dev/null +++ b/packages/sentence-structure-data-parser-by-spacy/spacy-parser.ts @@ -0,0 +1,170 @@ +import * as z from "zod"; +import { loadPyodide } from "pyodide"; + +// See https://universaldependencies.org/u/pos/ +const posSchema = z.literal([ + "ADJ", // adjective + "ADP", // adposition + "ADV", // adverb + "AUX", // auxiliary + "CCONJ", // coordinating conjunction + "DET", // determiner + "INTJ", // interjection + "NOUN", // noun + "NUM", // numeral + "PART", // particle + "PRON", // pronoun + "PROPN", // proper noun + "PUNCT", // punctuation + "SCONJ", // subordinating conjunction + "SYM", // symbol + "VERB", // verb + "X", // other +]); + +// See https://spacy.io/models/en https://github.com/explosion/spaCy/blob/453732d32d55029ea9787ef737c8cf8d626f45b0/spacy/glossary.py#L206-L279 +const DependencyLabelSchema = z.literal([ + "ROOT", // root + "acl", // clausal modifier of noun (adjectival clause) + "acomp", // adjectival complement + "advcl", // adverbial clause modifier + "advmod", // adverbial modifier + "agent", // agent + "amod", // adjectival modifier + "appos", // appositional modifier + "attr", // attribute + "aux", // auxiliary + "auxpass", // auxiliary (passive) + "case", // case marking + "cc", // coordinating conjunction + "ccomp", // clausal complement + "compound", // compound + "conj", // conjunct + "csubj", // clausal subject + "csubjpass", // clausal subject (passive) + "dative", // dative + "dep", // unclassified dependent + "det", // determiner + "dobj", // direct object + "expl", // expletive + "intj", // interjection + "mark", // marker + "meta", // meta modifier + "neg", // negation modifier + "nmod", // modifier of nominal + "npadvmod", // noun phrase as adverbial modifier + "nsubj", // nominal subject + "nsubjpass", // nominal subject (passive) + "nummod", // numeric modifier + "oprd", // object predicate + "parataxis", // parataxis + "pcomp", // complement of preposition + "pobj", // object of preposition + "poss", // possession modifier + "preconj", // pre-correlative conjunction + "predet", // None + "prep", // prepositional modifier + "prt", // particle + "punct", // punctuation + "quantmod", // modifier of quantifier + "relcl", // relative clause modifier + "xcomp", // open clausal complement +]); + +// See https://universaldependencies.org/u/feat/VerbForm.html +const VerbFormSchema = z.literal([ + "Fin", // finite verb + "Inf", // infinitive + "Part", // participle, verbal adjective + "Ger", // gerund +]); + +const TokenSchema = z.object({ + index: z.int().nonnegative(), + text: z.string(), + pos: posSchema, + headTokenIndex: z.int().nonnegative(), + dependencyLabel: DependencyLabelSchema, + verbForm: z.nullable(VerbFormSchema), + subtreeIndices: z.array(z.int().nonnegative()).min(1), +}); +export type Token = z.infer; + +const SpacyParseResultSchema = z.object({ + tokens: z.array(TokenSchema), + nounChunks: z.array( + z.object({ + text: z.string(), + startIndex: z.int().nonnegative(), + endIndex: z.int().nonnegative(), + }), + ), +}); +export type SpacyParseResult = z.infer; + +type Parser = { + parse: (text: string) => SpacyParseResult; +}; + +export async function createSpacyParser( + resolveWheelURL: (wheelFileName: string) => URL, + options?: { + indexURL?: string; + }, +): Promise { + const pyodide = await loadPyodide(options); + await pyodide.loadPackage("micropip"); + const micropip = pyodide.pyimport("micropip"); + await micropip.install([ + resolveWheelURL("murmurhash-1.0.15-cp313-cp313-pyodide_2025_0_wasm32.whl") + .href, + resolveWheelURL("cymem-2.0.13-cp313-cp313-pyodide_2025_0_wasm32.whl").href, + resolveWheelURL("preshed-3.0.12-cp313-cp313-pyodide_2025_0_wasm32.whl") + .href, + resolveWheelURL("blis-1.3.3-cp313-cp313-pyodide_2025_0_wasm32.whl").href, + resolveWheelURL("srsly-2.5.2-cp313-cp313-pyodide_2025_0_wasm32.whl").href, + resolveWheelURL("thinc-8.3.10-cp313-cp313-pyodide_2025_0_wasm32.whl").href, + resolveWheelURL("spacy-3.8.11-cp313-cp313-pyodide_2025_0_wasm32.whl").href, + resolveWheelURL("en_core_web_sm-3.8.0-py3-none-any.whl").href, + ]); + return { + parse: (text: string) => + SpacyParseResultSchema.parse( + pyodide.runPython(` + from pyodide.ffi import to_js, jsnull + import spacy + + nlp = spacy.load("en_core_web_sm") + + def dependencyParse(text): + doc = nlp(text) + return to_js( + { + "tokens": [ + { + "index": token.i, + "text": token.text, + "pos": token.pos_, + "headTokenIndex": token.head.i, + "dependencyLabel": token.dep_, + "verbForm": token.morph.to_dict().get("VerbForm") or jsnull, + "subtreeIndices": [subtree_token.i for subtree_token in token.subtree], + } + for token in doc + ], + "nounChunks": [ + { + "text": noun_chunk.text, + "startIndex": noun_chunk.start, + "endIndex": noun_chunk.end, + } + for noun_chunk in doc.noun_chunks + ], + } + ) + + dependencyParse + `)(text), + ), + }; +} diff --git a/packages/sentence-structure-diagram-data/tsconfig.json b/packages/sentence-structure-data-parser-by-spacy/tsconfig.json similarity index 100% rename from packages/sentence-structure-diagram-data/tsconfig.json rename to packages/sentence-structure-data-parser-by-spacy/tsconfig.json diff --git a/packages/sentence-structure-data-parser-by-spacy/wheels b/packages/sentence-structure-data-parser-by-spacy/wheels new file mode 120000 index 0000000..a863eb9 --- /dev/null +++ b/packages/sentence-structure-data-parser-by-spacy/wheels @@ -0,0 +1 @@ +../../tools/spacy-pyodide-wheel-builder/wheels \ No newline at end of file diff --git a/packages/sentence-structure-data/codecs/json-codec.ts b/packages/sentence-structure-data/codecs/json-codec.ts new file mode 100644 index 0000000..175d71a --- /dev/null +++ b/packages/sentence-structure-data/codecs/json-codec.ts @@ -0,0 +1,25 @@ +import * as z from "zod"; +import { SentenceStructureDocumentSchema } from "../schema.js"; +import { + simplifiedSentenceStructureDocumentToSentenceStructureDocument, + type SimplifiedSentenceStructureDocument, +} from "./simplified-codec.js"; + +export const jsonStringToSentenceStructureDocument = z.codec( + z.string(), + SentenceStructureDocumentSchema, + { + decode: (jsonString) => + simplifiedSentenceStructureDocumentToSentenceStructureDocument.decode( + JSON.parse(jsonString) as SimplifiedSentenceStructureDocument, + ), + encode: (sentenceStructureDocument) => + JSON.stringify( + simplifiedSentenceStructureDocumentToSentenceStructureDocument.encode( + sentenceStructureDocument, + ), + null, + 2, + ), + }, +); diff --git a/packages/sentence-structure-data/codecs/simplified-codec.ts b/packages/sentence-structure-data/codecs/simplified-codec.ts new file mode 100644 index 0000000..e63bd28 --- /dev/null +++ b/packages/sentence-structure-data/codecs/simplified-codec.ts @@ -0,0 +1,629 @@ +import * as z from "zod"; +import { + coreSentenceElementAllowedSentenceElementNameOptions, + sentenceConstituentTypeToAllowedSentenceElementNameOptionsMap, + SentenceStructureDocumentSchema, + type Coordination, + type SentenceStructureElement, + type Modification, + type Word, +} from "../schema.js"; +import { normalizeSentenceStructureDocument } from "../operations.js"; + +export const SimplifiedSentenceStructureDocumentSchema = z + .object({ + sentences: z.array( + z + .object({ + words: z.array( + z + .object({ + index: z.int().nonnegative().meta({ + title: "インデックス", + description: "単語を一意に識別するためのインデックス。", + }), + text: z.string().meta({ + title: "テキスト", + description: "単語のテキスト。", + }), + whitespaceAfter: z.string().meta({ + title: "単語の直後の空白", + description: + "元の文を復元するための、この単語の直後の空白文字列。", + }), + }) + .meta({ + title: "単語", + description: "単語。", + }), + ), + sentenceStructureElements: z.array( + z + .union([ + z + .object({ + kind: z.literal("core-sentence-element").meta({ + title: "区分", + description: + "文構造要素の区分。`core-sentence-element`は文の主要素を表す。", + }), + index: z.int().nonnegative().meta({ + title: "インデックス", + description: + "文構造要素を一意に識別するためのインデックス。", + }), + startWordIndex: z.int().nonnegative().meta({ + title: "開始単語インデックス", + description: + "文構造要素に含まれる最初の単語のインデックス。", + }), + endWordIndex: z.int().nonnegative().meta({ + title: "終了単語インデックス", + description: + "文構造要素に含まれる最後の単語のインデックス。", + }), + sentenceElementName: z + .nullable( + z.literal( + coreSentenceElementAllowedSentenceElementNameOptions, + ), + ) + .meta({ + title: "文の要素名", + description: "文の要素の名前。", + }), + }) + .meta({ + title: "文の主要素", + description: + "英文構造図において文の主要素として表される構造要素。主語(S)、動詞(V)、目的語(O)、補語(C)のいずれかとなっており、文の構成要素でないもの。", + }), + z + .union([ + z + .object({ + kind: z.literal("sentence-constituent").meta({ + title: "区分", + description: + "文構造要素の区分。`sentence-constituent`は文の構成要素を表す。", + }), + type: z.literal("phrase").meta({ + title: "種別", + description: + "文の構成要素の種別。`phrase`は句を表す。", + }), + usage: z + .literal(["nominal", "adjectival", "adverbial"]) + .meta({ + title: "用法", + description: + "文中での用法。名詞的用法、形容詞的用法、副詞的用法のいずれかからなる。", + }), + index: z.int().nonnegative().meta({ + title: "インデックス", + description: + "文構造要素を一意に識別するためのインデックス。", + }), + startWordIndex: z.int().nonnegative().meta({ + title: "開始単語インデックス", + description: + "文構造要素に含まれる最初の単語のインデックス。", + }), + endWordIndex: z.int().nonnegative().meta({ + title: "終了単語インデックス", + description: + "文構造要素に含まれる最後の単語のインデックス。", + }), + sentenceElementName: z + .nullable( + z.literal( + sentenceConstituentTypeToAllowedSentenceElementNameOptionsMap[ + "phrase" + ], + ), + ) + .meta({ + title: "文の要素名", + description: "文の要素の名前。", + }), + }) + .meta({ + title: "句", + description: "任意の準動詞句。", + }), + z + .object({ + kind: z.literal("sentence-constituent").meta({ + title: "区分", + description: + "文構造要素の区分。`sentence-constituent`は文の構成要素を表す。", + }), + type: z.literal("clause").meta({ + title: "種別", + description: + "文の構成要素の種別。`clause`は節を表す。", + }), + usage: z + .literal(["nominal", "adjectival", "adverbial"]) + .meta({ + title: "用法", + description: + "文中での用法。名詞的用法、形容詞的用法、副詞的用法のいずれかからなる。", + }), + index: z.int().nonnegative().meta({ + title: "インデックス", + description: + "文構造要素を一意に識別するためのインデックス。", + }), + startWordIndex: z.int().nonnegative().meta({ + title: "開始単語インデックス", + description: + "文構造要素に含まれる最初の単語のインデックス。", + }), + endWordIndex: z.int().nonnegative().meta({ + title: "終了単語インデックス", + description: + "文構造要素に含まれる最後の単語のインデックス。", + }), + sentenceElementName: z + .nullable( + z.literal( + sentenceConstituentTypeToAllowedSentenceElementNameOptionsMap[ + "clause" + ], + ), + ) + .meta({ + title: "文の要素名", + description: "文の要素の名前。", + }), + }) + .meta({ + title: "節", + description: "任意の節。", + }), + z + .object({ + kind: z.literal("sentence-constituent").meta({ + title: "区分", + description: + "文構造要素の区分。`sentence-constituent`は文の構成要素を表す。", + }), + type: z.literal("adverbial-phrase").meta({ + title: "種別", + description: + "文の構成要素の種別。`adverbial-phrase`は修飾語句を表す。", + }), + index: z.int().nonnegative().meta({ + title: "インデックス", + description: + "文構造要素を一意に識別するためのインデックス。", + }), + startWordIndex: z.int().nonnegative().meta({ + title: "開始単語インデックス", + description: + "文構造要素に含まれる最初の単語のインデックス。", + }), + endWordIndex: z.int().nonnegative().meta({ + title: "終了単語インデックス", + description: + "文構造要素に含まれる最後の単語のインデックス。", + }), + sentenceElementName: z + .nullable( + z.literal( + sentenceConstituentTypeToAllowedSentenceElementNameOptionsMap[ + "adverbial-phrase" + ], + ), + ) + .meta({ + title: "文の要素名", + description: "文の要素の名前。", + }), + }) + .meta({ + title: "修飾語句", + description: + "前置詞句、副詞句のいずれかからなる構造要素。", + }), + ]) + .meta({ + title: "文の構成要素", + description: "句、節、修飾語句のいずれかからなる構造要素。", + }), + z + .object({ + kind: z.literal("modification-element").meta({ + title: "区分", + description: + "文構造要素の区分。`modification-element`は修飾関係の要素を表す。", + }), + index: z.int().nonnegative().meta({ + title: "インデックス", + description: + "文構造要素を一意に識別するためのインデックス。", + }), + startWordIndex: z.int().nonnegative().meta({ + title: "開始単語インデックス", + description: + "文構造要素に含まれる最初の単語のインデックス。", + }), + endWordIndex: z.int().nonnegative().meta({ + title: "終了単語インデックス", + description: + "文構造要素に含まれる最後の単語のインデックス。", + }), + }) + .meta({ + title: "修飾関係の要素", + description: + "修飾関係の修飾要素あるいは被修飾要素を表す構造要素。修飾要素あるいは被修飾要素が文の主要素でも文の構成要素でもない場合にのみ使用される。", + }), + ]) + .meta({ + title: "文構造要素", + description: + "1つ以上の連続した単語からなる構造要素。文の主要素、文の構成要素、修飾関係の要素のいずれかからなる。", + }), + ), + modifications: z.array( + z + .object({ + modifierSentenceStructureElementIndex: z + .int() + .nonnegative() + .meta({ + title: "修飾要素インデックス", + description: + "修飾関係の修飾要素となる文構造要素のインデックス。", + }), + modifiedSentenceStructureElementIndex: z + .int() + .nonnegative() + .meta({ + title: "被修飾要素インデックス", + description: + "修飾関係の被修飾要素となる文構造要素のインデックス。", + }), + }) + .meta({ + title: "修飾関係", + description: "修飾要素と被修飾要素の関係。", + }), + ), + coordinations: z.array( + z + .object({ + parts: z.array( + z + .object({ + type: z + .literal(["coordinator", "correlative", "conjunct"]) + .meta({ + title: "種別", + description: + "並列関係の構成要素の種別。等位接続詞、相関接続詞、並列要素のいずれかからなる。", + }), + startWordIndex: z.int().nonnegative().meta({ + title: "開始単語インデックス", + description: + "並列関係の構成要素に含まれる最初の単語のインデックス。", + }), + endWordIndex: z.int().nonnegative().meta({ + title: "終了単語インデックス", + description: + "並列関係の構成要素に含まれる最後の単語のインデックス。", + }), + }) + .meta({ + title: "並列関係の構成要素", + description: + "並列関係を構成する個々の要素。等位接続詞、相関接続詞、並列要素のいずれかからなる。", + }), + ), + }) + .meta({ + title: "並列関係", + description: "複数の要素が並列に結びつけられている関係。", + }), + ), + }) + .meta({ + title: "文", + description: "1つの文に関する情報。", + }) + .refine( + (sentence) => + sentence.words + .toSorted((a, b) => a.index - b.index) + .every((word, index) => word.index === index), + { + error: + "単語のインデックスが0から始まる連続した整数ではありません。", + }, + ) + .refine( + (sentence) => + sentence.sentenceStructureElements + .toSorted((a, b) => a.index - b.index) + .every( + (sentenceStructureElement, index) => + sentenceStructureElement.index === index, + ), + { + error: + "文構造要素のインデックスが0から始まる連続した整数ではありません。", + }, + ) + .refine( + (sentence) => + sentence.sentenceStructureElements.every( + (sentenceStructureElement) => + sentenceStructureElement.startWordIndex < + sentence.words.length && + sentenceStructureElement.endWordIndex < sentence.words.length, + ), + { error: "文構造要素が存在しない単語を参照しています。" }, + ) + .refine( + (sentence) => + sentence.modifications.every( + (modification) => + modification.modifierSentenceStructureElementIndex < + sentence.sentenceStructureElements.length && + modification.modifiedSentenceStructureElementIndex < + sentence.sentenceStructureElements.length, + ), + { error: "修飾関係が存在しない文構造要素を参照しています。" }, + ) + .refine( + (sentence) => + sentence.coordinations.every((coordination) => + coordination.parts.every( + (part) => + part.startWordIndex < sentence.words.length && + part.endWordIndex < sentence.words.length, + ), + ), + { error: "並列関係の構成要素が存在しない単語を参照しています。" }, + ), + ), + }) + .meta({ + title: "英文構造に関する情報", + description: "英文構造図が共通して内部的に持つ英文構造に関する情報。", + }); +export type SimplifiedSentenceStructureDocument = z.infer< + typeof SimplifiedSentenceStructureDocumentSchema +>; + +export const simplifiedSentenceStructureDocumentToSentenceStructureDocument = + z.codec( + SimplifiedSentenceStructureDocumentSchema, + SentenceStructureDocumentSchema, + { + decode: (simplifiedSentenceStructureDocument) => { + const sortedSimplifiedSentenceStructureDocument = { + sentences: simplifiedSentenceStructureDocument.sentences.map( + (sentence) => ({ + words: sentence.words.toSorted((a, b) => a.index - b.index), + sentenceStructureElements: + sentence.sentenceStructureElements.toSorted( + (a, b) => a.index - b.index, + ), + modifications: sentence.modifications, + coordinations: sentence.coordinations.map((coordination) => ({ + parts: coordination.parts.toSorted( + (a, b) => a.startWordIndex - b.startWordIndex, + ), + })), + }), + ), + } satisfies SimplifiedSentenceStructureDocument; + + return normalizeSentenceStructureDocument({ + sentences: sortedSimplifiedSentenceStructureDocument.sentences.map( + (sentence, index) => { + const wordIds = sentence.words.map(() => crypto.randomUUID()); + const sentenceStructureElementIds = + sentence.sentenceStructureElements.map(() => + crypto.randomUUID(), + ); + + return { + id: crypto.randomUUID(), + index, + words: sentence.words.map((word, index) => ({ + id: wordIds[index]!, + index: word.index, + text: word.text, + whitespaceAfter: word.whitespaceAfter, + })) satisfies Word[], + sentenceStructureElements: + sentence.sentenceStructureElements.map( + (sentenceStructureElement, index) => + sentenceStructureElement.kind === "core-sentence-element" + ? ({ + kind: sentenceStructureElement.kind, + id: sentenceStructureElementIds[index]!, + startWordId: + wordIds[sentenceStructureElement.startWordIndex]!, + endWordId: + wordIds[sentenceStructureElement.endWordIndex]!, + sentenceElementName: + sentenceStructureElement.sentenceElementName, + } satisfies SentenceStructureElement) + : sentenceStructureElement.kind === + "sentence-constituent" + ? sentenceStructureElement.type === "phrase" || + sentenceStructureElement.type === "clause" + ? ({ + kind: sentenceStructureElement.kind, + type: sentenceStructureElement.type, + usage: sentenceStructureElement.usage, + id: sentenceStructureElementIds[index]!, + startWordId: + wordIds[ + sentenceStructureElement.startWordIndex + ]!, + endWordId: + wordIds[ + sentenceStructureElement.endWordIndex + ]!, + sentenceElementName: + sentenceStructureElement.sentenceElementName, + } satisfies SentenceStructureElement) + : ({ + kind: sentenceStructureElement.kind, + type: sentenceStructureElement.type, + id: sentenceStructureElementIds[index]!, + startWordId: + wordIds[ + sentenceStructureElement.startWordIndex + ]!, + endWordId: + wordIds[ + sentenceStructureElement.endWordIndex + ]!, + sentenceElementName: + sentenceStructureElement.sentenceElementName, + } satisfies SentenceStructureElement) + : ({ + kind: sentenceStructureElement.kind, + id: sentenceStructureElementIds[index]!, + startWordId: + wordIds[ + sentenceStructureElement.startWordIndex + ]!, + endWordId: + wordIds[sentenceStructureElement.endWordIndex]!, + } satisfies SentenceStructureElement), + ) satisfies SentenceStructureElement[], + modifications: sentence.modifications.map((modification) => { + return { + id: crypto.randomUUID(), + modifierSentenceStructureElementId: + sentenceStructureElementIds[ + modification.modifierSentenceStructureElementIndex + ]!, + modifiedSentenceStructureElementId: + sentenceStructureElementIds[ + modification.modifiedSentenceStructureElementIndex + ]!, + }; + }) satisfies Modification[], + coordinations: sentence.coordinations.map((coordination) => ({ + id: crypto.randomUUID(), + parts: coordination.parts.map((part, index) => ({ + type: part.type, + id: crypto.randomUUID(), + index, + startWordId: wordIds[part.startWordIndex]!, + endWordId: wordIds[part.endWordIndex]!, + })), + })) satisfies Coordination[], + }; + }, + ), + }); + }, + encode: (sentenceStructureDocument) => ({ + sentences: sentenceStructureDocument.sentences.map((sentence) => ({ + words: sentence.words.map((word) => ({ + index: word.index, + text: word.text, + whitespaceAfter: word.whitespaceAfter, + })) satisfies SimplifiedSentenceStructureDocument["sentences"][number]["words"], + sentenceStructureElements: sentence.sentenceStructureElements.map( + (sentenceStructureElement, index) => + sentenceStructureElement.kind === "core-sentence-element" + ? ({ + kind: sentenceStructureElement.kind, + index, + startWordIndex: sentence.words.findIndex( + (word) => + word.id === sentenceStructureElement.startWordId, + )!, + endWordIndex: sentence.words.findIndex( + (word) => word.id === sentenceStructureElement.endWordId, + )!, + sentenceElementName: + sentenceStructureElement.sentenceElementName, + } satisfies SimplifiedSentenceStructureDocument["sentences"][number]["sentenceStructureElements"][number]) + : sentenceStructureElement.kind === "sentence-constituent" + ? sentenceStructureElement.type === "phrase" || + sentenceStructureElement.type === "clause" + ? ({ + kind: sentenceStructureElement.kind, + type: sentenceStructureElement.type, + usage: sentenceStructureElement.usage, + index, + startWordIndex: sentence.words.findIndex( + (word) => + word.id === sentenceStructureElement.startWordId, + )!, + endWordIndex: sentence.words.findIndex( + (word) => + word.id === sentenceStructureElement.endWordId, + )!, + sentenceElementName: + sentenceStructureElement.sentenceElementName, + } satisfies SimplifiedSentenceStructureDocument["sentences"][number]["sentenceStructureElements"][number]) + : ({ + kind: sentenceStructureElement.kind, + type: sentenceStructureElement.type, + index, + startWordIndex: sentence.words.findIndex( + (word) => + word.id === sentenceStructureElement.startWordId, + )!, + endWordIndex: sentence.words.findIndex( + (word) => + word.id === sentenceStructureElement.endWordId, + )!, + sentenceElementName: + sentenceStructureElement.sentenceElementName, + } satisfies SimplifiedSentenceStructureDocument["sentences"][number]["sentenceStructureElements"][number]) + : ({ + kind: sentenceStructureElement.kind, + index, + startWordIndex: sentence.words.findIndex( + (word) => + word.id === sentenceStructureElement.startWordId, + )!, + endWordIndex: sentence.words.findIndex( + (word) => + word.id === sentenceStructureElement.endWordId, + )!, + } satisfies SimplifiedSentenceStructureDocument["sentences"][number]["sentenceStructureElements"][number]), + ) satisfies SimplifiedSentenceStructureDocument["sentences"][number]["sentenceStructureElements"], + modifications: sentence.modifications.map((modification) => ({ + modifierSentenceStructureElementIndex: + sentence.sentenceStructureElements.findIndex( + (sentenceStructureElement) => + sentenceStructureElement.id === + modification.modifierSentenceStructureElementId, + ), + modifiedSentenceStructureElementIndex: + sentence.sentenceStructureElements.findIndex( + (sentenceStructureElement) => + sentenceStructureElement.id === + modification.modifiedSentenceStructureElementId, + ), + })) satisfies SimplifiedSentenceStructureDocument["sentences"][number]["modifications"], + coordinations: sentence.coordinations.map((coordination) => ({ + parts: coordination.parts.map((part) => ({ + type: part.type, + startWordIndex: sentence.words.findIndex( + (word) => word.id === part.startWordId, + )!, + endWordIndex: sentence.words.findIndex( + (word) => word.id === part.endWordId, + )!, + })), + })) satisfies SimplifiedSentenceStructureDocument["sentences"][number]["coordinations"], + })), + }), + }, + ); diff --git a/packages/sentence-structure-data/codecs/xml-codec.ts b/packages/sentence-structure-data/codecs/xml-codec.ts new file mode 100644 index 0000000..ccacc86 --- /dev/null +++ b/packages/sentence-structure-data/codecs/xml-codec.ts @@ -0,0 +1,411 @@ +import * as z from "zod"; +import { XMLBuilder, XMLParser } from "fast-xml-parser"; +import { + coreSentenceElementAllowedSentenceElementNameOptions, + sentenceConstituentTypeToAllowedSentenceElementNameOptionsMap, + SentenceStructureDocumentSchema, + type SentenceStructureDocument, +} from "../schema.js"; +import { + simplifiedSentenceStructureDocumentToSentenceStructureDocument, + type SimplifiedSentenceStructureDocument, +} from "./simplified-codec.js"; + +const XMLSentenceStructureDocumentSchema = z.object({ + "sentence-structure-document": z.object({ + "@_xmlns": z.literal("https://utlis.github.io/sv-marker/"), + "@_version": z.literal("0.1.0"), + sentences: z.object({ + sentence: z.array( + z.object({ + words: z.object({ + word: z.array( + z.object({ + "@_index": z.string(), + "#text": z.string(), + "@_whitespace-after": z.string(), + }), + ), + }), + "sentence-structure-elements": z.optional( + z.object({ + "sentence-structure-element": z.array( + z.union([ + z.object({ + "@_kind": z.literal("core-sentence-element"), + "@_index": z.string(), + "@_start-word-index": z.string(), + "@_end-word-index": z.string(), + "@_sentence-element-name": z.optional( + z.literal( + coreSentenceElementAllowedSentenceElementNameOptions, + ), + ), + }), + z.object({ + "@_kind": z.literal("sentence-constituent"), + "@_type": z.literal("phrase"), + "@_usage": z.literal([ + "nominal", + "adjectival", + "adverbial", + ]), + "@_index": z.string(), + "@_start-word-index": z.string(), + "@_end-word-index": z.string(), + "@_sentence-element-name": z.optional( + z.literal( + sentenceConstituentTypeToAllowedSentenceElementNameOptionsMap[ + "phrase" + ], + ), + ), + }), + z.object({ + "@_kind": z.literal("sentence-constituent"), + "@_type": z.literal("clause"), + "@_usage": z.literal([ + "nominal", + "adjectival", + "adverbial", + ]), + "@_index": z.string(), + "@_start-word-index": z.string(), + "@_end-word-index": z.string(), + "@_sentence-element-name": z.optional( + z.literal( + sentenceConstituentTypeToAllowedSentenceElementNameOptionsMap[ + "clause" + ], + ), + ), + }), + z.object({ + "@_kind": z.literal("sentence-constituent"), + "@_type": z.literal("adverbial-phrase"), + "@_index": z.string(), + "@_start-word-index": z.string(), + "@_end-word-index": z.string(), + "@_sentence-element-name": z.optional( + z.literal( + sentenceConstituentTypeToAllowedSentenceElementNameOptionsMap[ + "adverbial-phrase" + ], + ), + ), + }), + z.object({ + "@_kind": z.literal("modification-element"), + "@_index": z.string(), + "@_start-word-index": z.string(), + "@_end-word-index": z.string(), + }), + ]), + ), + }), + ), + modifications: z.optional( + z.object({ + modification: z.array( + z.object({ + "@_modifier-sentence-structure-element-index": z.string(), + "@_modified-sentence-structure-element-index": z.string(), + }), + ), + }), + ), + coordinations: z.optional( + z.object({ + coordination: z.array( + z.object({ + "coordination-part": z.array( + z.object({ + "@_type": z.literal([ + "coordinator", + "correlative", + "conjunct", + ]), + "@_start-word-index": z.string(), + "@_end-word-index": z.string(), + }), + ), + }), + ), + }), + ), + }), + ), + }), + }), +}); + +const xmlSentenceStructureDocumentToSentenceStructureDocument = z.codec( + XMLSentenceStructureDocumentSchema, + SentenceStructureDocumentSchema, + { + decode: (xmlSentenceStructureDocument) => + simplifiedSentenceStructureDocumentToSentenceStructureDocument.decode({ + sentences: xmlSentenceStructureDocument[ + "sentence-structure-document" + ].sentences.sentence.map((sentence) => ({ + words: sentence.words.word.map((word) => ({ + index: Number(word["@_index"]), + text: word["#text"], + whitespaceAfter: word["@_whitespace-after"], + })), + sentenceStructureElements: + sentence["sentence-structure-elements"]?.[ + "sentence-structure-element" + ].map((sentenceStructureElement) => { + switch (sentenceStructureElement["@_kind"]) { + case "core-sentence-element": + return { + kind: sentenceStructureElement["@_kind"], + index: Number(sentenceStructureElement["@_index"]), + startWordIndex: Number( + sentenceStructureElement["@_start-word-index"], + ), + endWordIndex: Number( + sentenceStructureElement["@_end-word-index"], + ), + sentenceElementName: + sentenceStructureElement["@_sentence-element-name"] ?? + null, + }; + case "sentence-constituent": + switch (sentenceStructureElement["@_type"]) { + case "phrase": + case "clause": + return { + kind: sentenceStructureElement["@_kind"], + type: sentenceStructureElement["@_type"], + usage: sentenceStructureElement["@_usage"], + index: Number(sentenceStructureElement["@_index"]), + startWordIndex: Number( + sentenceStructureElement["@_start-word-index"], + ), + endWordIndex: Number( + sentenceStructureElement["@_end-word-index"], + ), + sentenceElementName: + sentenceStructureElement["@_sentence-element-name"] ?? + null, + }; + case "adverbial-phrase": + return { + kind: sentenceStructureElement["@_kind"], + type: sentenceStructureElement["@_type"], + index: Number(sentenceStructureElement["@_index"]), + startWordIndex: Number( + sentenceStructureElement["@_start-word-index"], + ), + endWordIndex: Number( + sentenceStructureElement["@_end-word-index"], + ), + sentenceElementName: + sentenceStructureElement["@_sentence-element-name"] ?? + null, + }; + default: + sentenceStructureElement satisfies never; + throw new Error("Unreachable"); + } + case "modification-element": + return { + kind: sentenceStructureElement["@_kind"], + index: Number(sentenceStructureElement["@_index"]), + startWordIndex: Number( + sentenceStructureElement["@_start-word-index"], + ), + endWordIndex: Number( + sentenceStructureElement["@_end-word-index"], + ), + }; + default: + sentenceStructureElement satisfies never; + throw new Error("Unreachable"); + } + }) ?? [], + modifications: + sentence.modifications?.modification.map((modification) => ({ + modifierSentenceStructureElementIndex: Number( + modification["@_modifier-sentence-structure-element-index"], + ), + modifiedSentenceStructureElementIndex: Number( + modification["@_modified-sentence-structure-element-index"], + ), + })) ?? [], + coordinations: + sentence.coordinations?.coordination.map((coordination) => ({ + parts: coordination["coordination-part"].map((part) => ({ + type: part["@_type"], + startWordIndex: Number(part["@_start-word-index"]), + endWordIndex: Number(part["@_end-word-index"]), + })), + })) ?? [], + })), + } satisfies SimplifiedSentenceStructureDocument), + encode: (sentenceStructureDocument) => { + const simplifiedSentenceStructureDocument = + simplifiedSentenceStructureDocumentToSentenceStructureDocument.encode( + sentenceStructureDocument, + ); + + return { + "sentence-structure-document": { + "@_xmlns": "https://utlis.github.io/sv-marker/", + "@_version": "0.1.0", + sentences: { + sentence: simplifiedSentenceStructureDocument.sentences.map( + (sentence) => ({ + words: { + word: sentence.words.map((word) => ({ + "@_index": String(word.index), + "#text": word.text, + "@_whitespace-after": word.whitespaceAfter, + })), + }, + "sentence-structure-elements": { + "sentence-structure-element": + sentence.sentenceStructureElements.map( + (sentenceStructureElement) => { + switch (sentenceStructureElement.kind) { + case "core-sentence-element": + return { + "@_kind": sentenceStructureElement.kind, + "@_index": String(sentenceStructureElement.index), + "@_start-word-index": String( + sentenceStructureElement.startWordIndex, + ), + "@_end-word-index": String( + sentenceStructureElement.endWordIndex, + ), + "@_sentence-element-name": + sentenceStructureElement.sentenceElementName ?? + undefined, + }; + case "sentence-constituent": + switch (sentenceStructureElement.type) { + case "phrase": + case "clause": + return { + "@_kind": sentenceStructureElement.kind, + "@_type": sentenceStructureElement.type, + "@_usage": sentenceStructureElement.usage, + "@_index": String( + sentenceStructureElement.index, + ), + "@_start-word-index": String( + sentenceStructureElement.startWordIndex, + ), + "@_end-word-index": String( + sentenceStructureElement.endWordIndex, + ), + "@_sentence-element-name": + sentenceStructureElement.sentenceElementName ?? + undefined, + }; + case "adverbial-phrase": + return { + "@_kind": sentenceStructureElement.kind, + "@_type": sentenceStructureElement.type, + "@_index": String( + sentenceStructureElement.index, + ), + "@_start-word-index": String( + sentenceStructureElement.startWordIndex, + ), + "@_end-word-index": String( + sentenceStructureElement.endWordIndex, + ), + "@_sentence-element-name": + sentenceStructureElement.sentenceElementName ?? + undefined, + }; + default: + sentenceStructureElement satisfies never; + throw new Error("Unreachable"); + } + case "modification-element": + return { + "@_kind": sentenceStructureElement.kind, + "@_index": String(sentenceStructureElement.index), + "@_start-word-index": String( + sentenceStructureElement.startWordIndex, + ), + "@_end-word-index": String( + sentenceStructureElement.endWordIndex, + ), + }; + default: + sentenceStructureElement satisfies never; + throw new Error("Unreachable"); + } + }, + ), + }, + modifications: { + modification: sentence.modifications.map((modification) => ({ + "@_modifier-sentence-structure-element-index": String( + modification.modifierSentenceStructureElementIndex, + ), + "@_modified-sentence-structure-element-index": String( + modification.modifiedSentenceStructureElementIndex, + ), + })), + }, + coordinations: { + coordination: sentence.coordinations.map((coordination) => ({ + "coordination-part": coordination.parts.map((part) => ({ + "@_type": part.type, + "@_start-word-index": String(part.startWordIndex), + "@_end-word-index": String(part.endWordIndex), + })), + })), + }, + }), + ), + }, + }, + } satisfies z.infer; + }, + }, +); + +export const xmlStringToSentenceStructureDocument = z.codec( + z.string(), + SentenceStructureDocumentSchema, + { + decode: (xmlString) => { + try { + return xmlSentenceStructureDocumentToSentenceStructureDocument.decode( + new XMLParser({ + ignoreAttributes: false, + isArray: (tagName) => + [ + "sentence", + "word", + "sentence-structure-element", + "modification", + "coordination-part", + "coordination", + ].includes(tagName), + parseTagValue: false, + }).parse(xmlString), + ) satisfies SentenceStructureDocument; + } catch { + return null as any; + } + }, + encode: (sentenceStructureDocument) => + new XMLBuilder({ + format: true, + ignoreAttributes: false, + suppressEmptyNode: true, + }).build( + xmlSentenceStructureDocumentToSentenceStructureDocument.encode( + sentenceStructureDocument, + ) satisfies z.infer, + ), + }, +); diff --git a/packages/sentence-structure-data/format.ts b/packages/sentence-structure-data/format.ts deleted file mode 100644 index cbfe436..0000000 --- a/packages/sentence-structure-data/format.ts +++ /dev/null @@ -1,281 +0,0 @@ -import * as z from "zod"; -import { XMLBuilder, XMLParser } from "fast-xml-parser"; -import { - sentenceElementRangeTypeOptions, - SentenceStructureDataSchema, - type Coordination, - type CoordinationChildType, - type Range, - type Relation, - type SentenceElementRangeType, - type SentenceStructureRangeType, -} from "./schema.js"; -import { - SimplifiedSentenceStructureDataSchema, - type SimplifiedSentenceStructureData, -} from "./simplified-schema.js"; - -const sentenceElementRangeTypePairs = [ - ["core-sentence-element", "文の主要素"], -] as const satisfies [SentenceElementRangeType, string][]; -const sentenceElementRangeTypeToSimplifiedSentenceElementRangeTypeMap = new Map( - sentenceElementRangeTypePairs, -); -const simplifiedSentenceElementRangeTypeToSentenceElementRangeTypeMap = new Map( - sentenceElementRangeTypePairs.map(([a, b]) => [b, a]), -); - -const sentenceStructureRangeTypePairs = [ - ["modifier", "修飾語"], - ["phrase", "句"], - ["clause", "節"], -] as const satisfies [SentenceStructureRangeType, string][]; -const sentenceStructureRangeTypeToSimplifiedSentenceStructureRangeTypeMap = - new Map(sentenceStructureRangeTypePairs); -const simplifiedSentenceStructureRangeTypeToSentenceStructureRangeTypeMap = - new Map(sentenceStructureRangeTypePairs.map(([a, b]) => [b, a])); - -const coordinationChildTypePairs = [ - ["coordinating conjunction", "等位接続詞"], - ["correlative conjunction", "相関接続詞"], - ["conjunct", "並列要素"], -] as const satisfies [CoordinationChildType, string][]; -const coordinationChildTypeToSimplifiedCoordinationChildTypeMap = new Map( - coordinationChildTypePairs, -); -const simplifiedCoordinationChildTypeToCoordinationChildTypeMap = new Map( - coordinationChildTypePairs.map(([a, b]) => [b, a]), -); - -export const simplifiedSentenceStructureDataToSentenceStructureData = z.codec( - SimplifiedSentenceStructureDataSchema, - SentenceStructureDataSchema, - { - decode: (simplifiedSentenceStructureData) => { - const sortedSimplifiedSentenceStructureData = { - text: simplifiedSentenceStructureData.text, - words: simplifiedSentenceStructureData.words.sort( - (a, b) => a.index - b.index, - ), - ranges: simplifiedSentenceStructureData.ranges.sort( - (a, b) => a.index - b.index, - ), - relations: simplifiedSentenceStructureData.relations, - coordinations: simplifiedSentenceStructureData.coordinations.map( - (coordination) => ({ - children: coordination.children.sort( - (a, b) => a.startWordIndex - b.startWordIndex, - ), - }), - ), - }; - const rangeIds = sortedSimplifiedSentenceStructureData.ranges.map(() => - crypto.randomUUID(), - ); - - return { - text: sortedSimplifiedSentenceStructureData.text, - words: sortedSimplifiedSentenceStructureData.words, - ranges: sortedSimplifiedSentenceStructureData.ranges.map((range) => - range.type === "関係" - ? { - kind: "relation", - type: "relation", - id: rangeIds[range.index]!, - startWordIndex: range.startWordIndex, - endWordIndex: range.endWordIndex, - } - : sentenceElementRangeTypeOptions.includes( - simplifiedSentenceElementRangeTypeToSentenceElementRangeTypeMap.get( - range.type as any, - )!, - ) - ? { - kind: "core-sentence-element", - type: simplifiedSentenceElementRangeTypeToSentenceElementRangeTypeMap.get( - range.type as any, - )!, - id: rangeIds[range.index]!, - startWordIndex: range.startWordIndex, - endWordIndex: range.endWordIndex, - sentenceElementName: range.sentenceElementName, - } - : { - kind: "sentence-structure", - type: simplifiedSentenceStructureRangeTypeToSentenceStructureRangeTypeMap.get( - range.type as any, - )!, - id: rangeIds[range.index]!, - startWordIndex: range.startWordIndex, - endWordIndex: range.endWordIndex, - sentenceElementName: range.sentenceElementName, - }, - ) as Range[], - relations: sortedSimplifiedSentenceStructureData.relations.map( - (relation) => { - return { - id: crypto.randomUUID(), - fromRangeId: rangeIds[relation.fromRangeIndex]!, - toRangeId: rangeIds[relation.toRangeIndex]!, - }; - }, - ) satisfies Relation[], - coordinations: sortedSimplifiedSentenceStructureData.coordinations.map( - (coordination) => ({ - id: crypto.randomUUID(), - children: coordination.children.map((child, index) => ({ - type: simplifiedCoordinationChildTypeToCoordinationChildTypeMap.get( - child.type, - )!, - index: index, - startWordIndex: child.startWordIndex, - endWordIndex: child.endWordIndex, - })), - }), - ) satisfies Coordination[], - }; - }, - encode: (sentenceStructureData) => { - const sortedSentenceStructureData = { - ...sentenceStructureData, - words: sentenceStructureData.words.sort((a, b) => a.index - b.index), - ranges: sentenceStructureData.ranges.sort((a, b) => { - if (a.startWordIndex !== b.startWordIndex) { - return a.startWordIndex - b.startWordIndex; - } - return b.endWordIndex - a.endWordIndex; - }), - relations: sentenceStructureData.relations, - coordinations: sentenceStructureData.coordinations - .map((coordination) => ({ - children: coordination.children.sort((a, b) => a.index - b.index), - })) - .sort((a, b) => { - if ( - a.children.at(0)!.startWordIndex !== - b.children.at(0)!.startWordIndex - ) { - return ( - a.children.at(0)!.startWordIndex - - b.children.at(0)!.startWordIndex - ); - } - return ( - b.children.at(-1)!.endWordIndex - a.children.at(-1)!.endWordIndex - ); - }), - }; - return { - text: sortedSentenceStructureData.text, - words: sortedSentenceStructureData.words, - ranges: sortedSentenceStructureData.ranges.map((range, index) => - range.kind === "relation" - ? { - type: "関係", - index: index, - startWordIndex: range.startWordIndex, - endWordIndex: range.endWordIndex, - } - : { - type: - range.kind === "core-sentence-element" - ? sentenceElementRangeTypeToSimplifiedSentenceElementRangeTypeMap.get( - range.type, - )! - : sentenceStructureRangeTypeToSimplifiedSentenceStructureRangeTypeMap.get( - range.type, - )!, - index: index, - startWordIndex: range.startWordIndex, - endWordIndex: range.endWordIndex, - sentenceElementName: range.sentenceElementName, - }, - ) as SimplifiedSentenceStructureData["ranges"], - relations: ( - sortedSentenceStructureData.relations.map((relation) => ({ - fromRangeIndex: sortedSentenceStructureData.ranges.findIndex( - (range) => range.id === relation.fromRangeId, - ), - toRangeIndex: sortedSentenceStructureData.ranges.findIndex( - (range) => range.id === relation.toRangeId, - ), - })) satisfies SimplifiedSentenceStructureData["relations"] - ).sort((a, b) => { - if (a.fromRangeIndex !== b.fromRangeIndex) { - return a.fromRangeIndex - b.fromRangeIndex; - } - return b.toRangeIndex - a.toRangeIndex; - }), - coordinations: sortedSentenceStructureData.coordinations.map( - (coordination) => ({ - children: coordination.children.map((child) => ({ - type: coordinationChildTypeToSimplifiedCoordinationChildTypeMap.get( - child.type, - )!, - startWordIndex: child.startWordIndex, - endWordIndex: child.endWordIndex, - })), - }), - ) satisfies SimplifiedSentenceStructureData["coordinations"], - }; - }, - }, -); - -export const stringToSentenceStructureData = z.codec( - z.string(), - SentenceStructureDataSchema, - { - decode: (string) => - simplifiedSentenceStructureDataToSentenceStructureData.decode( - JSON.parse(string), - ), - encode: (sentenceStructureData) => - JSON.stringify( - simplifiedSentenceStructureDataToSentenceStructureData.encode( - sentenceStructureData, - ), - null, - 2, - ), - }, -); - -export const xmlStringToSentenceStructureData = z.codec( - z.string(), - SentenceStructureDataSchema, - { - decode: (xml) => { - const parsed = new XMLParser({ - isArray: (tagName) => - [ - "words", - "ranges", - "relations", - "coordinations", - "children", - ].includes(tagName), - }).parse(xml); - return simplifiedSentenceStructureDataToSentenceStructureData.decode({ - words: [], - relations: [], - coordinations: [], - ...parsed, - ranges: - parsed.ranges?.map((range: any) => ({ - ...range, - sentenceElementName: - range.sentenceElementName === "" - ? null - : range.sentenceElementName, - })) ?? [], - }); - }, - encode: (sentenceStructureData) => - new XMLBuilder({ format: true }).build( - simplifiedSentenceStructureDataToSentenceStructureData.encode( - sentenceStructureData, - ), - ), - }, -); diff --git a/packages/sentence-structure-data/index.ts b/packages/sentence-structure-data/index.ts index db80ee6..50bcaf3 100644 --- a/packages/sentence-structure-data/index.ts +++ b/packages/sentence-structure-data/index.ts @@ -1,47 +1,62 @@ export { sentenceElementNameOptions, - sentenceElementRangeTypeOptions, - sentenceElementRangeTypeToAllowedSentenceElementNameOptionsMap, - sentenceStructureRangeTypeOptions, - sentenceStructureRangeTypeToAllowedSentenceElementNameOptionsMap, - coordinationChildTypeOptions, - SentenceStructureDataSchema, + coreSentenceElementAllowedSentenceElementNameOptions, + sentenceConstituentTypeToAllowedSentenceElementNameOptionsMap, + SentenceStructureDocumentSchema, type Word, type SentenceElementName, - type SentenceElementRangeType, - type SentenceStructureRangeType, - type RangeType, - type Range, - type Relation, - type CoordinationChildType, - type CoordinationChild, + type SentenceConstituentType, + type SentenceStructureElement, + type Modification, + type CoordinationPartType, + type CoordinationPart, type Coordination, - type SentenceStructureData, + type Sentence, + type SentenceStructureDocument, } from "./schema.js"; export { - SimplifiedAnnotationDataSchema, - SimplifiedSentenceStructureDataSchema, - type SimplifiedAnnotationData, - type SimplifiedSentenceStructureData, -} from "./simplified-schema.js"; + SimplifiedSentenceStructureDocumentSchema, + type SimplifiedSentenceStructureDocument, +} from "./codecs/simplified-codec.js"; export { - createSentenceStructureDataFromText, - createSentenceStructureDataFromStringData, - createSentenceStructureDataFromXMLData, - createSentenceStructureDataFromSimplifiedAnnotationData, - sentenceStructureDataToString, - sentenceStructureDataToXMLString, - createSentenceElementRange, - createSentenceStructureRange, - findRangeById, - findRangeByStartAndEndWordIndex, + createSentenceStructureDocumentFromWords, + createSentenceStructureDocumentFromJSONString, + createSentenceStructureDocumentFromXMLString, + sentenceStructureDocumentToJSONString, + sentenceStructureDocumentToXMLString, + sentenceStructureDocumentToSentenceStructureDocumentForest, + sentenceStructureDocumentToSentenceStructureDecoratedDocumentForest, + findWordById, + addSentenceStructureElement, + findSentenceStructureElementById, + findSentenceStructureElementByStartAndEndWordId, updateSentenceElementName, - deleteRange, - createRelation, - findRelationById, - deleteRelation, - createCoordination, + deleteSentenceStructureElement, + addModification, + findModificationById, + deleteModification, + addCoordination, findCoordinationById, - findCoordinationByStartAndEndWordIndex, + findCoordinationByStartAndEndWordId, deleteCoordination, } from "./operations.js"; +export type { + SentenceStructureDocumentWordNode, + SentenceStructureDocumentSentenceStructureElementNode, + SentenceStructureDocumentCoordinationPartNode, + SentenceStructureDocumentCoordinationNode, + SentenceStructureDocumentRootNode, + SentenceStructureDocumentSentenceTree, + SentenceStructureDocumentForest, + SentenceStructureDocumentNode, +} from "./tree/types.js"; +export type { + SentenceStructureDecoratedDocumentWordNode, + SentenceStructureDecoratedDocumentSentenceStructureElementNode, + SentenceStructureDecoratedDocumentCoordinationPartNode, + SentenceStructureDecoratedDocumentCoordinationNode, + SentenceStructureDecoratedDocumentRootNode, + SentenceStructureDecoratedDocumentSentenceTree, + SentenceStructureDecoratedDocumentForest, + SentenceStructureDecoratedDocumentNode, +} from "./tree/decorated/types.js"; diff --git a/packages/sentence-structure-data/operations.ts b/packages/sentence-structure-data/operations.ts index 6a07291..c557740 100644 --- a/packages/sentence-structure-data/operations.ts +++ b/packages/sentence-structure-data/operations.ts @@ -1,25 +1,22 @@ import { - sentenceElementRangeTypeToAllowedSentenceElementNameOptionsMap, - SentenceStructureDataSchema, - sentenceStructureRangeTypeToAllowedSentenceElementNameOptionsMap, + coreSentenceElementAllowedSentenceElementNameOptions, + sentenceConstituentTypeToAllowedSentenceElementNameOptionsMap, + SentenceStructureDocumentSchema, type Coordination, - type CoordinationChildType, - type Range, - type Relation, - type SentenceElementRangeType, - type SentenceStructureData, - type SentenceStructureRangeType, + type CoordinationPart, + type CoordinationPartType, + type Modification, + type SentenceStructureDocument, + type SentenceElementName, + type SentenceStructureElement, + type Word, } from "./schema.js"; -import { - simplifiedSentenceStructureDataToSentenceStructureData, - stringToSentenceStructureData, - xmlStringToSentenceStructureData, -} from "./format.js"; -import { tokenizeText } from "./tokenize-text.js"; -import type { - SimplifiedAnnotationData, - SimplifiedSentenceStructureData, -} from "./simplified-schema.js"; +import { jsonStringToSentenceStructureDocument } from "./codecs/json-codec.js"; +import { xmlStringToSentenceStructureDocument } from "./codecs/xml-codec.js"; +import type { SentenceStructureDocumentForest } from "./tree/types.js"; +import { createSentenceStructureDocumentForest } from "./tree/create.js"; +import type { SentenceStructureDecoratedDocumentForest } from "./tree/decorated/types.js"; +import { createSentenceStructureDecoratedDocumentForest } from "./tree/decorated/create.js"; type Result = | { @@ -31,34 +28,144 @@ type Result = message: string; }; -export function createSentenceStructureDataFromText(input: { - text: string; -}): SentenceStructureData { - return SentenceStructureDataSchema.parse({ - text: input.text, - words: tokenizeText(input.text), - ranges: [], - relations: [], - coordinations: [], - } satisfies SentenceStructureData); +export function normalizeSentenceStructureDocument( + sentenceStructureDocument: SentenceStructureDocument, +): SentenceStructureDocument { + return { + sentences: sentenceStructureDocument.sentences + .toSorted((a, b) => a.index - b.index) + .map((sentence) => { + const normalizedSentenceStructureElements = + sentence.sentenceStructureElements.toSorted((a, b) => { + const aStartWordIndex = findWordById(sentenceStructureDocument, { + sentenceId: sentence.id, + wordId: a.startWordId, + })!.index; + const aEndWordIndex = findWordById(sentenceStructureDocument, { + sentenceId: sentence.id, + wordId: a.endWordId, + })!.index; + const bStartWordIndex = findWordById(sentenceStructureDocument, { + sentenceId: sentence.id, + wordId: b.startWordId, + })!.index; + const bEndWordIndex = findWordById(sentenceStructureDocument, { + sentenceId: sentence.id, + wordId: b.endWordId, + })!.index; + if (aStartWordIndex !== bStartWordIndex) { + return aStartWordIndex - bStartWordIndex; + } + return bEndWordIndex - aEndWordIndex; + }); + return { + id: sentence.id, + index: sentence.index, + words: sentence.words.toSorted((a, b) => a.index - b.index), + sentenceStructureElements: normalizedSentenceStructureElements, + modifications: sentence.modifications.toSorted((a, b) => { + const aModifierIndex = + normalizedSentenceStructureElements.findIndex( + (sentenceStructureElement) => + sentenceStructureElement.id === + a.modifierSentenceStructureElementId, + )!; + const aModifiedIndex = + normalizedSentenceStructureElements.findIndex( + (sentenceStructureElement) => + sentenceStructureElement.id === + a.modifiedSentenceStructureElementId, + )!; + const bModifierIndex = + normalizedSentenceStructureElements.findIndex( + (sentenceStructureElement) => + sentenceStructureElement.id === + b.modifierSentenceStructureElementId, + )!; + const bModifiedIndex = + normalizedSentenceStructureElements.findIndex( + (sentenceStructureElement) => + sentenceStructureElement.id === + b.modifiedSentenceStructureElementId, + )!; + if (aModifierIndex !== bModifierIndex) { + return aModifierIndex - bModifierIndex; + } + return aModifiedIndex - bModifiedIndex; + }), + coordinations: sentence.coordinations + .map((coordination) => ({ + ...coordination, + parts: coordination.parts.toSorted((a, b) => a.index - b.index), + })) + .toSorted((a, b) => { + const aStartWordIndex = findWordById(sentenceStructureDocument, { + sentenceId: sentence.id, + wordId: a.parts.at(0)!.startWordId, + })!.index; + const aEndWordIndex = findWordById(sentenceStructureDocument, { + sentenceId: sentence.id, + wordId: a.parts.at(-1)!.endWordId, + })!.index; + const bStartWordIndex = findWordById(sentenceStructureDocument, { + sentenceId: sentence.id, + wordId: b.parts.at(0)!.startWordId, + })!.index; + const bEndWordIndex = findWordById(sentenceStructureDocument, { + sentenceId: sentence.id, + wordId: b.parts.at(-1)!.endWordId, + })!.index; + if (aStartWordIndex !== bStartWordIndex) { + return aStartWordIndex - bStartWordIndex; + } + return bEndWordIndex - aEndWordIndex; + }), + }; + }), + }; +} + +export function createSentenceStructureDocumentFromWords( + sentences: { + words: { + text: string; + whitespaceAfter: string; + }[]; + }[], +): SentenceStructureDocument { + return SentenceStructureDocumentSchema.parse({ + sentences: sentences.map((sentence, index) => ({ + id: crypto.randomUUID(), + index, + words: sentence.words.map((word, index) => ({ + id: crypto.randomUUID(), + index, + text: word.text, + whitespaceAfter: word.whitespaceAfter, + })), + sentenceStructureElements: [], + modifications: [], + coordinations: [], + })), + } satisfies SentenceStructureDocument); } -export function createSentenceStructureDataFromStringData( - string: string, -): Result<{ newSentenceStructureData: SentenceStructureData }> { - const newSentenceStructureData = - stringToSentenceStructureData.safeDecode(string); +export function createSentenceStructureDocumentFromJSONString( + jsonString: string, +): Result<{ newSentenceStructureDocument: SentenceStructureDocument }> { + const newSentenceStructureDocument = + jsonStringToSentenceStructureDocument.safeDecode(jsonString); - if (newSentenceStructureData.success) { + if (newSentenceStructureDocument.success) { return { success: true, data: { - newSentenceStructureData: newSentenceStructureData.data, + newSentenceStructureDocument: newSentenceStructureDocument.data, }, }; } const errorMessage = - newSentenceStructureData.error.issues.find( + newSentenceStructureDocument.error.issues.find( (issue) => issue.code === "custom", )?.message ?? null; if (errorMessage) { @@ -73,22 +180,22 @@ export function createSentenceStructureDataFromStringData( }; } -export function createSentenceStructureDataFromXMLData( +export function createSentenceStructureDocumentFromXMLString( xmlString: string, -): Result<{ newSentenceStructureData: SentenceStructureData }> { - const newSentenceStructureData = - xmlStringToSentenceStructureData.safeDecode(xmlString); +): Result<{ newSentenceStructureDocument: SentenceStructureDocument }> { + const newSentenceStructureDocument = + xmlStringToSentenceStructureDocument.safeDecode(xmlString); - if (newSentenceStructureData.success) { + if (newSentenceStructureDocument.success) { return { success: true, data: { - newSentenceStructureData: newSentenceStructureData.data, + newSentenceStructureDocument: newSentenceStructureDocument.data, }, }; } const errorMessage = - newSentenceStructureData.error.issues.find( + newSentenceStructureDocument.error.issues.find( (issue) => issue.code === "custom", )?.message ?? null; if (errorMessage) { @@ -103,91 +210,147 @@ export function createSentenceStructureDataFromXMLData( }; } -export function createSentenceStructureDataFromSimplifiedAnnotationData( - text: string, - simplifiedAnnotationData: SimplifiedAnnotationData, -): Result<{ newSentenceStructureData: SentenceStructureData }> { - const simplifiedSentenceStructureData: SimplifiedSentenceStructureData = { - text: text, - words: tokenizeText(text), - ...simplifiedAnnotationData, - }; - const newSentenceStructureData = - simplifiedSentenceStructureDataToSentenceStructureData.safeDecode( - simplifiedSentenceStructureData, - ); - - if (newSentenceStructureData.success) { - return { - success: true, - data: { - newSentenceStructureData: newSentenceStructureData.data, - }, - }; - } - const errorMessage = - newSentenceStructureData.error.issues.find( - (issue) => issue.code === "custom", - )?.message ?? null; - if (errorMessage) { - return { - success: false, - message: errorMessage, - }; - } - throw newSentenceStructureData.error; +export function sentenceStructureDocumentToJSONString( + sentenceStructureDocument: SentenceStructureDocument, +): string { + return jsonStringToSentenceStructureDocument.encode( + sentenceStructureDocument, + ); } -export function sentenceStructureDataToString( - sentenceStructureData: SentenceStructureData, +export function sentenceStructureDocumentToXMLString( + sentenceStructureDocument: SentenceStructureDocument, ): string { - return stringToSentenceStructureData.encode(sentenceStructureData); + return xmlStringToSentenceStructureDocument.encode(sentenceStructureDocument); } -export function sentenceStructureDataToXMLString( - sentenceStructureData: SentenceStructureData, -): string { - return xmlStringToSentenceStructureData.encode(sentenceStructureData); +export function sentenceStructureDocumentToSentenceStructureDocumentForest( + sentenceStructureDocument: SentenceStructureDocument, +): SentenceStructureDocumentForest { + return createSentenceStructureDocumentForest(sentenceStructureDocument); } -export function createSentenceElementRange( - sentenceStructureData: SentenceStructureData, - input: { - type: SentenceElementRangeType; - startWordIndex: number; - endWordIndex: number; - }, +export function sentenceStructureDocumentToSentenceStructureDecoratedDocumentForest( + sentenceStructureDocument: SentenceStructureDocument, +): SentenceStructureDecoratedDocumentForest { + return createSentenceStructureDecoratedDocumentForest( + createSentenceStructureDocumentForest(sentenceStructureDocument), + ); +} + +export function findWordById( + sentenceStructureDocument: SentenceStructureDocument, + input: { sentenceId: string; wordId: string }, +): Word | null { + return ( + sentenceStructureDocument.sentences + .find((sentence) => sentence.id === input.sentenceId) + ?.words.find((word) => word.id === input.wordId) ?? null + ); +} + +export function addSentenceStructureElement( + sentenceStructureDocument: SentenceStructureDocument, + input: + | { + sentenceId: string; + kind: "core-sentence-element"; + startWordId: string; + endWordId: string; + sentenceElementName: + | (typeof coreSentenceElementAllowedSentenceElementNameOptions)[number] + | null; + } + | { + sentenceId: string; + kind: "sentence-constituent"; + type: "phrase"; + usage: "nominal" | "adjectival" | "adverbial"; + startWordId: string; + endWordId: string; + sentenceElementName: + | (typeof sentenceConstituentTypeToAllowedSentenceElementNameOptionsMap)["phrase"][number] + | null; + } + | { + sentenceId: string; + kind: "sentence-constituent"; + type: "clause"; + usage: "nominal" | "adjectival" | "adverbial"; + startWordId: string; + endWordId: string; + sentenceElementName: + | (typeof sentenceConstituentTypeToAllowedSentenceElementNameOptionsMap)["clause"][number] + | null; + } + | { + sentenceId: string; + kind: "sentence-constituent"; + type: "adverbial-phrase"; + startWordId: string; + endWordId: string; + sentenceElementName: + | (typeof sentenceConstituentTypeToAllowedSentenceElementNameOptionsMap)["adverbial-phrase"][number] + | null; + }, ): Result<{ - newSentenceStructureData: SentenceStructureData; - rangeId: string; + newSentenceStructureDocument: SentenceStructureDocument; + sentenceStructureElementId: string; }> { - const rangeId = crypto.randomUUID(); - const newSentenceStructureData = SentenceStructureDataSchema.safeParse({ - ...sentenceStructureData, - ranges: [ - ...sentenceStructureData.ranges, - { - kind: "core-sentence-element", - type: input.type, - id: rangeId, - startWordIndex: input.startWordIndex, - endWordIndex: input.endWordIndex, - sentenceElementName: null, - }, - ], - } satisfies SentenceStructureData); + const sentenceStructureElementId = crypto.randomUUID(); + const newSentenceStructureDocument = + SentenceStructureDocumentSchema.safeParse( + normalizeSentenceStructureDocument({ + sentences: sentenceStructureDocument.sentences.map((sentence) => + sentence.id === input.sentenceId + ? { + ...sentence, + sentenceStructureElements: [ + ...sentence.sentenceStructureElements, + input.kind === "core-sentence-element" + ? ({ + kind: "core-sentence-element", + id: sentenceStructureElementId, + startWordId: input.startWordId, + endWordId: input.endWordId, + sentenceElementName: input.sentenceElementName, + } satisfies SentenceStructureElement) + : input.type === "phrase" || input.type === "clause" + ? ({ + kind: "sentence-constituent", + type: input.type, + usage: input.usage, + id: sentenceStructureElementId, + startWordId: input.startWordId, + endWordId: input.endWordId, + sentenceElementName: input.sentenceElementName, + } satisfies SentenceStructureElement) + : ({ + kind: "sentence-constituent", + type: "adverbial-phrase", + id: sentenceStructureElementId, + startWordId: input.startWordId, + endWordId: input.endWordId, + sentenceElementName: input.sentenceElementName, + } satisfies SentenceStructureElement), + ] satisfies SentenceStructureElement[], + } + : sentence, + ), + }), + ); - if (newSentenceStructureData.success) { + if (newSentenceStructureDocument.success) { return { success: true, data: { - newSentenceStructureData: newSentenceStructureData.data, - rangeId, + newSentenceStructureDocument: newSentenceStructureDocument.data, + sentenceStructureElementId: sentenceStructureElementId, }, }; } const errorMessage = - newSentenceStructureData.error.issues.find( + newSentenceStructureDocument.error.issues.find( (issue) => issue.code === "custom", )?.message ?? null; if (errorMessage) { @@ -196,47 +359,80 @@ export function createSentenceElementRange( message: errorMessage, }; } - throw newSentenceStructureData.error; + throw newSentenceStructureDocument.error; } -export function createSentenceStructureRange( - sentenceStructureData: SentenceStructureData, +export function findSentenceStructureElementById( + sentenceStructureDocument: SentenceStructureDocument, + input: { sentenceId: string; sentenceStructureElementId: string }, +): SentenceStructureElement | null { + return ( + sentenceStructureDocument.sentences + .find((sentence) => sentence.id === input.sentenceId) + ?.sentenceStructureElements.find( + (sentenceStructureElement) => + sentenceStructureElement.id === input.sentenceStructureElementId, + ) ?? null + ); +} + +export function findSentenceStructureElementByStartAndEndWordId( + sentenceStructureDocument: SentenceStructureDocument, + input: { sentenceId: string; startWordId: string; endWordId: string }, +): SentenceStructureElement | null { + return ( + sentenceStructureDocument.sentences + .find((sentence) => sentence.id === input.sentenceId) + ?.sentenceStructureElements.find( + (sentenceStructureElement) => + sentenceStructureElement.startWordId === input.startWordId && + sentenceStructureElement.endWordId === input.endWordId, + ) ?? null + ); +} + +export function updateSentenceElementName( + sentenceStructureDocument: SentenceStructureDocument, input: { - type: SentenceStructureRangeType; - startWordIndex: number; - endWordIndex: number; + sentenceId: string; + sentenceStructureElementId: string; + sentenceElementName: SentenceElementName | null; }, -): Result<{ - newSentenceStructureData: SentenceStructureData; - rangeId: string; -}> { - const rangeId = crypto.randomUUID(); - const newSentenceStructureData = SentenceStructureDataSchema.safeParse({ - ...sentenceStructureData, - ranges: [ - ...sentenceStructureData.ranges, - { - kind: "sentence-structure", - type: input.type, - id: rangeId, - startWordIndex: input.startWordIndex, - endWordIndex: input.endWordIndex, - sentenceElementName: null, - }, - ], - } satisfies SentenceStructureData); +): Result<{ newSentenceStructureDocument: SentenceStructureDocument }> { + const newSentenceStructureDocument = + SentenceStructureDocumentSchema.safeParse( + normalizeSentenceStructureDocument({ + sentences: sentenceStructureDocument.sentences.map((sentence) => + sentence.id === input.sentenceId + ? { + ...sentence, + sentenceStructureElements: + sentence.sentenceStructureElements.map( + (sentenceStructureElement) => + sentenceStructureElement.id === + input.sentenceStructureElementId + ? ({ + ...sentenceStructureElement, + sentenceElementName: input.sentenceElementName, + } as SentenceStructureElement) + : sentenceStructureElement, + ), + } + : sentence, + ), + }), + ); - if (newSentenceStructureData.success) { + if (newSentenceStructureDocument.success) { return { success: true, data: { - newSentenceStructureData: newSentenceStructureData.data, - rangeId, + newSentenceStructureDocument: newSentenceStructureDocument.data, }, }; } const errorMessage = - newSentenceStructureData.error.issues.find( + newSentenceStructureDocument.error.issues.find( (issue) => issue.code === "custom", )?.message ?? null; if (errorMessage) { @@ -245,177 +441,181 @@ export function createSentenceStructureRange( message: errorMessage, }; } - throw newSentenceStructureData.error; -} - -export function findRangeById( - sentenceStructureData: SentenceStructureData, - input: { rangeId: string }, -): Range | null { - return ( - sentenceStructureData.ranges.find((range) => range.id === input.rangeId) ?? - null - ); + throw newSentenceStructureDocument.error; } -export function findRangeByStartAndEndWordIndex( - sentenceStructureData: SentenceStructureData, - input: { startWordIndex: number; endWordIndex: number }, -): Range | null { - return ( - sentenceStructureData.ranges.find( - (range) => - range.startWordIndex === input.startWordIndex && - range.endWordIndex === input.endWordIndex, - ) ?? null +export function deleteSentenceStructureElement( + sentenceStructureDocument: SentenceStructureDocument, + input: { sentenceId: string; sentenceStructureElementId: string }, +): SentenceStructureDocument { + return SentenceStructureDocumentSchema.parse( + normalizeSentenceStructureDocument({ + sentences: sentenceStructureDocument.sentences.map((sentence) => + sentence.id === input.sentenceId + ? { + ...sentence, + sentenceStructureElements: + sentence.sentenceStructureElements.filter( + (sentenceStructureElement) => + sentenceStructureElement.id !== + input.sentenceStructureElementId, + ), + modifications: sentence.modifications.filter( + (modification) => + modification.modifierSentenceStructureElementId !== + input.sentenceStructureElementId && + modification.modifiedSentenceStructureElementId !== + input.sentenceStructureElementId, + ), + } + : sentence, + ), + }), ); } -export function updateSentenceElementName( - sentenceStructureData: SentenceStructureData, +export function addModification( + sentenceStructureDocument: SentenceStructureDocument, input: { - rangeId: string; - sentenceElementName: RangeType extends SentenceElementRangeType - ? (typeof sentenceElementRangeTypeToAllowedSentenceElementNameOptionsMap)[RangeType][number] - : RangeType extends SentenceStructureRangeType - ? (typeof sentenceStructureRangeTypeToAllowedSentenceElementNameOptionsMap)[RangeType][number] - : never; - }, -): SentenceStructureData { - return SentenceStructureDataSchema.parse({ - ...sentenceStructureData, - ranges: sentenceStructureData.ranges.map((range) => - range.id === input.rangeId - ? ({ - ...range, - sentenceElementName: input.sentenceElementName, - } as Range) - : range, - ), - } satisfies SentenceStructureData); -} - -export function deleteRange( - sentenceStructureData: SentenceStructureData, - input: { rangeId: string }, -): SentenceStructureData { - return SentenceStructureDataSchema.parse({ - ...sentenceStructureData, - ranges: sentenceStructureData.ranges.filter( - (range) => range.id !== input.rangeId, - ), - relations: sentenceStructureData.relations.filter( - (relation) => - relation.fromRangeId !== input.rangeId && - relation.toRangeId !== input.rangeId, - ), - } satisfies SentenceStructureData); -} - -export function createRelation( - sentenceStructureData: SentenceStructureData, - input: { - fromRange: { startWordIndex: number; endWordIndex: number }; - toRange: { startWordIndex: number; endWordIndex: number }; + sentenceId: string; + modifierSentenceStructureElement: { + startWordId: string; + endWordId: string; + }; + modifiedSentenceStructureElement: { + startWordId: string; + endWordId: string; + }; }, -): Result<{ newSentenceStructureData: SentenceStructureData }> { - const fromRangeResult: { - newSentenceStructureData: SentenceStructureData; - rangeId: string; +): Result<{ newSentenceStructureDocument: SentenceStructureDocument }> { + const modifierSentenceStructureElementResult: { + newSentenceStructureDocument: SentenceStructureDocument; + sentenceStructureElementId: string; } = (() => { - const existingFromRange = findRangeByStartAndEndWordIndex( - sentenceStructureData, - { - startWordIndex: input.fromRange.startWordIndex, - endWordIndex: input.fromRange.endWordIndex, - }, - ); - if (existingFromRange) { + const existingModifierSentenceStructureElement = + findSentenceStructureElementByStartAndEndWordId( + sentenceStructureDocument, + { + sentenceId: input.sentenceId, + startWordId: input.modifierSentenceStructureElement.startWordId, + endWordId: input.modifierSentenceStructureElement.endWordId, + }, + ); + if (existingModifierSentenceStructureElement) { return { - newSentenceStructureData: sentenceStructureData, - rangeId: existingFromRange.id, + newSentenceStructureDocument: sentenceStructureDocument, + sentenceStructureElementId: existingModifierSentenceStructureElement.id, }; } - const rangeId = crypto.randomUUID(); - const newSentenceStructureData: SentenceStructureData = { - ...sentenceStructureData, - ranges: [ - ...sentenceStructureData.ranges, - { - kind: "relation", - type: "relation", - id: rangeId, - startWordIndex: input.fromRange.startWordIndex, - endWordIndex: input.fromRange.endWordIndex, - }, - ], - }; + const sentenceStructureElementId = crypto.randomUUID(); return { - newSentenceStructureData, - rangeId, + newSentenceStructureDocument: { + sentences: sentenceStructureDocument.sentences.map((sentence) => + sentence.id === input.sentenceId + ? { + ...sentence, + sentenceStructureElements: [ + ...sentence.sentenceStructureElements, + { + kind: "modification-element", + id: sentenceStructureElementId, + startWordId: + input.modifierSentenceStructureElement.startWordId, + endWordId: input.modifierSentenceStructureElement.endWordId, + } satisfies SentenceStructureElement, + ], + } + : sentence, + ), + } satisfies SentenceStructureDocument, + sentenceStructureElementId: sentenceStructureElementId, }; })(); - const toRangeResult: { - newSentenceStructureData: SentenceStructureData; - rangeId: string; + const modifiedSentenceStructureElementResult: { + newSentenceStructureDocument: SentenceStructureDocument; + sentenceStructureElementId: string; } = (() => { - const existingToRange = findRangeByStartAndEndWordIndex( - fromRangeResult.newSentenceStructureData, - { - startWordIndex: input.toRange.startWordIndex, - endWordIndex: input.toRange.endWordIndex, - }, - ); - if (existingToRange) { + const existingModifiedSentenceStructureElement = + findSentenceStructureElementByStartAndEndWordId( + modifierSentenceStructureElementResult.newSentenceStructureDocument, + { + sentenceId: input.sentenceId, + startWordId: input.modifiedSentenceStructureElement.startWordId, + endWordId: input.modifiedSentenceStructureElement.endWordId, + }, + ); + if (existingModifiedSentenceStructureElement) { return { - newSentenceStructureData: fromRangeResult.newSentenceStructureData, - rangeId: existingToRange.id, + newSentenceStructureDocument: + modifierSentenceStructureElementResult.newSentenceStructureDocument, + sentenceStructureElementId: existingModifiedSentenceStructureElement.id, }; } - const rangeId = crypto.randomUUID(); - const newSentenceStructureData: SentenceStructureData = { - ...fromRangeResult.newSentenceStructureData, - ranges: [ - ...fromRangeResult.newSentenceStructureData.ranges, - { - kind: "relation", - type: "relation", - id: rangeId, - startWordIndex: input.toRange.startWordIndex, - endWordIndex: input.toRange.endWordIndex, - }, - ], - }; + const sentenceStructureElementId = crypto.randomUUID(); return { - newSentenceStructureData, - rangeId, + newSentenceStructureDocument: { + sentences: + modifierSentenceStructureElementResult.newSentenceStructureDocument.sentences.map( + (sentence) => + sentence.id === input.sentenceId + ? { + ...sentence, + sentenceStructureElements: [ + ...sentence.sentenceStructureElements, + { + kind: "modification-element", + id: sentenceStructureElementId, + startWordId: + input.modifiedSentenceStructureElement.startWordId, + endWordId: + input.modifiedSentenceStructureElement.endWordId, + } satisfies SentenceStructureElement, + ], + } + : sentence, + ), + } satisfies SentenceStructureDocument, + sentenceStructureElementId, }; })(); - const newSentenceStructureData = SentenceStructureDataSchema.safeParse({ - ...toRangeResult.newSentenceStructureData, - relations: [ - ...toRangeResult.newSentenceStructureData.relations, - { - id: crypto.randomUUID(), - fromRangeId: fromRangeResult.rangeId, - toRangeId: toRangeResult.rangeId, - }, - ], - } satisfies SentenceStructureData); - if (newSentenceStructureData.success) { + const newSentenceStructureDocument = + SentenceStructureDocumentSchema.safeParse( + normalizeSentenceStructureDocument({ + sentences: + modifiedSentenceStructureElementResult.newSentenceStructureDocument.sentences.map( + (sentence) => + sentence.id === input.sentenceId + ? { + ...sentence, + modifications: [ + ...sentence.modifications, + { + id: crypto.randomUUID(), + modifierSentenceStructureElementId: + modifierSentenceStructureElementResult.sentenceStructureElementId, + modifiedSentenceStructureElementId: + modifiedSentenceStructureElementResult.sentenceStructureElementId, + } satisfies Modification, + ], + } + : sentence, + ), + }), + ); + if (newSentenceStructureDocument.success) { return { success: true, data: { - newSentenceStructureData: newSentenceStructureData.data, + newSentenceStructureDocument: newSentenceStructureDocument.data, }, }; } const errorMessage = - newSentenceStructureData.error.issues.find( + newSentenceStructureDocument.error.issues.find( (issue) => issue.code === "custom", )?.message ?? null; if (errorMessage) { @@ -424,80 +624,124 @@ export function createRelation( message: errorMessage, }; } - throw newSentenceStructureData.error; + throw newSentenceStructureDocument.error; } -export function findRelationById( - sentenceStructureData: SentenceStructureData, - input: { relationId: string }, -): Relation | null { +export function findModificationById( + sentenceStructureDocument: SentenceStructureDocument, + input: { sentenceId: string; modificationId: string }, +): Modification | null { return ( - sentenceStructureData.relations.find( - (relation) => relation.id === input.relationId, - ) ?? null + sentenceStructureDocument.sentences + .find((sentence) => sentence.id === input.sentenceId) + ?.modifications.find( + (modification) => modification.id === input.modificationId, + ) ?? null ); } -export function deleteRelation( - sentenceStructureData: SentenceStructureData, - input: { relationId: string }, -): SentenceStructureData { - return SentenceStructureDataSchema.parse({ - ...sentenceStructureData, - ranges: sentenceStructureData.ranges.filter( - (range) => - range.kind !== "relation" || - sentenceStructureData.relations.some( - (relation) => - relation.fromRangeId !== range.id && - relation.toRangeId !== range.id, - ), - ), - relations: sentenceStructureData.relations.filter( - (relation) => relation.id !== input.relationId, - ), - } satisfies SentenceStructureData); +export function deleteModification( + sentenceStructureDocument: SentenceStructureDocument, + input: { sentenceId: string; modificationId: string }, +): SentenceStructureDocument { + const newModifications = + sentenceStructureDocument.sentences + .find((sentence) => sentence.id === input.sentenceId) + ?.modifications.filter( + (modification) => modification.id !== input.modificationId, + ) ?? null; + if (newModifications === null) { + return sentenceStructureDocument; + } + return SentenceStructureDocumentSchema.parse( + normalizeSentenceStructureDocument({ + sentences: sentenceStructureDocument.sentences.map((sentence) => + sentence.id === input.sentenceId + ? { + ...sentence, + sentenceStructureElements: + sentence.sentenceStructureElements.filter( + (sentenceStructureElement) => + sentenceStructureElement.kind !== "modification-element" || + newModifications.some( + (modification) => + modification.modifierSentenceStructureElementId === + sentenceStructureElement.id || + modification.modifiedSentenceStructureElementId === + sentenceStructureElement.id, + ), + ), + modifications: newModifications, + } + : sentence, + ), + }), + ); } -export function createCoordination( - sentenceStructureData: SentenceStructureData, +export function addCoordination( + sentenceStructureDocument: SentenceStructureDocument, input: { - children: { - type: CoordinationChildType; - startWordIndex: number; - endWordIndex: number; + sentenceId: string; + coordinationParts: { + type: CoordinationPartType; + startWordId: string; + endWordId: string; }[]; }, ): Result<{ - newSentenceStructureData: SentenceStructureData; + newSentenceStructureDocument: SentenceStructureDocument; }> { - const newSentenceStructureData = SentenceStructureDataSchema.safeParse({ - ...sentenceStructureData, - coordinations: [ - ...sentenceStructureData.coordinations, - { - id: crypto.randomUUID(), - children: input.children - .sort((a, b) => a.startWordIndex - b.startWordIndex) - .map((child, index) => ({ - type: child.type, - index, - startWordIndex: child.startWordIndex, - endWordIndex: child.endWordIndex, - })), - }, - ], - } satisfies SentenceStructureData); - if (newSentenceStructureData.success) { + const newSentenceStructureDocument = + SentenceStructureDocumentSchema.safeParse( + normalizeSentenceStructureDocument({ + sentences: sentenceStructureDocument.sentences.map((sentence) => + sentence.id === input.sentenceId + ? { + ...sentence, + coordinations: [ + ...sentence.coordinations, + { + id: crypto.randomUUID(), + parts: input.coordinationParts + .toSorted( + (a, b) => + (findWordById(sentenceStructureDocument, { + sentenceId: sentence.id, + wordId: a.startWordId, + })?.index ?? -1) - + (findWordById(sentenceStructureDocument, { + sentenceId: sentence.id, + wordId: b.startWordId, + })?.index ?? -1), + ) + .map( + (part, index) => + ({ + type: part.type, + id: crypto.randomUUID(), + index, + startWordId: part.startWordId, + endWordId: part.endWordId, + }) satisfies CoordinationPart, + ), + } satisfies Coordination, + ], + } + : sentence, + ), + }), + ); + if (newSentenceStructureDocument.success) { return { success: true, data: { - newSentenceStructureData: newSentenceStructureData.data, + newSentenceStructureDocument: newSentenceStructureDocument.data, }, }; } const errorMessage = - newSentenceStructureData.error.issues.find( + newSentenceStructureDocument.error.issues.find( (issue) => issue.code === "custom", )?.message ?? null; if (errorMessage) { @@ -506,41 +750,53 @@ export function createCoordination( message: errorMessage, }; } - throw newSentenceStructureData.error; + throw newSentenceStructureDocument.error; } export function findCoordinationById( - sentenceStructureData: SentenceStructureData, - input: { coordinationId: string }, + sentenceStructureDocument: SentenceStructureDocument, + input: { sentenceId: string; coordinationId: string }, ): Coordination | null { return ( - sentenceStructureData.coordinations.find( - (coordination) => coordination.id === input.coordinationId, - ) ?? null + sentenceStructureDocument.sentences + .find((sentence) => sentence.id === input.sentenceId) + ?.coordinations.find( + (coordination) => coordination.id === input.coordinationId, + ) ?? null ); } -export function findCoordinationByStartAndEndWordIndex( - sentenceStructureData: SentenceStructureData, - input: { startWordIndex: number; endWordIndex: number }, +export function findCoordinationByStartAndEndWordId( + sentenceStructureDocument: SentenceStructureDocument, + input: { sentenceId: string; startWordId: string; endWordId: string }, ): Coordination | null { return ( - sentenceStructureData.coordinations.find( - (coordination) => - coordination.children.at(0)!.startWordIndex === input.startWordIndex && - coordination.children.at(-1)!.endWordIndex === input.endWordIndex, - ) ?? null + sentenceStructureDocument.sentences + .find((sentence) => sentence.id === input.sentenceId) + ?.coordinations.find( + (coordination) => + coordination.parts.at(0)!.startWordId === input.startWordId && + coordination.parts.at(-1)!.endWordId === input.endWordId, + ) ?? null ); } export function deleteCoordination( - sentenceStructureData: SentenceStructureData, - input: { coordinationId: string }, -): SentenceStructureData { - return SentenceStructureDataSchema.parse({ - ...sentenceStructureData, - coordinations: sentenceStructureData.coordinations.filter( - (coordination) => coordination.id !== input.coordinationId, - ), - } satisfies SentenceStructureData); + sentenceStructureDocument: SentenceStructureDocument, + input: { sentenceId: string; coordinationId: string }, +): SentenceStructureDocument { + return SentenceStructureDocumentSchema.parse( + normalizeSentenceStructureDocument({ + sentences: sentenceStructureDocument.sentences.map((sentence) => + sentence.id === input.sentenceId + ? { + ...sentence, + coordinations: sentence.coordinations.filter( + (coordination) => coordination.id !== input.coordinationId, + ), + } + : sentence, + ), + }), + ); } diff --git a/packages/sentence-structure-data/package.json b/packages/sentence-structure-data/package.json index ecedf55..6bdb48f 100644 --- a/packages/sentence-structure-data/package.json +++ b/packages/sentence-structure-data/package.json @@ -9,9 +9,8 @@ "clean": "rm -r dist" }, "dependencies": { - "compromise": "^14.14.4", - "fast-xml-parser": "^5.3.2", - "zod": "^4.1.13" + "fast-xml-parser": "^5.3.3", + "zod": "^4.3.5" }, "devDependencies": { "typescript": "^5.9.3" diff --git a/packages/sentence-structure-data/schema.ts b/packages/sentence-structure-data/schema.ts index bf869d8..b674ab4 100644 --- a/packages/sentence-structure-data/schema.ts +++ b/packages/sentence-structure-data/schema.ts @@ -1,353 +1,499 @@ import * as z from "zod"; const WordSchema = z.object({ + id: z.uuid(), index: z.int().nonnegative(), text: z.string(), + whitespaceAfter: z.string(), }); export type Word = z.infer; export const sentenceElementNameOptions = ["S", "V", "O", "C", "M"] as const; export type SentenceElementName = (typeof sentenceElementNameOptions)[number]; -export const sentenceElementRangeTypeOptions = [ - "core-sentence-element", -] as const; -export type SentenceElementRangeType = - (typeof sentenceElementRangeTypeOptions)[number]; -export const sentenceElementRangeTypeToAllowedSentenceElementNameOptionsMap = { - "core-sentence-element": ["S", "V", "O", "C"] as const, -} satisfies Record; +export const coreSentenceElementAllowedSentenceElementNameOptions = [ + "S", + "V", + "O", + "C", +] as const satisfies readonly SentenceElementName[]; -export const sentenceStructureRangeTypeOptions = [ - "modifier", +const sentenceConstituentTypeOptions = [ "phrase", "clause", + "adverbial-phrase", ] as const; -export type SentenceStructureRangeType = - (typeof sentenceStructureRangeTypeOptions)[number]; -export const sentenceStructureRangeTypeToAllowedSentenceElementNameOptionsMap = - { - modifier: ["M"] as const, - phrase: ["S", "O", "C", "M"] as const, - clause: ["S", "O", "C", "M"] as const, - } satisfies Record; - -const relationRangeTypeOption = "relation" as const; -export type RelationRangeType = typeof relationRangeTypeOption; +export type SentenceConstituentType = + (typeof sentenceConstituentTypeOptions)[number]; +export const sentenceConstituentTypeToAllowedSentenceElementNameOptionsMap = { + phrase: ["S", "O", "C", "M"] as const, + clause: ["S", "O", "C", "M"] as const, + "adverbial-phrase": ["M"] as const, +} satisfies Record; -export type RangeType = - | SentenceElementRangeType - | SentenceStructureRangeType - | RelationRangeType; - -const RangeSchema = z.union([ - ...sentenceElementRangeTypeOptions.map( - (sentenceElementRangeTypeOption) => - ({ - "core-sentence-element": z.object({ - kind: z.literal("core-sentence-element"), - type: z.literal("core-sentence-element"), - id: z.uuid(), - startWordIndex: z.int().nonnegative(), - endWordIndex: z.int().nonnegative(), - sentenceElementName: z.nullable( - z.literal( - sentenceElementRangeTypeToAllowedSentenceElementNameOptionsMap[ - "core-sentence-element" - ], - ), - ), - }), - })[sentenceElementRangeTypeOption], - ), - ...sentenceStructureRangeTypeOptions.map( - (sentenceStructureRangeTypeOption) => - ({ - modifier: z.object({ - kind: z.literal("sentence-structure"), - type: z.literal("modifier"), - id: z.uuid(), - startWordIndex: z.int().nonnegative(), - endWordIndex: z.int().nonnegative(), - sentenceElementName: z.nullable( - z.literal( - sentenceStructureRangeTypeToAllowedSentenceElementNameOptionsMap[ - "modifier" - ], - ), - ), - }), - phrase: z.object({ - kind: z.literal("sentence-structure"), - type: z.literal("phrase"), - id: z.uuid(), - startWordIndex: z.int().nonnegative(), - endWordIndex: z.int().nonnegative(), - sentenceElementName: z.nullable( - z.literal( - sentenceStructureRangeTypeToAllowedSentenceElementNameOptionsMap[ - "phrase" - ], - ), - ), - }), - clause: z.object({ - kind: z.literal("sentence-structure"), - type: z.literal("clause"), - id: z.uuid(), - startWordIndex: z.int().nonnegative(), - endWordIndex: z.int().nonnegative(), - sentenceElementName: z.nullable( - z.literal( - sentenceStructureRangeTypeToAllowedSentenceElementNameOptionsMap[ - "clause" - ], - ), - ), - }), - })[sentenceStructureRangeTypeOption], - ), +const SentenceStructureElementSchema = z.union([ + z.object({ + kind: z.literal("core-sentence-element"), + id: z.uuid(), + startWordId: z.uuid(), + endWordId: z.uuid(), + sentenceElementName: z.nullable( + z.literal(coreSentenceElementAllowedSentenceElementNameOptions), + ), + }), + z.object({ + kind: z.literal("sentence-constituent"), + type: z.literal("phrase"), + usage: z.literal(["nominal", "adjectival", "adverbial"]), + id: z.uuid(), + startWordId: z.uuid(), + endWordId: z.uuid(), + sentenceElementName: z.nullable( + z.literal( + sentenceConstituentTypeToAllowedSentenceElementNameOptionsMap["phrase"], + ), + ), + }), + z.object({ + kind: z.literal("sentence-constituent"), + type: z.literal("clause"), + usage: z.literal(["nominal", "adjectival", "adverbial"]), + id: z.uuid(), + startWordId: z.uuid(), + endWordId: z.uuid(), + sentenceElementName: z.nullable( + z.literal( + sentenceConstituentTypeToAllowedSentenceElementNameOptionsMap["clause"], + ), + ), + }), + z.object({ + kind: z.literal("sentence-constituent"), + type: z.literal("adverbial-phrase"), + id: z.uuid(), + startWordId: z.uuid(), + endWordId: z.uuid(), + sentenceElementName: z.nullable( + z.literal( + sentenceConstituentTypeToAllowedSentenceElementNameOptionsMap[ + "adverbial-phrase" + ], + ), + ), + }), z.object({ - kind: z.literal("relation"), - type: z.literal("relation"), + kind: z.literal("modification-element"), id: z.uuid(), - startWordIndex: z.int().nonnegative(), - endWordIndex: z.int().nonnegative(), + startWordId: z.uuid(), + endWordId: z.uuid(), }), ]); -export type Range = z.infer; +export type SentenceStructureElement = z.infer< + typeof SentenceStructureElementSchema +>; -const RelationSchema = z.object({ +const ModificationSchema = z.object({ id: z.uuid(), - fromRangeId: z.uuid(), - toRangeId: z.uuid(), + modifierSentenceStructureElementId: z.uuid(), + modifiedSentenceStructureElementId: z.uuid(), }); -export type Relation = z.infer; +export type Modification = z.infer; -export const coordinationChildTypeOptions = [ - "coordinating conjunction", - "correlative conjunction", +const coordinationPartTypeOptions = [ + "coordinator", + "correlative", "conjunct", ] as const; -export type CoordinationChildType = - (typeof coordinationChildTypeOptions)[number]; +export type CoordinationPartType = (typeof coordinationPartTypeOptions)[number]; -const CoordinationChildSchema = z.object({ - type: z.literal(coordinationChildTypeOptions), +const CoordinationPartSchema = z.object({ + type: z.literal(coordinationPartTypeOptions), + id: z.uuid(), index: z.int().nonnegative(), - startWordIndex: z.int().nonnegative(), - endWordIndex: z.int().nonnegative(), + startWordId: z.uuid(), + endWordId: z.uuid(), }); -export type CoordinationChild = z.infer; +export type CoordinationPart = z.infer; const CoordinationSchema = z.object({ id: z.uuid(), - children: z.array(CoordinationChildSchema), + parts: z.array(CoordinationPartSchema), }); export type Coordination = z.infer; -export const SentenceStructureDataSchema = z +const SentenceSchema = z .object({ - text: z.string(), + id: z.uuid(), + index: z.int().nonnegative(), words: z.array(WordSchema), - ranges: z.array(RangeSchema), - relations: z.array(RelationSchema), + sentenceStructureElements: z.array(SentenceStructureElementSchema), + modifications: z.array(ModificationSchema), coordinations: z.array(CoordinationSchema), }) .refine( - (sentenceStructureData) => - sentenceStructureData.words.every((word, index) => word.index === index), - { error: "単語のインデックスが0から始まる連続した整数ではありません。" }, + (sentence) => sentence.words.every((word, index) => word.index === index), + { + error: "単語のインデックスが0から始まる連続した整数ではありません。", + }, ) .refine( - (sentenceStructureData) => - sentenceStructureData.ranges.every( - (range) => - range.startWordIndex <= range.endWordIndex && - range.endWordIndex < sentenceStructureData.words.length, + (sentence) => + sentence.sentenceStructureElements.every( + (sentenceStructureElement) => + sentence.words.some( + (word) => word.id === sentenceStructureElement.startWordId, + ) && + sentence.words.some( + (word) => word.id === sentenceStructureElement.endWordId, + ), ), - { error: "範囲を示す単語のインデックスが不正な値です。" }, + { error: "文構造要素が存在しない単語を参照しています。" }, ) .refine( - (sentenceStructureData) => - sentenceStructureData.ranges.every((range) => - sentenceStructureData.ranges.every( - (otherRange) => - !( - range.id !== otherRange.id && - range.startWordIndex === otherRange.startWordIndex && - range.endWordIndex === otherRange.endWordIndex - ), - ), - ), + (sentence) => { + const wordIdToWordIndexMap = new Map( + sentence.words.map((word) => [word.id, word.index]), + ); + return sentence.sentenceStructureElements.every( + (sentenceStructureElement) => { + const startWordIndex = wordIdToWordIndexMap.get( + sentenceStructureElement.startWordId, + )!; + const endWordIndex = wordIdToWordIndexMap.get( + sentenceStructureElement.endWordId, + )!; + return startWordIndex <= endWordIndex; + }, + ); + }, + { error: "文構造要素の開始・終了位置が不正な値です。" }, + ) + .refine( + (sentence) => { + const wordIdToWordIndexMap = new Map( + sentence.words.map((word) => [word.id, word.index]), + ); + return sentence.sentenceStructureElements.every( + (sentenceStructureElement) => + sentence.sentenceStructureElements + .filter( + (otherSentenceStructureElement) => + otherSentenceStructureElement.id !== + sentenceStructureElement.id, + ) + .every((otherSentenceStructureElement) => { + const sentenceStructureElementStartWordIndex = + wordIdToWordIndexMap.get(sentenceStructureElement.startWordId)!; + const sentenceStructureElementEndWordIndex = + wordIdToWordIndexMap.get(sentenceStructureElement.endWordId)!; + const otherSentenceStructureElementStartWordIndex = + wordIdToWordIndexMap.get( + otherSentenceStructureElement.startWordId, + )!; + const otherSentenceStructureElementEndWordIndex = + wordIdToWordIndexMap.get( + otherSentenceStructureElement.endWordId, + )!; + return !( + sentenceStructureElementStartWordIndex === + otherSentenceStructureElementStartWordIndex && + sentenceStructureElementEndWordIndex === + otherSentenceStructureElementEndWordIndex + ); + }), + ); + }, { - error: "開始位置と終了位置が同じ範囲が複数存在します。", + error: "開始・終了位置が同じ文構造要素が複数存在します。", }, ) .refine( - (sentenceStructureData) => - sentenceStructureData.ranges.every((range) => - sentenceStructureData.ranges.every( - (otherRange) => - range.id === otherRange.id || - otherRange.endWordIndex < range.startWordIndex || - (range.startWordIndex <= otherRange.startWordIndex && - otherRange.endWordIndex <= range.endWordIndex) || - (otherRange.startWordIndex <= range.startWordIndex && - range.endWordIndex <= otherRange.endWordIndex) || - range.endWordIndex < otherRange.startWordIndex, - ), - ), - { error: "範囲が部分的に重なっています。" }, + (sentence) => { + const wordIdToWordIndexMap = new Map( + sentence.words.map((word) => [word.id, word.index]), + ); + return sentence.sentenceStructureElements.every( + (sentenceStructureElement) => + sentence.sentenceStructureElements + .filter( + (otherSentenceStructureElement) => + otherSentenceStructureElement.id !== + sentenceStructureElement.id, + ) + .every((otherSentenceStructureElement) => { + const sentenceStructureElementStartWordIndex = + wordIdToWordIndexMap.get(sentenceStructureElement.startWordId)!; + const sentenceStructureElementEndWordIndex = + wordIdToWordIndexMap.get(sentenceStructureElement.endWordId)!; + const otherSentenceStructureElementStartWordIndex = + wordIdToWordIndexMap.get( + otherSentenceStructureElement.startWordId, + )!; + const otherSentenceStructureElementEndWordIndex = + wordIdToWordIndexMap.get( + otherSentenceStructureElement.endWordId, + )!; + return ( + otherSentenceStructureElementEndWordIndex < + sentenceStructureElementStartWordIndex || + (sentenceStructureElementStartWordIndex <= + otherSentenceStructureElementStartWordIndex && + otherSentenceStructureElementEndWordIndex <= + sentenceStructureElementEndWordIndex) || + (otherSentenceStructureElementStartWordIndex <= + sentenceStructureElementStartWordIndex && + sentenceStructureElementEndWordIndex <= + otherSentenceStructureElementEndWordIndex) || + sentenceStructureElementEndWordIndex < + otherSentenceStructureElementStartWordIndex + ); + }), + ); + }, + { error: "文構造要素が交差しています。" }, ) .refine( - (sentenceStructureData) => - sentenceStructureData.ranges - .filter((range) => range.kind === "relation") - .every((relationRange) => - sentenceStructureData.relations.some( - (relation) => - relation.fromRangeId === relationRange.id || - relation.toRangeId === relationRange.id, + (sentence) => + sentence.sentenceStructureElements + .filter( + (sentenceStructureElement) => + sentenceStructureElement.kind === "modification-element", + ) + .every((modificationSentenceStructureElement) => + sentence.modifications.some( + (modification) => + modification.modifierSentenceStructureElementId === + modificationSentenceStructureElement.id || + modification.modifiedSentenceStructureElementId === + modificationSentenceStructureElement.id, ), ), - { error: "範囲(関係)が関係の始点または終点として使われていません。" }, + { + error: + "修飾関係の要素が修飾関係の修飾要素・被修飾要素として参照されていません。", + }, ) .refine( - (sentenceStructureData) => - sentenceStructureData.relations.every( - (relation) => - sentenceStructureData.ranges.some( - (range) => range.id === relation.fromRangeId, + (sentence) => + sentence.modifications.every( + (modification) => + sentence.sentenceStructureElements.some( + (sentenceStructureElement) => + sentenceStructureElement.id === + modification.modifierSentenceStructureElementId, ) && - sentenceStructureData.ranges.some( - (range) => range.id === relation.toRangeId, + sentence.sentenceStructureElements.some( + (sentenceStructureElement) => + sentenceStructureElement.id === + modification.modifiedSentenceStructureElementId, ), ), - { error: "関係が存在しない範囲を参照しています。" }, + { error: "修飾関係が存在しない文構造要素を参照しています。" }, ) .refine( - (sentenceStructureData) => - sentenceStructureData.relations.every((relation) => - sentenceStructureData.relations.every( - (otherRelation) => - !( - relation.id !== otherRelation.id && - relation.fromRangeId === otherRelation.fromRangeId && - relation.toRangeId === otherRelation.toRangeId - ), - ), + (sentence) => + sentence.modifications.every((modification) => + sentence.modifications + .filter( + (otherModification) => otherModification.id !== modification.id, + ) + .every( + (otherModification) => + !( + otherModification.modifierSentenceStructureElementId === + modification.modifierSentenceStructureElementId && + otherModification.modifiedSentenceStructureElementId === + modification.modifiedSentenceStructureElementId + ), + ), ), { - error: "同じ始点と終点を持つ関係が複数存在します。", + error: "修飾要素・被修飾要素が同じ修飾関係が複数存在します。", }, ) .refine( - (sentenceStructureData) => - sentenceStructureData.coordinations.every((coordination) => - coordination.children.every((child, index) => child.index === index), + (sentence) => + sentence.coordinations.every( + (coordination) => 2 <= coordination.parts.length, + ), + { error: "並列関係の構成要素は2つ以上必要です。" }, + ) + .refine( + (sentence) => + sentence.coordinations.every((coordination) => + coordination.parts.every((part, index) => part.index === index), ), { error: - "並列構造の子要素のインデックスが0から始まる連続した整数ではありません。", + "並列関係の構成要素のインデックスが0から始まる連続した整数ではありません。", }, ) .refine( - (sentenceStructureData) => - sentenceStructureData.coordinations.every( - (coordination) => 3 <= coordination.children.length, + (sentence) => + sentence.coordinations.every((coordination) => + coordination.parts.every( + (part) => + sentence.words.some((word) => word.id === part.startWordId) && + sentence.words.some((word) => word.id === part.endWordId), + ), ), - { error: "並列構造の子要素は3つ以上必要です。" }, + { error: "並列関係の構成要素が存在しない単語を参照しています。" }, ) .refine( - (sentenceStructureData) => - sentenceStructureData.coordinations.every((coordination) => - coordination.children.every( - (child) => - child.startWordIndex <= child.endWordIndex && - child.endWordIndex < sentenceStructureData.words.length, - ), - ), - { error: "並列構造の子要素の単語のインデックスが不正な値です。" }, + (sentence) => { + const wordIdToWordIndexMap = new Map( + sentence.words.map((word) => [word.id, word.index]), + ); + return sentence.coordinations.every((coordination) => + coordination.parts.every((part) => { + const startWordIndex = wordIdToWordIndexMap.get(part.startWordId)!; + const endWordIndex = wordIdToWordIndexMap.get(part.endWordId)!; + return startWordIndex <= endWordIndex; + }), + ); + }, + { error: "並列関係の構成要素の開始・終了位置が不正な値です。" }, ) .refine( - (sentenceStructureData) => - sentenceStructureData.coordinations.every((coordination) => { - for (let i = 1; i < coordination.children.length; i++) { - if ( - coordination.children[i - 1]!.endWordIndex + 1 !== - coordination.children[i]!.startWordIndex - ) { - return false; + (sentence) => { + const wordIdToWordIndexMap = new Map( + sentence.words.map((word) => [word.id, word.index]), + ); + return sentence.coordinations.every((coordination) => + coordination.parts.every((part, index) => { + if (index === 0) { + return true; } - } - return true; - }), - { error: "並列構造の子要素が連続していません。" }, + + const previousPartEndWordIndex = wordIdToWordIndexMap.get( + coordination.parts.at(index - 1)!.endWordId, + )!; + const currentPartStartWordIndex = wordIdToWordIndexMap.get( + part.startWordId, + )!; + return previousPartEndWordIndex + 1 === currentPartStartWordIndex; + }), + ); + }, + { error: "並列関係の構成要素が連続していません。" }, ) .refine( - (sentenceStructureData) => - sentenceStructureData.coordinations.every((coordination) => - coordination.children.every((child) => - sentenceStructureData.ranges.every( - (range) => - range.endWordIndex < child.startWordIndex || - (child.startWordIndex <= range.startWordIndex && - range.endWordIndex <= child.endWordIndex) || - (range.startWordIndex <= child.startWordIndex && - child.endWordIndex <= range.endWordIndex) || - child.endWordIndex < range.startWordIndex, + (sentence) => { + const wordIdToWordIndexMap = new Map( + sentence.words.map((word) => [word.id, word.index]), + ); + return sentence.coordinations.every((coordination) => + coordination.parts.every((part) => + sentence.sentenceStructureElements.every( + (sentenceStructureElement) => { + const sentenceStructureElementStartWordIndex = + wordIdToWordIndexMap.get(sentenceStructureElement.startWordId)!; + const sentenceStructureElementEndWordIndex = + wordIdToWordIndexMap.get(sentenceStructureElement.endWordId)!; + const partStartWordIndex = wordIdToWordIndexMap.get( + part.startWordId, + )!; + const partEndWordIndex = wordIdToWordIndexMap.get( + part.endWordId, + )!; + return ( + sentenceStructureElementEndWordIndex < partStartWordIndex || + (partStartWordIndex <= sentenceStructureElementStartWordIndex && + sentenceStructureElementEndWordIndex <= partEndWordIndex) || + (sentenceStructureElementStartWordIndex <= partStartWordIndex && + partEndWordIndex <= sentenceStructureElementEndWordIndex) || + partEndWordIndex < sentenceStructureElementStartWordIndex + ); + }, ), ), - ), - { error: "並列構造の子要素が範囲と部分的に重なっています。" }, + ); + }, + { error: "並列関係の構成要素が文構造要素と交差しています。" }, ) .refine( - (sentenceStructureData) => - sentenceStructureData.coordinations.every((coordination) => - sentenceStructureData.coordinations.every((otherCoordination) => { - if (coordination.id === otherCoordination.id) return true; - const coordinationStart = coordination.children.at(0)!.startWordIndex; - const coordinationEnd = coordination.children.at(-1)!.endWordIndex; - const otherCoordinationStart = - otherCoordination.children.at(0)!.startWordIndex; - const otherCoordinationEnd = - otherCoordination.children.at(-1)!.endWordIndex; - return !( - coordinationStart === otherCoordinationStart && - coordinationEnd === otherCoordinationEnd - ); - }), - ), - { error: "開始位置と終了位置が同じ並列構造が複数存在します。" }, + (sentence) => { + const wordIdToWordIndexMap = new Map( + sentence.words.map((word) => [word.id, word.index]), + ); + return sentence.coordinations.every((coordination) => + sentence.coordinations + .filter( + (otherCoordination) => otherCoordination.id !== coordination.id, + ) + .every((otherCoordination) => { + const coordinationStartWordIndex = wordIdToWordIndexMap.get( + coordination.parts.at(0)!.startWordId, + )!; + const coordinationEndWordIndex = wordIdToWordIndexMap.get( + coordination.parts.at(-1)!.endWordId, + )!; + const otherCoordinationStartWordIndex = wordIdToWordIndexMap.get( + otherCoordination.parts.at(0)!.startWordId, + )!; + const otherCoordinationEndWordIndex = wordIdToWordIndexMap.get( + otherCoordination.parts.at(-1)!.endWordId, + )!; + return !( + otherCoordinationStartWordIndex === coordinationStartWordIndex && + otherCoordinationEndWordIndex === coordinationEndWordIndex + ); + }), + ); + }, + { error: "開始・終了位置が同じ並列関係が複数存在します。" }, ) .refine( - (sentenceStructureData) => - sentenceStructureData.coordinations.every((coordination) => - sentenceStructureData.coordinations.every((otherCoordination) => { - if (coordination.id === otherCoordination.id) return true; - const coordinationStart = coordination.children.at(0)!.startWordIndex; - const coordinationEnd = coordination.children.at(-1)!.endWordIndex; - const otherCoordinationStart = - otherCoordination.children.at(0)!.startWordIndex; - const otherCoordinationEnd = - otherCoordination.children.at(-1)!.endWordIndex; - if ( - coordinationEnd - coordinationStart < - otherCoordinationEnd - otherCoordinationStart + (sentence) => { + const wordIdToWordIndexMap = new Map( + sentence.words.map((word) => [word.id, word.index]), + ); + return sentence.coordinations.every((coordination) => + sentence.coordinations + .filter( + (otherCoordination) => otherCoordination.id !== coordination.id, ) - return true; - return ( - otherCoordinationEnd < coordinationStart || - coordination.children.some( - (child) => - child.startWordIndex <= otherCoordinationStart && - otherCoordinationEnd <= child.endWordIndex, - ) || - coordinationEnd < otherCoordinationStart - ); - }), - ), - { error: "並列構造が部分的に重なっています。" }, + .every((otherCoordination) => { + const coordinationStartWordIndex = wordIdToWordIndexMap.get( + coordination.parts.at(0)!.startWordId, + )!; + const coordinationEndWordIndex = wordIdToWordIndexMap.get( + coordination.parts.at(-1)!.endWordId, + )!; + const otherCoordinationStartWordIndex = wordIdToWordIndexMap.get( + otherCoordination.parts.at(0)!.startWordId, + )!; + const otherCoordinationEndWordIndex = wordIdToWordIndexMap.get( + otherCoordination.parts.at(-1)!.endWordId, + )!; + return ( + otherCoordinationEndWordIndex < coordinationStartWordIndex || + coordination.parts.some( + (part) => + wordIdToWordIndexMap.get(part.startWordId)! <= + otherCoordinationStartWordIndex && + otherCoordinationEndWordIndex <= + wordIdToWordIndexMap.get(part.endWordId)!, + ) || + otherCoordination.parts.some( + (part) => + wordIdToWordIndexMap.get(part.startWordId)! <= + coordinationStartWordIndex && + coordinationEndWordIndex <= + wordIdToWordIndexMap.get(part.endWordId)!, + ) || + coordinationEndWordIndex < otherCoordinationStartWordIndex + ); + }), + ); + }, + { error: "並列関係が交差しています。" }, ); -export type SentenceStructureData = z.infer; +export type Sentence = z.infer; + +export const SentenceStructureDocumentSchema = z.object({ + sentences: z.array(SentenceSchema), +}); +export type SentenceStructureDocument = z.infer< + typeof SentenceStructureDocumentSchema +>; diff --git a/packages/sentence-structure-data/simplified-schema.ts b/packages/sentence-structure-data/simplified-schema.ts deleted file mode 100644 index f7b3156..0000000 --- a/packages/sentence-structure-data/simplified-schema.ts +++ /dev/null @@ -1,219 +0,0 @@ -import * as z from "zod"; -import { - sentenceElementRangeTypeOptions, - sentenceElementRangeTypeToAllowedSentenceElementNameOptionsMap, - sentenceStructureRangeTypeOptions, - sentenceStructureRangeTypeToAllowedSentenceElementNameOptionsMap, -} from "./schema.js"; - -export const SimplifiedAnnotationDataSchema = z.object({ - ranges: z.array( - z - .union([ - ...sentenceElementRangeTypeOptions.map( - (sentenceElementRangeTypeOption) => - ({ - "core-sentence-element": z - .object({ - type: z.literal("文の主要素").describe("範囲の種類"), - index: z - .int() - .nonnegative() - .describe("範囲を識別するためのインデックス"), - startWordIndex: z - .int() - .nonnegative() - .describe("範囲に含まれる先頭の単語のインデックス"), - endWordIndex: z - .int() - .nonnegative() - .describe("範囲に含まれる末尾の単語のインデックス"), - sentenceElementName: z - .nullable( - z.literal( - sentenceElementRangeTypeToAllowedSentenceElementNameOptionsMap[ - "core-sentence-element" - ], - ), - ) - .describe("文の要素の名前"), - }) - .describe("文の主要素の範囲"), - })[sentenceElementRangeTypeOption], - ), - ...sentenceStructureRangeTypeOptions.map( - (sentenceStructureRangeTypeOption) => - ({ - modifier: z - .object({ - type: z.literal("修飾語").describe("範囲の種類"), - index: z - .int() - .nonnegative() - .describe("範囲を識別するためのインデックス"), - startWordIndex: z - .int() - .nonnegative() - .describe("範囲に含まれる先頭の単語のインデックス"), - endWordIndex: z - .int() - .nonnegative() - .describe("範囲に含まれる末尾の単語のインデックス"), - sentenceElementName: z - .nullable( - z.literal( - sentenceStructureRangeTypeToAllowedSentenceElementNameOptionsMap[ - "modifier" - ], - ), - ) - .describe("文の要素の名前"), - }) - .describe("修飾語の範囲"), - phrase: z - .object({ - type: z.literal("句").describe("範囲の種類"), - index: z - .int() - .nonnegative() - .describe("範囲を識別するためのインデックス"), - startWordIndex: z - .int() - .nonnegative() - .describe("範囲に含まれる先頭の単語のインデックス"), - endWordIndex: z - .int() - .nonnegative() - .describe("範囲に含まれる末尾の単語のインデックス"), - sentenceElementName: z - .nullable( - z.literal( - sentenceStructureRangeTypeToAllowedSentenceElementNameOptionsMap[ - "phrase" - ], - ), - ) - .describe("文の要素の名前"), - }) - .describe("句の範囲"), - clause: z - .object({ - type: z.literal("節").describe("範囲の種類"), - index: z - .int() - .nonnegative() - .describe("範囲を識別するためのインデックス"), - startWordIndex: z - .int() - .nonnegative() - .describe("範囲に含まれる先頭の単語のインデックス"), - endWordIndex: z - .int() - .nonnegative() - .describe("範囲に含まれる末尾の単語のインデックス"), - sentenceElementName: z - .nullable( - z.literal( - sentenceStructureRangeTypeToAllowedSentenceElementNameOptionsMap[ - "clause" - ], - ), - ) - .describe("文の要素の名前"), - }) - .describe("節の範囲"), - })[sentenceStructureRangeTypeOption], - ), - z - .object({ - type: z.literal("関係").describe("範囲の種類"), - index: z - .int() - .nonnegative() - .describe("範囲を識別するためのインデックス"), - startWordIndex: z - .int() - .nonnegative() - .describe("範囲に含まれる先頭の単語のインデックス"), - endWordIndex: z - .int() - .nonnegative() - .describe("範囲に含まれる末尾の単語のインデックス"), - }) - .describe("関係の範囲"), - ]) - .describe("範囲"), - ), - relations: z.array( - z - .object({ - fromRangeIndex: z - .int() - .nonnegative() - .describe("関係の始点となる範囲のインデックス"), - toRangeIndex: z - .int() - .nonnegative() - .describe("関係の終点となる範囲のインデックス"), - }) - .describe("関係"), - ), - coordinations: z.array( - z - .object({ - children: z.array( - z - .object({ - type: z - .literal(["等位接続詞", "相関接続詞", "並列要素"]) - .describe("子要素の種類"), - startWordIndex: z - .int() - .nonnegative() - .describe("子要素に含まれる先頭の単語のインデックス"), - endWordIndex: z - .int() - .nonnegative() - .describe("子要素に含まれる末尾の単語のインデックス"), - }) - .describe("子要素"), - ), - }) - .describe("並列構造"), - ), -}); -export type SimplifiedAnnotationData = z.infer< - typeof SimplifiedAnnotationDataSchema ->; - -export const SimplifiedSentenceStructureDataSchema = - SimplifiedAnnotationDataSchema.extend({ - text: z.string(), - words: z.array( - z.object({ - index: z.int().nonnegative(), - text: z.string(), - }), - ), - }) - .refine( - (simplifiedSentenceStructureData) => - simplifiedSentenceStructureData.ranges - .sort((a, b) => a.index - b.index) - .every((range, index) => range.index === index), - { error: "範囲のインデックスが0から始まる連続した整数ではありません。" }, - ) - .refine( - (simplifiedSentenceStructureData) => - simplifiedSentenceStructureData.relations.every( - (relation) => - relation.fromRangeIndex < - simplifiedSentenceStructureData.ranges.length && - relation.toRangeIndex < - simplifiedSentenceStructureData.ranges.length, - ), - { error: "関係が存在しない範囲を参照しています。" }, - ); -export type SimplifiedSentenceStructureData = z.infer< - typeof SimplifiedSentenceStructureDataSchema ->; diff --git a/packages/sentence-structure-data/tokenize-text.ts b/packages/sentence-structure-data/tokenize-text.ts deleted file mode 100644 index 93725dc..0000000 --- a/packages/sentence-structure-data/tokenize-text.ts +++ /dev/null @@ -1,25 +0,0 @@ -// import nlp from "compromise"; -import type { Word } from "./schema.js"; - -const segmenter = new Intl.Segmenter("en", { granularity: "word" }); -export function tokenizeText(text: string): Word[] { - if (text.trim() === "") return []; - - return [...segmenter.segment(text)] - .filter((segment) => segment.segment.trim() !== "") - .map((segment, index) => ({ - index: index, - text: segment.segment, - })); -} - -// export function tokenizeText(text: string): Word[] { -// const tokenizedText: string[] = nlp(text) -// .terms() -// .json() -// .map((term: { text: string }) => term.text); -// return tokenizedText.map((text, index) => ({ -// index: index, -// text: text, -// })); -// } diff --git a/packages/sentence-structure-data/tree/create.ts b/packages/sentence-structure-data/tree/create.ts new file mode 100644 index 0000000..e226776 --- /dev/null +++ b/packages/sentence-structure-data/tree/create.ts @@ -0,0 +1,447 @@ +import type { + Coordination, + CoordinationPart, + Sentence, + SentenceStructureDocument, + SentenceStructureElement, + Word, +} from "../schema.js"; +import { + findCoordinationByStartAndEndWordId, + findSentenceStructureElementByStartAndEndWordId, + findWordById, +} from "../operations.js"; +import type { + SentenceStructureDocumentCoordinationNode, + SentenceStructureDocumentCoordinationPartNode, + SentenceStructureDocumentRootNode, + SentenceStructureDocumentSentenceStructureElementNode, + SentenceStructureDocumentForest, + SentenceStructureDocumentWordNode, + SentenceStructureDocumentSentenceTree, +} from "./types.js"; + +function createSentenceStructureDocumentWordNode( + word: Word, +): SentenceStructureDocumentWordNode { + return { + type: "word", + word, + }; +} + +function _createSpanChildren( + sentenceStructureDocument: SentenceStructureDocument, + sentenceId: string, + span: { + startWordId: string; + endWordId: string; + }, +): ( + | SentenceStructureDocumentWordNode + | SentenceStructureDocumentSentenceStructureElementNode + | SentenceStructureDocumentCoordinationNode +)[] { + const sentence = sentenceStructureDocument.sentences.find( + (sentence) => sentence.id === sentenceId, + ); + if (!sentence) { + throw new Error("Invalid sentenceId"); + } + const children: ( + | SentenceStructureDocumentWordNode + | SentenceStructureDocumentSentenceStructureElementNode + | SentenceStructureDocumentCoordinationNode + )[] = []; + + const spanStartWordIndex = findWordById(sentenceStructureDocument, { + sentenceId, + wordId: span.startWordId, + })!.index; + const spanEndWordIndex = findWordById(sentenceStructureDocument, { + sentenceId, + wordId: span.endWordId, + })!.index; + let currentWordIndex = spanStartWordIndex; + while (currentWordIndex <= spanEndWordIndex) { + const matchedSentenceStructureElement = + sentence.sentenceStructureElements + .filter( + (sentenceStructureElement) => + !( + sentenceStructureElement.startWordId === span.startWordId && + sentenceStructureElement.endWordId === span.endWordId + ), + ) + .filter((sentenceStructureElement) => { + const sentenceStructureElementStartWordIndex = findWordById( + sentenceStructureDocument, + { + sentenceId, + wordId: sentenceStructureElement.startWordId, + }, + )!.index; + const sentenceStructureElementEndWordIndex = findWordById( + sentenceStructureDocument, + { + sentenceId, + wordId: sentenceStructureElement.endWordId, + }, + )!.index; + return ( + sentenceStructureElementStartWordIndex === currentWordIndex && + sentenceStructureElementEndWordIndex <= spanEndWordIndex + ); + }) + .sort((a, b) => { + const aEndWordIndex = findWordById(sentenceStructureDocument, { + sentenceId, + wordId: a.endWordId, + })!.index; + const bEndWordIndex = findWordById(sentenceStructureDocument, { + sentenceId, + wordId: b.endWordId, + })!.index; + return bEndWordIndex - aEndWordIndex; + }) + .at(0) ?? null; + const matchedCoordination = + sentence.coordinations + .filter( + (coordination) => + !( + coordination.parts.at(0)!.startWordId === span.startWordId && + coordination.parts.at(-1)!.endWordId === span.endWordId + ), + ) + .filter((coordination) => { + const coordinationStartWordIndex = findWordById( + sentenceStructureDocument, + { + sentenceId, + wordId: coordination.parts.at(0)!.startWordId, + }, + )!.index; + const coordinationEndWordIndex = findWordById( + sentenceStructureDocument, + { + sentenceId, + wordId: coordination.parts.at(-1)!.endWordId, + }, + )!.index; + return ( + coordinationStartWordIndex === currentWordIndex && + coordinationEndWordIndex <= spanEndWordIndex + ); + }) + .sort((a, b) => { + const aEndWordIndex = findWordById(sentenceStructureDocument, { + sentenceId, + wordId: a.parts.at(-1)!.endWordId, + })!.index; + const bEndWordIndex = findWordById(sentenceStructureDocument, { + sentenceId, + wordId: b.parts.at(-1)!.endWordId, + })!.index; + return bEndWordIndex - aEndWordIndex; + }) + .at(0) ?? null; + const matchedSentenceStructureElementEndWordIndex = + matchedSentenceStructureElement + ? findWordById(sentenceStructureDocument, { + sentenceId, + wordId: matchedSentenceStructureElement.endWordId, + })!.index + : -1; + const matchedCoordinationEndWordIndex = matchedCoordination + ? findWordById(sentenceStructureDocument, { + sentenceId, + wordId: matchedCoordination.parts.at(-1)!.endWordId, + })!.index + : -1; + if (!matchedSentenceStructureElement && !matchedCoordination) { + children.push( + createSentenceStructureDocumentWordNode( + sentence.words.find((word) => word.index === currentWordIndex)!, + ), + ); + currentWordIndex++; + } else if ( + (matchedSentenceStructureElement && !matchedCoordination) || + (matchedSentenceStructureElement && + matchedCoordination && + matchedCoordinationEndWordIndex <= + matchedSentenceStructureElementEndWordIndex) + ) { + children.push( + createSentenceStructureDocumentSentenceStructureElementNode( + sentenceStructureDocument, + sentenceId, + matchedSentenceStructureElement, + ), + ); + currentWordIndex = matchedSentenceStructureElementEndWordIndex + 1; + } else { + if (!matchedCoordination) throw new Error("Unreachable"); + children.push( + createSentenceStructureDocumentCoordinationNode( + sentenceStructureDocument, + sentenceId, + matchedCoordination, + ), + ); + currentWordIndex = matchedCoordinationEndWordIndex + 1; + } + } + + return children; +} + +function createSentenceStructureDocumentSentenceStructureElementNode( + sentenceStructureDocument: SentenceStructureDocument, + sentenceId: string, + sentenceStructureElement: SentenceStructureElement, +): SentenceStructureDocumentSentenceStructureElementNode { + const matchedCoordination = findCoordinationByStartAndEndWordId( + sentenceStructureDocument, + { + sentenceId, + startWordId: sentenceStructureElement.startWordId, + endWordId: sentenceStructureElement.endWordId, + }, + ); + if (matchedCoordination) { + return { + type: "sentence-structure-element", + sentenceStructureElement, + children: [ + createSentenceStructureDocumentCoordinationNode( + sentenceStructureDocument, + sentenceId, + matchedCoordination, + ), + ], + }; + } + + if ( + sentenceStructureElement.startWordId === sentenceStructureElement.endWordId + ) { + return { + type: "sentence-structure-element", + sentenceStructureElement, + children: [ + createSentenceStructureDocumentWordNode( + findWordById(sentenceStructureDocument, { + sentenceId, + wordId: sentenceStructureElement.startWordId, + })!, + ), + ], + }; + } + + return { + type: "sentence-structure-element", + sentenceStructureElement, + children: _createSpanChildren(sentenceStructureDocument, sentenceId, { + startWordId: sentenceStructureElement.startWordId, + endWordId: sentenceStructureElement.endWordId, + }), + }; +} + +function createSentenceStructureDocumentCoordinationPartNode( + sentenceStructureDocument: SentenceStructureDocument, + sentenceId: string, + coordinationPart: CoordinationPart, +): SentenceStructureDocumentCoordinationPartNode { + const matchedSentenceStructureElement = + findSentenceStructureElementByStartAndEndWordId(sentenceStructureDocument, { + sentenceId, + startWordId: coordinationPart.startWordId, + endWordId: coordinationPart.endWordId, + }); + if (matchedSentenceStructureElement) { + return { + type: "coordination-part", + coordinationPart, + children: [ + createSentenceStructureDocumentSentenceStructureElementNode( + sentenceStructureDocument, + sentenceId, + matchedSentenceStructureElement, + ), + ], + }; + } + + const matchedCoordination = findCoordinationByStartAndEndWordId( + sentenceStructureDocument, + { + sentenceId, + startWordId: coordinationPart.startWordId, + endWordId: coordinationPart.endWordId, + }, + ); + if (matchedCoordination) { + return { + type: "coordination-part", + coordinationPart, + children: [ + createSentenceStructureDocumentCoordinationNode( + sentenceStructureDocument, + sentenceId, + matchedCoordination, + ), + ], + }; + } + + if (coordinationPart.startWordId === coordinationPart.endWordId) { + return { + type: "coordination-part", + coordinationPart, + children: [ + createSentenceStructureDocumentWordNode( + findWordById(sentenceStructureDocument, { + sentenceId, + wordId: coordinationPart.startWordId, + })!, + ), + ], + }; + } + + return { + type: "coordination-part", + coordinationPart, + children: _createSpanChildren(sentenceStructureDocument, sentenceId, { + startWordId: coordinationPart.startWordId, + endWordId: coordinationPart.endWordId, + }), + }; +} + +function createSentenceStructureDocumentCoordinationNode( + sentenceStructureDocument: SentenceStructureDocument, + sentenceId: string, + coordination: Coordination, +): SentenceStructureDocumentCoordinationNode { + return { + type: "coordination", + coordination, + children: coordination.parts.map((coordinationPart) => + createSentenceStructureDocumentCoordinationPartNode( + sentenceStructureDocument, + sentenceId, + coordinationPart, + ), + ), + }; +} + +function createSentenceStructureDocumentRootNode( + sentenceStructureDocument: SentenceStructureDocument, + sentenceId: string, +): SentenceStructureDocumentRootNode { + const sentence = sentenceStructureDocument.sentences.find( + (sentence) => sentence.id === sentenceId, + ); + if (!sentence) { + throw new Error("Invalid sentenceId"); + } + + const sentenceStartWord = sentence.words.at(0)!; + const sentenceEndWord = sentence.words.at(-1)!; + + if (sentence.words.length === 0) { + return { + type: "root", + children: [], + }; + } + + const matchedSentenceStructureElement = + findSentenceStructureElementByStartAndEndWordId(sentenceStructureDocument, { + sentenceId, + startWordId: sentenceStartWord.id, + endWordId: sentenceEndWord.id, + }); + if (matchedSentenceStructureElement) { + return { + type: "root", + children: [ + createSentenceStructureDocumentSentenceStructureElementNode( + sentenceStructureDocument, + sentenceId, + matchedSentenceStructureElement, + ), + ], + }; + } + + const matchedCoordination = findCoordinationByStartAndEndWordId( + sentenceStructureDocument, + { + sentenceId, + startWordId: sentenceStartWord.id, + endWordId: sentenceEndWord.id, + }, + ); + if (matchedCoordination) { + return { + type: "root", + children: [ + createSentenceStructureDocumentCoordinationNode( + sentenceStructureDocument, + sentenceId, + matchedCoordination, + ), + ], + }; + } + + if (sentence.words.length === 1) { + return { + type: "root", + children: [createSentenceStructureDocumentWordNode(sentenceStartWord)], + }; + } + + return { + type: "root", + children: _createSpanChildren(sentenceStructureDocument, sentenceId, { + startWordId: sentenceStartWord.id, + endWordId: sentenceEndWord.id, + }), + }; +} + +function createSentenceStructureDocumentSentenceTree( + sentenceStructureDocument: SentenceStructureDocument, + sentence: Sentence, +): SentenceStructureDocumentSentenceTree { + return { + sentenceId: sentence.id, + sentenceIndex: sentence.index, + root: createSentenceStructureDocumentRootNode( + sentenceStructureDocument, + sentence.id, + ), + modifications: sentence.modifications, + }; +} + +export function createSentenceStructureDocumentForest( + sentenceStructureDocument: SentenceStructureDocument, +): SentenceStructureDocumentForest { + return { + sentences: sentenceStructureDocument.sentences.map((sentence) => + createSentenceStructureDocumentSentenceTree( + sentenceStructureDocument, + sentence, + ), + ), + }; +} diff --git a/packages/sentence-structure-data/tree/decorated/create.ts b/packages/sentence-structure-data/tree/decorated/create.ts new file mode 100644 index 0000000..6a39d74 --- /dev/null +++ b/packages/sentence-structure-data/tree/decorated/create.ts @@ -0,0 +1,195 @@ +import type { + SentenceStructureDocumentCoordinationNode, + SentenceStructureDocumentCoordinationPartNode, + SentenceStructureDocumentRootNode, + SentenceStructureDocumentSentenceStructureElementNode, + SentenceStructureDocumentForest, + SentenceStructureDocumentWordNode, + SentenceStructureDocumentSentenceTree, +} from "../types.js"; +import type { + SentenceStructureDecoratedDocumentCoordinationNode, + SentenceStructureDecoratedDocumentCoordinationPartNode, + SentenceStructureDecoratedDocumentRootNode, + SentenceStructureDecoratedDocumentSentenceStructureElementNode, + SentenceStructureDecoratedDocumentForest, + SentenceStructureDecoratedDocumentWordNode, + SentenceStructureDecoratedDocumentSentenceTree, +} from "./types.js"; + +function createSentenceStructureDecoratedDocumentWordNode( + sentenceStructureDocumentWordNode: SentenceStructureDocumentWordNode, +): SentenceStructureDecoratedDocumentWordNode { + return { + type: "word", + word: sentenceStructureDocumentWordNode.word, + }; +} + +function createSentenceStructureDecoratedDocumentSentenceStructureElementNode( + sentenceStructureDocumentSentenceStructureElementNode: SentenceStructureDocumentSentenceStructureElementNode, + nestingDepth: number, + conjunctOrdinalPath: number[], +): SentenceStructureDecoratedDocumentSentenceStructureElementNode { + return { + type: "sentence-structure-element", + sentenceStructureElement: + sentenceStructureDocumentSentenceStructureElementNode.sentenceStructureElement, + nestingDepth, + conjunctOrdinalPath, + children: + sentenceStructureDocumentSentenceStructureElementNode.children.map( + (child) => { + switch (child.type) { + case "word": + return createSentenceStructureDecoratedDocumentWordNode(child); + case "sentence-structure-element": + return createSentenceStructureDecoratedDocumentSentenceStructureElementNode( + child, + nestingDepth + + (sentenceStructureDocumentSentenceStructureElementNode + .sentenceStructureElement.kind === "sentence-constituent" + ? 1 + : 0), + conjunctOrdinalPath, + ); + case "coordination": + return createSentenceStructureDecoratedDocumentCoordinationNode( + child, + nestingDepth + + (sentenceStructureDocumentSentenceStructureElementNode + .sentenceStructureElement.kind === "sentence-constituent" + ? 1 + : 0), + conjunctOrdinalPath, + ); + default: + child satisfies never; + throw new Error("Unreachable"); + } + }, + ), + }; +} + +function createSentenceStructureDecoratedDocumentCoordinationPartNode( + sentenceStructureDocumentCoordinationPartNode: SentenceStructureDocumentCoordinationPartNode, + coordinationId: string, + nestingDepth: number, + conjunctOrdinalPath: number[], +): SentenceStructureDecoratedDocumentCoordinationPartNode { + return { + type: "coordination-part", + coordinationPart: + sentenceStructureDocumentCoordinationPartNode.coordinationPart, + coordinationId, + children: sentenceStructureDocumentCoordinationPartNode.children.map( + (child) => { + switch (child.type) { + case "word": + return createSentenceStructureDecoratedDocumentWordNode(child); + case "sentence-structure-element": + return createSentenceStructureDecoratedDocumentSentenceStructureElementNode( + child, + nestingDepth, + conjunctOrdinalPath, + ); + case "coordination": + return createSentenceStructureDecoratedDocumentCoordinationNode( + child, + nestingDepth, + conjunctOrdinalPath, + ); + default: + child satisfies never; + throw new Error("Unreachable"); + } + }, + ), + }; +} + +function createSentenceStructureDecoratedDocumentCoordinationNode( + sentenceStructureDocumentCoordinationNode: SentenceStructureDocumentCoordinationNode, + nestingDepth: number, + conjunctOrdinalPath: number[], +): SentenceStructureDecoratedDocumentCoordinationNode { + let conjunctOrdinal = 0; + const children = sentenceStructureDocumentCoordinationNode.children.map( + (child) => { + if ( + child.type === "coordination-part" && + child.coordinationPart.type === "conjunct" + ) { + conjunctOrdinal++; + } + return createSentenceStructureDecoratedDocumentCoordinationPartNode( + child, + sentenceStructureDocumentCoordinationNode.coordination.id, + nestingDepth, + [...conjunctOrdinalPath, conjunctOrdinal], + ); + }, + ); + + return { + type: "coordination", + coordination: sentenceStructureDocumentCoordinationNode.coordination, + children, + }; +} + +function createSentenceStructureDecoratedDocumentRootNode( + sentenceStructureDocumentRootNode: SentenceStructureDocumentRootNode, +): SentenceStructureDecoratedDocumentRootNode { + return { + type: "root", + children: sentenceStructureDocumentRootNode.children.map((child) => { + switch (child.type) { + case "word": + return createSentenceStructureDecoratedDocumentWordNode(child); + case "sentence-structure-element": + return createSentenceStructureDecoratedDocumentSentenceStructureElementNode( + child, + 0, + [], + ); + case "coordination": + return createSentenceStructureDecoratedDocumentCoordinationNode( + child, + 0, + [], + ); + default: + child satisfies never; + throw new Error("Unreachable"); + } + }), + }; +} + +function createSentenceStructureDecoratedDocumentSentenceTree( + sentenceStructureDocumentSentenceTree: SentenceStructureDocumentSentenceTree, +): SentenceStructureDecoratedDocumentSentenceTree { + return { + sentenceId: sentenceStructureDocumentSentenceTree.sentenceId, + sentenceIndex: sentenceStructureDocumentSentenceTree.sentenceIndex, + root: createSentenceStructureDecoratedDocumentRootNode( + sentenceStructureDocumentSentenceTree.root, + ), + modifications: sentenceStructureDocumentSentenceTree.modifications, + }; +} + +export function createSentenceStructureDecoratedDocumentForest( + sentenceStructureDocumentForest: SentenceStructureDocumentForest, +): SentenceStructureDecoratedDocumentForest { + return { + sentences: sentenceStructureDocumentForest.sentences.map( + (sentenceStructureDocumentSentenceTree) => + createSentenceStructureDecoratedDocumentSentenceTree( + sentenceStructureDocumentSentenceTree, + ), + ), + }; +} diff --git a/packages/sentence-structure-data/tree/decorated/types.ts b/packages/sentence-structure-data/tree/decorated/types.ts new file mode 100644 index 0000000..23baa53 --- /dev/null +++ b/packages/sentence-structure-data/tree/decorated/types.ts @@ -0,0 +1,68 @@ +import type { + Coordination, + CoordinationPart, + Modification, + SentenceStructureElement, + Word, +} from "../../schema.js"; + +export type SentenceStructureDecoratedDocumentWordNode = { + type: "word"; + word: Word; +}; + +export type SentenceStructureDecoratedDocumentSentenceStructureElementNode = { + type: "sentence-structure-element"; + sentenceStructureElement: SentenceStructureElement; + nestingDepth: number; + conjunctOrdinalPath: number[]; + children: ( + | SentenceStructureDecoratedDocumentWordNode + | SentenceStructureDecoratedDocumentSentenceStructureElementNode + | SentenceStructureDecoratedDocumentCoordinationNode + )[]; +}; + +export type SentenceStructureDecoratedDocumentCoordinationPartNode = { + type: "coordination-part"; + coordinationPart: CoordinationPart; + coordinationId: string; + children: ( + | SentenceStructureDecoratedDocumentWordNode + | SentenceStructureDecoratedDocumentSentenceStructureElementNode + | SentenceStructureDecoratedDocumentCoordinationNode + )[]; +}; + +export type SentenceStructureDecoratedDocumentCoordinationNode = { + type: "coordination"; + coordination: Coordination; + children: SentenceStructureDecoratedDocumentCoordinationPartNode[]; +}; + +export type SentenceStructureDecoratedDocumentRootNode = { + type: "root"; + children: ( + | SentenceStructureDecoratedDocumentWordNode + | SentenceStructureDecoratedDocumentSentenceStructureElementNode + | SentenceStructureDecoratedDocumentCoordinationNode + )[]; +}; + +export type SentenceStructureDecoratedDocumentSentenceTree = { + sentenceId: string; + sentenceIndex: number; + root: SentenceStructureDecoratedDocumentRootNode; + modifications: Modification[]; +}; + +export type SentenceStructureDecoratedDocumentForest = { + sentences: SentenceStructureDecoratedDocumentSentenceTree[]; +}; + +export type SentenceStructureDecoratedDocumentNode = + | SentenceStructureDecoratedDocumentWordNode + | SentenceStructureDecoratedDocumentSentenceStructureElementNode + | SentenceStructureDecoratedDocumentCoordinationPartNode + | SentenceStructureDecoratedDocumentCoordinationNode + | SentenceStructureDecoratedDocumentRootNode; diff --git a/packages/sentence-structure-data/tree/types.ts b/packages/sentence-structure-data/tree/types.ts new file mode 100644 index 0000000..030d09e --- /dev/null +++ b/packages/sentence-structure-data/tree/types.ts @@ -0,0 +1,65 @@ +import type { + Coordination, + CoordinationPart, + Modification, + SentenceStructureElement, + Word, +} from "../schema.js"; + +export type SentenceStructureDocumentWordNode = { + type: "word"; + word: Word; +}; + +export type SentenceStructureDocumentSentenceStructureElementNode = { + type: "sentence-structure-element"; + sentenceStructureElement: SentenceStructureElement; + children: ( + | SentenceStructureDocumentWordNode + | SentenceStructureDocumentSentenceStructureElementNode + | SentenceStructureDocumentCoordinationNode + )[]; +}; + +export type SentenceStructureDocumentCoordinationPartNode = { + type: "coordination-part"; + coordinationPart: CoordinationPart; + children: ( + | SentenceStructureDocumentWordNode + | SentenceStructureDocumentSentenceStructureElementNode + | SentenceStructureDocumentCoordinationNode + )[]; +}; + +export type SentenceStructureDocumentCoordinationNode = { + type: "coordination"; + coordination: Coordination; + children: SentenceStructureDocumentCoordinationPartNode[]; +}; + +export type SentenceStructureDocumentRootNode = { + type: "root"; + children: ( + | SentenceStructureDocumentWordNode + | SentenceStructureDocumentSentenceStructureElementNode + | SentenceStructureDocumentCoordinationNode + )[]; +}; + +export type SentenceStructureDocumentSentenceTree = { + sentenceId: string; + sentenceIndex: number; + root: SentenceStructureDocumentRootNode; + modifications: Modification[]; +}; + +export type SentenceStructureDocumentForest = { + sentences: SentenceStructureDocumentSentenceTree[]; +}; + +export type SentenceStructureDocumentNode = + | SentenceStructureDocumentWordNode + | SentenceStructureDocumentSentenceStructureElementNode + | SentenceStructureDocumentCoordinationPartNode + | SentenceStructureDocumentCoordinationNode + | SentenceStructureDocumentRootNode; diff --git a/packages/sentence-structure-diagram-configurations/default-configurations.ts b/packages/sentence-structure-diagram-configurations/default-configurations.ts deleted file mode 100644 index ffef31a..0000000 --- a/packages/sentence-structure-diagram-configurations/default-configurations.ts +++ /dev/null @@ -1,26 +0,0 @@ -import type { Configurations } from "./schema.js"; - -export const defaultConfigurations: Configurations = { - color: { - primaryColor: "#1976d2", - textColor: "#000000", - }, - sentenceStructureRangeTypeToBracketNameMap: { - modifier: "(parenthesis)", - phrase: "", - clause: "[square-bracket]", - }, - sentenceElementNameToSentenceElementSymbolMap: { - S: "S", - V: "V", - C: "C", - O: "O", - M: "M", - }, - sentenceElementPositionType: { - sentenceElementRangeSentenceElementPositionType: "bottom-center", - sentenceStructureRangeSentenceElementPositionType: "bottom-left", - }, - relationShapeType: "curved", - layoutMode: "structured", -}; diff --git a/packages/sentence-structure-diagram-configurations/format.ts b/packages/sentence-structure-diagram-configurations/format.ts deleted file mode 100644 index b3877b8..0000000 --- a/packages/sentence-structure-diagram-configurations/format.ts +++ /dev/null @@ -1,22 +0,0 @@ -import * as z from "zod"; -import { XMLBuilder, XMLParser } from "fast-xml-parser"; -import { ConfigurationsSchema } from "./schema.js"; - -export const stringToConfigurations = z.codec( - z.string(), - ConfigurationsSchema, - { - decode: (string) => JSON.parse(string), - encode: (configurations) => JSON.stringify(configurations, null, 2), - }, -); - -export const xmlStringToConfigurations = z.codec( - z.string(), - ConfigurationsSchema, - { - decode: (xml) => new XMLParser().parse(xml), - encode: (configurations) => - new XMLBuilder({ format: true }).build(configurations), - }, -); diff --git a/packages/sentence-structure-diagram-configurations/index.ts b/packages/sentence-structure-diagram-configurations/index.ts deleted file mode 100644 index 6459ab8..0000000 --- a/packages/sentence-structure-diagram-configurations/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -export type { - BracketName, - SentenceElementPositionType, - RelationShapeType, - LayoutMode, - Configurations, -} from "./schema.js"; -export { defaultConfigurations } from "./default-configurations.js"; -export { stringToConfigurations, xmlStringToConfigurations } from "./format.js"; diff --git a/packages/sentence-structure-diagram-configurations/schema.ts b/packages/sentence-structure-diagram-configurations/schema.ts deleted file mode 100644 index 5de0bca..0000000 --- a/packages/sentence-structure-diagram-configurations/schema.ts +++ /dev/null @@ -1,51 +0,0 @@ -import * as z from "zod"; -import { - sentenceElementNameOptions, - sentenceStructureRangeTypeOptions, -} from "@sentence-structure-diagram-app/sentence-structure-data"; - -const BracketNameSchema = z.literal([ - "(parenthesis)", - "", - "{curly-bracket}", - "[square-bracket]", -]); -export type BracketName = z.infer; - -const SentenceElementPositionTypeSchema = z.literal([ - "bottom-center", - "bottom-left", -]); -export type SentenceElementPositionType = z.infer< - typeof SentenceElementPositionTypeSchema ->; - -const RelationShapeTypeSchema = z.literal(["curved", "right-angle"]); -export type RelationShapeType = z.infer; - -const LayoutModeSchema = z.literal(["linear", "structured"]); -export type LayoutMode = z.infer; - -export const ConfigurationsSchema = z.object({ - color: z.object({ - primaryColor: z.string(), - textColor: z.string(), - }), - sentenceStructureRangeTypeToBracketNameMap: z.record( - z.literal(sentenceStructureRangeTypeOptions), - BracketNameSchema, - ), - sentenceElementNameToSentenceElementSymbolMap: z.record( - z.literal(sentenceElementNameOptions), - z.string(), - ), - sentenceElementPositionType: z.object({ - sentenceElementRangeSentenceElementPositionType: - SentenceElementPositionTypeSchema, - sentenceStructureRangeSentenceElementPositionType: - SentenceElementPositionTypeSchema, - }), - relationShapeType: RelationShapeTypeSchema, - layoutMode: LayoutModeSchema, -}); -export type Configurations = z.infer; diff --git a/packages/sentence-structure-diagram-data/extract-position.ts b/packages/sentence-structure-diagram-data/extract-position.ts deleted file mode 100644 index abf1e79..0000000 --- a/packages/sentence-structure-diagram-data/extract-position.ts +++ /dev/null @@ -1,74 +0,0 @@ -import type { - SentenceStructureDiagramNode, - SentenceStructureDiagramTree, -} from "@sentence-structure-diagram-app/sentence-structure-diagram-tree"; - -type WordPosition = { - wordIndex: number; - start: number; - end: number; - top: number; - bottom: number; -}; - -export function extractWordPositions( - treePosition: SentenceStructureDiagramTree, -): WordPosition[] { - const wordPositions: WordPosition[] = []; - - function traverse(node: SentenceStructureDiagramNode): void { - if (!("children" in node)) { - wordPositions.push({ - wordIndex: node.word.index, - start: node.position.start, - end: node.position.end, - top: node.position.top, - bottom: node.position.bottom, - }); - return; - } - for (const child of node.children) { - traverse(child); - } - } - - traverse(treePosition); - - return wordPositions.sort((a, b) => a.wordIndex - b.wordIndex); -} - -type SpanPosition = { - start: number; - end: number; - top: number; - bottom: number; -}[]; - -export function extractSpanPosition( - wordPositions: WordPosition[], - startWordIndex: number, - endWordIndex: number, -): SpanPosition { - const wordPositionsInRange = wordPositions.filter( - (wordPosition) => - startWordIndex <= wordPosition.wordIndex && - wordPosition.wordIndex <= endWordIndex, - ); - - const rangePosition: SpanPosition = []; - - for (const wordPosition of wordPositionsInRange) { - if (wordPosition.bottom === rangePosition.at(-1)?.bottom) { - rangePosition.at(-1)!.end = wordPosition.end; - } else { - rangePosition.push({ - start: wordPosition.start, - end: wordPosition.end, - top: wordPosition.top, - bottom: wordPosition.bottom, - }); - } - } - - return rangePosition; -} diff --git a/packages/sentence-structure-diagram-data/generate.ts b/packages/sentence-structure-diagram-data/generate.ts deleted file mode 100644 index b0a3667..0000000 --- a/packages/sentence-structure-diagram-data/generate.ts +++ /dev/null @@ -1,172 +0,0 @@ -import { - findRangeById, - type SentenceStructureData, -} from "@sentence-structure-diagram-app/sentence-structure-data"; -import type { Configurations } from "@sentence-structure-diagram-app/sentence-structure-diagram-configurations"; -import { createTree } from "@sentence-structure-diagram-app/sentence-structure-tree"; -import { createSentenceStructureDiagramTree } from "@sentence-structure-diagram-app/sentence-structure-diagram-tree"; -import type { SentenceStructureDiagramData } from "./types.js"; -import { - extractSpanPosition, - extractWordPositions, -} from "./extract-position.js"; -import { resolveConfigurations } from "./resolve-configurations.js"; - -export function convertSentenceStructureDataToSentenceStructureDiagramData( - sentenceStructureData: SentenceStructureData, - maxWidth: number, - measureTextWidth: (text: string) => number, - customConfigurations: Partial, -): SentenceStructureDiagramData { - const resolvedConfigurations = resolveConfigurations(customConfigurations); - - const tree = createTree(sentenceStructureData); - const treePosition = createSentenceStructureDiagramTree( - tree, - maxWidth, - measureTextWidth, - resolvedConfigurations.layoutMode, - ); - const wordPositions = extractWordPositions(treePosition); - - return { - position: { - left: treePosition.position.start, - right: treePosition.position.end, - top: treePosition.position.top, - bottom: treePosition.position.bottom, - }, - color: { - primaryColor: resolvedConfigurations.color.primaryColor, - textColor: resolvedConfigurations.color.textColor, - }, - words: sentenceStructureData.words.map((word) => ({ - index: word.index, - text: word.text, - openingBrackets: sentenceStructureData.ranges - .filter((range) => range.kind === "sentence-structure") - .filter((range) => range.startWordIndex === word.index) - .sort((a, b) => b.endWordIndex - a.endWordIndex) - .map( - (range) => - resolvedConfigurations.sentenceStructureRangeTypeToBracketsMap[ - range.type - ].openingBracket, - ), - closingBrackets: sentenceStructureData.ranges - .filter((range) => range.kind === "sentence-structure") - .filter((range) => range.endWordIndex === word.index) - .sort((a, b) => b.startWordIndex - a.startWordIndex) - .map( - (range) => - resolvedConfigurations.sentenceStructureRangeTypeToBracketsMap[ - range.type - ].closingBracket, - ), - position: { - left: wordPositions[word.index]!.start, - right: wordPositions[word.index]!.end, - top: wordPositions[word.index]!.top, - bottom: wordPositions[word.index]!.bottom, - }, - })), - underlines: sentenceStructureData.ranges - .filter((range) => range.type === "core-sentence-element") - .map((range) => { - const rangePosition = extractSpanPosition( - wordPositions, - range.startWordIndex, - range.endWordIndex, - ); - return { - rangeId: range.id, - position: rangePosition.map((position) => ({ - start: position.start, - end: position.end, - bottom: position.bottom + 4, - })), - }; - }), - sentenceElements: sentenceStructureData.ranges - .filter((range) => range.kind !== "relation") - .map((range) => { - const rangePosition = extractSpanPosition( - wordPositions, - range.startWordIndex, - range.endWordIndex, - ); - return { - rangeId: range.id, - symbol: range.sentenceElementName - ? resolvedConfigurations - .sentenceElementNameToSentenceElementSymbolMap[ - range.sentenceElementName - ] - : null, - position: - range.kind === "core-sentence-element" - ? resolvedConfigurations.sentenceElementPosition.determineSentenceElementRangeSentenceElementPosition( - rangePosition, - ) - : resolvedConfigurations.sentenceElementPosition.determineSentenceStructureRangeSentenceElementPosition( - rangePosition, - ), - }; - }), - relations: sentenceStructureData.relations.map((relation) => { - const fromRange = findRangeById(sentenceStructureData, { - rangeId: relation.fromRangeId, - }); - const toRange = findRangeById(sentenceStructureData, { - rangeId: relation.toRangeId, - }); - if (!fromRange || !toRange) { - throw new Error( - `Relation ${relation.id} has invalid range references.`, - ); - } - - const fromRangePosition = extractSpanPosition( - wordPositions, - fromRange.startWordIndex, - fromRange.endWordIndex, - ); - const toRangePosition = extractSpanPosition( - wordPositions, - toRange.startWordIndex, - toRange.endWordIndex, - ); - - return { - relationId: relation.id, - svgPathData: resolvedConfigurations.determineRelationSvgPathData( - fromRangePosition, - toRangePosition, - ), - }; - }), - coordinations: sentenceStructureData.coordinations.map((coordination) => { - const coordinationPosition = { - position: extractSpanPosition( - wordPositions, - coordination.children.at(0)!.startWordIndex, - coordination.children.at(-1)!.endWordIndex, - ), - childrenPositions: coordination.children.map((child) => - extractSpanPosition( - wordPositions, - child.startWordIndex, - child.endWordIndex, - ), - ), - }; - return { - coordinationId: coordination.id, - svgPathData: - resolvedConfigurations.determineCoordinationSvgPathData( - coordinationPosition, - ), - }; - }), - }; -} diff --git a/packages/sentence-structure-diagram-data/index.ts b/packages/sentence-structure-diagram-data/index.ts deleted file mode 100644 index 0c08cf0..0000000 --- a/packages/sentence-structure-diagram-data/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { convertSentenceStructureDataToSentenceStructureDiagramData } from "./generate.js"; -export type { SentenceStructureDiagramData } from "./types.js"; diff --git a/packages/sentence-structure-diagram-data/package.json b/packages/sentence-structure-diagram-data/package.json deleted file mode 100644 index c5da8cc..0000000 --- a/packages/sentence-structure-diagram-data/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "@sentence-structure-diagram-app/sentence-structure-diagram-data", - "version": "0.1.0", - "type": "module", - "main": "./dist/index.js", - "scripts": { - "build": "tsc", - "build:watch": "tsc --watch", - "clean": "rm -r dist" - }, - "dependencies": { - "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0", - "@sentence-structure-diagram-app/sentence-structure-diagram-configurations": "^0.1.0", - "@sentence-structure-diagram-app/sentence-structure-diagram-tree": "^0.1.0", - "fast-xml-parser": "^5.3.2", - "zod": "^4.1.13" - }, - "devDependencies": { - "typescript": "^5.9.3" - } -} diff --git a/packages/sentence-structure-diagram-data/resolve-configurations.ts b/packages/sentence-structure-diagram-data/resolve-configurations.ts deleted file mode 100644 index 1bc3230..0000000 --- a/packages/sentence-structure-diagram-data/resolve-configurations.ts +++ /dev/null @@ -1,300 +0,0 @@ -import type { - SentenceElementName, - SentenceStructureRangeType, -} from "@sentence-structure-diagram-app/sentence-structure-data"; -import { - defaultConfigurations, - type BracketName, - type Configurations, - type LayoutMode, - type RelationShapeType, - type SentenceElementPositionType, -} from "@sentence-structure-diagram-app/sentence-structure-diagram-configurations"; - -type SpanPosition = { - start: number; - end: number; - top: number; - bottom: number; -}[]; - -type RangePosition = SpanPosition; - -type CoordinationPosition = { - position: SpanPosition; - childrenPositions: SpanPosition[]; -}; - -type ResolvedConfigurations = { - color: { - primaryColor: string; - textColor: string; - }; - sentenceStructureRangeTypeToBracketsMap: Record< - SentenceStructureRangeType, - { openingBracket: string; closingBracket: string } - >; - sentenceElementNameToSentenceElementSymbolMap: Record< - SentenceElementName, - string - >; - sentenceElementPosition: { - determineSentenceElementRangeSentenceElementPosition: ( - rangePosition: RangePosition, - ) => { x: number; y: number }; - determineSentenceStructureRangeSentenceElementPosition: ( - rangePosition: RangePosition, - ) => { x: number; y: number }; - }; - determineRelationSvgPathData: ( - fromRangePosition: RangePosition, - toRangePosition: RangePosition, - ) => string; - determineCoordinationSvgPathData: ( - coordinationPosition: CoordinationPosition, - ) => string; - layoutMode: LayoutMode; -}; - -function convertBracketNameToBrackets(bracketName: BracketName) { - switch (bracketName) { - case "(parenthesis)": - return { openingBracket: "(", closingBracket: ")" }; - case "": - return { openingBracket: "<", closingBracket: ">" }; - case "{curly-bracket}": - return { openingBracket: "{", closingBracket: "}" }; - case "[square-bracket]": - return { openingBracket: "[", closingBracket: "]" }; - } -} - -function determineSentenceElementPosition( - sentenceElementPositionType: SentenceElementPositionType, - rangePosition: RangePosition, -) { - switch (sentenceElementPositionType) { - case "bottom-center": - return { - x: (rangePosition[0]!.start + rangePosition[0]!.end) / 2, - y: rangePosition[0]!.bottom + 8, - }; - case "bottom-left": - return { - x: rangePosition[0]!.start + 6, - y: rangePosition[0]!.bottom + 8, - }; - } -} - -function determineRelationSvgPathData( - relationShapeType: RelationShapeType, - fromRangePosition: RangePosition, - toRangePosition: RangePosition, -) { - const fromPoint = { - x: - fromRangePosition[0]!.start + - (fromRangePosition[0]!.end - fromRangePosition[0]!.start) / 2, - y: fromRangePosition[0]!.top, - }; - const toPoint = { - x: - toRangePosition[0]!.start + - (toRangePosition[0]!.end - toRangePosition[0]!.start) / 2, - y: toRangePosition[0]!.top, - }; - - const height = 40; - - const curveTopY = Math.min(fromPoint.y, toPoint.y) - height; - const arrowSize = 4; - const arrowPosition = { - left: toPoint.x - arrowSize, - right: toPoint.x + arrowSize, - top: toPoint.y - arrowSize, - }; - const curveSvgPathData = [ - // 曲線 - `M ${fromPoint.x},${fromPoint.y}`, - `C ${fromPoint.x},${curveTopY} ${toPoint.x},${curveTopY} ${toPoint.x},${toPoint.y}`, - // 矢印 - `M ${arrowPosition.left},${arrowPosition.top}`, - `L ${toPoint.x},${toPoint.y}`, - `L ${arrowPosition.right},${arrowPosition.top}`, - ].join(" "); - - const straightSvgPathData = [ - // 直線 - `M ${fromPoint.x},${fromPoint.y}`, - `L ${fromPoint.x},${fromPoint.y - height / 2}`, - `L ${toPoint.x},${toPoint.y - height / 2}`, - `L ${toPoint.x},${toPoint.y}`, - // 矢印 - `M ${arrowPosition.left},${arrowPosition.top}`, - `L ${toPoint.x},${toPoint.y}`, - `L ${arrowPosition.right},${arrowPosition.top}`, - ].join(" "); - - switch (relationShapeType) { - case "curved": - return curveSvgPathData; - case "right-angle": - if (fromPoint.y === toPoint.y) { - return straightSvgPathData; - } else { - return curveSvgPathData; - } - } -} - -function determineCoordinationSvgPathData( - layoutMode: LayoutMode, - coordinationPosition: CoordinationPosition, -) { - switch (layoutMode) { - case "linear": { - const verticalLines = coordinationPosition.childrenPositions.map( - (position) => ({ - start: (position.at(0)!.start + position.at(0)!.end) / 2, - top: position.at(0)!.top - 20, - bottom: position.at(0)!.top, - }), - ); - const overLines = coordinationPosition.position.map( - (position, index) => ({ - start: - index === 0 - ? (coordinationPosition.childrenPositions.at(0)!.at(0)!.start + - coordinationPosition.childrenPositions.at(0)!.at(0)!.end) / - 2 - : position.start, - end: - index === coordinationPosition.position.length - 1 - ? (coordinationPosition.childrenPositions.at(-1)!.at(-1)!.start + - coordinationPosition.childrenPositions.at(-1)!.at(-1)!.end) / - 2 - : position.end, - top: position.top - 20, - }), - ); - return ( - verticalLines - .map( - (verticalLine) => - `M ${verticalLine.start} ${verticalLine.top} L ${verticalLine.start} ${verticalLine.bottom}`, - ) - .join(" ") + - " " + - overLines - .map( - (overLine) => - `M ${overLine.start} ${overLine.top} L ${overLine.end} ${overLine.top}`, - ) - .join(" ") - ); - } - case "structured": - const verticalLine = { - start: coordinationPosition.position.at(0)!.start - 10, - top: - (coordinationPosition.position.at(0)!.top + - coordinationPosition.position.at(0)!.bottom) / - 2, - bottom: - (coordinationPosition.position.at(-1)!.top + - coordinationPosition.position.at(-1)!.bottom) / - 2 + - 8, - }; - const topHorizontalLine = { - start: verticalLine.start, - end: verticalLine.start + 10, - top: verticalLine.top, - }; - const bottomHorizontalLine = { - start: verticalLine.start, - end: verticalLine.start + 10, - top: verticalLine.bottom, - }; - return [ - // 縦線 - `M ${verticalLine.start} ${verticalLine.top} L ${verticalLine.start} ${verticalLine.bottom}`, - // 上の横線 - `M ${topHorizontalLine.start} ${topHorizontalLine.top} L ${topHorizontalLine.end} ${topHorizontalLine.top}`, - // 下の横線 - `M ${bottomHorizontalLine.start} ${bottomHorizontalLine.top} L ${bottomHorizontalLine.end} ${bottomHorizontalLine.top}`, - ].join(" "); - } -} - -export function resolveConfigurations( - customConfigurations: Partial, -): ResolvedConfigurations { - const configurations: Configurations = { - color: customConfigurations.color - ? customConfigurations.color - : defaultConfigurations.color, - sentenceStructureRangeTypeToBracketNameMap: - customConfigurations.sentenceStructureRangeTypeToBracketNameMap - ? customConfigurations.sentenceStructureRangeTypeToBracketNameMap - : defaultConfigurations.sentenceStructureRangeTypeToBracketNameMap, - sentenceElementNameToSentenceElementSymbolMap: - customConfigurations.sentenceElementNameToSentenceElementSymbolMap - ? customConfigurations.sentenceElementNameToSentenceElementSymbolMap - : defaultConfigurations.sentenceElementNameToSentenceElementSymbolMap, - sentenceElementPositionType: - customConfigurations.sentenceElementPositionType - ? customConfigurations.sentenceElementPositionType - : defaultConfigurations.sentenceElementPositionType, - relationShapeType: customConfigurations.relationShapeType - ? customConfigurations.relationShapeType - : defaultConfigurations.relationShapeType, - layoutMode: customConfigurations.layoutMode - ? customConfigurations.layoutMode - : defaultConfigurations.layoutMode, - }; - - return { - color: configurations.color, - sentenceStructureRangeTypeToBracketsMap: { - modifier: convertBracketNameToBrackets( - configurations.sentenceStructureRangeTypeToBracketNameMap.modifier, - ), - phrase: convertBracketNameToBrackets( - configurations.sentenceStructureRangeTypeToBracketNameMap.phrase, - ), - clause: convertBracketNameToBrackets( - configurations.sentenceStructureRangeTypeToBracketNameMap.clause, - ), - }, - sentenceElementNameToSentenceElementSymbolMap: - configurations.sentenceElementNameToSentenceElementSymbolMap, - sentenceElementPosition: { - determineSentenceElementRangeSentenceElementPosition: (rangePosition) => - determineSentenceElementPosition( - configurations.sentenceElementPositionType - .sentenceElementRangeSentenceElementPositionType, - rangePosition, - ), - determineSentenceStructureRangeSentenceElementPosition: (rangePosition) => - determineSentenceElementPosition( - configurations.sentenceElementPositionType - .sentenceStructureRangeSentenceElementPositionType, - rangePosition, - ), - }, - determineRelationSvgPathData: (fromRangePosition, toRangePosition) => - determineRelationSvgPathData( - configurations.relationShapeType, - fromRangePosition, - toRangePosition, - ), - determineCoordinationSvgPathData: (coordinationPosition) => - determineCoordinationSvgPathData( - configurations.layoutMode, - coordinationPosition, - ), - layoutMode: configurations.layoutMode, - }; -} diff --git a/packages/sentence-structure-diagram-data/types.ts b/packages/sentence-structure-diagram-data/types.ts deleted file mode 100644 index ab65ba0..0000000 --- a/packages/sentence-structure-diagram-data/types.ts +++ /dev/null @@ -1,48 +0,0 @@ -export type SentenceStructureDiagramData = { - position: { - left: number; - right: number; - top: number; - bottom: number; - }; - color: { - primaryColor: string; - textColor: string; - }; - words: { - index: number; - text: string; - openingBrackets: string[]; - closingBrackets: string[]; - position: { - left: number; - right: number; - top: number; - bottom: number; - }; - }[]; - underlines: { - rangeId: string; - position: { - start: number; - end: number; - bottom: number; - }[]; - }[]; - sentenceElements: { - rangeId: string; - symbol: string | null; - position: { - x: number; - y: number; - }; - }[]; - relations: { - relationId: string; - svgPathData: string; - }[]; - coordinations: { - coordinationId: string; - svgPathData: string; - }[]; -}; diff --git a/packages/sentence-structure-diagram-svg/.gitignore b/packages/sentence-structure-diagram-notation/.gitignore similarity index 100% rename from packages/sentence-structure-diagram-svg/.gitignore rename to packages/sentence-structure-diagram-notation/.gitignore diff --git a/packages/sentence-structure-diagram-notation/codecs/json-codec.ts b/packages/sentence-structure-diagram-notation/codecs/json-codec.ts new file mode 100644 index 0000000..6e2ef26 --- /dev/null +++ b/packages/sentence-structure-diagram-notation/codecs/json-codec.ts @@ -0,0 +1,12 @@ +import * as z from "zod"; +import { SentenceStructureDiagramNotationSchema } from "../schema.js"; + +export const jsonStringToSentenceStructureDiagramNotation = z.codec( + z.string(), + SentenceStructureDiagramNotationSchema, + { + decode: (jsonString) => JSON.parse(jsonString), + encode: (sentenceStructureDiagramNotation) => + JSON.stringify(sentenceStructureDiagramNotation, null, 2), + }, +); diff --git a/packages/sentence-structure-diagram-notation/codecs/xml-codec.ts b/packages/sentence-structure-diagram-notation/codecs/xml-codec.ts new file mode 100644 index 0000000..180a283 --- /dev/null +++ b/packages/sentence-structure-diagram-notation/codecs/xml-codec.ts @@ -0,0 +1,1118 @@ +import * as z from "zod"; +import { XMLBuilder, XMLParser } from "fast-xml-parser"; +import { + bracketTypeOptions, + CanvasSchema, + colorOptions, + HexRGBColorSchema, + labelPlacementOptions, + lineStyleOptions, + ModificationNotationSchema, + RangeMarkerSchema, + SentenceStructureDiagramNotationSchema, + SentenceStructureElementNotationSchema, + ThemeSchema, + type SentenceStructureDiagramNotation, +} from "../schema.js"; + +const XMLCanvasSchema = z.object({ + "@_width": z.number(), +}); + +const xmlCanvasToCanvas = z.codec(XMLCanvasSchema, CanvasSchema, { + decode: (xmlCanvas) => { + return { + width: xmlCanvas["@_width"], + }; + }, + encode: (canvas) => { + return { + "@_width": canvas.width, + }; + }, +}); + +const XMLThemeSchema = z.object({ + colors: z.object({ + "@_primary": HexRGBColorSchema, + "@_text": HexRGBColorSchema, + "@_background": HexRGBColorSchema, + }), + typography: z.object({ + "@_font-size": z.string(), + }), + spacing: z.object({ + "@_padding": z.string(), + "@_word-spacing": z.string(), + "@_line-spacing": z.string(), + "@_continuation-indent": z.string(), + }), +}); + +const xmlThemeToTheme = z.codec(XMLThemeSchema, ThemeSchema, { + decode: (xmlTheme) => { + return { + colors: { + primary: xmlTheme.colors["@_primary"], + text: xmlTheme.colors["@_text"], + background: xmlTheme.colors["@_background"], + }, + typography: { + fontSize: Number(xmlTheme.typography["@_font-size"]), + }, + spacing: { + padding: Number(xmlTheme.spacing["@_padding"]), + wordSpacing: Number(xmlTheme.spacing["@_word-spacing"]), + lineSpacing: Number(xmlTheme.spacing["@_line-spacing"]), + continuationIndent: Number(xmlTheme.spacing["@_continuation-indent"]), + }, + }; + }, + encode: (theme) => { + return { + colors: { + "@_primary": theme.colors.primary, + "@_text": theme.colors.text, + "@_background": theme.colors.background, + }, + typography: { + "@_font-size": String(theme.typography.fontSize), + }, + spacing: { + "@_padding": String(theme.spacing.padding), + "@_word-spacing": String(theme.spacing.wordSpacing), + "@_line-spacing": String(theme.spacing.lineSpacing), + "@_continuation-indent": String(theme.spacing.continuationIndent), + }, + }; + }, +}); + +const XMLRangeMarkerSchema = z.optional( + z.union([ + z.object({ + underline: z.object({ + "@_line-style": z.literal(lineStyleOptions), + "@_color": z.literal(colorOptions), + }), + }), + z.object({ + bracket: z.object({ + "@_type": z.literal(bracketTypeOptions), + "@_color": z.literal(colorOptions), + }), + }), + z.object({ + box: z.object({ + "@_color": z.literal(colorOptions), + }), + }), + z.object({ + "text-emphasis": z.object({ + "@_color": z.literal(colorOptions), + }), + }), + z.object({ + highlight: z.object({ + "@_color": z.literal(colorOptions), + }), + }), + z.object({ + bold: z.literal(""), + }), + ]), +); + +const xmlRangeMarkerToRangeMarker = z.codec( + XMLRangeMarkerSchema, + RangeMarkerSchema, + { + decode: (xmlRangeMarker) => { + if (!xmlRangeMarker) { + return { type: "none" } satisfies z.infer; + } + + if ("underline" in xmlRangeMarker) { + return { + type: "underline", + lineStyle: xmlRangeMarker.underline["@_line-style"], + color: xmlRangeMarker.underline["@_color"], + } satisfies z.infer; + } else if ("bracket" in xmlRangeMarker) { + return { + type: "bracket", + bracketType: xmlRangeMarker.bracket["@_type"], + color: xmlRangeMarker.bracket["@_color"], + } satisfies z.infer; + } else if ("box" in xmlRangeMarker) { + return { + type: "box", + color: xmlRangeMarker.box["@_color"], + } satisfies z.infer; + } else if ("text-emphasis" in xmlRangeMarker) { + return { + type: "text-emphasis", + color: xmlRangeMarker["text-emphasis"]["@_color"], + } satisfies z.infer; + } else if ("highlight" in xmlRangeMarker) { + return { + type: "highlight", + color: xmlRangeMarker["highlight"]["@_color"], + } satisfies z.infer; + } else { + return { + type: "bold", + } satisfies z.infer; + } + }, + encode: (rangeMarker) => { + switch (rangeMarker.type) { + case "underline": + return { + underline: { + "@_line-style": rangeMarker.lineStyle, + "@_color": rangeMarker.color, + }, + } satisfies z.infer; + case "bracket": + return { + bracket: { + "@_type": rangeMarker.bracketType, + "@_color": rangeMarker.color, + }, + } satisfies z.infer; + case "box": + return { + box: { + "@_color": rangeMarker.color, + }, + } satisfies z.infer; + case "text-emphasis": + return { + "text-emphasis": { + "@_color": rangeMarker.color, + }, + } satisfies z.infer; + case "highlight": + return { + highlight: { + "@_color": rangeMarker.color, + }, + } satisfies z.infer; + case "bold": + return { + bold: "", + } satisfies z.infer; + case "none": + return undefined; + default: + rangeMarker satisfies never; + throw new Error("Unreachable"); + } + }, + }, +); + +const XMLSentenceStructureElementNotationSchema = z.object({ + "range-marking": z.object({ + "core-sentence-element": XMLRangeMarkerSchema, + "sentence-constituent": z.object({ + phrase: z.object({ + nominal: XMLRangeMarkerSchema, + adjectival: XMLRangeMarkerSchema, + adverbial: XMLRangeMarkerSchema, + }), + clause: z.object({ + nominal: XMLRangeMarkerSchema, + adjectival: XMLRangeMarkerSchema, + adverbial: XMLRangeMarkerSchema, + }), + "adverbial-phrase": XMLRangeMarkerSchema, + }), + "modification-element": XMLRangeMarkerSchema, + }), + "sentence-element-labeling": z.object({ + labels: z.object({ + "@_S": z.string(), + "@_V": z.string(), + "@_O": z.string(), + "@_C": z.string(), + "@_M": z.string(), + }), + placement: z.object({ + "core-sentence-element": z.literal(labelPlacementOptions), + "sentence-constituent": z.object({ + phrase: z.object({ + nominal: z.literal(labelPlacementOptions), + adjectival: z.literal(labelPlacementOptions), + adverbial: z.literal(labelPlacementOptions), + }), + clause: z.object({ + nominal: z.literal(labelPlacementOptions), + adjectival: z.literal(labelPlacementOptions), + adverbial: z.literal(labelPlacementOptions), + }), + "adverbial-phrase": z.literal(labelPlacementOptions), + }), + }), + "@_color": z.literal(colorOptions), + "label-suffixes": z.object({ + "@_show-nesting-depth-primes": z.boolean(), + "@_show-conjunct-numbering": z.boolean(), + }), + }), + "sentence-constituent-labeling": z.object({ + labels: z.object({ + phrase: z.object({ + nominal: z.string(), + adjectival: z.string(), + adverbial: z.string(), + }), + clause: z.object({ + nominal: z.string(), + adjectival: z.string(), + adverbial: z.string(), + }), + "adverbial-phrase": z.string(), + }), + placement: z.object({ + phrase: z.object({ + nominal: z.literal(labelPlacementOptions), + adjectival: z.literal(labelPlacementOptions), + adverbial: z.literal(labelPlacementOptions), + }), + clause: z.object({ + nominal: z.literal(labelPlacementOptions), + adjectival: z.literal(labelPlacementOptions), + adverbial: z.literal(labelPlacementOptions), + }), + "adverbial-phrase": z.literal(labelPlacementOptions), + }), + "@_color": z.literal(colorOptions), + }), +}); + +const xmlSentenceStructureElementNotationToSentenceStructureElementNotation = + z.codec( + XMLSentenceStructureElementNotationSchema, + SentenceStructureElementNotationSchema, + { + decode: (xmlSentenceStructureElementNotation) => ({ + rangeMarking: { + coreSentenceElement: xmlRangeMarkerToRangeMarker.decode( + xmlSentenceStructureElementNotation["range-marking"][ + "core-sentence-element" + ], + ), + sentenceConstituent: { + phrase: { + nominal: xmlRangeMarkerToRangeMarker.decode( + xmlSentenceStructureElementNotation["range-marking"][ + "sentence-constituent" + ].phrase.nominal, + ), + adjectival: xmlRangeMarkerToRangeMarker.decode( + xmlSentenceStructureElementNotation["range-marking"][ + "sentence-constituent" + ].phrase.adjectival, + ), + adverbial: xmlRangeMarkerToRangeMarker.decode( + xmlSentenceStructureElementNotation["range-marking"][ + "sentence-constituent" + ].phrase.adverbial, + ), + }, + clause: { + nominal: xmlRangeMarkerToRangeMarker.decode( + xmlSentenceStructureElementNotation["range-marking"][ + "sentence-constituent" + ].clause.nominal, + ), + adjectival: xmlRangeMarkerToRangeMarker.decode( + xmlSentenceStructureElementNotation["range-marking"][ + "sentence-constituent" + ].clause.adjectival, + ), + adverbial: xmlRangeMarkerToRangeMarker.decode( + xmlSentenceStructureElementNotation["range-marking"][ + "sentence-constituent" + ].clause.adverbial, + ), + }, + adverbialPhrase: xmlRangeMarkerToRangeMarker.decode( + xmlSentenceStructureElementNotation["range-marking"][ + "sentence-constituent" + ]["adverbial-phrase"], + ), + }, + modificationElement: xmlRangeMarkerToRangeMarker.decode( + xmlSentenceStructureElementNotation["range-marking"][ + "modification-element" + ], + ), + }, + sentenceElementLabeling: { + labels: { + S: xmlSentenceStructureElementNotation["sentence-element-labeling"] + .labels["@_S"], + V: xmlSentenceStructureElementNotation["sentence-element-labeling"] + .labels["@_V"], + O: xmlSentenceStructureElementNotation["sentence-element-labeling"] + .labels["@_O"], + C: xmlSentenceStructureElementNotation["sentence-element-labeling"] + .labels["@_C"], + M: xmlSentenceStructureElementNotation["sentence-element-labeling"] + .labels["@_M"], + }, + placement: { + coreSentenceElement: + xmlSentenceStructureElementNotation["sentence-element-labeling"] + .placement["core-sentence-element"], + sentenceConstituent: { + phrase: { + nominal: + xmlSentenceStructureElementNotation[ + "sentence-element-labeling" + ].placement["sentence-constituent"].phrase.nominal, + adjectival: + xmlSentenceStructureElementNotation[ + "sentence-element-labeling" + ].placement["sentence-constituent"].phrase.adjectival, + adverbial: + xmlSentenceStructureElementNotation[ + "sentence-element-labeling" + ].placement["sentence-constituent"].phrase.adverbial, + }, + clause: { + nominal: + xmlSentenceStructureElementNotation[ + "sentence-element-labeling" + ].placement["sentence-constituent"].clause.nominal, + adjectival: + xmlSentenceStructureElementNotation[ + "sentence-element-labeling" + ].placement["sentence-constituent"].clause.adjectival, + adverbial: + xmlSentenceStructureElementNotation[ + "sentence-element-labeling" + ].placement["sentence-constituent"].clause.adverbial, + }, + adverbialPhrase: + xmlSentenceStructureElementNotation["sentence-element-labeling"] + .placement["sentence-constituent"]["adverbial-phrase"], + }, + }, + color: + xmlSentenceStructureElementNotation["sentence-element-labeling"][ + "@_color" + ], + labelSuffixes: { + showNestingDepthPrimes: + xmlSentenceStructureElementNotation["sentence-element-labeling"][ + "label-suffixes" + ]["@_show-nesting-depth-primes"], + showConjunctNumbering: + xmlSentenceStructureElementNotation["sentence-element-labeling"][ + "label-suffixes" + ]["@_show-conjunct-numbering"], + }, + }, + sentenceConstituentLabeling: { + labels: { + phrase: { + nominal: + xmlSentenceStructureElementNotation[ + "sentence-constituent-labeling" + ].labels.phrase.nominal, + adjectival: + xmlSentenceStructureElementNotation[ + "sentence-constituent-labeling" + ].labels.phrase.adjectival, + adverbial: + xmlSentenceStructureElementNotation[ + "sentence-constituent-labeling" + ].labels.phrase.adverbial, + }, + clause: { + nominal: + xmlSentenceStructureElementNotation[ + "sentence-constituent-labeling" + ].labels.clause.nominal, + adjectival: + xmlSentenceStructureElementNotation[ + "sentence-constituent-labeling" + ].labels.clause.adjectival, + adverbial: + xmlSentenceStructureElementNotation[ + "sentence-constituent-labeling" + ].labels.clause.adverbial, + }, + adverbialPhrase: + xmlSentenceStructureElementNotation[ + "sentence-constituent-labeling" + ].labels["adverbial-phrase"], + }, + placement: { + phrase: { + nominal: + xmlSentenceStructureElementNotation[ + "sentence-constituent-labeling" + ].placement.phrase.nominal, + adjectival: + xmlSentenceStructureElementNotation[ + "sentence-constituent-labeling" + ].placement.phrase.adjectival, + adverbial: + xmlSentenceStructureElementNotation[ + "sentence-constituent-labeling" + ].placement.phrase.adverbial, + }, + clause: { + nominal: + xmlSentenceStructureElementNotation[ + "sentence-constituent-labeling" + ].placement.clause.nominal, + adjectival: + xmlSentenceStructureElementNotation[ + "sentence-constituent-labeling" + ].placement.clause.adjectival, + adverbial: + xmlSentenceStructureElementNotation[ + "sentence-constituent-labeling" + ].placement.clause.adverbial, + }, + adverbialPhrase: + xmlSentenceStructureElementNotation[ + "sentence-constituent-labeling" + ].placement["adverbial-phrase"], + }, + color: + xmlSentenceStructureElementNotation[ + "sentence-constituent-labeling" + ]["@_color"], + }, + }), + encode: (sentenceStructureElementNotation) => ({ + "range-marking": { + "core-sentence-element": xmlRangeMarkerToRangeMarker.encode( + sentenceStructureElementNotation.rangeMarking.coreSentenceElement, + ), + "sentence-constituent": { + phrase: { + nominal: xmlRangeMarkerToRangeMarker.encode( + sentenceStructureElementNotation.rangeMarking + .sentenceConstituent.phrase.nominal, + ), + adjectival: xmlRangeMarkerToRangeMarker.encode( + sentenceStructureElementNotation.rangeMarking + .sentenceConstituent.phrase.adjectival, + ), + adverbial: xmlRangeMarkerToRangeMarker.encode( + sentenceStructureElementNotation.rangeMarking + .sentenceConstituent.phrase.adverbial, + ), + }, + clause: { + nominal: xmlRangeMarkerToRangeMarker.encode( + sentenceStructureElementNotation.rangeMarking + .sentenceConstituent.clause.nominal, + ), + adjectival: xmlRangeMarkerToRangeMarker.encode( + sentenceStructureElementNotation.rangeMarking + .sentenceConstituent.clause.adjectival, + ), + adverbial: xmlRangeMarkerToRangeMarker.encode( + sentenceStructureElementNotation.rangeMarking + .sentenceConstituent.clause.adverbial, + ), + }, + "adverbial-phrase": xmlRangeMarkerToRangeMarker.encode( + sentenceStructureElementNotation.rangeMarking.sentenceConstituent + .adverbialPhrase, + ), + }, + "modification-element": xmlRangeMarkerToRangeMarker.encode( + sentenceStructureElementNotation.rangeMarking.modificationElement, + ), + }, + "sentence-element-labeling": { + labels: { + "@_S": + sentenceStructureElementNotation.sentenceElementLabeling.labels.S, + "@_V": + sentenceStructureElementNotation.sentenceElementLabeling.labels.V, + "@_O": + sentenceStructureElementNotation.sentenceElementLabeling.labels.O, + "@_C": + sentenceStructureElementNotation.sentenceElementLabeling.labels.C, + "@_M": + sentenceStructureElementNotation.sentenceElementLabeling.labels.M, + }, + placement: { + "core-sentence-element": + sentenceStructureElementNotation.sentenceElementLabeling.placement + .coreSentenceElement, + "sentence-constituent": { + phrase: { + nominal: + sentenceStructureElementNotation.sentenceElementLabeling + .placement.sentenceConstituent.phrase.nominal, + adjectival: + sentenceStructureElementNotation.sentenceElementLabeling + .placement.sentenceConstituent.phrase.adjectival, + adverbial: + sentenceStructureElementNotation.sentenceElementLabeling + .placement.sentenceConstituent.phrase.adverbial, + }, + clause: { + nominal: + sentenceStructureElementNotation.sentenceElementLabeling + .placement.sentenceConstituent.clause.nominal, + adjectival: + sentenceStructureElementNotation.sentenceElementLabeling + .placement.sentenceConstituent.clause.adjectival, + adverbial: + sentenceStructureElementNotation.sentenceElementLabeling + .placement.sentenceConstituent.clause.adverbial, + }, + "adverbial-phrase": + sentenceStructureElementNotation.sentenceElementLabeling + .placement.sentenceConstituent.adverbialPhrase, + }, + }, + "@_color": + sentenceStructureElementNotation.sentenceElementLabeling.color, + "label-suffixes": { + "@_show-nesting-depth-primes": + sentenceStructureElementNotation.sentenceElementLabeling + .labelSuffixes.showNestingDepthPrimes, + "@_show-conjunct-numbering": + sentenceStructureElementNotation.sentenceElementLabeling + .labelSuffixes.showConjunctNumbering, + }, + }, + "sentence-constituent-labeling": { + labels: { + phrase: { + nominal: + sentenceStructureElementNotation.sentenceConstituentLabeling + .labels.phrase.nominal, + adjectival: + sentenceStructureElementNotation.sentenceConstituentLabeling + .labels.phrase.adjectival, + adverbial: + sentenceStructureElementNotation.sentenceConstituentLabeling + .labels.phrase.adverbial, + }, + clause: { + nominal: + sentenceStructureElementNotation.sentenceConstituentLabeling + .labels.clause.nominal, + adjectival: + sentenceStructureElementNotation.sentenceConstituentLabeling + .labels.clause.adjectival, + adverbial: + sentenceStructureElementNotation.sentenceConstituentLabeling + .labels.clause.adverbial, + }, + "adverbial-phrase": + sentenceStructureElementNotation.sentenceConstituentLabeling + .labels.adverbialPhrase, + }, + placement: { + phrase: { + nominal: + sentenceStructureElementNotation.sentenceConstituentLabeling + .placement.phrase.nominal, + adjectival: + sentenceStructureElementNotation.sentenceConstituentLabeling + .placement.phrase.adjectival, + adverbial: + sentenceStructureElementNotation.sentenceConstituentLabeling + .placement.phrase.adverbial, + }, + clause: { + nominal: + sentenceStructureElementNotation.sentenceConstituentLabeling + .placement.clause.nominal, + adjectival: + sentenceStructureElementNotation.sentenceConstituentLabeling + .placement.clause.adjectival, + adverbial: + sentenceStructureElementNotation.sentenceConstituentLabeling + .placement.clause.adverbial, + }, + "adverbial-phrase": + sentenceStructureElementNotation.sentenceConstituentLabeling + .placement.adverbialPhrase, + }, + "@_color": + sentenceStructureElementNotation.sentenceConstituentLabeling.color, + }, + }), + }, + ); + +const XMLModificationNotationSchema = z.object({ + arrow: z.object({ + "@_type": z.literal(["curved", "orthogonal"]), + "@_color": z.literal(colorOptions), + }), +}); + +const xmlModificationNotationToModificationNotation = z.codec( + XMLModificationNotationSchema, + ModificationNotationSchema, + { + decode: (xmlModificationNotation) => ({ + arrow: { + type: xmlModificationNotation.arrow["@_type"], + color: xmlModificationNotation.arrow["@_color"], + }, + }), + encode: (modificationNotation) => ({ + arrow: { + "@_type": modificationNotation.arrow.type, + "@_color": modificationNotation.arrow.color, + }, + }), + }, +); + +const XMLSentenceStructureDiagramNotation = z.union([ + z.object({ + "sentence-structure-diagram-notation": z.object({ + "@_xmlns": z.literal("https://utlis.github.io/sv-marker/"), + "@_version": z.literal("0.1.0"), + canvas: XMLCanvasSchema, + theme: XMLThemeSchema, + "@_enable-reflow": z.literal("false"), + "sentence-structure-element-notation": + XMLSentenceStructureElementNotationSchema, + "modification-notation": XMLModificationNotationSchema, + "coordination-notation": z.object({ + layout: z.object({ + "@_direction": z.literal("horizontal"), + }), + "range-marking": z.object({ + coordinator: XMLRangeMarkerSchema, + correlative: XMLRangeMarkerSchema, + conjunct: XMLRangeMarkerSchema, + }), + "group-indicator": z.optional( + z.object({ + "bus-connector": z.object({ + "@_color": z.literal(colorOptions), + }), + }), + ), + }), + }), + }), + z.object({ + "sentence-structure-diagram-notation": z.object({ + "@_xmlns": z.literal("https://utlis.github.io/sv-marker/"), + "@_version": z.literal("0.1.0"), + canvas: XMLCanvasSchema, + theme: XMLThemeSchema, + "@_enable-reflow": z.literal("true"), + "sentence-structure-element-notation": + XMLSentenceStructureElementNotationSchema, + "modification-notation": XMLModificationNotationSchema, + "coordination-notation": z.object({ + layout: z.object({ + "@_direction": z.literal("vertical"), + }), + "range-marking": z.object({ + coordinator: XMLRangeMarkerSchema, + correlative: XMLRangeMarkerSchema, + conjunct: XMLRangeMarkerSchema, + }), + "group-indicator": z.optional( + z.union([ + z.object({ + bracket: z.object({ + "@_bracket-type": z.literal(bracketTypeOptions), + "@_placement": z.literal(["left", "both-sides"]), + "@_color": z.literal(colorOptions), + }), + }), + z.object({ + "bus-connector": z.object({ + "@_color": z.literal(colorOptions), + }), + }), + ]), + ), + }), + "layout-strategy": z.union([ + z.object({ + "@_line-break-strategy": z.literal("greedy-word-wrap"), + }), + z.object({ + "@_line-break-strategy": z.literal("largest-boundary-first"), + "@_continuation-line-start": z.literal([ + "content-start", + "scope-start", + ]), + }), + ]), + }), + }), +]); + +const xmlSentenceStructureDiagramNotationToSentenceStructureDiagramNotation = + z.codec( + XMLSentenceStructureDiagramNotation, + SentenceStructureDiagramNotationSchema, + { + decode: (xmlSentenceStructureDiagramNotation) => { + if ( + xmlSentenceStructureDiagramNotation[ + "sentence-structure-diagram-notation" + ]["@_enable-reflow"] === "false" + ) { + return { + canvas: xmlCanvasToCanvas.decode( + xmlSentenceStructureDiagramNotation[ + "sentence-structure-diagram-notation" + ].canvas, + ), + theme: xmlThemeToTheme.decode( + xmlSentenceStructureDiagramNotation[ + "sentence-structure-diagram-notation" + ].theme, + ), + enableReflow: false, + sentenceStructureElementNotation: + xmlSentenceStructureElementNotationToSentenceStructureElementNotation.decode( + xmlSentenceStructureDiagramNotation[ + "sentence-structure-diagram-notation" + ]["sentence-structure-element-notation"], + ), + modificationNotation: + xmlModificationNotationToModificationNotation.decode( + xmlSentenceStructureDiagramNotation[ + "sentence-structure-diagram-notation" + ]["modification-notation"], + ), + coordinationNotation: { + layout: { + direction: + xmlSentenceStructureDiagramNotation[ + "sentence-structure-diagram-notation" + ]["coordination-notation"].layout["@_direction"], + }, + rangeMarking: { + coordinator: xmlRangeMarkerToRangeMarker.decode( + xmlSentenceStructureDiagramNotation[ + "sentence-structure-diagram-notation" + ]["coordination-notation"]["range-marking"].coordinator, + ), + correlative: xmlRangeMarkerToRangeMarker.decode( + xmlSentenceStructureDiagramNotation[ + "sentence-structure-diagram-notation" + ]["coordination-notation"]["range-marking"].correlative, + ), + conjunct: xmlRangeMarkerToRangeMarker.decode( + xmlSentenceStructureDiagramNotation[ + "sentence-structure-diagram-notation" + ]["coordination-notation"]["range-marking"].conjunct, + ), + }, + groupIndicator: xmlSentenceStructureDiagramNotation[ + "sentence-structure-diagram-notation" + ]["coordination-notation"]["group-indicator"] + ? { + type: "bus-connector", + color: + xmlSentenceStructureDiagramNotation[ + "sentence-structure-diagram-notation" + ]["coordination-notation"]["group-indicator"][ + "bus-connector" + ]["@_color"], + } + : { + type: "none", + }, + }, + } satisfies SentenceStructureDiagramNotation; + } else { + return { + canvas: xmlCanvasToCanvas.decode( + xmlSentenceStructureDiagramNotation[ + "sentence-structure-diagram-notation" + ].canvas, + ), + theme: xmlThemeToTheme.decode( + xmlSentenceStructureDiagramNotation[ + "sentence-structure-diagram-notation" + ].theme, + ), + enableReflow: true, + sentenceStructureElementNotation: + xmlSentenceStructureElementNotationToSentenceStructureElementNotation.decode( + xmlSentenceStructureDiagramNotation[ + "sentence-structure-diagram-notation" + ]["sentence-structure-element-notation"], + ), + modificationNotation: + xmlModificationNotationToModificationNotation.decode( + xmlSentenceStructureDiagramNotation[ + "sentence-structure-diagram-notation" + ]["modification-notation"], + ), + coordinationNotation: { + layout: { + direction: + xmlSentenceStructureDiagramNotation[ + "sentence-structure-diagram-notation" + ]["coordination-notation"].layout["@_direction"], + }, + rangeMarking: { + coordinator: xmlRangeMarkerToRangeMarker.decode( + xmlSentenceStructureDiagramNotation[ + "sentence-structure-diagram-notation" + ]["coordination-notation"]["range-marking"].coordinator, + ), + correlative: xmlRangeMarkerToRangeMarker.decode( + xmlSentenceStructureDiagramNotation[ + "sentence-structure-diagram-notation" + ]["coordination-notation"]["range-marking"].correlative, + ), + conjunct: xmlRangeMarkerToRangeMarker.decode( + xmlSentenceStructureDiagramNotation[ + "sentence-structure-diagram-notation" + ]["coordination-notation"]["range-marking"].conjunct, + ), + }, + groupIndicator: xmlSentenceStructureDiagramNotation[ + "sentence-structure-diagram-notation" + ]["coordination-notation"]["group-indicator"] + ? "bracket" in + xmlSentenceStructureDiagramNotation[ + "sentence-structure-diagram-notation" + ]["coordination-notation"]["group-indicator"] + ? { + type: "bracket", + bracketType: + xmlSentenceStructureDiagramNotation[ + "sentence-structure-diagram-notation" + ]["coordination-notation"]["group-indicator"].bracket[ + "@_bracket-type" + ], + placement: + xmlSentenceStructureDiagramNotation[ + "sentence-structure-diagram-notation" + ]["coordination-notation"]["group-indicator"].bracket[ + "@_placement" + ], + color: + xmlSentenceStructureDiagramNotation[ + "sentence-structure-diagram-notation" + ]["coordination-notation"]["group-indicator"].bracket[ + "@_color" + ], + } + : { + type: "bus-connector", + color: + xmlSentenceStructureDiagramNotation[ + "sentence-structure-diagram-notation" + ]["coordination-notation"]["group-indicator"][ + "bus-connector" + ]["@_color"], + } + : { + type: "none", + }, + }, + layoutStrategy: + xmlSentenceStructureDiagramNotation[ + "sentence-structure-diagram-notation" + ]["layout-strategy"]["@_line-break-strategy"] === + "greedy-word-wrap" + ? { + lineBreakStrategy: "greedy-word-wrap", + } + : { + lineBreakStrategy: "largest-boundary-first", + continuationLineStart: + xmlSentenceStructureDiagramNotation[ + "sentence-structure-diagram-notation" + ]["layout-strategy"]["@_continuation-line-start"], + }, + } satisfies SentenceStructureDiagramNotation; + } + }, + encode: (sentenceStructureDiagramNotation) => { + if (!sentenceStructureDiagramNotation.enableReflow) { + return { + "sentence-structure-diagram-notation": { + "@_xmlns": "https://utlis.github.io/sv-marker/", + "@_version": "0.1.0", + canvas: xmlCanvasToCanvas.encode( + sentenceStructureDiagramNotation.canvas, + ), + theme: xmlThemeToTheme.encode( + sentenceStructureDiagramNotation.theme, + ), + "@_enable-reflow": "false", + "sentence-structure-element-notation": + xmlSentenceStructureElementNotationToSentenceStructureElementNotation.encode( + sentenceStructureDiagramNotation.sentenceStructureElementNotation, + ), + "modification-notation": + xmlModificationNotationToModificationNotation.encode( + sentenceStructureDiagramNotation.modificationNotation, + ), + "coordination-notation": { + layout: { + "@_direction": + sentenceStructureDiagramNotation.coordinationNotation.layout + .direction, + }, + "range-marking": { + coordinator: xmlRangeMarkerToRangeMarker.encode( + sentenceStructureDiagramNotation.coordinationNotation + .rangeMarking.coordinator, + ), + correlative: xmlRangeMarkerToRangeMarker.encode( + sentenceStructureDiagramNotation.coordinationNotation + .rangeMarking.correlative, + ), + conjunct: xmlRangeMarkerToRangeMarker.encode( + sentenceStructureDiagramNotation.coordinationNotation + .rangeMarking.conjunct, + ), + }, + "group-indicator": + sentenceStructureDiagramNotation.coordinationNotation + .groupIndicator.type === "bus-connector" + ? { + "bus-connector": { + "@_color": + sentenceStructureDiagramNotation + .coordinationNotation.groupIndicator.color, + }, + } + : undefined, + }, + }, + } satisfies z.infer; + } else { + return { + "sentence-structure-diagram-notation": { + "@_xmlns": "https://utlis.github.io/sv-marker/", + "@_version": "0.1.0", + canvas: xmlCanvasToCanvas.encode( + sentenceStructureDiagramNotation.canvas, + ), + theme: xmlThemeToTheme.encode( + sentenceStructureDiagramNotation.theme, + ), + "@_enable-reflow": "true", + "sentence-structure-element-notation": + xmlSentenceStructureElementNotationToSentenceStructureElementNotation.encode( + sentenceStructureDiagramNotation.sentenceStructureElementNotation, + ), + "modification-notation": + xmlModificationNotationToModificationNotation.encode( + sentenceStructureDiagramNotation.modificationNotation, + ), + "coordination-notation": { + layout: { + "@_direction": + sentenceStructureDiagramNotation.coordinationNotation.layout + .direction, + }, + "range-marking": { + coordinator: xmlRangeMarkerToRangeMarker.encode( + sentenceStructureDiagramNotation.coordinationNotation + .rangeMarking.coordinator, + ), + correlative: xmlRangeMarkerToRangeMarker.encode( + sentenceStructureDiagramNotation.coordinationNotation + .rangeMarking.correlative, + ), + conjunct: xmlRangeMarkerToRangeMarker.encode( + sentenceStructureDiagramNotation.coordinationNotation + .rangeMarking.conjunct, + ), + }, + "group-indicator": + sentenceStructureDiagramNotation.coordinationNotation + .groupIndicator.type === "bracket" + ? { + bracket: { + "@_bracket-type": + sentenceStructureDiagramNotation + .coordinationNotation.groupIndicator.bracketType, + "@_placement": + sentenceStructureDiagramNotation + .coordinationNotation.groupIndicator.placement, + "@_color": + sentenceStructureDiagramNotation + .coordinationNotation.groupIndicator.color, + }, + } + : sentenceStructureDiagramNotation.coordinationNotation + .groupIndicator.type === "bus-connector" + ? { + "bus-connector": { + "@_color": + sentenceStructureDiagramNotation + .coordinationNotation.groupIndicator.color, + }, + } + : undefined, + }, + "layout-strategy": + sentenceStructureDiagramNotation.layoutStrategy + .lineBreakStrategy === "greedy-word-wrap" + ? { + "@_line-break-strategy": "greedy-word-wrap", + } + : { + "@_line-break-strategy": "largest-boundary-first", + "@_continuation-line-start": + sentenceStructureDiagramNotation.layoutStrategy + .continuationLineStart, + }, + }, + } satisfies z.infer; + } + }, + }, + ); + +export const xmlStringToSentenceStructureDiagramNotation = z.codec( + z.string(), + SentenceStructureDiagramNotationSchema, + { + decode: (xmlString) => { + try { + return xmlSentenceStructureDiagramNotationToSentenceStructureDiagramNotation.decode( + XMLSentenceStructureDiagramNotation.parse( + new XMLParser({ + ignoreAttributes: false, + parseTagValue: false, + }).parse(xmlString), + ), + ) satisfies SentenceStructureDiagramNotation; + } catch { + return null as any; + } + }, + encode: (sentenceStructureDiagramNotation) => + new XMLBuilder({ + format: true, + ignoreAttributes: false, + suppressEmptyNode: true, + }).build( + xmlSentenceStructureDiagramNotationToSentenceStructureDiagramNotation.encode( + sentenceStructureDiagramNotation, + ) satisfies z.infer, + ), + }, +); diff --git a/packages/sentence-structure-diagram-notation/index.ts b/packages/sentence-structure-diagram-notation/index.ts new file mode 100644 index 0000000..f289611 --- /dev/null +++ b/packages/sentence-structure-diagram-notation/index.ts @@ -0,0 +1,11 @@ +export type { + HexRGBColor, + SentenceStructureDiagramNotation, +} from "./schema.js"; +export { presets } from "./presets/index.js"; +export { + createSentenceStructureDiagramNotationFromJSONString, + createSentenceStructureDiagramNotationFromXMLString, + sentenceStructureDiagramNotationToJSONString, + sentenceStructureDiagramNotationToXMLString, +} from "./operations.js"; diff --git a/packages/sentence-structure-diagram-notation/operations.ts b/packages/sentence-structure-diagram-notation/operations.ts new file mode 100644 index 0000000..e72ba85 --- /dev/null +++ b/packages/sentence-structure-diagram-notation/operations.ts @@ -0,0 +1,77 @@ +import type { SentenceStructureDiagramNotation } from "./schema.js"; +import { jsonStringToSentenceStructureDiagramNotation } from "./codecs/json-codec.js"; +import { xmlStringToSentenceStructureDiagramNotation } from "./codecs/xml-codec.js"; + +type Result = + | { + success: true; + data: T; + } + | { + success: false; + message: string; + }; + +export function createSentenceStructureDiagramNotationFromJSONString( + jsonString: string, +): Result<{ + newSentenceStructureDiagramNotation: SentenceStructureDiagramNotation; +}> { + const newSentenceStructureDiagramNotation = + jsonStringToSentenceStructureDiagramNotation.safeDecode(jsonString); + + if (newSentenceStructureDiagramNotation.success) { + return { + success: true, + data: { + newSentenceStructureDiagramNotation: + newSentenceStructureDiagramNotation.data, + }, + }; + } + + return { + success: false, + message: "フォーマットが正しくありません。", + }; +} + +export function createSentenceStructureDiagramNotationFromXMLString( + xmlString: string, +): Result<{ + newSentenceStructureDiagramNotation: SentenceStructureDiagramNotation; +}> { + const newSentenceStructureDiagramNotation = + xmlStringToSentenceStructureDiagramNotation.safeDecode(xmlString); + + if (newSentenceStructureDiagramNotation.success) { + return { + success: true, + data: { + newSentenceStructureDiagramNotation: + newSentenceStructureDiagramNotation.data, + }, + }; + } + + return { + success: false, + message: "フォーマットが正しくありません。", + }; +} + +export function sentenceStructureDiagramNotationToJSONString( + sentenceStructureDiagramNotation: SentenceStructureDiagramNotation, +): string { + return jsonStringToSentenceStructureDiagramNotation.encode( + sentenceStructureDiagramNotation, + ); +} + +export function sentenceStructureDiagramNotationToXMLString( + sentenceStructureDiagramNotation: SentenceStructureDiagramNotation, +): string { + return xmlStringToSentenceStructureDiagramNotation.encode( + sentenceStructureDiagramNotation, + ); +} diff --git a/packages/sentence-structure-diagram-configurations/package.json b/packages/sentence-structure-diagram-notation/package.json similarity index 75% rename from packages/sentence-structure-diagram-configurations/package.json rename to packages/sentence-structure-diagram-notation/package.json index 188cf55..171d322 100644 --- a/packages/sentence-structure-diagram-configurations/package.json +++ b/packages/sentence-structure-diagram-notation/package.json @@ -1,5 +1,5 @@ { - "name": "@sentence-structure-diagram-app/sentence-structure-diagram-configurations", + "name": "@sentence-structure-diagram-app/sentence-structure-diagram-notation", "version": "0.1.0", "type": "module", "main": "./dist/index.js", @@ -9,9 +9,8 @@ "clean": "rm -r dist" }, "dependencies": { - "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0", "fast-xml-parser": "^5.3.3", - "zod": "^4.2.1" + "zod": "^4.3.5" }, "devDependencies": { "typescript": "^5.9.3" diff --git a/packages/sentence-structure-diagram-notation/presets/index.ts b/packages/sentence-structure-diagram-notation/presets/index.ts new file mode 100644 index 0000000..8a7f598 --- /dev/null +++ b/packages/sentence-structure-diagram-notation/presets/index.ts @@ -0,0 +1,8 @@ +import type { SentenceStructureDiagramNotation } from "../schema.js"; +import { originalLayoutAnnotationPreset } from "./original-layout-annotation.js"; +import { reflowLayoutAnnotationPreset } from "./reflow-layout-annotation.js"; + +export const presets = { + "original-layout-annotation": originalLayoutAnnotationPreset, + "reflow-layout-annotation": reflowLayoutAnnotationPreset, +} as const satisfies Record; diff --git a/packages/sentence-structure-diagram-notation/presets/original-layout-annotation.ts b/packages/sentence-structure-diagram-notation/presets/original-layout-annotation.ts new file mode 100644 index 0000000..dd56d40 --- /dev/null +++ b/packages/sentence-structure-diagram-notation/presets/original-layout-annotation.ts @@ -0,0 +1,167 @@ +import type { SentenceStructureDiagramNotation } from "../schema.js"; + +export const originalLayoutAnnotationPreset: SentenceStructureDiagramNotation = + { + canvas: { + width: 1200, + }, + theme: { + colors: { + primary: "#1976d2", + text: "#000000", + background: "#e0e0e0", + }, + typography: { + fontSize: 16, + }, + spacing: { + padding: 48, + wordSpacing: 32, + lineSpacing: 48, + continuationIndent: 32, + }, + }, + enableReflow: false, + sentenceStructureElementNotation: { + rangeMarking: { + coreSentenceElement: { + type: "underline", + lineStyle: "solid", + color: "primary", + }, + sentenceConstituent: { + phrase: { + nominal: { + type: "bracket", + bracketType: "angle-bracket", + color: "primary", + }, + adjectival: { + type: "bracket", + bracketType: "angle-bracket", + color: "primary", + }, + adverbial: { + type: "bracket", + bracketType: "angle-bracket", + color: "primary", + }, + }, + clause: { + nominal: { + type: "bracket", + bracketType: "square-bracket", + color: "primary", + }, + adjectival: { + type: "bracket", + bracketType: "square-bracket", + color: "primary", + }, + adverbial: { + type: "bracket", + bracketType: "square-bracket", + color: "primary", + }, + }, + adverbialPhrase: { + type: "bracket", + bracketType: "parenthesis", + color: "primary", + }, + }, + modificationElement: { + type: "underline", + lineStyle: "solid", + color: "primary", + }, + }, + sentenceElementLabeling: { + labels: { + S: "S", + V: "V", + O: "O", + C: "C", + M: "M", + }, + placement: { + coreSentenceElement: "below-center", + sentenceConstituent: { + phrase: { + nominal: "below-left", + adjectival: "below-left", + adverbial: "below-left", + }, + clause: { + nominal: "below-left", + adjectival: "below-left", + adverbial: "below-left", + }, + adverbialPhrase: "below-left", + }, + }, + color: "primary", + labelSuffixes: { + showNestingDepthPrimes: true, + showConjunctNumbering: true, + }, + }, + sentenceConstituentLabeling: { + labels: { + phrase: { + nominal: "名詞句", + adjectival: "形容詞句", + adverbial: "副詞句", + }, + clause: { + nominal: "名詞節", + adjectival: "形容詞節", + adverbial: "副詞節", + }, + adverbialPhrase: "", + }, + placement: { + phrase: { + nominal: "above-left", + adjectival: "above-left", + adverbial: "above-left", + }, + clause: { + nominal: "above-left", + adjectival: "above-left", + adverbial: "above-left", + }, + adverbialPhrase: "above-left", + }, + color: "text", + }, + }, + modificationNotation: { + arrow: { + type: "curved", + color: "primary", + }, + }, + coordinationNotation: { + layout: { + direction: "horizontal", + }, + rangeMarking: { + coordinator: { + type: "box", + color: "text", + }, + correlative: { + type: "box", + color: "text", + }, + conjunct: { + type: "none", + }, + }, + groupIndicator: { + type: "bus-connector", + color: "primary", + }, + }, + }; diff --git a/packages/sentence-structure-diagram-notation/presets/reflow-layout-annotation.ts b/packages/sentence-structure-diagram-notation/presets/reflow-layout-annotation.ts new file mode 100644 index 0000000..ff25aa5 --- /dev/null +++ b/packages/sentence-structure-diagram-notation/presets/reflow-layout-annotation.ts @@ -0,0 +1,172 @@ +import type { SentenceStructureDiagramNotation } from "../schema.js"; + +export const reflowLayoutAnnotationPreset: SentenceStructureDiagramNotation = { + canvas: { + width: 1200, + }, + theme: { + colors: { + primary: "#1976d2", + text: "#000000", + background: "#e0e0e0", + }, + typography: { + fontSize: 16, + }, + spacing: { + padding: 48, + wordSpacing: 32, + lineSpacing: 48, + continuationIndent: 32, + }, + }, + enableReflow: true, + sentenceStructureElementNotation: { + rangeMarking: { + coreSentenceElement: { + type: "underline", + lineStyle: "solid", + color: "primary", + }, + sentenceConstituent: { + phrase: { + nominal: { + type: "bracket", + bracketType: "angle-bracket", + color: "primary", + }, + adjectival: { + type: "bracket", + bracketType: "angle-bracket", + color: "primary", + }, + adverbial: { + type: "bracket", + bracketType: "angle-bracket", + color: "primary", + }, + }, + clause: { + nominal: { + type: "bracket", + bracketType: "square-bracket", + color: "primary", + }, + adjectival: { + type: "bracket", + bracketType: "square-bracket", + color: "primary", + }, + adverbial: { + type: "bracket", + bracketType: "square-bracket", + color: "primary", + }, + }, + adverbialPhrase: { + type: "bracket", + bracketType: "parenthesis", + color: "primary", + }, + }, + modificationElement: { + type: "underline", + lineStyle: "solid", + color: "primary", + }, + }, + sentenceElementLabeling: { + labels: { + S: "S", + V: "V", + O: "O", + C: "C", + M: "M", + }, + placement: { + coreSentenceElement: "below-center", + sentenceConstituent: { + phrase: { + nominal: "below-left", + adjectival: "below-left", + adverbial: "below-left", + }, + clause: { + nominal: "below-left", + adjectival: "below-left", + adverbial: "below-left", + }, + adverbialPhrase: "below-left", + }, + }, + color: "primary", + labelSuffixes: { + showNestingDepthPrimes: true, + showConjunctNumbering: true, + }, + }, + sentenceConstituentLabeling: { + labels: { + phrase: { + nominal: "", + adjectival: "", + adverbial: "", + }, + clause: { + nominal: "", + adjectival: "", + adverbial: "", + }, + adverbialPhrase: "", + }, + placement: { + phrase: { + nominal: "above-left", + adjectival: "above-left", + adverbial: "above-left", + }, + clause: { + nominal: "above-left", + adjectival: "above-left", + adverbial: "above-left", + }, + adverbialPhrase: "above-left", + }, + color: "text", + }, + }, + modificationNotation: { + arrow: { + type: "curved", + color: "primary", + }, + }, + coordinationNotation: { + layout: { + direction: "vertical", + }, + rangeMarking: { + coordinator: { + type: "box", + color: "text", + }, + correlative: { + type: "box", + color: "text", + }, + conjunct: { + type: "none", + }, + }, + groupIndicator: { + type: "bracket", + placement: "left", + bracketType: "square-bracket", + color: "primary", + }, + }, + layoutStrategy: { + lineBreakStrategy: "largest-boundary-first", + continuationLineStart: "scope-start", + }, +}; diff --git a/packages/sentence-structure-diagram-notation/schema.ts b/packages/sentence-structure-diagram-notation/schema.ts new file mode 100644 index 0000000..ca64965 --- /dev/null +++ b/packages/sentence-structure-diagram-notation/schema.ts @@ -0,0 +1,233 @@ +import * as z from "zod"; + +export const CanvasSchema = z.object({ + width: z.number(), +}); + +export const HexRGBColorSchema = z.templateLiteral(["#", z.hex().length(6)]); +export type HexRGBColor = z.infer; + +export const ThemeSchema = z.object({ + colors: z.object({ + primary: HexRGBColorSchema, + text: HexRGBColorSchema, + background: HexRGBColorSchema, + }), + typography: z.object({ + fontSize: z.number(), + }), + spacing: z.object({ + padding: z.number(), + wordSpacing: z.number(), + lineSpacing: z.number(), + continuationIndent: z.number(), + }), +}); + +export const lineStyleOptions = ["solid", "dashed"] as const; + +export const colorOptions = ["primary", "text", "background"] as const; + +export const bracketTypeOptions = [ + "parenthesis", + "angle-bracket", + "curly-bracket", + "square-bracket", +] as const; + +export const RangeMarkerSchema = z.union([ + z.object({ + type: z.literal("underline"), + lineStyle: z.literal(lineStyleOptions), + color: z.literal(colorOptions), + }), + z.object({ + type: z.literal("bracket"), + bracketType: z.literal(bracketTypeOptions), + color: z.literal(colorOptions), + }), + z.object({ + type: z.literal("box"), + color: z.literal(colorOptions), + }), + z.object({ + type: z.literal("text-emphasis"), + color: z.literal(colorOptions), + }), + z.object({ + type: z.literal("highlight"), + color: z.literal(colorOptions), + }), + z.object({ + type: z.literal("bold"), + }), + z.object({ + type: z.literal("none"), + }), +]); + +export const labelPlacementOptions = [ + "below-center", + "below-left", + "above-center", + "above-left", +] as const; + +export const SentenceStructureElementNotationSchema = z.object({ + rangeMarking: z.object({ + coreSentenceElement: RangeMarkerSchema, + sentenceConstituent: z.object({ + phrase: z.object({ + nominal: RangeMarkerSchema, + adjectival: RangeMarkerSchema, + adverbial: RangeMarkerSchema, + }), + clause: z.object({ + nominal: RangeMarkerSchema, + adjectival: RangeMarkerSchema, + adverbial: RangeMarkerSchema, + }), + adverbialPhrase: RangeMarkerSchema, + }), + modificationElement: RangeMarkerSchema, + }), + sentenceElementLabeling: z.object({ + labels: z.object({ + S: z.string(), + V: z.string(), + O: z.string(), + C: z.string(), + M: z.string(), + }), + placement: z.object({ + coreSentenceElement: z.literal(labelPlacementOptions), + sentenceConstituent: z.object({ + phrase: z.object({ + nominal: z.literal(labelPlacementOptions), + adjectival: z.literal(labelPlacementOptions), + adverbial: z.literal(labelPlacementOptions), + }), + clause: z.object({ + nominal: z.literal(labelPlacementOptions), + adjectival: z.literal(labelPlacementOptions), + adverbial: z.literal(labelPlacementOptions), + }), + adverbialPhrase: z.literal(labelPlacementOptions), + }), + }), + color: z.literal(colorOptions), + labelSuffixes: z.object({ + showNestingDepthPrimes: z.boolean(), + showConjunctNumbering: z.boolean(), + }), + }), + sentenceConstituentLabeling: z.object({ + labels: z.object({ + phrase: z.object({ + nominal: z.string(), + adjectival: z.string(), + adverbial: z.string(), + }), + clause: z.object({ + nominal: z.string(), + adjectival: z.string(), + adverbial: z.string(), + }), + adverbialPhrase: z.string(), + }), + placement: z.object({ + phrase: z.object({ + nominal: z.literal(labelPlacementOptions), + adjectival: z.literal(labelPlacementOptions), + adverbial: z.literal(labelPlacementOptions), + }), + clause: z.object({ + nominal: z.literal(labelPlacementOptions), + adjectival: z.literal(labelPlacementOptions), + adverbial: z.literal(labelPlacementOptions), + }), + adverbialPhrase: z.literal(labelPlacementOptions), + }), + color: z.literal(colorOptions), + }), +}); + +export const ModificationNotationSchema = z.object({ + arrow: z.object({ + type: z.literal(["curved", "orthogonal"]), + color: z.literal(colorOptions), + }), +}); + +export const SentenceStructureDiagramNotationSchema = z.union([ + z.object({ + canvas: CanvasSchema, + theme: ThemeSchema, + enableReflow: z.literal(false), + sentenceStructureElementNotation: SentenceStructureElementNotationSchema, + modificationNotation: ModificationNotationSchema, + coordinationNotation: z.object({ + layout: z.object({ + direction: z.literal("horizontal"), + }), + rangeMarking: z.object({ + coordinator: RangeMarkerSchema, + correlative: RangeMarkerSchema, + conjunct: RangeMarkerSchema, + }), + groupIndicator: z.union([ + z.object({ + type: z.literal("bus-connector"), + color: z.literal(colorOptions), + }), + z.object({ + type: z.literal("none"), + }), + ]), + }), + }), + z.object({ + canvas: CanvasSchema, + theme: ThemeSchema, + enableReflow: z.literal(true), + sentenceStructureElementNotation: SentenceStructureElementNotationSchema, + modificationNotation: ModificationNotationSchema, + coordinationNotation: z.object({ + layout: z.object({ + direction: z.literal("vertical"), + }), + rangeMarking: z.object({ + coordinator: RangeMarkerSchema, + correlative: RangeMarkerSchema, + conjunct: RangeMarkerSchema, + }), + groupIndicator: z.union([ + z.object({ + type: z.literal("bracket"), + bracketType: z.literal(bracketTypeOptions), + placement: z.literal(["left", "both-sides"]), + color: z.literal(colorOptions), + }), + z.object({ + type: z.literal("bus-connector"), + color: z.literal(colorOptions), + }), + z.object({ + type: z.literal("none"), + }), + ]), + }), + layoutStrategy: z.union([ + z.object({ + lineBreakStrategy: z.literal("greedy-word-wrap"), + }), + z.object({ + lineBreakStrategy: z.literal("largest-boundary-first"), + continuationLineStart: z.literal(["content-start", "scope-start"]), + }), + ]), + }), +]); +export type SentenceStructureDiagramNotation = z.infer< + typeof SentenceStructureDiagramNotationSchema +>; diff --git a/packages/sentence-structure-diagram-svg/tsconfig.json b/packages/sentence-structure-diagram-notation/tsconfig.json similarity index 100% rename from packages/sentence-structure-diagram-svg/tsconfig.json rename to packages/sentence-structure-diagram-notation/tsconfig.json diff --git a/packages/sentence-structure-diagram-svg/SentenceStructureDiagram.tsx b/packages/sentence-structure-diagram-svg/SentenceStructureDiagram.tsx deleted file mode 100644 index ad116bb..0000000 --- a/packages/sentence-structure-diagram-svg/SentenceStructureDiagram.tsx +++ /dev/null @@ -1,142 +0,0 @@ -import { Fragment } from "react"; -import { - sentenceStructureDataToXMLString, - type SentenceStructureData, -} from "@sentence-structure-diagram-app/sentence-structure-data"; -import { - xmlStringToConfigurations, - type Configurations, -} from "@sentence-structure-diagram-app/sentence-structure-diagram-configurations"; -import { convertSentenceStructureDataToSentenceStructureDiagramData } from "@sentence-structure-diagram-app/sentence-structure-diagram-data"; - -type SentenceStructureDiagramProps = { - sentenceStructureData: SentenceStructureData; - maxWidth: number; - measureTextWidth: (text: string) => number; - configurations: Configurations; -}; - -export default function SentenceStructureDiagram( - props: SentenceStructureDiagramProps, -) { - const sentenceStructureDiagramData = - convertSentenceStructureDataToSentenceStructureDiagramData( - props.sentenceStructureData, - props.maxWidth, - props.measureTextWidth, - props.configurations, - ); - return ( - - - ${sentenceStructureDataToXMLString(props.sentenceStructureData)} - - - ${xmlStringToConfigurations.encode(props.configurations)} - -`, - }} - > - {/* 単語と括弧類 */} - {sentenceStructureDiagramData.words.map((word) => ( - - {word.openingBrackets.length > 0 && ( - - {word.openingBrackets.join("")} - - )} - - {word.text} - - {word.closingBrackets.length > 0 && ( - - {word.closingBrackets.join("")} - - )} - - ))} - {/* 下線 */} - {sentenceStructureDiagramData.underlines.map((underline) => - underline.position.map((position, index) => ( - - )), - )} - {/* 文の要素の記号 */} - {sentenceStructureDiagramData.sentenceElements.map( - (sentenceElement) => - sentenceElement.symbol && ( - - {sentenceElement.symbol} - - ), - )} - {/* 修飾 */} - {sentenceStructureDiagramData.relations.map((relation) => ( - - ))} - {/* 並列 */} - {sentenceStructureDiagramData.coordinations.map((coordination) => ( - - ))} - - ); -} diff --git a/packages/sentence-structure-diagram-svg/generate.ts b/packages/sentence-structure-diagram-svg/generate.ts deleted file mode 100644 index 4528cb0..0000000 --- a/packages/sentence-structure-diagram-svg/generate.ts +++ /dev/null @@ -1,28 +0,0 @@ -import ReactDOMServer from "react-dom/server"; -// import prettier from "prettier/standalone"; -// import prettierPluginHtml from "prettier/plugins/html"; -import type { SentenceStructureData } from "@sentence-structure-diagram-app/sentence-structure-data"; -import type { Configurations } from "@sentence-structure-diagram-app/sentence-structure-diagram-configurations"; -import SentenceStructureDiagram from "./SentenceStructureDiagram.js"; - -export function generateSvgString( - sentenceStructureData: SentenceStructureData, - maxWidth: number, - measureTextWidth: (text: string) => number, - configurations: Configurations, -): string { - // return await prettier.format( - return ReactDOMServer.renderToStaticMarkup( - SentenceStructureDiagram({ - sentenceStructureData, - maxWidth, - measureTextWidth, - configurations, - }), - ); - // { - // parser: "html", - // plugins: [prettierPluginHtml], - // }, - // ); -} diff --git a/packages/sentence-structure-diagram-svg/index.ts b/packages/sentence-structure-diagram-svg/index.ts deleted file mode 100644 index 6619bc8..0000000 --- a/packages/sentence-structure-diagram-svg/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { generateSvgString } from "./generate.js"; diff --git a/packages/sentence-structure-diagram-svg/package.json b/packages/sentence-structure-diagram-svg/package.json deleted file mode 100644 index 2012eda..0000000 --- a/packages/sentence-structure-diagram-svg/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "@sentence-structure-diagram-app/sentence-structure-diagram-svg", - "version": "0.1.0", - "type": "module", - "main": "./dist/index.js", - "scripts": { - "build": "tsc", - "build:watch": "tsc --watch", - "clean": "rm -r dist" - }, - "dependencies": { - "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0", - "@sentence-structure-diagram-app/sentence-structure-diagram-configurations": "^0.1.0", - "@sentence-structure-diagram-app/sentence-structure-diagram-data": "^0.1.0", - "prettier": "^3.7.4", - "react": "^19.2.3", - "react-dom": "^19.2.3" - }, - "devDependencies": { - "typescript": "^5.9.3" - } -} diff --git a/packages/sentence-structure-diagram-tree/constants.ts b/packages/sentence-structure-diagram-tree/constants.ts deleted file mode 100644 index cf238aa..0000000 --- a/packages/sentence-structure-diagram-tree/constants.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const lineHeight = 16; -export const lineSpacing = 48; -export const wordSpacing = 32; -export const indentSpacing = 32; -export const padding = 48; diff --git a/packages/sentence-structure-diagram-tree/create-linear-sentence-structure-diagram-tree.ts b/packages/sentence-structure-diagram-tree/create-linear-sentence-structure-diagram-tree.ts deleted file mode 100644 index 18c4706..0000000 --- a/packages/sentence-structure-diagram-tree/create-linear-sentence-structure-diagram-tree.ts +++ /dev/null @@ -1,268 +0,0 @@ -import type { - SentenceStructureCoordinationChildNode, - SentenceStructureCoordinationNode, - SentenceStructureNode, - SentenceStructureRangeNode, - SentenceStructureTree, - SentenceStructureWordNode, -} from "@sentence-structure-diagram-app/sentence-structure-tree"; -import type { - SentenceStructureDiagramCoordinationChildNode, - SentenceStructureDiagramCoordinationNode, - SentenceStructureDiagramNode, - SentenceStructureDiagramTree, - SentenceStructureDiagramRangeNode, - SentenceStructureDiagramWordNode, -} from "./types.js"; -import { lineHeight, lineSpacing, padding, wordSpacing } from "./constants.js"; - -type Input = { - sentenceStructureNode: T; - nextNodeStartPosition: { - start: number; - top: number; - }; - nextLineStartPosition: { - start: number; - top: number; - }; - maxWidth: number; - measureTextWidth: (text: string) => number; -}; - -type Result = { - sentenceStructureDiagramNode: T; - nextNodeStartPosition: { - start: number; - top: number; - }; - nextLineStartPosition: { - start: number; - top: number; - }; -}; - -function createSentenceStructureDiagramWordNode( - input: Input, -): Result { - let nextNodeStartPosition = { ...input.nextNodeStartPosition }; - let nextLineStartPosition = { ...input.nextLineStartPosition }; - if ( - input.maxWidth < - input.nextNodeStartPosition.start + - input.measureTextWidth(input.sentenceStructureNode.word.text) + - padding - ) { - nextNodeStartPosition = { ...input.nextLineStartPosition }; - nextLineStartPosition.top += lineHeight + lineSpacing; - } - return { - sentenceStructureDiagramNode: { - word: input.sentenceStructureNode.word, - position: { - start: nextNodeStartPosition.start, - end: - nextNodeStartPosition.start + - input.measureTextWidth(input.sentenceStructureNode.word.text), - top: nextNodeStartPosition.top, - bottom: nextNodeStartPosition.top + lineHeight, - }, - }, - nextNodeStartPosition: { - start: - nextNodeStartPosition.start + - input.measureTextWidth(input.sentenceStructureNode.word.text) + - wordSpacing, - top: nextNodeStartPosition.top, - }, - nextLineStartPosition, - }; -} - -function createSentenceStructureDiagramRangeNode( - input: Input, -): Result { - let nextNodeStartPosition = { ...input.nextNodeStartPosition }; - let nextLineStartPosition = { ...input.nextLineStartPosition }; - const children: SentenceStructureDiagramRangeNode["children"] = []; - for (const child of input.sentenceStructureNode.children) { - const result = - child.type === "word" - ? createSentenceStructureDiagramWordNode({ - sentenceStructureNode: child, - nextNodeStartPosition, - nextLineStartPosition, - maxWidth: input.maxWidth, - measureTextWidth: input.measureTextWidth, - }) - : child.type === "range" - ? createSentenceStructureDiagramRangeNode({ - sentenceStructureNode: child, - nextNodeStartPosition, - nextLineStartPosition, - maxWidth: input.maxWidth, - measureTextWidth: input.measureTextWidth, - }) - : createSentenceStructureDiagramCoordinationNode({ - sentenceStructureNode: child, - nextNodeStartPosition, - nextLineStartPosition, - maxWidth: input.maxWidth, - measureTextWidth: input.measureTextWidth, - }); - children.push(result.sentenceStructureDiagramNode); - nextNodeStartPosition = { ...result.nextNodeStartPosition }; - nextLineStartPosition = { ...result.nextLineStartPosition }; - } - - return { - sentenceStructureDiagramNode: { - range: input.sentenceStructureNode.range, - position: { - end: nextNodeStartPosition.start - wordSpacing, - top: input.nextNodeStartPosition.top, - bottom: nextLineStartPosition.top - lineSpacing, - }, - children: children, - }, - nextNodeStartPosition, - nextLineStartPosition, - }; -} - -function createSentenceStructureDiagramCoordinationChildNode( - input: Input, -): Result { - let nextNodeStartPosition = { ...input.nextNodeStartPosition }; - let nextLineStartPosition = { ...input.nextLineStartPosition }; - const children: SentenceStructureDiagramRangeNode["children"] = []; - for (const child of input.sentenceStructureNode.children) { - const result = - child.type === "word" - ? createSentenceStructureDiagramWordNode({ - sentenceStructureNode: child, - nextNodeStartPosition, - nextLineStartPosition, - maxWidth: input.maxWidth, - measureTextWidth: input.measureTextWidth, - }) - : child.type === "range" - ? createSentenceStructureDiagramRangeNode({ - sentenceStructureNode: child, - nextNodeStartPosition, - nextLineStartPosition, - maxWidth: input.maxWidth, - measureTextWidth: input.measureTextWidth, - }) - : createSentenceStructureDiagramCoordinationNode({ - sentenceStructureNode: child, - nextNodeStartPosition, - nextLineStartPosition, - maxWidth: input.maxWidth, - measureTextWidth: input.measureTextWidth, - }); - children.push(result.sentenceStructureDiagramNode); - nextNodeStartPosition = { ...result.nextNodeStartPosition }; - nextLineStartPosition = { ...result.nextLineStartPosition }; - } - - return { - sentenceStructureDiagramNode: { - coordinationChild: input.sentenceStructureNode.coordinationChild, - position: { - end: nextNodeStartPosition.start - wordSpacing, - top: input.nextNodeStartPosition.top, - bottom: nextLineStartPosition.top - lineSpacing, - }, - children: children, - }, - nextNodeStartPosition, - nextLineStartPosition, - }; -} - -function createSentenceStructureDiagramCoordinationNode( - input: Input, -): Result { - let nextNodeStartPosition = { ...input.nextNodeStartPosition }; - let nextLineStartPosition = { ...input.nextLineStartPosition }; - const children: SentenceStructureDiagramCoordinationNode["children"] = []; - for (const child of input.sentenceStructureNode.children) { - const result = createSentenceStructureDiagramCoordinationChildNode({ - sentenceStructureNode: child, - nextNodeStartPosition, - nextLineStartPosition, - maxWidth: input.maxWidth, - measureTextWidth: input.measureTextWidth, - }); - children.push(result.sentenceStructureDiagramNode); - nextNodeStartPosition = { ...result.nextNodeStartPosition }; - nextLineStartPosition = { ...result.nextLineStartPosition }; - } - - return { - sentenceStructureDiagramNode: { - coordination: input.sentenceStructureNode.coordination, - position: { - end: nextNodeStartPosition.start - wordSpacing, - top: input.nextNodeStartPosition.top, - bottom: nextLineStartPosition.top - lineSpacing, - }, - children, - }, - nextNodeStartPosition, - nextLineStartPosition, - }; -} - -export function createLinearSentenceStructureDiagramTree( - sentenceStructureTree: SentenceStructureTree, - maxWidth: number, - measureTextWidth: (text: string) => number, -): SentenceStructureDiagramTree { - let nextNodeStartPosition = { start: padding, top: padding }; - let nextLineStartPosition = { - start: padding, - top: padding + lineHeight + lineSpacing, - }; - const children: SentenceStructureDiagramTree["children"] = []; - for (const child of sentenceStructureTree.children) { - const result = - child.type === "word" - ? createSentenceStructureDiagramWordNode({ - sentenceStructureNode: child, - nextNodeStartPosition, - nextLineStartPosition, - maxWidth, - measureTextWidth, - }) - : child.type === "range" - ? createSentenceStructureDiagramRangeNode({ - sentenceStructureNode: child, - nextNodeStartPosition, - nextLineStartPosition, - maxWidth, - measureTextWidth, - }) - : createSentenceStructureDiagramCoordinationNode({ - sentenceStructureNode: child, - nextNodeStartPosition, - nextLineStartPosition, - maxWidth, - measureTextWidth, - }); - children.push(result.sentenceStructureDiagramNode); - nextNodeStartPosition = { ...result.nextNodeStartPosition }; - nextLineStartPosition = { ...result.nextLineStartPosition }; - } - - return { - position: { - start: 0, - end: maxWidth, - top: 0, - bottom: nextLineStartPosition.top - lineSpacing + padding, - }, - children: children, - }; -} diff --git a/packages/sentence-structure-diagram-tree/create-structured-sentence-structure-diagram-tree.ts b/packages/sentence-structure-diagram-tree/create-structured-sentence-structure-diagram-tree.ts deleted file mode 100644 index cf52fc5..0000000 --- a/packages/sentence-structure-diagram-tree/create-structured-sentence-structure-diagram-tree.ts +++ /dev/null @@ -1,391 +0,0 @@ -import type { - SentenceStructureCoordinationChildNode, - SentenceStructureCoordinationNode, - SentenceStructureNode, - SentenceStructureRangeNode, - SentenceStructureTree, - SentenceStructureWordNode, -} from "@sentence-structure-diagram-app/sentence-structure-tree"; -import type { - SentenceStructureDiagramCoordinationChildNode, - SentenceStructureDiagramCoordinationNode, - SentenceStructureDiagramNode, - SentenceStructureDiagramTree, - SentenceStructureDiagramRangeNode, - SentenceStructureDiagramWordNode, -} from "./types.js"; -import { - indentSpacing, - lineHeight, - lineSpacing, - padding, - wordSpacing, -} from "./constants.js"; - -type Input = { - sentenceStructureNode: T; - nextNodeStartPosition: { - start: number; - top: number; - }; - nextLineStartPosition: { - start: number; - top: number; - }; - maxWidth: number; - measureTextWidth: (text: string) => number; -}; - -type Result = { - sentenceStructureDiagramNode: T; - nextNodeStartPosition: { - start: number; - top: number; - }; - nextLineStartPosition: { - start: number; - top: number; - }; -}; - -function createSentenceStructureDiagramWordNode( - input: Input, -): Result { - return { - sentenceStructureDiagramNode: { - word: input.sentenceStructureNode.word, - position: { - start: input.nextNodeStartPosition.start, - end: - input.nextNodeStartPosition.start + - input.measureTextWidth(input.sentenceStructureNode.word.text), - top: input.nextNodeStartPosition.top, - bottom: input.nextNodeStartPosition.top + lineHeight, - }, - }, - nextNodeStartPosition: { - start: - input.nextNodeStartPosition.start + - input.measureTextWidth(input.sentenceStructureNode.word.text) + - wordSpacing, - top: input.nextNodeStartPosition.top, - }, - nextLineStartPosition: input.nextLineStartPosition, - }; -} - -function createSentenceStructureDiagramRangeNode( - input: Input, -): Result { - let nextNodeStartPosition = { ...input.nextNodeStartPosition }; - let nextLineStartPosition = { - start: nextNodeStartPosition.start + indentSpacing, - top: input.nextLineStartPosition.top, - }; - const children: SentenceStructureDiagramRangeNode["children"] = []; - for (const child of input.sentenceStructureNode.children) { - const childWidth = - child.type === "word" - ? createSentenceStructureDiagramWordNode({ - sentenceStructureNode: child, - nextNodeStartPosition, - nextLineStartPosition, - maxWidth: Infinity, - measureTextWidth: input.measureTextWidth, - }).sentenceStructureDiagramNode.position.end - - nextNodeStartPosition.start - : child.type === "range" - ? createSentenceStructureDiagramRangeNode({ - sentenceStructureNode: child, - nextNodeStartPosition, - nextLineStartPosition, - maxWidth: Infinity, - measureTextWidth: input.measureTextWidth, - }).sentenceStructureDiagramNode.position.end - - nextNodeStartPosition.start - : createSentenceStructureDiagramCoordinationNode({ - sentenceStructureNode: child, - nextNodeStartPosition, - nextLineStartPosition, - maxWidth: Infinity, - measureTextWidth: input.measureTextWidth, - }).sentenceStructureDiagramNode.position.end - - nextNodeStartPosition.start; - if ( - nextNodeStartPosition.start !== input.nextNodeStartPosition.start && - input.maxWidth < nextNodeStartPosition.start + childWidth + padding - ) { - nextNodeStartPosition = { ...nextLineStartPosition }; - nextLineStartPosition.start += indentSpacing; - nextLineStartPosition.top += lineHeight + lineSpacing; - } - const result = - child.type === "word" - ? createSentenceStructureDiagramWordNode({ - sentenceStructureNode: child, - nextNodeStartPosition, - nextLineStartPosition, - maxWidth: input.maxWidth, - measureTextWidth: input.measureTextWidth, - }) - : child.type === "range" - ? createSentenceStructureDiagramRangeNode({ - sentenceStructureNode: child, - nextNodeStartPosition, - nextLineStartPosition, - maxWidth: input.maxWidth, - measureTextWidth: input.measureTextWidth, - }) - : createSentenceStructureDiagramCoordinationNode({ - sentenceStructureNode: child, - nextNodeStartPosition, - nextLineStartPosition, - maxWidth: input.maxWidth, - measureTextWidth: input.measureTextWidth, - }); - children.push(result.sentenceStructureDiagramNode); - nextNodeStartPosition = { ...result.nextNodeStartPosition }; - nextLineStartPosition = { ...result.nextLineStartPosition }; - } - - return { - sentenceStructureDiagramNode: { - range: input.sentenceStructureNode.range, - position: { - end: nextNodeStartPosition.start - wordSpacing, - top: input.nextNodeStartPosition.top, - bottom: nextLineStartPosition.top - lineSpacing, - }, - children: children, - }, - nextNodeStartPosition, - nextLineStartPosition: { - start: input.nextLineStartPosition.start, - top: nextLineStartPosition.top, - }, - }; -} - -function createSentenceStructureDiagramCoordinationChildNode( - input: Input, -): Result { - let nextNodeStartPosition = { ...input.nextNodeStartPosition }; - let nextLineStartPosition = { - start: nextNodeStartPosition.start + indentSpacing, - top: input.nextLineStartPosition.top, - }; - const children: SentenceStructureDiagramCoordinationChildNode["children"] = - []; - for (const child of input.sentenceStructureNode.children) { - const childWidth = - child.type === "word" - ? createSentenceStructureDiagramWordNode({ - sentenceStructureNode: child, - nextNodeStartPosition, - nextLineStartPosition, - maxWidth: Infinity, - measureTextWidth: input.measureTextWidth, - }).sentenceStructureDiagramNode.position.end - - nextNodeStartPosition.start - : child.type === "range" - ? createSentenceStructureDiagramRangeNode({ - sentenceStructureNode: child, - nextNodeStartPosition, - nextLineStartPosition, - maxWidth: Infinity, - measureTextWidth: input.measureTextWidth, - }).sentenceStructureDiagramNode.position.end - - nextNodeStartPosition.start - : createSentenceStructureDiagramCoordinationNode({ - sentenceStructureNode: child, - nextNodeStartPosition, - nextLineStartPosition, - maxWidth: Infinity, - measureTextWidth: input.measureTextWidth, - }).sentenceStructureDiagramNode.position.end - - nextNodeStartPosition.start; - if ( - nextNodeStartPosition.start !== input.nextNodeStartPosition.start && - input.maxWidth < nextNodeStartPosition.start + childWidth + padding - ) { - nextNodeStartPosition = { ...nextLineStartPosition }; - nextLineStartPosition.start += indentSpacing; - nextLineStartPosition.top += lineHeight + lineSpacing; - } - const result = - child.type === "word" - ? createSentenceStructureDiagramWordNode({ - sentenceStructureNode: child, - nextNodeStartPosition, - nextLineStartPosition, - maxWidth: input.maxWidth, - measureTextWidth: input.measureTextWidth, - }) - : child.type === "range" - ? createSentenceStructureDiagramRangeNode({ - sentenceStructureNode: child, - nextNodeStartPosition, - nextLineStartPosition, - maxWidth: input.maxWidth, - measureTextWidth: input.measureTextWidth, - }) - : createSentenceStructureDiagramCoordinationNode({ - sentenceStructureNode: child, - nextNodeStartPosition, - nextLineStartPosition, - maxWidth: input.maxWidth, - measureTextWidth: input.measureTextWidth, - }); - children.push(result.sentenceStructureDiagramNode); - nextNodeStartPosition = { ...result.nextNodeStartPosition }; - nextLineStartPosition = { ...result.nextLineStartPosition }; - } - - return { - sentenceStructureDiagramNode: { - coordinationChild: input.sentenceStructureNode.coordinationChild, - position: { - end: nextNodeStartPosition.start - wordSpacing, - top: input.nextNodeStartPosition.top, - bottom: nextLineStartPosition.top - lineSpacing, - }, - children: children, - }, - nextNodeStartPosition, - nextLineStartPosition: { - start: input.nextLineStartPosition.start, - top: nextLineStartPosition.top, - }, - }; -} - -function createSentenceStructureDiagramCoordinationNode( - input: Input, -): Result { - let nextNodeStartPosition = { ...input.nextNodeStartPosition }; - let nextLineStartPosition = { - start: nextNodeStartPosition.start, - top: input.nextLineStartPosition.top, - }; - const children: SentenceStructureDiagramCoordinationNode["children"] = []; - for (const child of input.sentenceStructureNode.children) { - const result = createSentenceStructureDiagramCoordinationChildNode({ - sentenceStructureNode: child, - nextNodeStartPosition, - nextLineStartPosition, - maxWidth: input.maxWidth, - measureTextWidth: input.measureTextWidth, - }); - children.push(result.sentenceStructureDiagramNode); - nextNodeStartPosition = { ...result.nextLineStartPosition }; - nextLineStartPosition = { ...nextNodeStartPosition }; - nextLineStartPosition.top += lineHeight + lineSpacing; - } - - return { - sentenceStructureDiagramNode: { - coordination: input.sentenceStructureNode.coordination, - position: { - end: Math.max(...children.map((child) => child.position.end)), - top: input.nextNodeStartPosition.top, - bottom: nextLineStartPosition.top - lineSpacing, - }, - children, - }, - nextNodeStartPosition: { - start: - Math.max(...children.map((child) => child.position.end)) + wordSpacing, - top: input.nextNodeStartPosition.top, - }, - nextLineStartPosition: { - start: input.nextLineStartPosition.start, - top: nextNodeStartPosition.top, - }, - }; -} - -export function createStructuredSentenceStructureDiagramTree( - sentenceStructureTree: SentenceStructureTree, - maxWidth: number, - measureTextWidth: (text: string) => number, -): SentenceStructureDiagramTree { - let nextNodeStartPosition = { start: padding, top: padding }; - let nextLineStartPosition = { - start: padding, - top: padding + lineHeight + lineSpacing, - }; - const children: SentenceStructureDiagramTree["children"] = []; - for (const child of sentenceStructureTree.children) { - const childWidth = - child.type === "word" - ? createSentenceStructureDiagramWordNode({ - sentenceStructureNode: child, - nextNodeStartPosition, - nextLineStartPosition, - maxWidth: Infinity, - measureTextWidth: measureTextWidth, - }).sentenceStructureDiagramNode.position.end - - nextNodeStartPosition.start - : child.type === "range" - ? createSentenceStructureDiagramRangeNode({ - sentenceStructureNode: child, - nextNodeStartPosition, - nextLineStartPosition, - maxWidth: Infinity, - measureTextWidth: measureTextWidth, - }).sentenceStructureDiagramNode.position.end - - nextNodeStartPosition.start - : createSentenceStructureDiagramCoordinationNode({ - sentenceStructureNode: child, - nextNodeStartPosition, - nextLineStartPosition, - maxWidth: Infinity, - measureTextWidth: measureTextWidth, - }).sentenceStructureDiagramNode.position.end - - nextNodeStartPosition.start; - if ( - padding !== nextNodeStartPosition.start && - maxWidth < nextNodeStartPosition.start + childWidth + padding - ) { - nextNodeStartPosition = { ...nextLineStartPosition }; - nextLineStartPosition.top += lineHeight + lineSpacing; - } - const result = - child.type === "word" - ? createSentenceStructureDiagramWordNode({ - sentenceStructureNode: child, - nextNodeStartPosition, - nextLineStartPosition, - maxWidth: maxWidth, - measureTextWidth: measureTextWidth, - }) - : child.type === "range" - ? createSentenceStructureDiagramRangeNode({ - sentenceStructureNode: child, - nextNodeStartPosition, - nextLineStartPosition, - maxWidth: maxWidth, - measureTextWidth: measureTextWidth, - }) - : createSentenceStructureDiagramCoordinationNode({ - sentenceStructureNode: child, - nextNodeStartPosition, - nextLineStartPosition, - maxWidth: maxWidth, - measureTextWidth: measureTextWidth, - }); - children.push(result.sentenceStructureDiagramNode); - nextNodeStartPosition = { ...result.nextNodeStartPosition }; - nextLineStartPosition = { ...result.nextLineStartPosition }; - } - - return { - position: { - start: 0, - end: maxWidth, - top: 0, - bottom: nextLineStartPosition.top - lineSpacing + padding, - }, - children: children, - }; -} diff --git a/packages/sentence-structure-diagram-tree/create-tree.ts b/packages/sentence-structure-diagram-tree/create-tree.ts deleted file mode 100644 index 85b8711..0000000 --- a/packages/sentence-structure-diagram-tree/create-tree.ts +++ /dev/null @@ -1,26 +0,0 @@ -import type { SentenceStructureTree } from "@sentence-structure-diagram-app/sentence-structure-tree"; -import type { LayoutMode } from "@sentence-structure-diagram-app/sentence-structure-diagram-configurations"; -import type { SentenceStructureDiagramTree } from "./types.js"; -import { createStructuredSentenceStructureDiagramTree } from "./create-structured-sentence-structure-diagram-tree.js"; -import { createLinearSentenceStructureDiagramTree } from "./create-linear-sentence-structure-diagram-tree.js"; - -export function createSentenceStructureDiagramTree( - tree: SentenceStructureTree, - maxWidth: number, - measureTextWidth: (text: string) => number, - layoutMode: LayoutMode, -): SentenceStructureDiagramTree { - if (layoutMode === "structured") { - return createStructuredSentenceStructureDiagramTree( - tree, - maxWidth, - measureTextWidth, - ); - } else { - return createLinearSentenceStructureDiagramTree( - tree, - maxWidth, - measureTextWidth, - ); - } -} diff --git a/packages/sentence-structure-diagram-tree/index.ts b/packages/sentence-structure-diagram-tree/index.ts deleted file mode 100644 index 04f379c..0000000 --- a/packages/sentence-structure-diagram-tree/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -export { createSentenceStructureDiagramTree } from "./create-tree.js"; -export type { - SentenceStructureDiagramWordNode, - SentenceStructureDiagramRangeNode, - SentenceStructureDiagramCoordinationChildNode, - SentenceStructureDiagramCoordinationNode, - SentenceStructureDiagramTree, - SentenceStructureDiagramNode, -} from "./types.js"; diff --git a/packages/sentence-structure-diagram-tree/types.ts b/packages/sentence-structure-diagram-tree/types.ts deleted file mode 100644 index 292b911..0000000 --- a/packages/sentence-structure-diagram-tree/types.ts +++ /dev/null @@ -1,75 +0,0 @@ -import type { - Coordination, - CoordinationChild, - Range, - Word, -} from "@sentence-structure-diagram-app/sentence-structure-data"; - -export type SentenceStructureDiagramWordNode = { - word: Word; - position: { - start: number; - end: number; - top: number; - bottom: number; - }; -}; - -export type SentenceStructureDiagramRangeNode = { - range: Range; - position: { - end: number; - top: number; - bottom: number; - }; - children: ( - | SentenceStructureDiagramWordNode - | SentenceStructureDiagramRangeNode - | SentenceStructureDiagramCoordinationNode - )[]; -}; - -export type SentenceStructureDiagramCoordinationChildNode = { - coordinationChild: CoordinationChild; - position: { - end: number; - top: number; - bottom: number; - }; - children: ( - | SentenceStructureDiagramWordNode - | SentenceStructureDiagramRangeNode - | SentenceStructureDiagramCoordinationNode - )[]; -}; - -export type SentenceStructureDiagramCoordinationNode = { - coordination: Coordination; - position: { - end: number; - top: number; - bottom: number; - }; - children: SentenceStructureDiagramCoordinationChildNode[]; -}; - -export type SentenceStructureDiagramTree = { - position: { - start: number; - end: number; - top: number; - bottom: number; - }; - children: ( - | SentenceStructureDiagramWordNode - | SentenceStructureDiagramRangeNode - | SentenceStructureDiagramCoordinationNode - )[]; -}; - -export type SentenceStructureDiagramNode = - | SentenceStructureDiagramWordNode - | SentenceStructureDiagramRangeNode - | SentenceStructureDiagramCoordinationChildNode - | SentenceStructureDiagramCoordinationNode - | SentenceStructureDiagramTree; diff --git a/packages/sentence-structure-diagram-tree/.gitignore b/packages/sentence-structure-diagram/.gitignore similarity index 100% rename from packages/sentence-structure-diagram-tree/.gitignore rename to packages/sentence-structure-diagram/.gitignore diff --git a/packages/sentence-structure-diagram/constants.ts b/packages/sentence-structure-diagram/constants.ts new file mode 100644 index 0000000..4b562a7 --- /dev/null +++ b/packages/sentence-structure-diagram/constants.ts @@ -0,0 +1,38 @@ +export const underlineRangeMarkerMetrics = { + underlineOffsetY: 2, + strokeWidth: 2, +}; + +export const boxRangeMarkerMetrics = { + outlineOffset: 2, + strokeWidth: 1, +}; + +export const sentenceStructureElementLabelMetrics = { + offsetY: 4, +}; + +export const modificationArrowMetrics = { + curvedRouteOffsetY: 40, + orthogonalRouteOffsetY: 20, + arrowheadSize: 4, + strokeWidth: 2, +}; + +export const coordinationGroupIndicatorMetrics = { + horizontal: { + busConnector: { + attachmentPointOffsetY: 4, + tapLineBaseHeight: 12, + tapLineNestingStepY: 6, + }, + }, + vertical: { + groupIndicatorWidth: 8, + groupIndicatorOffsetX: 6, + bracket: { + offsetY: 2, + }, + }, + strokeWidth: 2, +}; diff --git a/packages/sentence-structure-diagram/create.ts b/packages/sentence-structure-diagram/create.ts new file mode 100644 index 0000000..a4609cb --- /dev/null +++ b/packages/sentence-structure-diagram/create.ts @@ -0,0 +1,44 @@ +import { + sentenceStructureDocumentToSentenceStructureDecoratedDocumentForest, + type SentenceStructureDocument, +} from "@sentence-structure-diagram-app/sentence-structure-data"; +import type { SentenceStructureDiagramNotation } from "@sentence-structure-diagram-app/sentence-structure-diagram-notation"; +import { createSentenceStructureDiagramModelForest } from "./diagram-model/create.js"; +import { createSentenceStructureDiagramLayoutTree } from "./diagram-layout/create.js"; +import type { SentenceStructureDiagramData } from "./diagram-data/types.js"; +import { createSentenceStructureDiagramData as _createSentenceStructureDiagramData } from "./diagram-data/create.js"; +import { createSentenceStructureDiagramSVGString as _createSentenceStructureDiagramSVGString } from "./diagram-svg/create.js"; + +export function createSentenceStructureDiagramData( + sentenceStructureDocument: SentenceStructureDocument, + sentenceStructureDiagramNotation: SentenceStructureDiagramNotation, + measureTextWidth: (text: string, fontSize: number) => number, +): SentenceStructureDiagramData { + return _createSentenceStructureDiagramData( + createSentenceStructureDiagramLayoutTree( + createSentenceStructureDiagramModelForest( + sentenceStructureDocumentToSentenceStructureDecoratedDocumentForest( + sentenceStructureDocument, + ), + sentenceStructureDiagramNotation, + ), + measureTextWidth, + ), + ); +} + +export function createSentenceStructureDiagramSvgString( + sentenceStructureDocument: SentenceStructureDocument, + sentenceStructureDiagramNotation: SentenceStructureDiagramNotation, + measureTextWidth: (text: string, fontSize: number) => number, +): string { + return _createSentenceStructureDiagramSVGString( + sentenceStructureDocument, + sentenceStructureDiagramNotation, + createSentenceStructureDiagramData( + sentenceStructureDocument, + sentenceStructureDiagramNotation, + measureTextWidth, + ), + ); +} diff --git a/packages/sentence-structure-diagram/diagram-data/create.ts b/packages/sentence-structure-diagram/diagram-data/create.ts new file mode 100644 index 0000000..38b2033 --- /dev/null +++ b/packages/sentence-structure-diagram/diagram-data/create.ts @@ -0,0 +1,1735 @@ +import { + boxRangeMarkerMetrics, + coordinationGroupIndicatorMetrics, + modificationArrowMetrics, + sentenceStructureElementLabelMetrics, + underlineRangeMarkerMetrics, +} from "../constants.js"; +import type { + SentenceStructureDiagramLayoutCoordinationNode, + SentenceStructureDiagramLayoutCoordinationPartNode, + SentenceStructureDiagramLayoutModification, + SentenceStructureDiagramLayoutNode, + SentenceStructureDiagramLayoutSentenceStructureElementNode, + SentenceStructureDiagramLayoutTree, + SentenceStructureDiagramLayoutWordNode, +} from "../diagram-layout/types.js"; +import type { PathCommand, SentenceStructureDiagramData } from "./types.js"; + +function createWord( + sentenceStructureDiagramLayoutWordNode: SentenceStructureDiagramLayoutWordNode, +): SentenceStructureDiagramData["words"][number] { + return { + id: sentenceStructureDiagramLayoutWordNode.id, + text: + sentenceStructureDiagramLayoutWordNode.text + + sentenceStructureDiagramLayoutWordNode.whitespaceAfter, + rectangle: { + x: sentenceStructureDiagramLayoutWordNode.rectangle.left, + y: sentenceStructureDiagramLayoutWordNode.rectangle.top, + }, + style: sentenceStructureDiagramLayoutWordNode.style, + }; +} + +function createUnderline( + sentenceStructureDiagramLayoutNode: + | SentenceStructureDiagramLayoutSentenceStructureElementNode + | SentenceStructureDiagramLayoutCoordinationPartNode, +): SentenceStructureDiagramData["underlines"][number] { + if (sentenceStructureDiagramLayoutNode.rangeMarker?.type !== "underline") { + throw new Error("Unreachable"); + } + + function getMaxUnderlineNestingDepth( + node: SentenceStructureDiagramLayoutNode, + ): number { + if (node.type === "word") { + return 0; + } + + return ( + Math.max(...node.children.map(getMaxUnderlineNestingDepth)) + + ("rangeMarker" in node && node.rangeMarker?.type === "underline" ? 1 : 0) + ); + } + + return { + id: sentenceStructureDiagramLayoutNode.id, + lineSegments: sentenceStructureDiagramLayoutNode.rectangles.map( + (rectangle) => ({ + x1: rectangle.left, + x2: rectangle.right, + y: + rectangle.bottom + + underlineRangeMarkerMetrics.underlineOffsetY + + (getMaxUnderlineNestingDepth(sentenceStructureDiagramLayoutNode) - + 1) * + (underlineRangeMarkerMetrics.underlineOffsetY + + underlineRangeMarkerMetrics.strokeWidth), + }), + ), + style: { + ...sentenceStructureDiagramLayoutNode.rangeMarker.style, + strokeWidth: underlineRangeMarkerMetrics.strokeWidth, + }, + }; +} + +function createBracket( + sentenceStructureDiagramLayoutNode: + | SentenceStructureDiagramLayoutSentenceStructureElementNode + | SentenceStructureDiagramLayoutCoordinationPartNode, +): SentenceStructureDiagramData["brackets"][number] { + if (sentenceStructureDiagramLayoutNode.rangeMarker?.type !== "bracket") { + throw new Error("Unreachable"); + } + + return { + id: sentenceStructureDiagramLayoutNode.id, + openingBracket: { + text: sentenceStructureDiagramLayoutNode.rangeMarker.openingBracket.text, + x: sentenceStructureDiagramLayoutNode.rangeMarker.openingBracket.left, + y: sentenceStructureDiagramLayoutNode.rangeMarker.openingBracket.top, + }, + closingBracket: { + text: sentenceStructureDiagramLayoutNode.rangeMarker.closingBracket.text, + x: sentenceStructureDiagramLayoutNode.rangeMarker.closingBracket.left, + y: sentenceStructureDiagramLayoutNode.rangeMarker.closingBracket.top, + }, + style: sentenceStructureDiagramLayoutNode.rangeMarker.style, + }; +} + +function createBox( + sentenceStructureDiagramLayoutNode: + | SentenceStructureDiagramLayoutSentenceStructureElementNode + | SentenceStructureDiagramLayoutCoordinationPartNode, +): SentenceStructureDiagramData["boxes"][number] { + if (sentenceStructureDiagramLayoutNode.rangeMarker?.type !== "box") { + throw new Error("Unreachable"); + } + + return { + id: sentenceStructureDiagramLayoutNode.id, + linePaths: sentenceStructureDiagramLayoutNode.rectangles.map( + (rectangle, index) => { + if ( + index === 0 && + sentenceStructureDiagramLayoutNode.rectangles.length === 1 + ) { + return { + pathCommands: [ + { + type: "move-to", + to: { + x: rectangle.left - boxRangeMarkerMetrics.outlineOffset, + y: rectangle.top - boxRangeMarkerMetrics.outlineOffset, + }, + }, + { + type: "line-to", + to: { + x: rectangle.right + boxRangeMarkerMetrics.outlineOffset, + y: rectangle.top - boxRangeMarkerMetrics.outlineOffset, + }, + }, + { + type: "line-to", + to: { + x: rectangle.right + boxRangeMarkerMetrics.outlineOffset, + y: rectangle.bottom + boxRangeMarkerMetrics.outlineOffset, + }, + }, + { + type: "line-to", + to: { + x: rectangle.left - boxRangeMarkerMetrics.outlineOffset, + y: rectangle.bottom + boxRangeMarkerMetrics.outlineOffset, + }, + }, + { + type: "line-to", + to: { + x: rectangle.left - boxRangeMarkerMetrics.outlineOffset, + y: rectangle.top - boxRangeMarkerMetrics.outlineOffset, + }, + }, + { + type: "close-path", + }, + ], + }; + } else if (index === 0) { + return { + pathCommands: [ + { + type: "move-to", + to: { + x: rectangle.right + boxRangeMarkerMetrics.outlineOffset, + y: rectangle.top - boxRangeMarkerMetrics.outlineOffset, + }, + }, + { + type: "line-to", + to: { + x: rectangle.left - boxRangeMarkerMetrics.outlineOffset, + y: rectangle.top - boxRangeMarkerMetrics.outlineOffset, + }, + }, + { + type: "line-to", + to: { + x: rectangle.left - boxRangeMarkerMetrics.outlineOffset, + y: rectangle.bottom + boxRangeMarkerMetrics.outlineOffset, + }, + }, + { + type: "line-to", + to: { + x: rectangle.right + boxRangeMarkerMetrics.outlineOffset, + y: rectangle.bottom + boxRangeMarkerMetrics.outlineOffset, + }, + }, + ], + }; + } else if ( + index === + sentenceStructureDiagramLayoutNode.rectangles.length - 1 + ) { + return { + pathCommands: [ + { + type: "move-to", + to: { + x: rectangle.left - boxRangeMarkerMetrics.outlineOffset, + y: rectangle.top - boxRangeMarkerMetrics.outlineOffset, + }, + }, + { + type: "line-to", + to: { + x: rectangle.right + boxRangeMarkerMetrics.outlineOffset, + y: rectangle.top - boxRangeMarkerMetrics.outlineOffset, + }, + }, + { + type: "line-to", + to: { + x: rectangle.right + boxRangeMarkerMetrics.outlineOffset, + y: rectangle.bottom + boxRangeMarkerMetrics.outlineOffset, + }, + }, + { + type: "line-to", + to: { + x: rectangle.left - boxRangeMarkerMetrics.outlineOffset, + y: rectangle.bottom + boxRangeMarkerMetrics.outlineOffset, + }, + }, + ], + }; + } else { + return { + pathCommands: [ + { + type: "move-to", + to: { + x: rectangle.left - boxRangeMarkerMetrics.outlineOffset, + y: rectangle.top - boxRangeMarkerMetrics.outlineOffset, + }, + }, + { + type: "line-to", + to: { + x: rectangle.right + boxRangeMarkerMetrics.outlineOffset, + y: rectangle.top - boxRangeMarkerMetrics.outlineOffset, + }, + }, + { + type: "move-to", + to: { + x: rectangle.left - boxRangeMarkerMetrics.outlineOffset, + y: rectangle.bottom + boxRangeMarkerMetrics.outlineOffset, + }, + }, + { + type: "line-to", + to: { + x: rectangle.right + boxRangeMarkerMetrics.outlineOffset, + y: rectangle.bottom + boxRangeMarkerMetrics.outlineOffset, + }, + }, + ], + }; + } + }, + ), + style: { + ...sentenceStructureDiagramLayoutNode.rangeMarker.style, + strokeWidth: boxRangeMarkerMetrics.strokeWidth, + }, + }; +} + +function createHighlight( + sentenceStructureDiagramLayoutNode: + | SentenceStructureDiagramLayoutSentenceStructureElementNode + | SentenceStructureDiagramLayoutCoordinationPartNode, +): SentenceStructureDiagramData["highlights"][number] { + if (sentenceStructureDiagramLayoutNode.rangeMarker?.type !== "highlight") { + throw new Error("Unreachable"); + } + + return { + id: sentenceStructureDiagramLayoutNode.id, + lineRectangles: sentenceStructureDiagramLayoutNode.rectangles.map( + (rectangle) => ({ + x: rectangle.left, + y: rectangle.top, + width: rectangle.right - rectangle.left, + height: rectangle.bottom - rectangle.top, + }), + ), + style: sentenceStructureDiagramLayoutNode.rangeMarker.style, + }; +} + +function createSentenceElementLabel( + sentenceStructureDiagramLayoutSentenceStructureElementNode: SentenceStructureDiagramLayoutSentenceStructureElementNode, +): SentenceStructureDiagramData["sentenceElementLabels"][number] { + if ( + !sentenceStructureDiagramLayoutSentenceStructureElementNode.sentenceElementLabel + ) { + throw new Error("Unreachable"); + } + + const firstLineRectangle = + sentenceStructureDiagramLayoutSentenceStructureElementNode.rectangles.at( + 0, + )!; + + switch ( + sentenceStructureDiagramLayoutSentenceStructureElementNode + .sentenceElementLabel.placement + ) { + case "below-center": + return { + id: sentenceStructureDiagramLayoutSentenceStructureElementNode.id, + text: sentenceStructureDiagramLayoutSentenceStructureElementNode + .sentenceElementLabel.text, + x: (firstLineRectangle.left + firstLineRectangle.right) / 2, + y: + firstLineRectangle.bottom + + sentenceStructureElementLabelMetrics.offsetY, + anchorX: "center", + anchorY: "top", + style: + sentenceStructureDiagramLayoutSentenceStructureElementNode + .sentenceElementLabel.style, + }; + case "below-left": + return { + id: sentenceStructureDiagramLayoutSentenceStructureElementNode.id, + text: sentenceStructureDiagramLayoutSentenceStructureElementNode + .sentenceElementLabel.text, + x: firstLineRectangle.left, + y: + firstLineRectangle.bottom + + sentenceStructureElementLabelMetrics.offsetY, + anchorX: "left", + anchorY: "top", + style: + sentenceStructureDiagramLayoutSentenceStructureElementNode + .sentenceElementLabel.style, + }; + case "above-center": + return { + id: sentenceStructureDiagramLayoutSentenceStructureElementNode.id, + text: sentenceStructureDiagramLayoutSentenceStructureElementNode + .sentenceElementLabel.text, + x: (firstLineRectangle.left + firstLineRectangle.right) / 2, + y: + firstLineRectangle.top - sentenceStructureElementLabelMetrics.offsetY, + anchorX: "center", + anchorY: "bottom", + style: + sentenceStructureDiagramLayoutSentenceStructureElementNode + .sentenceElementLabel.style, + }; + case "above-left": + return { + id: sentenceStructureDiagramLayoutSentenceStructureElementNode.id, + text: sentenceStructureDiagramLayoutSentenceStructureElementNode + .sentenceElementLabel.text, + x: firstLineRectangle.left, + y: + firstLineRectangle.top - sentenceStructureElementLabelMetrics.offsetY, + anchorX: "left", + anchorY: "bottom", + style: + sentenceStructureDiagramLayoutSentenceStructureElementNode + .sentenceElementLabel.style, + }; + default: + sentenceStructureDiagramLayoutSentenceStructureElementNode + .sentenceElementLabel.placement satisfies never; + throw new Error("Unreachable"); + } +} + +function createSentenceConstituentLabel( + sentenceStructureDiagramLayoutSentenceStructureElementNode: SentenceStructureDiagramLayoutSentenceStructureElementNode, +): SentenceStructureDiagramData["sentenceConstituentLabels"][number] { + if ( + !sentenceStructureDiagramLayoutSentenceStructureElementNode.sentenceConstituentLabel + ) { + throw new Error("Unreachable"); + } + + const firstLineRectangle = + sentenceStructureDiagramLayoutSentenceStructureElementNode.rectangles.at( + 0, + )!; + + switch ( + sentenceStructureDiagramLayoutSentenceStructureElementNode + .sentenceConstituentLabel.placement + ) { + case "below-center": + return { + id: sentenceStructureDiagramLayoutSentenceStructureElementNode.id, + text: sentenceStructureDiagramLayoutSentenceStructureElementNode + .sentenceConstituentLabel.text, + x: (firstLineRectangle.left + firstLineRectangle.right) / 2, + y: + firstLineRectangle.bottom + + sentenceStructureElementLabelMetrics.offsetY, + anchorX: "center", + anchorY: "top", + style: + sentenceStructureDiagramLayoutSentenceStructureElementNode + .sentenceConstituentLabel.style, + }; + case "below-left": + return { + id: sentenceStructureDiagramLayoutSentenceStructureElementNode.id, + text: sentenceStructureDiagramLayoutSentenceStructureElementNode + .sentenceConstituentLabel.text, + x: firstLineRectangle.left, + y: + firstLineRectangle.bottom + + sentenceStructureElementLabelMetrics.offsetY, + anchorX: "left", + anchorY: "top", + style: + sentenceStructureDiagramLayoutSentenceStructureElementNode + .sentenceConstituentLabel.style, + }; + case "above-center": + return { + id: sentenceStructureDiagramLayoutSentenceStructureElementNode.id, + text: sentenceStructureDiagramLayoutSentenceStructureElementNode + .sentenceConstituentLabel.text, + x: (firstLineRectangle.left + firstLineRectangle.right) / 2, + y: + firstLineRectangle.top - sentenceStructureElementLabelMetrics.offsetY, + anchorX: "center", + anchorY: "bottom", + style: + sentenceStructureDiagramLayoutSentenceStructureElementNode + .sentenceConstituentLabel.style, + }; + case "above-left": + return { + id: sentenceStructureDiagramLayoutSentenceStructureElementNode.id, + text: sentenceStructureDiagramLayoutSentenceStructureElementNode + .sentenceConstituentLabel.text, + x: firstLineRectangle.left, + y: + firstLineRectangle.top - sentenceStructureElementLabelMetrics.offsetY, + anchorX: "left", + anchorY: "bottom", + style: + sentenceStructureDiagramLayoutSentenceStructureElementNode + .sentenceConstituentLabel.style, + }; + default: + sentenceStructureDiagramLayoutSentenceStructureElementNode + .sentenceConstituentLabel.placement satisfies never; + throw new Error("Unreachable"); + } +} + +function createArrow( + sentenceStructureDiagramLayoutModification: SentenceStructureDiagramLayoutModification, + modifierSentenceStructureDiagramLayoutSentenceStructureElementNode: SentenceStructureDiagramLayoutSentenceStructureElementNode, + modifiedSentenceStructureDiagramLayoutSentenceStructureElementNode: SentenceStructureDiagramLayoutSentenceStructureElementNode, +): SentenceStructureDiagramData["arrows"][number] { + const { fromEndpoint, toEndpoint } = (() => { + function getArrowEndpointCandidates( + sentenceStructureDiagramLayoutNode: SentenceStructureDiagramLayoutNode, + ): { x: number; y: number }[] { + if (sentenceStructureDiagramLayoutNode.type === "word") { + return [ + { + x: + (sentenceStructureDiagramLayoutNode.rectangle.left + + sentenceStructureDiagramLayoutNode.rectangle.right) / + 2, + y: sentenceStructureDiagramLayoutNode.rectangle.top, + }, + ]; + } + + return sentenceStructureDiagramLayoutNode.children.flatMap( + getArrowEndpointCandidates, + ); + } + + const fromEndpointCandidates = getArrowEndpointCandidates( + modifierSentenceStructureDiagramLayoutSentenceStructureElementNode, + ); + const toEndpointCandidates = getArrowEndpointCandidates( + modifiedSentenceStructureDiagramLayoutSentenceStructureElementNode, + ); + + let fromEndpoint = fromEndpointCandidates.at(0)!; + let toEndpoint = toEndpointCandidates.at(0)!; + let minSquaredDistance = + (fromEndpoint.x - toEndpoint.x) ** 2 + + (fromEndpoint.y - toEndpoint.y) ** 2; + for (const fromEndpointCandidate of fromEndpointCandidates) { + for (const toEndpointCandidate of toEndpointCandidates) { + const squaredDistance = + (fromEndpointCandidate.x - toEndpointCandidate.x) ** 2 + + (fromEndpointCandidate.y - toEndpointCandidate.y) ** 2; + if (squaredDistance < minSquaredDistance) { + fromEndpoint = fromEndpointCandidate; + toEndpoint = toEndpointCandidate; + minSquaredDistance = squaredDistance; + } + } + } + + return { fromEndpoint, toEndpoint }; + })(); + + const controlPointY = + Math.min(fromEndpoint.y, toEndpoint.y) - + modificationArrowMetrics.curvedRouteOffsetY; + const curvedArrowShaftPathCommands: PathCommand[] = [ + { + type: "move-to", + to: { + x: fromEndpoint.x, + y: fromEndpoint.y, + }, + }, + { + type: "cubic-bezier-curve", + startControlPoint: { + x: fromEndpoint.x, + y: controlPointY, + }, + endControlPoint: { + x: toEndpoint.x, + y: controlPointY, + }, + to: { + x: toEndpoint.x, + y: toEndpoint.y, + }, + }, + ]; + + const orthogonalArrowShaftPathCommands: PathCommand[] = [ + { + type: "move-to", + to: { + x: fromEndpoint.x, + y: fromEndpoint.y, + }, + }, + { + type: "line-to", + to: { + x: fromEndpoint.x, + y: fromEndpoint.y - modificationArrowMetrics.orthogonalRouteOffsetY, + }, + }, + { + type: "line-to", + to: { + x: toEndpoint.x, + y: toEndpoint.y - modificationArrowMetrics.orthogonalRouteOffsetY, + }, + }, + { + type: "line-to", + to: { + x: toEndpoint.x, + y: toEndpoint.y, + }, + }, + ]; + + const arrowheadPathCommands: PathCommand[] = [ + { + type: "move-to", + to: { + x: toEndpoint.x - modificationArrowMetrics.arrowheadSize, + y: toEndpoint.y - modificationArrowMetrics.arrowheadSize, + }, + }, + { + type: "line-to", + to: { + x: toEndpoint.x, + y: toEndpoint.y, + }, + }, + { + type: "line-to", + to: { + x: toEndpoint.x + modificationArrowMetrics.arrowheadSize, + y: toEndpoint.y - modificationArrowMetrics.arrowheadSize, + }, + }, + ]; + + switch (sentenceStructureDiagramLayoutModification.arrow.type) { + case "curved": + return { + id: sentenceStructureDiagramLayoutModification.id, + pathCommands: [ + ...curvedArrowShaftPathCommands, + ...arrowheadPathCommands, + ], + style: { + ...sentenceStructureDiagramLayoutModification.arrow.style, + strokeWidth: modificationArrowMetrics.strokeWidth, + }, + }; + case "orthogonal": + if (fromEndpoint.y === toEndpoint.y) { + return { + id: sentenceStructureDiagramLayoutModification.id, + pathCommands: [ + ...orthogonalArrowShaftPathCommands, + ...arrowheadPathCommands, + ], + style: { + ...sentenceStructureDiagramLayoutModification.arrow.style, + strokeWidth: modificationArrowMetrics.strokeWidth, + }, + }; + } else { + return { + id: sentenceStructureDiagramLayoutModification.id, + pathCommands: [ + ...curvedArrowShaftPathCommands, + ...arrowheadPathCommands, + ], + style: { + ...sentenceStructureDiagramLayoutModification.arrow.style, + strokeWidth: modificationArrowMetrics.strokeWidth, + }, + }; + } + default: + sentenceStructureDiagramLayoutModification.arrow.type satisfies never; + throw new Error("Unreachable"); + } +} + +function createCoordinationGroupIndicator( + sentenceStructureDiagramLayoutCoordinationNode: SentenceStructureDiagramLayoutCoordinationNode, + layoutSettings: SentenceStructureDiagramLayoutTree["layoutSettings"], +): SentenceStructureDiagramData["coordinationGroupIndicators"][number] { + if (!layoutSettings.coordination.groupIndicator) { + throw new Error("Unreachable"); + } + + function getMaxCoordinationNestingDepth( + node: SentenceStructureDiagramLayoutNode, + ): number { + if (node.type === "word") { + return 0; + } + + return ( + Math.max(...node.children.map(getMaxCoordinationNestingDepth)) + + (node.type === "coordination" ? 1 : 0) + ); + } + + if (layoutSettings.coordination.layoutDirection === "horizontal") { + switch (layoutSettings.coordination.groupIndicator.type) { + case "bus-connector": + const attachmentPoints = + sentenceStructureDiagramLayoutCoordinationNode.children.map( + (coordinationPartNode) => { + const widestRectangle = coordinationPartNode.rectangles + .sort((a, b) => b.right - b.left - (a.right - a.left)) + .at(0)!; + return { + x: (widestRectangle.left + widestRectangle.right) / 2, + y: widestRectangle.top, + }; + }, + ); + + const attachmentPointsByLine: { + attachmentPoints: { + x: number; + y: number; + }[]; + }[] = []; + for (const [index, attachmentPoint] of attachmentPoints.entries()) { + if ( + index === 0 || + attachmentPoint.y !== + attachmentPointsByLine.at(-1)!.attachmentPoints.at(0)!.y + ) { + attachmentPointsByLine.push({ + attachmentPoints: [ + { + x: attachmentPoint.x, + y: attachmentPoint.y, + }, + ], + }); + } else { + attachmentPointsByLine + .at(-1)! + .attachmentPoints.push(attachmentPoint); + } + } + + return { + id: sentenceStructureDiagramLayoutCoordinationNode.id, + linePaths: attachmentPointsByLine.map( + (attachmentPointsOnLine, index) => { + const attachmentPointY = + attachmentPointsOnLine.attachmentPoints.at(0)!.y - + coordinationGroupIndicatorMetrics.horizontal.busConnector + .attachmentPointOffsetY; + const busLineY = + attachmentPointY - + coordinationGroupIndicatorMetrics.horizontal.busConnector + .tapLineBaseHeight - + (getMaxCoordinationNestingDepth( + sentenceStructureDiagramLayoutCoordinationNode, + ) - + 1) * + coordinationGroupIndicatorMetrics.horizontal.busConnector + .tapLineNestingStepY; + const lineRectangle = + sentenceStructureDiagramLayoutCoordinationNode.rectangles.find( + (rectangle) => + rectangle.top === + attachmentPointsOnLine.attachmentPoints.at(0)!.y, + )!; + if (attachmentPointsByLine.length === 1) { + return { + pathCommands: [ + { + type: "move-to", + to: { + x: attachmentPointsOnLine.attachmentPoints.at(0)!.x, + y: attachmentPointY, + }, + }, + { + type: "line-to", + to: { + x: attachmentPointsOnLine.attachmentPoints.at(0)!.x, + y: busLineY, + }, + }, + { + type: "line-to", + to: { + x: attachmentPointsOnLine.attachmentPoints.at(-1)!.x, + y: busLineY, + }, + }, + { + type: "line-to", + to: { + x: attachmentPointsOnLine.attachmentPoints.at(-1)!.x, + y: attachmentPointY, + }, + }, + ...(attachmentPointsOnLine.attachmentPoints + .slice(1, -1) + .flatMap((attachmentPoint) => [ + { + type: "move-to", + to: { + x: attachmentPoint.x, + y: attachmentPointY, + }, + }, + { + type: "line-to", + to: { + x: attachmentPoint.x, + y: busLineY, + }, + }, + ]) satisfies PathCommand[]), + ], + }; + } + if (index === 0) { + return { + pathCommands: [ + { + type: "move-to", + to: { + x: attachmentPointsOnLine.attachmentPoints.at(0)!.x, + y: attachmentPointY, + }, + }, + { + type: "line-to", + to: { + x: attachmentPointsOnLine.attachmentPoints.at(0)!.x, + y: busLineY, + }, + }, + { + type: "line-to", + to: { + x: lineRectangle.right, + y: busLineY, + }, + }, + ...(attachmentPointsOnLine.attachmentPoints + .slice(1) + .flatMap((attachmentPoint) => [ + { + type: "move-to", + to: { + x: attachmentPoint.x, + y: attachmentPointY, + }, + }, + { + type: "line-to", + to: { + x: attachmentPoint.x, + y: busLineY, + }, + }, + ]) satisfies PathCommand[]), + ], + }; + } + if (index === attachmentPointsByLine.length - 1) { + return { + pathCommands: [ + { + type: "move-to", + to: { + x: lineRectangle.left, + y: busLineY, + }, + }, + { + type: "line-to", + to: { + x: attachmentPointsOnLine.attachmentPoints.at(-1)!.x, + y: busLineY, + }, + }, + { + type: "line-to", + to: { + x: attachmentPointsOnLine.attachmentPoints.at(-1)!.x, + y: attachmentPointY, + }, + }, + ...(attachmentPointsOnLine.attachmentPoints + .slice(0, -1) + .flatMap((attachmentPoint) => [ + { + type: "move-to", + to: { + x: attachmentPoint.x, + y: attachmentPointY, + }, + }, + { + type: "line-to", + to: { + x: attachmentPoint.x, + y: busLineY, + }, + }, + ]) satisfies PathCommand[]), + ], + }; + } + return { + pathCommands: [ + { + type: "move-to", + to: { + x: lineRectangle.left, + y: busLineY, + }, + }, + { + type: "line-to", + to: { + x: lineRectangle.right, + y: busLineY, + }, + }, + ...(attachmentPointsOnLine.attachmentPoints.flatMap( + (attachmentPoint) => [ + { + type: "move-to", + to: { + x: attachmentPoint.x, + y: attachmentPointY, + }, + }, + { + type: "line-to", + to: { + x: attachmentPoint.x, + y: busLineY, + }, + }, + ], + ) satisfies PathCommand[]), + ], + }; + }, + ), + style: { + ...layoutSettings.coordination.groupIndicator.style, + strokeWidth: coordinationGroupIndicatorMetrics.strokeWidth, + }, + }; + default: + layoutSettings.coordination.groupIndicator.type satisfies never; + throw new Error("Unreachable"); + } + } else { + if ( + sentenceStructureDiagramLayoutCoordinationNode.rectangles.length !== 1 + ) { + throw new Error( + "Vertical coordination node must have a single rectangle", + ); + } + const sentenceStructureDiagramLayoutCoordinationNodeRectangle = + sentenceStructureDiagramLayoutCoordinationNode.rectangles[0]!; + switch (layoutSettings.coordination.groupIndicator.type) { + case "bracket": + switch (layoutSettings.coordination.groupIndicator.bracketType) { + case "parenthesis": + return { + id: sentenceStructureDiagramLayoutCoordinationNode.id, + linePaths: [ + { + pathCommands: [ + { + type: "move-to", + to: { + x: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.left + + coordinationGroupIndicatorMetrics.vertical + .groupIndicatorWidth, + y: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.top - + coordinationGroupIndicatorMetrics.vertical.bracket + .offsetY, + }, + }, + { + type: "quadratic-bezier-curve", + controlPoint: { + x: sentenceStructureDiagramLayoutCoordinationNodeRectangle.left, + y: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.top - + coordinationGroupIndicatorMetrics.vertical.bracket + .offsetY, + }, + to: { + x: sentenceStructureDiagramLayoutCoordinationNodeRectangle.left, + y: + (sentenceStructureDiagramLayoutCoordinationNodeRectangle.top + + sentenceStructureDiagramLayoutCoordinationNodeRectangle.bottom) / + 2, + }, + }, + { + type: "quadratic-bezier-curve", + controlPoint: { + x: sentenceStructureDiagramLayoutCoordinationNodeRectangle.left, + y: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.bottom + + coordinationGroupIndicatorMetrics.vertical.bracket + .offsetY, + }, + to: { + x: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.left + + coordinationGroupIndicatorMetrics.vertical + .groupIndicatorWidth, + y: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.bottom + + coordinationGroupIndicatorMetrics.vertical.bracket + .offsetY, + }, + }, + ], + }, + ...(layoutSettings.coordination.groupIndicator.placement === + "both-sides" + ? [ + { + pathCommands: [ + { + type: "move-to", + to: { + x: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.right - + coordinationGroupIndicatorMetrics.vertical + .groupIndicatorWidth, + y: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.top - + coordinationGroupIndicatorMetrics.vertical + .bracket.offsetY, + }, + }, + { + type: "quadratic-bezier-curve", + controlPoint: { + x: sentenceStructureDiagramLayoutCoordinationNodeRectangle.right, + y: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.top - + coordinationGroupIndicatorMetrics.vertical + .bracket.offsetY, + }, + to: { + x: sentenceStructureDiagramLayoutCoordinationNodeRectangle.right, + y: + (sentenceStructureDiagramLayoutCoordinationNodeRectangle.top + + sentenceStructureDiagramLayoutCoordinationNodeRectangle.bottom) / + 2, + }, + }, + { + type: "quadratic-bezier-curve", + controlPoint: { + x: sentenceStructureDiagramLayoutCoordinationNodeRectangle.right, + y: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.bottom + + coordinationGroupIndicatorMetrics.vertical + .bracket.offsetY, + }, + to: { + x: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.right - + coordinationGroupIndicatorMetrics.vertical + .groupIndicatorWidth, + y: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.bottom + + coordinationGroupIndicatorMetrics.vertical + .bracket.offsetY, + }, + }, + ] satisfies PathCommand[], + }, + ] + : []), + ], + style: { + ...layoutSettings.coordination.groupIndicator.style, + strokeWidth: coordinationGroupIndicatorMetrics.strokeWidth, + }, + }; + case "angle-bracket": + return { + id: sentenceStructureDiagramLayoutCoordinationNode.id, + linePaths: [ + { + pathCommands: [ + { + type: "move-to", + to: { + x: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.left + + coordinationGroupIndicatorMetrics.vertical + .groupIndicatorWidth, + y: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.top - + coordinationGroupIndicatorMetrics.vertical.bracket + .offsetY, + }, + }, + { + type: "line-to", + to: { + x: sentenceStructureDiagramLayoutCoordinationNodeRectangle.left, + y: + (sentenceStructureDiagramLayoutCoordinationNodeRectangle.top + + sentenceStructureDiagramLayoutCoordinationNodeRectangle.bottom) / + 2, + }, + }, + { + type: "line-to", + to: { + x: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.left + + coordinationGroupIndicatorMetrics.vertical + .groupIndicatorWidth, + y: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.bottom + + coordinationGroupIndicatorMetrics.vertical.bracket + .offsetY, + }, + }, + ], + }, + ...(layoutSettings.coordination.groupIndicator.placement === + "both-sides" + ? [ + { + pathCommands: [ + { + type: "move-to", + to: { + x: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.right - + coordinationGroupIndicatorMetrics.vertical + .groupIndicatorWidth, + y: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.top - + coordinationGroupIndicatorMetrics.vertical + .bracket.offsetY, + }, + }, + { + type: "line-to", + to: { + x: sentenceStructureDiagramLayoutCoordinationNodeRectangle.right, + y: + (sentenceStructureDiagramLayoutCoordinationNodeRectangle.top + + sentenceStructureDiagramLayoutCoordinationNodeRectangle.bottom) / + 2, + }, + }, + { + type: "line-to", + to: { + x: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.right - + coordinationGroupIndicatorMetrics.vertical + .groupIndicatorWidth, + y: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.bottom + + coordinationGroupIndicatorMetrics.vertical + .bracket.offsetY, + }, + }, + ] satisfies PathCommand[], + }, + ] + : []), + ], + style: { + ...layoutSettings.coordination.groupIndicator.style, + strokeWidth: coordinationGroupIndicatorMetrics.strokeWidth, + }, + }; + case "curly-bracket": + return { + id: sentenceStructureDiagramLayoutCoordinationNode.id, + linePaths: [ + { + pathCommands: [ + { + type: "move-to", + to: { + x: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.left + + coordinationGroupIndicatorMetrics.vertical + .groupIndicatorWidth, + y: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.top - + coordinationGroupIndicatorMetrics.vertical.bracket + .offsetY, + }, + }, + { + type: "quadratic-bezier-curve", + controlPoint: { + x: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.left + + coordinationGroupIndicatorMetrics.vertical + .groupIndicatorWidth / + 2, + y: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.top - + coordinationGroupIndicatorMetrics.vertical.bracket + .offsetY, + }, + to: { + x: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.left + + coordinationGroupIndicatorMetrics.vertical + .groupIndicatorWidth / + 2, + y: + (sentenceStructureDiagramLayoutCoordinationNodeRectangle.top * + 3 + + sentenceStructureDiagramLayoutCoordinationNodeRectangle.bottom) / + 4, + }, + }, + { + type: "quadratic-bezier-curve", + controlPoint: { + x: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.left + + coordinationGroupIndicatorMetrics.vertical + .groupIndicatorWidth / + 2, + y: + (sentenceStructureDiagramLayoutCoordinationNodeRectangle.top + + sentenceStructureDiagramLayoutCoordinationNodeRectangle.bottom) / + 2, + }, + to: { + x: sentenceStructureDiagramLayoutCoordinationNodeRectangle.left, + y: + (sentenceStructureDiagramLayoutCoordinationNodeRectangle.top + + sentenceStructureDiagramLayoutCoordinationNodeRectangle.bottom) / + 2, + }, + }, + { + type: "quadratic-bezier-curve", + controlPoint: { + x: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.left + + coordinationGroupIndicatorMetrics.vertical + .groupIndicatorWidth / + 2, + y: + (sentenceStructureDiagramLayoutCoordinationNodeRectangle.top + + sentenceStructureDiagramLayoutCoordinationNodeRectangle.bottom) / + 2, + }, + to: { + x: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.left + + coordinationGroupIndicatorMetrics.vertical + .groupIndicatorWidth / + 2, + y: + (sentenceStructureDiagramLayoutCoordinationNodeRectangle.top + + sentenceStructureDiagramLayoutCoordinationNodeRectangle.bottom * + 3) / + 4, + }, + }, + { + type: "quadratic-bezier-curve", + controlPoint: { + x: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.left + + coordinationGroupIndicatorMetrics.vertical + .groupIndicatorWidth / + 2, + y: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.bottom + + coordinationGroupIndicatorMetrics.vertical.bracket + .offsetY, + }, + to: { + x: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.left + + coordinationGroupIndicatorMetrics.vertical + .groupIndicatorWidth, + y: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.bottom + + coordinationGroupIndicatorMetrics.vertical.bracket + .offsetY, + }, + }, + ], + }, + ...(layoutSettings.coordination.groupIndicator.placement === + "both-sides" + ? [ + { + pathCommands: [ + { + type: "move-to", + to: { + x: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.right - + coordinationGroupIndicatorMetrics.vertical + .groupIndicatorWidth, + y: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.top - + coordinationGroupIndicatorMetrics.vertical + .bracket.offsetY, + }, + }, + { + type: "quadratic-bezier-curve", + controlPoint: { + x: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.right - + coordinationGroupIndicatorMetrics.vertical + .groupIndicatorWidth / + 2, + y: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.top - + coordinationGroupIndicatorMetrics.vertical + .bracket.offsetY, + }, + to: { + x: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.right - + coordinationGroupIndicatorMetrics.vertical + .groupIndicatorWidth / + 2, + y: + (sentenceStructureDiagramLayoutCoordinationNodeRectangle.top * + 3 + + sentenceStructureDiagramLayoutCoordinationNodeRectangle.bottom) / + 4, + }, + }, + { + type: "quadratic-bezier-curve", + controlPoint: { + x: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.right - + coordinationGroupIndicatorMetrics.vertical + .groupIndicatorWidth / + 2, + y: + (sentenceStructureDiagramLayoutCoordinationNodeRectangle.top + + sentenceStructureDiagramLayoutCoordinationNodeRectangle.bottom) / + 2, + }, + to: { + x: sentenceStructureDiagramLayoutCoordinationNodeRectangle.right, + y: + (sentenceStructureDiagramLayoutCoordinationNodeRectangle.top + + sentenceStructureDiagramLayoutCoordinationNodeRectangle.bottom) / + 2, + }, + }, + { + type: "quadratic-bezier-curve", + controlPoint: { + x: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.right - + coordinationGroupIndicatorMetrics.vertical + .groupIndicatorWidth / + 2, + y: + (sentenceStructureDiagramLayoutCoordinationNodeRectangle.top + + sentenceStructureDiagramLayoutCoordinationNodeRectangle.bottom) / + 2, + }, + to: { + x: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.right - + coordinationGroupIndicatorMetrics.vertical + .groupIndicatorWidth / + 2, + y: + (sentenceStructureDiagramLayoutCoordinationNodeRectangle.top + + sentenceStructureDiagramLayoutCoordinationNodeRectangle.bottom * + 3) / + 4, + }, + }, + { + type: "quadratic-bezier-curve", + controlPoint: { + x: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.right - + coordinationGroupIndicatorMetrics.vertical + .groupIndicatorWidth / + 2, + y: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.bottom + + coordinationGroupIndicatorMetrics.vertical + .bracket.offsetY, + }, + to: { + x: sentenceStructureDiagramLayoutCoordinationNodeRectangle.right, + y: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.bottom + + coordinationGroupIndicatorMetrics.vertical + .bracket.offsetY, + }, + }, + ] satisfies PathCommand[], + }, + ] + : []), + ], + style: { + ...layoutSettings.coordination.groupIndicator.style, + strokeWidth: coordinationGroupIndicatorMetrics.strokeWidth, + }, + }; + case "square-bracket": + return { + id: sentenceStructureDiagramLayoutCoordinationNode.id, + linePaths: [ + { + pathCommands: [ + { + type: "move-to", + to: { + x: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.left + + coordinationGroupIndicatorMetrics.vertical + .groupIndicatorWidth, + y: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.top - + coordinationGroupIndicatorMetrics.vertical.bracket + .offsetY, + }, + }, + { + type: "line-to", + to: { + x: sentenceStructureDiagramLayoutCoordinationNodeRectangle.left, + y: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.top - + coordinationGroupIndicatorMetrics.vertical.bracket + .offsetY, + }, + }, + { + type: "line-to", + to: { + x: sentenceStructureDiagramLayoutCoordinationNodeRectangle.left, + y: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.bottom + + coordinationGroupIndicatorMetrics.vertical.bracket + .offsetY, + }, + }, + { + type: "line-to", + to: { + x: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.left + + coordinationGroupIndicatorMetrics.vertical + .groupIndicatorWidth, + y: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.bottom + + coordinationGroupIndicatorMetrics.vertical.bracket + .offsetY, + }, + }, + ], + }, + ...(layoutSettings.coordination.groupIndicator.placement === + "both-sides" + ? [ + { + pathCommands: [ + { + type: "move-to", + to: { + x: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.right - + coordinationGroupIndicatorMetrics.vertical + .groupIndicatorWidth, + y: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.top - + coordinationGroupIndicatorMetrics.vertical + .bracket.offsetY, + }, + }, + { + type: "line-to", + to: { + x: sentenceStructureDiagramLayoutCoordinationNodeRectangle.right, + y: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.top - + coordinationGroupIndicatorMetrics.vertical + .bracket.offsetY, + }, + }, + { + type: "line-to", + to: { + x: sentenceStructureDiagramLayoutCoordinationNodeRectangle.right, + y: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.bottom + + coordinationGroupIndicatorMetrics.vertical + .bracket.offsetY, + }, + }, + { + type: "line-to", + to: { + x: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.right - + coordinationGroupIndicatorMetrics.vertical + .groupIndicatorWidth, + y: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.bottom + + coordinationGroupIndicatorMetrics.vertical + .bracket.offsetY, + }, + }, + ] satisfies PathCommand[], + }, + ] + : []), + ], + style: { + ...layoutSettings.coordination.groupIndicator.style, + strokeWidth: coordinationGroupIndicatorMetrics.strokeWidth, + }, + }; + default: + layoutSettings.coordination.groupIndicator + .bracketType satisfies never; + throw new Error("Unreachable"); + } + case "bus-connector": + const attachmentPoints = + sentenceStructureDiagramLayoutCoordinationNode.children.map( + (coordinationPartNode) => ({ + x: + sentenceStructureDiagramLayoutCoordinationNodeRectangle.left + + coordinationGroupIndicatorMetrics.vertical.groupIndicatorWidth, + y: + (coordinationPartNode.rectangles.at(0)!.top + + coordinationPartNode.rectangles.at(0)!.bottom) / + 2, + }), + ); + return { + id: sentenceStructureDiagramLayoutCoordinationNode.id, + linePaths: [ + { + pathCommands: [ + { + type: "move-to", + to: { + x: attachmentPoints.at(0)!.x, + y: attachmentPoints.at(0)!.y, + }, + }, + { + type: "line-to", + to: { + x: + attachmentPoints.at(0)!.x - + coordinationGroupIndicatorMetrics.vertical + .groupIndicatorWidth, + y: attachmentPoints.at(0)!.y, + }, + }, + { + type: "line-to", + to: { + x: + attachmentPoints.at(-1)!.x - + coordinationGroupIndicatorMetrics.vertical + .groupIndicatorWidth, + y: attachmentPoints.at(-1)!.y, + }, + }, + { + type: "line-to", + to: { + x: attachmentPoints.at(-1)!.x, + y: attachmentPoints.at(-1)!.y, + }, + }, + ...attachmentPoints.slice(1, -1).flatMap( + (attachmentPoint) => + [ + { + type: "move-to", + to: { + x: attachmentPoint.x, + y: attachmentPoint.y, + }, + }, + { + type: "line-to", + to: { + x: + attachmentPoint.x - + coordinationGroupIndicatorMetrics.vertical + .groupIndicatorWidth, + y: attachmentPoint.y, + }, + }, + ] satisfies PathCommand[], + ), + ], + }, + ], + style: { + ...layoutSettings.coordination.groupIndicator.style, + strokeWidth: coordinationGroupIndicatorMetrics.strokeWidth, + }, + }; + default: + layoutSettings.coordination.groupIndicator satisfies never; + throw new Error("Unreachable"); + } + } +} + +export function createSentenceStructureDiagramData( + sentenceStructureDiagramLayoutTree: SentenceStructureDiagramLayoutTree, +): SentenceStructureDiagramData { + const words: SentenceStructureDiagramData["words"] = []; + const underlines: SentenceStructureDiagramData["underlines"] = []; + const brackets: SentenceStructureDiagramData["brackets"] = []; + const boxes: SentenceStructureDiagramData["boxes"] = []; + const highlights: SentenceStructureDiagramData["highlights"] = []; + const sentenceElementLabels: SentenceStructureDiagramData["sentenceElementLabels"] = + []; + const sentenceConstituentLabels: SentenceStructureDiagramData["sentenceConstituentLabels"] = + []; + const coordinationGroupIndicators: SentenceStructureDiagramData["coordinationGroupIndicators"] = + []; + + const sentenceStructureElementIdToSentenceStructureDiagramLayoutSentenceStructureElementNodeMap: Map< + string, + SentenceStructureDiagramLayoutSentenceStructureElementNode + > = new Map(); + + function visit(node: SentenceStructureDiagramLayoutNode) { + switch (node.type) { + case "word": + words.push(createWord(node)); + return; + case "sentence-structure-element": + if (node.rangeMarker) { + switch (node.rangeMarker.type) { + case "underline": + underlines.push(createUnderline(node)); + break; + case "bracket": + brackets.push(createBracket(node)); + break; + case "box": + boxes.push(createBox(node)); + break; + case "highlight": + highlights.push(createHighlight(node)); + break; + default: + node.rangeMarker satisfies never; + throw new Error("Unreachable"); + } + } + if (node.sentenceElementLabel) { + sentenceElementLabels.push(createSentenceElementLabel(node)); + } + if (node.sentenceConstituentLabel) { + sentenceConstituentLabels.push(createSentenceConstituentLabel(node)); + } + sentenceStructureElementIdToSentenceStructureDiagramLayoutSentenceStructureElementNodeMap.set( + node.id, + node, + ); + break; + case "coordination-part": + if (node.rangeMarker) { + switch (node.rangeMarker.type) { + case "underline": + underlines.push(createUnderline(node)); + break; + case "bracket": + brackets.push(createBracket(node)); + break; + case "box": + boxes.push(createBox(node)); + break; + case "highlight": + highlights.push(createHighlight(node)); + break; + default: + node.rangeMarker satisfies never; + throw new Error("Unreachable"); + } + } + break; + case "coordination": + if ( + sentenceStructureDiagramLayoutTree.layoutSettings.coordination + .groupIndicator + ) { + coordinationGroupIndicators.push( + createCoordinationGroupIndicator( + node, + sentenceStructureDiagramLayoutTree.layoutSettings, + ), + ); + } + break; + case "root": + break; + default: + node satisfies never; + throw new Error("Unreachable"); + } + + for (const child of node.children) { + visit(child); + } + } + + for (const sentenceStructureDiagramLayoutSentenceTree of sentenceStructureDiagramLayoutTree.sentences) { + visit(sentenceStructureDiagramLayoutSentenceTree.root); + } + + return { + canvas: sentenceStructureDiagramLayoutTree.canvas, + words, + underlines, + brackets, + boxes, + highlights, + sentenceElementLabels, + sentenceConstituentLabels, + arrows: sentenceStructureDiagramLayoutTree.sentences.flatMap( + (sentenceStructureDiagramLayoutSentenceTree) => + sentenceStructureDiagramLayoutSentenceTree.modifications.map( + (modification) => + createArrow( + modification, + sentenceStructureElementIdToSentenceStructureDiagramLayoutSentenceStructureElementNodeMap.get( + modification.modifierSentenceStructureElementNodeId, + )!, + sentenceStructureElementIdToSentenceStructureDiagramLayoutSentenceStructureElementNodeMap.get( + modification.modifiedSentenceStructureElementNodeId, + )!, + ), + ), + ), + coordinationGroupIndicators, + }; +} diff --git a/packages/sentence-structure-diagram/diagram-data/types.ts b/packages/sentence-structure-diagram/diagram-data/types.ts new file mode 100644 index 0000000..0c7caf0 --- /dev/null +++ b/packages/sentence-structure-diagram/diagram-data/types.ts @@ -0,0 +1,129 @@ +import type { HexRGBColor } from "@sentence-structure-diagram-app/sentence-structure-diagram-notation"; + +type TextStyle = { + fontSize: number; + fontWeight: "normal" | "bold"; + textColor: HexRGBColor; +}; + +type BackgroundStyle = { + backgroundColor: HexRGBColor; +}; + +type StrokeStyle = { + strokeStyle: "solid" | "dashed"; + strokeColor: HexRGBColor; + strokeWidth: number; +}; + +type Point = { x: number; y: number }; + +export type PathCommand = + | { + type: "move-to"; + to: Point; + } + | { + type: "line-to"; + to: Point; + } + | { + type: "cubic-bezier-curve"; + startControlPoint: Point; + endControlPoint: Point; + to: Point; + } + | { + type: "quadratic-bezier-curve"; + controlPoint: Point; + to: Point; + } + | { + type: "close-path"; + }; + +export type SentenceStructureDiagramData = { + canvas: { + width: number; + height: number; + }; + words: { + id: string; + text: string; + rectangle: { + x: number; + y: number; + }; + style: TextStyle; + }[]; + underlines: { + id: string; + lineSegments: { + x1: number; + x2: number; + y: number; + }[]; + style: StrokeStyle; + }[]; + brackets: { + id: string; + openingBracket: { + text: string; + x: number; + y: number; + }; + closingBracket: { + text: string; + x: number; + y: number; + }; + style: TextStyle; + }[]; + boxes: { + id: string; + linePaths: { + pathCommands: PathCommand[]; + }[]; + style: StrokeStyle; + }[]; + highlights: { + id: string; + lineRectangles: { + x: number; + y: number; + width: number; + height: number; + }[]; + style: BackgroundStyle; + }[]; + sentenceElementLabels: { + id: string; + text: string; + x: number; + y: number; + anchorX: "left" | "center"; + anchorY: "top" | "bottom"; + style: TextStyle; + }[]; + sentenceConstituentLabels: { + id: string; + text: string; + x: number; + y: number; + anchorX: "left" | "center"; + anchorY: "top" | "bottom"; + style: TextStyle; + }[]; + arrows: { + id: string; + pathCommands: PathCommand[]; + style: StrokeStyle; + }[]; + coordinationGroupIndicators: { + id: string; + linePaths: { + pathCommands: PathCommand[]; + }[]; + style: StrokeStyle; + }[]; +}; diff --git a/packages/sentence-structure-diagram/diagram-layout/create.ts b/packages/sentence-structure-diagram/diagram-layout/create.ts new file mode 100644 index 0000000..dc4c084 --- /dev/null +++ b/packages/sentence-structure-diagram/diagram-layout/create.ts @@ -0,0 +1,814 @@ +import { coordinationGroupIndicatorMetrics } from "../constants.js"; +import type { + SentenceStructureDiagramModelCoordinationNode, + SentenceStructureDiagramModelCoordinationPartNode, + SentenceStructureDiagramModelForest, + SentenceStructureDiagramModelNode, + SentenceStructureDiagramModelRootNode, + SentenceStructureDiagramModelSentenceStructureElementNode, + SentenceStructureDiagramModelSentenceTree, + SentenceStructureDiagramModelWordNode, +} from "../diagram-model/types.js"; +import type { + RangeMarker, + Rectangle, + SentenceStructureDiagramLayoutCoordinationNode, + SentenceStructureDiagramLayoutCoordinationPartNode, + SentenceStructureDiagramLayoutNode, + SentenceStructureDiagramLayoutRootNode, + SentenceStructureDiagramLayoutSentenceStructureElementNode, + SentenceStructureDiagramLayoutSentenceTree, + SentenceStructureDiagramLayoutTree, + SentenceStructureDiagramLayoutWordNode, +} from "./types.js"; + +type LayoutContext = + | { + canvas: { + width: number; + }; + spacing: { + padding: number; + wordSpacing: number; + lineSpacing: number; + continuationIndent: number; + }; + enableReflow: false; + coordination: { + layoutDirection: "horizontal"; + groupIndicator: { + type: "bus-connector"; + } | null; + }; + measureTextWidth: (text: string, fontSize: number) => number; + } + | { + canvas: { + width: number; + }; + spacing: { + padding: number; + wordSpacing: number; + lineSpacing: number; + continuationIndent: number; + }; + enableReflow: true; + coordination: { + layoutDirection: "vertical"; + groupIndicator: + | { + type: "bracket"; + placement: "left" | "both-sides"; + bracketType: + | "parenthesis" + | "angle-bracket" + | "curly-bracket" + | "square-bracket"; + } + | { + type: "bus-connector"; + } + | null; + }; + layoutStrategy: + | { + lineBreakStrategy: "greedy-word-wrap"; + } + | { + lineBreakStrategy: "largest-boundary-first"; + continuationLineStart: "content-start" | "scope-start"; + }; + measureTextWidth: (text: string, fontSize: number) => number; + }; + +function estimateWidth( + sentenceStructureDiagramModelNode: SentenceStructureDiagramModelNode, + layoutContext: LayoutContext, +): number { + switch (sentenceStructureDiagramModelNode.type) { + case "word": + return layoutContext.measureTextWidth( + sentenceStructureDiagramModelNode.text, + sentenceStructureDiagramModelNode.style.fontSize, + ); + case "sentence-structure-element": + case "coordination-part": + case "coordination": + case "root": + if ( + sentenceStructureDiagramModelNode.type === "coordination" && + layoutContext.coordination.layoutDirection === "vertical" + ) { + const coordinationGroupIndicatorReservedWidth = + (coordinationGroupIndicatorMetrics.vertical.groupIndicatorOffsetX + + coordinationGroupIndicatorMetrics.vertical.groupIndicatorWidth) * + (layoutContext.coordination.groupIndicator + ? layoutContext.coordination.groupIndicator.type === "bracket" && + layoutContext.coordination.groupIndicator.placement === + "both-sides" + ? 2 + : 1 + : 0); + return ( + Math.max( + ...sentenceStructureDiagramModelNode.children.map((child) => + estimateWidth(child, layoutContext), + ), + ) + coordinationGroupIndicatorReservedWidth + ); + } + + const bracketRangeMarkerReservedWidth = + layoutContext.enableReflow && + sentenceStructureDiagramModelNode.type === + "sentence-structure-element" && + sentenceStructureDiagramModelNode.rangeMarker?.type === "bracket" + ? layoutContext.measureTextWidth( + sentenceStructureDiagramModelNode.rangeMarker.openingBracket, + sentenceStructureDiagramModelNode.rangeMarker.style.fontSize, + ) + + layoutContext.measureTextWidth( + sentenceStructureDiagramModelNode.rangeMarker.closingBracket, + sentenceStructureDiagramModelNode.rangeMarker.style.fontSize, + ) + : 0; + return ( + sentenceStructureDiagramModelNode.children.reduce( + (sum, child, index) => { + return ( + sum + + (index === 0 ? 0 : layoutContext.spacing.wordSpacing) + + estimateWidth(child, layoutContext) + ); + }, + 0, + ) + bracketRangeMarkerReservedWidth + ); + default: + sentenceStructureDiagramModelNode satisfies never; + throw new Error("Unreachable"); + } +} + +function createSentenceStructureDiagramLayoutNode( + sentenceStructureDiagramModelNode: SentenceStructureDiagramModelWordNode, + origin: { left: number; top: number }, + lineHeight: number, + layoutContext: LayoutContext, +): SentenceStructureDiagramLayoutWordNode; +function createSentenceStructureDiagramLayoutNode( + sentenceStructureDiagramModelNode: SentenceStructureDiagramModelSentenceStructureElementNode, + origin: { left: number; top: number }, + lineHeight: number, + layoutContext: LayoutContext, +): SentenceStructureDiagramLayoutSentenceStructureElementNode; +function createSentenceStructureDiagramLayoutNode( + sentenceStructureDiagramModelNode: SentenceStructureDiagramModelCoordinationPartNode, + origin: { left: number; top: number }, + lineHeight: number, + layoutContext: LayoutContext, +): SentenceStructureDiagramLayoutCoordinationPartNode; +function createSentenceStructureDiagramLayoutNode( + sentenceStructureDiagramModelNode: SentenceStructureDiagramModelCoordinationNode, + origin: { left: number; top: number }, + lineHeight: number, + layoutContext: LayoutContext, +): SentenceStructureDiagramLayoutCoordinationNode; +function createSentenceStructureDiagramLayoutNode( + sentenceStructureDiagramModelNode: SentenceStructureDiagramModelRootNode, + origin: { left: number; top: number }, + lineHeight: number, + layoutContext: LayoutContext, +): SentenceStructureDiagramLayoutRootNode; +function createSentenceStructureDiagramLayoutNode( + sentenceStructureDiagramModelNode: SentenceStructureDiagramModelNode, + origin: { left: number; top: number }, + lineHeight: number, + layoutContext: LayoutContext, +): SentenceStructureDiagramLayoutNode { + switch (sentenceStructureDiagramModelNode.type) { + case "word": { + const width = layoutContext.measureTextWidth( + sentenceStructureDiagramModelNode.text, + sentenceStructureDiagramModelNode.style.fontSize, + ); + const height = sentenceStructureDiagramModelNode.style.fontSize; + + return { + type: "word", + id: sentenceStructureDiagramModelNode.id, + text: sentenceStructureDiagramModelNode.text, + whitespaceAfter: sentenceStructureDiagramModelNode.whitespaceAfter, + rectangle: { + left: origin.left, + top: origin.top, + right: origin.left + width, + bottom: origin.top + height, + }, + style: sentenceStructureDiagramModelNode.style, + }; + } + case "sentence-structure-element": + case "coordination-part": + case "root": + const openingBracketRangeMarkerReservedWidth = + sentenceStructureDiagramModelNode.type === + "sentence-structure-element" && + sentenceStructureDiagramModelNode.rangeMarker?.type === "bracket" + ? layoutContext.measureTextWidth( + sentenceStructureDiagramModelNode.rangeMarker.openingBracket, + sentenceStructureDiagramModelNode.rangeMarker.style.fontSize, + ) + : 0; + let cursor = { + left: + origin.left + + (layoutContext.enableReflow + ? openingBracketRangeMarkerReservedWidth + : 0), + top: origin.top, + }; + const nextLineCursor = + (layoutContext.enableReflow && + layoutContext.layoutStrategy.lineBreakStrategy === + "largest-boundary-first" && + layoutContext.layoutStrategy.continuationLineStart === + "scope-start") || + (sentenceStructureDiagramModelNode.type === "coordination-part" && + layoutContext.coordination.layoutDirection === "vertical") + ? { + left: origin.left + layoutContext.spacing.continuationIndent, + top: origin.top + layoutContext.spacing.lineSpacing, + } + : { + left: layoutContext.spacing.padding, + top: origin.top + lineHeight + layoutContext.spacing.lineSpacing, + }; + const rectangles: Rectangle[] = [ + { + left: origin.left, + top: origin.top, + right: origin.left, + bottom: origin.top, + }, + ]; + const children = []; + for (const child of sentenceStructureDiagramModelNode.children) { + const shouldBreakLine = (() => { + if ( + estimateWidth(child, layoutContext) <= + layoutContext.canvas.width - + layoutContext.spacing.padding - + cursor.left + ) { + return false; + } + + if (cursor.left <= nextLineCursor.left) { + return false; + } + + if ( + (!layoutContext.enableReflow || + layoutContext.layoutStrategy.lineBreakStrategy === + "greedy-word-wrap") && + child.type !== "word" + ) { + return false; + } + + return true; + })(); + + if (shouldBreakLine) { + cursor = { ...nextLineCursor }; + nextLineCursor.left += + (layoutContext.enableReflow && + layoutContext.layoutStrategy.lineBreakStrategy === + "largest-boundary-first" && + layoutContext.layoutStrategy.continuationLineStart === + "scope-start") || + (sentenceStructureDiagramModelNode.type === "coordination-part" && + layoutContext.coordination.layoutDirection === "vertical") + ? layoutContext.spacing.continuationIndent + : 0; + nextLineCursor.top += layoutContext.spacing.lineSpacing; + rectangles.push({ + left: cursor.left, + top: cursor.top, + right: cursor.left, + bottom: cursor.top, + }); + } + + const childResult = (() => { + switch (child.type) { + case "word": + return createSentenceStructureDiagramLayoutNode( + child, + cursor, + nextLineCursor.top - + layoutContext.spacing.lineSpacing - + cursor.top, + layoutContext, + ); + case "sentence-structure-element": + return createSentenceStructureDiagramLayoutNode( + child, + cursor, + nextLineCursor.top - + layoutContext.spacing.lineSpacing - + cursor.top, + layoutContext, + ); + case "coordination": + return createSentenceStructureDiagramLayoutNode( + child, + cursor, + nextLineCursor.top - + layoutContext.spacing.lineSpacing - + cursor.top, + layoutContext, + ); + default: + child satisfies never; + throw new Error("Unreachable"); + } + })(); + + cursor = + (layoutContext.enableReflow && + layoutContext.layoutStrategy.lineBreakStrategy === + "largest-boundary-first" && + layoutContext.layoutStrategy.continuationLineStart === + "scope-start") || + (sentenceStructureDiagramModelNode.type === "coordination-part" && + layoutContext.coordination.layoutDirection === "vertical") + ? { + left: + childResult.type === "word" + ? childResult.rectangle.right + + layoutContext.spacing.wordSpacing + : Math.max( + ...childResult.rectangles.map( + (rectangle) => rectangle.right, + ), + ) + layoutContext.spacing.wordSpacing, + top: cursor.top, + } + : { + left: + childResult.type === "word" + ? childResult.rectangle.right + + layoutContext.spacing.wordSpacing + : childResult.rectangles.at(-1)!.right + + layoutContext.spacing.wordSpacing, + top: + childResult.type === "word" + ? childResult.rectangle.top + : childResult.rectangles.at(-1)!.top, + }; + nextLineCursor.top = Math.max( + nextLineCursor.top, + ...(childResult.type === "word" + ? [childResult.rectangle.bottom + layoutContext.spacing.lineSpacing] + : childResult.rectangles.map( + (rectangle) => + rectangle.bottom + layoutContext.spacing.lineSpacing, + )), + ); + for (const rectangle of childResult.type === "word" + ? [childResult.rectangle] + : childResult.rectangles) { + if (rectangle.top === rectangles.at(-1)!.top) { + rectangles.at(-1)!.right = rectangle.right; + rectangles.at(-1)!.bottom = Math.max( + rectangles.at(-1)!.bottom, + rectangle.bottom, + ); + } else { + rectangles.push({ + left: rectangle.left, + top: rectangle.top, + right: rectangle.right, + bottom: rectangle.bottom, + }); + } + } + children.push(childResult); + } + + const closingBracketRangeMarkerReservedWidth = + sentenceStructureDiagramModelNode.type === + "sentence-structure-element" && + sentenceStructureDiagramModelNode.rangeMarker?.type === "bracket" + ? layoutContext.measureTextWidth( + sentenceStructureDiagramModelNode.rangeMarker.closingBracket, + sentenceStructureDiagramModelNode.rangeMarker.style.fontSize, + ) + : 0; + cursor.left += layoutContext.enableReflow + ? closingBracketRangeMarkerReservedWidth + : 0; + rectangles.at(-1)!.right += layoutContext.enableReflow + ? closingBracketRangeMarkerReservedWidth + : 0; + + switch (sentenceStructureDiagramModelNode.type) { + case "sentence-structure-element": + case "coordination-part": + const rangeMarker: RangeMarker = (() => { + if ( + sentenceStructureDiagramModelNode.rangeMarker?.type !== "bracket" + ) { + return sentenceStructureDiagramModelNode.rangeMarker; + } + + function calculateOpeningBracketReservedWidth( + sentenceStructureDiagramModelNode: SentenceStructureDiagramModelNode, + ): number { + if (sentenceStructureDiagramModelNode.type === "word") { + return 0; + } + + if ( + sentenceStructureDiagramModelNode.type === + "sentence-structure-element" && + sentenceStructureDiagramModelNode.rangeMarker?.type === + "bracket" + ) { + return ( + calculateOpeningBracketReservedWidth( + sentenceStructureDiagramModelNode.children[0]!, + ) + + layoutContext.measureTextWidth( + sentenceStructureDiagramModelNode.rangeMarker + .openingBracket, + sentenceStructureDiagramModelNode.rangeMarker.style + .fontSize, + ) + ); + } + + return calculateOpeningBracketReservedWidth( + sentenceStructureDiagramModelNode.children[0]!, + ); + } + + function calculateClosingBracketReservedWidth( + sentenceStructureDiagramModelNode: SentenceStructureDiagramModelNode, + ): number { + if (sentenceStructureDiagramModelNode.type === "word") { + return 0; + } + + if ( + sentenceStructureDiagramModelNode.type === + "sentence-structure-element" && + sentenceStructureDiagramModelNode.rangeMarker?.type === + "bracket" + ) { + return ( + calculateClosingBracketReservedWidth( + sentenceStructureDiagramModelNode.children[0]!, + ) + + layoutContext.measureTextWidth( + sentenceStructureDiagramModelNode.rangeMarker + .closingBracket, + sentenceStructureDiagramModelNode.rangeMarker.style + .fontSize, + ) + ); + } + + return calculateClosingBracketReservedWidth( + sentenceStructureDiagramModelNode.children[0]!, + ); + } + + return { + type: "bracket", + openingBracket: { + text: sentenceStructureDiagramModelNode.rangeMarker + .openingBracket, + left: + origin.left - + (layoutContext.enableReflow + ? 0 + : calculateOpeningBracketReservedWidth( + sentenceStructureDiagramModelNode, + )), + top: origin.top, + }, + closingBracket: { + text: sentenceStructureDiagramModelNode.rangeMarker + .closingBracket, + left: + cursor.left - + layoutContext.spacing.wordSpacing - + closingBracketRangeMarkerReservedWidth + + (layoutContext.enableReflow + ? 0 + : calculateClosingBracketReservedWidth( + sentenceStructureDiagramModelNode, + )), + top: cursor.top, + }, + style: sentenceStructureDiagramModelNode.rangeMarker.style, + }; + })(); + switch (sentenceStructureDiagramModelNode.type) { + case "sentence-structure-element": + return { + type: "sentence-structure-element", + id: sentenceStructureDiagramModelNode.id, + rectangles, + rangeMarker, + sentenceElementLabel: + sentenceStructureDiagramModelNode.sentenceElementLabel, + sentenceConstituentLabel: + sentenceStructureDiagramModelNode.sentenceConstituentLabel, + children, + }; + case "coordination-part": + return { + type: "coordination-part", + id: sentenceStructureDiagramModelNode.id, + rectangles, + rangeMarker, + children, + }; + default: + sentenceStructureDiagramModelNode satisfies never; + throw new Error("Unreachable"); + } + case "root": + return { + type: "root", + rectangles, + children, + }; + default: + sentenceStructureDiagramModelNode satisfies never; + throw new Error("Unreachable"); + } + case "coordination": + if (layoutContext.coordination.layoutDirection === "horizontal") { + let cursor = { ...origin }; + const nextLineCursor = { + left: layoutContext.spacing.padding, + top: origin.top + lineHeight + layoutContext.spacing.lineSpacing, + }; + const rectangles: Rectangle[] = [ + { + left: origin.left, + top: origin.top, + right: origin.left, + bottom: origin.top, + }, + ]; + const children = []; + for (const child of sentenceStructureDiagramModelNode.children) { + const childResult = (() => { + switch (child.type) { + case "coordination-part": + return createSentenceStructureDiagramLayoutNode( + child, + cursor, + nextLineCursor.top - + layoutContext.spacing.lineSpacing - + cursor.top, + layoutContext, + ); + default: + child.type satisfies never; + throw new Error("Unreachable"); + } + })(); + + cursor = { + left: + childResult.rectangles.at(-1)!.right + + layoutContext.spacing.wordSpacing, + top: childResult.rectangles.at(-1)!.top, + }; + nextLineCursor.top = Math.max( + nextLineCursor.top, + ...childResult.rectangles.map( + (rectangle) => + rectangle.bottom + layoutContext.spacing.lineSpacing, + ), + ); + for (const rectangle of childResult.rectangles) { + if (rectangle.top === rectangles.at(-1)!.top) { + rectangles.at(-1)!.right = rectangle.right; + rectangles.at(-1)!.bottom = Math.max( + rectangles.at(-1)!.bottom, + rectangle.bottom, + ); + } else { + rectangles.push({ + left: rectangle.left, + top: rectangle.top, + right: rectangle.right, + bottom: rectangle.bottom, + }); + } + } + children.push(childResult); + } + + switch (sentenceStructureDiagramModelNode.type) { + case "coordination": + return { + type: "coordination", + id: sentenceStructureDiagramModelNode.id, + rectangles, + children, + }; + default: + sentenceStructureDiagramModelNode satisfies never; + throw new Error("Unreachable"); + } + } else { + const coordinationGroupIndicatorReservedWidth = layoutContext + .coordination.groupIndicator + ? coordinationGroupIndicatorMetrics.vertical.groupIndicatorOffsetX + + coordinationGroupIndicatorMetrics.vertical.groupIndicatorWidth + : 0; + const cursor = { + left: origin.left + coordinationGroupIndicatorReservedWidth, + top: origin.top, + }; + const children = []; + for (const child of sentenceStructureDiagramModelNode.children) { + const childResult = (() => { + switch (child.type) { + case "coordination-part": + return createSentenceStructureDiagramLayoutNode( + child, + cursor, + 0, + layoutContext, + ); + default: + child.type satisfies never; + throw new Error("Unreachable"); + } + })(); + + cursor.top = + Math.max( + ...childResult.rectangles.map((rectangle) => rectangle.bottom), + ) + layoutContext.spacing.lineSpacing; + children.push(childResult); + } + + return { + type: "coordination", + id: sentenceStructureDiagramModelNode.id, + rectangles: [ + { + left: origin.left, + top: origin.top, + right: Math.max( + ...children.flatMap((child) => + child.rectangles.map((rectangle) => rectangle.right), + ), + ), + bottom: cursor.top - layoutContext.spacing.lineSpacing, + }, + ], + children, + }; + } + default: + sentenceStructureDiagramModelNode satisfies never; + throw new Error("Unreachable"); + } +} + +function createSentenceStructureDiagramLayoutSentenceTree( + sentenceStructureDiagramModelSentenceTree: SentenceStructureDiagramModelSentenceTree, + origin: { left: number; top: number }, + lineHeight: number, + layoutContext: LayoutContext, +): SentenceStructureDiagramLayoutSentenceTree { + return { + id: sentenceStructureDiagramModelSentenceTree.id, + root: createSentenceStructureDiagramLayoutNode( + sentenceStructureDiagramModelSentenceTree.root, + origin, + lineHeight, + layoutContext, + ), + modifications: sentenceStructureDiagramModelSentenceTree.modifications, + }; +} + +export function createSentenceStructureDiagramLayoutTree( + sentenceStructureDiagramModelTree: SentenceStructureDiagramModelForest, + measureTextWidth: (text: string, fontSize: number) => number, +): SentenceStructureDiagramLayoutTree { + const layoutContext: LayoutContext = !sentenceStructureDiagramModelTree + .layoutSettings.enableReflow + ? { + canvas: sentenceStructureDiagramModelTree.layoutSettings.canvas, + spacing: sentenceStructureDiagramModelTree.layoutSettings.spacing, + enableReflow: false, + coordination: { + layoutDirection: + sentenceStructureDiagramModelTree.layoutSettings.coordination + .layoutDirection, + groupIndicator: + sentenceStructureDiagramModelTree.layoutSettings.coordination + .groupIndicator, + }, + measureTextWidth, + } + : { + canvas: sentenceStructureDiagramModelTree.layoutSettings.canvas, + spacing: sentenceStructureDiagramModelTree.layoutSettings.spacing, + enableReflow: true, + coordination: { + layoutDirection: + sentenceStructureDiagramModelTree.layoutSettings.coordination + .layoutDirection, + groupIndicator: + sentenceStructureDiagramModelTree.layoutSettings.coordination + .groupIndicator, + }, + layoutStrategy: + sentenceStructureDiagramModelTree.layoutSettings.layoutStrategy, + measureTextWidth, + }; + + let cursor = { + left: layoutContext.spacing.padding, + top: layoutContext.spacing.padding, + }; + let currentLineBottom = cursor.top; + const sentences = []; + for (const sentence of sentenceStructureDiagramModelTree.sentences) { + const sentenceResult = createSentenceStructureDiagramLayoutSentenceTree( + sentence, + cursor, + currentLineBottom - cursor.top, + layoutContext, + ); + sentences.push(sentenceResult); + + currentLineBottom = Math.max( + currentLineBottom, + ...sentenceResult.root.rectangles.map((rectangle) => rectangle.bottom), + ); + cursor = + layoutContext.enableReflow && + layoutContext.layoutStrategy.lineBreakStrategy === + "largest-boundary-first" && + layoutContext.layoutStrategy.continuationLineStart === "scope-start" + ? { + left: layoutContext.spacing.padding, + top: currentLineBottom + layoutContext.spacing.lineSpacing, + } + : { + left: + sentenceResult.root.rectangles.at(-1)!.right + + layoutContext.spacing.wordSpacing, + top: sentenceResult.root.rectangles.at(-1)!.top, + }; + } + + return { + canvas: { + width: sentenceStructureDiagramModelTree.layoutSettings.canvas.width, + height: + currentLineBottom + + sentenceStructureDiagramModelTree.layoutSettings.spacing.padding, + }, + layoutSettings: + sentenceStructureDiagramModelTree.layoutSettings.coordination + .layoutDirection === "horizontal" + ? { + coordination: { + layoutDirection: + sentenceStructureDiagramModelTree.layoutSettings.coordination + .layoutDirection, + groupIndicator: + sentenceStructureDiagramModelTree.layoutSettings.coordination + .groupIndicator, + }, + } + : { + coordination: { + layoutDirection: + sentenceStructureDiagramModelTree.layoutSettings.coordination + .layoutDirection, + groupIndicator: + sentenceStructureDiagramModelTree.layoutSettings.coordination + .groupIndicator, + }, + }, + sentences, + }; +} diff --git a/packages/sentence-structure-diagram/diagram-layout/types.ts b/packages/sentence-structure-diagram/diagram-layout/types.ts new file mode 100644 index 0000000..3a7c150 --- /dev/null +++ b/packages/sentence-structure-diagram/diagram-layout/types.ts @@ -0,0 +1,171 @@ +import type { HexRGBColor } from "@sentence-structure-diagram-app/sentence-structure-diagram-notation"; + +export type Rectangle = { + left: number; + top: number; + right: number; + bottom: number; +}; + +type TextStyle = { + fontSize: number; + fontWeight: "normal" | "bold"; + textColor: HexRGBColor; +}; + +type BackgroundStyle = { + backgroundColor: HexRGBColor; +}; + +type StrokeStyle = { + strokeStyle: "solid" | "dashed"; + strokeColor: HexRGBColor; +}; + +export type RangeMarker = + | { + type: "underline"; + style: StrokeStyle; + } + | { + type: "bracket"; + openingBracket: { + text: string; + left: number; + top: number; + }; + closingBracket: { + text: string; + left: number; + top: number; + }; + style: TextStyle; + } + | { + type: "box"; + style: StrokeStyle; + } + | { + type: "highlight"; + style: BackgroundStyle; + } + | null; + +export type SentenceStructureDiagramLayoutWordNode = { + type: "word"; + id: string; + text: string; + whitespaceAfter: string; + rectangle: Rectangle; + style: TextStyle; +}; + +export type SentenceStructureDiagramLayoutSentenceStructureElementNode = { + type: "sentence-structure-element"; + id: string; + rectangles: Rectangle[]; + rangeMarker: RangeMarker; + sentenceElementLabel: { + text: string; + placement: "below-center" | "below-left" | "above-center" | "above-left"; + style: TextStyle; + } | null; + sentenceConstituentLabel: { + text: string; + placement: "below-center" | "below-left" | "above-center" | "above-left"; + style: TextStyle; + } | null; + children: ( + | SentenceStructureDiagramLayoutWordNode + | SentenceStructureDiagramLayoutSentenceStructureElementNode + | SentenceStructureDiagramLayoutCoordinationNode + )[]; +}; + +export type SentenceStructureDiagramLayoutCoordinationPartNode = { + type: "coordination-part"; + id: string; + rectangles: Rectangle[]; + rangeMarker: RangeMarker; + children: ( + | SentenceStructureDiagramLayoutWordNode + | SentenceStructureDiagramLayoutSentenceStructureElementNode + | SentenceStructureDiagramLayoutCoordinationNode + )[]; +}; + +export type SentenceStructureDiagramLayoutCoordinationNode = { + type: "coordination"; + id: string; + rectangles: Rectangle[]; + children: SentenceStructureDiagramLayoutCoordinationPartNode[]; +}; + +export type SentenceStructureDiagramLayoutRootNode = { + type: "root"; + rectangles: Rectangle[]; + children: ( + | SentenceStructureDiagramLayoutWordNode + | SentenceStructureDiagramLayoutSentenceStructureElementNode + | SentenceStructureDiagramLayoutCoordinationNode + )[]; +}; + +export type SentenceStructureDiagramLayoutModification = { + id: string; + modifierSentenceStructureElementNodeId: string; + modifiedSentenceStructureElementNodeId: string; + arrow: { + type: "curved" | "orthogonal"; + style: StrokeStyle; + }; +}; + +export type SentenceStructureDiagramLayoutSentenceTree = { + id: string; + root: SentenceStructureDiagramLayoutRootNode; + modifications: SentenceStructureDiagramLayoutModification[]; +}; + +export type SentenceStructureDiagramLayoutTree = { + canvas: { width: number; height: number }; + layoutSettings: + | { + coordination: { + layoutDirection: "horizontal"; + groupIndicator: { + type: "bus-connector"; + style: StrokeStyle; + } | null; + }; + } + | { + coordination: { + layoutDirection: "vertical"; + groupIndicator: + | { + type: "bracket"; + placement: "left" | "both-sides"; + bracketType: + | "parenthesis" + | "angle-bracket" + | "curly-bracket" + | "square-bracket"; + style: StrokeStyle; + } + | { + type: "bus-connector"; + style: StrokeStyle; + } + | null; + }; + }; + sentences: SentenceStructureDiagramLayoutSentenceTree[]; +}; + +export type SentenceStructureDiagramLayoutNode = + | SentenceStructureDiagramLayoutWordNode + | SentenceStructureDiagramLayoutSentenceStructureElementNode + | SentenceStructureDiagramLayoutCoordinationPartNode + | SentenceStructureDiagramLayoutCoordinationNode + | SentenceStructureDiagramLayoutRootNode; diff --git a/packages/sentence-structure-diagram/diagram-model/create.ts b/packages/sentence-structure-diagram/diagram-model/create.ts new file mode 100644 index 0000000..02dcb3a --- /dev/null +++ b/packages/sentence-structure-diagram/diagram-model/create.ts @@ -0,0 +1,956 @@ +import type { + CoordinationPart, + Modification, + SentenceStructureDecoratedDocumentCoordinationNode, + SentenceStructureDecoratedDocumentCoordinationPartNode, + SentenceStructureDecoratedDocumentForest, + SentenceStructureDecoratedDocumentNode, + SentenceStructureDecoratedDocumentRootNode, + SentenceStructureDecoratedDocumentSentenceStructureElementNode, + SentenceStructureDecoratedDocumentSentenceTree, + SentenceStructureDecoratedDocumentWordNode, + SentenceStructureElement, +} from "@sentence-structure-diagram-app/sentence-structure-data"; +import type { + HexRGBColor, + SentenceStructureDiagramNotation, +} from "@sentence-structure-diagram-app/sentence-structure-diagram-notation"; +import type { + BackgroundStyle, + StrokeStyle, + SentenceStructureDiagramModelCoordinationNode, + SentenceStructureDiagramModelCoordinationPartNode, + SentenceStructureDiagramModelForest, + SentenceStructureDiagramModelModification, + SentenceStructureDiagramModelNode, + SentenceStructureDiagramModelRootNode, + SentenceStructureDiagramModelSentenceStructureElementNode, + SentenceStructureDiagramModelSentenceTree, + SentenceStructureDiagramModelWordNode, + TextStyle, +} from "./types.js"; + +function resolveRangeMarker( + range: + | { + type: "sentence-structure-element"; + sentenceStructureElement: SentenceStructureElement; + } + | { + type: "coordination-part"; + coordinationPart: CoordinationPart; + }, + sentenceStructureDiagramNotation: SentenceStructureDiagramNotation, +): + | { + type: "underline"; + style: StrokeStyle; + } + | { + type: "bracket"; + openingBracket: string; + closingBracket: string; + style: TextStyle; + } + | { + type: "box"; + style: StrokeStyle; + } + | { + type: "text-emphasis"; + style: { + textColor: HexRGBColor; + }; + } + | { + type: "highlight"; + style: BackgroundStyle; + } + | { + type: "bold"; + } + | null { + const rangeMarker = (() => { + switch (range.type) { + case "sentence-structure-element": + switch (range.sentenceStructureElement.kind) { + case "core-sentence-element": + return sentenceStructureDiagramNotation + .sentenceStructureElementNotation.rangeMarking + .coreSentenceElement; + case "sentence-constituent": + switch (range.sentenceStructureElement.type) { + case "phrase": + switch (range.sentenceStructureElement.usage) { + case "nominal": + return sentenceStructureDiagramNotation + .sentenceStructureElementNotation.rangeMarking + .sentenceConstituent.phrase.nominal; + case "adjectival": + return sentenceStructureDiagramNotation + .sentenceStructureElementNotation.rangeMarking + .sentenceConstituent.phrase.adjectival; + case "adverbial": + return sentenceStructureDiagramNotation + .sentenceStructureElementNotation.rangeMarking + .sentenceConstituent.phrase.adverbial; + default: + range.sentenceStructureElement.usage satisfies never; + throw new Error("Unreachable"); + } + case "clause": + switch (range.sentenceStructureElement.usage) { + case "nominal": + return sentenceStructureDiagramNotation + .sentenceStructureElementNotation.rangeMarking + .sentenceConstituent.clause.nominal; + case "adjectival": + return sentenceStructureDiagramNotation + .sentenceStructureElementNotation.rangeMarking + .sentenceConstituent.clause.adjectival; + case "adverbial": + return sentenceStructureDiagramNotation + .sentenceStructureElementNotation.rangeMarking + .sentenceConstituent.clause.adverbial; + default: + range.sentenceStructureElement.usage satisfies never; + throw new Error("Unreachable"); + } + case "adverbial-phrase": + return sentenceStructureDiagramNotation + .sentenceStructureElementNotation.rangeMarking + .sentenceConstituent.adverbialPhrase; + default: + range.sentenceStructureElement satisfies never; + throw new Error("Unreachable"); + } + case "modification-element": + return sentenceStructureDiagramNotation + .sentenceStructureElementNotation.rangeMarking + .modificationElement; + default: + range.sentenceStructureElement satisfies never; + throw new Error("Unreachable"); + } + case "coordination-part": + switch (range.coordinationPart.type) { + case "coordinator": + return sentenceStructureDiagramNotation.coordinationNotation + .rangeMarking.coordinator; + case "correlative": + return sentenceStructureDiagramNotation.coordinationNotation + .rangeMarking.correlative; + case "conjunct": + return sentenceStructureDiagramNotation.coordinationNotation + .rangeMarking.conjunct; + default: + range.coordinationPart.type satisfies never; + throw new Error("Unreachable"); + } + default: + range satisfies never; + throw new Error("Unreachable"); + } + })(); + + switch (rangeMarker.type) { + case "underline": + return { + type: "underline", + style: { + strokeStyle: rangeMarker.lineStyle, + strokeColor: + sentenceStructureDiagramNotation.theme.colors[rangeMarker.color], + }, + }; + case "bracket": + const bracketSymbols = (() => { + switch (rangeMarker.bracketType) { + case "parenthesis": + return { openingBracket: "(", closingBracket: ")" }; + case "angle-bracket": + return { openingBracket: "<", closingBracket: ">" }; + case "curly-bracket": + return { openingBracket: "{", closingBracket: "}" }; + case "square-bracket": + return { openingBracket: "[", closingBracket: "]" }; + default: + rangeMarker.bracketType satisfies never; + throw new Error("Unreachable"); + } + })(); + return { + type: "bracket", + openingBracket: bracketSymbols.openingBracket, + closingBracket: bracketSymbols.closingBracket, + style: { + fontSize: sentenceStructureDiagramNotation.theme.typography.fontSize, + fontWeight: "normal", + textColor: + sentenceStructureDiagramNotation.theme.colors[rangeMarker.color], + }, + }; + case "box": + return { + type: "box", + style: { + strokeStyle: "solid", + strokeColor: + sentenceStructureDiagramNotation.theme.colors[rangeMarker.color], + }, + }; + case "text-emphasis": + return { + type: "text-emphasis", + style: { + textColor: + sentenceStructureDiagramNotation.theme.colors[rangeMarker.color], + }, + }; + case "highlight": + return { + type: "highlight", + style: { + backgroundColor: + sentenceStructureDiagramNotation.theme.colors[rangeMarker.color], + }, + }; + case "bold": + return { + type: "bold", + }; + case "none": + return null; + default: + rangeMarker satisfies never; + throw new Error("Unreachable"); + } +} + +function toSubscriptDigits(number: number): string { + return String(number).replace(/\d/g, (digit) => { + switch (digit) { + case "0": + return "₀"; + case "1": + return "₁"; + case "2": + return "₂"; + case "3": + return "₃"; + case "4": + return "₄"; + case "5": + return "₅"; + case "6": + return "₆"; + case "7": + return "₇"; + case "8": + return "₈"; + case "9": + return "₉"; + default: + throw new Error("Unreachable"); + } + }); +} + +function resolveSentenceElementLabel( + sentenceStructureElement: SentenceStructureElement, + nestingDepth: number, + conjunctOrdinalPath: number[], + sentenceStructureDiagramNotation: SentenceStructureDiagramNotation, +): { + text: string; + placement: "below-center" | "below-left" | "above-center" | "above-left"; + style: TextStyle; +} | null { + if ( + sentenceStructureElement.kind === "modification-element" || + !sentenceStructureElement.sentenceElementName + ) { + return null; + } + + const sentenceElementLabel = (() => { + switch (sentenceStructureElement.sentenceElementName) { + case "S": + return sentenceStructureDiagramNotation.sentenceStructureElementNotation + .sentenceElementLabeling.labels.S; + case "V": + return sentenceStructureDiagramNotation.sentenceStructureElementNotation + .sentenceElementLabeling.labels.V; + case "O": + return sentenceStructureDiagramNotation.sentenceStructureElementNotation + .sentenceElementLabeling.labels.O; + case "C": + return sentenceStructureDiagramNotation.sentenceStructureElementNotation + .sentenceElementLabeling.labels.C; + case "M": + return sentenceStructureDiagramNotation.sentenceStructureElementNotation + .sentenceElementLabeling.labels.M; + default: + sentenceStructureElement.sentenceElementName satisfies never; + throw new Error("Unreachable"); + } + })(); + + if (sentenceElementLabel === "") { + return null; + } + + const sentenceElementLabelWithSuffixes = (() => { + const sentenceElementLabelWithNestingDepthPrimes = + sentenceElementLabel + + (sentenceStructureDiagramNotation.sentenceStructureElementNotation + .sentenceElementLabeling.labelSuffixes.showNestingDepthPrimes + ? "′".repeat(nestingDepth) + : ""); + return ( + sentenceElementLabelWithNestingDepthPrimes + + (sentenceStructureDiagramNotation.sentenceStructureElementNotation + .sentenceElementLabeling.labelSuffixes.showConjunctNumbering + ? conjunctOrdinalPath + .map((ordinal) => toSubscriptDigits(ordinal)) + .join("-") + : "") + ); + })(); + + const sentenceElementPlacement = (() => { + switch (sentenceStructureElement.kind) { + case "core-sentence-element": + return sentenceStructureDiagramNotation.sentenceStructureElementNotation + .sentenceElementLabeling.placement.coreSentenceElement; + case "sentence-constituent": + switch (sentenceStructureElement.type) { + case "phrase": + switch (sentenceStructureElement.usage) { + case "nominal": + return sentenceStructureDiagramNotation + .sentenceStructureElementNotation.sentenceElementLabeling + .placement.sentenceConstituent.phrase.nominal; + case "adjectival": + return sentenceStructureDiagramNotation + .sentenceStructureElementNotation.sentenceElementLabeling + .placement.sentenceConstituent.phrase.adjectival; + case "adverbial": + return sentenceStructureDiagramNotation + .sentenceStructureElementNotation.sentenceElementLabeling + .placement.sentenceConstituent.phrase.adverbial; + default: + sentenceStructureElement.usage satisfies never; + throw new Error("Unreachable"); + } + case "clause": + switch (sentenceStructureElement.usage) { + case "nominal": + return sentenceStructureDiagramNotation + .sentenceStructureElementNotation.sentenceElementLabeling + .placement.sentenceConstituent.clause.nominal; + case "adjectival": + return sentenceStructureDiagramNotation + .sentenceStructureElementNotation.sentenceElementLabeling + .placement.sentenceConstituent.clause.adjectival; + case "adverbial": + return sentenceStructureDiagramNotation + .sentenceStructureElementNotation.sentenceElementLabeling + .placement.sentenceConstituent.clause.adverbial; + default: + sentenceStructureElement.usage satisfies never; + throw new Error("Unreachable"); + } + case "adverbial-phrase": + return sentenceStructureDiagramNotation + .sentenceStructureElementNotation.sentenceElementLabeling + .placement.sentenceConstituent.adverbialPhrase; + default: + sentenceStructureElement satisfies never; + throw new Error("Unreachable"); + } + default: + sentenceStructureElement satisfies never; + throw new Error("Unreachable"); + } + })(); + + return { + text: sentenceElementLabelWithSuffixes, + placement: sentenceElementPlacement, + style: { + fontSize: sentenceStructureDiagramNotation.theme.typography.fontSize, + fontWeight: "normal", + textColor: + sentenceStructureDiagramNotation.theme.colors[ + sentenceStructureDiagramNotation.sentenceStructureElementNotation + .sentenceElementLabeling.color + ], + }, + }; +} + +function resolveSentenceConstituentLabel( + sentenceStructureElement: SentenceStructureElement, + sentenceStructureDiagramNotation: SentenceStructureDiagramNotation, +): { + text: string; + placement: "below-center" | "below-left" | "above-center" | "above-left"; + style: TextStyle; +} | null { + if (sentenceStructureElement.kind !== "sentence-constituent") { + return null; + } + + const sentenceConstituentLabel = (() => { + switch (sentenceStructureElement.type) { + case "phrase": + switch (sentenceStructureElement.usage) { + case "nominal": + return sentenceStructureDiagramNotation + .sentenceStructureElementNotation.sentenceConstituentLabeling + .labels.phrase.nominal; + case "adjectival": + return sentenceStructureDiagramNotation + .sentenceStructureElementNotation.sentenceConstituentLabeling + .labels.phrase.adjectival; + case "adverbial": + return sentenceStructureDiagramNotation + .sentenceStructureElementNotation.sentenceConstituentLabeling + .labels.phrase.adverbial; + default: + sentenceStructureElement.usage satisfies never; + throw new Error("Unreachable"); + } + case "clause": + switch (sentenceStructureElement.usage) { + case "nominal": + return sentenceStructureDiagramNotation + .sentenceStructureElementNotation.sentenceConstituentLabeling + .labels.clause.nominal; + case "adjectival": + return sentenceStructureDiagramNotation + .sentenceStructureElementNotation.sentenceConstituentLabeling + .labels.clause.adjectival; + case "adverbial": + return sentenceStructureDiagramNotation + .sentenceStructureElementNotation.sentenceConstituentLabeling + .labels.clause.adverbial; + default: + sentenceStructureElement.usage satisfies never; + throw new Error("Unreachable"); + } + case "adverbial-phrase": + return sentenceStructureDiagramNotation.sentenceStructureElementNotation + .sentenceConstituentLabeling.labels.adverbialPhrase; + default: + sentenceStructureElement satisfies never; + throw new Error("Unreachable"); + } + })(); + + if (sentenceConstituentLabel === "") { + return null; + } + + const sentenceConstituentPlacement = (() => { + switch (sentenceStructureElement.type) { + case "phrase": + switch (sentenceStructureElement.usage) { + case "nominal": + return sentenceStructureDiagramNotation + .sentenceStructureElementNotation.sentenceConstituentLabeling + .placement.phrase.nominal; + case "adjectival": + return sentenceStructureDiagramNotation + .sentenceStructureElementNotation.sentenceConstituentLabeling + .placement.phrase.adjectival; + case "adverbial": + return sentenceStructureDiagramNotation + .sentenceStructureElementNotation.sentenceConstituentLabeling + .placement.phrase.adverbial; + default: + sentenceStructureElement.usage satisfies never; + throw new Error("Unreachable"); + } + case "clause": + switch (sentenceStructureElement.usage) { + case "nominal": + return sentenceStructureDiagramNotation + .sentenceStructureElementNotation.sentenceConstituentLabeling + .placement.clause.nominal; + case "adjectival": + return sentenceStructureDiagramNotation + .sentenceStructureElementNotation.sentenceConstituentLabeling + .placement.clause.adjectival; + case "adverbial": + return sentenceStructureDiagramNotation + .sentenceStructureElementNotation.sentenceConstituentLabeling + .placement.clause.adverbial; + default: + sentenceStructureElement.usage satisfies never; + throw new Error("Unreachable"); + } + case "adverbial-phrase": + return sentenceStructureDiagramNotation.sentenceStructureElementNotation + .sentenceConstituentLabeling.placement.adverbialPhrase; + default: + sentenceStructureElement satisfies never; + throw new Error("Unreachable"); + } + })(); + + return { + text: sentenceConstituentLabel, + placement: sentenceConstituentPlacement, + style: { + fontSize: sentenceStructureDiagramNotation.theme.typography.fontSize, + fontWeight: "normal", + textColor: + sentenceStructureDiagramNotation.theme.colors[ + sentenceStructureDiagramNotation.sentenceStructureElementNotation + .sentenceConstituentLabeling.color + ], + }, + }; +} + +function resolveCoordinationGroupIndicator( + sentenceStructureDiagramNotation: SentenceStructureDiagramNotation & { + coordinationNotation: { + layout: { + direction: "horizontal"; + }; + }; + }, +): { + type: "bus-connector"; + style: StrokeStyle; +} | null; +function resolveCoordinationGroupIndicator( + sentenceStructureDiagramNotation: SentenceStructureDiagramNotation & { + coordinationNotation: { + layout: { + direction: "vertical"; + }; + }; + }, +): + | { + type: "bracket"; + bracketType: + | "parenthesis" + | "angle-bracket" + | "curly-bracket" + | "square-bracket"; + placement: "left" | "both-sides"; + style: StrokeStyle; + } + | { + type: "bus-connector"; + style: StrokeStyle; + } + | null; +function resolveCoordinationGroupIndicator( + sentenceStructureDiagramNotation: SentenceStructureDiagramNotation, +): + | { + type: "bracket"; + bracketType: + | "parenthesis" + | "angle-bracket" + | "curly-bracket" + | "square-bracket"; + placement: "left" | "both-sides"; + style: StrokeStyle; + } + | { + type: "bus-connector"; + style: StrokeStyle; + } + | null { + switch ( + sentenceStructureDiagramNotation.coordinationNotation.groupIndicator.type + ) { + case "bracket": + return { + type: "bracket", + bracketType: + sentenceStructureDiagramNotation.coordinationNotation.groupIndicator + .bracketType, + placement: + sentenceStructureDiagramNotation.coordinationNotation.groupIndicator + .placement, + style: { + strokeStyle: "solid", + strokeColor: + sentenceStructureDiagramNotation.theme.colors[ + sentenceStructureDiagramNotation.coordinationNotation + .groupIndicator.color + ], + }, + }; + case "bus-connector": + return { + type: "bus-connector", + style: { + strokeStyle: "solid", + strokeColor: + sentenceStructureDiagramNotation.theme.colors[ + sentenceStructureDiagramNotation.coordinationNotation + .groupIndicator.color + ], + }, + }; + case "none": + return null; + default: + sentenceStructureDiagramNotation.coordinationNotation + .groupIndicator satisfies never; + throw new Error("Unreachable"); + } +} + +function createSentenceStructureDiagramModelNode( + sentenceStructureDecoratedDocumentNode: SentenceStructureDecoratedDocumentWordNode, + sentenceStructureDiagramNotation: SentenceStructureDiagramNotation, + textStyleOverride?: Partial, +): SentenceStructureDiagramModelWordNode; +function createSentenceStructureDiagramModelNode( + sentenceStructureDecoratedDocumentNode: SentenceStructureDecoratedDocumentSentenceStructureElementNode, + sentenceStructureDiagramNotation: SentenceStructureDiagramNotation, + textStyleOverride?: Partial, +): SentenceStructureDiagramModelSentenceStructureElementNode; +function createSentenceStructureDiagramModelNode( + sentenceStructureDecoratedDocumentNode: SentenceStructureDecoratedDocumentCoordinationPartNode, + sentenceStructureDiagramNotation: SentenceStructureDiagramNotation, + textStyleOverride?: Partial, +): SentenceStructureDiagramModelCoordinationPartNode; +function createSentenceStructureDiagramModelNode( + sentenceStructureDecoratedDocumentNode: SentenceStructureDecoratedDocumentCoordinationNode, + sentenceStructureDiagramNotation: SentenceStructureDiagramNotation, + textStyleOverride?: Partial, +): SentenceStructureDiagramModelCoordinationNode; +function createSentenceStructureDiagramModelNode( + sentenceStructureDecoratedDocumentNode: SentenceStructureDecoratedDocumentRootNode, + sentenceStructureDiagramNotation: SentenceStructureDiagramNotation, + textStyleOverride?: Partial, +): SentenceStructureDiagramModelRootNode; +function createSentenceStructureDiagramModelNode( + sentenceStructureDecoratedDocumentNode: SentenceStructureDecoratedDocumentNode, + sentenceStructureDiagramNotation: SentenceStructureDiagramNotation, + textStyleOverride?: Partial, +): SentenceStructureDiagramModelNode { + switch (sentenceStructureDecoratedDocumentNode.type) { + case "word": + return { + type: "word", + id: sentenceStructureDecoratedDocumentNode.word.id, + text: sentenceStructureDecoratedDocumentNode.word.text, + whitespaceAfter: + sentenceStructureDecoratedDocumentNode.word.whitespaceAfter, + style: { + fontSize: sentenceStructureDiagramNotation.theme.typography.fontSize, + fontWeight: textStyleOverride?.fontWeight ?? "normal", + textColor: + textStyleOverride?.textColor ?? + sentenceStructureDiagramNotation.theme.colors.text, + }, + }; + case "sentence-structure-element": { + const rangeMarker = resolveRangeMarker( + { + type: "sentence-structure-element", + sentenceStructureElement: + sentenceStructureDecoratedDocumentNode.sentenceStructureElement, + }, + sentenceStructureDiagramNotation, + ); + + return { + type: "sentence-structure-element", + id: sentenceStructureDecoratedDocumentNode.sentenceStructureElement.id, + rangeMarker: + rangeMarker && + rangeMarker.type !== "text-emphasis" && + rangeMarker.type !== "bold" + ? rangeMarker + : null, + sentenceElementLabel: resolveSentenceElementLabel( + sentenceStructureDecoratedDocumentNode.sentenceStructureElement, + sentenceStructureDecoratedDocumentNode.nestingDepth, + sentenceStructureDecoratedDocumentNode.conjunctOrdinalPath, + sentenceStructureDiagramNotation, + ), + sentenceConstituentLabel: resolveSentenceConstituentLabel( + sentenceStructureDecoratedDocumentNode.sentenceStructureElement, + sentenceStructureDiagramNotation, + ), + children: sentenceStructureDecoratedDocumentNode.children.map( + (child) => { + switch (child.type) { + case "word": + return createSentenceStructureDiagramModelNode( + child, + sentenceStructureDiagramNotation, + { + ...textStyleOverride, + ...(rangeMarker?.type === "text-emphasis" + ? { textColor: rangeMarker.style.textColor } + : {}), + ...(rangeMarker?.type === "bold" + ? { fontWeight: "bold" } + : {}), + }, + ); + case "sentence-structure-element": + return createSentenceStructureDiagramModelNode( + child, + sentenceStructureDiagramNotation, + { + ...textStyleOverride, + ...(rangeMarker?.type === "text-emphasis" + ? { textColor: rangeMarker.style.textColor } + : {}), + ...(rangeMarker?.type === "bold" + ? { fontWeight: "bold" } + : {}), + }, + ); + case "coordination": + return createSentenceStructureDiagramModelNode( + child, + sentenceStructureDiagramNotation, + { + ...textStyleOverride, + ...(rangeMarker?.type === "text-emphasis" + ? { textColor: rangeMarker.style.textColor } + : {}), + ...(rangeMarker?.type === "bold" + ? { fontWeight: "bold" } + : {}), + }, + ); + default: + child satisfies never; + throw new Error("Unreachable"); + } + }, + ), + }; + } + case "coordination-part": { + const rangeMarker = resolveRangeMarker( + { + type: "coordination-part", + coordinationPart: + sentenceStructureDecoratedDocumentNode.coordinationPart, + }, + sentenceStructureDiagramNotation, + ); + + return { + type: "coordination-part", + id: sentenceStructureDecoratedDocumentNode.coordinationPart.id, + rangeMarker: + rangeMarker && + rangeMarker.type !== "text-emphasis" && + rangeMarker.type !== "bold" + ? rangeMarker + : null, + children: sentenceStructureDecoratedDocumentNode.children.map( + (child) => { + switch (child.type) { + case "word": + return createSentenceStructureDiagramModelNode( + child, + sentenceStructureDiagramNotation, + { + ...textStyleOverride, + ...(rangeMarker?.type === "text-emphasis" + ? { textColor: rangeMarker.style.textColor } + : {}), + ...(rangeMarker?.type === "bold" + ? { fontWeight: "bold" } + : {}), + }, + ); + case "sentence-structure-element": + return createSentenceStructureDiagramModelNode( + child, + sentenceStructureDiagramNotation, + { + ...textStyleOverride, + ...(rangeMarker?.type === "text-emphasis" + ? { textColor: rangeMarker.style.textColor } + : {}), + ...(rangeMarker?.type === "bold" + ? { fontWeight: "bold" } + : {}), + }, + ); + case "coordination": + return createSentenceStructureDiagramModelNode( + child, + sentenceStructureDiagramNotation, + { + ...textStyleOverride, + ...(rangeMarker?.type === "text-emphasis" + ? { textColor: rangeMarker.style.textColor } + : {}), + ...(rangeMarker?.type === "bold" + ? { fontWeight: "bold" } + : {}), + }, + ); + default: + child satisfies never; + throw new Error("Unreachable"); + } + }, + ), + }; + } + case "coordination": + return { + type: "coordination", + id: sentenceStructureDecoratedDocumentNode.coordination.id, + children: sentenceStructureDecoratedDocumentNode.children.map( + (child) => { + switch (child.type) { + case "coordination-part": + return createSentenceStructureDiagramModelNode( + child, + sentenceStructureDiagramNotation, + textStyleOverride, + ); + default: + child.type satisfies never; + throw new Error("Unreachable"); + } + }, + ), + }; + case "root": + return { + type: "root", + children: sentenceStructureDecoratedDocumentNode.children.map( + (child) => { + switch (child.type) { + case "word": + return createSentenceStructureDiagramModelNode( + child, + sentenceStructureDiagramNotation, + textStyleOverride, + ); + case "sentence-structure-element": + return createSentenceStructureDiagramModelNode( + child, + sentenceStructureDiagramNotation, + textStyleOverride, + ); + case "coordination": + return createSentenceStructureDiagramModelNode( + child, + sentenceStructureDiagramNotation, + textStyleOverride, + ); + default: + child satisfies never; + throw new Error("Unreachable"); + } + }, + ), + }; + default: + sentenceStructureDecoratedDocumentNode satisfies never; + throw new Error("Unreachable"); + } +} + +function createSentenceStructureDiagramModelModification( + modification: Modification, + sentenceStructureDiagramNotation: SentenceStructureDiagramNotation, +): SentenceStructureDiagramModelModification { + return { + id: modification.id, + modifierSentenceStructureElementNodeId: + modification.modifierSentenceStructureElementId, + modifiedSentenceStructureElementNodeId: + modification.modifiedSentenceStructureElementId, + arrow: { + type: sentenceStructureDiagramNotation.modificationNotation.arrow.type, + style: { + strokeStyle: "solid", + strokeColor: + sentenceStructureDiagramNotation.theme.colors[ + sentenceStructureDiagramNotation.modificationNotation.arrow.color + ], + }, + }, + }; +} + +function createSentenceStructureDiagramModelSentenceTree( + sentenceStructureDecoratedDocumentSentenceTree: SentenceStructureDecoratedDocumentSentenceTree, + sentenceStructureDiagramNotation: SentenceStructureDiagramNotation, +): SentenceStructureDiagramModelSentenceTree { + return { + id: sentenceStructureDecoratedDocumentSentenceTree.sentenceId, + root: createSentenceStructureDiagramModelNode( + sentenceStructureDecoratedDocumentSentenceTree.root, + sentenceStructureDiagramNotation, + ), + modifications: + sentenceStructureDecoratedDocumentSentenceTree.modifications.map( + (modification) => + createSentenceStructureDiagramModelModification( + modification, + sentenceStructureDiagramNotation, + ), + ), + }; +} + +export function createSentenceStructureDiagramModelForest( + sentenceStructureDecoratedDocumentForest: SentenceStructureDecoratedDocumentForest, + sentenceStructureDiagramNotation: SentenceStructureDiagramNotation, +): SentenceStructureDiagramModelForest { + return { + layoutSettings: !sentenceStructureDiagramNotation.enableReflow + ? { + canvas: sentenceStructureDiagramNotation.canvas, + spacing: sentenceStructureDiagramNotation.theme.spacing, + enableReflow: false, + coordination: { + layoutDirection: + sentenceStructureDiagramNotation.coordinationNotation.layout + .direction, + groupIndicator: resolveCoordinationGroupIndicator( + sentenceStructureDiagramNotation, + ), + }, + } + : { + canvas: sentenceStructureDiagramNotation.canvas, + spacing: sentenceStructureDiagramNotation.theme.spacing, + enableReflow: true, + coordination: { + layoutDirection: + sentenceStructureDiagramNotation.coordinationNotation.layout + .direction, + groupIndicator: resolveCoordinationGroupIndicator( + sentenceStructureDiagramNotation, + ), + }, + layoutStrategy: sentenceStructureDiagramNotation.layoutStrategy, + }, + sentences: sentenceStructureDecoratedDocumentForest.sentences.map( + (sentenceStructureDecoratedDocumentSentenceTree) => + createSentenceStructureDiagramModelSentenceTree( + sentenceStructureDecoratedDocumentSentenceTree, + sentenceStructureDiagramNotation, + ), + ), + }; +} diff --git a/packages/sentence-structure-diagram/diagram-model/types.ts b/packages/sentence-structure-diagram/diagram-model/types.ts new file mode 100644 index 0000000..07fad69 --- /dev/null +++ b/packages/sentence-structure-diagram/diagram-model/types.ts @@ -0,0 +1,178 @@ +import type { HexRGBColor } from "@sentence-structure-diagram-app/sentence-structure-diagram-notation"; + +export type TextStyle = { + fontSize: number; + fontWeight: "normal" | "bold"; + textColor: HexRGBColor; +}; + +export type BackgroundStyle = { + backgroundColor: HexRGBColor; +}; + +export type StrokeStyle = { + strokeStyle: "solid" | "dashed"; + strokeColor: HexRGBColor; +}; + +type RangeMarker = + | { + type: "underline"; + style: StrokeStyle; + } + | { + type: "bracket"; + openingBracket: string; + closingBracket: string; + style: TextStyle; + } + | { + type: "box"; + style: StrokeStyle; + } + | { + type: "highlight"; + style: BackgroundStyle; + } + | null; + +export type SentenceStructureDiagramModelWordNode = { + type: "word"; + id: string; + text: string; + whitespaceAfter: string; + style: TextStyle; +}; + +export type SentenceStructureDiagramModelSentenceStructureElementNode = { + type: "sentence-structure-element"; + id: string; + rangeMarker: RangeMarker; + sentenceElementLabel: { + text: string; + placement: "below-center" | "below-left" | "above-center" | "above-left"; + style: TextStyle; + } | null; + sentenceConstituentLabel: { + text: string; + placement: "below-center" | "below-left" | "above-center" | "above-left"; + style: TextStyle; + } | null; + children: ( + | SentenceStructureDiagramModelWordNode + | SentenceStructureDiagramModelSentenceStructureElementNode + | SentenceStructureDiagramModelCoordinationNode + )[]; +}; + +export type SentenceStructureDiagramModelCoordinationPartNode = { + type: "coordination-part"; + id: string; + rangeMarker: RangeMarker; + children: ( + | SentenceStructureDiagramModelWordNode + | SentenceStructureDiagramModelSentenceStructureElementNode + | SentenceStructureDiagramModelCoordinationNode + )[]; +}; + +export type SentenceStructureDiagramModelCoordinationNode = { + type: "coordination"; + id: string; + children: SentenceStructureDiagramModelCoordinationPartNode[]; +}; + +export type SentenceStructureDiagramModelRootNode = { + type: "root"; + children: ( + | SentenceStructureDiagramModelWordNode + | SentenceStructureDiagramModelSentenceStructureElementNode + | SentenceStructureDiagramModelCoordinationNode + )[]; +}; + +export type SentenceStructureDiagramModelModification = { + id: string; + modifierSentenceStructureElementNodeId: string; + modifiedSentenceStructureElementNodeId: string; + arrow: { + type: "curved" | "orthogonal"; + style: StrokeStyle; + }; +}; + +export type SentenceStructureDiagramModelSentenceTree = { + id: string; + root: SentenceStructureDiagramModelRootNode; + modifications: SentenceStructureDiagramModelModification[]; +}; + +export type SentenceStructureDiagramModelForest = { + layoutSettings: + | { + canvas: { + width: number; + }; + spacing: { + padding: number; + wordSpacing: number; + lineSpacing: number; + continuationIndent: number; + }; + enableReflow: false; + coordination: { + layoutDirection: "horizontal"; + groupIndicator: { + type: "bus-connector"; + style: StrokeStyle; + } | null; + }; + } + | { + canvas: { + width: number; + }; + spacing: { + padding: number; + wordSpacing: number; + lineSpacing: number; + continuationIndent: number; + }; + enableReflow: true; + coordination: { + layoutDirection: "vertical"; + groupIndicator: + | { + type: "bracket"; + placement: "left" | "both-sides"; + bracketType: + | "parenthesis" + | "angle-bracket" + | "curly-bracket" + | "square-bracket"; + style: StrokeStyle; + } + | { + type: "bus-connector"; + style: StrokeStyle; + } + | null; + }; + layoutStrategy: + | { + lineBreakStrategy: "greedy-word-wrap"; + } + | { + lineBreakStrategy: "largest-boundary-first"; + continuationLineStart: "content-start" | "scope-start"; + }; + }; + sentences: SentenceStructureDiagramModelSentenceTree[]; +}; + +export type SentenceStructureDiagramModelNode = + | SentenceStructureDiagramModelWordNode + | SentenceStructureDiagramModelSentenceStructureElementNode + | SentenceStructureDiagramModelCoordinationPartNode + | SentenceStructureDiagramModelCoordinationNode + | SentenceStructureDiagramModelRootNode; diff --git a/packages/sentence-structure-diagram/diagram-svg/SentenceStructureDiagram.tsx b/packages/sentence-structure-diagram/diagram-svg/SentenceStructureDiagram.tsx new file mode 100644 index 0000000..ad4691d --- /dev/null +++ b/packages/sentence-structure-diagram/diagram-svg/SentenceStructureDiagram.tsx @@ -0,0 +1,212 @@ +import { Fragment } from "react/jsx-runtime"; +import { + sentenceStructureDocumentToXMLString, + type SentenceStructureDocument, +} from "@sentence-structure-diagram-app/sentence-structure-data"; +import { + sentenceStructureDiagramNotationToXMLString, + type SentenceStructureDiagramNotation, +} from "@sentence-structure-diagram-app/sentence-structure-diagram-notation"; +import type { + PathCommand, + SentenceStructureDiagramData, +} from "../diagram-data/types.js"; + +function pathCommandsToSVGPathData(pathCommands: PathCommand[]): string { + return pathCommands + .map((pathCommand) => { + switch (pathCommand.type) { + case "move-to": + return `M ${pathCommand.to.x} ${pathCommand.to.y}`; + case "line-to": + return `L ${pathCommand.to.x} ${pathCommand.to.y}`; + case "cubic-bezier-curve": + return `C ${pathCommand.startControlPoint.x} ${pathCommand.startControlPoint.y} ${pathCommand.endControlPoint.x} ${pathCommand.endControlPoint.y} ${pathCommand.to.x} ${pathCommand.to.y}`; + case "quadratic-bezier-curve": + return `Q ${pathCommand.controlPoint.x} ${pathCommand.controlPoint.y} ${pathCommand.to.x} ${pathCommand.to.y}`; + case "close-path": + return "Z"; + default: + pathCommand satisfies never; + throw new Error("Unreachable"); + } + }) + .join(" "); +} + +type SentenceStructureDiagramProps = { + sentenceStructureDocument: SentenceStructureDocument; + sentenceStructureDiagramNotation: SentenceStructureDiagramNotation; + sentenceStructureDiagramData: SentenceStructureDiagramData; +}; + +export default function SentenceStructureDiagram( + props: SentenceStructureDiagramProps, +) { + return ( + + + {props.sentenceStructureDiagramData.words.map((word) => ( + + {word.text} + + ))} + {props.sentenceStructureDiagramData.underlines.map((underline) => + underline.lineSegments.map((lineSegment, index) => ( + + )), + )} + {props.sentenceStructureDiagramData.brackets.map((bracket) => ( + + + {bracket.openingBracket.text} + + + {bracket.closingBracket.text} + + + ))} + {props.sentenceStructureDiagramData.boxes.map((box) => + box.linePaths.map((linePath, index) => ( + + )), + )} + {props.sentenceStructureDiagramData.highlights.map((highlight) => + highlight.lineRectangles.map((lineRectangle, index) => ( + + )), + )} + {props.sentenceStructureDiagramData.sentenceElementLabels.map( + (sentenceElementLabel) => ( + + {sentenceElementLabel.text} + + ), + )} + {props.sentenceStructureDiagramData.sentenceConstituentLabels.map( + (sentenceConstituentLabel) => ( + + {sentenceConstituentLabel.text} + + ), + )} + {props.sentenceStructureDiagramData.arrows.map((arrow) => ( + + ))} + {props.sentenceStructureDiagramData.coordinationGroupIndicators.map( + (coordinationGroupIndicator) => + coordinationGroupIndicator.linePaths.map((linePath, index) => ( + + )), + )} + + ); +} diff --git a/packages/sentence-structure-diagram/diagram-svg/create.ts b/packages/sentence-structure-diagram/diagram-svg/create.ts new file mode 100644 index 0000000..81169f6 --- /dev/null +++ b/packages/sentence-structure-diagram/diagram-svg/create.ts @@ -0,0 +1,19 @@ +import { renderToStaticMarkup } from "react-dom/server"; +import type { SentenceStructureDocument } from "@sentence-structure-diagram-app/sentence-structure-data"; +import type { SentenceStructureDiagramNotation } from "@sentence-structure-diagram-app/sentence-structure-diagram-notation"; +import type { SentenceStructureDiagramData } from "../diagram-data/types.js"; +import SentenceStructureDiagram from "./SentenceStructureDiagram.js"; + +export function createSentenceStructureDiagramSVGString( + sentenceStructureDocument: SentenceStructureDocument, + sentenceStructureDiagramNotation: SentenceStructureDiagramNotation, + sentenceStructureDiagramData: SentenceStructureDiagramData, +): string { + return renderToStaticMarkup( + SentenceStructureDiagram({ + sentenceStructureDocument: sentenceStructureDocument, + sentenceStructureDiagramNotation, + sentenceStructureDiagramData, + }), + ); +} diff --git a/packages/sentence-structure-diagram/index.ts b/packages/sentence-structure-diagram/index.ts new file mode 100644 index 0000000..7ff30b8 --- /dev/null +++ b/packages/sentence-structure-diagram/index.ts @@ -0,0 +1,8 @@ +export type { + PathCommand, + SentenceStructureDiagramData, +} from "./diagram-data/types.js"; +export { + createSentenceStructureDiagramData, + createSentenceStructureDiagramSvgString, +} from "./create.js"; diff --git a/packages/sentence-structure-diagram-tree/package.json b/packages/sentence-structure-diagram/package.json similarity index 80% rename from packages/sentence-structure-diagram-tree/package.json rename to packages/sentence-structure-diagram/package.json index dbe0906..353eea5 100644 --- a/packages/sentence-structure-diagram-tree/package.json +++ b/packages/sentence-structure-diagram/package.json @@ -1,5 +1,5 @@ { - "name": "@sentence-structure-diagram-app/sentence-structure-diagram-tree", + "name": "@sentence-structure-diagram-app/sentence-structure-diagram", "version": "0.1.0", "type": "module", "main": "./dist/index.js", @@ -10,8 +10,8 @@ }, "dependencies": { "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0", - "@sentence-structure-diagram-app/sentence-structure-diagram-configurations": "^0.1.0", - "@sentence-structure-diagram-app/sentence-structure-tree": "^0.1.0" + "@sentence-structure-diagram-app/sentence-structure-diagram-notation": "^0.1.0", + "zod": "^4.3.5" }, "devDependencies": { "typescript": "^5.9.3" diff --git a/packages/sentence-structure-diagram-tree/tsconfig.json b/packages/sentence-structure-diagram/tsconfig.json similarity index 100% rename from packages/sentence-structure-diagram-tree/tsconfig.json rename to packages/sentence-structure-diagram/tsconfig.json diff --git a/packages/sentence-structure-tree/.gitignore b/packages/sentence-structure-tree/.gitignore deleted file mode 100644 index 9b1c8b1..0000000 --- a/packages/sentence-structure-tree/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/dist diff --git a/packages/sentence-structure-tree/create-tree.ts b/packages/sentence-structure-tree/create-tree.ts deleted file mode 100644 index 548a7e3..0000000 --- a/packages/sentence-structure-tree/create-tree.ts +++ /dev/null @@ -1,299 +0,0 @@ -import { - findCoordinationByStartAndEndWordIndex, - findRangeByStartAndEndWordIndex, - type Coordination, - type SentenceStructureData, - type Word, -} from "@sentence-structure-diagram-app/sentence-structure-data"; -import type { - SentenceStructureCoordinationNode, - SentenceStructureRangeNode, - SentenceStructureTree, - SentenceStructureWordNode, -} from "./types.js"; - -function createSentenceStructureWordNode( - word: Word, -): SentenceStructureWordNode { - return { - type: "word", - word, - }; -} - -function createSpanChildren( - sentenceStructureData: SentenceStructureData, - span: { - startWordIndex: number; - endWordIndex: number; - }, -): ( - | SentenceStructureWordNode - | SentenceStructureRangeNode - | SentenceStructureCoordinationNode -)[] { - const children: ( - | SentenceStructureWordNode - | SentenceStructureRangeNode - | SentenceStructureCoordinationNode - )[] = []; - - let currentWordIndex = span.startWordIndex; - while (currentWordIndex <= span.endWordIndex) { - const nestedRange = - sentenceStructureData.ranges - .filter( - (range) => - range.startWordIndex !== span.startWordIndex || - range.endWordIndex !== span.endWordIndex, - ) - .filter( - (range) => - range.startWordIndex === currentWordIndex && - range.endWordIndex <= span.endWordIndex, - ) - .sort((a, b) => b.endWordIndex - a.endWordIndex) - .at(0) ?? null; - const nestedCoordination = - sentenceStructureData.coordinations - .filter( - (coordination) => - coordination.children.at(0)!.startWordIndex === currentWordIndex && - coordination.children.at(-1)!.endWordIndex <= span.endWordIndex, - ) - .sort( - (a, b) => - b.children.at(-1)!.endWordIndex - a.children.at(-1)!.endWordIndex, - ) - .at(0) ?? null; - if (!nestedRange && !nestedCoordination) { - children.push( - createSentenceStructureWordNode( - sentenceStructureData.words[currentWordIndex]!, - ), - ); - currentWordIndex++; - } else if (nestedRange && !nestedCoordination) { - children.push({ - type: "range", - range: nestedRange, - children: createSpanChildren(sentenceStructureData, nestedRange), - }); - currentWordIndex = nestedRange.endWordIndex + 1; - } else if (!nestedRange && nestedCoordination) { - children.push( - createSentenceStructureCoordinationNode( - sentenceStructureData, - nestedCoordination, - ), - ); - currentWordIndex = nestedCoordination.children.at(-1)!.endWordIndex + 1; - } else if (nestedRange && nestedCoordination) { - const nestedRangeEndWordIndex = nestedRange.endWordIndex; - const nestedCoordinationEndWordIndex = - nestedCoordination.children.at(-1)!.endWordIndex; - if (nestedCoordinationEndWordIndex < nestedRangeEndWordIndex) { - children.push({ - type: "range", - range: nestedRange, - children: createSpanChildren(sentenceStructureData, nestedRange), - }); - currentWordIndex = nestedRangeEndWordIndex + 1; - } else { - children.push( - createSentenceStructureCoordinationNode( - sentenceStructureData, - nestedCoordination, - ), - ); - currentWordIndex = nestedCoordinationEndWordIndex + 1; - } - } - } - - return children; -} - -function createSentenceStructureCoordinationNode( - sentenceStructureData: SentenceStructureData, - coordination: Coordination, -): SentenceStructureCoordinationNode { - const children: SentenceStructureCoordinationNode["children"] = - coordination.children.map((child) => { - const nestedRange = findRangeByStartAndEndWordIndex( - sentenceStructureData, - { - startWordIndex: child.startWordIndex, - endWordIndex: child.endWordIndex, - }, - ); - const nestedCoordination = findCoordinationByStartAndEndWordIndex( - sentenceStructureData, - { - startWordIndex: child.startWordIndex, - endWordIndex: child.endWordIndex, - }, - ); - if (nestedRange && nestedCoordination) { - return { - type: "coordination-child", - coordinationChild: child, - children: [ - { - type: "range", - range: nestedRange, - children: [ - createSentenceStructureCoordinationNode( - sentenceStructureData, - nestedCoordination, - ), - ], - }, - ], - }; - } else if (nestedRange) { - return { - type: "coordination-child", - coordinationChild: child, - children: [ - child.startWordIndex === child.endWordIndex - ? { - type: "range", - range: nestedRange, - children: [ - createSentenceStructureWordNode( - sentenceStructureData.words[child.startWordIndex]!, - ), - ], - } - : { - type: "range", - range: nestedRange, - children: createSpanChildren( - sentenceStructureData, - nestedRange, - ), - }, - ], - }; - } else if (nestedCoordination) { - return { - type: "coordination-child", - coordinationChild: child, - children: [ - createSentenceStructureCoordinationNode( - sentenceStructureData, - nestedCoordination, - ), - ], - }; - } else { - return child.startWordIndex === child.endWordIndex - ? { - type: "coordination-child", - coordinationChild: child, - children: [ - createSentenceStructureWordNode( - sentenceStructureData.words[child.startWordIndex]!, - ), - ], - } - : { - type: "coordination-child", - coordinationChild: child, - children: createSpanChildren(sentenceStructureData, child), - }; - } - }); - - return { - type: "coordination", - coordination, - children, - }; -} - -export function createTree( - sentenceStructureData: SentenceStructureData, -): SentenceStructureTree { - const nestedRange = findRangeByStartAndEndWordIndex(sentenceStructureData, { - startWordIndex: 0, - endWordIndex: sentenceStructureData.words.length - 1, - }); - const nestedCoordination = findCoordinationByStartAndEndWordIndex( - sentenceStructureData, - { - startWordIndex: 0, - endWordIndex: sentenceStructureData.words.length - 1, - }, - ); - if (sentenceStructureData.words.length === 0) { - return { - type: "tree", - children: [], - }; - } - if (nestedRange && nestedCoordination) { - return { - type: "tree", - children: [ - { - type: "range", - range: nestedRange, - children: [ - createSentenceStructureCoordinationNode( - sentenceStructureData, - nestedCoordination, - ), - ], - }, - ], - }; - } else if (nestedRange) { - return { - type: "tree", - children: [ - sentenceStructureData.words.length === 1 - ? { - type: "range", - range: nestedRange, - children: [ - createSentenceStructureWordNode( - sentenceStructureData.words[0]!, - ), - ], - } - : { - type: "range", - range: nestedRange, - children: createSpanChildren(sentenceStructureData, nestedRange), - }, - ], - }; - } else if (nestedCoordination) { - return { - type: "tree", - children: [ - createSentenceStructureCoordinationNode( - sentenceStructureData, - nestedCoordination, - ), - ], - }; - } else { - return sentenceStructureData.words.length === 1 - ? { - type: "tree", - children: [ - createSentenceStructureWordNode(sentenceStructureData.words[0]!), - ], - } - : { - type: "tree", - children: createSpanChildren(sentenceStructureData, { - startWordIndex: 0, - endWordIndex: sentenceStructureData.words.length - 1, - }), - }; - } -} diff --git a/packages/sentence-structure-tree/index.ts b/packages/sentence-structure-tree/index.ts deleted file mode 100644 index ada3a88..0000000 --- a/packages/sentence-structure-tree/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -export { createTree } from "./create-tree.js"; -export type { - SentenceStructureWordNode, - SentenceStructureRangeNode, - SentenceStructureCoordinationChildNode, - SentenceStructureCoordinationNode, - SentenceStructureTree, - SentenceStructureNode, -} from "./types.js"; diff --git a/packages/sentence-structure-tree/tsconfig.json b/packages/sentence-structure-tree/tsconfig.json deleted file mode 100644 index d6a2b39..0000000 --- a/packages/sentence-structure-tree/tsconfig.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - // Visit https://aka.ms/tsconfig to read more about this file - "compilerOptions": { - // File Layout - // "rootDir": "./src", - "outDir": "./dist", - - // Environment Settings - // See also https://aka.ms/tsconfig/module - "module": "nodenext", - "target": "esnext", - "types": [], - // For nodejs: - // "lib": ["esnext"], - // "types": ["node"], - // and npm install -D @types/node - - // Other Outputs - "sourceMap": true, - "declaration": true, - "declarationMap": true, - - // Stricter Typechecking Options - "noUncheckedIndexedAccess": true, - "exactOptionalPropertyTypes": true, - - // Style Options - // "noImplicitReturns": true, - // "noImplicitOverride": true, - // "noUnusedLocals": true, - // "noUnusedParameters": true, - // "noFallthroughCasesInSwitch": true, - // "noPropertyAccessFromIndexSignature": true, - - // Recommended Options - "strict": true, - "jsx": "react-jsx", - "verbatimModuleSyntax": true, - "isolatedModules": true, - "noUncheckedSideEffectImports": true, - "moduleDetection": "force", - "skipLibCheck": true - } -} diff --git a/packages/sentence-structure-tree/types.ts b/packages/sentence-structure-tree/types.ts deleted file mode 100644 index ff3f8f4..0000000 --- a/packages/sentence-structure-tree/types.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { - type Coordination, - type CoordinationChild, - type Range, - type Word, -} from "@sentence-structure-diagram-app/sentence-structure-data"; - -export type SentenceStructureWordNode = { - type: "word"; - word: Word; -}; - -export type SentenceStructureRangeNode = { - type: "range"; - range: Range; - children: ( - | SentenceStructureWordNode - | SentenceStructureRangeNode - | SentenceStructureCoordinationNode - )[]; -}; - -export type SentenceStructureCoordinationChildNode = { - type: "coordination-child"; - coordinationChild: CoordinationChild; - children: ( - | SentenceStructureWordNode - | SentenceStructureRangeNode - | SentenceStructureCoordinationNode - )[]; -}; - -export type SentenceStructureCoordinationNode = { - type: "coordination"; - coordination: Coordination; - children: SentenceStructureCoordinationChildNode[]; -}; - -export type SentenceStructureTree = { - type: "tree"; - children: ( - | SentenceStructureWordNode - | SentenceStructureRangeNode - | SentenceStructureCoordinationNode - )[]; -}; - -export type SentenceStructureNode = - | SentenceStructureWordNode - | SentenceStructureRangeNode - | SentenceStructureCoordinationChildNode - | SentenceStructureCoordinationNode - | SentenceStructureTree; diff --git a/stanza-server/.env.sample b/stanza-server/.env.sample new file mode 100644 index 0000000..f1a78c5 --- /dev/null +++ b/stanza-server/.env.sample @@ -0,0 +1 @@ +WEB_ORIGIN=http://localhost:5173 diff --git a/stanza-server/.gitignore b/stanza-server/.gitignore new file mode 100644 index 0000000..d9fe400 --- /dev/null +++ b/stanza-server/.gitignore @@ -0,0 +1,2 @@ +/__pycache__ +/.env diff --git a/stanza-server/.python-version b/stanza-server/.python-version new file mode 100644 index 0000000..6324d40 --- /dev/null +++ b/stanza-server/.python-version @@ -0,0 +1 @@ +3.14 diff --git a/stanza-server/main.py b/stanza-server/main.py new file mode 100644 index 0000000..38ff70b --- /dev/null +++ b/stanza-server/main.py @@ -0,0 +1,303 @@ +from fastapi import Body, FastAPI +from fastapi.middleware.cors import CORSMiddleware +import os +from typing import Annotated, Literal +from pydantic import BaseModel +import stanza + +app = FastAPI() + +origins = [ + os.getenv("WEB_ORIGIN"), +] + +app.add_middleware( + CORSMiddleware, + allow_origins=origins, + allow_methods=["*"], +) + +stanza.download( + "en", + processors="tokenize,mwt,pos,lemma,depparse,constituency", + package="default_accurate", +) + +nlp = stanza.Pipeline( + "en", + processors="tokenize,mwt,pos,lemma,depparse,constituency", + package="default_accurate", + download_method=None, +) + + +class Word(BaseModel): + id: int + text: str + lemma: str + upos: Literal[ + # See https://universaldependencies.org/u/pos/ + # + # Extracted by: + # print( + # torch.load( + # ".../stanza/1.11.0/resources/en/pos/combined_electra-large.pt", + # map_location=torch.device("cpu"), + # )["vocab"]["upos"]["_id2unit"] + # ) + "ADJ", # adjective + "ADP", # adposition + "ADV", # adverb + "AUX", # auxiliary + "CCONJ", # coordinating conjunction + "DET", # determiner + "INTJ", # interjection + "NOUN", # noun + "NUM", # numeral + "PART", # particle + "PRON", # pronoun + "PROPN", # proper noun + "PUNCT", # punctuation + "SCONJ", # subordinating conjunction + "SYM", # symbol + "VERB", # verb + "X", # other + ] + xpos: str + feats: str | None + head: int + deprel: Literal[ + # See + # - https://stanfordnlp.github.io/stanza/combined_models.html#combined-models + # - https://universaldependencies.org/treebanks/en_ewt/index.html#relations + # - https://universaldependencies.org/treebanks/en_gum/index.html#relations + # - https://universaldependencies.org/treebanks/en_gumreddit/index.html#relations + # - https://universaldependencies.org/treebanks/en_pud/index.html#relations + # - https://universaldependencies.org/treebanks/en_pronouns/index.html#relations + # - https://universaldependencies.org/u/dep/ + # + # Extracted by: + # print( + # torch.load( + # ".../stanza/1.11.0/resources/en/depparse/combined_electra-large.pt", + # map_location=torch.device("cpu"), + # )["vocab"]["deprel"]["_id2unit"] + # ) + "acl", # clausal modifier of noun (adnominal clause) + "acl:relcl", # relative clause modifier + "advcl", # adverbial clause modifier + "advcl:relcl", # adverbial relative clause modifier + "advmod", # adverbial modifier + "amod", # adjectival modifier + "appos", # appositional modifier + "aux", # auxiliary + "aux:pass", # passive auxiliary + "case", # case marking + "cc", # coordinating conjunction + "cc:preconj", # preconjunct + "ccomp", # clausal complement + "compound", # compound + "compound:prt", # phrasal verb particle + "conj", # conjunct + "cop", # copula + "csubj", # clausal subject + "csubj:outer", # outer clause clausal subject + "csubj:pass", # clausal passive subject + "dep", # unspecified dependency + "det", # determiner + "det:predet", + "discourse", # discourse element + "dislocated", # dislocated elements + "expl", # expletive + "fixed", # fixed multiword expression + "flat", # flat expression + "goeswith", # goes with + "iobj", # indirect object + "list", # list + "mark", # marker + "nmod", # nominal modifier + "nmod:desc", + "nmod:poss", # possessive nominal modifier + "nmod:unmarked", + "nsubj", # nominal subject + "nsubj:outer", # outer clause nominal subject + "nsubj:pass", # passive nominal subject + "nummod", # numeric modifier + "obj", # object + "obl", # oblique nominal + "obl:agent", # oblique agent in passive construction + "obl:npmod", + "obl:tmod", # temporal modifier + "obl:unmarked", + "orphan", # orphan + "parataxis", # parataxis + "punct", # punctuation + "reparandum", # overridden disfluency + "root", # root + "vocative", # vocative + "xcomp", # open clausal complement + ] + + +class Token(BaseModel): + text: str + words: list["Word"] + spaces_after: str + spaces_before: str + + +class ParseTree(BaseModel): + label: ( + # See + # - https://stanfordnlp.github.io/stanza/constituency.html#english + # - https://aclanthology.org/J93-2004/ + Literal[ + # Extracted by: + # print( + # torch.load( + # ".../stanza/1.11.0/resources/en/constituency/ptb3-revised_electra-large.pt", + # map_location=torch.device("cpu"), + # )["params"]["constituents"] + # ) + "ADJP", # Adjective phrase + "ADVP", # Adverb phrase + "CONJP", + "FRAG", + "INTJ", + "LST", + "NAC", + "NML", + "NP", # Noun phrase + "PP", # Prepositional phrase + "PRN", + "PRT", + "QP", + "ROOT", + "RRC", + "S", # Simple declarative clause + "SBAR", # Clause introduced by subordinating conjunction or 0 + "SBARQ", # Direct question introduced by wh-word or wh-phrase + "SINV", # Declarative sentence with subject-aux inversion + "SQ", # Subconstituent of SBARQ excluding wh-word or wh-phrase + "UCP", + "VP", # Verb phrase + "WHADJP", + "WHADVP", # wh-adverb phrase + "WHNP", # wh-noun phrase + "WHPP", # wh-prepositional phrase + "X", # Constituent of unknown or uncertain category + ] + | Literal[ + # Extracted by: + # print( + # torch.load( + # ".../stanza/1.11.0/resources/en/constituency/ptb3-revised_electra-large.pt", + # map_location=torch.device("cpu"), + # )["params"]["tags"] + # ) + "$", # Dollar sign + "''", + ",", # Comma + "-LRB-", + "-RRB-", + ".", # Sentence-final punctuation + ":", # Colon, semi-colon + "ADD", + "AFX", + "CC", # Coordinating conjunction + "CD", # Cardinal number + "DT", # Determiner + "EX", # Existential there + "FW", # Foreign word + "HYPH", + "IN", # Preposition/subordinating conjunction + "JJ", # Adjective + "JJR", # Adjective, comparative + "JJS", # Adjective, superlative + "LS", # List item marker + "MD", # Modal + "NFP", + "NN", # Noun, singular or mass + "NNP", # Proper noun, singular + "NNPS", # Proper noun, plural + "NNS", # Noun, plural + "PDT", # Predeterminer + "POS", # Possessive ending + "PRP", # Personal pronoun + "PRP$", + "RB", # Adverb + "RBR", # Adverb, comparative + "RBS", # Adverb, superlative + "RP", # Particle + "SYM", # Symbol (mathematical or scientific) + "TO", # to + "UH", # Interjection + "VB", # Verb, base form + "VBD", # Verb, past tense + "VBG", # Verb, gerund/present participle + "VBN", # Verb, past participle + "VBP", # Verb, non-3rd ps. sing. present + "VBZ", # Verb, 3rd ps. sing. present + "WDT", # wh-determiner + "WP", # wh-pronoun + "WP$", # Possessive wh-pronoun + "WRB", # wh-adverb + "``", + ] + | str + ) + children: list["ParseTree"] + + +class Sentence(BaseModel): + text: str + tokens: list["Token"] + constituency: "ParseTree" + + +class Document(BaseModel): + text: str + sentences: list["Sentence"] + + +@app.post("/") +def parse(text: Annotated[str, Body(embed=True)]) -> Document: + doc = nlp(text) + + def convert_constituency(constituency) -> ParseTree: + return ParseTree( + label=constituency.label, + children=[convert_constituency(child) for child in constituency.children], + ) + + return Document( + text=doc.text, + sentences=[ + Sentence( + text=sentence.text, + tokens=[ + Token( + text=token.text, + words=[ + Word( + id=word.id, + text=word.text, + lemma=word.lemma, + upos=word.upos, + xpos=word.xpos, + feats=word.feats, + head=word.head, + deprel=word.deprel, + ) + for word in token.words + ], + spaces_after=token.spaces_after, + spaces_before=token.spaces_before, + ) + for token in sentence.tokens + ], + constituency=convert_constituency(sentence.constituency), + ) + for sentence in doc.sentences + ], + ) diff --git a/stanza-server/pyproject.toml b/stanza-server/pyproject.toml new file mode 100644 index 0000000..6afbac9 --- /dev/null +++ b/stanza-server/pyproject.toml @@ -0,0 +1,13 @@ +[project] +name = "stanza-server" +version = "0.1.0" +requires-python = ">=3.14" +dependencies = [ + "fastapi[standard]>=0.135.1", + "stanza[transformers]>=1.11.1", +] + +[dependency-groups] +dev = [ + "ruff>=0.15.5", +] diff --git a/stanza-server/uv.lock b/stanza-server/uv.lock new file mode 100644 index 0000000..2c8d379 --- /dev/null +++ b/stanza-server/uv.lock @@ -0,0 +1,1408 @@ +version = 1 +revision = 3 +requires-python = ">=3.14" + +[[package]] +name = "accelerate" +version = "1.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pyyaml" }, + { name = "safetensors" }, + { name = "torch" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ca/14/787e5498cd062640f0f3d92ef4ae4063174f76f9afd29d13fc52a319daae/accelerate-1.13.0.tar.gz", hash = "sha256:d631b4e0f5b3de4aff2d7e9e6857d164810dfc3237d54d017f075122d057b236", size = 402835, upload-time = "2026-03-04T19:34:12.359Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/46/02ac5e262d4af18054b3e922b2baedbb2a03289ee792162de60a865defc5/accelerate-1.13.0-py3-none-any.whl", hash = "sha256:cf1a3efb96c18f7b152eb0fa7490f3710b19c3f395699358f08decca2b8b62e0", size = 383744, upload-time = "2026-03-04T19:34:10.313Z" }, +] + +[[package]] +name = "annotated-doc" +version = "0.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "anyio" +version = "4.12.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, +] + +[[package]] +name = "certifi" +version = "2026.2.25" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/35/02daf95b9cd686320bb622eb148792655c9412dbb9b67abb5694e5910a24/charset_normalizer-3.4.5.tar.gz", hash = "sha256:95adae7b6c42a6c5b5b559b1a99149f090a57128155daeea91732c8d970d8644", size = 134804, upload-time = "2026-03-06T06:03:19.46Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/be/0f0fd9bb4a7fa4fb5067fb7d9ac693d4e928d306f80a0d02bde43a7c4aee/charset_normalizer-3.4.5-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8197abe5ca1ffb7d91e78360f915eef5addff270f8a71c1fc5be24a56f3e4873", size = 280232, upload-time = "2026-03-06T06:02:01.508Z" }, + { url = "https://files.pythonhosted.org/packages/28/02/983b5445e4bef49cd8c9da73a8e029f0825f39b74a06d201bfaa2e55142a/charset_normalizer-3.4.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2aecdb364b8a1802afdc7f9327d55dad5366bc97d8502d0f5854e50712dbc5f", size = 189688, upload-time = "2026-03-06T06:02:02.857Z" }, + { url = "https://files.pythonhosted.org/packages/d0/88/152745c5166437687028027dc080e2daed6fe11cfa95a22f4602591c42db/charset_normalizer-3.4.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a66aa5022bf81ab4b1bebfb009db4fd68e0c6d4307a1ce5ef6a26e5878dfc9e4", size = 206833, upload-time = "2026-03-06T06:02:05.127Z" }, + { url = "https://files.pythonhosted.org/packages/cb/0f/ebc15c8b02af2f19be9678d6eed115feeeccc45ce1f4b098d986c13e8769/charset_normalizer-3.4.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d77f97e515688bd615c1d1f795d540f32542d514242067adcb8ef532504cb9ee", size = 202879, upload-time = "2026-03-06T06:02:06.446Z" }, + { url = "https://files.pythonhosted.org/packages/38/9c/71336bff6934418dc8d1e8a1644176ac9088068bc571da612767619c97b3/charset_normalizer-3.4.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01a1ed54b953303ca7e310fafe0fe347aab348bd81834a0bcd602eb538f89d66", size = 195764, upload-time = "2026-03-06T06:02:08.763Z" }, + { url = "https://files.pythonhosted.org/packages/b7/95/ce92fde4f98615661871bc282a856cf9b8a15f686ba0af012984660d480b/charset_normalizer-3.4.5-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:b2d37d78297b39a9eb9eb92c0f6df98c706467282055419df141389b23f93362", size = 183728, upload-time = "2026-03-06T06:02:10.137Z" }, + { url = "https://files.pythonhosted.org/packages/1c/e7/f5b4588d94e747ce45ae680f0f242bc2d98dbd4eccfab73e6160b6893893/charset_normalizer-3.4.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e71bbb595973622b817c042bd943c3f3667e9c9983ce3d205f973f486fec98a7", size = 192937, upload-time = "2026-03-06T06:02:11.663Z" }, + { url = "https://files.pythonhosted.org/packages/f9/29/9d94ed6b929bf9f48bf6ede6e7474576499f07c4c5e878fb186083622716/charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4cd966c2559f501c6fd69294d082c2934c8dd4719deb32c22961a5ac6db0df1d", size = 192040, upload-time = "2026-03-06T06:02:13.489Z" }, + { url = "https://files.pythonhosted.org/packages/15/d2/1a093a1cf827957f9445f2fe7298bcc16f8fc5e05c1ed2ad1af0b239035e/charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d5e52d127045d6ae01a1e821acfad2f3a1866c54d0e837828538fabe8d9d1bd6", size = 184107, upload-time = "2026-03-06T06:02:14.83Z" }, + { url = "https://files.pythonhosted.org/packages/0f/7d/82068ce16bd36135df7b97f6333c5d808b94e01d4599a682e2337ed5fd14/charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:30a2b1a48478c3428d047ed9690d57c23038dac838a87ad624c85c0a78ebeb39", size = 208310, upload-time = "2026-03-06T06:02:16.165Z" }, + { url = "https://files.pythonhosted.org/packages/84/4e/4dfb52307bb6af4a5c9e73e482d171b81d36f522b21ccd28a49656baa680/charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:d8ed79b8f6372ca4254955005830fd61c1ccdd8c0fac6603e2c145c61dd95db6", size = 192918, upload-time = "2026-03-06T06:02:18.144Z" }, + { url = "https://files.pythonhosted.org/packages/08/a4/159ff7da662cf7201502ca89980b8f06acf3e887b278956646a8aeb178ab/charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:c5af897b45fa606b12464ccbe0014bbf8c09191e0a66aab6aa9d5cf6e77e0c94", size = 204615, upload-time = "2026-03-06T06:02:19.821Z" }, + { url = "https://files.pythonhosted.org/packages/d6/62/0dd6172203cb6b429ffffc9935001fde42e5250d57f07b0c28c6046deb6b/charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1088345bcc93c58d8d8f3d783eca4a6e7a7752bbff26c3eee7e73c597c191c2e", size = 197784, upload-time = "2026-03-06T06:02:21.86Z" }, + { url = "https://files.pythonhosted.org/packages/c7/5e/1aab5cb737039b9c59e63627dc8bbc0d02562a14f831cc450e5f91d84ce1/charset_normalizer-3.4.5-cp314-cp314-win32.whl", hash = "sha256:ee57b926940ba00bca7ba7041e665cc956e55ef482f851b9b65acb20d867e7a2", size = 133009, upload-time = "2026-03-06T06:02:23.289Z" }, + { url = "https://files.pythonhosted.org/packages/40/65/e7c6c77d7aaa4c0d7974f2e403e17f0ed2cb0fc135f77d686b916bf1eead/charset_normalizer-3.4.5-cp314-cp314-win_amd64.whl", hash = "sha256:4481e6da1830c8a1cc0b746b47f603b653dadb690bcd851d039ffaefe70533aa", size = 143511, upload-time = "2026-03-06T06:02:26.195Z" }, + { url = "https://files.pythonhosted.org/packages/ba/91/52b0841c71f152f563b8e072896c14e3d83b195c188b338d3cc2e582d1d4/charset_normalizer-3.4.5-cp314-cp314-win_arm64.whl", hash = "sha256:97ab7787092eb9b50fb47fa04f24c75b768a606af1bcba1957f07f128a7219e4", size = 133775, upload-time = "2026-03-06T06:02:27.473Z" }, + { url = "https://files.pythonhosted.org/packages/c5/60/3a621758945513adfd4db86827a5bafcc615f913dbd0b4c2ed64a65731be/charset_normalizer-3.4.5-py3-none-any.whl", hash = "sha256:9db5e3fcdcee89a78c04dffb3fe33c79f77bd741a624946db2591c81b2fc85b0", size = 55455, upload-time = "2026-03-06T06:03:17.827Z" }, +] + +[[package]] +name = "click" +version = "8.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "cuda-bindings" +version = "12.9.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-pathfinder" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/af/6dfd8f2ed90b1d4719bc053ff8940e494640fe4212dc3dd72f383e4992da/cuda_bindings-12.9.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8b72ee72a9cc1b531db31eebaaee5c69a8ec3500e32c6933f2d3b15297b53686", size = 11922703, upload-time = "2025-10-21T14:52:03.585Z" }, + { url = "https://files.pythonhosted.org/packages/6c/19/90ac264acc00f6df8a49378eedec9fd2db3061bf9263bf9f39fd3d8377c3/cuda_bindings-12.9.4-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d80bffc357df9988dca279734bc9674c3934a654cab10cadeed27ce17d8635ee", size = 11924658, upload-time = "2025-10-21T14:52:10.411Z" }, +] + +[[package]] +name = "cuda-pathfinder" +version = "1.4.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/02/59a5bc738a09def0b49aea0e460bdf97f65206d0d041246147cf6207e69c/cuda_pathfinder-1.4.1-py3-none-any.whl", hash = "sha256:40793006082de88e0950753655e55558a446bed9a7d9d0bcb48b2506d50ed82a", size = 43903, upload-time = "2026-03-06T21:05:24.372Z" }, +] + +[[package]] +name = "dnspython" +version = "2.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, +] + +[[package]] +name = "email-validator" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dnspython" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238, upload-time = "2025-08-26T13:09:06.831Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" }, +] + +[[package]] +name = "emoji" +version = "2.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/78/0d2db9382c92a163d7095fc08efff7800880f830a152cfced40161e7638d/emoji-2.15.0.tar.gz", hash = "sha256:eae4ab7d86456a70a00a985125a03263a5eac54cd55e51d7e184b1ed3b6757e4", size = 615483, upload-time = "2025-09-21T12:13:02.755Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/5e/4b5aaaabddfacfe36ba7768817bd1f71a7a810a43705e531f3ae4c690767/emoji-2.15.0-py3-none-any.whl", hash = "sha256:205296793d66a89d88af4688fa57fd6496732eb48917a87175a023c8138995eb", size = 608433, upload-time = "2025-09-21T12:13:01.197Z" }, +] + +[[package]] +name = "fastapi" +version = "0.135.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc" }, + { name = "pydantic" }, + { name = "starlette" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e7/7b/f8e0211e9380f7195ba3f3d40c292594fd81ba8ec4629e3854c353aaca45/fastapi-0.135.1.tar.gz", hash = "sha256:d04115b508d936d254cea545b7312ecaa58a7b3a0f84952535b4c9afae7668cd", size = 394962, upload-time = "2026-03-01T18:18:29.369Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e4/72/42e900510195b23a56bde950d26a51f8b723846bfcaa0286e90287f0422b/fastapi-0.135.1-py3-none-any.whl", hash = "sha256:46e2fc5745924b7c840f71ddd277382af29ce1cdb7d5eab5bf697e3fb9999c9e", size = 116999, upload-time = "2026-03-01T18:18:30.831Z" }, +] + +[package.optional-dependencies] +standard = [ + { name = "email-validator" }, + { name = "fastapi-cli", extra = ["standard"] }, + { name = "httpx" }, + { name = "jinja2" }, + { name = "pydantic-extra-types" }, + { name = "pydantic-settings" }, + { name = "python-multipart" }, + { name = "uvicorn", extra = ["standard"] }, +] + +[[package]] +name = "fastapi-cli" +version = "0.0.24" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "rich-toolkit" }, + { name = "typer" }, + { name = "uvicorn", extra = ["standard"] }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6e/58/74797ae9e4610cfa0c6b34c8309096d3b20bb29be3b8b5fbf1004d10fa5f/fastapi_cli-0.0.24.tar.gz", hash = "sha256:1afc9c9e21d7ebc8a3ca5e31790cd8d837742be7e4f8b9236e99cb3451f0de00", size = 19043, upload-time = "2026-02-24T10:45:10.476Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/4b/68f9fe268e535d79c76910519530026a4f994ce07189ac0dded45c6af825/fastapi_cli-0.0.24-py3-none-any.whl", hash = "sha256:4a1f78ed798f106b4fee85ca93b85d8fe33c0a3570f775964d37edb80b8f0edc", size = 12304, upload-time = "2026-02-24T10:45:09.552Z" }, +] + +[package.optional-dependencies] +standard = [ + { name = "fastapi-cloud-cli" }, + { name = "uvicorn", extra = ["standard"] }, +] + +[[package]] +name = "fastapi-cloud-cli" +version = "0.14.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fastar" }, + { name = "httpx" }, + { name = "pydantic", extra = ["email"] }, + { name = "rich-toolkit" }, + { name = "rignore" }, + { name = "sentry-sdk" }, + { name = "typer" }, + { name = "uvicorn", extra = ["standard"] }, +] +sdist = { url = "https://files.pythonhosted.org/packages/74/30/1665ad6bd1c285d1c6947e6ab0eae168bc44a9b45d5fc11fa930603db1c7/fastapi_cloud_cli-0.14.1.tar.gz", hash = "sha256:5b086182570008f67d9ae989870102f595e4e216493cabcd270ef6cd0401339f", size = 39999, upload-time = "2026-03-08T01:40:24.166Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/c2/0117d2a1b93eb7c6d2084e6be320d34a404f621eb01a26c1471c0eb4ee82/fastapi_cloud_cli-0.14.1-py3-none-any.whl", hash = "sha256:99ab3a2fbd1880121a62fb9c4f584e15c42ef0fe762cb5f7380a548081e60d05", size = 28359, upload-time = "2026-03-08T01:40:24.949Z" }, +] + +[[package]] +name = "fastar" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/69/e7/f89d54fb04104114dd0552836dc2b47914f416cc0e200b409dd04a33de5e/fastar-0.8.0.tar.gz", hash = "sha256:f4d4d68dbf1c4c2808f0e730fac5843493fc849f70fe3ad3af60dfbaf68b9a12", size = 68524, upload-time = "2025-11-26T02:36:00.72Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/4a/9573b87a0ef07580ed111e7230259aec31bb33ca3667963ebee77022ec61/fastar-0.8.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:50b36ce654ba44b0e13fae607ae17ee6e1597b69f71df1bee64bb8328d881dfc", size = 706041, upload-time = "2025-11-26T02:34:40.638Z" }, + { url = "https://files.pythonhosted.org/packages/4a/19/f95444a1d4f375333af49300aa75ee93afa3335c0e40fda528e460ed859c/fastar-0.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:63a892762683d7ab00df0227d5ea9677c62ff2cde9b875e666c0be569ed940f3", size = 628617, upload-time = "2025-11-26T02:34:24.893Z" }, + { url = "https://files.pythonhosted.org/packages/b3/c9/b51481b38b7e3f16ef2b9e233b1a3623386c939d745d6e41bbd389eaae30/fastar-0.8.0-cp314-cp314-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4ae6a145c1bff592644bde13f2115e0239f4b7babaf506d14e7d208483cf01a5", size = 869299, upload-time = "2025-11-26T02:33:54.274Z" }, + { url = "https://files.pythonhosted.org/packages/bf/02/3ba1267ee5ba7314e29c431cf82eaa68586f2c40cdfa08be3632b7d07619/fastar-0.8.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ae0ff7c0a1c7e1428404b81faee8aebef466bfd0be25bfe4dabf5d535c68741", size = 764667, upload-time = "2025-11-26T02:32:49.606Z" }, + { url = "https://files.pythonhosted.org/packages/1b/84/bf33530fd015b5d7c2cc69e0bce4a38d736754a6955487005aab1af6adcd/fastar-0.8.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dbfd87dbd217b45c898b2dbcd0169aae534b2c1c5cbe3119510881f6a5ac8ef5", size = 763993, upload-time = "2025-11-26T02:33:05.782Z" }, + { url = "https://files.pythonhosted.org/packages/da/e0/9564d24e7cea6321a8d921c6d2a457044a476ef197aa4708e179d3d97f0d/fastar-0.8.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a5abd99fcba83ef28c8fe6ae2927edc79053db43a0457a962ed85c9bf150d37", size = 930153, upload-time = "2025-11-26T02:33:21.53Z" }, + { url = "https://files.pythonhosted.org/packages/35/b1/6f57fcd8d6e192cfebf97e58eb27751640ad93784c857b79039e84387b51/fastar-0.8.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:91d4c685620c3a9d6b5ae091dbabab4f98b20049b7ecc7976e19cc9016c0d5d6", size = 821177, upload-time = "2025-11-26T02:33:35.839Z" }, + { url = "https://files.pythonhosted.org/packages/b3/78/9e004ea9f3aa7466f5ddb6f9518780e1d2f0ed3ca55f093632982598bace/fastar-0.8.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f77c2f2cad76e9dc7b6701297adb1eba87d0485944b416fc2ccf5516c01219a3", size = 820652, upload-time = "2025-11-26T02:34:09.776Z" }, + { url = "https://files.pythonhosted.org/packages/42/95/b604ed536544005c9f1aee7c4c74b00150db3d8d535cd8232dc20f947063/fastar-0.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e7f07c4a3dada7757a8fc430a5b4a29e6ef696d2212747213f57086ffd970316", size = 985961, upload-time = "2025-11-26T02:34:56.401Z" }, + { url = "https://files.pythonhosted.org/packages/f2/7b/fa9d4d96a5d494bdb8699363bb9de8178c0c21a02e1d89cd6f913d127018/fastar-0.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:90c0c3fe55105c0aed8a83135dbdeb31e683455dbd326a1c48fa44c378b85616", size = 1039316, upload-time = "2025-11-26T02:35:13.807Z" }, + { url = "https://files.pythonhosted.org/packages/4e/f9/8462789243bc3f33e8401378ec6d54de4e20cfa60c96a0e15e3e9d1389bb/fastar-0.8.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:fb9ee51e5bffe0dab3d3126d3a4fac8d8f7235cedcb4b8e74936087ce1c157f3", size = 1045028, upload-time = "2025-11-26T02:35:31.079Z" }, + { url = "https://files.pythonhosted.org/packages/a5/71/9abb128777e616127194b509e98fcda3db797d76288c1a8c23dd22afc14f/fastar-0.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e380b1e8d30317f52406c43b11e98d11e1d68723bbd031e18049ea3497b59a6d", size = 994677, upload-time = "2025-11-26T02:35:49.391Z" }, + { url = "https://files.pythonhosted.org/packages/de/c1/b81b3f194853d7ad232a67a1d768f5f51a016f165cfb56cb31b31bbc6177/fastar-0.8.0-cp314-cp314-win32.whl", hash = "sha256:1c4ffc06e9c4a8ca498c07e094670d8d8c0d25b17ca6465b9774da44ea997ab1", size = 456687, upload-time = "2025-11-26T02:36:30.205Z" }, + { url = "https://files.pythonhosted.org/packages/cb/87/9e0cd4768a98181d56f0cdbab2363404cc15deb93f4aad3b99cd2761bbaa/fastar-0.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:5517a8ad4726267c57a3e0e2a44430b782e00b230bf51c55b5728e758bb3a692", size = 490578, upload-time = "2025-11-26T02:36:16.218Z" }, + { url = "https://files.pythonhosted.org/packages/aa/1e/580a76cf91847654f2ad6520e956e93218f778540975bc4190d363f709e2/fastar-0.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:58030551046ff4a8616931e52a36c83545ff05996db5beb6e0cd2b7e748aa309", size = 461473, upload-time = "2025-11-26T02:36:06.373Z" }, + { url = "https://files.pythonhosted.org/packages/58/4c/bdb5c6efe934f68708529c8c9d4055ebef5c4be370621966438f658b29bd/fastar-0.8.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:1e7d29b6bfecb29db126a08baf3c04a5ab667f6cea2b7067d3e623a67729c4a6", size = 705570, upload-time = "2025-11-26T02:34:42.01Z" }, + { url = "https://files.pythonhosted.org/packages/6d/78/f01ac7e71d5a37621bd13598a26e948a12b85ca8042f7ee1a0a8c9f59cda/fastar-0.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:05eb7b96940f9526b485f1d0b02393839f0f61cac4b1f60024984f8b326d2640", size = 627761, upload-time = "2025-11-26T02:34:26.152Z" }, + { url = "https://files.pythonhosted.org/packages/06/45/6df0ecda86ea9d2e95053c1a655d153dee55fc121b6e13ea6d1e246a50b6/fastar-0.8.0-cp314-cp314t-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:619352d8ac011794e2345c462189dc02ba634750d23cd9d86a9267dd71b1f278", size = 869414, upload-time = "2025-11-26T02:33:55.618Z" }, + { url = "https://files.pythonhosted.org/packages/b2/72/486421f5a8c0c377cc82e7a50c8a8ea899a6ec2aa72bde8f09fb667a2dc8/fastar-0.8.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74ebfecef3fe6d7a90355fac1402fd30636988332a1d33f3e80019a10782bb24", size = 763863, upload-time = "2025-11-26T02:32:51.051Z" }, + { url = "https://files.pythonhosted.org/packages/d4/64/39f654dbb41a3867fb1f2c8081c014d8f1d32ea10585d84cacbef0b32995/fastar-0.8.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2975aca5a639e26a3ab0d23b4b0628d6dd6d521146c3c11486d782be621a35aa", size = 763065, upload-time = "2025-11-26T02:33:07.274Z" }, + { url = "https://files.pythonhosted.org/packages/4e/bd/c011a34fb3534c4c3301f7c87c4ffd7e47f6113c904c092ddc8a59a303ea/fastar-0.8.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afc438eaed8ff0dcdd9308268be5cb38c1db7e94c3ccca7c498ca13a4a4535a3", size = 930530, upload-time = "2025-11-26T02:33:23.117Z" }, + { url = "https://files.pythonhosted.org/packages/55/9d/aa6e887a7033c571b1064429222bbe09adc9a3c1e04f3d1788ba5838ebd5/fastar-0.8.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6ced0a5399cc0a84a858ef0a31ca2d0c24d3bbec4bcda506a9192d8119f3590a", size = 820572, upload-time = "2025-11-26T02:33:37.542Z" }, + { url = "https://files.pythonhosted.org/packages/ad/9c/7a3a2278a1052e1a5d98646de7c095a00cffd2492b3b84ce730e2f1cd93a/fastar-0.8.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec9b23da8c4c039da3fe2e358973c66976a0c8508aa06d6626b4403cb5666c19", size = 820649, upload-time = "2025-11-26T02:34:11.108Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/d38edc1f4438cd047e56137c26d94783ffade42e1b3bde620ccf17b771ef/fastar-0.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:dfba078fcd53478032fd0ceed56960ec6b7ff0511cfc013a8a3a4307e3a7bac4", size = 985653, upload-time = "2025-11-26T02:34:57.884Z" }, + { url = "https://files.pythonhosted.org/packages/69/d9/2147d0c19757e165cd62d41cec3f7b38fad2ad68ab784978b5f81716c7ea/fastar-0.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:ade56c94c14be356d295fecb47a3fcd473dd43a8803ead2e2b5b9e58feb6dcfa", size = 1038140, upload-time = "2025-11-26T02:35:15.778Z" }, + { url = "https://files.pythonhosted.org/packages/7f/1d/ec4c717ffb8a308871e9602ec3197d957e238dc0227127ac573ec9bca952/fastar-0.8.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:e48d938f9366db5e59441728f70b7f6c1ccfab7eff84f96f9b7e689b07786c52", size = 1045195, upload-time = "2025-11-26T02:35:32.865Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9f/637334dc8c8f3bb391388b064ae13f0ad9402bc5a6c3e77b8887d0c31921/fastar-0.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:79c441dc1482ff51a54fb3f57ae6f7bb3d2cff88fa2cc5d196c519f8aab64a56", size = 994686, upload-time = "2025-11-26T02:35:51.392Z" }, + { url = "https://files.pythonhosted.org/packages/c9/e2/dfa19a4b260b8ab3581b7484dcb80c09b25324f4daa6b6ae1c7640d1607a/fastar-0.8.0-cp314-cp314t-win32.whl", hash = "sha256:187f61dc739afe45ac8e47ed7fd1adc45d52eac110cf27d579155720507d6fbe", size = 455767, upload-time = "2025-11-26T02:36:34.758Z" }, + { url = "https://files.pythonhosted.org/packages/51/47/df65c72afc1297797b255f90c4778b5d6f1f0f80282a134d5ab610310ed9/fastar-0.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:40e9d763cf8bf85ce2fa256e010aa795c0fe3d3bd1326d5c3084e6ce7857127e", size = 489971, upload-time = "2025-11-26T02:36:22.081Z" }, + { url = "https://files.pythonhosted.org/packages/85/11/0aa8455af26f0ae89e42be67f3a874255ee5d7f0f026fc86e8d56f76b428/fastar-0.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:e59673307b6a08210987059a2bdea2614fe26e3335d0e5d1a3d95f49a05b1418", size = 460467, upload-time = "2025-11-26T02:36:07.978Z" }, +] + +[[package]] +name = "filelock" +version = "3.25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/77/18/a1fd2231c679dcb9726204645721b12498aeac28e1ad0601038f94b42556/filelock-3.25.0.tar.gz", hash = "sha256:8f00faf3abf9dc730a1ffe9c354ae5c04e079ab7d3a683b7c32da5dd05f26af3", size = 40158, upload-time = "2026-03-01T15:08:45.916Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/0b/de6f54d4a8bedfe8645c41497f3c18d749f0bd3218170c667bf4b81d0cdd/filelock-3.25.0-py3-none-any.whl", hash = "sha256:5ccf8069f7948f494968fc0713c10e5c182a9c9d9eef3a636307a20c2490f047", size = 26427, upload-time = "2026-03-01T15:08:44.593Z" }, +] + +[[package]] +name = "fsspec" +version = "2026.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/51/7c/f60c259dcbf4f0c47cc4ddb8f7720d2dcdc8888c8e5ad84c73ea4531cc5b/fsspec-2026.2.0.tar.gz", hash = "sha256:6544e34b16869f5aacd5b90bdf1a71acb37792ea3ddf6125ee69a22a53fb8bff", size = 313441, upload-time = "2026-02-05T21:50:53.743Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl", hash = "sha256:98de475b5cb3bd66bedd5c4679e87b4fdfe1a3bf4d707b151b3c07e58c9a2437", size = 202505, upload-time = "2026-02-05T21:50:51.819Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "hf-xet" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8b/cb/9bb543bd987ffa1ee48202cc96a756951b734b79a542335c566148ade36c/hf_xet-1.3.2.tar.gz", hash = "sha256:e130ee08984783d12717444e538587fa2119385e5bd8fc2bb9f930419b73a7af", size = 643646, upload-time = "2026-02-27T17:26:08.051Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/75/9d54c1ae1d05fb704f977eca1671747babf1957f19f38ae75c5933bc2dc1/hf_xet-1.3.2-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:c34e2c7aefad15792d57067c1c89b2b02c1bbaeabd7f8456ae3d07b4bbaf4094", size = 3761076, upload-time = "2026-02-27T17:25:55.42Z" }, + { url = "https://files.pythonhosted.org/packages/f2/8a/08a24b6c6f52b5d26848c16e4b6d790bb810d1bf62c3505bed179f7032d3/hf_xet-1.3.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:4bc995d6c41992831f762096020dc14a65fdf3963f86ffed580b596d04de32e3", size = 3521745, upload-time = "2026-02-27T17:25:54.217Z" }, + { url = "https://files.pythonhosted.org/packages/b5/db/a75cf400dd8a1a8acf226a12955ff6ee999f272dfc0505bafd8079a61267/hf_xet-1.3.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:959083c89dee30f7d6f890b36cdadda823386c4de63b1a30384a75bfd2ae995d", size = 4176301, upload-time = "2026-02-27T17:25:46.044Z" }, + { url = "https://files.pythonhosted.org/packages/01/40/6c4c798ffdd83e740dd3925c4e47793b07442a9efa3bc3866ba141a82365/hf_xet-1.3.2-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:cfa760888633b08c01b398d212ce7e8c0d7adac6c86e4b20dfb2397d8acd78ee", size = 3955437, upload-time = "2026-02-27T17:25:44.703Z" }, + { url = "https://files.pythonhosted.org/packages/0c/09/9a3aa7c5f07d3e5cc57bb750d12a124ffa72c273a87164bd848f9ac5cc14/hf_xet-1.3.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3155a02e083aa21fd733a7485c7c36025e49d5975c8d6bda0453d224dd0b0ac4", size = 4154535, upload-time = "2026-02-27T17:26:05.207Z" }, + { url = "https://files.pythonhosted.org/packages/ae/e0/831f7fa6d90cb47a230bc23284b502c700e1483bbe459437b3844cdc0776/hf_xet-1.3.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:91b1dc03c31cbf733d35dc03df7c5353686233d86af045e716f1e0ea4a2673cf", size = 4393891, upload-time = "2026-02-27T17:26:06.607Z" }, + { url = "https://files.pythonhosted.org/packages/ab/96/6ed472fdce7f8b70f5da6e3f05be76816a610063003bfd6d9cea0bbb58a3/hf_xet-1.3.2-cp314-cp314t-win_amd64.whl", hash = "sha256:211f30098512d95e85ad03ae63bd7dd2c4df476558a5095d09f9e38e78cbf674", size = 3637583, upload-time = "2026-02-27T17:26:17.349Z" }, + { url = "https://files.pythonhosted.org/packages/8b/e8/a069edc4570b3f8e123c0b80fadc94530f3d7b01394e1fc1bb223339366c/hf_xet-1.3.2-cp314-cp314t-win_arm64.whl", hash = "sha256:4a6817c41de7c48ed9270da0b02849347e089c5ece9a0e72ae4f4b3a57617f82", size = 3497977, upload-time = "2026-02-27T17:26:14.966Z" }, + { url = "https://files.pythonhosted.org/packages/d8/28/dbb024e2e3907f6f3052847ca7d1a2f7a3972fafcd53ff79018977fcb3e4/hf_xet-1.3.2-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:f93b7595f1d8fefddfede775c18b5c9256757824f7f6832930b49858483cd56f", size = 3763961, upload-time = "2026-02-27T17:25:52.537Z" }, + { url = "https://files.pythonhosted.org/packages/e4/71/b99aed3823c9d1795e4865cf437d651097356a3f38c7d5877e4ac544b8e4/hf_xet-1.3.2-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:a85d3d43743174393afe27835bde0cd146e652b5fcfdbcd624602daef2ef3259", size = 3526171, upload-time = "2026-02-27T17:25:50.968Z" }, + { url = "https://files.pythonhosted.org/packages/9d/ca/907890ce6ef5598b5920514f255ed0a65f558f820515b18db75a51b2f878/hf_xet-1.3.2-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7c2a054a97c44e136b1f7f5a78f12b3efffdf2eed3abc6746fc5ea4b39511633", size = 4180750, upload-time = "2026-02-27T17:25:43.125Z" }, + { url = "https://files.pythonhosted.org/packages/8c/ad/bc7f41f87173d51d0bce497b171c4ee0cbde1eed2d7b4216db5d0ada9f50/hf_xet-1.3.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:06b724a361f670ae557836e57801b82c75b534812e351a87a2c739f77d1e0635", size = 3961035, upload-time = "2026-02-27T17:25:41.837Z" }, + { url = "https://files.pythonhosted.org/packages/73/38/600f4dda40c4a33133404d9fe644f1d35ff2d9babb4d0435c646c63dd107/hf_xet-1.3.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:305f5489d7241a47e0458ef49334be02411d1d0f480846363c1c8084ed9916f7", size = 4161378, upload-time = "2026-02-27T17:26:00.365Z" }, + { url = "https://files.pythonhosted.org/packages/00/b3/7bc1ff91d1ac18420b7ad1e169b618b27c00001b96310a89f8a9294fe509/hf_xet-1.3.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:06cdbde243c85f39a63b28e9034321399c507bcd5e7befdd17ed2ccc06dfe14e", size = 4398020, upload-time = "2026-02-27T17:26:03.977Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0b/99bfd948a3ed3620ab709276df3ad3710dcea61976918cce8706502927af/hf_xet-1.3.2-cp37-abi3-win_amd64.whl", hash = "sha256:9298b47cce6037b7045ae41482e703c471ce36b52e73e49f71226d2e8e5685a1", size = 3641624, upload-time = "2026-02-27T17:26:13.542Z" }, + { url = "https://files.pythonhosted.org/packages/cc/02/9a6e4ca1f3f73a164c0cd48e41b3cc56585dcc37e809250de443d673266f/hf_xet-1.3.2-cp37-abi3-win_arm64.whl", hash = "sha256:83d8ec273136171431833a6957e8f3af496bee227a0fe47c7b8b39c106d1749a", size = 3503976, upload-time = "2026-02-27T17:26:12.123Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httptools" +version = "0.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", hash = "sha256:abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9", size = 258961, upload-time = "2025-10-10T03:55:08.559Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/34/50/9d095fcbb6de2d523e027a2f304d4551855c2f46e0b82befd718b8b20056/httptools-0.7.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c08fe65728b8d70b6923ce31e3956f859d5e1e8548e6f22ec520a962c6757270", size = 203619, upload-time = "2025-10-10T03:54:54.321Z" }, + { url = "https://files.pythonhosted.org/packages/07/f0/89720dc5139ae54b03f861b5e2c55a37dba9a5da7d51e1e824a1f343627f/httptools-0.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7aea2e3c3953521c3c51106ee11487a910d45586e351202474d45472db7d72d3", size = 108714, upload-time = "2025-10-10T03:54:55.163Z" }, + { url = "https://files.pythonhosted.org/packages/b3/cb/eea88506f191fb552c11787c23f9a405f4c7b0c5799bf73f2249cd4f5228/httptools-0.7.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0e68b8582f4ea9166be62926077a3334064d422cf08ab87d8b74664f8e9058e1", size = 472909, upload-time = "2025-10-10T03:54:56.056Z" }, + { url = "https://files.pythonhosted.org/packages/e0/4a/a548bdfae6369c0d078bab5769f7b66f17f1bfaa6fa28f81d6be6959066b/httptools-0.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df091cf961a3be783d6aebae963cc9b71e00d57fa6f149025075217bc6a55a7b", size = 470831, upload-time = "2025-10-10T03:54:57.219Z" }, + { url = "https://files.pythonhosted.org/packages/4d/31/14df99e1c43bd132eec921c2e7e11cda7852f65619bc0fc5bdc2d0cb126c/httptools-0.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f084813239e1eb403ddacd06a30de3d3e09a9b76e7894dcda2b22f8a726e9c60", size = 452631, upload-time = "2025-10-10T03:54:58.219Z" }, + { url = "https://files.pythonhosted.org/packages/22/d2/b7e131f7be8d854d48cb6d048113c30f9a46dca0c9a8b08fcb3fcd588cdc/httptools-0.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7347714368fb2b335e9063bc2b96f2f87a9ceffcd9758ac295f8bbcd3ffbc0ca", size = 452910, upload-time = "2025-10-10T03:54:59.366Z" }, + { url = "https://files.pythonhosted.org/packages/53/cf/878f3b91e4e6e011eff6d1fa9ca39f7eb17d19c9d7971b04873734112f30/httptools-0.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:cfabda2a5bb85aa2a904ce06d974a3f30fb36cc63d7feaddec05d2050acede96", size = 88205, upload-time = "2025-10-10T03:55:00.389Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "huggingface-hub" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "hf-xet", marker = "platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, + { name = "httpx" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "tqdm" }, + { name = "typer" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d5/7a/304cec37112382c4fe29a43bcb0d5891f922785d18745883d2aa4eb74e4b/huggingface_hub-1.6.0.tar.gz", hash = "sha256:d931ddad8ba8dfc1e816bf254810eb6f38e5c32f60d4184b5885662a3b167325", size = 717071, upload-time = "2026-03-06T14:19:18.524Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/e3/e3a44f54c8e2f28983fcf07f13d4260b37bd6a0d3a081041bc60b91d230e/huggingface_hub-1.6.0-py3-none-any.whl", hash = "sha256:ef40e2d5cb85e48b2c067020fa5142168342d5108a1b267478ed384ecbf18961", size = 612874, upload-time = "2026-03-06T14:19:16.844Z" }, +] + +[[package]] +name = "idna" +version = "3.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + +[[package]] +name = "mpmath" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, +] + +[[package]] +name = "networkx" +version = "3.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025, upload-time = "2025-12-08T17:02:39.908Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" }, +] + +[[package]] +name = "numpy" +version = "2.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/fd/0005efbd0af48e55eb3c7208af93f2862d4b1a56cd78e84309a2d959208d/numpy-2.4.2.tar.gz", hash = "sha256:659a6107e31a83c4e33f763942275fd278b21d095094044eb35569e86a21ddae", size = 20723651, upload-time = "2026-01-31T23:13:10.135Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/88/b7df6050bf18fdcfb7046286c6535cabbdd2064a3440fca3f069d319c16e/numpy-2.4.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:444be170853f1f9d528428eceb55f12918e4fda5d8805480f36a002f1415e09b", size = 16663092, upload-time = "2026-01-31T23:12:04.521Z" }, + { url = "https://files.pythonhosted.org/packages/25/7a/1fee4329abc705a469a4afe6e69b1ef7e915117747886327104a8493a955/numpy-2.4.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d1240d50adff70c2a88217698ca844723068533f3f5c5fa6ee2e3220e3bdb000", size = 14698770, upload-time = "2026-01-31T23:12:06.96Z" }, + { url = "https://files.pythonhosted.org/packages/fb/0b/f9e49ba6c923678ad5bc38181c08ac5e53b7a5754dbca8e581aa1a56b1ff/numpy-2.4.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:7cdde6de52fb6664b00b056341265441192d1291c130e99183ec0d4b110ff8b1", size = 5208562, upload-time = "2026-01-31T23:12:09.632Z" }, + { url = "https://files.pythonhosted.org/packages/7d/12/d7de8f6f53f9bb76997e5e4c069eda2051e3fe134e9181671c4391677bb2/numpy-2.4.2-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:cda077c2e5b780200b6b3e09d0b42205a3d1c68f30c6dceb90401c13bff8fe74", size = 6543710, upload-time = "2026-01-31T23:12:11.969Z" }, + { url = "https://files.pythonhosted.org/packages/09/63/c66418c2e0268a31a4cf8a8b512685748200f8e8e8ec6c507ce14e773529/numpy-2.4.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d30291931c915b2ab5717c2974bb95ee891a1cf22ebc16a8006bd59cd210d40a", size = 15677205, upload-time = "2026-01-31T23:12:14.33Z" }, + { url = "https://files.pythonhosted.org/packages/5d/6c/7f237821c9642fb2a04d2f1e88b4295677144ca93285fd76eff3bcba858d/numpy-2.4.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bba37bc29d4d85761deed3954a1bc62be7cf462b9510b51d367b769a8c8df325", size = 16611738, upload-time = "2026-01-31T23:12:16.525Z" }, + { url = "https://files.pythonhosted.org/packages/c2/a7/39c4cdda9f019b609b5c473899d87abff092fc908cfe4d1ecb2fcff453b0/numpy-2.4.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b2f0073ed0868db1dcd86e052d37279eef185b9c8db5bf61f30f46adac63c909", size = 17028888, upload-time = "2026-01-31T23:12:19.306Z" }, + { url = "https://files.pythonhosted.org/packages/da/b3/e84bb64bdfea967cc10950d71090ec2d84b49bc691df0025dddb7c26e8e3/numpy-2.4.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7f54844851cdb630ceb623dcec4db3240d1ac13d4990532446761baede94996a", size = 18339556, upload-time = "2026-01-31T23:12:21.816Z" }, + { url = "https://files.pythonhosted.org/packages/88/f5/954a291bc1192a27081706862ac62bb5920fbecfbaa302f64682aa90beed/numpy-2.4.2-cp314-cp314-win32.whl", hash = "sha256:12e26134a0331d8dbd9351620f037ec470b7c75929cb8a1537f6bfe411152a1a", size = 6006899, upload-time = "2026-01-31T23:12:24.14Z" }, + { url = "https://files.pythonhosted.org/packages/05/cb/eff72a91b2efdd1bc98b3b8759f6a1654aa87612fc86e3d87d6fe4f948c4/numpy-2.4.2-cp314-cp314-win_amd64.whl", hash = "sha256:068cdb2d0d644cdb45670810894f6a0600797a69c05f1ac478e8d31670b8ee75", size = 12443072, upload-time = "2026-01-31T23:12:26.33Z" }, + { url = "https://files.pythonhosted.org/packages/37/75/62726948db36a56428fce4ba80a115716dc4fad6a3a4352487f8bb950966/numpy-2.4.2-cp314-cp314-win_arm64.whl", hash = "sha256:6ed0be1ee58eef41231a5c943d7d1375f093142702d5723ca2eb07db9b934b05", size = 10494886, upload-time = "2026-01-31T23:12:28.488Z" }, + { url = "https://files.pythonhosted.org/packages/36/2f/ee93744f1e0661dc267e4b21940870cabfae187c092e1433b77b09b50ac4/numpy-2.4.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:98f16a80e917003a12c0580f97b5f875853ebc33e2eaa4bccfc8201ac6869308", size = 14818567, upload-time = "2026-01-31T23:12:30.709Z" }, + { url = "https://files.pythonhosted.org/packages/a7/24/6535212add7d76ff938d8bdc654f53f88d35cddedf807a599e180dcb8e66/numpy-2.4.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:20abd069b9cda45874498b245c8015b18ace6de8546bf50dfa8cea1696ed06ef", size = 5328372, upload-time = "2026-01-31T23:12:32.962Z" }, + { url = "https://files.pythonhosted.org/packages/5e/9d/c48f0a035725f925634bf6b8994253b43f2047f6778a54147d7e213bc5a7/numpy-2.4.2-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:e98c97502435b53741540a5717a6749ac2ada901056c7db951d33e11c885cc7d", size = 6649306, upload-time = "2026-01-31T23:12:34.797Z" }, + { url = "https://files.pythonhosted.org/packages/81/05/7c73a9574cd4a53a25907bad38b59ac83919c0ddc8234ec157f344d57d9a/numpy-2.4.2-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:da6cad4e82cb893db4b69105c604d805e0c3ce11501a55b5e9f9083b47d2ffe8", size = 15722394, upload-time = "2026-01-31T23:12:36.565Z" }, + { url = "https://files.pythonhosted.org/packages/35/fa/4de10089f21fc7d18442c4a767ab156b25c2a6eaf187c0db6d9ecdaeb43f/numpy-2.4.2-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e4424677ce4b47fe73c8b5556d876571f7c6945d264201180db2dc34f676ab5", size = 16653343, upload-time = "2026-01-31T23:12:39.188Z" }, + { url = "https://files.pythonhosted.org/packages/b8/f9/d33e4ffc857f3763a57aa85650f2e82486832d7492280ac21ba9efda80da/numpy-2.4.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2b8f157c8a6f20eb657e240f8985cc135598b2b46985c5bccbde7616dc9c6b1e", size = 17078045, upload-time = "2026-01-31T23:12:42.041Z" }, + { url = "https://files.pythonhosted.org/packages/c8/b8/54bdb43b6225badbea6389fa038c4ef868c44f5890f95dd530a218706da3/numpy-2.4.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5daf6f3914a733336dab21a05cdec343144600e964d2fcdabaac0c0269874b2a", size = 18380024, upload-time = "2026-01-31T23:12:44.331Z" }, + { url = "https://files.pythonhosted.org/packages/a5/55/6e1a61ded7af8df04016d81b5b02daa59f2ea9252ee0397cb9f631efe9e5/numpy-2.4.2-cp314-cp314t-win32.whl", hash = "sha256:8c50dd1fc8826f5b26a5ee4d77ca55d88a895f4e4819c7ecc2a9f5905047a443", size = 6153937, upload-time = "2026-01-31T23:12:47.229Z" }, + { url = "https://files.pythonhosted.org/packages/45/aa/fa6118d1ed6d776b0983f3ceac9b1a5558e80df9365b1c3aa6d42bf9eee4/numpy-2.4.2-cp314-cp314t-win_amd64.whl", hash = "sha256:fcf92bee92742edd401ba41135185866f7026c502617f422eb432cfeca4fe236", size = 12631844, upload-time = "2026-01-31T23:12:48.997Z" }, + { url = "https://files.pythonhosted.org/packages/32/0a/2ec5deea6dcd158f254a7b372fb09cfba5719419c8d66343bab35237b3fb/numpy-2.4.2-cp314-cp314t-win_arm64.whl", hash = "sha256:1f92f53998a17265194018d1cc321b2e96e900ca52d54c7c77837b71b9465181", size = 10565379, upload-time = "2026-01-31T23:12:51.345Z" }, +] + +[[package]] +name = "nvidia-cublas-cu12" +version = "12.8.4.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921, upload-time = "2025-03-07T01:44:31.254Z" }, +] + +[[package]] +name = "nvidia-cuda-cupti-cu12" +version = "12.8.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621, upload-time = "2025-03-07T01:40:21.213Z" }, +] + +[[package]] +name = "nvidia-cuda-nvrtc-cu12" +version = "12.8.93" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029, upload-time = "2025-03-07T01:42:13.562Z" }, +] + +[[package]] +name = "nvidia-cuda-runtime-cu12" +version = "12.8.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765, upload-time = "2025-03-07T01:40:01.615Z" }, +] + +[[package]] +name = "nvidia-cudnn-cu12" +version = "9.10.2.21" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" }, +] + +[[package]] +name = "nvidia-cufft-cu12" +version = "11.3.3.83" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" }, +] + +[[package]] +name = "nvidia-cufile-cu12" +version = "1.13.1.3" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834, upload-time = "2025-03-07T01:45:50.723Z" }, +] + +[[package]] +name = "nvidia-curand-cu12" +version = "10.3.9.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976, upload-time = "2025-03-07T01:46:23.323Z" }, +] + +[[package]] +name = "nvidia-cusolver-cu12" +version = "11.7.3.90" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12" }, + { name = "nvidia-cusparse-cu12" }, + { name = "nvidia-nvjitlink-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" }, +] + +[[package]] +name = "nvidia-cusparse-cu12" +version = "12.5.8.93" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" }, +] + +[[package]] +name = "nvidia-cusparselt-cu12" +version = "0.7.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691, upload-time = "2025-02-26T00:15:44.104Z" }, +] + +[[package]] +name = "nvidia-nccl-cu12" +version = "2.27.5" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229, upload-time = "2025-06-26T04:11:28.385Z" }, +] + +[[package]] +name = "nvidia-nvjitlink-cu12" +version = "12.8.93" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836, upload-time = "2025-03-07T01:49:55.661Z" }, +] + +[[package]] +name = "nvidia-nvshmem-cu12" +version = "3.4.5" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/09/6ea3ea725f82e1e76684f0708bbedd871fc96da89945adeba65c3835a64c/nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:042f2500f24c021db8a06c5eec2539027d57460e1c1a762055a6554f72c369bd", size = 139103095, upload-time = "2025-09-06T00:32:31.266Z" }, +] + +[[package]] +name = "nvidia-nvtx-cu12" +version = "12.8.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954, upload-time = "2025-03-07T01:42:44.131Z" }, +] + +[[package]] +name = "packaging" +version = "26.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, +] + +[[package]] +name = "peft" +version = "0.18.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "accelerate" }, + { name = "huggingface-hub" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pyyaml" }, + { name = "safetensors" }, + { name = "torch" }, + { name = "tqdm" }, + { name = "transformers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d8/48/147b3ea999560b40a34fd78724c7777aa9d18409c2250bdcaf9c4f2db7fc/peft-0.18.1.tar.gz", hash = "sha256:2dd0d6bfce936d1850e48aaddbd250941c5c02fc8ef3237cd8fd5aac35e0bae2", size = 635030, upload-time = "2026-01-09T13:08:01.136Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/14/b4e3f574acf349ae6f61f9c000a77f97a3b315b4bb6ad03791e79ae4a568/peft-0.18.1-py3-none-any.whl", hash = "sha256:0bf06847a3551e3019fc58c440cffc9a6b73e6e2962c95b52e224f77bbdb50f1", size = 556960, upload-time = "2026-01-09T13:07:55.865Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.9.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/19/56/8d4c30c8a1d07013911a8fdbd8f89440ef9f08d07a1b50ab8ca8be5a20f9/platformdirs-4.9.4.tar.gz", hash = "sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934", size = 28737, upload-time = "2026-03-05T18:34:13.271Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl", hash = "sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868", size = 21216, upload-time = "2026-03-05T18:34:12.172Z" }, +] + +[[package]] +name = "protobuf" +version = "7.34.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/00/04a2ab36b70a52d0356852979e08b44edde0435f2115dc66e25f2100f3ab/protobuf-7.34.0.tar.gz", hash = "sha256:3871a3df67c710aaf7bb8d214cc997342e63ceebd940c8c7fc65c9b3d697591a", size = 454726, upload-time = "2026-02-27T00:30:25.421Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/c4/6322ab5c8f279c4c358bc14eb8aefc0550b97222a39f04eb3c1af7a830fa/protobuf-7.34.0-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:8e329966799f2c271d5e05e236459fe1cbfdb8755aaa3b0914fa60947ddea408", size = 429248, upload-time = "2026-02-27T00:30:14.924Z" }, + { url = "https://files.pythonhosted.org/packages/45/99/b029bbbc61e8937545da5b79aa405ab2d9cf307a728f8c9459ad60d7a481/protobuf-7.34.0-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:9d7a5005fb96f3c1e64f397f91500b0eb371b28da81296ae73a6b08a5b76cdd6", size = 325753, upload-time = "2026-02-27T00:30:17.247Z" }, + { url = "https://files.pythonhosted.org/packages/cc/79/09f02671eb75b251c5550a1c48e7b3d4b0623efd7c95a15a50f6f9fc1e2e/protobuf-7.34.0-cp310-abi3-manylinux2014_s390x.whl", hash = "sha256:4a72a8ec94e7a9f7ef7fe818ed26d073305f347f8b3b5ba31e22f81fd85fca02", size = 340200, upload-time = "2026-02-27T00:30:18.672Z" }, + { url = "https://files.pythonhosted.org/packages/b5/57/89727baef7578897af5ed166735ceb315819f1c184da8c3441271dbcfde7/protobuf-7.34.0-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:964cf977e07f479c0697964e83deda72bcbc75c3badab506fb061b352d991b01", size = 324268, upload-time = "2026-02-27T00:30:20.088Z" }, + { url = "https://files.pythonhosted.org/packages/1f/3e/38ff2ddee5cc946f575c9d8cc822e34bde205cf61acf8099ad88ef19d7d2/protobuf-7.34.0-cp310-abi3-win32.whl", hash = "sha256:f791ec509707a1d91bd02e07df157e75e4fb9fbdad12a81b7396201ec244e2e3", size = 426628, upload-time = "2026-02-27T00:30:21.555Z" }, + { url = "https://files.pythonhosted.org/packages/cb/71/7c32eaf34a61a1bae1b62a2ac4ffe09b8d1bb0cf93ad505f42040023db89/protobuf-7.34.0-cp310-abi3-win_amd64.whl", hash = "sha256:9f9079f1dde4e32342ecbd1c118d76367090d4aaa19da78230c38101c5b3dd40", size = 437901, upload-time = "2026-02-27T00:30:22.836Z" }, + { url = "https://files.pythonhosted.org/packages/a4/e7/14dc9366696dcb53a413449881743426ed289d687bcf3d5aee4726c32ebb/protobuf-7.34.0-py3-none-any.whl", hash = "sha256:e3b914dd77fa33fa06ab2baa97937746ab25695f389869afdf03e81f34e45dc7", size = 170716, upload-time = "2026-02-27T00:30:23.994Z" }, +] + +[[package]] +name = "psutil" +version = "7.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740, upload-time = "2026-01-28T18:14:54.428Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/69/ef179ab5ca24f32acc1dac0c247fd6a13b501fd5534dbae0e05a1c48b66d/psutil-7.2.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00", size = 130664, upload-time = "2026-01-28T18:15:09.469Z" }, + { url = "https://files.pythonhosted.org/packages/7b/64/665248b557a236d3fa9efc378d60d95ef56dd0a490c2cd37dafc7660d4a9/psutil-7.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9", size = 131087, upload-time = "2026-01-28T18:15:11.724Z" }, + { url = "https://files.pythonhosted.org/packages/d5/2e/e6782744700d6759ebce3043dcfa661fb61e2fb752b91cdeae9af12c2178/psutil-7.2.2-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a", size = 182383, upload-time = "2026-01-28T18:15:13.445Z" }, + { url = "https://files.pythonhosted.org/packages/57/49/0a41cefd10cb7505cdc04dab3eacf24c0c2cb158a998b8c7b1d27ee2c1f5/psutil-7.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf", size = 185210, upload-time = "2026-01-28T18:15:16.002Z" }, + { url = "https://files.pythonhosted.org/packages/dd/2c/ff9bfb544f283ba5f83ba725a3c5fec6d6b10b8f27ac1dc641c473dc390d/psutil-7.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1", size = 141228, upload-time = "2026-01-28T18:15:18.385Z" }, + { url = "https://files.pythonhosted.org/packages/f2/fc/f8d9c31db14fcec13748d373e668bc3bed94d9077dbc17fb0eebc073233c/psutil-7.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841", size = 136284, upload-time = "2026-01-28T18:15:19.912Z" }, + { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090, upload-time = "2026-01-28T18:15:22.168Z" }, + { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859, upload-time = "2026-01-28T18:15:23.795Z" }, + { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560, upload-time = "2026-01-28T18:15:25.976Z" }, + { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997, upload-time = "2026-01-28T18:15:27.794Z" }, + { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972, upload-time = "2026-01-28T18:15:29.342Z" }, + { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266, upload-time = "2026-01-28T18:15:31.597Z" }, + { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737, upload-time = "2026-01-28T18:15:33.849Z" }, + { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" }, +] + +[[package]] +name = "pydantic" +version = "2.12.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, +] + +[package.optional-dependencies] +email = [ + { name = "email-validator" }, +] + +[[package]] +name = "pydantic-core" +version = "2.41.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, + { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, + { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, + { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, + { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, + { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, + { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, + { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, + { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, + { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, + { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, + { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, + { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, + { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, + { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, + { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, + { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, + { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, + { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, + { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, + { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, + { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, + { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, + { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, + { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, + { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, +] + +[[package]] +name = "pydantic-extra-types" +version = "2.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fd/35/2fee58b1316a73e025728583d3b1447218a97e621933fc776fb8c0f2ebdd/pydantic_extra_types-2.11.0.tar.gz", hash = "sha256:4e9991959d045b75feb775683437a97991d02c138e00b59176571db9ce634f0e", size = 157226, upload-time = "2025-12-31T16:18:27.944Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/17/fabd56da47096d240dd45ba627bead0333b0cf0ee8ada9bec579287dadf3/pydantic_extra_types-2.11.0-py3-none-any.whl", hash = "sha256:84b864d250a0fc62535b7ec591e36f2c5b4d1325fa0017eb8cda9aeb63b374a6", size = 74296, upload-time = "2025-12-31T16:18:26.38Z" }, +] + +[[package]] +name = "pydantic-settings" +version = "2.13.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/52/6d/fffca34caecc4a3f97bda81b2098da5e8ab7efc9a66e819074a11955d87e/pydantic_settings-2.13.1.tar.gz", hash = "sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025", size = 223826, upload-time = "2026-02-19T13:45:08.055Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/4b/ccc026168948fec4f7555b9164c724cf4125eac006e176541483d2c959be/pydantic_settings-2.13.1-py3-none-any.whl", hash = "sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237", size = 58929, upload-time = "2026-02-19T13:45:06.034Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + +[[package]] +name = "python-dotenv" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, +] + +[[package]] +name = "python-multipart" +version = "0.0.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/01/979e98d542a70714b0cb2b6728ed0b7c46792b695e3eaec3e20711271ca3/python_multipart-0.0.22.tar.gz", hash = "sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58", size = 37612, upload-time = "2026-01-25T10:15:56.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/d0/397f9626e711ff749a95d96b7af99b9c566a9bb5129b8e4c10fc4d100304/python_multipart-0.0.22-py3-none-any.whl", hash = "sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155", size = 24579, upload-time = "2026-01-25T10:15:54.811Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, +] + +[[package]] +name = "regex" +version = "2026.2.28" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8b/71/41455aa99a5a5ac1eaf311f5d8efd9ce6433c03ac1e0962de163350d0d97/regex-2026.2.28.tar.gz", hash = "sha256:a729e47d418ea11d03469f321aaf67cdee8954cde3ff2cf8403ab87951ad10f2", size = 415184, upload-time = "2026-02-28T02:19:42.792Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/03/691015f7a7cb1ed6dacb2ea5de5682e4858e05a4c5506b2839cd533bbcd6/regex-2026.2.28-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:78454178c7df31372ea737996fb7f36b3c2c92cccc641d251e072478afb4babc", size = 489497, upload-time = "2026-02-28T02:18:30.889Z" }, + { url = "https://files.pythonhosted.org/packages/c6/ba/8db8fd19afcbfa0e1036eaa70c05f20ca8405817d4ad7a38a6b4c2f031ac/regex-2026.2.28-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:5d10303dd18cedfd4d095543998404df656088240bcfd3cd20a8f95b861f74bd", size = 291295, upload-time = "2026-02-28T02:18:33.426Z" }, + { url = "https://files.pythonhosted.org/packages/5a/79/9aa0caf089e8defef9b857b52fc53801f62ff868e19e5c83d4a96612eba1/regex-2026.2.28-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:19a9c9e0a8f24f39d575a6a854d516b48ffe4cbdcb9de55cb0570a032556ecff", size = 289275, upload-time = "2026-02-28T02:18:35.247Z" }, + { url = "https://files.pythonhosted.org/packages/eb/26/ee53117066a30ef9c883bf1127eece08308ccf8ccd45c45a966e7a665385/regex-2026.2.28-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09500be324f49b470d907b3ef8af9afe857f5cca486f853853f7945ddbf75911", size = 797176, upload-time = "2026-02-28T02:18:37.15Z" }, + { url = "https://files.pythonhosted.org/packages/05/1b/67fb0495a97259925f343ae78b5d24d4a6624356ae138b57f18bd43006e4/regex-2026.2.28-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fb1c4ff62277d87a7335f2c1ea4e0387b8f2b3ad88a64efd9943906aafad4f33", size = 863813, upload-time = "2026-02-28T02:18:39.478Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/93ac9bbafc53618091c685c7ed40239a90bf9f2a82c983f0baa97cb7ae07/regex-2026.2.28-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b8b3f1be1738feadc69f62daa250c933e85c6f34fa378f54a7ff43807c1b9117", size = 908678, upload-time = "2026-02-28T02:18:41.619Z" }, + { url = "https://files.pythonhosted.org/packages/c7/7a/a8f5e0561702b25239846a16349feece59712ae20598ebb205580332a471/regex-2026.2.28-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc8ed8c3f41c27acb83f7b6a9eb727a73fc6663441890c5cb3426a5f6a91ce7d", size = 801528, upload-time = "2026-02-28T02:18:43.624Z" }, + { url = "https://files.pythonhosted.org/packages/96/5d/ed6d4cbde80309854b1b9f42d9062fee38ade15f7eb4909f6ef2440403b5/regex-2026.2.28-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa539be029844c0ce1114762d2952ab6cfdd7c7c9bd72e0db26b94c3c36dcc5a", size = 775373, upload-time = "2026-02-28T02:18:46.102Z" }, + { url = "https://files.pythonhosted.org/packages/6a/e9/6e53c34e8068b9deec3e87210086ecb5b9efebdefca6b0d3fa43d66dcecb/regex-2026.2.28-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7900157786428a79615a8264dac1f12c9b02957c473c8110c6b1f972dcecaddf", size = 784859, upload-time = "2026-02-28T02:18:48.269Z" }, + { url = "https://files.pythonhosted.org/packages/48/3c/736e1c7ca7f0dcd2ae33819888fdc69058a349b7e5e84bc3e2f296bbf794/regex-2026.2.28-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:0b1d2b07614d95fa2bf8a63fd1e98bd8fa2b4848dc91b1efbc8ba219fdd73952", size = 857813, upload-time = "2026-02-28T02:18:50.576Z" }, + { url = "https://files.pythonhosted.org/packages/6e/7c/48c4659ad9da61f58e79dbe8c05223e0006696b603c16eb6b5cbfbb52c27/regex-2026.2.28-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:b389c61aa28a79c2e0527ac36da579869c2e235a5b208a12c5b5318cda2501d8", size = 763705, upload-time = "2026-02-28T02:18:52.59Z" }, + { url = "https://files.pythonhosted.org/packages/cf/a1/bc1c261789283128165f71b71b4b221dd1b79c77023752a6074c102f18d8/regex-2026.2.28-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f467cb602f03fbd1ab1908f68b53c649ce393fde056628dc8c7e634dab6bfc07", size = 848734, upload-time = "2026-02-28T02:18:54.595Z" }, + { url = "https://files.pythonhosted.org/packages/10/d8/979407faf1397036e25a5ae778157366a911c0f382c62501009f4957cf86/regex-2026.2.28-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e8c8cb2deba42f5ec1ede46374e990f8adc5e6456a57ac1a261b19be6f28e4e6", size = 789871, upload-time = "2026-02-28T02:18:57.34Z" }, + { url = "https://files.pythonhosted.org/packages/03/23/da716821277115fcb1f4e3de1e5dc5023a1e6533598c486abf5448612579/regex-2026.2.28-cp314-cp314-win32.whl", hash = "sha256:9036b400b20e4858d56d117108d7813ed07bb7803e3eed766675862131135ca6", size = 271825, upload-time = "2026-02-28T02:18:59.202Z" }, + { url = "https://files.pythonhosted.org/packages/91/ff/90696f535d978d5f16a52a419be2770a8d8a0e7e0cfecdbfc31313df7fab/regex-2026.2.28-cp314-cp314-win_amd64.whl", hash = "sha256:1d367257cd86c1cbb97ea94e77b373a0bbc2224976e247f173d19e8f18b4afa7", size = 280548, upload-time = "2026-02-28T02:19:01.049Z" }, + { url = "https://files.pythonhosted.org/packages/69/f9/5e1b5652fc0af3fcdf7677e7df3ad2a0d47d669b34ac29a63bb177bb731b/regex-2026.2.28-cp314-cp314-win_arm64.whl", hash = "sha256:5e68192bb3a1d6fb2836da24aa494e413ea65853a21505e142e5b1064a595f3d", size = 273444, upload-time = "2026-02-28T02:19:03.255Z" }, + { url = "https://files.pythonhosted.org/packages/d3/eb/8389f9e940ac89bcf58d185e230a677b4fd07c5f9b917603ad5c0f8fa8fe/regex-2026.2.28-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:a5dac14d0872eeb35260a8e30bac07ddf22adc1e3a0635b52b02e180d17c9c7e", size = 492546, upload-time = "2026-02-28T02:19:05.378Z" }, + { url = "https://files.pythonhosted.org/packages/7b/c7/09441d27ce2a6fa6a61ea3150ea4639c1dcda9b31b2ea07b80d6937b24dd/regex-2026.2.28-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:ec0c608b7a7465ffadb344ed7c987ff2f11ee03f6a130b569aa74d8a70e8333c", size = 292986, upload-time = "2026-02-28T02:19:07.24Z" }, + { url = "https://files.pythonhosted.org/packages/fb/69/4144b60ed7760a6bd235e4087041f487aa4aa62b45618ce018b0c14833ea/regex-2026.2.28-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c7815afb0ca45456613fdaf60ea9c993715511c8d53a83bc468305cbc0ee23c7", size = 291518, upload-time = "2026-02-28T02:19:09.698Z" }, + { url = "https://files.pythonhosted.org/packages/2d/be/77e5426cf5948c82f98c53582009ca9e94938c71f73a8918474f2e2990bb/regex-2026.2.28-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b059e71ec363968671693a78c5053bd9cb2fe410f9b8e4657e88377ebd603a2e", size = 809464, upload-time = "2026-02-28T02:19:12.494Z" }, + { url = "https://files.pythonhosted.org/packages/45/99/2c8c5ac90dc7d05c6e7d8e72c6a3599dc08cd577ac476898e91ca787d7f1/regex-2026.2.28-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b8cf76f1a29f0e99dcfd7aef1551a9827588aae5a737fe31442021165f1920dc", size = 869553, upload-time = "2026-02-28T02:19:15.151Z" }, + { url = "https://files.pythonhosted.org/packages/53/34/daa66a342f0271e7737003abf6c3097aa0498d58c668dbd88362ef94eb5d/regex-2026.2.28-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:180e08a435a0319e6a4821c3468da18dc7001987e1c17ae1335488dfe7518dd8", size = 915289, upload-time = "2026-02-28T02:19:17.331Z" }, + { url = "https://files.pythonhosted.org/packages/c5/c7/e22c2aaf0a12e7e22ab19b004bb78d32ca1ecc7ef245949935463c5567de/regex-2026.2.28-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1e496956106fd59ba6322a8ea17141a27c5040e5ee8f9433ae92d4e5204462a0", size = 812156, upload-time = "2026-02-28T02:19:20.011Z" }, + { url = "https://files.pythonhosted.org/packages/7f/bb/2dc18c1efd9051cf389cd0d7a3a4d90f6804b9fff3a51b5dc3c85b935f71/regex-2026.2.28-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bba2b18d70eeb7b79950f12f633beeecd923f7c9ad6f6bae28e59b4cb3ab046b", size = 782215, upload-time = "2026-02-28T02:19:22.047Z" }, + { url = "https://files.pythonhosted.org/packages/17/1e/9e4ec9b9013931faa32226ec4aa3c71fe664a6d8a2b91ac56442128b332f/regex-2026.2.28-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6db7bfae0f8a2793ff1f7021468ea55e2699d0790eb58ee6ab36ae43aa00bc5b", size = 798925, upload-time = "2026-02-28T02:19:24.173Z" }, + { url = "https://files.pythonhosted.org/packages/71/57/a505927e449a9ccb41e2cc8d735e2abe3444b0213d1cf9cb364a8c1f2524/regex-2026.2.28-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:d0b02e8b7e5874b48ae0f077ecca61c1a6a9f9895e9c6dfb191b55b242862033", size = 864701, upload-time = "2026-02-28T02:19:26.376Z" }, + { url = "https://files.pythonhosted.org/packages/a6/ad/c62cb60cdd93e13eac5b3d9d6bd5d284225ed0e3329426f94d2552dd7cca/regex-2026.2.28-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:25b6eb660c5cf4b8c3407a1ed462abba26a926cc9965e164268a3267bcc06a43", size = 770899, upload-time = "2026-02-28T02:19:29.38Z" }, + { url = "https://files.pythonhosted.org/packages/3c/5a/874f861f5c3d5ab99633e8030dee1bc113db8e0be299d1f4b07f5b5ec349/regex-2026.2.28-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:5a932ea8ad5d0430351ff9c76c8db34db0d9f53c1d78f06022a21f4e290c5c18", size = 854727, upload-time = "2026-02-28T02:19:31.494Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ca/d2c03b0efde47e13db895b975b2be6a73ed90b8ba963677927283d43bf74/regex-2026.2.28-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1c2c95e1a2b0f89d01e821ff4de1be4b5d73d1f4b0bf679fa27c1ad8d2327f1a", size = 800366, upload-time = "2026-02-28T02:19:34.248Z" }, + { url = "https://files.pythonhosted.org/packages/14/bd/ee13b20b763b8989f7c75d592bfd5de37dc1181814a2a2747fedcf97e3ba/regex-2026.2.28-cp314-cp314t-win32.whl", hash = "sha256:bbb882061f742eb5d46f2f1bd5304055be0a66b783576de3d7eef1bed4778a6e", size = 274936, upload-time = "2026-02-28T02:19:36.313Z" }, + { url = "https://files.pythonhosted.org/packages/cb/e7/d8020e39414c93af7f0d8688eabcecece44abfd5ce314b21dfda0eebd3d8/regex-2026.2.28-cp314-cp314t-win_amd64.whl", hash = "sha256:6591f281cb44dc13de9585b552cec6fc6cf47fb2fe7a48892295ee9bc4a612f9", size = 284779, upload-time = "2026-02-28T02:19:38.625Z" }, + { url = "https://files.pythonhosted.org/packages/13/c0/ad225f4a405827486f1955283407cf758b6d2fb966712644c5f5aef33d1b/regex-2026.2.28-cp314-cp314t-win_arm64.whl", hash = "sha256:dee50f1be42222f89767b64b283283ef963189da0dda4a515aa54a5563c62dec", size = 275010, upload-time = "2026-02-28T02:19:40.65Z" }, +] + +[[package]] +name = "requests" +version = "2.32.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, +] + +[[package]] +name = "rich" +version = "14.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b", size = 230582, upload-time = "2026-02-19T17:23:12.474Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458, upload-time = "2026-02-19T17:23:13.732Z" }, +] + +[[package]] +name = "rich-toolkit" +version = "0.19.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "rich" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/ba/dae9e3096651042754da419a4042bc1c75e07d615f9b15066d738838e4df/rich_toolkit-0.19.7.tar.gz", hash = "sha256:133c0915872da91d4c25d85342d5ec1dfacc69b63448af1a08a0d4b4f23ef46e", size = 195877, upload-time = "2026-02-24T16:06:20.555Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/3c/c923619f6d2f5fafcc96fec0aaf9550a46cd5b6481f06e0c6b66a2a4fed0/rich_toolkit-0.19.7-py3-none-any.whl", hash = "sha256:0288e9203728c47c5a4eb60fd2f0692d9df7455a65901ab6f898437a2ba5989d", size = 32963, upload-time = "2026-02-24T16:06:22.066Z" }, +] + +[[package]] +name = "rignore" +version = "0.7.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/f5/8bed2310abe4ae04b67a38374a4d311dd85220f5d8da56f47ae9361be0b0/rignore-0.7.6.tar.gz", hash = "sha256:00d3546cd793c30cb17921ce674d2c8f3a4b00501cb0e3dd0e82217dbeba2671", size = 57140, upload-time = "2025-11-05T21:41:21.968Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/b9/1f5bd82b87e5550cd843ceb3768b4a8ef274eb63f29333cf2f29644b3d75/rignore-0.7.6-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:8e41be9fa8f2f47239ded8920cc283699a052ac4c371f77f5ac017ebeed75732", size = 882632, upload-time = "2025-11-05T20:42:44.063Z" }, + { url = "https://files.pythonhosted.org/packages/e9/6b/07714a3efe4a8048864e8a5b7db311ba51b921e15268b17defaebf56d3db/rignore-0.7.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6dc1e171e52cefa6c20e60c05394a71165663b48bca6c7666dee4f778f2a7d90", size = 820760, upload-time = "2025-11-05T20:42:27.885Z" }, + { url = "https://files.pythonhosted.org/packages/ac/0f/348c829ea2d8d596e856371b14b9092f8a5dfbb62674ec9b3f67e4939a9d/rignore-0.7.6-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ce2268837c3600f82ab8db58f5834009dc638ee17103582960da668963bebc5", size = 899044, upload-time = "2025-11-05T20:40:55.336Z" }, + { url = "https://files.pythonhosted.org/packages/f0/30/2e1841a19b4dd23878d73edd5d82e998a83d5ed9570a89675f140ca8b2ad/rignore-0.7.6-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:690a3e1b54bfe77e89c4bacb13f046e642f8baadafc61d68f5a726f324a76ab6", size = 874144, upload-time = "2025-11-05T20:41:10.195Z" }, + { url = "https://files.pythonhosted.org/packages/c2/bf/0ce9beb2e5f64c30e3580bef09f5829236889f01511a125f98b83169b993/rignore-0.7.6-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09d12ac7a0b6210c07bcd145007117ebd8abe99c8eeb383e9e4673910c2754b2", size = 1168062, upload-time = "2025-11-05T20:41:26.511Z" }, + { url = "https://files.pythonhosted.org/packages/b9/8b/571c178414eb4014969865317da8a02ce4cf5241a41676ef91a59aab24de/rignore-0.7.6-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a2b2b74a8c60203b08452479b90e5ce3dbe96a916214bc9eb2e5af0b6a9beb0", size = 942542, upload-time = "2025-11-05T20:41:41.838Z" }, + { url = "https://files.pythonhosted.org/packages/19/62/7a3cf601d5a45137a7e2b89d10c05b5b86499190c4b7ca5c3c47d79ee519/rignore-0.7.6-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8fc5a531ef02131e44359419a366bfac57f773ea58f5278c2cdd915f7d10ea94", size = 958739, upload-time = "2025-11-05T20:42:12.463Z" }, + { url = "https://files.pythonhosted.org/packages/5f/1f/4261f6a0d7caf2058a5cde2f5045f565ab91aa7badc972b57d19ce58b14e/rignore-0.7.6-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b7a1f77d9c4cd7e76229e252614d963442686bfe12c787a49f4fe481df49e7a9", size = 984138, upload-time = "2025-11-05T20:41:56.775Z" }, + { url = "https://files.pythonhosted.org/packages/2b/bf/628dfe19c75e8ce1f45f7c248f5148b17dfa89a817f8e3552ab74c3ae812/rignore-0.7.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ead81f728682ba72b5b1c3d5846b011d3e0174da978de87c61645f2ed36659a7", size = 1079299, upload-time = "2025-11-05T21:40:16.639Z" }, + { url = "https://files.pythonhosted.org/packages/af/a5/be29c50f5c0c25c637ed32db8758fdf5b901a99e08b608971cda8afb293b/rignore-0.7.6-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:12ffd50f520c22ffdabed8cd8bfb567d9ac165b2b854d3e679f4bcaef11a9441", size = 1139618, upload-time = "2025-11-05T21:40:34.507Z" }, + { url = "https://files.pythonhosted.org/packages/2a/40/3c46cd7ce4fa05c20b525fd60f599165e820af66e66f2c371cd50644558f/rignore-0.7.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:e5a16890fbe3c894f8ca34b0fcacc2c200398d4d46ae654e03bc9b3dbf2a0a72", size = 1117626, upload-time = "2025-11-05T21:40:51.494Z" }, + { url = "https://files.pythonhosted.org/packages/8c/b9/aea926f263b8a29a23c75c2e0d8447965eb1879d3feb53cfcf84db67ed58/rignore-0.7.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3abab3bf99e8a77488ef6c7c9a799fac22224c28fe9f25cc21aa7cc2b72bfc0b", size = 1128144, upload-time = "2025-11-05T21:41:09.169Z" }, + { url = "https://files.pythonhosted.org/packages/a4/f6/0d6242f8d0df7f2ecbe91679fefc1f75e7cd2072cb4f497abaab3f0f8523/rignore-0.7.6-cp314-cp314-win32.whl", hash = "sha256:eeef421c1782953c4375aa32f06ecae470c1285c6381eee2a30d2e02a5633001", size = 646385, upload-time = "2025-11-05T21:41:55.105Z" }, + { url = "https://files.pythonhosted.org/packages/d5/38/c0dcd7b10064f084343d6af26fe9414e46e9619c5f3224b5272e8e5d9956/rignore-0.7.6-cp314-cp314-win_amd64.whl", hash = "sha256:6aeed503b3b3d5af939b21d72a82521701a4bd3b89cd761da1e7dc78621af304", size = 725738, upload-time = "2025-11-05T21:41:39.736Z" }, + { url = "https://files.pythonhosted.org/packages/d9/7a/290f868296c1ece914d565757ab363b04730a728b544beb567ceb3b2d96f/rignore-0.7.6-cp314-cp314-win_arm64.whl", hash = "sha256:104f215b60b3c984c386c3e747d6ab4376d5656478694e22c7bd2f788ddd8304", size = 656008, upload-time = "2025-11-05T21:41:29.028Z" }, + { url = "https://files.pythonhosted.org/packages/ca/d2/3c74e3cd81fe8ea08a8dcd2d755c09ac2e8ad8fe409508904557b58383d3/rignore-0.7.6-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:bb24a5b947656dd94cb9e41c4bc8b23cec0c435b58be0d74a874f63c259549e8", size = 882835, upload-time = "2025-11-05T20:42:45.443Z" }, + { url = "https://files.pythonhosted.org/packages/77/61/a772a34b6b63154877433ac2d048364815b24c2dd308f76b212c408101a2/rignore-0.7.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5b1e33c9501cefe24b70a1eafd9821acfd0ebf0b35c3a379430a14df089993e3", size = 820301, upload-time = "2025-11-05T20:42:29.226Z" }, + { url = "https://files.pythonhosted.org/packages/71/30/054880b09c0b1b61d17eeb15279d8bf729c0ba52b36c3ada52fb827cbb3c/rignore-0.7.6-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bec3994665a44454df86deb762061e05cd4b61e3772f5b07d1882a8a0d2748d5", size = 897611, upload-time = "2025-11-05T20:40:56.475Z" }, + { url = "https://files.pythonhosted.org/packages/1e/40/b2d1c169f833d69931bf232600eaa3c7998ba4f9a402e43a822dad2ea9f2/rignore-0.7.6-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26cba2edfe3cff1dfa72bddf65d316ddebf182f011f2f61538705d6dbaf54986", size = 873875, upload-time = "2025-11-05T20:41:11.561Z" }, + { url = "https://files.pythonhosted.org/packages/55/59/ca5ae93d83a1a60e44b21d87deb48b177a8db1b85e82fc8a9abb24a8986d/rignore-0.7.6-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ffa86694fec604c613696cb91e43892aa22e1fec5f9870e48f111c603e5ec4e9", size = 1167245, upload-time = "2025-11-05T20:41:28.29Z" }, + { url = "https://files.pythonhosted.org/packages/a5/52/cf3dce392ba2af806cba265aad6bcd9c48bb2a6cb5eee448d3319f6e505b/rignore-0.7.6-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48efe2ed95aa8104145004afb15cdfa02bea5cdde8b0344afeb0434f0d989aa2", size = 941750, upload-time = "2025-11-05T20:41:43.111Z" }, + { url = "https://files.pythonhosted.org/packages/ec/be/3f344c6218d779395e785091d05396dfd8b625f6aafbe502746fcd880af2/rignore-0.7.6-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dcae43eb44b7f2457fef7cc87f103f9a0013017a6f4e62182c565e924948f21", size = 958896, upload-time = "2025-11-05T20:42:13.784Z" }, + { url = "https://files.pythonhosted.org/packages/c9/34/d3fa71938aed7d00dcad87f0f9bcb02ad66c85d6ffc83ba31078ce53646a/rignore-0.7.6-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2cd649a7091c0dad2f11ef65630d30c698d505cbe8660dd395268e7c099cc99f", size = 983992, upload-time = "2025-11-05T20:41:58.022Z" }, + { url = "https://files.pythonhosted.org/packages/24/a4/52a697158e9920705bdbd0748d59fa63e0f3233fb92e9df9a71afbead6ca/rignore-0.7.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42de84b0289d478d30ceb7ae59023f7b0527786a9a5b490830e080f0e4ea5aeb", size = 1078181, upload-time = "2025-11-05T21:40:18.151Z" }, + { url = "https://files.pythonhosted.org/packages/ac/65/aa76dbcdabf3787a6f0fd61b5cc8ed1e88580590556d6c0207960d2384bb/rignore-0.7.6-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:875a617e57b53b4acbc5a91de418233849711c02e29cc1f4f9febb2f928af013", size = 1139232, upload-time = "2025-11-05T21:40:35.966Z" }, + { url = "https://files.pythonhosted.org/packages/08/44/31b31a49b3233c6842acc1c0731aa1e7fb322a7170612acf30327f700b44/rignore-0.7.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:8703998902771e96e49968105207719f22926e4431b108450f3f430b4e268b7c", size = 1117349, upload-time = "2025-11-05T21:40:53.013Z" }, + { url = "https://files.pythonhosted.org/packages/e9/ae/1b199a2302c19c658cf74e5ee1427605234e8c91787cfba0015f2ace145b/rignore-0.7.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:602ef33f3e1b04c1e9a10a3c03f8bc3cef2d2383dcc250d309be42b49923cabc", size = 1127702, upload-time = "2025-11-05T21:41:10.881Z" }, + { url = "https://files.pythonhosted.org/packages/fc/d3/18210222b37e87e36357f7b300b7d98c6dd62b133771e71ae27acba83a4f/rignore-0.7.6-cp314-cp314t-win32.whl", hash = "sha256:c1d8f117f7da0a4a96a8daef3da75bc090e3792d30b8b12cfadc240c631353f9", size = 647033, upload-time = "2025-11-05T21:42:00.095Z" }, + { url = "https://files.pythonhosted.org/packages/3e/87/033eebfbee3ec7d92b3bb1717d8f68c88e6fc7de54537040f3b3a405726f/rignore-0.7.6-cp314-cp314t-win_amd64.whl", hash = "sha256:ca36e59408bec81de75d307c568c2d0d410fb880b1769be43611472c61e85c96", size = 725647, upload-time = "2025-11-05T21:41:44.449Z" }, + { url = "https://files.pythonhosted.org/packages/79/62/b88e5879512c55b8ee979c666ee6902adc4ed05007226de266410ae27965/rignore-0.7.6-cp314-cp314t-win_arm64.whl", hash = "sha256:b83adabeb3e8cf662cabe1931b83e165b88c526fa6af6b3aa90429686e474896", size = 656035, upload-time = "2025-11-05T21:41:31.13Z" }, +] + +[[package]] +name = "ruff" +version = "0.15.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/77/9b/840e0039e65fcf12758adf684d2289024d6140cde9268cc59887dc55189c/ruff-0.15.5.tar.gz", hash = "sha256:7c3601d3b6d76dce18c5c824fc8d06f4eef33d6df0c21ec7799510cde0f159a2", size = 4574214, upload-time = "2026-03-05T20:06:34.946Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/20/5369c3ce21588c708bcbe517a8fbe1a8dfdb5dfd5137e14790b1da71612c/ruff-0.15.5-py3-none-linux_armv6l.whl", hash = "sha256:4ae44c42281f42e3b06b988e442d344a5b9b72450ff3c892e30d11b29a96a57c", size = 10478185, upload-time = "2026-03-05T20:06:29.093Z" }, + { url = "https://files.pythonhosted.org/packages/44/ed/e81dd668547da281e5dce710cf0bc60193f8d3d43833e8241d006720e42b/ruff-0.15.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6edd3792d408ebcf61adabc01822da687579a1a023f297618ac27a5b51ef0080", size = 10859201, upload-time = "2026-03-05T20:06:32.632Z" }, + { url = "https://files.pythonhosted.org/packages/c4/8f/533075f00aaf19b07c5cd6aa6e5d89424b06b3b3f4583bfa9c640a079059/ruff-0.15.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:89f463f7c8205a9f8dea9d658d59eff49db05f88f89cc3047fb1a02d9f344010", size = 10184752, upload-time = "2026-03-05T20:06:40.312Z" }, + { url = "https://files.pythonhosted.org/packages/66/0e/ba49e2c3fa0395b3152bad634c7432f7edfc509c133b8f4529053ff024fb/ruff-0.15.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba786a8295c6574c1116704cf0b9e6563de3432ac888d8f83685654fe528fd65", size = 10534857, upload-time = "2026-03-05T20:06:19.581Z" }, + { url = "https://files.pythonhosted.org/packages/59/71/39234440f27a226475a0659561adb0d784b4d247dfe7f43ffc12dd02e288/ruff-0.15.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd4b801e57955fe9f02b31d20375ab3a5c4415f2e5105b79fb94cf2642c91440", size = 10309120, upload-time = "2026-03-05T20:06:00.435Z" }, + { url = "https://files.pythonhosted.org/packages/f5/87/4140aa86a93df032156982b726f4952aaec4a883bb98cb6ef73c347da253/ruff-0.15.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:391f7c73388f3d8c11b794dbbc2959a5b5afe66642c142a6effa90b45f6f5204", size = 11047428, upload-time = "2026-03-05T20:05:51.867Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f7/4953e7e3287676f78fbe85e3a0ca414c5ca81237b7575bdadc00229ac240/ruff-0.15.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dc18f30302e379fe1e998548b0f5e9f4dff907f52f73ad6da419ea9c19d66c8", size = 11914251, upload-time = "2026-03-05T20:06:22.887Z" }, + { url = "https://files.pythonhosted.org/packages/77/46/0f7c865c10cf896ccf5a939c3e84e1cfaeed608ff5249584799a74d33835/ruff-0.15.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc6e7f90087e2d27f98dc34ed1b3ab7c8f0d273cc5431415454e22c0bd2a681", size = 11333801, upload-time = "2026-03-05T20:05:57.168Z" }, + { url = "https://files.pythonhosted.org/packages/d3/01/a10fe54b653061585e655f5286c2662ebddb68831ed3eaebfb0eb08c0a16/ruff-0.15.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1cb7169f53c1ddb06e71a9aebd7e98fc0fea936b39afb36d8e86d36ecc2636a", size = 11206821, upload-time = "2026-03-05T20:06:03.441Z" }, + { url = "https://files.pythonhosted.org/packages/7a/0d/2132ceaf20c5e8699aa83da2706ecb5c5dcdf78b453f77edca7fb70f8a93/ruff-0.15.5-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9b037924500a31ee17389b5c8c4d88874cc6ea8e42f12e9c61a3d754ff72f1ca", size = 11133326, upload-time = "2026-03-05T20:06:25.655Z" }, + { url = "https://files.pythonhosted.org/packages/72/cb/2e5259a7eb2a0f87c08c0fe5bf5825a1e4b90883a52685524596bfc93072/ruff-0.15.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:65bb414e5b4eadd95a8c1e4804f6772bbe8995889f203a01f77ddf2d790929dd", size = 10510820, upload-time = "2026-03-05T20:06:37.79Z" }, + { url = "https://files.pythonhosted.org/packages/ff/20/b67ce78f9e6c59ffbdb5b4503d0090e749b5f2d31b599b554698a80d861c/ruff-0.15.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d20aa469ae3b57033519c559e9bc9cd9e782842e39be05b50e852c7c981fa01d", size = 10302395, upload-time = "2026-03-05T20:05:54.504Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e5/719f1acccd31b720d477751558ed74e9c88134adcc377e5e886af89d3072/ruff-0.15.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:15388dd28c9161cdb8eda68993533acc870aa4e646a0a277aa166de9ad5a8752", size = 10754069, upload-time = "2026-03-05T20:06:06.422Z" }, + { url = "https://files.pythonhosted.org/packages/c3/9c/d1db14469e32d98f3ca27079dbd30b7b44dbb5317d06ab36718dee3baf03/ruff-0.15.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b30da330cbd03bed0c21420b6b953158f60c74c54c5f4c1dabbdf3a57bf355d2", size = 11304315, upload-time = "2026-03-05T20:06:10.867Z" }, + { url = "https://files.pythonhosted.org/packages/28/3a/950367aee7c69027f4f422059227b290ed780366b6aecee5de5039d50fa8/ruff-0.15.5-py3-none-win32.whl", hash = "sha256:732e5ee1f98ba5b3679029989a06ca39a950cced52143a0ea82a2102cb592b74", size = 10551676, upload-time = "2026-03-05T20:06:13.705Z" }, + { url = "https://files.pythonhosted.org/packages/b8/00/bf077a505b4e649bdd3c47ff8ec967735ce2544c8e4a43aba42ee9bf935d/ruff-0.15.5-py3-none-win_amd64.whl", hash = "sha256:821d41c5fa9e19117616c35eaa3f4b75046ec76c65e7ae20a333e9a8696bc7fe", size = 11678972, upload-time = "2026-03-05T20:06:45.379Z" }, + { url = "https://files.pythonhosted.org/packages/fe/4e/cd76eca6db6115604b7626668e891c9dd03330384082e33662fb0f113614/ruff-0.15.5-py3-none-win_arm64.whl", hash = "sha256:b498d1c60d2fe5c10c45ec3f698901065772730b411f164ae270bb6bfcc4740b", size = 10965572, upload-time = "2026-03-05T20:06:16.984Z" }, +] + +[[package]] +name = "safetensors" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/29/9c/6e74567782559a63bd040a236edca26fd71bc7ba88de2ef35d75df3bca5e/safetensors-0.7.0.tar.gz", hash = "sha256:07663963b67e8bd9f0b8ad15bb9163606cd27cc5a1b96235a50d8369803b96b0", size = 200878, upload-time = "2025-11-19T15:18:43.199Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/47/aef6c06649039accf914afef490268e1067ed82be62bcfa5b7e886ad15e8/safetensors-0.7.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c82f4d474cf725255d9e6acf17252991c3c8aac038d6ef363a4bf8be2f6db517", size = 467781, upload-time = "2025-11-19T15:18:35.84Z" }, + { url = "https://files.pythonhosted.org/packages/e8/00/374c0c068e30cd31f1e1b46b4b5738168ec79e7689ca82ee93ddfea05109/safetensors-0.7.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:94fd4858284736bb67a897a41608b5b0c2496c9bdb3bf2af1fa3409127f20d57", size = 447058, upload-time = "2025-11-19T15:18:34.416Z" }, + { url = "https://files.pythonhosted.org/packages/f1/06/578ffed52c2296f93d7fd2d844cabfa92be51a587c38c8afbb8ae449ca89/safetensors-0.7.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e07d91d0c92a31200f25351f4acb2bc6aff7f48094e13ebb1d0fb995b54b6542", size = 491748, upload-time = "2025-11-19T15:18:09.79Z" }, + { url = "https://files.pythonhosted.org/packages/ae/33/1debbbb70e4791dde185edb9413d1fe01619255abb64b300157d7f15dddd/safetensors-0.7.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8469155f4cb518bafb4acf4865e8bb9d6804110d2d9bdcaa78564b9fd841e104", size = 503881, upload-time = "2025-11-19T15:18:16.145Z" }, + { url = "https://files.pythonhosted.org/packages/8e/1c/40c2ca924d60792c3be509833df711b553c60effbd91da6f5284a83f7122/safetensors-0.7.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54bef08bf00a2bff599982f6b08e8770e09cc012d7bba00783fc7ea38f1fb37d", size = 623463, upload-time = "2025-11-19T15:18:21.11Z" }, + { url = "https://files.pythonhosted.org/packages/9b/3a/13784a9364bd43b0d61eef4bea2845039bc2030458b16594a1bd787ae26e/safetensors-0.7.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42cb091236206bb2016d245c377ed383aa7f78691748f3bb6ee1bfa51ae2ce6a", size = 532855, upload-time = "2025-11-19T15:18:25.719Z" }, + { url = "https://files.pythonhosted.org/packages/a0/60/429e9b1cb3fc651937727befe258ea24122d9663e4d5709a48c9cbfceecb/safetensors-0.7.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac7252938f0696ddea46f5e855dd3138444e82236e3be475f54929f0c510d48", size = 507152, upload-time = "2025-11-19T15:18:33.023Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a8/4b45e4e059270d17af60359713ffd83f97900d45a6afa73aaa0d737d48b6/safetensors-0.7.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1d060c70284127fa805085d8f10fbd0962792aed71879d00864acda69dbab981", size = 541856, upload-time = "2025-11-19T15:18:31.075Z" }, + { url = "https://files.pythonhosted.org/packages/06/87/d26d8407c44175d8ae164a95b5a62707fcc445f3c0c56108e37d98070a3d/safetensors-0.7.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cdab83a366799fa730f90a4ebb563e494f28e9e92c4819e556152ad55e43591b", size = 674060, upload-time = "2025-11-19T15:18:37.211Z" }, + { url = "https://files.pythonhosted.org/packages/11/f5/57644a2ff08dc6325816ba7217e5095f17269dada2554b658442c66aed51/safetensors-0.7.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:672132907fcad9f2aedcb705b2d7b3b93354a2aec1b2f706c4db852abe338f85", size = 771715, upload-time = "2025-11-19T15:18:38.689Z" }, + { url = "https://files.pythonhosted.org/packages/86/31/17883e13a814bd278ae6e266b13282a01049b0c81341da7fd0e3e71a80a3/safetensors-0.7.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:5d72abdb8a4d56d4020713724ba81dac065fedb7f3667151c4a637f1d3fb26c0", size = 714377, upload-time = "2025-11-19T15:18:40.162Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d8/0c8a7dc9b41dcac53c4cbf9df2b9c83e0e0097203de8b37a712b345c0be5/safetensors-0.7.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b0f6d66c1c538d5a94a73aa9ddca8ccc4227e6c9ff555322ea40bdd142391dd4", size = 677368, upload-time = "2025-11-19T15:18:41.627Z" }, + { url = "https://files.pythonhosted.org/packages/05/e5/cb4b713c8a93469e3c5be7c3f8d77d307e65fe89673e731f5c2bfd0a9237/safetensors-0.7.0-cp38-abi3-win32.whl", hash = "sha256:c74af94bf3ac15ac4d0f2a7c7b4663a15f8c2ab15ed0fc7531ca61d0835eccba", size = 326423, upload-time = "2025-11-19T15:18:45.74Z" }, + { url = "https://files.pythonhosted.org/packages/5d/e6/ec8471c8072382cb91233ba7267fd931219753bb43814cbc71757bfd4dab/safetensors-0.7.0-cp38-abi3-win_amd64.whl", hash = "sha256:d1239932053f56f3456f32eb9625590cc7582e905021f94636202a864d470755", size = 341380, upload-time = "2025-11-19T15:18:44.427Z" }, +] + +[[package]] +name = "sentry-sdk" +version = "2.54.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c8/e9/2e3a46c304e7fa21eaa70612f60354e32699c7102eb961f67448e222ad7c/sentry_sdk-2.54.0.tar.gz", hash = "sha256:2620c2575128d009b11b20f7feb81e4e4e8ae08ec1d36cbc845705060b45cc1b", size = 413813, upload-time = "2026-03-02T15:12:41.355Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/53/39/be412cc86bc6247b8f69e9383d7950711bd86f8d0a4a4b0fe8fad685bc21/sentry_sdk-2.54.0-py2.py3-none-any.whl", hash = "sha256:fd74e0e281dcda63afff095d23ebcd6e97006102cdc8e78a29f19ecdf796a0de", size = 439198, upload-time = "2026-03-02T15:12:39.546Z" }, +] + +[[package]] +name = "setuptools" +version = "82.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/f3/748f4d6f65d1756b9ae577f329c951cda23fb900e4de9f70900ced962085/setuptools-82.0.0.tar.gz", hash = "sha256:22e0a2d69474c6ae4feb01951cb69d515ed23728cf96d05513d36e42b62b37cb", size = 1144893, upload-time = "2026-02-08T15:08:40.206Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/c6/76dc613121b793286a3f91621d7b75a2b493e0390ddca50f11993eadf192/setuptools-82.0.0-py3-none-any.whl", hash = "sha256:70b18734b607bd1da571d097d236cfcfacaf01de45717d59e6e04b96877532e0", size = 1003468, upload-time = "2026-02-08T15:08:38.723Z" }, +] + +[[package]] +name = "shellingham" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, +] + +[[package]] +name = "stanza" +version = "1.11.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "emoji" }, + { name = "networkx" }, + { name = "numpy" }, + { name = "platformdirs" }, + { name = "protobuf" }, + { name = "requests" }, + { name = "torch" }, + { name = "tqdm" }, + { name = "udtools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f6/bd/c2d7f7c3e13688dd07738a5cf911a30ba760d260752df55c7c77ed4b070f/stanza-1.11.1.tar.gz", hash = "sha256:eff84c40c9dab1badf89d09f04a9d887eebff01976e14f876582c0bd4bcc51e8", size = 1489287, upload-time = "2026-02-27T00:28:52.476Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/d4/38e98f8152b1f82fb2bbf89b8bafd63415383b4b2b7cf34d93f911e62d03/stanza-1.11.1-py3-none-any.whl", hash = "sha256:fb57509c182df4c8556f179ecaa3d47a1612a107d781f78d4084a24f3e7ee98c", size = 1711245, upload-time = "2026-02-27T00:28:48.512Z" }, +] + +[package.optional-dependencies] +transformers = [ + { name = "peft" }, + { name = "transformers" }, +] + +[[package]] +name = "stanza-server" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "fastapi", extra = ["standard"] }, + { name = "stanza", extra = ["transformers"] }, +] + +[package.dev-dependencies] +dev = [ + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "fastapi", extras = ["standard"], specifier = ">=0.135.1" }, + { name = "stanza", extras = ["transformers"], specifier = ">=1.11.1" }, +] + +[package.metadata.requires-dev] +dev = [{ name = "ruff", specifier = ">=0.15.5" }] + +[[package]] +name = "starlette" +version = "0.52.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c4/68/79977123bb7be889ad680d79a40f339082c1978b5cfcf62c2d8d196873ac/starlette-0.52.1.tar.gz", hash = "sha256:834edd1b0a23167694292e94f597773bc3f89f362be6effee198165a35d62933", size = 2653702, upload-time = "2026-01-18T13:34:11.062Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/0d/13d1d239a25cbfb19e740db83143e95c772a1fe10202dda4b76792b114dd/starlette-0.52.1-py3-none-any.whl", hash = "sha256:0029d43eb3d273bc4f83a08720b4912ea4b071087a3b48db01b7c839f7954d74", size = 74272, upload-time = "2026-01-18T13:34:09.188Z" }, +] + +[[package]] +name = "sympy" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, +] + +[[package]] +name = "termcolor" +version = "3.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/46/79/cf31d7a93a8fdc6aa0fbb665be84426a8c5a557d9240b6239e9e11e35fc5/termcolor-3.3.0.tar.gz", hash = "sha256:348871ca648ec6a9a983a13ab626c0acce02f515b9e1983332b17af7979521c5", size = 14434, upload-time = "2025-12-29T12:55:21.882Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/d1/8bb87d21e9aeb323cc03034f5eaf2c8f69841e40e4853c2627edf8111ed3/termcolor-3.3.0-py3-none-any.whl", hash = "sha256:cf642efadaf0a8ebbbf4bc7a31cec2f9b5f21a9f726f4ccbb08192c9c26f43a5", size = 7734, upload-time = "2025-12-29T12:55:20.718Z" }, +] + +[[package]] +name = "tokenizers" +version = "0.22.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115, upload-time = "2026-01-05T10:45:15.988Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/97/5dbfabf04c7e348e655e907ed27913e03db0923abb5dfdd120d7b25630e1/tokenizers-0.22.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", size = 3100275, upload-time = "2026-01-05T10:41:02.158Z" }, + { url = "https://files.pythonhosted.org/packages/2e/47/174dca0502ef88b28f1c9e06b73ce33500eedfac7a7692108aec220464e7/tokenizers-0.22.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", size = 2981472, upload-time = "2026-01-05T10:41:00.276Z" }, + { url = "https://files.pythonhosted.org/packages/d6/84/7990e799f1309a8b87af6b948f31edaa12a3ed22d11b352eaf4f4b2e5753/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", size = 3290736, upload-time = "2026-01-05T10:40:32.165Z" }, + { url = "https://files.pythonhosted.org/packages/78/59/09d0d9ba94dcd5f4f1368d4858d24546b4bdc0231c2354aa31d6199f0399/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", size = 3168835, upload-time = "2026-01-05T10:40:38.847Z" }, + { url = "https://files.pythonhosted.org/packages/47/50/b3ebb4243e7160bda8d34b731e54dd8ab8b133e50775872e7a434e524c28/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", size = 3521673, upload-time = "2026-01-05T10:40:56.614Z" }, + { url = "https://files.pythonhosted.org/packages/e0/fa/89f4cb9e08df770b57adb96f8cbb7e22695a4cb6c2bd5f0c4f0ebcf33b66/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", size = 3724818, upload-time = "2026-01-05T10:40:44.507Z" }, + { url = "https://files.pythonhosted.org/packages/64/04/ca2363f0bfbe3b3d36e95bf67e56a4c88c8e3362b658e616d1ac185d47f2/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", size = 3379195, upload-time = "2026-01-05T10:40:51.139Z" }, + { url = "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", size = 3274982, upload-time = "2026-01-05T10:40:58.331Z" }, + { url = "https://files.pythonhosted.org/packages/1d/28/5f9f5a4cc211b69e89420980e483831bcc29dade307955cc9dc858a40f01/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", size = 9478245, upload-time = "2026-01-05T10:41:04.053Z" }, + { url = "https://files.pythonhosted.org/packages/6c/fb/66e2da4704d6aadebf8cb39f1d6d1957df667ab24cff2326b77cda0dcb85/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", size = 9560069, upload-time = "2026-01-05T10:45:10.673Z" }, + { url = "https://files.pythonhosted.org/packages/16/04/fed398b05caa87ce9b1a1bb5166645e38196081b225059a6edaff6440fac/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", size = 9899263, upload-time = "2026-01-05T10:45:12.559Z" }, + { url = "https://files.pythonhosted.org/packages/05/a1/d62dfe7376beaaf1394917e0f8e93ee5f67fea8fcf4107501db35996586b/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", size = 10033429, upload-time = "2026-01-05T10:45:14.333Z" }, + { url = "https://files.pythonhosted.org/packages/fd/18/a545c4ea42af3df6effd7d13d250ba77a0a86fb20393143bbb9a92e434d4/tokenizers-0.22.2-cp39-abi3-win32.whl", hash = "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", size = 2502363, upload-time = "2026-01-05T10:45:20.593Z" }, + { url = "https://files.pythonhosted.org/packages/65/71/0670843133a43d43070abeb1949abfdef12a86d490bea9cd9e18e37c5ff7/tokenizers-0.22.2-cp39-abi3-win_amd64.whl", hash = "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", size = 2747786, upload-time = "2026-01-05T10:45:18.411Z" }, + { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133, upload-time = "2026-01-05T10:45:17.232Z" }, +] + +[[package]] +name = "torch" +version = "2.10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-bindings", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "filelock" }, + { name = "fsspec" }, + { name = "jinja2" }, + { name = "networkx" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvshmem-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "setuptools" }, + { name = "sympy" }, + { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/93/716b5ac0155f1be70ed81bacc21269c3ece8dba0c249b9994094110bfc51/torch-2.10.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:bf0d9ff448b0218e0433aeb198805192346c4fd659c852370d5cc245f602a06a", size = 79464992, upload-time = "2026-01-21T16:23:05.162Z" }, + { url = "https://files.pythonhosted.org/packages/69/2b/51e663ff190c9d16d4a8271203b71bc73a16aa7619b9f271a69b9d4a936b/torch-2.10.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:233aed0659a2503b831d8a67e9da66a62c996204c0bba4f4c442ccc0c68a3f60", size = 146018567, upload-time = "2026-01-21T16:22:23.393Z" }, + { url = "https://files.pythonhosted.org/packages/5e/cd/4b95ef7f293b927c283db0b136c42be91c8ec6845c44de0238c8c23bdc80/torch-2.10.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:682497e16bdfa6efeec8cde66531bc8d1fbbbb4d8788ec6173c089ed3cc2bfe5", size = 915721646, upload-time = "2026-01-21T16:21:16.983Z" }, + { url = "https://files.pythonhosted.org/packages/56/97/078a007208f8056d88ae43198833469e61a0a355abc0b070edd2c085eb9a/torch-2.10.0-cp314-cp314-win_amd64.whl", hash = "sha256:6528f13d2a8593a1a412ea07a99812495bec07e9224c28b2a25c0a30c7da025c", size = 113752373, upload-time = "2026-01-21T16:22:13.471Z" }, + { url = "https://files.pythonhosted.org/packages/d8/94/71994e7d0d5238393df9732fdab607e37e2b56d26a746cb59fdb415f8966/torch-2.10.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:f5ab4ba32383061be0fb74bda772d470140a12c1c3b58a0cfbf3dae94d164c28", size = 79850324, upload-time = "2026-01-21T16:22:09.494Z" }, + { url = "https://files.pythonhosted.org/packages/e2/65/1a05346b418ea8ccd10360eef4b3e0ce688fba544e76edec26913a8d0ee0/torch-2.10.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:716b01a176c2a5659c98f6b01bf868244abdd896526f1c692712ab36dbaf9b63", size = 146006482, upload-time = "2026-01-21T16:22:18.42Z" }, + { url = "https://files.pythonhosted.org/packages/1d/b9/5f6f9d9e859fc3235f60578fa64f52c9c6e9b4327f0fe0defb6de5c0de31/torch-2.10.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:d8f5912ba938233f86361e891789595ff35ca4b4e2ac8fe3670895e5976731d6", size = 915613050, upload-time = "2026-01-21T16:20:49.035Z" }, + { url = "https://files.pythonhosted.org/packages/66/4d/35352043ee0eaffdeff154fad67cd4a31dbed7ff8e3be1cc4549717d6d51/torch-2.10.0-cp314-cp314t-win_amd64.whl", hash = "sha256:71283a373f0ee2c89e0f0d5f446039bdabe8dbc3c9ccf35f0f784908b0acd185", size = 113995816, upload-time = "2026-01-21T16:22:05.312Z" }, +] + +[[package]] +name = "tqdm" +version = "4.67.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598, upload-time = "2026-02-03T17:35:53.048Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" }, +] + +[[package]] +name = "transformers" +version = "5.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "regex" }, + { name = "safetensors" }, + { name = "tokenizers" }, + { name = "tqdm" }, + { name = "typer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/1a/70e830d53ecc96ce69cfa8de38f163712d2b43ac52fbd743f39f56025c31/transformers-5.3.0.tar.gz", hash = "sha256:009555b364029da9e2946d41f1c5de9f15e6b1df46b189b7293f33a161b9c557", size = 8830831, upload-time = "2026-03-04T17:41:46.119Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/88/ae8320064e32679a5429a2c9ebbc05c2bf32cefb6e076f9b07f6d685a9b4/transformers-5.3.0-py3-none-any.whl", hash = "sha256:50ac8c89c3c7033444fb3f9f53138096b997ebb70d4b5e50a2e810bf12d3d29a", size = 10661827, upload-time = "2026-03-04T17:41:42.722Z" }, +] + +[[package]] +name = "triton" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/3d/9e7eee57b37c80cec63322c0231bb6da3cfe535a91d7a4d64896fcb89357/triton-3.6.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a17a5d5985f0ac494ed8a8e54568f092f7057ef60e1b0fa09d3fd1512064e803", size = 188273063, upload-time = "2026-01-20T16:01:07.278Z" }, + { url = "https://files.pythonhosted.org/packages/f6/56/6113c23ff46c00aae423333eb58b3e60bdfe9179d542781955a5e1514cb3/triton-3.6.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46bd1c1af4b6704e554cad2eeb3b0a6513a980d470ccfa63189737340c7746a7", size = 188397994, upload-time = "2026-01-20T16:01:14.236Z" }, +] + +[[package]] +name = "typer" +version = "0.24.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc" }, + { name = "click" }, + { name = "rich" }, + { name = "shellingham" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/24/cb09efec5cc954f7f9b930bf8279447d24618bb6758d4f6adf2574c41780/typer-0.24.1.tar.gz", hash = "sha256:e39b4732d65fbdcde189ae76cf7cd48aeae72919dea1fdfc16593be016256b45", size = 118613, upload-time = "2026-02-21T16:54:40.609Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl", hash = "sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21f04e9aa84c9e", size = 56085, upload-time = "2026-02-21T16:54:41.616Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, +] + +[[package]] +name = "udapi" +version = "0.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama" }, + { name = "termcolor" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/93/15/c929f9a49122af0395184402f5d038b3c7aa590157e5650ca20014d3ef70/udapi-0.5.1.tar.gz", hash = "sha256:5f8db0c5e6a63108e0f2f8001970c3ecb8381da471509593393941259e44f44a", size = 257084, upload-time = "2025-11-05T14:56:44.448Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/57/18bff9f98e921e87695c141513ceecd6b55dcd00b2a5feb13a602f2ee508/udapi-0.5.1-py3-none-any.whl", hash = "sha256:92b32fd7dbaaf2ce4ca6bbc6133778b698360aea6378e8c057621b352ae0f0d5", size = 337233, upload-time = "2025-11-05T14:56:42.758Z" }, +] + +[[package]] +name = "udtools" +version = "0.2.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "regex" }, + { name = "udapi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d5/c5/ec0e1cd4238e3a7ed564fe8a4a027587d00a59acf0e7ae9f121e94eb0994/udtools-0.2.7.tar.gz", hash = "sha256:423396ececa70e2903c2c7cc1e6a259716122550cf41f77c1c2261ee27852213", size = 1687968, upload-time = "2026-01-08T15:21:50.303Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/6d/504e02a92690db238ad11a67f1dae46b8e9615a5497124c9a38e2207f451/udtools-0.2.7-py3-none-any.whl", hash = "sha256:a394372f081a673056995723770362fbe77b0291a86b11ea5d59f2957793442f", size = 773663, upload-time = "2026-01-08T15:21:48.667Z" }, +] + +[[package]] +name = "urllib3" +version = "2.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, +] + +[[package]] +name = "uvicorn" +version = "0.41.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/32/ce/eeb58ae4ac36fe09e3842eb02e0eb676bf2c53ae062b98f1b2531673efdd/uvicorn-0.41.0.tar.gz", hash = "sha256:09d11cf7008da33113824ee5a1c6422d89fbc2ff476540d69a34c87fab8b571a", size = 82633, upload-time = "2026-02-16T23:07:24.1Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/e4/d04a086285c20886c0daad0e026f250869201013d18f81d9ff5eada73a88/uvicorn-0.41.0-py3-none-any.whl", hash = "sha256:29e35b1d2c36a04b9e180d4007ede3bcb32a85fbdfd6c6aeb3f26839de088187", size = 68783, upload-time = "2026-02-16T23:07:22.357Z" }, +] + +[package.optional-dependencies] +standard = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "httptools" }, + { name = "python-dotenv" }, + { name = "pyyaml" }, + { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" }, + { name = "watchfiles" }, + { name = "websockets" }, +] + +[[package]] +name = "uvloop" +version = "0.22.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250, upload-time = "2025-10-16T22:17:19.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/cd/b62bdeaa429758aee8de8b00ac0dd26593a9de93d302bff3d21439e9791d/uvloop-0.22.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3879b88423ec7e97cd4eba2a443aa26ed4e59b45e6b76aabf13fe2f27023a142", size = 1362067, upload-time = "2025-10-16T22:16:44.503Z" }, + { url = "https://files.pythonhosted.org/packages/0d/f8/a132124dfda0777e489ca86732e85e69afcd1ff7686647000050ba670689/uvloop-0.22.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4baa86acedf1d62115c1dc6ad1e17134476688f08c6efd8a2ab076e815665c74", size = 752423, upload-time = "2025-10-16T22:16:45.968Z" }, + { url = "https://files.pythonhosted.org/packages/a3/94/94af78c156f88da4b3a733773ad5ba0b164393e357cc4bd0ab2e2677a7d6/uvloop-0.22.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:297c27d8003520596236bdb2335e6b3f649480bd09e00d1e3a99144b691d2a35", size = 4272437, upload-time = "2025-10-16T22:16:47.451Z" }, + { url = "https://files.pythonhosted.org/packages/b5/35/60249e9fd07b32c665192cec7af29e06c7cd96fa1d08b84f012a56a0b38e/uvloop-0.22.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1955d5a1dd43198244d47664a5858082a3239766a839b2102a269aaff7a4e25", size = 4292101, upload-time = "2025-10-16T22:16:49.318Z" }, + { url = "https://files.pythonhosted.org/packages/02/62/67d382dfcb25d0a98ce73c11ed1a6fba5037a1a1d533dcbb7cab033a2636/uvloop-0.22.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b31dc2fccbd42adc73bc4e7cdbae4fc5086cf378979e53ca5d0301838c5682c6", size = 4114158, upload-time = "2025-10-16T22:16:50.517Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/f1171b4a882a5d13c8b7576f348acfe6074d72eaf52cccef752f748d4a9f/uvloop-0.22.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:93f617675b2d03af4e72a5333ef89450dfaa5321303ede6e67ba9c9d26878079", size = 4177360, upload-time = "2025-10-16T22:16:52.646Z" }, + { url = "https://files.pythonhosted.org/packages/79/7b/b01414f31546caf0919da80ad57cbfe24c56b151d12af68cee1b04922ca8/uvloop-0.22.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:37554f70528f60cad66945b885eb01f1bb514f132d92b6eeed1c90fd54ed6289", size = 1454790, upload-time = "2025-10-16T22:16:54.355Z" }, + { url = "https://files.pythonhosted.org/packages/d4/31/0bb232318dd838cad3fa8fb0c68c8b40e1145b32025581975e18b11fab40/uvloop-0.22.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b76324e2dc033a0b2f435f33eb88ff9913c156ef78e153fb210e03c13da746b3", size = 796783, upload-time = "2025-10-16T22:16:55.906Z" }, + { url = "https://files.pythonhosted.org/packages/42/38/c9b09f3271a7a723a5de69f8e237ab8e7803183131bc57c890db0b6bb872/uvloop-0.22.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:badb4d8e58ee08dad957002027830d5c3b06aea446a6a3744483c2b3b745345c", size = 4647548, upload-time = "2025-10-16T22:16:57.008Z" }, + { url = "https://files.pythonhosted.org/packages/c1/37/945b4ca0ac27e3dc4952642d4c900edd030b3da6c9634875af6e13ae80e5/uvloop-0.22.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b91328c72635f6f9e0282e4a57da7470c7350ab1c9f48546c0f2866205349d21", size = 4467065, upload-time = "2025-10-16T22:16:58.206Z" }, + { url = "https://files.pythonhosted.org/packages/97/cc/48d232f33d60e2e2e0b42f4e73455b146b76ebe216487e862700457fbf3c/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:daf620c2995d193449393d6c62131b3fbd40a63bf7b307a1527856ace637fe88", size = 4328384, upload-time = "2025-10-16T22:16:59.36Z" }, + { url = "https://files.pythonhosted.org/packages/e4/16/c1fd27e9549f3c4baf1dc9c20c456cd2f822dbf8de9f463824b0c0357e06/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e", size = 4296730, upload-time = "2025-10-16T22:17:00.744Z" }, +] + +[[package]] +name = "watchfiles" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440, upload-time = "2025-10-14T15:06:21.08Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/f4/0872229324ef69b2c3edec35e84bd57a1289e7d3fe74588048ed8947a323/watchfiles-1.1.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:d1715143123baeeaeadec0528bb7441103979a1d5f6fd0e1f915383fea7ea6d5", size = 404315, upload-time = "2025-10-14T15:05:26.501Z" }, + { url = "https://files.pythonhosted.org/packages/7b/22/16d5331eaed1cb107b873f6ae1b69e9ced582fcf0c59a50cd84f403b1c32/watchfiles-1.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:39574d6370c4579d7f5d0ad940ce5b20db0e4117444e39b6d8f99db5676c52fd", size = 390869, upload-time = "2025-10-14T15:05:27.649Z" }, + { url = "https://files.pythonhosted.org/packages/b2/7e/5643bfff5acb6539b18483128fdc0ef2cccc94a5b8fbda130c823e8ed636/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7365b92c2e69ee952902e8f70f3ba6360d0d596d9299d55d7d386df84b6941fb", size = 449919, upload-time = "2025-10-14T15:05:28.701Z" }, + { url = "https://files.pythonhosted.org/packages/51/2e/c410993ba5025a9f9357c376f48976ef0e1b1aefb73b97a5ae01a5972755/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bfff9740c69c0e4ed32416f013f3c45e2ae42ccedd1167ef2d805c000b6c71a5", size = 460845, upload-time = "2025-10-14T15:05:30.064Z" }, + { url = "https://files.pythonhosted.org/packages/8e/a4/2df3b404469122e8680f0fcd06079317e48db58a2da2950fb45020947734/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b27cf2eb1dda37b2089e3907d8ea92922b673c0c427886d4edc6b94d8dfe5db3", size = 489027, upload-time = "2025-10-14T15:05:31.064Z" }, + { url = "https://files.pythonhosted.org/packages/ea/84/4587ba5b1f267167ee715b7f66e6382cca6938e0a4b870adad93e44747e6/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:526e86aced14a65a5b0ec50827c745597c782ff46b571dbfe46192ab9e0b3c33", size = 595615, upload-time = "2025-10-14T15:05:32.074Z" }, + { url = "https://files.pythonhosted.org/packages/6a/0f/c6988c91d06e93cd0bb3d4a808bcf32375ca1904609835c3031799e3ecae/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04e78dd0b6352db95507fd8cb46f39d185cf8c74e4cf1e4fbad1d3df96faf510", size = 474836, upload-time = "2025-10-14T15:05:33.209Z" }, + { url = "https://files.pythonhosted.org/packages/b4/36/ded8aebea91919485b7bbabbd14f5f359326cb5ec218cd67074d1e426d74/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c85794a4cfa094714fb9c08d4a218375b2b95b8ed1666e8677c349906246c05", size = 455099, upload-time = "2025-10-14T15:05:34.189Z" }, + { url = "https://files.pythonhosted.org/packages/98/e0/8c9bdba88af756a2fce230dd365fab2baf927ba42cd47521ee7498fd5211/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:74d5012b7630714b66be7b7b7a78855ef7ad58e8650c73afc4c076a1f480a8d6", size = 630626, upload-time = "2025-10-14T15:05:35.216Z" }, + { url = "https://files.pythonhosted.org/packages/2a/84/a95db05354bf2d19e438520d92a8ca475e578c647f78f53197f5a2f17aaf/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:8fbe85cb3201c7d380d3d0b90e63d520f15d6afe217165d7f98c9c649654db81", size = 622519, upload-time = "2025-10-14T15:05:36.259Z" }, + { url = "https://files.pythonhosted.org/packages/1d/ce/d8acdc8de545de995c339be67711e474c77d643555a9bb74a9334252bd55/watchfiles-1.1.1-cp314-cp314-win32.whl", hash = "sha256:3fa0b59c92278b5a7800d3ee7733da9d096d4aabcfabb9a928918bd276ef9b9b", size = 272078, upload-time = "2025-10-14T15:05:37.63Z" }, + { url = "https://files.pythonhosted.org/packages/c4/c9/a74487f72d0451524be827e8edec251da0cc1fcf111646a511ae752e1a3d/watchfiles-1.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:c2047d0b6cea13b3316bdbafbfa0c4228ae593d995030fda39089d36e64fc03a", size = 287664, upload-time = "2025-10-14T15:05:38.95Z" }, + { url = "https://files.pythonhosted.org/packages/df/b8/8ac000702cdd496cdce998c6f4ee0ca1f15977bba51bdf07d872ebdfc34c/watchfiles-1.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:842178b126593addc05acf6fce960d28bc5fae7afbaa2c6c1b3a7b9460e5be02", size = 277154, upload-time = "2025-10-14T15:05:39.954Z" }, + { url = "https://files.pythonhosted.org/packages/47/a8/e3af2184707c29f0f14b1963c0aace6529f9d1b8582d5b99f31bbf42f59e/watchfiles-1.1.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:88863fbbc1a7312972f1c511f202eb30866370ebb8493aef2812b9ff28156a21", size = 403820, upload-time = "2025-10-14T15:05:40.932Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ec/e47e307c2f4bd75f9f9e8afbe3876679b18e1bcec449beca132a1c5ffb2d/watchfiles-1.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:55c7475190662e202c08c6c0f4d9e345a29367438cf8e8037f3155e10a88d5a5", size = 390510, upload-time = "2025-10-14T15:05:41.945Z" }, + { url = "https://files.pythonhosted.org/packages/d5/a0/ad235642118090f66e7b2f18fd5c42082418404a79205cdfca50b6309c13/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f53fa183d53a1d7a8852277c92b967ae99c2d4dcee2bfacff8868e6e30b15f7", size = 448408, upload-time = "2025-10-14T15:05:43.385Z" }, + { url = "https://files.pythonhosted.org/packages/df/85/97fa10fd5ff3332ae17e7e40e20784e419e28521549780869f1413742e9d/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6aae418a8b323732fa89721d86f39ec8f092fc2af67f4217a2b07fd3e93c6101", size = 458968, upload-time = "2025-10-14T15:05:44.404Z" }, + { url = "https://files.pythonhosted.org/packages/47/c2/9059c2e8966ea5ce678166617a7f75ecba6164375f3b288e50a40dc6d489/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f096076119da54a6080e8920cbdaac3dbee667eb91dcc5e5b78840b87415bd44", size = 488096, upload-time = "2025-10-14T15:05:45.398Z" }, + { url = "https://files.pythonhosted.org/packages/94/44/d90a9ec8ac309bc26db808a13e7bfc0e4e78b6fc051078a554e132e80160/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00485f441d183717038ed2e887a7c868154f216877653121068107b227a2f64c", size = 596040, upload-time = "2025-10-14T15:05:46.502Z" }, + { url = "https://files.pythonhosted.org/packages/95/68/4e3479b20ca305cfc561db3ed207a8a1c745ee32bf24f2026a129d0ddb6e/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a55f3e9e493158d7bfdb60a1165035f1cf7d320914e7b7ea83fe22c6023b58fc", size = 473847, upload-time = "2025-10-14T15:05:47.484Z" }, + { url = "https://files.pythonhosted.org/packages/4f/55/2af26693fd15165c4ff7857e38330e1b61ab8c37d15dc79118cdba115b7a/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c91ed27800188c2ae96d16e3149f199d62f86c7af5f5f4d2c61a3ed8cd3666c", size = 455072, upload-time = "2025-10-14T15:05:48.928Z" }, + { url = "https://files.pythonhosted.org/packages/66/1d/d0d200b10c9311ec25d2273f8aad8c3ef7cc7ea11808022501811208a750/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:311ff15a0bae3714ffb603e6ba6dbfba4065ab60865d15a6ec544133bdb21099", size = 629104, upload-time = "2025-10-14T15:05:49.908Z" }, + { url = "https://files.pythonhosted.org/packages/e3/bd/fa9bb053192491b3867ba07d2343d9f2252e00811567d30ae8d0f78136fe/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a916a2932da8f8ab582f242c065f5c81bed3462849ca79ee357dd9551b0e9b01", size = 622112, upload-time = "2025-10-14T15:05:50.941Z" }, +] + +[[package]] +name = "websockets" +version = "16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/1d/e88022630271f5bd349ed82417136281931e558d628dd52c4d8621b4a0b2/websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", size = 177406, upload-time = "2026-01-10T09:23:12.178Z" }, + { url = "https://files.pythonhosted.org/packages/f2/78/e63be1bf0724eeb4616efb1ae1c9044f7c3953b7957799abb5915bffd38e/websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", size = 175085, upload-time = "2026-01-10T09:23:13.511Z" }, + { url = "https://files.pythonhosted.org/packages/bb/f4/d3c9220d818ee955ae390cf319a7c7a467beceb24f05ee7aaaa2414345ba/websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4", size = 175328, upload-time = "2026-01-10T09:23:14.727Z" }, + { url = "https://files.pythonhosted.org/packages/63/bc/d3e208028de777087e6fb2b122051a6ff7bbcca0d6df9d9c2bf1dd869ae9/websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", size = 185044, upload-time = "2026-01-10T09:23:15.939Z" }, + { url = "https://files.pythonhosted.org/packages/ad/6e/9a0927ac24bd33a0a9af834d89e0abc7cfd8e13bed17a86407a66773cc0e/websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", size = 186279, upload-time = "2026-01-10T09:23:17.148Z" }, + { url = "https://files.pythonhosted.org/packages/b9/ca/bf1c68440d7a868180e11be653c85959502efd3a709323230314fda6e0b3/websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", size = 185711, upload-time = "2026-01-10T09:23:18.372Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f8/fdc34643a989561f217bb477cbc47a3a07212cbda91c0e4389c43c296ebf/websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", size = 184982, upload-time = "2026-01-10T09:23:19.652Z" }, + { url = "https://files.pythonhosted.org/packages/dd/d1/574fa27e233764dbac9c52730d63fcf2823b16f0856b3329fc6268d6ae4f/websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", size = 177915, upload-time = "2026-01-10T09:23:21.458Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f1/ae6b937bf3126b5134ce1f482365fde31a357c784ac51852978768b5eff4/websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", size = 178381, upload-time = "2026-01-10T09:23:22.715Z" }, + { url = "https://files.pythonhosted.org/packages/06/9b/f791d1db48403e1f0a27577a6beb37afae94254a8c6f08be4a23e4930bc0/websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", size = 177737, upload-time = "2026-01-10T09:23:24.523Z" }, + { url = "https://files.pythonhosted.org/packages/bd/40/53ad02341fa33b3ce489023f635367a4ac98b73570102ad2cdd770dacc9a/websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", size = 175268, upload-time = "2026-01-10T09:23:25.781Z" }, + { url = "https://files.pythonhosted.org/packages/74/9b/6158d4e459b984f949dcbbb0c5d270154c7618e11c01029b9bbd1bb4c4f9/websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", size = 175486, upload-time = "2026-01-10T09:23:27.033Z" }, + { url = "https://files.pythonhosted.org/packages/e5/2d/7583b30208b639c8090206f95073646c2c9ffd66f44df967981a64f849ad/websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", size = 185331, upload-time = "2026-01-10T09:23:28.259Z" }, + { url = "https://files.pythonhosted.org/packages/45/b0/cce3784eb519b7b5ad680d14b9673a31ab8dcb7aad8b64d81709d2430aa8/websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", size = 186501, upload-time = "2026-01-10T09:23:29.449Z" }, + { url = "https://files.pythonhosted.org/packages/19/60/b8ebe4c7e89fb5f6cdf080623c9d92789a53636950f7abacfc33fe2b3135/websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", size = 186062, upload-time = "2026-01-10T09:23:31.368Z" }, + { url = "https://files.pythonhosted.org/packages/88/a8/a080593f89b0138b6cba1b28f8df5673b5506f72879322288b031337c0b8/websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", size = 185356, upload-time = "2026-01-10T09:23:32.627Z" }, + { url = "https://files.pythonhosted.org/packages/c2/b6/b9afed2afadddaf5ebb2afa801abf4b0868f42f8539bfe4b071b5266c9fe/websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", size = 178085, upload-time = "2026-01-10T09:23:33.816Z" }, + { url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531, upload-time = "2026-01-10T09:23:35.016Z" }, + { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, +] diff --git a/tools/spacy-pyodide-wheel-builder/.gitignore b/tools/spacy-pyodide-wheel-builder/.gitignore new file mode 100644 index 0000000..01d71cd --- /dev/null +++ b/tools/spacy-pyodide-wheel-builder/.gitignore @@ -0,0 +1 @@ +/wheels diff --git a/tools/spacy-pyodide-wheel-builder/.python-version b/tools/spacy-pyodide-wheel-builder/.python-version new file mode 100644 index 0000000..3e388a4 --- /dev/null +++ b/tools/spacy-pyodide-wheel-builder/.python-version @@ -0,0 +1 @@ +3.13.2 diff --git a/tools/spacy-pyodide-wheel-builder/Makefile b/tools/spacy-pyodide-wheel-builder/Makefile new file mode 100644 index 0000000..82b4d75 --- /dev/null +++ b/tools/spacy-pyodide-wheel-builder/Makefile @@ -0,0 +1,3 @@ +build: + mkdir -p wheels + docker run --rm -v "$(PWD):/workspace" ghcr.io/astral-sh/uv:0.10.7-debian bash /workspace/build-wheels.sh diff --git a/tools/spacy-pyodide-wheel-builder/build-wheels.sh b/tools/spacy-pyodide-wheel-builder/build-wheels.sh new file mode 100644 index 0000000..a3e441a --- /dev/null +++ b/tools/spacy-pyodide-wheel-builder/build-wheels.sh @@ -0,0 +1,52 @@ +# See https://pyodide.org/en/stable/development/building-packages.html + +cd /tmp +apt-get update && apt-get install -y git + +uv run --locked --project /workspace -- pyodide xbuildenv install 0.29.3 + +git clone --depth 1 --branch 5.0.2 https://github.com/emscripten-core/emsdk +cd emsdk +PYODIDE_EMSCRIPTEN_VERSION=$(uv run --locked --project /workspace -- pyodide config get emscripten_version) +./emsdk install ${PYODIDE_EMSCRIPTEN_VERSION} +./emsdk activate ${PYODIDE_EMSCRIPTEN_VERSION} +source emsdk_env.sh + +cd /tmp +git clone --depth 1 --branch release-v3.8.11 https://github.com/explosion/spaCy.git +cd spaCy +uv run --locked --project /workspace -- pyodide build -o /workspace/wheels + +cd /tmp +git clone --depth 1 --branch release-v1.0.15 https://github.com/explosion/murmurhash.git +cd murmurhash +uv run --locked --project /workspace -- pyodide build -o /workspace/wheels + +cd /tmp +git clone --depth 1 --branch release-v2.0.13 https://github.com/explosion/cymem.git +cd cymem +uv run --locked --project /workspace -- pyodide build -o /workspace/wheels + +cd /tmp +git clone --depth 1 --branch release-v3.0.12 https://github.com/explosion/preshed.git +cd preshed +uv run --locked --project /workspace -- pyodide build -o /workspace/wheels + +cd /tmp +git clone --depth 1 --branch release-v8.3.10 https://github.com/explosion/thinc.git +cd thinc +uv run --locked --project /workspace -- pyodide build -o /workspace/wheels + +cd /tmp +git clone --depth 1 --branch release-v1.3.3 https://github.com/explosion/cython-blis.git +cd cython-blis +export BLIS_ARCH=generic +uv run --locked --project /workspace -- pyodide build -o /workspace/wheels + +cd /tmp +git clone --depth 1 --branch release-v2.5.2 https://github.com/explosion/srsly.git +cd srsly +uv run --locked --project /workspace -- pyodide build -o /workspace/wheels + +cd /workspace/wheels +curl -L -O https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.8.0/en_core_web_sm-3.8.0-py3-none-any.whl diff --git a/tools/spacy-pyodide-wheel-builder/pyproject.toml b/tools/spacy-pyodide-wheel-builder/pyproject.toml new file mode 100644 index 0000000..b7d6b4e --- /dev/null +++ b/tools/spacy-pyodide-wheel-builder/pyproject.toml @@ -0,0 +1,7 @@ +[project] +name = "spacy-pyodide-wheel-builder" +version = "0.1.0" +requires-python = ">=3.13.2" +dependencies = [ + "pyodide-build>=0.33.0", +] diff --git a/tools/spacy-pyodide-wheel-builder/uv.lock b/tools/spacy-pyodide-wheel-builder/uv.lock new file mode 100644 index 0000000..7d80d04 --- /dev/null +++ b/tools/spacy-pyodide-wheel-builder/uv.lock @@ -0,0 +1,440 @@ +version = 1 +revision = 3 +requires-python = ">=3.13.2" + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "auditwheel-emscripten" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "leb128" }, + { name = "packaging" }, + { name = "pyodide-cli" }, + { name = "wheel" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/5a/fe0ba9a827d6ccfc33c86169f8cb1488d55e2e6de674d1d4e8589ac13ccd/auditwheel_emscripten-0.2.3.tar.gz", hash = "sha256:022a1526e549f9365aab5ca52402d54756c4ff57f0fd88f56548c329f91740b1", size = 5835948, upload-time = "2026-02-02T14:23:13.633Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/fe/62e23a0c43ec8e57c6a36a2174f783215e444a4705a45028fff7f4ed5896/auditwheel_emscripten-0.2.3-py3-none-any.whl", hash = "sha256:729a03cc28041e48282fac22e15aa5498594412c56a6d16b239b7c2c3dd0ad7b", size = 32773, upload-time = "2026-02-02T14:23:12.452Z" }, +] + +[[package]] +name = "build" +version = "1.2.2.post1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "os_name == 'nt'" }, + { name = "packaging" }, + { name = "pyproject-hooks" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7d/46/aeab111f8e06793e4f0e421fcad593d547fb8313b50990f31681ee2fb1ad/build-1.2.2.post1.tar.gz", hash = "sha256:b36993e92ca9375a219c99e606a122ff365a760a2d4bba0caa09bd5278b608b7", size = 46701, upload-time = "2024-10-06T17:22:25.251Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/c2/80633736cd183ee4a62107413def345f7e6e3c01563dbca1417363cf957e/build-1.2.2.post1-py3-none-any.whl", hash = "sha256:1d61c0887fa860c01971625baae8bdd338e517b836a2f70dd1f7aa3a6b2fc5b5", size = 22950, upload-time = "2024-10-06T17:22:23.299Z" }, +] + +[[package]] +name = "certifi" +version = "2026.2.25" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" }, + { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" }, + { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" }, + { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" }, + { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" }, + { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" }, + { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" }, + { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" }, + { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" }, + { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" }, + { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" }, + { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" }, + { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, + { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, + { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, + { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" }, + { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" }, + { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" }, + { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" }, + { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" }, + { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" }, + { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" }, + { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" }, + { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" }, + { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" }, + { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" }, + { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" }, + { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, + { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, + { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, + { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, +] + +[[package]] +name = "click" +version = "8.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "distlib" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, +] + +[[package]] +name = "filelock" +version = "3.24.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/73/92/a8e2479937ff39185d20dd6a851c1a63e55849e447a55e798cc2e1f49c65/filelock-3.24.3.tar.gz", hash = "sha256:011a5644dc937c22699943ebbfc46e969cdde3e171470a6e40b9533e5a72affa", size = 37935, upload-time = "2026-02-19T00:48:20.543Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9c/0f/5d0c71a1aefeb08efff26272149e07ab922b64f46c63363756224bd6872e/filelock-3.24.3-py3-none-any.whl", hash = "sha256:426e9a4660391f7f8a810d71b0555bce9008b0a1cc342ab1f6947d37639e002d", size = 24331, upload-time = "2026-02-19T00:48:18.465Z" }, +] + +[[package]] +name = "idna" +version = "3.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, +] + +[[package]] +name = "leb128" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a4/3c/ef6fe994b4b45d84187fea994f124173d587f4f8ab0641693ef269e80f56/leb128-1.0.9.tar.gz", hash = "sha256:8f8b0e2216ba8a318e2897360d9a34d88e2c968656fcb7c7bbb1aef31010f1c6", size = 26710, upload-time = "2026-01-09T08:29:39.261Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/f6/62cd379fe8527c6d685013ebed11c85fd4fced125bde9b3c80ebd5759850/leb128-1.0.9-py2.py3-none-any.whl", hash = "sha256:fef16ef20aca33dfdd2f4841d8004ec4acb7ed8545b63a7bc1183292c9a3e594", size = 3700, upload-time = "2026-01-09T08:29:37.446Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + +[[package]] +name = "packaging" +version = "26.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.9.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/04/fea538adf7dbbd6d186f551d595961e564a3b6715bdf276b477460858672/platformdirs-4.9.2.tar.gz", hash = "sha256:9a33809944b9db043ad67ca0db94b14bf452cc6aeaac46a88ea55b26e2e9d291", size = 28394, upload-time = "2026-02-16T03:56:10.574Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/31/05e764397056194206169869b50cf2fee4dbbbc71b344705b9c0d878d4d8/platformdirs-4.9.2-py3-none-any.whl", hash = "sha256:9170634f126f8efdae22fb58ae8a0eaa86f38365bc57897a6c4f781d1f5875bd", size = 21168, upload-time = "2026-02-16T03:56:08.891Z" }, +] + +[[package]] +name = "pydantic" +version = "2.12.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.41.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, + { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, + { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, + { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, + { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, + { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, + { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, + { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, + { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, + { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, + { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, + { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, + { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, + { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, + { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, + { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, + { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, + { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, + { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, + { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, + { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, + { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, + { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, + { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, + { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, + { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, + { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, + { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, + { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, + { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, + { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, + { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, + { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, + { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, + { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, + { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, + { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, + { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, + { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, + { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + +[[package]] +name = "pyodide-build" +version = "0.33.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "auditwheel-emscripten" }, + { name = "build" }, + { name = "click" }, + { name = "packaging" }, + { name = "platformdirs" }, + { name = "pydantic" }, + { name = "pyodide-cli" }, + { name = "pyodide-lock" }, + { name = "requests" }, + { name = "rich" }, + { name = "ruamel-yaml" }, + { name = "virtualenv" }, + { name = "wheel" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/87/68/9419c577d2c30df0a75552f72bd4af1423998b42944f582ad27db069d361/pyodide_build-0.33.0.tar.gz", hash = "sha256:e2c695942adab40b57a99c285e0b5b6c9579f66d5257dbb81220c35cd28d1eeb", size = 118775, upload-time = "2026-02-26T06:30:05.467Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/80/b8/efe014703b49dcdd8366f3e9fb61c35195ee2879d679e6fa1a960aca41b7/pyodide_build-0.33.0-py3-none-any.whl", hash = "sha256:1dce1ec1af3a0c8f8f5b09c48c77a5a0f95136d22b82537ddbca605d3f7fc13a", size = 121280, upload-time = "2026-02-26T06:30:03.709Z" }, +] + +[[package]] +name = "pyodide-cli" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "rich" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f1/44/fe74049e3e776bd56fae8bd9370900d6d6dc5e541299ae90cb2330a9a8a4/pyodide_cli-0.5.0.tar.gz", hash = "sha256:14df0797e791558b6976d4612f9df9619334f8914bfe3efbdd22e3886f5275b7", size = 12630, upload-time = "2026-01-28T03:29:06.623Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/07/76977bbd73c686df55fbc3685c6c336158dc11b2ba155abe3e3a802447b2/pyodide_cli-0.5.0-py3-none-any.whl", hash = "sha256:24c02736f1d6c99396f5e9330fd30bd98db88cf49ef059b62b025fa1f5f6e29c", size = 12684, upload-time = "2026-01-28T03:29:05.527Z" }, +] + +[[package]] +name = "pyodide-lock" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/51/7d/f6cda69ebf4af81a4389d522be998a503dfe1880173a843abd3e97f3d2d2/pyodide_lock-0.1.2.tar.gz", hash = "sha256:5c438045833b40f937b3017e8ca6dceae4ec1122eb845ccac00f5ae234db9a4c", size = 54230, upload-time = "2026-02-27T05:21:39.308Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/2a/2e695f75e699824156dc84f3f267344fb4225ec305a5fa111f8a0c3574df/pyodide_lock-0.1.2-py3-none-any.whl", hash = "sha256:439c4a56b5361d3c7c368f5613eccdc0839e0f5d78899962176e4ab533eed2a3", size = 15457, upload-time = "2026-02-27T05:21:38.079Z" }, +] + +[[package]] +name = "pyproject-hooks" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/82/28175b2414effca1cdac8dc99f76d660e7a4fb0ceefa4b4ab8f5f6742925/pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", size = 19228, upload-time = "2024-09-29T09:24:13.293Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913", size = 10216, upload-time = "2024-09-29T09:24:11.978Z" }, +] + +[[package]] +name = "python-discovery" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/82/bb/93a3e83bdf9322c7e21cafd092e56a4a17c4d8ef4277b6eb01af1a540a6f/python_discovery-1.1.0.tar.gz", hash = "sha256:447941ba1aed8cc2ab7ee3cb91be5fc137c5bdbb05b7e6ea62fbdcb66e50b268", size = 55674, upload-time = "2026-02-26T09:42:49.668Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/54/82a6e2ef37f0f23dccac604b9585bdcbd0698604feb64807dcb72853693e/python_discovery-1.1.0-py3-none-any.whl", hash = "sha256:a162893b8809727f54594a99ad2179d2ede4bf953e12d4c7abc3cc9cdbd1437b", size = 30687, upload-time = "2026-02-26T09:42:48.548Z" }, +] + +[[package]] +name = "requests" +version = "2.32.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, +] + +[[package]] +name = "rich" +version = "14.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b", size = 230582, upload-time = "2026-02-19T17:23:12.474Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458, upload-time = "2026-02-19T17:23:13.732Z" }, +] + +[[package]] +name = "ruamel-yaml" +version = "0.19.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/3b/ebda527b56beb90cb7652cb1c7e4f91f48649fbcd8d2eb2fb6e77cd3329b/ruamel_yaml-0.19.1.tar.gz", hash = "sha256:53eb66cd27849eff968ebf8f0bf61f46cdac2da1d1f3576dd4ccee9b25c31993", size = 142709, upload-time = "2026-01-02T16:50:31.84Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/0c/51f6841f1d84f404f92463fc2b1ba0da357ca1e3db6b7fbda26956c3b82a/ruamel_yaml-0.19.1-py3-none-any.whl", hash = "sha256:27592957fedf6e0b62f281e96effd28043345e0e66001f97683aa9a40c667c93", size = 118102, upload-time = "2026-01-02T16:50:29.201Z" }, +] + +[[package]] +name = "spacy-pyodide-wheel-builder" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "pyodide-build" }, +] + +[package.metadata] +requires-dist = [{ name = "pyodide-build", specifier = ">=0.33.0" }] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, +] + +[[package]] +name = "urllib3" +version = "2.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, +] + +[[package]] +name = "virtualenv" +version = "21.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, + { name = "python-discovery" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2f/c9/18d4b36606d6091844daa3bd93cf7dc78e6f5da21d9f21d06c221104b684/virtualenv-21.1.0.tar.gz", hash = "sha256:1990a0188c8f16b6b9cf65c9183049007375b26aad415514d377ccacf1e4fb44", size = 5840471, upload-time = "2026-02-27T08:49:29.702Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/55/896b06bf93a49bec0f4ae2a6f1ed12bd05c8860744ac3a70eda041064e4d/virtualenv-21.1.0-py3-none-any.whl", hash = "sha256:164f5e14c5587d170cf98e60378eb91ea35bf037be313811905d3a24ea33cc07", size = 5825072, upload-time = "2026-02-27T08:49:27.516Z" }, +] + +[[package]] +name = "wheel" +version = "0.46.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/89/24/a2eb353a6edac9a0303977c4cb048134959dd2a51b48a269dfc9dde00c8a/wheel-0.46.3.tar.gz", hash = "sha256:e3e79874b07d776c40bd6033f8ddf76a7dad46a7b8aa1b2787a83083519a1803", size = 60605, upload-time = "2026-01-22T12:39:49.136Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/22/b76d483683216dde3d67cba61fb2444be8d5be289bf628c13fc0fd90e5f9/wheel-0.46.3-py3-none-any.whl", hash = "sha256:4b399d56c9d9338230118d705d9737a2a468ccca63d5e813e2a4fc7815d8bc4d", size = 30557, upload-time = "2026-01-22T12:39:48.099Z" }, +]