diff --git a/eslint.config.js b/eslint.config.js index dfc5677..ec86e81 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -20,6 +20,9 @@ export default ts.config( ...globals.browser, ...globals.node } + }, + rules: { + "@typescript-eslint/no-unused-vars": "off" } }, { diff --git a/package-lock.json b/package-lock.json index ce15af0..4da6342 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@sveltejs/vite-plugin-svelte": "^4.0.0", "@tailwindcss/vite": "^4.0.0", "@types/eslint": "^9.6.1", + "@types/node": "^24.0.1", "@typescript-eslint/eslint-plugin": "^8.22.0", "@typescript-eslint/parser": "^8.22.0", "embla-carousel-svelte": "^8.5.2", @@ -32,7 +33,6 @@ "svelte-check": "^4.1.4", "tailwindcss": "^4.0.0", "tslib": "^2.4.1", - "typescript": "^5.5.0", "typescript-eslint": "^8.22.0", "vite": "^5.4.4" } @@ -51,6 +51,34 @@ "node": ">=6.0.0" } }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", @@ -443,9 +471,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", - "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", "dev": true, "license": "MIT", "dependencies": { @@ -1385,6 +1413,42 @@ "vite": "^5.2.0 || ^6" } }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/@types/cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", @@ -1417,22 +1481,32 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/node": { + "version": "24.0.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.1.tgz", + "integrity": "sha512-MX4Zioh39chHlDJbKmEgydJDS3tspMP/lnQC67G3SWsTnb9NeYVWOjkxpOSy4oMfPs4StcWHwBrvUb4ybfnuaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.8.0" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.22.0.tgz", - "integrity": "sha512-4Uta6REnz/xEJMvwf72wdUnC3rr4jAQf5jnTkeRQ9b6soxLxhDEbS/pfMPoJLDfFPNVRdryqWUIV/2GZzDJFZw==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.34.0.tgz", + "integrity": "sha512-QXwAlHlbcAwNlEEMKQS2RCgJsgXrTJdjXT08xEgbPFa2yYQgVjBymxP5DrfrE7X7iodSzd9qBUHUycdyVJTW1w==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.22.0", - "@typescript-eslint/type-utils": "8.22.0", - "@typescript-eslint/utils": "8.22.0", - "@typescript-eslint/visitor-keys": "8.22.0", + "@typescript-eslint/scope-manager": "8.34.0", + "@typescript-eslint/type-utils": "8.34.0", + "@typescript-eslint/utils": "8.34.0", + "@typescript-eslint/visitor-keys": "8.34.0", "graphemer": "^1.4.0", - "ignore": "^5.3.1", + "ignore": "^7.0.0", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.0.0" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1442,22 +1516,32 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "@typescript-eslint/parser": "^8.34.0", "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.22.0.tgz", - "integrity": "sha512-MqtmbdNEdoNxTPzpWiWnqNac54h8JDAmkWtJExBVVnSrSmi9z+sZUt0LfKqk9rjqmKOIeRhO4fHHJ1nQIjduIQ==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.34.0.tgz", + "integrity": "sha512-vxXJV1hVFx3IXz/oy2sICsJukaBrtDEQSBiV48/YIV5KWjX1dO+bcIr/kCPrW6weKXvsaGKFNlwH0v2eYdRRbA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.22.0", - "@typescript-eslint/types": "8.22.0", - "@typescript-eslint/typescript-estree": "8.22.0", - "@typescript-eslint/visitor-keys": "8.22.0", + "@typescript-eslint/scope-manager": "8.34.0", + "@typescript-eslint/types": "8.34.0", + "@typescript-eslint/typescript-estree": "8.34.0", + "@typescript-eslint/visitor-keys": "8.34.0", "debug": "^4.3.4" }, "engines": { @@ -1469,18 +1553,40 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.34.0.tgz", + "integrity": "sha512-iEgDALRf970/B2YExmtPMPF54NenZUf4xpL3wsCRx/lgjz6ul/l13R81ozP/ZNuXfnLCS+oPmG7JIxfdNYKELw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.34.0", + "@typescript-eslint/types": "^8.34.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.22.0.tgz", - "integrity": "sha512-/lwVV0UYgkj7wPSw0o8URy6YI64QmcOdwHuGuxWIYznO6d45ER0wXUbksr9pYdViAofpUCNJx/tAzNukgvaaiQ==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.34.0.tgz", + "integrity": "sha512-9Ac0X8WiLykl0aj1oYQNcLZjHgBojT6cW68yAgZ19letYu+Hxd0rE0veI1XznSSst1X5lwnxhPbVdwjDRIomRw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.22.0", - "@typescript-eslint/visitor-keys": "8.22.0" + "@typescript-eslint/types": "8.34.0", + "@typescript-eslint/visitor-keys": "8.34.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1490,17 +1596,34 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.34.0.tgz", + "integrity": "sha512-+W9VYHKFIzA5cBeooqQxqNriAP0QeQ7xTiDuIOr71hzgffm3EL2hxwWBIIj4GuofIbKxGNarpKqIq6Q6YrShOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.22.0.tgz", - "integrity": "sha512-NzE3aB62fDEaGjaAYZE4LH7I1MUwHooQ98Byq0G0y3kkibPJQIXVUspzlFOmOfHhiDLwKzMlWxaNv+/qcZurJA==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.34.0.tgz", + "integrity": "sha512-n7zSmOcUVhcRYC75W2pnPpbO1iwhJY3NLoHEtbJwJSNlVAZuwqu05zY3f3s2SDWWDSo9FdN5szqc73DCtDObAg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.22.0", - "@typescript-eslint/utils": "8.22.0", + "@typescript-eslint/typescript-estree": "8.34.0", + "@typescript-eslint/utils": "8.34.0", "debug": "^4.3.4", - "ts-api-utils": "^2.0.0" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1511,13 +1634,13 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.22.0.tgz", - "integrity": "sha512-0S4M4baNzp612zwpD4YOieP3VowOARgK2EkN/GBn95hpyF8E2fbMT55sRHWBq+Huaqk3b3XK+rxxlM8sPgGM6A==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.34.0.tgz", + "integrity": "sha512-9V24k/paICYPniajHfJ4cuAWETnt7Ssy+R0Rbcqo5sSFr3QEZ/8TSoUi9XeXVBGXCaLtwTOKSLGcInCAvyZeMA==", "dev": true, "license": "MIT", "engines": { @@ -1529,20 +1652,22 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.22.0.tgz", - "integrity": "sha512-SJX99NAS2ugGOzpyhMza/tX+zDwjvwAtQFLsBo3GQxiGcvaKlqGBkmZ+Y1IdiSi9h4Q0Lr5ey+Cp9CGWNY/F/w==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.34.0.tgz", + "integrity": "sha512-rOi4KZxI7E0+BMqG7emPSK1bB4RICCpF7QD3KCLXn9ZvWoESsOMlHyZPAHyG04ujVplPaHbmEvs34m+wjgtVtg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.22.0", - "@typescript-eslint/visitor-keys": "8.22.0", + "@typescript-eslint/project-service": "8.34.0", + "@typescript-eslint/tsconfig-utils": "8.34.0", + "@typescript-eslint/types": "8.34.0", + "@typescript-eslint/visitor-keys": "8.34.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^2.0.0" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1552,20 +1677,20 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.8.0" + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/utils": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.22.0.tgz", - "integrity": "sha512-T8oc1MbF8L+Bk2msAvCUzjxVB2Z2f+vXYfcucE2wOmYs7ZUwco5Ep0fYZw8quNwOiw9K8GYVL+Kgc2pETNTLOg==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.34.0.tgz", + "integrity": "sha512-8L4tWatGchV9A1cKbjaavS6mwYwp39jql8xUmIIKJdm+qiaeHy5KMKlBrf30akXAWBzn2SqKsNOtSENWUwg7XQ==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.22.0", - "@typescript-eslint/types": "8.22.0", - "@typescript-eslint/typescript-estree": "8.22.0" + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.34.0", + "@typescript-eslint/types": "8.34.0", + "@typescript-eslint/typescript-estree": "8.34.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1576,17 +1701,17 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.22.0.tgz", - "integrity": "sha512-AWpYAXnUgvLNabGTy3uBylkgZoosva/miNd1I8Bz3SjotmQPbVqhO4Cczo8AsZ44XVErEBPr/CRSgaj8sG7g0w==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.34.0.tgz", + "integrity": "sha512-qHV7pW7E85A0x6qyrFn+O+q1k1p3tQCsqIZ1KZ5ESLXY57aTvUd3/a4rdPTeXisvhXn2VQG0VSKUqs8KHF2zcA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/types": "8.34.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -1598,9 +1723,9 @@ } }, "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1643,6 +1768,21 @@ "acorn": ">=8.9.0" } }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1676,6 +1816,15 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1711,9 +1860,9 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1823,6 +1972,15 @@ "node": ">= 0.6" } }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -1906,6 +2064,18 @@ "dev": true, "license": "MIT" }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/embla-carousel": { "version": "8.5.2", "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.5.2.tgz", @@ -2356,9 +2526,9 @@ "license": "MIT" }, "node_modules/fastq": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", - "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "dev": true, "license": "ISC", "dependencies": { @@ -2966,6 +3136,15 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -3468,9 +3647,9 @@ } }, "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, "license": "MIT", "engines": { @@ -3832,9 +4011,9 @@ } }, "node_modules/ts-api-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.0.tgz", - "integrity": "sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", "dev": true, "license": "MIT", "engines": { @@ -3844,6 +4023,52 @@ "typescript": ">=4.8.4" } }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -3870,6 +4095,7 @@ "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -3901,6 +4127,206 @@ "typescript": ">=4.8.4 <5.8.0" } }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.22.0.tgz", + "integrity": "sha512-4Uta6REnz/xEJMvwf72wdUnC3rr4jAQf5jnTkeRQ9b6soxLxhDEbS/pfMPoJLDfFPNVRdryqWUIV/2GZzDJFZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.22.0", + "@typescript-eslint/type-utils": "8.22.0", + "@typescript-eslint/utils": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": { + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.22.0.tgz", + "integrity": "sha512-MqtmbdNEdoNxTPzpWiWnqNac54h8JDAmkWtJExBVVnSrSmi9z+sZUt0LfKqk9rjqmKOIeRhO4fHHJ1nQIjduIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.22.0", + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/typescript-estree": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": { + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.22.0.tgz", + "integrity": "sha512-/lwVV0UYgkj7wPSw0o8URy6YI64QmcOdwHuGuxWIYznO6d45ER0wXUbksr9pYdViAofpUCNJx/tAzNukgvaaiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/type-utils": { + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.22.0.tgz", + "integrity": "sha512-NzE3aB62fDEaGjaAYZE4LH7I1MUwHooQ98Byq0G0y3kkibPJQIXVUspzlFOmOfHhiDLwKzMlWxaNv+/qcZurJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.22.0", + "@typescript-eslint/utils": "8.22.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": { + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.22.0.tgz", + "integrity": "sha512-0S4M4baNzp612zwpD4YOieP3VowOARgK2EkN/GBn95hpyF8E2fbMT55sRHWBq+Huaqk3b3XK+rxxlM8sPgGM6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.22.0.tgz", + "integrity": "sha512-SJX99NAS2ugGOzpyhMza/tX+zDwjvwAtQFLsBo3GQxiGcvaKlqGBkmZ+Y1IdiSi9h4Q0Lr5ey+Cp9CGWNY/F/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.22.0.tgz", + "integrity": "sha512-T8oc1MbF8L+Bk2msAvCUzjxVB2Z2f+vXYfcucE2wOmYs7ZUwco5Ep0fYZw8quNwOiw9K8GYVL+Kgc2pETNTLOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.22.0", + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/typescript-estree": "8.22.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.22.0.tgz", + "integrity": "sha512-AWpYAXnUgvLNabGTy3uBylkgZoosva/miNd1I8Bz3SjotmQPbVqhO4Cczo8AsZ44XVErEBPr/CRSgaj8sG7g0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.22.0", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "dev": true, + "license": "MIT" + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -3918,6 +4344,15 @@ "dev": true, "license": "MIT" }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/vite": { "version": "5.4.14", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.14.tgz", @@ -4043,6 +4478,18 @@ "node": ">= 6" } }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 909c582..311fbc4 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@sveltejs/vite-plugin-svelte": "^4.0.0", "@tailwindcss/vite": "^4.0.0", "@types/eslint": "^9.6.1", + "@types/node": "^24.0.1", "@typescript-eslint/eslint-plugin": "^8.22.0", "@typescript-eslint/parser": "^8.22.0", "embla-carousel-svelte": "^8.5.2", @@ -46,7 +47,6 @@ "svelte-check": "^4.1.4", "tailwindcss": "^4.0.0", "tslib": "^2.4.1", - "typescript": "^5.5.0", "typescript-eslint": "^8.22.0", "vite": "^5.4.4" }, diff --git a/src/lib/analysis.ts b/src/lib/analysis.ts new file mode 100644 index 0000000..dc734c2 --- /dev/null +++ b/src/lib/analysis.ts @@ -0,0 +1,149 @@ +import { Almanac } from "./almanac" + +// find random shortest play sequence and word count +export function getRandomShortestPlay({ countLetters = false }: { countLetters?: boolean } = {}): { + count: number + sequence: number[] +} { + const almanacDoc = Almanac.getDom() + const segmentDivs = Array.from(almanacDoc.querySelectorAll('div[type="segment"]')) + + const shortestPlaySequence: number[] = [] + let shortestPlayLength = 0 + const regEx = countLetters ? /\p{L}/gu : /\p{L}+(?:['’]\p{L}+)*/gu + // Regex explanation: + // \p{L} => matches any kind of letter from any language (Unicode letter) + // + => matches one or more letters (i.e., a whole word) + // (?:['’]\p{L}+)* => allows one or more apostrophes (straight ' or curly ’) within a word + // g flag => global: find all matches, not just the first one + // u flag => unicode: enables full unicode matching (necessary for \p{} to work) + + for (let roll = 1; roll <= 200; roll++) { + let minLength = Infinity + let shortestPips: number[] = [] + + for (let pips = 1; pips <= 6; pips++) { + const segmentId = Almanac.getSegmentId(roll, pips) + const segmentDiv = segmentDivs[segmentId - 1] + const divText = segmentDiv.textContent || "" + const count = (divText.match(regEx) || []).length // count is word count or letter count depending on countLetters + if (count < minLength) { + minLength = count + shortestPips = [pips] + } else if (count === minLength) { + // track multiple pips candidates of one roll that result in the same shortest word count + shortestPips.push(pips) + } + } + + // console.log("Roll", roll, ":", shortestPips) // for debugging + + shortestPlayLength += minLength + // randomly pick one pips candidate + shortestPlaySequence[roll - 1] = shortestPips[Math.floor(Math.random() * shortestPips.length)] + } + + return { + count: shortestPlayLength, + sequence: shortestPlaySequence + } +} + +// find random longest play sequence and word count +export function getRandomLongestPlay({ countLetters = false }: { countLetters?: boolean } = {}): { + count: number + sequence: number[] +} { + const almanacDoc = Almanac.getDom() + const segmentDivs = Array.from(almanacDoc.querySelectorAll('div[type="segment"]')) + + const longestPlaySequence: number[] = [] + let longestPlayLength = 0 + const regEx = countLetters ? /\p{L}/gu : /\p{L}+(?:['’]\p{L}+)*/gu + // Regex explanation: + // \p{L} => matches any kind of letter from any language (Unicode letter) + // + => matches one or more letters (i.e., a whole word) + // (?:['’]\p{L}+)* => allows one or more apostrophes (straight ' or curly ’) within a word + // g flag => global: find all matches, not just the first one + // u flag => unicode: enables full unicode matching (necessary for \p{} to work) + + for (let roll = 1; roll <= 200; roll++) { + let maxLength = -Infinity + let longestPips: number[] = [] + + for (let pips = 1; pips <= 6; pips++) { + const segmentId = Almanac.getSegmentId(roll, pips) + const segmentDiv = segmentDivs[segmentId - 1] + const divText = segmentDiv.textContent || "" + const count = (divText.match(regEx) || []).length + if (count > maxLength) { + maxLength = count + longestPips = [pips] + } else if (count === maxLength) { + // track multiple pips candidates of one roll that result in the same longest word count + longestPips.push(pips) + } + } + + // console.log("Roll", roll, ":", longestPips) // for debugging + + longestPlayLength += maxLength + // randomly pick one pips candidate + longestPlaySequence[roll - 1] = longestPips[Math.floor(Math.random() * longestPips.length)] + } + + return { + count: longestPlayLength, + sequence: longestPlaySequence + } +} + +// function to print both results +export function getRandomLongestAndShortestPlay({ + countLetters = false +}: { countLetters?: boolean } = {}): void { + const shortest = getRandomShortestPlay({ countLetters: countLetters }) + const longest = getRandomLongestPlay({ countLetters: countLetters }) + + let result = {} + + if (countLetters) { + result = { + shortestPlay: { + letterCount: shortest.count, + sequence: shortest.sequence + }, + longestPlay: { + letterCount: longest.count, + sequence: longest.sequence + } + } + } else { + result = { + shortestPlay: { + wordCount: shortest.count, + sequence: shortest.sequence + }, + longestPlay: { + wordCount: longest.count, + sequence: longest.sequence + } + } + } + + console.log(JSON.stringify(result, null, 2)) +} + +export function generateAllSamePipsSequence({ pips = 1 }: { pips?: number } = {}): { + sequence: number[] +} { + return { + sequence: new Array(200).fill(pips) + } +} + +// NOTES +// Ohne List glückt’s keiner Liebe. --> 5 Wörter ✔ +// shortest/longest play: Buchstaben zählen ✔ +// nur 1,2,3 etc. ✔ +// 100 random Stücke für Dracor diff --git a/src/lib/components/Dice3D.svelte b/src/lib/components/Dice3D.svelte index 3710ea1..b0d3d9d 100644 --- a/src/lib/components/Dice3D.svelte +++ b/src/lib/components/Dice3D.svelte @@ -3,9 +3,11 @@ import { locale } from "$lib/stores/locale" let { - isRolling = $bindable() + isRolling = $bindable(), + playMode = $bindable() }: { isRolling?: boolean + playMode: string // to switch between: generate random with dice or random shortest or longest play } = $props() let buttonLabel = $derived($locale === "de" ? "Würfeln" : "Roll the dice") @@ -110,6 +112,7 @@ if (isRolling) return // Prevent multiple clicks during animation isRolling = true + playMode = "dice" attractMode = false // Get a different random face diff --git a/src/lib/components/ShareButton.svelte b/src/lib/components/ShareButton.svelte index 82f1669..dfac61c 100644 --- a/src/lib/components/ShareButton.svelte +++ b/src/lib/components/ShareButton.svelte @@ -11,7 +11,6 @@ let sharingUrl = $derived( window.location.origin + window.location.pathname + "#" + sequence.join("") ) - // function to copy url including sequence to clipboard async function copyUrlToClipboard(event: MouseEvent) { try { diff --git a/src/lib/tei.ts b/src/lib/tei.ts new file mode 100644 index 0000000..f070630 --- /dev/null +++ b/src/lib/tei.ts @@ -0,0 +1,150 @@ +import { Almanac } from "./almanac" + +const TEINS = "http://www.tei-c.org/ns/1.0" + +function generateIdFromSequence(sequence: number[]): string { + return sequence.join("-") // this is a temporary solution +} + +async function fetchTemplate(): Promise { + const res = await fetch("/play-template.xml") + const xmlText = await res.text() + const parser = new DOMParser() + return parser.parseFromString(xmlText, "text/xml") +} + +function getSegment(almanacDoc: Document, roll: number, pips: number): Element | null { + const segmentId = Almanac.getSegmentId(roll, pips).toString() + const segment = almanacDoc.querySelector(`div[type="segment"][n="${segmentId}"]`) + return segment +} + +// in the almanacDoc, find divs corresponding to current segment id described by roll number and pips +function collectSegments(sequence: number[], almanacDoc: Document): DocumentFragment { + const segments = document.createDocumentFragment() + const sceneStartRolls = [3, 4, 13, 18, 27, 28, 29, 57, 61, 96, 112, 114, 131, 145, 170, 184, 191] + let currentSceneDiv: Element | null = null + + sequence.forEach((pips, i) => { + const roll = i + 1 + const segment = getSegment(almanacDoc, roll, pips) + const sceneIndex = sceneStartRolls.indexOf(roll) // if roll number is in sceneStartRolls then it marks a scene start + const sceneNumber = sceneIndex >= 0 ? sceneIndex + 1 : null // if sceneIndex is -1 then current roll is not a scene start and scenenNumber is null + + if (segment) { + if (sceneNumber != null) { + if (currentSceneDiv) segments.appendChild(currentSceneDiv) // finish previous scene + currentSceneDiv = document.createElementNS("http://www.tei-c.org/ns/1.0", "div") // start new scene + currentSceneDiv.setAttribute("type", "scene") + currentSceneDiv.setAttribute("n", sceneNumber.toString()) + } + // append only the children of the div, not the outer
tag + Array.from(segment.childNodes).forEach((child) => { + if (currentSceneDiv) { + currentSceneDiv.appendChild(child.cloneNode(true)) + } else { + // if no current scene yet, append directly + segments.appendChild(child.cloneNode(true)) + } + }) + } + }) + // append the last scene + if (currentSceneDiv) { + segments.appendChild(currentSceneDiv) + } + return segments +} + +export async function createTEIDoc( + sequence: number[] +): Promise<{ doc: string; timestamp: string }> { + const almanacDoc = Almanac.getDom() // parse almanac text XML into dom document + const templateDoc = await fetchTemplate() // get xml file template as dom document + + const segments = collectSegments(sequence, almanacDoc) + + // set titles in tei header and front page + const mainTitle = segments.children[0].textContent + const subTitle = segments.children[1].textContent + templateDoc.querySelector('teiHeader titleStmt > title[type="main"]')!.textContent = mainTitle + templateDoc.querySelector('teiHeader titleStmt > title[type="sub"]')!.textContent = subTitle + templateDoc.querySelector('front > titlePage > docTitle > titlePart[type="main"]')!.textContent = + mainTitle + templateDoc.querySelector('front > titlePage > docTitle > titlePart[type="sub"]')!.textContent = + subTitle + + // add url to generated play + templateDoc.querySelector( + 'teiHeader sourceDesc > bibl[type="edition"] > edition > idno' + )!.textContent = sequence.join("").toString() + + // set sequence as edition in source description + templateDoc.querySelector( + 'teiHeader sourceDesc > bibl[type="digitalSource"] > idno' + )!.textContent = "https://temporal-communities.github.io/999/" + sequence.join("").toString() + + // add timestamp + const date = new Date() + const timestamp = date.toISOString() // keep full ISO 8601 string as enforced by oxygen + templateDoc + .querySelector("teiHeader revisionDesc > listChange > change")! + .setAttribute("when", timestamp) + + // add titles, cast list and set description to front page and remove from segments + const front = templateDoc.querySelector("front") + for (let i = 0; i < 4 && segments.children[i]; i++) { + front?.appendChild(segments.children[i]) + segments.removeChild(segments.children[i]) + } + + // append segments wrapped in scene divs to template's main section + const mainDiv = templateDoc.querySelector('body > div[type="main"]') + mainDiv?.appendChild(segments) + + // convert DOM to XML string + const serializer = new XMLSerializer() + + return { doc: serializer.serializeToString(templateDoc), timestamp: timestamp } +} + +export async function downloadTEIDoc(sequence: number[]) { + const result = await createTEIDoc(sequence) + const xmlString = result.doc + const timestamp = result.timestamp + // create a downloadable file and temporary URL pointing to the file + const file = new File([xmlString], `play-${timestamp}.xml`, { type: "application/xml" }) + const url = URL.createObjectURL(file) + const a = document.createElement("a") + a.href = url + a.download = file.name + a.click() + // window.open(url, "_blank") // open file in new window + setTimeout(() => URL.revokeObjectURL(url), 10000) +} + +// NOTES +// improve and complete download function +// wann endet letzte szene? bevor der vorhang fällt oder danach? oder direkt vor Ende des Vorspiels? + +// 1. Wurf: Titel + Untertitel +// 2. Wurf: Personen +// 3. Wurf: Szene 1 +// 4. Wurf: Szene 2 +// 13. Wurf: Szene 3 // WENN 2 GEWÜRFELT WIRD, FEHLT HEADER FÜR SZENE 3 +// 18. Wurf: Szene 4 +// 27. Wurf: Szene 5 +// 28. Wurf: Szene 6 +// 29. Wurf: Szene 7 +// 57. Wurf: Szene 8 +// 61. Wurf: Szene 9 +// 96. Wurf: Szene 10 +// 112. Wurf: Szene 11 +// 114. Wurf: Szene 12 +// 131. Wurf: Szene 13 +// 145. Wurf: Szene 14 // WENN 2 ODER 6 GEWÜRFELT WIRD, FEHLT HEADER FÜR SZENE 14 +// 170. Wurf: Szene 15 +// 184. Wurf: Szene 16 +// 191. Wurf: Szene 17 (Letzter Auftritt) +// 199. Wurf: (Der Vorhang fällt.) +// 200. Wurf: Ende des Vorspiels. diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 364da72..6fe4ba8 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -9,17 +9,40 @@ import emblaCarouselSvelte from "embla-carousel-svelte" import { onMount } from "svelte" import { fade } from "svelte/transition" + import { downloadTEIDoc } from "$lib/tei" + import { + getRandomShortestPlay, + getRandomLongestPlay, + generateAllSamePipsSequence + } from "$lib/analysis" emblaCarouselSvelte.globalOptions = { loop: true, skipSnaps: true // Allow the carousel to skip scroll snaps if it's dragged vigorously } + type PlayMode = + | "dice" + | "shortestWords" + | "longestWords" + | "shortestLetters" + | "longestLetters" + | "allOne" + | "allTwo" + | "allThree" + | "allFour" + | "allFive" + | "allSix" + + let playMode: PlayMode = $state("dice") // to switch between: generate random with dice or random shortest or longest play + + let sequence: number[] = $state([]) + let isRolling = $state(false) + function toggleLanguage() { $locale = $locale === "de" ? "en" : "de" } - let sequence: number[] = $state([]) const sequenceLength = 200 const sequenceRegex = new RegExp(`^[1-6]{${sequenceLength}}$`) @@ -69,11 +92,51 @@ mainElement?.scrollIntoView({ behavior: "smooth", block: "start" }) } - let isRolling = $state(false) // On change to isRolling, set a new sequence + // update sequence according to playMode $effect(() => { if (isRolling) { - sequence = generateRandomSequence() + switch (playMode) { + case "dice": + sequence = generateRandomSequence() + break + case "shortestWords": + sequence = getRandomShortestPlay().sequence + break + case "longestWords": + sequence = getRandomLongestPlay().sequence + break + case "shortestLetters": + sequence = getRandomShortestPlay({ countLetters: true }).sequence + break + case "longestLetters": + sequence = getRandomLongestPlay({ countLetters: true }).sequence + break + case "allOne": + sequence = generateAllSamePipsSequence({ pips: 1 }).sequence + break + case "allTwo": + sequence = generateAllSamePipsSequence({ pips: 2 }).sequence + break + case "allThree": + sequence = generateAllSamePipsSequence({ pips: 3 }).sequence + break + case "allFour": + sequence = generateAllSamePipsSequence({ pips: 4 }).sequence + break + case "allFive": + sequence = generateAllSamePipsSequence({ pips: 5 }).sequence + break + case "allSix": + sequence = generateAllSamePipsSequence({ pips: 6 }).sequence + break + } + + if (playMode !== "dice") { + setTimeout(() => { + isRolling = false + }, 1000) + } } }) @@ -90,23 +153,237 @@ behavior: "smooth" }) } + + const playGenerationOptions: { mode: PlayMode; labelDe: string; labelEn: string }[] = [ + { + mode: "shortestWords", + labelDe: "Kürzestes (Wörter)", + labelEn: "Shortest (words)" + }, + { + mode: "longestWords", + labelDe: "Längstes (Wörter)", + labelEn: "Longest (words)" + }, + { + mode: "shortestLetters", + labelDe: "Kürzestes (Buchstaben)", + labelEn: "Shortest (letters)" + }, + { + mode: "longestLetters", + labelDe: "Längstes (Buchstaben)", + labelEn: "Longest (letters)" + } + ] + + const pipOptions: { mode: PlayMode; label: string }[] = [ + { mode: "allOne", label: "1" }, + { mode: "allTwo", label: "2" }, + { mode: "allThree", label: "3" }, + { mode: "allFour", label: "4" }, + { mode: "allFive", label: "5" }, + { mode: "allSix", label: "6" } + ] + + // hamburger menu state + let isMenuOpen = $state(false) + + function toggleMenu() { + isMenuOpen = !isMenuOpen + } + + function closeMenu() { + isMenuOpen = false + } + + function handleCloseButtonClick() { + closeMenu() + } + + // Simple escape key handler + function handleEscape(event: KeyboardEvent) { + if (event.key === "Escape" && isMenuOpen) { + closeMenu() + } + } + + // Handle clicks outside the menu + function handleDocumentClick(event: MouseEvent) { + if (!isMenuOpen) return + + const target = event.target as Element + const menuElement = document.querySelector('nav[data-menu="true"]') + const hamburgerButton = document.querySelector('button[data-hamburger="true"]') + + // Check if click is outside menu and hamburger button + if (menuElement && hamburgerButton) { + if (!menuElement.contains(target) && !hamburgerButton.contains(target)) { + closeMenu() + } + } + } + + // Add document click listener when component mounts + $effect(() => { + if (mounted) { + document.addEventListener("click", handleDocumentClick) + return () => { + document.removeEventListener("click", handleDocumentClick) + } + } + }) - +
- + + + + {#if $locale === "de"}

Ein Dramenautomat von 1829 digital aufbereitet

@@ -167,7 +444,7 @@
- +
@@ -232,4 +509,35 @@ .pulse-animation { animation: pulse 2s infinite; } + + /* Hamburger menu styles */ + .hamburger-icon { + position: relative; + width: 42px; + height: 33px; + padding-inline: 0.5em; + } + + .hamburger-line { + display: block; + position: absolute; + width: 100%; + height: 4px; + background-color: var(--color-amber-50); + border-radius: 1px; + transition: all 0.3s ease-in-out; + } + + .hamburger-line:nth-child(1) { + top: 0; + } + + .hamburger-line:nth-child(2) { + top: 50%; + transform: translateY(-50%); + } + + .hamburger-line:nth-child(3) { + bottom: 0; + } diff --git a/static/play-template.xml b/static/play-template.xml new file mode 100644 index 0000000..50ec896 --- /dev/null +++ b/static/play-template.xml @@ -0,0 +1,114 @@ + + + + + + + + + <!-- generated title --> + + + <!-- generated subtitle --> + + + Simplicius + + Georg + Nikolaus + Bärmann + + Q1505487 + 107968169 + + + Du + + + Edited by + Frank Fischer + Viktor J. Illmer + Nele Heindorf + + + + RA5 of EXC2020 »Temporal Communities« at Freie Universität Berlin + https://www.temporal-communities.de/research/digital-communities/ + + + CC0 1.0 + Licence + + + + + + + + »999 und noch etliche [mehr]«: Web app for Georg Nikolaus Bärmann’s Würfelalmanach from 1829 + + + + + +

In the public domain.

+
+ Simplicius (= Georg Nikolaus Bärmann): + Neunhundert neun und neunzig und noch etliche Almanachs-Lustspiele durch den + Würfel. Das ist: Almanach Dramatischer Spiele für die Jahre 1829 bis 1961. Ein Noth- + und Hülfs-Büchlein für alle stehenden, gehenden und verwehenden Bühnen, so wie für + alle Liebhabertheater und Theaterliebhaber Deutschlands. + Zwickau: Gebrüder Schumann + 1829. +
+ + + + + + +
+
+ + + file generated + + +
+ + + + + + + + + + + + + + „– Dans l’art dangereux de rimer et d’écrire, + Il n’est point de degrés du médiocre au pire.“ + + Boileau. + + + + + + +
+ +
+ +
+
+ + + diff --git a/vite.config.ts b/vite.config.ts index 44c44e7..81d6bd5 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -4,5 +4,8 @@ import { defineConfig } from "vite" export default defineConfig({ plugins: [tailwindcss(), sveltekit()], - assetsInclude: "src/lib/assets/**/*.xsl" + assetsInclude: "src/lib/assets/**/*.xsl", + build: { + sourcemap: true // enable sourcemaps for debugging + } })