update
This commit is contained in:
parent
d40106445e
commit
013af75cb6
26
package-lock.json
generated
26
package-lock.json
generated
@ -82,6 +82,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz",
|
||||||
"integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==",
|
"integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ampproject/remapping": "^2.2.0",
|
"@ampproject/remapping": "^2.2.0",
|
||||||
"@babel/code-frame": "^7.26.2",
|
"@babel/code-frame": "^7.26.2",
|
||||||
@ -722,6 +723,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.26.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.26.0.tgz",
|
||||||
"integrity": "sha512-B+O2DnPc0iG+YXFqOxv2WNuNU97ToWjOomUQ78DouOENWUaM5sVrmet9mcomUGQFwpJd//gvUagXBSdzO1fRKg==",
|
"integrity": "sha512-B+O2DnPc0iG+YXFqOxv2WNuNU97ToWjOomUQ78DouOENWUaM5sVrmet9mcomUGQFwpJd//gvUagXBSdzO1fRKg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-plugin-utils": "^7.25.9"
|
"@babel/helper-plugin-utils": "^7.25.9"
|
||||||
},
|
},
|
||||||
@ -1586,6 +1588,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz",
|
||||||
"integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==",
|
"integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-annotate-as-pure": "^7.25.9",
|
"@babel/helper-annotate-as-pure": "^7.25.9",
|
||||||
"@babel/helper-module-imports": "^7.25.9",
|
"@babel/helper-module-imports": "^7.25.9",
|
||||||
@ -3106,6 +3109,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||||
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
|
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
"url": "https://opencollective.com/popperjs"
|
"url": "https://opencollective.com/popperjs"
|
||||||
@ -3542,6 +3546,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz",
|
||||||
"integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==",
|
"integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.10.4",
|
"@babel/code-frame": "^7.10.4",
|
||||||
"@babel/runtime": "^7.12.5",
|
"@babel/runtime": "^7.12.5",
|
||||||
@ -3997,6 +4002,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.2.tgz",
|
||||||
"integrity": "sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==",
|
"integrity": "sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
}
|
}
|
||||||
@ -4123,6 +4129,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz",
|
||||||
"integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==",
|
"integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/regexpp": "^4.4.0",
|
"@eslint-community/regexpp": "^4.4.0",
|
||||||
"@typescript-eslint/scope-manager": "5.62.0",
|
"@typescript-eslint/scope-manager": "5.62.0",
|
||||||
@ -4176,6 +4183,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz",
|
||||||
"integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==",
|
"integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==",
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "5.62.0",
|
"@typescript-eslint/scope-manager": "5.62.0",
|
||||||
"@typescript-eslint/types": "5.62.0",
|
"@typescript-eslint/types": "5.62.0",
|
||||||
@ -4545,6 +4553,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
|
||||||
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
|
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
@ -4631,6 +4640,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-deep-equal": "^3.1.1",
|
"fast-deep-equal": "^3.1.1",
|
||||||
"fast-json-stable-stringify": "^2.0.0",
|
"fast-json-stable-stringify": "^2.0.0",
|
||||||
@ -5547,6 +5557,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"caniuse-lite": "^1.0.30001688",
|
"caniuse-lite": "^1.0.30001688",
|
||||||
"electron-to-chromium": "^1.5.73",
|
"electron-to-chromium": "^1.5.73",
|
||||||
@ -6659,6 +6670,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz",
|
"resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz",
|
||||||
"integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==",
|
"integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10"
|
"node": ">=0.10"
|
||||||
}
|
}
|
||||||
@ -7041,6 +7053,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
|
||||||
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
@ -8006,6 +8019,7 @@
|
|||||||
"integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
|
"integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
|
||||||
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
|
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.2.0",
|
"@eslint-community/eslint-utils": "^4.2.0",
|
||||||
"@eslint-community/regexpp": "^4.6.1",
|
"@eslint-community/regexpp": "^4.6.1",
|
||||||
@ -10984,6 +10998,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz",
|
||||||
"integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==",
|
"integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jest/core": "^27.5.1",
|
"@jest/core": "^27.5.1",
|
||||||
"import-local": "^3.0.2",
|
"import-local": "^3.0.2",
|
||||||
@ -14192,6 +14207,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.8",
|
"nanoid": "^3.3.8",
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.1.1",
|
||||||
@ -15379,6 +15395,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
|
||||||
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
|
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cssesc": "^3.0.0",
|
"cssesc": "^3.0.0",
|
||||||
"util-deprecate": "^1.0.2"
|
"util-deprecate": "^1.0.2"
|
||||||
@ -15776,6 +15793,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
|
||||||
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
|
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@ -15944,6 +15962,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
|
||||||
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
|
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"scheduler": "^0.26.0"
|
"scheduler": "^0.26.0"
|
||||||
},
|
},
|
||||||
@ -16022,6 +16041,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz",
|
||||||
"integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==",
|
"integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@ -16647,6 +16667,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz",
|
||||||
"integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==",
|
"integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"rollup": "dist/bin/rollup"
|
"rollup": "dist/bin/rollup"
|
||||||
},
|
},
|
||||||
@ -16907,6 +16928,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
"fast-uri": "^3.0.1",
|
"fast-uri": "^3.0.1",
|
||||||
@ -18587,6 +18609,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
|
||||||
"integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
|
"integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
|
||||||
"license": "(MIT OR CC0-1.0)",
|
"license": "(MIT OR CC0-1.0)",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
},
|
},
|
||||||
@ -19212,6 +19235,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.6.tgz",
|
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.6.tgz",
|
||||||
"integrity": "sha512-TJOLrJ6oeccsGWPl7ujCYuc0pIq2cNsuD6GZDma8i5o5Npvcco/z+NKvZSFsP0/x6SShVb0+X2JK/JHUjKY9dQ==",
|
"integrity": "sha512-TJOLrJ6oeccsGWPl7ujCYuc0pIq2cNsuD6GZDma8i5o5Npvcco/z+NKvZSFsP0/x6SShVb0+X2JK/JHUjKY9dQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/eslint-scope": "^3.7.7",
|
"@types/eslint-scope": "^3.7.7",
|
||||||
"@types/estree": "^1.0.6",
|
"@types/estree": "^1.0.6",
|
||||||
@ -19281,6 +19305,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz",
|
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz",
|
||||||
"integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==",
|
"integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/bonjour": "^3.5.9",
|
"@types/bonjour": "^3.5.9",
|
||||||
"@types/connect-history-api-fallback": "^1.3.5",
|
"@types/connect-history-api-fallback": "^1.3.5",
|
||||||
@ -19693,6 +19718,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
"fast-uri": "^3.0.1",
|
"fast-uri": "^3.0.1",
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import ServerPage from "./projects/server";
|
|||||||
import LanguagestudyPage from "./projects/languageStudy";
|
import LanguagestudyPage from "./projects/languageStudy";
|
||||||
import OnlineCasinoPage from "./finalprojects/onlineCasino";
|
import OnlineCasinoPage from "./finalprojects/onlineCasino";
|
||||||
import WohnungUndFahrgemeinschaftenPage from "./finalprojects/wohnungUndFahrgemeinschaften";
|
import WohnungUndFahrgemeinschaftenPage from "./finalprojects/wohnungUndFahrgemeinschaften";
|
||||||
import KrimiDinnerPage from "./finalprojects/krimiDinner";
|
import KrimiDinnerPage from "./projects/krimiDinner";
|
||||||
|
|
||||||
function Home() {
|
function Home() {
|
||||||
return <HomePage />;
|
return <HomePage />;
|
||||||
@ -75,7 +75,7 @@ function App() {
|
|||||||
<Route path="/projects/server" element={<Server />} />
|
<Route path="/projects/server" element={<Server />} />
|
||||||
<Route path="/finalprojects/onlineCasino" element={<OnlineCasino />} />
|
<Route path="/finalprojects/onlineCasino" element={<OnlineCasino />} />
|
||||||
<Route path="/finalprojects/wufg" element={<WohnungUndFahrgemeinschaften />} />
|
<Route path="/finalprojects/wufg" element={<WohnungUndFahrgemeinschaften />} />
|
||||||
<Route path="/finalprojects/krimidinner" element={<KrimiDinner />} />
|
<Route path="/projects/crime-dinner" element={<KrimiDinner />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
<FooterSection />
|
<FooterSection />
|
||||||
|
|||||||
@ -1,307 +0,0 @@
|
|||||||
import React, { useEffect } from "react";
|
|
||||||
import { Container, Row, Col, Card, Badge, Table } from "react-bootstrap";
|
|
||||||
import mermaid from "mermaid";
|
|
||||||
|
|
||||||
function KrimiDinner() {
|
|
||||||
useEffect(() => {
|
|
||||||
mermaid.initialize({ startOnLoad: false, theme: 'neutral' });
|
|
||||||
mermaid.contentLoaded();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="project-page">
|
|
||||||
<Container>
|
|
||||||
<header className="text-center border-b pb-4 mb-5">
|
|
||||||
<h1 className="project-title">Projekt-Blaupause: KI-Krimi-Dinner "Projekt Chimera"</h1>
|
|
||||||
<div className="mt-4 d-flex justify-content-center flex-wrap">
|
|
||||||
<div className="project-meta-item">
|
|
||||||
<Badge bg="secondary" className="text-uppercase">Version</Badge>
|
|
||||||
<span className="meta-value">3.0 (Final & Complete UI)</span>
|
|
||||||
</div>
|
|
||||||
<div className="project-meta-item">
|
|
||||||
<Badge bg="secondary" className="text-uppercase">Autor</Badge>
|
|
||||||
<span className="meta-value">D. Schuhbaum & S. Altschäffl</span>
|
|
||||||
</div>
|
|
||||||
<div className="project-meta-item">
|
|
||||||
<Badge bg="secondary" className="text-uppercase">Datum</Badge>
|
|
||||||
<span className="meta-value">03.10.2025</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<section className="mb-5">
|
|
||||||
<h2>1. Projektübersicht</h2>
|
|
||||||
<p>
|
|
||||||
"Projekt Chimera" ist ein hybrides Krimi-Dinner-Erlebnis, das
|
|
||||||
physische soziale Interaktion mit einer digitalen App-Komponente
|
|
||||||
verbindet. Das Alleinstellungsmerkmal ist der Einsatz einer
|
|
||||||
zweistufigen KI-Architektur, die nicht nur einzigartige narrative
|
|
||||||
Szenarien generiert, sondern diese auch proaktiv auf logische
|
|
||||||
Konsistenz validiert und korrigiert.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Die Architektur basiert auf einer klaren Trennung:{" "}
|
|
||||||
<strong>Supabase</strong> dient als dedizierter Service für die
|
|
||||||
Benutzerauthentifizierung, während alle Spieldaten (Szenarien,
|
|
||||||
Spieler, Spielstände) in einer{" "}
|
|
||||||
<strong>separaten, dedizierten PostgreSQL-Datenbank</strong>{" "}
|
|
||||||
gehostet werden. Ein zentrales <strong>FastAPI-Backend</strong>{" "}
|
|
||||||
fungiert als sichere Schnittstelle zwischen den Clients und
|
|
||||||
beiden Diensten.
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="mb-5">
|
|
||||||
<h2>2. Narratives Design & Validierung</h2>
|
|
||||||
<p>
|
|
||||||
Die Generierung jedes Spiels folgt einem strengen, zweistufigen
|
|
||||||
Prozess, um die narrative Qualität und logische Integrität zu
|
|
||||||
gewährleisten.
|
|
||||||
</p>
|
|
||||||
<Row className="mt-4">
|
|
||||||
<Col md={6}>
|
|
||||||
<Card className="h-100">
|
|
||||||
<Card.Body>
|
|
||||||
<Card.Title as="h3">Schritt 1: Kreative Generierung (Der "Autor")</Card.Title>
|
|
||||||
<Card.Text>
|
|
||||||
In dieser Phase agiert die KI als kreativer Autor. Sie erhält einen umfassenden Prompt, der die gewünschten Rahmenbedingungen vorgibt.
|
|
||||||
</Card.Text>
|
|
||||||
<ul>
|
|
||||||
<li>Hintergrundgeschichte</li>
|
|
||||||
<li>Charaktere</li>
|
|
||||||
<li>Geheimnisse & Ziele</li>
|
|
||||||
<li>Der Mord & lückenloser Zeitstrahl</li>
|
|
||||||
<li>Beziehungsgeflecht</li>
|
|
||||||
<li>Hinweise</li>
|
|
||||||
</ul>
|
|
||||||
</Card.Body>
|
|
||||||
</Card>
|
|
||||||
</Col>
|
|
||||||
<Col md={6}>
|
|
||||||
<Card className="h-100">
|
|
||||||
<Card.Body>
|
|
||||||
<Card.Title as="h3">Schritt 2: Logische Validierung & Korrektur (Der "Logik-Lektor")</Card.Title>
|
|
||||||
<Card.Text>
|
|
||||||
Der rohe Output wird an eine zweite KI-Instanz übergeben, die als akribischer Logik-Lektor agiert und das Szenario auf Fehler überprüft.
|
|
||||||
</Card.Text>
|
|
||||||
<ul>
|
|
||||||
<li>Widersprüche in Zeitlinien</li>
|
|
||||||
<li>Motiv-Konsistenz</li>
|
|
||||||
<li>Hinweis-Validität</li>
|
|
||||||
<li>Charakter-Konsistenz</li>
|
|
||||||
<li>Regelkonformität</li>
|
|
||||||
</ul>
|
|
||||||
</Card.Body>
|
|
||||||
</Card>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="mb-5">
|
|
||||||
<h2>3. Technische Architektur</h2>
|
|
||||||
<p>
|
|
||||||
Die Architektur ist auf maximale Kontrolle und Skalierbarkeit ausgelegt. Die React Native-Clients kommunizieren ausschließlich mit dem FastAPI-Backend. Echtzeit-Updates werden über eine WebSocket-Verbindung gepusht.
|
|
||||||
</p>
|
|
||||||
<div className="text-center bg-light p-3 rounded">
|
|
||||||
<pre className="mermaid">
|
|
||||||
{`
|
|
||||||
graph TD
|
|
||||||
subgraph "Spieler-Geräte"
|
|
||||||
P1["React Native Apps"]
|
|
||||||
end
|
|
||||||
subgraph "Authentifizierung"
|
|
||||||
Auth["Supabase Auth"]
|
|
||||||
end
|
|
||||||
subgraph "Sicheres Backend"
|
|
||||||
FastAPI["FastAPI Server <br> REST & WebSockets"]
|
|
||||||
end
|
|
||||||
subgraph "Spieldaten-Bank"
|
|
||||||
PG_DB["PostgreSQL DB"]
|
|
||||||
end
|
|
||||||
subgraph "Externe Dienste"
|
|
||||||
OAI["OpenAI API"]
|
|
||||||
end
|
|
||||||
P1 -- "1. Login" --> Auth
|
|
||||||
P1 -- "2. API Calls (mit Token)" --> FastAPI
|
|
||||||
FastAPI -- "3. KI Call" --> OAI
|
|
||||||
FastAPI -- "4. DB-Zugriff" --> PG_DB
|
|
||||||
FastAPI -- "5. WebSocket Updates" --> P1
|
|
||||||
`}
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
<h3 className="mt-4">Datenfluss-Beschreibung:</h3>
|
|
||||||
<ol>
|
|
||||||
<li><strong>Authentifizierung:</strong> Benutzer authentifiziert sich bei Supabase Auth und erhält ein JWT.</li>
|
|
||||||
<li><strong>API-Anfragen:</strong> Die App sendet Anfragen mit dem JWT an den FastAPI-Server.</li>
|
|
||||||
<li><strong>KI-Generierung:</strong> FastAPI ruft die OpenAI-API für die Spielerstellung auf.</li>
|
|
||||||
<li><strong>Datenbank-Interaktion:</strong> FastAPI ist die einzige Komponente, die mit der PostgreSQL-DB kommuniziert.</li>
|
|
||||||
<li><strong>Echtzeit-Updates:</strong> Clients bauen eine WebSocket-Verbindung zum FastAPI-Server auf.</li>
|
|
||||||
</ol>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="mb-5">
|
|
||||||
<h2>4. User Interface (UI) Konzept</h2>
|
|
||||||
<p>Die UI-Konzepte werden als klare, moderne Wireframes visualisiert, um den gesamten App-Flow darzustellen.</p>
|
|
||||||
<Row>
|
|
||||||
{[
|
|
||||||
{title: "1. Login", content: "Standard-Login-Formular mit E-Mail und Passwort."},
|
|
||||||
{title: "2. Registrierung", content: "Formular zur Erstellung eines neuen Kontos."},
|
|
||||||
{title: "3. Home Dashboard", content: "Übersicht der Spiele des Nutzers und Optionen, ein neues Spiel zu starten oder einem beizutreten."},
|
|
||||||
{title: "4. Spiel erstellen", content: "Formular zur Definition des Szenarios und der Rundenanzahl."},
|
|
||||||
{title: "5. Host-Lobby", content: "Anzeige des Spiel-Codes und der beigetretenen Spieler."},
|
|
||||||
{title: "6. Spieler-Ansicht (Rolle)", content: "Anzeige der zugewiesenen Rolle, Ziele und Geheimnisse."},
|
|
||||||
{title: "7. Spieler-Ansicht (Hinweise)", content: "Darstellung der pro Runde freigeschalteten Hinweise."},
|
|
||||||
{title: "8. Auflösung", content: "Enthüllung des Mörders und des Abstimmungsergebnisses."},
|
|
||||||
{title: "9. Host Notfall-Dashboard", content: "Steuerungselemente für den Host, um das Spiel zu managen."}
|
|
||||||
].map(item => (
|
|
||||||
<Col md={4} className="mb-4" key={item.title}>
|
|
||||||
<Card>
|
|
||||||
<Card.Header as="h5" className="text-center">{item.title}</Card.Header>
|
|
||||||
<Card.Body>
|
|
||||||
<Card.Text>{item.content}</Card.Text>
|
|
||||||
</Card.Body>
|
|
||||||
</Card>
|
|
||||||
</Col>
|
|
||||||
))}
|
|
||||||
</Row>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="mb-5">
|
|
||||||
<h2>5. API-Endpunkte & Datenbank-Interaktion</h2>
|
|
||||||
<Row>
|
|
||||||
<Col md={6}>
|
|
||||||
<h3>FastAPI-Backend</h3>
|
|
||||||
<ul className="list-group">
|
|
||||||
<li className="list-group-item"><code>POST /game/create</code></li>
|
|
||||||
<li className="list-group-item"><code>POST /game/{'{game_code}'}/join</code></li>
|
|
||||||
<li className="list-group-item"><code>GET /game/{'{game_code}'}/state</code></li>
|
|
||||||
<li className="list-group-item"><code>POST /game/{'{game_code}'}/vote</code></li>
|
|
||||||
<li className="list-group-item"><code>GET /game/{'{game_code}'}/character</code></li>
|
|
||||||
<li className="list-group-item"><code>/ws/{'{game_code}'}</code></li>
|
|
||||||
</ul>
|
|
||||||
</Col>
|
|
||||||
<Col md={6}>
|
|
||||||
<h3>Client-Interaktionen</h3>
|
|
||||||
<ul>
|
|
||||||
<li><strong>Supabase Auth (`supabase-js`):</strong> Nur für Login, Registrierung und Auth-Zustand.</li>
|
|
||||||
<li><strong>API-Client (z.B. Axios):</strong> Für alle HTTP-Anfragen an das FastAPI-Backend.</li>
|
|
||||||
<li><strong>WebSocket-Client:</strong> Für Echtzeit-Verbindung zum Backend.</li>
|
|
||||||
</ul>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="mb-5">
|
|
||||||
<h2>6. Datenmodelle</h2>
|
|
||||||
<p>Die dedizierte PostgreSQL-Datenbank enthält die gesamte Spiellogik.</p>
|
|
||||||
<h3 className="mt-4">Tabelle: `games`</h3>
|
|
||||||
<Table striped bordered hover responsive>
|
|
||||||
<thead>
|
|
||||||
<tr><th>Spalte</th><th>Datentyp</th><th>Beschreibung</th></tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr><td>id</td><td>uuid</td><td>Eindeutige ID für ein Spiel.</td></tr>
|
|
||||||
<tr><td>host_id</td><td>uuid</td><td>Referenz auf die User-ID des Hosts.</td></tr>
|
|
||||||
<tr><td>game_code</td><td>varchar</td><td>Kurzer Code zum Beitreten.</td></tr>
|
|
||||||
<tr><td>status</td><td>varchar</td><td>setup, lobby, active, finished.</td></tr>
|
|
||||||
<tr><td>game_data</td><td>jsonb</td><td>Das komplette KI-generierte Szenario.</td></tr>
|
|
||||||
</tbody>
|
|
||||||
</Table>
|
|
||||||
|
|
||||||
<h3 className="mt-4">Tabelle: `players`</h3>
|
|
||||||
<Table striped bordered hover responsive>
|
|
||||||
<thead>
|
|
||||||
<tr><th>Spalte</th><th>Datentyp</th><th>Beschreibung</th></tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr><td>id</td><td>uuid</td><td>Eindeutiger Eintrag für einen Spieler.</td></tr>
|
|
||||||
<tr><td>game_id</td><td>uuid</td><td>Verweist auf das zugehörige Spiel.</td></tr>
|
|
||||||
<tr><td>player_id</td><td>uuid</td><td>Referenz auf die User-ID aus Supabase.</td></tr>
|
|
||||||
<tr><td>player_name</td><td>varchar</td><td>Der im Spiel angezeigte Name.</td></tr>
|
|
||||||
<tr><td>character_id</td><td>varchar</td><td>Verknüpft den Spieler mit seiner Rolle.</td></tr>
|
|
||||||
</tbody>
|
|
||||||
</Table>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="mb-5">
|
|
||||||
<h2>7. Prompt-Engineering-Strategie</h2>
|
|
||||||
<Row>
|
|
||||||
<Col md={6}>
|
|
||||||
<Card className="h-100">
|
|
||||||
<Card.Body>
|
|
||||||
<Card.Title>Prompt 1: Der kreative Autor</Card.Title>
|
|
||||||
<Card.Subtitle className="mb-2 text-muted">System-Prompt:</Card.Subtitle>
|
|
||||||
<Card.Text as="div"><pre className="prompt-box">Du bist ein preisgekrönter Autor von Kriminalromanen...</pre></Card.Text>
|
|
||||||
<Card.Subtitle className="mb-2 mt-3 text-muted">User-Prompt:</Card.Subtitle>
|
|
||||||
<Card.Text as="div"><pre className="prompt-box">Erstelle ein Krimi-Dinner-Szenario für 5 Spieler...</pre></Card.Text>
|
|
||||||
</Card.Body>
|
|
||||||
</Card>
|
|
||||||
</Col>
|
|
||||||
<Col md={6}>
|
|
||||||
<Card className="h-100">
|
|
||||||
<Card.Body>
|
|
||||||
<Card.Title>Prompt 2: Der Logik-Lektor</Card.Title>
|
|
||||||
<Card.Subtitle className="mb-2 text-muted">System-Prompt:</Card.Subtitle>
|
|
||||||
<Card.Text as="div"><pre className="prompt-box">Du bist ein extrem detailorientierter und logisch denkender Lektor...</pre></Card.Text>
|
|
||||||
<Card.Subtitle className="mb-2 mt-3 text-muted">User-Prompt:</Card.Subtitle>
|
|
||||||
<Card.Text as="div"><pre className="prompt-box">Hier ist ein JSON-Objekt. Bitte agiere als Logik-Lektor...</pre></Card.Text>
|
|
||||||
</Card.Body>
|
|
||||||
</Card>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="mb-5">
|
|
||||||
<h2>8. User Journey & Spielablauf</h2>
|
|
||||||
<ol>
|
|
||||||
<li><strong>Phase 0: Generierung & Vorbereitung:</strong> Host generiert Spiel und erhält Einladungscode.</li>
|
|
||||||
<li><strong>Phase 1: Lobby:</strong> Spieler treten bei, Host startet das Spiel.</li>
|
|
||||||
<li><strong>Phase 2: Rollenverteilung:</strong> Backend weist Rollen zu und sendet geheime Infos.</li>
|
|
||||||
<li><strong>Phase 3: Spielrunden:</strong> System gibt pro Runde neue Hinweise frei.</li>
|
|
||||||
<li><strong>Phase 4: Abstimmung & Auflösung:</strong> Abstimmung wird freigeschaltet, Mörder wird enthüllt.</li>
|
|
||||||
</ol>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="mb-5">
|
|
||||||
<h2>9. Geschäftsmodell & Monetarisierung</h2>
|
|
||||||
<Row>
|
|
||||||
<Col md={6}>
|
|
||||||
<div className="p-3 bg-light rounded h-100">
|
|
||||||
<h3>Abonnement-Modell</h3>
|
|
||||||
<p>Nutzer zahlen einen monatlichen oder jährlichen Beitrag für ein festes Kontingent an Spielen.</p>
|
|
||||||
</div>
|
|
||||||
</Col>
|
|
||||||
<Col md={6}>
|
|
||||||
<div className="p-3 bg-light rounded h-100">
|
|
||||||
<h3>Einmalkauf (Pay-per-Game)</h3>
|
|
||||||
<p>Alternativ können Nutzer einzelne Spiele per Einmalkauf freischalten.</p>
|
|
||||||
</div>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="mb-5">
|
|
||||||
<h2>10. Skalierbarkeit, Kosten & Fehlerbehandlung</h2>
|
|
||||||
<ul>
|
|
||||||
<li><strong>Kostenkontrolle (OpenAI):</strong> Rate-Limiting auf dem Endpunkt zur Spielerstellung.</li>
|
|
||||||
<li><strong>Fehlerbehandlung bei Generierung:</strong> Backend unternimmt automatische Wiederholungsversuche.</li>
|
|
||||||
<li><strong>Nahtloses Wiederverbinden:</strong> Spieler können jederzeit wieder in ein laufendes Spiel einsteigen.</li>
|
|
||||||
<li><strong>Notfall-Dashboard (Host):</strong> Spoiler-freies Admin-Panel für den Host zur Spielsteuerung.</li>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="mb-5">
|
|
||||||
<h2>11. Rechtliches & Datenschutz</h2>
|
|
||||||
<ul>
|
|
||||||
<li><strong>DSGVO-konforme Datenschutzerklärung</strong></li>
|
|
||||||
<li><strong>Impressum</strong></li>
|
|
||||||
<li><strong>Möglichkeit zur Löschung des Accounts</strong></li>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
</Container>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default KrimiDinner;
|
|
||||||
582
src/components/projects/krimiDinner.jsx
Normal file
582
src/components/projects/krimiDinner.jsx
Normal file
@ -0,0 +1,582 @@
|
|||||||
|
import React, { useEffect } from "react";
|
||||||
|
import { Container, Row, Col, Card, Badge, Table, Button, Form, Nav } from "react-bootstrap";
|
||||||
|
import mermaid from "mermaid";
|
||||||
|
|
||||||
|
const Wireframe = ({ title, children }) => (
|
||||||
|
<Col md={6} lg={4} className="mb-4">
|
||||||
|
<Card className="h-100 shadow-sm">
|
||||||
|
<Card.Header as="h5" className="text-center bg-light">{title}</Card.Header>
|
||||||
|
<Card.Body className="d-flex justify-content-center align-items-center p-2">
|
||||||
|
<div className="bg-white w-100 h-100 border rounded-3 shadow-inner p-3 d-flex flex-column" style={{ aspectRatio: '9 / 19.5' }}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</Card.Body>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
function KrimiDinner() {
|
||||||
|
useEffect(() => {
|
||||||
|
mermaid.initialize({ startOnLoad: false, theme: 'neutral' });
|
||||||
|
try {
|
||||||
|
mermaid.run();
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Mermaid run error:", e);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="project-page">
|
||||||
|
<Container>
|
||||||
|
<header className="text-center border-bottom pb-4 mb-5">
|
||||||
|
<h1 className="project-title">Projekt-Blaupause: KI-Krimi-Dinner "Projekt Chimera"</h1>
|
||||||
|
<div className="mt-4 d-flex justify-content-center flex-wrap">
|
||||||
|
<div className="project-meta-item mx-2">
|
||||||
|
<Badge bg="secondary" className="text-uppercase">Version</Badge>
|
||||||
|
<span className="meta-value ms-2">3.0 (Complete UI)</span>
|
||||||
|
</div>
|
||||||
|
<div className="project-meta-item mx-2">
|
||||||
|
<Badge bg="secondary" className="text-uppercase">Autor</Badge>
|
||||||
|
<span className="meta-value ms-2">D. Schuhbaum & S. Altschäffl</span>
|
||||||
|
</div>
|
||||||
|
<div className="project-meta-item mx-2">
|
||||||
|
<Badge bg="secondary" className="text-uppercase">Datum</Badge>
|
||||||
|
<span className="meta-value ms-2">03.10.2025</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section className="mb-5">
|
||||||
|
<h2>1. Projektübersicht</h2>
|
||||||
|
<p>
|
||||||
|
"Projekt Chimera" ist ein hybrides Krimi-Dinner-Erlebnis, das
|
||||||
|
physische soziale Interaktion mit einer digitalen App-Komponente
|
||||||
|
verbindet. Das Alleinstellungsmerkmal ist der Einsatz einer
|
||||||
|
zweistufigen KI-Architektur, die nicht nur einzigartige narrative
|
||||||
|
Szenarien generiert, sondern diese auch proaktiv auf logische
|
||||||
|
Konsistenz validiert und korrigiert.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Die Architektur basiert auf einer klaren Trennung:{" "}
|
||||||
|
<strong>Supabase</strong> dient als dedizierter Service für die
|
||||||
|
Benutzerauthentifizierung, während alle Spieldaten (Szenarien,
|
||||||
|
Spieler, Spielstände) in einer{" "}
|
||||||
|
<strong>separaten, dedizierten PostgreSQL-Datenbank</strong>{" "}
|
||||||
|
gehostet werden. Ein zentrales <strong>FastAPI-Backend</strong>{" "}
|
||||||
|
fungiert als sichere Schnittstelle zwischen den Clients und
|
||||||
|
beiden Diensten.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-5">
|
||||||
|
<h2>2. Narratives Design & Validierung</h2>
|
||||||
|
<p>
|
||||||
|
Die Generierung jedes Spiels folgt einem strengen, zweistufigen
|
||||||
|
Prozess, um die narrative Qualität und logische Integrität zu
|
||||||
|
gewährleisten.
|
||||||
|
</p>
|
||||||
|
<Row className="mt-4">
|
||||||
|
<Col md={6}>
|
||||||
|
<Card className="h-100">
|
||||||
|
<Card.Body>
|
||||||
|
<Card.Title as="h3">Schritt 1: Kreative Generierung (Der "Autor")</Card.Title>
|
||||||
|
<ul>
|
||||||
|
<li>Hintergrundgeschichte</li>
|
||||||
|
<li>Charaktere</li>
|
||||||
|
<li>Geheimnisse & Ziele</li>
|
||||||
|
<li>Der Mord & lückenloser Zeitstrahl</li>
|
||||||
|
<li>Beziehungsgeflecht</li>
|
||||||
|
<li>Hinweise</li>
|
||||||
|
</ul>
|
||||||
|
</Card.Body>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
<Col md={6}>
|
||||||
|
<Card className="h-100">
|
||||||
|
<Card.Body>
|
||||||
|
<Card.Title as="h3">Schritt 2: Logische Validierung & Korrektur (Der "Logik-Lektor")</Card.Title>
|
||||||
|
<ul>
|
||||||
|
<li>Widersprüche in Zeitlinien</li>
|
||||||
|
<li>Motiv-Konsistenz</li>
|
||||||
|
<li>Hinweis-Validität</li>
|
||||||
|
<li>Charakter-Konsistenz</li>
|
||||||
|
<li>Regelkonformität</li>
|
||||||
|
</ul>
|
||||||
|
</Card.Body>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-5">
|
||||||
|
<h2>3. Technische Architektur</h2>
|
||||||
|
<p>
|
||||||
|
Die Architektur ist auf maximale Kontrolle und Skalierbarkeit ausgelegt. Die React Native-Clients kommunizieren ausschließlich mit dem FastAPI-Backend. Echtzeit-Updates werden über eine WebSocket-Verbindung gepusht.
|
||||||
|
</p>
|
||||||
|
<div className="text-center bg-light p-3 rounded">
|
||||||
|
<pre className="mermaid">
|
||||||
|
{`
|
||||||
|
graph TD
|
||||||
|
subgraph "Spieler-Geräte"
|
||||||
|
P1["React Native Apps"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Authentifizierung"
|
||||||
|
Auth["Supabase Auth"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Sicheres Backend"
|
||||||
|
FastAPI["FastAPI Server <br> REST & WebSockets"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Spieldaten-Bank"
|
||||||
|
PG_DB["PostgreSQL DB"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Externe Dienste"
|
||||||
|
OAI["OpenAI API"]
|
||||||
|
end
|
||||||
|
|
||||||
|
%% Verbindungen
|
||||||
|
P1 -- "1. Login" --> Auth
|
||||||
|
P1 -- "2. API Calls (mit Token)" --> FastAPI
|
||||||
|
|
||||||
|
FastAPI -- "3. KI Call" --> OAI
|
||||||
|
FastAPI -- "4. DB-Zugriff" --> PG_DB
|
||||||
|
|
||||||
|
FastAPI -- "5. WebSocket Updates" --> P1
|
||||||
|
`}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
<h3 className="mt-4">Datenfluss-Beschreibung:</h3>
|
||||||
|
<ol>
|
||||||
|
<li><strong>Authentifizierung:</strong> Benutzer authentifiziert sich bei Supabase Auth und erhält ein JWT.</li>
|
||||||
|
<li><strong>API-Anfragen:</strong> Die App sendet Anfragen mit dem JWT an den FastAPI-Server.</li>
|
||||||
|
<li><strong>KI-Generierung:</strong> FastAPI ruft die OpenAI-API für die Spielerstellung auf.</li>
|
||||||
|
<li><strong>Datenbank-Interaktion:</strong> FastAPI ist die einzige Komponente, die mit der PostgreSQL-DB kommuniziert.</li>
|
||||||
|
<li><strong>Echtzeit-Updates:</strong> Clients bauen eine WebSocket-Verbindung zum FastAPI-Server auf.</li>
|
||||||
|
</ol>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-5">
|
||||||
|
<h2>4. User Interface (UI) Konzept</h2>
|
||||||
|
<p>Die UI-Konzepte werden als klare, moderne Wireframes visualisiert, um den gesamten App-Flow darzustellen.</p>
|
||||||
|
<Row>
|
||||||
|
<Wireframe title="1. Login">
|
||||||
|
<div className="text-center pt-4 pb-3">
|
||||||
|
<h4 className="fw-bold fs-5">PROJEKT CHIMERA</h4>
|
||||||
|
</div>
|
||||||
|
<div className="px-3 flex-grow-1 d-flex flex-column">
|
||||||
|
<Form.Control type="text" placeholder="E-Mail-Adresse" className="mb-2" />
|
||||||
|
<Form.Control type="password" placeholder="Passwort" className="mb-3" />
|
||||||
|
<Button variant="primary" className="w-100 fw-semibold">Anmelden</Button>
|
||||||
|
<p className="text-center small text-muted mt-2">Noch kein Konto? <a href="#">Jetzt registrieren</a></p>
|
||||||
|
</div>
|
||||||
|
</Wireframe>
|
||||||
|
<Wireframe title="2. Registrierung">
|
||||||
|
<div className="text-center pt-4 pb-3">
|
||||||
|
<h4 className="fw-bold fs-5">Konto erstellen</h4>
|
||||||
|
</div>
|
||||||
|
<div className="px-3 flex-grow-1 d-flex flex-column">
|
||||||
|
<Form.Control type="text" placeholder="E-Mail-Adresse" className="mb-2" />
|
||||||
|
<Form.Control type="password" placeholder="Passwort" className="mb-2" />
|
||||||
|
<Form.Control type="password" placeholder="Passwort bestätigen" className="mb-3" />
|
||||||
|
<Button variant="primary" className="w-100 fw-semibold">Registrieren</Button>
|
||||||
|
<p className="text-center small text-muted mt-2">Bereits ein Konto? <a href="#">Zum Login</a></p>
|
||||||
|
</div>
|
||||||
|
</Wireframe>
|
||||||
|
<Wireframe title="3. Home Dashboard">
|
||||||
|
<div className="p-2 d-flex justify-content-between align-items-center border-bottom">
|
||||||
|
<h4 className="fw-bold fs-6 mb-0">Deine Spiele</h4>
|
||||||
|
<Button variant="link" size="sm">Logout</Button>
|
||||||
|
</div>
|
||||||
|
<div className="p-2 flex-grow-1 overflow-auto">
|
||||||
|
<Card className="mb-2">
|
||||||
|
<Card.Body className="p-2">
|
||||||
|
<p className="fw-bold mb-1">Tech-Konferenz 2035</p>
|
||||||
|
<p className="small text-muted mb-2">Status: <span className="fw-semibold text-success">Aktiv (Runde 2/3)</span></p>
|
||||||
|
<Button variant="primary" size="sm" className="w-100">Weiterspielen</Button>
|
||||||
|
</Card.Body>
|
||||||
|
</Card>
|
||||||
|
<Card>
|
||||||
|
<Card.Body className="p-2">
|
||||||
|
<p className="fw-bold mb-1">Villa Vendetta</p>
|
||||||
|
<p className="small text-muted mb-2">Status: <span className="fw-semibold text-secondary">Lobby (0 Spieler)</span></p>
|
||||||
|
<Button variant="primary" size="sm" className="w-100">Zur Lobby</Button>
|
||||||
|
</Card.Body>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
<div className="p-2 border-top">
|
||||||
|
<Row>
|
||||||
|
<Col><Button variant="success" className="w-100">Neues Spiel</Button></Col>
|
||||||
|
<Col><Button variant="secondary" className="w-100">Beitreten</Button></Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
</Wireframe>
|
||||||
|
<Wireframe title="4. Spiel erstellen (Host)">
|
||||||
|
<div className="text-center pt-3 pb-2 border-bottom">
|
||||||
|
<h4 className="fw-bold fs-6">Neues Spiel erstellen</h4>
|
||||||
|
</div>
|
||||||
|
<div className="px-3 py-3 flex-grow-1">
|
||||||
|
<Form.Label className="fw-semibold small">Szenario-Beschreibung</Form.Label>
|
||||||
|
<Form.Control as="textarea" placeholder="z.B. Ein Mord auf einer Tech-Konferenz im Jahr 2035..." rows={3} className="small mb-3" />
|
||||||
|
<Form.Label className="fw-semibold small">Rundenanzahl</Form.Label>
|
||||||
|
<div className="d-flex align-items-center">
|
||||||
|
<Form.Range min="2" max="5" defaultValue="3" className="w-100" />
|
||||||
|
<span className="fw-bold fs-5 ms-3">3</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-muted small mt-3">Die Spieleranzahl ist flexibel (mind. 3 Spieler) und wird in der Lobby bestimmt.</p>
|
||||||
|
</div>
|
||||||
|
<div className="px-3 pb-3">
|
||||||
|
<Button variant="primary" className="w-100 fw-semibold">Szenario generieren & Lobby öffnen</Button>
|
||||||
|
</div>
|
||||||
|
</Wireframe>
|
||||||
|
<Wireframe title="5. Spiel beitreten (Spieler)">
|
||||||
|
<div className="text-center pt-4 pb-3">
|
||||||
|
<h4 className="fw-bold fs-5">Spiel beitreten</h4>
|
||||||
|
</div>
|
||||||
|
<div className="px-3 flex-grow-1 d-flex flex-column justify-content-center">
|
||||||
|
<Form.Label htmlFor="game-code-input" className="fw-semibold small text-center">Gib den Spiel-Code ein</Form.Label>
|
||||||
|
<Form.Control id="game-code-input" type="text" placeholder="ABC-123" className="mb-3 text-center fs-4" style={{letterSpacing: '0.2rem'}} />
|
||||||
|
<Button variant="primary" className="w-100 fw-semibold">Lobby beitreten</Button>
|
||||||
|
</div>
|
||||||
|
</Wireframe>
|
||||||
|
<Wireframe title="6. Host-Lobby">
|
||||||
|
<div className="text-center pt-3 pb-2 border-bottom">
|
||||||
|
<p className="small text-muted mb-0">Spiel-Lobby</p>
|
||||||
|
<h4 className="fw-bold fs-6">Tech-Konferenz 2035</h4>
|
||||||
|
</div>
|
||||||
|
<div className="px-3 py-3 text-center flex-grow-1">
|
||||||
|
<p className="small text-muted">Teile diesen Code mit deinen Mitspielern:</p>
|
||||||
|
<div className="my-2 p-3 border border-2 border-dashed rounded-3 text-primary">
|
||||||
|
<span className="fw-bold fs-3" style={{letterSpacing: '0.2rem'}}>GHY-781</span>
|
||||||
|
</div>
|
||||||
|
<h5 className="fw-semibold text-start small mt-3 mb-2">Beigetretene Spieler (3)</h5>
|
||||||
|
<ul className="list-group text-start">
|
||||||
|
<li className="list-group-item d-flex justify-content-between align-items-center p-2">Maria <Button variant="outline-danger" size="sm">X</Button></li>
|
||||||
|
<li className="list-group-item d-flex justify-content-between align-items-center p-2">Jonas <Button variant="outline-danger" size="sm">X</Button></li>
|
||||||
|
<li className="list-group-item d-flex justify-content-between align-items-center p-2">Sandra <Button variant="outline-danger" size="sm">X</Button></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div className="px-3 pb-3">
|
||||||
|
<Button variant="outline-danger" className="w-100 mb-2 btn-sm">Szenario verwerfen & Neu generieren</Button>
|
||||||
|
<Button variant="success" className="w-100 fw-semibold">Spiel starten (3 Spieler)</Button>
|
||||||
|
</div>
|
||||||
|
</Wireframe>
|
||||||
|
<Wireframe title="7. Spieler-Ansicht (Rolle)">
|
||||||
|
<div className="p-2 text-center border-bottom">
|
||||||
|
<p className="small text-muted mb-0">Deine Rolle</p>
|
||||||
|
<h4 className="fw-bold fs-6">Dr. Aris Thorne</h4>
|
||||||
|
</div>
|
||||||
|
<Nav variant="tabs" defaultActiveKey="#rolle" justify>
|
||||||
|
<Nav.Item><Nav.Link href="#rolle" className="pb-1 pt-2 small">Rolle</Nav.Link></Nav.Item>
|
||||||
|
<Nav.Item><Nav.Link href="#hinweise" className="pb-1 pt-2 small text-muted">Hinweise</Nav.Link></Nav.Item>
|
||||||
|
<Nav.Item><Nav.Link href="#charaktere" className="pb-1 pt-2 small text-muted">Charaktere</Nav.Link></Nav.Item>
|
||||||
|
<Nav.Item><Nav.Link href="#zeitstrahl" className="pb-1 pt-2 small text-muted">Zeitstrahl</Nav.Link></Nav.Item>
|
||||||
|
</Nav>
|
||||||
|
<div className="p-3 flex-grow-1 overflow-auto small">
|
||||||
|
<h5 className="fw-bold small">Deine Ziele:</h5>
|
||||||
|
<ul className="list-unstyled small ps-3">
|
||||||
|
<li>- Finde heraus, wer deine Forschung sabotiert hat.</li>
|
||||||
|
<li>- Verhindere, dass dein Geheimnis ans Licht kommt.</li>
|
||||||
|
</ul>
|
||||||
|
<h5 className="fw-bold small pt-2">Deine Geheimnisse:</h5>
|
||||||
|
<p className="small">Du hast vor einem Monat bei einem nicht genehmigten Experiment die Stromversorgung der Station überlastet, was zu einem kritischen Ausfall führte. Nur das Opfer wusste davon.</p>
|
||||||
|
</div>
|
||||||
|
</Wireframe>
|
||||||
|
<Wireframe title="8. Spieler-Ansicht (Hinweise)">
|
||||||
|
<div className="p-2 text-center border-bottom">
|
||||||
|
<p className="small text-muted mb-0">Deine Rolle</p>
|
||||||
|
<h4 className="fw-bold fs-6">Dr. Aris Thorne</h4>
|
||||||
|
</div>
|
||||||
|
<Nav variant="tabs" defaultActiveKey="#hinweise" justify>
|
||||||
|
<Nav.Item><Nav.Link href="#rolle" className="pb-1 pt-2 small text-muted">Rolle</Nav.Link></Nav.Item>
|
||||||
|
<Nav.Item><Nav.Link href="#hinweise" className="pb-1 pt-2 small position-relative">Hinweise<Badge pill bg="danger" className="position-absolute top-0 start-100 translate-middle" style={{fontSize: '0.5rem'}}> </Badge></Nav.Link></Nav.Item>
|
||||||
|
<Nav.Item><Nav.Link href="#charaktere" className="pb-1 pt-2 small text-muted">Charaktere</Nav.Link></Nav.Item>
|
||||||
|
<Nav.Item><Nav.Link href="#zeitstrahl" className="pb-1 pt-2 small text-muted">Zeitstrahl</Nav.Link></Nav.Item>
|
||||||
|
</Nav>
|
||||||
|
<div className="p-3 flex-grow-1 overflow-auto small space-y-4">
|
||||||
|
<div>
|
||||||
|
<h5 className="fw-bold">Runde 2 (18:00 - 19:00 Uhr)</h5>
|
||||||
|
<div className="alert alert-warning p-2">
|
||||||
|
<p className="fw-semibold small text-warning-emphasis mb-0">HINWEIS #3</p>
|
||||||
|
<p className="small text-warning-emphasis mt-1">Ein zerrissener Brief wurde gefunden. Die Worte "Schulden" und "letzte Warnung" sind lesbar.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="opacity-75">
|
||||||
|
<h5 className="fw-bold">Runde 1 (16:00 - 18:00 Uhr)</h5>
|
||||||
|
<div className="alert alert-secondary p-2">
|
||||||
|
<p className="fw-semibold small mb-0">HINWEIS #1</p>
|
||||||
|
<p className="small mt-1">Das Opfer wurde mit einem schweren Gegenstand erschlagen.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Wireframe>
|
||||||
|
<Wireframe title="9. Spieler-Ansicht (Charaktere)">
|
||||||
|
<div className="p-2 text-center border-bottom">
|
||||||
|
<p className="small text-muted mb-0">Deine Rolle</p>
|
||||||
|
<h4 className="fw-bold fs-6">Dr. Aris Thorne</h4>
|
||||||
|
</div>
|
||||||
|
<Nav variant="tabs" defaultActiveKey="#charaktere" justify>
|
||||||
|
<Nav.Item><Nav.Link href="#rolle" className="pb-1 pt-2 small text-muted">Rolle</Nav.Link></Nav.Item>
|
||||||
|
<Nav.Item><Nav.Link href="#hinweise" className="pb-1 pt-2 small text-muted">Hinweise</Nav.Link></Nav.Item>
|
||||||
|
<Nav.Item><Nav.Link href="#charaktere" className="pb-1 pt-2 small">Charaktere</Nav.Link></Nav.Item>
|
||||||
|
<Nav.Item><Nav.Link href="#zeitstrahl" className="pb-1 pt-2 small text-muted">Zeitstrahl</Nav.Link></Nav.Item>
|
||||||
|
</Nav>
|
||||||
|
<div className="p-3 flex-grow-1 overflow-auto small">
|
||||||
|
<div className="list-group">
|
||||||
|
<div className="list-group-item list-group-item-action p-2">
|
||||||
|
<p className="fw-bold mb-0">Evelyn Reed</p>
|
||||||
|
<p className="small text-muted mb-0">Investorin</p>
|
||||||
|
</div>
|
||||||
|
<div className="list-group-item list-group-item-action p-2">
|
||||||
|
<p className="fw-bold mb-0">Ivan Petrov</p>
|
||||||
|
<p className="small text-muted mb-0">Sicherheitschef</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Wireframe>
|
||||||
|
<Wireframe title="10. Spieler-Ansicht (Zeitstrahl)">
|
||||||
|
<div className="p-2 text-center border-bottom">
|
||||||
|
<p className="small text-muted mb-0">Deine Rolle</p>
|
||||||
|
<h4 className="fw-bold fs-6">Dr. Aris Thorne</h4>
|
||||||
|
</div>
|
||||||
|
<Nav variant="tabs" defaultActiveKey="#zeitstrahl" justify>
|
||||||
|
<Nav.Item><Nav.Link href="#rolle" className="pb-1 pt-2 small text-muted">Rolle</Nav.Link></Nav.Item>
|
||||||
|
<Nav.Item><Nav.Link href="#hinweise" className="pb-1 pt-2 small text-muted">Hinweise</Nav.Link></Nav.Item>
|
||||||
|
<Nav.Item><Nav.Link href="#charaktere" className="pb-1 pt-2 small text-muted">Charaktere</Nav.Link></Nav.Item>
|
||||||
|
<Nav.Item><Nav.Link href="#zeitstrahl" className="pb-1 pt-2 small">Zeitstrahl</Nav.Link></Nav.Item>
|
||||||
|
</Nav>
|
||||||
|
<div className="p-3 flex-grow-1 overflow-auto small">
|
||||||
|
<ul className="list-unstyled">
|
||||||
|
<li className="mb-3">
|
||||||
|
<p className="fw-bold mb-0">16:00 Uhr</p>
|
||||||
|
<p className="small text-muted">Beginn der Konferenz-Keynote.</p>
|
||||||
|
</li>
|
||||||
|
<li className="mb-3">
|
||||||
|
<p className="fw-bold mb-0">17:15 Uhr</p>
|
||||||
|
<p className="small text-muted">Opfer verlässt den Saal für ein Telefonat.</p>
|
||||||
|
</li>
|
||||||
|
<li className="mb-3">
|
||||||
|
<p className="fw-bold mb-0 text-danger">17:30 Uhr - Mordzeitpunkt</p>
|
||||||
|
<p className="small text-muted">Stromausfall im Westflügel.</p>
|
||||||
|
</li>
|
||||||
|
<li className="mb-3">
|
||||||
|
<p className="fw-bold mb-0">17:45 Uhr</p>
|
||||||
|
<p className="small text-muted">Leiche wird im Serverraum gefunden.</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</Wireframe>
|
||||||
|
<Wireframe title="11. Abstimmung">
|
||||||
|
<div className="text-center pt-3 pb-2 border-bottom">
|
||||||
|
<p className="small text-muted mb-0">Runde 3/3</p>
|
||||||
|
<h4 className="fw-bold fs-6">Wer ist der Mörder?</h4>
|
||||||
|
</div>
|
||||||
|
<div className="px-3 py-3 flex-grow-1 overflow-auto">
|
||||||
|
<p className="small text-center text-muted">Wähle den Spieler aus, den du für den Täter hältst.</p>
|
||||||
|
<div className="d-grid gap-2 mt-3">
|
||||||
|
<Button variant="outline-secondary" className="p-3 text-start" disabled>
|
||||||
|
<span className="fw-bold">Dr. Aris Thorne (Du)</span><br/>
|
||||||
|
<span className="small text-muted">KI-Ethiker</span>
|
||||||
|
</Button>
|
||||||
|
<Button variant="outline-secondary" className="p-3 text-start">
|
||||||
|
<span className="fw-bold">Evelyn Reed</span><br/>
|
||||||
|
<span className="small text-muted">Investorin</span>
|
||||||
|
</Button>
|
||||||
|
<Button variant="outline-secondary" className="p-3 text-start">
|
||||||
|
<span className="fw-bold">Ivan Petrov</span><br/>
|
||||||
|
<span className="small text-muted">Sicherheitschef</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="px-3 pb-3">
|
||||||
|
<Button variant="primary" className="w-100 fw-semibold" disabled>Stimme abgeben</Button>
|
||||||
|
</div>
|
||||||
|
</Wireframe>
|
||||||
|
<Wireframe title="12. Auflösung">
|
||||||
|
<div className="text-center pt-3 pb-2 border-bottom bg-danger-subtle">
|
||||||
|
<h4 className="fw-bold fs-6 text-danger-emphasis">Der Mörder war...</h4>
|
||||||
|
<p className="fs-5 fw-bold">Ivan Petrov!</p>
|
||||||
|
</div>
|
||||||
|
<div className="p-3 flex-grow-1 overflow-auto small">
|
||||||
|
<p>Sein Motiv war Rache, da das Opfer ihn erpresst hat. Die Mordwaffe war ein schwerer Schraubenschlüssel aus dem Werkzeugraum.</p>
|
||||||
|
<h5 className="fw-semibold pt-2 small">Abstimmungsergebnis</h5>
|
||||||
|
<ul className="list-unstyled small">
|
||||||
|
<li>Du hast für <strong>Evelyn Reed</strong> gestimmt.</li>
|
||||||
|
<li>Maria hat für <strong>Ivan Petrov</strong> gestimmt. (Korrekt!)</li>
|
||||||
|
<li>Jonas hat für <strong>Ivan Petrov</strong> gestimmt.</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div className="p-3 border-top">
|
||||||
|
<Button variant="primary" className="w-100 fw-semibold btn-sm">Alle Geschichten lesen</Button>
|
||||||
|
</div>
|
||||||
|
</Wireframe>
|
||||||
|
<Wireframe title="13. Host Notfall-Dashboard">
|
||||||
|
<div className="text-center pt-3 pb-2 border-bottom">
|
||||||
|
<h4 className="fw-bold fs-6 text-danger">Host Notfall-Dashboard</h4>
|
||||||
|
<p className="small text-muted">Spoiler-freie Steuerung (im Spiel)</p>
|
||||||
|
</div>
|
||||||
|
<div className="px-3 py-3 flex-grow-1 d-grid gap-3">
|
||||||
|
<div>
|
||||||
|
<h5 className="fw-semibold small">Spiel-Steuerung</h5>
|
||||||
|
<div className="d-grid gap-2">
|
||||||
|
<Button variant="warning" size="sm">Spiel Pausieren</Button>
|
||||||
|
<Button variant="info" size="sm">Nächste Runde erzwingen</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h5 className="fw-semibold small">Experten-Optionen</h5>
|
||||||
|
<Button variant="dark" size="sm" className="w-100">Hinweise neu generieren</Button>
|
||||||
|
<p className="text-muted small mt-1">Achtung: Generiert nur die Hinweise für die AKTUELLE Runde neu.</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h5 className="fw-semibold small text-danger">Gefahrenzone</h5>
|
||||||
|
<Button variant="danger" size="sm" className="w-100">Spiel komplett neu starten</Button>
|
||||||
|
<p className="text-muted small mt-1">Beendet das Spiel für alle und startet die Lobby neu.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Wireframe>
|
||||||
|
</Row>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-5">
|
||||||
|
<h2>5. API-Endpunkte & Datenbank-Interaktion</h2>
|
||||||
|
<Row>
|
||||||
|
<Col md={6}>
|
||||||
|
<h3>FastAPI-Backend</h3>
|
||||||
|
<ul className="list-group">
|
||||||
|
<li className="list-group-item"><code>POST /game/create</code></li>
|
||||||
|
<li className="list-group-item"><code>POST /game/{'{game_code}'}/join</code></li>
|
||||||
|
<li className="list-group-item"><code>GET /game/{'{game_code}'}/state</code></li>
|
||||||
|
<li className="list-group-item"><code>POST /game/{'{game_code}'}/vote</code></li>
|
||||||
|
<li className="list-group-item"><code>GET /game/{'{game_code}'}/character</code></li>
|
||||||
|
<li className="list-group-item"><code>/ws/{'{game_code}'}</code></li>
|
||||||
|
</ul>
|
||||||
|
</Col>
|
||||||
|
<Col md={6}>
|
||||||
|
<h3>Client-Interaktionen</h3>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Supabase Auth (`supabase-js`):</strong> Nur für Login, Registrierung und Auth-Zustand.</li>
|
||||||
|
<li><strong>API-Client (z.B. Axios):</strong> Für alle HTTP-Anfragen an das FastAPI-Backend.</li>
|
||||||
|
<li><strong>WebSocket-Client:</strong> Für Echtzeit-Verbindung zum Backend.</li>
|
||||||
|
</ul>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-5">
|
||||||
|
<h2>6. Datenmodelle</h2>
|
||||||
|
<p>Die dedizierte PostgreSQL-Datenbank enthält die gesamte Spiellogik.</p>
|
||||||
|
<h3 className="mt-4">Tabelle: `games`</h3>
|
||||||
|
<Table striped bordered hover responsive>
|
||||||
|
<thead>
|
||||||
|
<tr><th>Spalte</th><th>Datentyp</th><th>Beschreibung</th></tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td>id</td><td>uuid</td><td>Eindeutige ID für ein Spiel.</td></tr>
|
||||||
|
<tr><td>host_id</td><td>uuid</td><td>Referenz auf die User-ID des Hosts.</td></tr>
|
||||||
|
<tr><td>game_code</td><td>varchar</td><td>Kurzer Code zum Beitreten.</td></tr>
|
||||||
|
<tr><td>status</td><td>varchar</td><td>setup, lobby, active, finished.</td></tr>
|
||||||
|
<tr><td>game_data</td><td>jsonb</td><td>Das komplette KI-generierte Szenario.</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</Table>
|
||||||
|
|
||||||
|
<h3 className="mt-4">Tabelle: `players`</h3>
|
||||||
|
<Table striped bordered hover responsive>
|
||||||
|
<thead>
|
||||||
|
<tr><th>Spalte</th><th>Datentyp</th><th>Beschreibung</th></tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td>id</td><td>uuid</td><td>Eindeutiger Eintrag für einen Spieler.</td></tr>
|
||||||
|
<tr><td>game_id</td><td>uuid</td><td>Verweist auf das zugehörige Spiel.</td></tr>
|
||||||
|
<tr><td>player_id</td><td>uuid</td><td>Referenz auf die User-ID aus Supabase.</td></tr>
|
||||||
|
<tr><td>player_name</td><td>varchar</td><td>Der im Spiel angezeigte Name.</td></tr>
|
||||||
|
<tr><td>character_id</td><td>varchar</td><td>Verknüpft den Spieler mit seiner Rolle.</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</Table>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-5">
|
||||||
|
<h2>7. Prompt-Engineering-Strategie</h2>
|
||||||
|
<Row>
|
||||||
|
<Col md={6}>
|
||||||
|
<Card className="h-100">
|
||||||
|
<Card.Body>
|
||||||
|
<Card.Title>Prompt 1: Der kreative Autor</Card.Title>
|
||||||
|
<Card.Subtitle className="mb-2 text-muted">System-Prompt:</Card.Subtitle>
|
||||||
|
<Card.Text as="div"><pre className="bg-light p-2 rounded">Du bist ein preisgekrönter Autor von Kriminalromanen...</pre></Card.Text>
|
||||||
|
<Card.Subtitle className="mb-2 mt-3 text-muted">User-Prompt:</Card.Subtitle>
|
||||||
|
<Card.Text as="div"><pre className="bg-light p-2 rounded">Erstelle ein Krimi-Dinner-Szenario für 5 Spieler...</pre></Card.Text>
|
||||||
|
</Card.Body>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
<Col md={6}>
|
||||||
|
<Card className="h-100">
|
||||||
|
<Card.Body>
|
||||||
|
<Card.Title>Prompt 2: Der Logik-Lektor</Card.Title>
|
||||||
|
<Card.Subtitle className="mb-2 text-muted">System-Prompt:</Card.Subtitle>
|
||||||
|
<Card.Text as="div"><pre className="bg-light p-2 rounded">Du bist ein extrem detailorientierter und logisch denkender Lektor...</pre></Card.Text>
|
||||||
|
<Card.Subtitle className="mb-2 mt-3 text-muted">User-Prompt:</Card.Subtitle>
|
||||||
|
<Card.Text as="div"><pre className="bg-light p-2 rounded">Hier ist ein JSON-Objekt. Bitte agiere als Logik-Lektor...</pre></Card.Text>
|
||||||
|
</Card.Body>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-5">
|
||||||
|
<h2>8. User Journey & Spielablauf</h2>
|
||||||
|
<ol>
|
||||||
|
<li><strong>Phase 0: Generierung & Vorbereitung:</strong> Host generiert Spiel und erhält Einladungscode.</li>
|
||||||
|
<li><strong>Phase 1: Lobby:</strong> Spieler treten bei, Host startet das Spiel.</li>
|
||||||
|
<li><strong>Phase 2: Rollenverteilung:</strong> Backend weist Rollen zu und sendet geheime Infos.</li>
|
||||||
|
<li><strong>Phase 3: Spielrunden:</strong> System gibt pro Runde neue Hinweise frei.</li>
|
||||||
|
<li><strong>Phase 4: Abstimmung & Auflösung:</strong> Abstimmung wird freigeschaltet, Mörder wird enthüllt.</li>
|
||||||
|
</ol>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-5">
|
||||||
|
<h2>9. Geschäftsmodell & Monetarisierung</h2>
|
||||||
|
<Row>
|
||||||
|
<Col md={6}>
|
||||||
|
<div className="p-3 bg-light rounded h-100">
|
||||||
|
<h3>Abonnement-Modell</h3>
|
||||||
|
<p>Nutzer zahlen einen monatlichen oder jährlichen Beitrag für ein festes Kontingent an Spielen.</p>
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
<Col md={6}>
|
||||||
|
<div className="p-3 bg-light rounded h-100">
|
||||||
|
<h3>Einmalkauf (Pay-per-Game)</h3>
|
||||||
|
<p>Alternativ können Nutzer einzelne Spiele per Einmalkauf freischalten.</p>
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-5">
|
||||||
|
<h2>10. Skalierbarkeit, Kosten & Fehlerbehandlung</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Kostenkontrolle (OpenAI):</strong> Rate-Limiting auf dem Endpunkt zur Spielerstellung.</li>
|
||||||
|
<li><strong>Fehlerbehandlung bei Generierung:</strong> Backend unternimmt automatische Wiederholungsversuche.</li>
|
||||||
|
<li><strong>Nahtloses Wiederverbinden:</strong> Spieler können jederzeit wieder in ein laufendes Spiel einsteigen.</li>
|
||||||
|
<li><strong>Notfall-Dashboard (Host):</strong> Spoiler-freies Admin-Panel für den Host zur Spielsteuerung.</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-5">
|
||||||
|
<h2>11. Rechtliches & Datenschutz</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>DSGVO-konforme Datenschutzerklärung</strong></li>
|
||||||
|
<li><strong>Impressum</strong></li>
|
||||||
|
<li><strong>Möglichkeit zur Löschung des Accounts</strong></li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</Container>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default KrimiDinner;
|
||||||
Loading…
Reference in New Issue
Block a user