Spaces:
Runtime error
Runtime error
Commit
β’
fd2aa6b
1
Parent(s):
a44c1b5
new UI
Browse files- components.json +1 -1
- package-lock.json +131 -1
- package.json +3 -1
- src/app/{engines.ts β engine/engines.ts} +1 -1
- src/app/{render.ts β engine/render.ts} +3 -3
- src/app/games/arizona.ts +1 -1
- src/app/games/city.ts +1 -1
- src/app/games/doom.ts +1 -1
- src/app/games/dungeon.ts +1 -1
- src/app/games/enchanters.ts +1 -1
- src/app/games/flamenco.ts +1 -1
- src/app/games/nexus.ts +1 -1
- src/app/games/pharaoh.ts +1 -1
- src/app/games/pirates.ts +1 -1
- src/app/games/tensor.ts +1 -1
- src/app/games/types.ts +2 -2
- src/app/games/vernian.ts +1 -1
- src/app/interface/dialogue/index.tsx +25 -0
- src/app/interface/help/index.tsx +23 -0
- src/{components β app/interface}/inventory/draggable-item.tsx +18 -13
- src/app/interface/inventory/index.tsx +39 -0
- src/app/interface/last-event/index.tsx +21 -0
- src/app/interface/progress/index.tsx +56 -0
- src/{components/misc/progress.tsx β app/interface/progress/progress-bar.tsx} +0 -0
- src/{components β app/interface}/renderer/cartesian-image.tsx +10 -12
- src/{components β app/interface}/renderer/cartesian-video.tsx +3 -7
- src/app/interface/renderer/full-screen-button.tsx +18 -0
- src/{components β app/interface}/renderer/index.tsx +31 -58
- src/{components β app/interface}/renderer/scene-menu.tsx +8 -6
- src/{components β app/interface}/renderer/scene-tooltip.tsx +6 -5
- src/{components β app/interface}/renderer/spherical-image.tsx +2 -2
- src/{components β app/interface}/renderer/types.ts +0 -0
- src/app/interface/top-menu/index.tsx +86 -0
- src/app/main.tsx +60 -91
- src/app/queries/getDialogue.ts +6 -1
- src/app/store.ts +1 -1
- src/components/icons/full-screen.tsx +16 -0
- src/components/inventory/index.tsx +0 -24
- src/components/ui/switch.tsx +2 -2
- src/components/ui/tooltip.tsx +1 -1
- src/{app/types.ts β types.ts} +0 -0
components.json
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
"tailwind": {
|
7 |
"config": "tailwind.config.js",
|
8 |
"css": "app/globals.css",
|
9 |
-
"baseColor": "
|
10 |
"cssVariables": false
|
11 |
},
|
12 |
"aliases": {
|
|
|
6 |
"tailwind": {
|
7 |
"config": "tailwind.config.js",
|
8 |
"css": "app/globals.css",
|
9 |
+
"baseColor": "stone",
|
10 |
"cssVariables": false
|
11 |
},
|
12 |
"aliases": {
|
package-lock.json
CHANGED
@@ -61,6 +61,7 @@
|
|
61 |
"react-dnd-html5-backend": "^16.0.1",
|
62 |
"react-dom": "18.2.0",
|
63 |
"react-photo-sphere-viewer": "^3.3.5-psv5.1.4",
|
|
|
64 |
"styled-components": "^6.0.7",
|
65 |
"tailwind-merge": "^1.13.2",
|
66 |
"tailwindcss": "3.3.3",
|
@@ -73,7 +74,8 @@
|
|
73 |
"zustand": "^4.3.9"
|
74 |
},
|
75 |
"devDependencies": {
|
76 |
-
"@types/qs": "^6.9.7"
|
|
|
77 |
}
|
78 |
},
|
79 |
"node_modules/@aashutoshrathi/word-wrap": {
|
@@ -3815,6 +3817,12 @@
|
|
3815 |
"@types/react": "*"
|
3816 |
}
|
3817 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
3818 |
"node_modules/@types/scheduler": {
|
3819 |
"version": "0.16.3",
|
3820 |
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
|
@@ -4912,6 +4920,14 @@
|
|
4912 |
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
|
4913 |
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="
|
4914 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4915 |
"node_modules/default-browser": {
|
4916 |
"version": "4.0.0",
|
4917 |
"resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz",
|
@@ -5033,6 +5049,57 @@
|
|
5033 |
"node": ">=6.0.0"
|
5034 |
}
|
5035 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5036 |
"node_modules/electron-to-chromium": {
|
5037 |
"version": "1.4.470",
|
5038 |
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.470.tgz",
|
@@ -5055,6 +5122,17 @@
|
|
5055 |
"node": ">=10.13.0"
|
5056 |
}
|
5057 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5058 |
"node_modules/es-abstract": {
|
5059 |
"version": "1.22.1",
|
5060 |
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz",
|
@@ -6074,6 +6152,24 @@
|
|
6074 |
"react-is": "^16.7.0"
|
6075 |
}
|
6076 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6077 |
"node_modules/human-signals": {
|
6078 |
"version": "4.3.1",
|
6079 |
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz",
|
@@ -6330,6 +6426,14 @@
|
|
6330 |
"node": ">=8"
|
6331 |
}
|
6332 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6333 |
"node_modules/is-regex": {
|
6334 |
"version": "1.1.4",
|
6335 |
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
|
@@ -7072,6 +7176,11 @@
|
|
7072 |
"node": ">=6"
|
7073 |
}
|
7074 |
},
|
|
|
|
|
|
|
|
|
|
|
7075 |
"node_modules/path-exists": {
|
7076 |
"version": "4.0.0",
|
7077 |
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
@@ -7832,6 +7941,19 @@
|
|
7832 |
"url": "https://github.com/sponsors/ljharb"
|
7833 |
}
|
7834 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7835 |
"node_modules/sass": {
|
7836 |
"version": "1.64.1",
|
7837 |
"resolved": "https://registry.npmjs.org/sass/-/sass-1.64.1.tgz",
|
@@ -7848,6 +7970,14 @@
|
|
7848 |
"node": ">=14.0.0"
|
7849 |
}
|
7850 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7851 |
"node_modules/scheduler": {
|
7852 |
"version": "0.23.0",
|
7853 |
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
|
|
|
61 |
"react-dnd-html5-backend": "^16.0.1",
|
62 |
"react-dom": "18.2.0",
|
63 |
"react-photo-sphere-viewer": "^3.3.5-psv5.1.4",
|
64 |
+
"sbd": "^1.0.19",
|
65 |
"styled-components": "^6.0.7",
|
66 |
"tailwind-merge": "^1.13.2",
|
67 |
"tailwindcss": "3.3.3",
|
|
|
74 |
"zustand": "^4.3.9"
|
75 |
},
|
76 |
"devDependencies": {
|
77 |
+
"@types/qs": "^6.9.7",
|
78 |
+
"@types/sbd": "^1.0.3"
|
79 |
}
|
80 |
},
|
81 |
"node_modules/@aashutoshrathi/word-wrap": {
|
|
|
3817 |
"@types/react": "*"
|
3818 |
}
|
3819 |
},
|
3820 |
+
"node_modules/@types/sbd": {
|
3821 |
+
"version": "1.0.3",
|
3822 |
+
"resolved": "https://registry.npmjs.org/@types/sbd/-/sbd-1.0.3.tgz",
|
3823 |
+
"integrity": "sha512-4rOX5JsLAEFrzOB0zvV2fG0lqwyNoo/TTYuQna/rjKi+ZHsN7s3BLfx70gsqGD6DM0j6Ha0QfneU/KspzMMeUg==",
|
3824 |
+
"dev": true
|
3825 |
+
},
|
3826 |
"node_modules/@types/scheduler": {
|
3827 |
"version": "0.16.3",
|
3828 |
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
|
|
|
4920 |
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
|
4921 |
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="
|
4922 |
},
|
4923 |
+
"node_modules/deepmerge": {
|
4924 |
+
"version": "4.3.1",
|
4925 |
+
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
|
4926 |
+
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
|
4927 |
+
"engines": {
|
4928 |
+
"node": ">=0.10.0"
|
4929 |
+
}
|
4930 |
+
},
|
4931 |
"node_modules/default-browser": {
|
4932 |
"version": "4.0.0",
|
4933 |
"resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz",
|
|
|
5049 |
"node": ">=6.0.0"
|
5050 |
}
|
5051 |
},
|
5052 |
+
"node_modules/dom-serializer": {
|
5053 |
+
"version": "2.0.0",
|
5054 |
+
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
|
5055 |
+
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
|
5056 |
+
"dependencies": {
|
5057 |
+
"domelementtype": "^2.3.0",
|
5058 |
+
"domhandler": "^5.0.2",
|
5059 |
+
"entities": "^4.2.0"
|
5060 |
+
},
|
5061 |
+
"funding": {
|
5062 |
+
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
|
5063 |
+
}
|
5064 |
+
},
|
5065 |
+
"node_modules/domelementtype": {
|
5066 |
+
"version": "2.3.0",
|
5067 |
+
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
|
5068 |
+
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
|
5069 |
+
"funding": [
|
5070 |
+
{
|
5071 |
+
"type": "github",
|
5072 |
+
"url": "https://github.com/sponsors/fb55"
|
5073 |
+
}
|
5074 |
+
]
|
5075 |
+
},
|
5076 |
+
"node_modules/domhandler": {
|
5077 |
+
"version": "5.0.3",
|
5078 |
+
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
5079 |
+
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
5080 |
+
"dependencies": {
|
5081 |
+
"domelementtype": "^2.3.0"
|
5082 |
+
},
|
5083 |
+
"engines": {
|
5084 |
+
"node": ">= 4"
|
5085 |
+
},
|
5086 |
+
"funding": {
|
5087 |
+
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
5088 |
+
}
|
5089 |
+
},
|
5090 |
+
"node_modules/domutils": {
|
5091 |
+
"version": "3.1.0",
|
5092 |
+
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
|
5093 |
+
"integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
|
5094 |
+
"dependencies": {
|
5095 |
+
"dom-serializer": "^2.0.0",
|
5096 |
+
"domelementtype": "^2.3.0",
|
5097 |
+
"domhandler": "^5.0.3"
|
5098 |
+
},
|
5099 |
+
"funding": {
|
5100 |
+
"url": "https://github.com/fb55/domutils?sponsor=1"
|
5101 |
+
}
|
5102 |
+
},
|
5103 |
"node_modules/electron-to-chromium": {
|
5104 |
"version": "1.4.470",
|
5105 |
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.470.tgz",
|
|
|
5122 |
"node": ">=10.13.0"
|
5123 |
}
|
5124 |
},
|
5125 |
+
"node_modules/entities": {
|
5126 |
+
"version": "4.5.0",
|
5127 |
+
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
5128 |
+
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
5129 |
+
"engines": {
|
5130 |
+
"node": ">=0.12"
|
5131 |
+
},
|
5132 |
+
"funding": {
|
5133 |
+
"url": "https://github.com/fb55/entities?sponsor=1"
|
5134 |
+
}
|
5135 |
+
},
|
5136 |
"node_modules/es-abstract": {
|
5137 |
"version": "1.22.1",
|
5138 |
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz",
|
|
|
6152 |
"react-is": "^16.7.0"
|
6153 |
}
|
6154 |
},
|
6155 |
+
"node_modules/htmlparser2": {
|
6156 |
+
"version": "8.0.2",
|
6157 |
+
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
|
6158 |
+
"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
|
6159 |
+
"funding": [
|
6160 |
+
"https://github.com/fb55/htmlparser2?sponsor=1",
|
6161 |
+
{
|
6162 |
+
"type": "github",
|
6163 |
+
"url": "https://github.com/sponsors/fb55"
|
6164 |
+
}
|
6165 |
+
],
|
6166 |
+
"dependencies": {
|
6167 |
+
"domelementtype": "^2.3.0",
|
6168 |
+
"domhandler": "^5.0.3",
|
6169 |
+
"domutils": "^3.0.1",
|
6170 |
+
"entities": "^4.4.0"
|
6171 |
+
}
|
6172 |
+
},
|
6173 |
"node_modules/human-signals": {
|
6174 |
"version": "4.3.1",
|
6175 |
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz",
|
|
|
6426 |
"node": ">=8"
|
6427 |
}
|
6428 |
},
|
6429 |
+
"node_modules/is-plain-object": {
|
6430 |
+
"version": "5.0.0",
|
6431 |
+
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
|
6432 |
+
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
|
6433 |
+
"engines": {
|
6434 |
+
"node": ">=0.10.0"
|
6435 |
+
}
|
6436 |
+
},
|
6437 |
"node_modules/is-regex": {
|
6438 |
"version": "1.1.4",
|
6439 |
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
|
|
|
7176 |
"node": ">=6"
|
7177 |
}
|
7178 |
},
|
7179 |
+
"node_modules/parse-srcset": {
|
7180 |
+
"version": "1.0.2",
|
7181 |
+
"resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz",
|
7182 |
+
"integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q=="
|
7183 |
+
},
|
7184 |
"node_modules/path-exists": {
|
7185 |
"version": "4.0.0",
|
7186 |
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
|
|
7941 |
"url": "https://github.com/sponsors/ljharb"
|
7942 |
}
|
7943 |
},
|
7944 |
+
"node_modules/sanitize-html": {
|
7945 |
+
"version": "2.11.0",
|
7946 |
+
"resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.11.0.tgz",
|
7947 |
+
"integrity": "sha512-BG68EDHRaGKqlsNjJ2xUB7gpInPA8gVx/mvjO743hZaeMCZ2DwzW7xvsqZ+KNU4QKwj86HJ3uu2liISf2qBBUA==",
|
7948 |
+
"dependencies": {
|
7949 |
+
"deepmerge": "^4.2.2",
|
7950 |
+
"escape-string-regexp": "^4.0.0",
|
7951 |
+
"htmlparser2": "^8.0.0",
|
7952 |
+
"is-plain-object": "^5.0.0",
|
7953 |
+
"parse-srcset": "^1.0.2",
|
7954 |
+
"postcss": "^8.3.11"
|
7955 |
+
}
|
7956 |
+
},
|
7957 |
"node_modules/sass": {
|
7958 |
"version": "1.64.1",
|
7959 |
"resolved": "https://registry.npmjs.org/sass/-/sass-1.64.1.tgz",
|
|
|
7970 |
"node": ">=14.0.0"
|
7971 |
}
|
7972 |
},
|
7973 |
+
"node_modules/sbd": {
|
7974 |
+
"version": "1.0.19",
|
7975 |
+
"resolved": "https://registry.npmjs.org/sbd/-/sbd-1.0.19.tgz",
|
7976 |
+
"integrity": "sha512-b5RyZMGSrFuIB4AHdbv12uYHS8YGEJ36gtuvG3RflbJGY+T0dXmAL0E4vZjQqT2RsX0v+ZwVqhV2zsGr5aFK9w==",
|
7977 |
+
"dependencies": {
|
7978 |
+
"sanitize-html": "^2.3.2"
|
7979 |
+
}
|
7980 |
+
},
|
7981 |
"node_modules/scheduler": {
|
7982 |
"version": "0.23.0",
|
7983 |
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
|
package.json
CHANGED
@@ -62,6 +62,7 @@
|
|
62 |
"react-dnd-html5-backend": "^16.0.1",
|
63 |
"react-dom": "18.2.0",
|
64 |
"react-photo-sphere-viewer": "^3.3.5-psv5.1.4",
|
|
|
65 |
"styled-components": "^6.0.7",
|
66 |
"tailwind-merge": "^1.13.2",
|
67 |
"tailwindcss": "3.3.3",
|
@@ -74,6 +75,7 @@
|
|
74 |
"zustand": "^4.3.9"
|
75 |
},
|
76 |
"devDependencies": {
|
77 |
-
"@types/qs": "^6.9.7"
|
|
|
78 |
}
|
79 |
}
|
|
|
62 |
"react-dnd-html5-backend": "^16.0.1",
|
63 |
"react-dom": "18.2.0",
|
64 |
"react-photo-sphere-viewer": "^3.3.5-psv5.1.4",
|
65 |
+
"sbd": "^1.0.19",
|
66 |
"styled-components": "^6.0.7",
|
67 |
"tailwind-merge": "^1.13.2",
|
68 |
"tailwindcss": "3.3.3",
|
|
|
75 |
"zustand": "^4.3.9"
|
76 |
},
|
77 |
"devDependencies": {
|
78 |
+
"@types/qs": "^6.9.7",
|
79 |
+
"@types/sbd": "^1.0.3"
|
80 |
}
|
81 |
}
|
src/app/{engines.ts β engine/engines.ts}
RENAMED
@@ -71,6 +71,6 @@ export const engines: Record<string, Engine> = {
|
|
71 |
}
|
72 |
}
|
73 |
|
74 |
-
export const defaultEngine: EngineType = "
|
75 |
|
76 |
export const getEngine = (type?: EngineType): Engine => engines[type || defaultEngine] || engines[defaultEngine]
|
|
|
71 |
}
|
72 |
}
|
73 |
|
74 |
+
export const defaultEngine: EngineType = "cartesian_image"
|
75 |
|
76 |
export const getEngine = (type?: EngineType): Engine => engines[type || defaultEngine] || engines[defaultEngine]
|
src/app/{render.ts β engine/render.ts}
RENAMED
@@ -1,8 +1,8 @@
|
|
1 |
"use server"
|
2 |
|
3 |
-
import Gorgon from "@gorgonjs/gorgon"
|
4 |
|
5 |
-
|
|
|
6 |
import { Engine, EngineType } from "./engines"
|
7 |
|
8 |
// note: there is no / at the end in the variable
|
@@ -76,7 +76,7 @@ export async function newRender({
|
|
76 |
actionnables,
|
77 |
segmentation: "firstframe", // one day we will remove this param, to make it automatic
|
78 |
width: isForVideo ? 576 : 1024,
|
79 |
-
height: isForVideo ? 320 :
|
80 |
}),
|
81 |
cache: 'no-store',
|
82 |
// we can also use this (see https://vercel.com/blog/vercel-cache-api-nextjs-cache)
|
|
|
1 |
"use server"
|
2 |
|
|
|
3 |
|
4 |
+
|
5 |
+
import { RenderedScene } from "@/types"
|
6 |
import { Engine, EngineType } from "./engines"
|
7 |
|
8 |
// note: there is no / at the end in the variable
|
|
|
76 |
actionnables,
|
77 |
segmentation: "firstframe", // one day we will remove this param, to make it automatic
|
78 |
width: isForVideo ? 576 : 1024,
|
79 |
+
height: isForVideo ? 320 : 768,
|
80 |
}),
|
81 |
cache: 'no-store',
|
82 |
// we can also use this (see https://vercel.com/blog/vercel-cache-api-nextjs-cache)
|
src/app/games/arizona.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
import { imfell } from "@/lib/fonts"
|
2 |
import { Game } from "./types"
|
3 |
-
import { InventoryItem } from "
|
4 |
|
5 |
const initialSituation = [
|
6 |
`looking at an abandonned mining town street`,
|
|
|
1 |
import { imfell } from "@/lib/fonts"
|
2 |
import { Game } from "./types"
|
3 |
+
import { InventoryItem } from "../../types"
|
4 |
|
5 |
const initialSituation = [
|
6 |
`looking at an abandonned mining town street`,
|
src/app/games/city.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
import { edu } from "@/lib/fonts"
|
2 |
import { Game } from "./types"
|
3 |
-
import { InventoryItem } from "
|
4 |
|
5 |
const actions = [
|
6 |
"busy pedestrians",
|
|
|
1 |
import { edu } from "@/lib/fonts"
|
2 |
import { Game } from "./types"
|
3 |
+
import { InventoryItem } from "../../types"
|
4 |
|
5 |
const actions = [
|
6 |
"busy pedestrians",
|
src/app/games/doom.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
import { orbitron } from "@/lib/fonts"
|
2 |
import { Game } from "./types"
|
3 |
-
import { InventoryItem } from "
|
4 |
|
5 |
const initialSituation = [
|
6 |
`looking at building on Mars, with multiple moons in the sky`,
|
|
|
1 |
import { orbitron } from "@/lib/fonts"
|
2 |
import { Game } from "./types"
|
3 |
+
import { InventoryItem } from "../../types"
|
4 |
|
5 |
const initialSituation = [
|
6 |
`looking at building on Mars, with multiple moons in the sky`,
|
src/app/games/dungeon.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
import { amatic } from "@/lib/fonts"
|
2 |
import { Game } from "./types"
|
3 |
-
import { InventoryItem } from "
|
4 |
|
5 |
const actions = [
|
6 |
"not moving",
|
|
|
1 |
import { amatic } from "@/lib/fonts"
|
2 |
import { Game } from "./types"
|
3 |
+
import { InventoryItem } from "../../types"
|
4 |
|
5 |
const actions = [
|
6 |
"not moving",
|
src/app/games/enchanters.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
import { macondo } from "@/lib/fonts"
|
2 |
import { Game } from "./types"
|
3 |
-
import { InventoryItem } from "
|
4 |
|
5 |
const initialSituation = [
|
6 |
`looking at a beautiful medieval castle on a lake, with a metallic gate, during golden hour, surrounded by mountain, with a flying dragon visible afar`,
|
|
|
1 |
import { macondo } from "@/lib/fonts"
|
2 |
import { Game } from "./types"
|
3 |
+
import { InventoryItem } from "../../types"
|
4 |
|
5 |
const initialSituation = [
|
6 |
`looking at a beautiful medieval castle on a lake, with a metallic gate, during golden hour, surrounded by mountain, with a flying dragon visible afar`,
|
src/app/games/flamenco.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
import { macondo } from "@/lib/fonts"
|
2 |
import { Game } from "./types"
|
3 |
-
import { InventoryItem } from "
|
4 |
|
5 |
const initialSituation = [
|
6 |
`beautiful view of an art deco building in new york`,
|
|
|
1 |
import { macondo } from "@/lib/fonts"
|
2 |
import { Game } from "./types"
|
3 |
+
import { InventoryItem } from "../../types"
|
4 |
|
5 |
const initialSituation = [
|
6 |
`beautiful view of an art deco building in new york`,
|
src/app/games/nexus.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
import { macondo } from "@/lib/fonts"
|
2 |
import { Game } from "./types"
|
3 |
-
import { InventoryItem } from "
|
4 |
|
5 |
const initialSituation = [
|
6 |
`first-person view of a futuristic street`,
|
|
|
1 |
import { macondo } from "@/lib/fonts"
|
2 |
import { Game } from "./types"
|
3 |
+
import { InventoryItem } from "../../types"
|
4 |
|
5 |
const initialSituation = [
|
6 |
`first-person view of a futuristic street`,
|
src/app/games/pharaoh.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
import { macondo } from "@/lib/fonts"
|
2 |
import { Game } from "./types"
|
3 |
-
import { InventoryItem } from "
|
4 |
|
5 |
const initialSituation = [
|
6 |
`looking at a beautiful pyramid, ancient egypt, during golden hour, surrounded by sand dunes, near the Nile`,
|
|
|
1 |
import { macondo } from "@/lib/fonts"
|
2 |
import { Game } from "./types"
|
3 |
+
import { InventoryItem } from "../../types"
|
4 |
|
5 |
const initialSituation = [
|
6 |
`looking at a beautiful pyramid, ancient egypt, during golden hour, surrounded by sand dunes, near the Nile`,
|
src/app/games/pirates.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
import { lugrasimo } from "@/lib/fonts"
|
2 |
import { Game } from "./types"
|
3 |
-
import { InventoryItem } from "
|
4 |
|
5 |
const actions = [
|
6 |
"idling",
|
|
|
1 |
import { lugrasimo } from "@/lib/fonts"
|
2 |
import { Game } from "./types"
|
3 |
+
import { InventoryItem } from "../../types"
|
4 |
|
5 |
const actions = [
|
6 |
"idling",
|
src/app/games/tensor.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
import { macondo } from "@/lib/fonts"
|
2 |
import { Game } from "./types"
|
3 |
-
import { InventoryItem } from "
|
4 |
|
5 |
const initialSituation = [
|
6 |
`in Martin Place, Sydney`,
|
|
|
1 |
import { macondo } from "@/lib/fonts"
|
2 |
import { Game } from "./types"
|
3 |
+
import { InventoryItem } from "../../types"
|
4 |
|
5 |
const initialSituation = [
|
6 |
`in Martin Place, Sydney`,
|
src/app/games/types.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
-
import {
|
2 |
-
import {
|
3 |
|
4 |
export type GameType =
|
5 |
| "pirates"
|
|
|
1 |
+
import { InventoryItem } from "@/types"
|
2 |
+
import { EngineType } from "@/app/engine/engines"
|
3 |
|
4 |
export type GameType =
|
5 |
| "pirates"
|
src/app/games/vernian.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
import { imfell } from "@/lib/fonts"
|
2 |
import { Game } from "./types"
|
3 |
-
import { InventoryItem } from "
|
4 |
|
5 |
const initialSituation = [
|
6 |
`inside a secret workshop inspired by Jules Verne`,
|
|
|
1 |
import { imfell } from "@/lib/fonts"
|
2 |
import { Game } from "./types"
|
3 |
+
import { InventoryItem } from "../../types"
|
4 |
|
5 |
const initialSituation = [
|
6 |
`inside a secret workshop inspired by Jules Verne`,
|
src/app/interface/dialogue/index.tsx
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { cn } from "@/lib/utils"
|
2 |
+
import { ReactNode } from "react"
|
3 |
+
|
4 |
+
export function Dialogue({ children, className = "", isLoading }: {
|
5 |
+
children: ReactNode
|
6 |
+
className?: string
|
7 |
+
isLoading: boolean
|
8 |
+
}) {
|
9 |
+
return (
|
10 |
+
<div
|
11 |
+
className={cn(
|
12 |
+
`fixed left-6 max-w-[60%]`,
|
13 |
+
`transition-all duration-300`,
|
14 |
+
`text-xl rounded-2xl backdrop-blur-xl bg-stone-600/40 p-4`,
|
15 |
+
className
|
16 |
+
)}
|
17 |
+
style={{
|
18 |
+
textShadow: "1px 0px 2px #000000ab"
|
19 |
+
}}>{
|
20 |
+
isLoading
|
21 |
+
? <p>β Generating story, please wait..</p>
|
22 |
+
: children
|
23 |
+
}</div>
|
24 |
+
)
|
25 |
+
}
|
src/app/interface/help/index.tsx
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export function Help({
|
2 |
+
clickables,
|
3 |
+
isLoading,
|
4 |
+
}: {
|
5 |
+
clickables: string[]
|
6 |
+
isLoading: boolean
|
7 |
+
}) {
|
8 |
+
return (
|
9 |
+
<div className="flex flex-row">
|
10 |
+
<div className="text-xl mr-2">
|
11 |
+
{isLoading
|
12 |
+
? <span>β Generating areas for clicks and drag & drop, please wait..</span>
|
13 |
+
: <span>π‘ Try to click on:</span>
|
14 |
+
}
|
15 |
+
</div>
|
16 |
+
{clickables.map((clickable, i) =>
|
17 |
+
<div key={i} className="flex flex-row text-xl mr-2">
|
18 |
+
<div className="">{clickable}</div>
|
19 |
+
{i < (clickables.length - 1) ? <div>,</div> : null}
|
20 |
+
</div>)}
|
21 |
+
</div>
|
22 |
+
)
|
23 |
+
}
|
src/{components β app/interface}/inventory/draggable-item.tsx
RENAMED
@@ -4,13 +4,15 @@ import { useDrag, useDrop } from "react-dnd"
|
|
4 |
|
5 |
import { Game } from "@/app/games/types"
|
6 |
import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip"
|
7 |
-
import { DropZoneTarget, InventoryEvent, InventoryItem, OnInventoryEvent } from "@/
|
8 |
|
9 |
import { store } from "@/app/store"
|
|
|
10 |
|
11 |
-
export function DraggableItem({ game, item, onEvent }: {
|
12 |
game: Game
|
13 |
item: InventoryItem
|
|
|
14 |
onEvent: OnInventoryEvent
|
15 |
}) {
|
16 |
const [{ isDragging }, drag] = useDrag(() => ({
|
@@ -73,28 +75,31 @@ export function DraggableItem({ game, item, onEvent }: {
|
|
73 |
ref={drag}
|
74 |
key={item.name}
|
75 |
onClick={() => onEvent("ClickOnItem", item)}
|
76 |
-
className={
|
77 |
"bg-gray-100 rounded-2xl overflow-hidden",
|
78 |
"transition-all duration-200",
|
|
|
|
|
|
|
79 |
isDragging
|
80 |
-
? "brightness-100 scale-
|
81 |
: "brightness-90 border-2 shadow-md cursor-grab",
|
82 |
isOver && canDrop
|
83 |
? "border-stone-100"
|
84 |
: "border-stone-600",
|
85 |
-
"hover:brightness-100 hover:scale-
|
86 |
-
|
87 |
<div ref={drop}>
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
</div>
|
95 |
</div>
|
96 |
</TooltipTrigger>
|
97 |
-
<TooltipContent>
|
98 |
<p className="text-xl">{item.title}</p>
|
99 |
</TooltipContent>
|
100 |
</Tooltip>
|
|
|
4 |
|
5 |
import { Game } from "@/app/games/types"
|
6 |
import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip"
|
7 |
+
import { DropZoneTarget, InventoryEvent, InventoryItem, OnInventoryEvent } from "@/types"
|
8 |
|
9 |
import { store } from "@/app/store"
|
10 |
+
import { cn } from "@/lib/utils"
|
11 |
|
12 |
+
export function DraggableItem({ game, item, isLoading, onEvent }: {
|
13 |
game: Game
|
14 |
item: InventoryItem
|
15 |
+
isLoading: boolean
|
16 |
onEvent: OnInventoryEvent
|
17 |
}) {
|
18 |
const [{ isDragging }, drag] = useDrag(() => ({
|
|
|
75 |
ref={drag}
|
76 |
key={item.name}
|
77 |
onClick={() => onEvent("ClickOnItem", item)}
|
78 |
+
className={cn(
|
79 |
"bg-gray-100 rounded-2xl overflow-hidden",
|
80 |
"transition-all duration-200",
|
81 |
+
isLoading
|
82 |
+
? `scale-0`
|
83 |
+
: `scale-100`,
|
84 |
isDragging
|
85 |
+
? "brightness-100 scale-110 border border-stone-300 shadow-2xl cursor-grabbing"
|
86 |
: "brightness-90 border-2 shadow-md cursor-grab",
|
87 |
isOver && canDrop
|
88 |
? "border-stone-100"
|
89 |
: "border-stone-600",
|
90 |
+
"hover:brightness-100 hover:scale-110 hover:border hover:border-stone-300 hover:shadow-2xl",
|
91 |
+
)}>
|
92 |
<div ref={drop}>
|
93 |
+
<Image
|
94 |
+
src={`/inventories/${game.type}/${item.name}.jpeg`}
|
95 |
+
width={1024}
|
96 |
+
height={1024}
|
97 |
+
alt={item.title}
|
98 |
+
/>
|
99 |
</div>
|
100 |
</div>
|
101 |
</TooltipTrigger>
|
102 |
+
<TooltipContent side="right" className="translate-x-[5px] translate-y-[-8px]">
|
103 |
<p className="text-xl">{item.title}</p>
|
104 |
</TooltipContent>
|
105 |
</Tooltip>
|
src/app/interface/inventory/index.tsx
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Game } from "@/app/games/types"
|
2 |
+
import { DraggableItem } from "./draggable-item"
|
3 |
+
import { OnInventoryEvent } from "@/types"
|
4 |
+
import { cn } from "@/lib/utils";
|
5 |
+
|
6 |
+
export function Inventory({
|
7 |
+
className = "",
|
8 |
+
game,
|
9 |
+
isLoading,
|
10 |
+
onEvent
|
11 |
+
}: {
|
12 |
+
className?: string;
|
13 |
+
game: Game;
|
14 |
+
isLoading: boolean;
|
15 |
+
onEvent: OnInventoryEvent;
|
16 |
+
}) {
|
17 |
+
return (
|
18 |
+
<div className={cn(
|
19 |
+
`fixed z-20 top-28 left-0 p-6 w-28`,
|
20 |
+
// `w-full bg-stone-500 rounded-xl backdrop-blur-md bg-white/10`
|
21 |
+
className,
|
22 |
+
)}>
|
23 |
+
<div className={cn(
|
24 |
+
`flex flex-col space-y-2`
|
25 |
+
// `w-full grid grid-cols-6 sm:grid-cols-8 md:grid-cols-10 lg:grid-cols-12 gap-4`,
|
26 |
+
)}>
|
27 |
+
{game.inventory.map(item => (
|
28 |
+
<DraggableItem
|
29 |
+
key={item.name}
|
30 |
+
game={game}
|
31 |
+
item={item}
|
32 |
+
isLoading={isLoading}
|
33 |
+
onEvent={onEvent}
|
34 |
+
/>
|
35 |
+
))}
|
36 |
+
</div>
|
37 |
+
</div>
|
38 |
+
)
|
39 |
+
}
|
src/app/interface/last-event/index.tsx
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { cn } from "@/lib/utils"
|
2 |
+
import { ReactNode } from "react"
|
3 |
+
|
4 |
+
export function LastEvent({ children, className = "" }: {
|
5 |
+
children: ReactNode
|
6 |
+
className?: string
|
7 |
+
}) {
|
8 |
+
return (
|
9 |
+
<div className={cn(
|
10 |
+
`fixed top-16 left-6 flex flex-row`,
|
11 |
+
`transition-all duration-300`,
|
12 |
+
`text-lg rounded-full backdrop-blur-md bg-stone-900/10 p-3`,
|
13 |
+
className
|
14 |
+
)}
|
15 |
+
style={{
|
16 |
+
textShadow: "1px 0px 2px #000000ab"
|
17 |
+
}}>
|
18 |
+
<div className="transition-all duration-300 text-xl px-2">{children}</div>
|
19 |
+
</div>
|
20 |
+
)
|
21 |
+
}
|
src/app/interface/progress/index.tsx
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { useEffect, useRef, useState } from "react"
|
2 |
+
|
3 |
+
import { ProgressBar } from "./progress-bar"
|
4 |
+
import { cn } from "@/lib/utils"
|
5 |
+
|
6 |
+
export function Progress({
|
7 |
+
isLoading,
|
8 |
+
resetKey = "", // when this key change, this will re-spawn the progress bar
|
9 |
+
className = "",
|
10 |
+
}: {
|
11 |
+
isLoading: boolean
|
12 |
+
resetKey: string
|
13 |
+
className?: string
|
14 |
+
}) {
|
15 |
+
const timeoutRef = useRef<any>()
|
16 |
+
const [progressPercent, setProcessPercent] = useState(0)
|
17 |
+
const progressRef = useRef(0)
|
18 |
+
const isLoadingRef = useRef(isLoading)
|
19 |
+
|
20 |
+
const updateProgressBar = () => {
|
21 |
+
const duration = 1000 // 1 sec
|
22 |
+
const frequency = 200 // 200ms
|
23 |
+
const nbUpdatesPerSec = duration / frequency // 5x per second
|
24 |
+
|
25 |
+
// normally it takes 45, and we will try to go below,
|
26 |
+
// but to be safe let's set the counter a 1 min
|
27 |
+
const nbSeconds = 32 // 1 min
|
28 |
+
const amountInPercent = 100 / (nbUpdatesPerSec * nbSeconds) // 0.333
|
29 |
+
|
30 |
+
progressRef.current = Math.min(100, progressRef.current + amountInPercent)
|
31 |
+
setProcessPercent(progressRef.current)
|
32 |
+
}
|
33 |
+
|
34 |
+
useEffect(() => {
|
35 |
+
clearInterval(timeoutRef.current)
|
36 |
+
isLoadingRef.current = isLoading
|
37 |
+
progressRef.current = 0
|
38 |
+
setProcessPercent(0)
|
39 |
+
if (isLoading) {
|
40 |
+
timeoutRef.current = setInterval(updateProgressBar, 200)
|
41 |
+
}
|
42 |
+
}, [isLoading, resetKey])
|
43 |
+
|
44 |
+
return (
|
45 |
+
<div className={cn(
|
46 |
+
`fixed flex w-20 h-20 top-16 right-6 z-50`,
|
47 |
+
`animation-all duration-300`,
|
48 |
+
isLoading
|
49 |
+
? `scale-100 opacity-100`
|
50 |
+
: `scale-0 opacity-0`,
|
51 |
+
className
|
52 |
+
)}>
|
53 |
+
<ProgressBar text="β" progressPercentage={progressPercent} />
|
54 |
+
</div>
|
55 |
+
)
|
56 |
+
}
|
src/{components/misc/progress.tsx β app/interface/progress/progress-bar.tsx}
RENAMED
File without changes
|
src/{components β app/interface}/renderer/cartesian-image.tsx
RENAMED
@@ -1,6 +1,9 @@
|
|
1 |
import { useEffect, useRef } from "react"
|
2 |
-
|
3 |
-
import { RenderedScene } from "@/
|
|
|
|
|
|
|
4 |
|
5 |
export function CartesianImage({
|
6 |
rendered,
|
@@ -84,24 +87,19 @@ export function CartesianImage({
|
|
84 |
return null
|
85 |
}
|
86 |
return (
|
87 |
-
|
88 |
-
className={[
|
89 |
-
"h-[512px]",
|
90 |
-
className
|
91 |
-
].join(" ")
|
92 |
-
}
|
93 |
-
>
|
94 |
<img
|
95 |
src={rendered.assetUrl || undefined}
|
96 |
ref={ref}
|
97 |
-
className="
|
98 |
onMouseUp={(event) => handleEvent(event, true)}
|
99 |
onMouseMove={(event) => handleEvent(event, false)}
|
100 |
/>
|
101 |
{debug && <img
|
102 |
src={rendered.maskUrl || undefined}
|
103 |
-
className="
|
104 |
/>}
|
105 |
-
|
|
|
106 |
)
|
107 |
}
|
|
|
1 |
import { useEffect, useRef } from "react"
|
2 |
+
|
3 |
+
import { RenderedScene } from "@/types"
|
4 |
+
import { MouseEventHandler } from "@/app/interface/renderer/types"
|
5 |
+
import { FullScreenIcon } from "@/components/icons/full-screen"
|
6 |
+
import { FullScreenButton } from "@/app/interface/renderer/full-screen-button"
|
7 |
|
8 |
export function CartesianImage({
|
9 |
rendered,
|
|
|
87 |
return null
|
88 |
}
|
89 |
return (
|
90 |
+
<>
|
|
|
|
|
|
|
|
|
|
|
|
|
91 |
<img
|
92 |
src={rendered.assetUrl || undefined}
|
93 |
ref={ref}
|
94 |
+
className="fixed w-screen top-0 left-0 right-0"
|
95 |
onMouseUp={(event) => handleEvent(event, true)}
|
96 |
onMouseMove={(event) => handleEvent(event, false)}
|
97 |
/>
|
98 |
{debug && <img
|
99 |
src={rendered.maskUrl || undefined}
|
100 |
+
className="fixed w-screen top-0 left-0 right-0 opacity-50 pointer-events-none"
|
101 |
/>}
|
102 |
+
{/* <FullScreenButton /> */}
|
103 |
+
</>
|
104 |
)
|
105 |
}
|
src/{components β app/interface}/renderer/cartesian-video.tsx
RENAMED
@@ -1,6 +1,6 @@
|
|
1 |
import { useEffect, useRef } from "react"
|
2 |
import { MouseEventHandler } from "./types"
|
3 |
-
import { RenderedScene } from "@/
|
4 |
|
5 |
export function CartesianVideo({
|
6 |
rendered,
|
@@ -86,18 +86,14 @@ export function CartesianVideo({
|
|
86 |
ref={ref}
|
87 |
onMouseUp={(event) => handleEvent(event, true)}
|
88 |
onMouseMove={(event) => handleEvent(event, false)}
|
89 |
-
className="
|
90 |
muted
|
91 |
autoPlay
|
92 |
loop
|
93 |
-
width="1024px"
|
94 |
-
height="512px"
|
95 |
/>
|
96 |
{debug && <img
|
97 |
src={rendered.maskUrl || undefined}
|
98 |
-
className="
|
99 |
-
width="1024px"
|
100 |
-
height="512px"
|
101 |
/>}
|
102 |
</div>
|
103 |
)
|
|
|
1 |
import { useEffect, useRef } from "react"
|
2 |
import { MouseEventHandler } from "./types"
|
3 |
+
import { RenderedScene } from "@/types"
|
4 |
|
5 |
export function CartesianVideo({
|
6 |
rendered,
|
|
|
86 |
ref={ref}
|
87 |
onMouseUp={(event) => handleEvent(event, true)}
|
88 |
onMouseMove={(event) => handleEvent(event, false)}
|
89 |
+
className="fixed w-screen top-0 left-0 right-0"
|
90 |
muted
|
91 |
autoPlay
|
92 |
loop
|
|
|
|
|
93 |
/>
|
94 |
{debug && <img
|
95 |
src={rendered.maskUrl || undefined}
|
96 |
+
className="fixed w-screen top-0 left-0 right-0 opacity-50 pointer-events-none"
|
|
|
|
|
97 |
/>}
|
98 |
</div>
|
99 |
)
|
src/app/interface/renderer/full-screen-button.tsx
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { cn } from "@/lib/utils"
|
2 |
+
import { FullScreenIcon } from "../../../components/icons/full-screen"
|
3 |
+
|
4 |
+
export function FullScreenButton() {
|
5 |
+
return (
|
6 |
+
<div className={cn(
|
7 |
+
`absolute z-10 right-0 bottom-0`,
|
8 |
+
)}>
|
9 |
+
<div
|
10 |
+
className={cn(
|
11 |
+
`transform-all duration-200 text-white opacity-80 cursor-pointer scale-100`,
|
12 |
+
`hover:opacity-100 hover:text-white hover:scale-110 p-4`
|
13 |
+
)}>
|
14 |
+
<FullScreenIcon />
|
15 |
+
</div>
|
16 |
+
</div>
|
17 |
+
)
|
18 |
+
}
|
src/{components β app/interface}/renderer/index.tsx
RENAMED
@@ -1,18 +1,20 @@
|
|
1 |
import { useEffect, useRef, useState } from "react"
|
|
|
2 |
|
3 |
-
import { DropZoneTarget, ImageSegment, RenderedScene, SceneEvent } from "@/
|
4 |
-
import {
|
|
|
5 |
import { Game } from "@/app/games/types"
|
6 |
-
import { Engine } from "@/app/engines"
|
|
|
7 |
import { CartesianImage } from "./cartesian-image"
|
8 |
import { MouseEventHandler, MouseEventType } from "./types"
|
9 |
import { CartesianVideo } from "./cartesian-video"
|
10 |
import { SphericalImage } from "./spherical-image"
|
11 |
-
|
12 |
-
import { useDrop } from "react-dnd"
|
13 |
-
import { formatActionnableName } from "@/lib/formatActionnableName"
|
14 |
import { SceneTooltip } from "./scene-tooltip"
|
15 |
import { SceneMenu } from "./scene-menu"
|
|
|
16 |
|
17 |
export const SceneRenderer = ({
|
18 |
rendered,
|
@@ -29,15 +31,13 @@ export const SceneRenderer = ({
|
|
29 |
engine: Engine
|
30 |
debug: boolean
|
31 |
}) => {
|
32 |
-
|
33 |
const containerRef = useRef<HTMLDivElement>(null)
|
34 |
const canvasRef = useRef<HTMLCanvasElement | null>(null)
|
35 |
const contextRef = useRef<CanvasRenderingContext2D | null>(null)
|
36 |
const [actionnable, setActionnable] = useState<string>("")
|
37 |
const actionnableRef = useRef<string>("")
|
38 |
-
|
39 |
-
const progressRef = useRef(0)
|
40 |
-
const isLoadingRef = useRef(isLoading)
|
41 |
const maskDimension = useImageDimension(rendered.maskUrl)
|
42 |
|
43 |
const [isHover, setHover] = useState(false)
|
@@ -141,6 +141,7 @@ export const SceneRenderer = ({
|
|
141 |
const noContext = !contextRef.current
|
142 |
const noSegmentationMask = !rendered.maskUrl
|
143 |
const noSegmentsToClickOn = rendered.segments.length == 0
|
|
|
144 |
|
145 |
const mustAbort =
|
146 |
noMenu
|
@@ -148,6 +149,7 @@ export const SceneRenderer = ({
|
|
148 |
|| noSegmentationMask
|
149 |
|| noSegmentsToClickOn
|
150 |
|| isLoading
|
|
|
151 |
|
152 |
if (mustAbort) {
|
153 |
// if (type === "click") { onEvent("ClickOnNothing") }
|
@@ -243,42 +245,16 @@ export const SceneRenderer = ({
|
|
243 |
|
244 |
}
|
245 |
}
|
246 |
-
};
|
247 |
-
|
248 |
-
const updateProgressBar = () => {
|
249 |
-
const duration = 1000 // 1 sec
|
250 |
-
const frequency = 200 // 200ms
|
251 |
-
const nbUpdatesPerSec = duration / frequency // 5x per second
|
252 |
-
|
253 |
-
// normally it takes 45, and we will try to go below,
|
254 |
-
// but to be safe let's set the counter a 1 min
|
255 |
-
const nbSeconds = 32 // 1 min
|
256 |
-
const amountInPercent = 100 / (nbUpdatesPerSec * nbSeconds) // 0.333
|
257 |
-
|
258 |
-
progressRef.current = Math.min(100, progressRef.current + amountInPercent)
|
259 |
-
setProcessPercent(progressRef.current)
|
260 |
}
|
261 |
|
262 |
-
|
263 |
-
clearInterval(timeoutRef.current)
|
264 |
-
isLoadingRef.current = isLoading
|
265 |
-
progressRef.current = 0
|
266 |
-
setProcessPercent(0)
|
267 |
-
if (isLoading) {
|
268 |
-
timeoutRef.current = setInterval(updateProgressBar, 200)
|
269 |
-
}
|
270 |
-
}, [isLoading, rendered.assetUrl, engine?.type])
|
271 |
-
|
272 |
return (
|
273 |
-
<div className="
|
274 |
<div
|
275 |
ref={containerRef}
|
276 |
-
className={
|
277 |
-
"
|
278 |
-
|
279 |
-
|| engine.type === "cartesian_image"
|
280 |
-
? "w-full" // w-[1024px] h-[512px]"
|
281 |
-
: "w-full",
|
282 |
|
283 |
isLoading
|
284 |
? "cursor-wait"
|
@@ -287,7 +263,7 @@ export const SceneRenderer = ({
|
|
287 |
? "cursor-crosshair"
|
288 |
: "cursor-crosshair"
|
289 |
: ""
|
290 |
-
|
291 |
{engine.type === "cartesian_video"
|
292 |
? <CartesianVideo
|
293 |
rendered={rendered}
|
@@ -307,32 +283,29 @@ export const SceneRenderer = ({
|
|
307 |
/>
|
308 |
}
|
309 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
310 |
</div>
|
311 |
|
312 |
-
<SceneTooltip
|
313 |
-
isVisible={isTooltipVisible && !isLoading}
|
314 |
-
x={tooltipX}
|
315 |
-
y={tooltipY}>
|
316 |
-
{actionnable}
|
317 |
-
</SceneTooltip>
|
318 |
-
|
319 |
{/*
|
|
|
|
|
320 |
<SceneMenu
|
321 |
actions={["Go here", "Interact"]}
|
322 |
isVisible={isMenuVisible && !isLoading}
|
323 |
x={menuX}
|
324 |
y={menuY}
|
325 |
-
/>
|
326 |
*/}
|
327 |
|
328 |
-
{isLoading
|
329 |
-
? <div className="fixed flex w-20 h-20 bottom-8 right-0 mr-8 z-50">
|
330 |
-
<ProgressBar
|
331 |
-
text="β"
|
332 |
-
progressPercentage={progressPercent}
|
333 |
-
/>
|
334 |
-
</div>
|
335 |
-
: null}
|
336 |
</div>
|
337 |
)
|
338 |
}
|
|
|
1 |
import { useEffect, useRef, useState } from "react"
|
2 |
+
import { useDrop } from "react-dnd"
|
3 |
|
4 |
+
import { DropZoneTarget, ImageSegment, RenderedScene, SceneEvent } from "@/types"
|
5 |
+
import { useImageDimension } from "@/lib/useImageDimension"
|
6 |
+
import { formatActionnableName } from "@/lib/formatActionnableName"
|
7 |
import { Game } from "@/app/games/types"
|
8 |
+
import { Engine } from "@/app/engine/engines"
|
9 |
+
|
10 |
import { CartesianImage } from "./cartesian-image"
|
11 |
import { MouseEventHandler, MouseEventType } from "./types"
|
12 |
import { CartesianVideo } from "./cartesian-video"
|
13 |
import { SphericalImage } from "./spherical-image"
|
14 |
+
|
|
|
|
|
15 |
import { SceneTooltip } from "./scene-tooltip"
|
16 |
import { SceneMenu } from "./scene-menu"
|
17 |
+
import { cn } from "@/lib/utils"
|
18 |
|
19 |
export const SceneRenderer = ({
|
20 |
rendered,
|
|
|
31 |
engine: Engine
|
32 |
debug: boolean
|
33 |
}) => {
|
34 |
+
|
35 |
const containerRef = useRef<HTMLDivElement>(null)
|
36 |
const canvasRef = useRef<HTMLCanvasElement | null>(null)
|
37 |
const contextRef = useRef<CanvasRenderingContext2D | null>(null)
|
38 |
const [actionnable, setActionnable] = useState<string>("")
|
39 |
const actionnableRef = useRef<string>("")
|
40 |
+
|
|
|
|
|
41 |
const maskDimension = useImageDimension(rendered.maskUrl)
|
42 |
|
43 |
const [isHover, setHover] = useState(false)
|
|
|
141 |
const noContext = !contextRef.current
|
142 |
const noSegmentationMask = !rendered.maskUrl
|
143 |
const noSegmentsToClickOn = rendered.segments.length == 0
|
144 |
+
const outOfBounds = relativeX < 0 || relativeX > 1 || relativeY < 0 || relativeY > 1
|
145 |
|
146 |
const mustAbort =
|
147 |
noMenu
|
|
|
149 |
|| noSegmentationMask
|
150 |
|| noSegmentsToClickOn
|
151 |
|| isLoading
|
152 |
+
|| outOfBounds
|
153 |
|
154 |
if (mustAbort) {
|
155 |
// if (type === "click") { onEvent("ClickOnNothing") }
|
|
|
245 |
|
246 |
}
|
247 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
248 |
}
|
249 |
|
250 |
+
const isFullScreen = true
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
251 |
return (
|
252 |
+
<div className="" ref={drop}>
|
253 |
<div
|
254 |
ref={containerRef}
|
255 |
+
className={cn(
|
256 |
+
"border-0 overflow-hidden",
|
257 |
+
`fixed top-0 left-0 right-0 w-screen`,
|
|
|
|
|
|
|
258 |
|
259 |
isLoading
|
260 |
? "cursor-wait"
|
|
|
263 |
? "cursor-crosshair"
|
264 |
: "cursor-crosshair"
|
265 |
: ""
|
266 |
+
)}>
|
267 |
{engine.type === "cartesian_video"
|
268 |
? <CartesianVideo
|
269 |
rendered={rendered}
|
|
|
283 |
/>
|
284 |
}
|
285 |
|
286 |
+
{/*
|
287 |
+
engine.type === "cartesian_image" || engine.type === "cartesian_video"
|
288 |
+
? <SceneTooltip
|
289 |
+
isVisible={isTooltipVisible && !isLoading}
|
290 |
+
x={tooltipX}
|
291 |
+
y={tooltipY}>
|
292 |
+
{actionnable}
|
293 |
+
</SceneTooltip> : null
|
294 |
+
*/
|
295 |
+
}
|
296 |
</div>
|
297 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
298 |
{/*
|
299 |
+
engine.type === "cartesian_image" || engine.type === "cartesian_video"
|
300 |
+
?
|
301 |
<SceneMenu
|
302 |
actions={["Go here", "Interact"]}
|
303 |
isVisible={isMenuVisible && !isLoading}
|
304 |
x={menuX}
|
305 |
y={menuY}
|
306 |
+
/> : null
|
307 |
*/}
|
308 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
309 |
</div>
|
310 |
)
|
311 |
}
|
src/{components β app/interface}/renderer/scene-menu.tsx
RENAMED
@@ -1,3 +1,5 @@
|
|
|
|
|
|
1 |
export function SceneMenu({
|
2 |
actions,
|
3 |
isVisible,
|
@@ -10,12 +12,12 @@ export function SceneMenu({
|
|
10 |
y: number
|
11 |
}) {
|
12 |
return (
|
13 |
-
<div className={
|
14 |
`z-20 fixed flex flex-col w-24 pt-8 px-2 pb-2`,
|
15 |
`translate-x-[-50%] translate-y-[-20px]`,
|
16 |
isVisible ? "" : "",
|
17 |
isVisible ? "" : "pointer-events-none"
|
18 |
-
|
19 |
style={{
|
20 |
top: `${y}px`,
|
21 |
left: `${x}px`,
|
@@ -24,17 +26,17 @@ export function SceneMenu({
|
|
24 |
{actions.map((action, i) =>
|
25 |
<div
|
26 |
key={action}
|
27 |
-
className={
|
28 |
`flex items-center justify-center px-2 py-1 cursor-pointer`
|
29 |
-
|
30 |
<div
|
31 |
-
className={
|
32 |
`transition-all duration-150`,
|
33 |
isVisible ? "opacity-100 scale-100" : "scale-0 opacity-0 pointer-events-none",
|
34 |
`flex items-center justify-center rounded-full h-8 px-4`,
|
35 |
`hover:bg-gray-50 bg-gray-100 hover:border-gray-800 border-gray-300 border`,
|
36 |
`rounded-2xl text-gray-800 text-md`,
|
37 |
-
|
38 |
{action}
|
39 |
</div>
|
40 |
</div>)}
|
|
|
1 |
+
import { cn } from "@/lib/utils"
|
2 |
+
|
3 |
export function SceneMenu({
|
4 |
actions,
|
5 |
isVisible,
|
|
|
12 |
y: number
|
13 |
}) {
|
14 |
return (
|
15 |
+
<div className={cn(
|
16 |
`z-20 fixed flex flex-col w-24 pt-8 px-2 pb-2`,
|
17 |
`translate-x-[-50%] translate-y-[-20px]`,
|
18 |
isVisible ? "" : "",
|
19 |
isVisible ? "" : "pointer-events-none"
|
20 |
+
)}
|
21 |
style={{
|
22 |
top: `${y}px`,
|
23 |
left: `${x}px`,
|
|
|
26 |
{actions.map((action, i) =>
|
27 |
<div
|
28 |
key={action}
|
29 |
+
className={cn(
|
30 |
`flex items-center justify-center px-2 py-1 cursor-pointer`
|
31 |
+
)}>
|
32 |
<div
|
33 |
+
className={cn(
|
34 |
`transition-all duration-150`,
|
35 |
isVisible ? "opacity-100 scale-100" : "scale-0 opacity-0 pointer-events-none",
|
36 |
`flex items-center justify-center rounded-full h-8 px-4`,
|
37 |
`hover:bg-gray-50 bg-gray-100 hover:border-gray-800 border-gray-300 border`,
|
38 |
`rounded-2xl text-gray-800 text-md`,
|
39 |
+
)}>
|
40 |
{action}
|
41 |
</div>
|
42 |
</div>)}
|
src/{components β app/interface}/renderer/scene-tooltip.tsx
RENAMED
@@ -1,3 +1,4 @@
|
|
|
|
1 |
import { ReactNode } from "react"
|
2 |
|
3 |
export function SceneTooltip({
|
@@ -12,25 +13,25 @@ export function SceneTooltip({
|
|
12 |
y: number
|
13 |
}) {
|
14 |
return (
|
15 |
-
<div className={
|
16 |
-
`z-
|
17 |
`translate-x-[-50%] translate-y-[-40px]`,
|
18 |
isVisible ? "cursor-pointer" : "",
|
19 |
"pointer-events-none"
|
20 |
-
|
21 |
style={{
|
22 |
top: `${y}px`,
|
23 |
left: `${x}px`,
|
24 |
}}
|
25 |
>
|
26 |
<div
|
27 |
-
className={
|
28 |
`transition-all duration-150`,
|
29 |
isVisible ? "opacity-100 scale-100" : "scale-0 opacity-0 pointer-events-none",
|
30 |
`flex items-center justify-center rounded-full h-8 px-4`,
|
31 |
`text-gray-50 text-xl`,
|
32 |
`cursor-pointer capitalize`
|
33 |
-
|
34 |
style={{
|
35 |
textShadow: "#000 0px 0px 1px, #000 0px 0px 1px, #000 0px 0px 1px"
|
36 |
}}>
|
|
|
1 |
+
import { cn } from "@/lib/utils"
|
2 |
import { ReactNode } from "react"
|
3 |
|
4 |
export function SceneTooltip({
|
|
|
13 |
y: number
|
14 |
}) {
|
15 |
return (
|
16 |
+
<div className={cn(
|
17 |
+
`z-30 fixed flex flex-col space-y-2 w-24 h-16 px-2`,
|
18 |
`translate-x-[-50%] translate-y-[-40px]`,
|
19 |
isVisible ? "cursor-pointer" : "",
|
20 |
"pointer-events-none"
|
21 |
+
)}
|
22 |
style={{
|
23 |
top: `${y}px`,
|
24 |
left: `${x}px`,
|
25 |
}}
|
26 |
>
|
27 |
<div
|
28 |
+
className={cn(
|
29 |
`transition-all duration-150`,
|
30 |
isVisible ? "opacity-100 scale-100" : "scale-0 opacity-0 pointer-events-none",
|
31 |
`flex items-center justify-center rounded-full h-8 px-4`,
|
32 |
`text-gray-50 text-xl`,
|
33 |
`cursor-pointer capitalize`
|
34 |
+
)}
|
35 |
style={{
|
36 |
textShadow: "#000 0px 0px 1px, #000 0px 0px 1px, #000 0px 0px 1px"
|
37 |
}}>
|
src/{components β app/interface}/renderer/spherical-image.tsx
RENAMED
@@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from "react"
|
|
2 |
import { PanoramaPosition, PluginConstructor, Point, Position, SphericalPosition, Viewer } from "@photo-sphere-viewer/core"
|
3 |
import { LensflarePlugin, ReactPhotoSphereViewer } from "react-photo-sphere-viewer"
|
4 |
|
5 |
-
import { RenderedScene } from "@/
|
6 |
|
7 |
import { MouseEventHandler } from "./types"
|
8 |
import { useImageDimension } from "@/lib/useImageDimension"
|
@@ -247,7 +247,7 @@ export function SphericalImage({
|
|
247 |
container=""
|
248 |
containerClass={className}
|
249 |
|
250 |
-
height="
|
251 |
width="100%"
|
252 |
|
253 |
// to access a plugin we must use viewer.getPlugin()
|
|
|
2 |
import { PanoramaPosition, PluginConstructor, Point, Position, SphericalPosition, Viewer } from "@photo-sphere-viewer/core"
|
3 |
import { LensflarePlugin, ReactPhotoSphereViewer } from "react-photo-sphere-viewer"
|
4 |
|
5 |
+
import { RenderedScene } from "@/types"
|
6 |
|
7 |
import { MouseEventHandler } from "./types"
|
8 |
import { useImageDimension } from "@/lib/useImageDimension"
|
|
|
247 |
container=""
|
248 |
containerClass={className}
|
249 |
|
250 |
+
height="100vh"
|
251 |
width="100%"
|
252 |
|
253 |
// to access a plugin we must use viewer.getPlugin()
|
src/{components β app/interface}/renderer/types.ts
RENAMED
File without changes
|
src/app/interface/top-menu/index.tsx
ADDED
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"use client"
|
2 |
+
|
3 |
+
import {
|
4 |
+
Select,
|
5 |
+
SelectContent,
|
6 |
+
SelectItem,
|
7 |
+
SelectTrigger,
|
8 |
+
SelectValue,
|
9 |
+
} from "@/components/ui/select"
|
10 |
+
import { Switch } from "@/components/ui/switch"
|
11 |
+
import { Label } from "@/components/ui/label"
|
12 |
+
|
13 |
+
import { Game, GameType } from "@/app/games/types"
|
14 |
+
import { games } from "@/app/games"
|
15 |
+
import { Engine, EngineType, engines } from "@/app/engine/engines"
|
16 |
+
import { cn } from "@/lib/utils"
|
17 |
+
|
18 |
+
export function TopMenu({
|
19 |
+
engine,
|
20 |
+
defaultGame,
|
21 |
+
game,
|
22 |
+
onChangeEngine,
|
23 |
+
onChangeGame,
|
24 |
+
onToggleDebug,
|
25 |
+
debug,
|
26 |
+
}: {
|
27 |
+
engine: Engine,
|
28 |
+
defaultGame: string
|
29 |
+
game: Game
|
30 |
+
onChangeEngine: (newEngine: EngineType) => void
|
31 |
+
onChangeGame: (newGameType: GameType) => void
|
32 |
+
onToggleDebug: (isToggledOn: boolean) => void
|
33 |
+
debug: boolean
|
34 |
+
}) {
|
35 |
+
return (
|
36 |
+
<div className={cn(
|
37 |
+
`z-10 fixed top-0 left-0 right-0`,
|
38 |
+
`flex flex-row w-full justify-between items-center`,
|
39 |
+
`backdrop-blur-xl`,
|
40 |
+
`px-2 py-2 border-b-1 border-gray-50 dark:border-gray-50`,
|
41 |
+
`bg-stone-900/50 dark:bg-stone-900/50 text-gray-50 dark:text-gray-50`
|
42 |
+
)}>
|
43 |
+
<div className="flex flex-row items-center space-x-3 font-mono">
|
44 |
+
<Label className="flex text-sm">Select a story:</Label>
|
45 |
+
<Select
|
46 |
+
defaultValue={defaultGame}
|
47 |
+
onValueChange={(value) => { onChangeGame(value as GameType) }}>
|
48 |
+
<SelectTrigger className="w-[180px]">
|
49 |
+
<SelectValue className="text-sm" placeholder="Type" />
|
50 |
+
</SelectTrigger>
|
51 |
+
<SelectContent>
|
52 |
+
{Object.entries(games).map(([key, game]) =>
|
53 |
+
<SelectItem key={key} value={key}>{game.title}</SelectItem>
|
54 |
+
)}
|
55 |
+
</SelectContent>
|
56 |
+
</Select>
|
57 |
+
</div>
|
58 |
+
<div className="flex flex-row items-center space-x-3 font-mono">
|
59 |
+
<Switch
|
60 |
+
checked={debug}
|
61 |
+
onCheckedChange={onToggleDebug}
|
62 |
+
// we won't disable it, so we can occupy our using while loading
|
63 |
+
// disabled={isLoading}
|
64 |
+
/>
|
65 |
+
<Label>Debug</Label>
|
66 |
+
</div>
|
67 |
+
<div className="flex flex-row items-center space-x-3 font-mono">
|
68 |
+
<Label className="flex text-sm">Rendering engine:</Label>
|
69 |
+
<Select
|
70 |
+
defaultValue={engine.type}
|
71 |
+
onValueChange={(value) => { onChangeEngine(value as EngineType) }}>
|
72 |
+
<SelectTrigger className="w-[300px]">
|
73 |
+
<SelectValue className="text-sm" placeholder="Type" />
|
74 |
+
</SelectTrigger>
|
75 |
+
<SelectContent>
|
76 |
+
{Object.entries(engines)
|
77 |
+
.filter(([_, engine]) => engine.visible)
|
78 |
+
.map(([key, engine]) =>
|
79 |
+
<SelectItem key={key} value={key} disabled={!engine.enabled}>{engine.label} ({engine.modelName})</SelectItem>
|
80 |
+
)}
|
81 |
+
</SelectContent>
|
82 |
+
</Select>
|
83 |
+
</div>
|
84 |
+
</div>
|
85 |
+
)
|
86 |
+
}
|
src/app/main.tsx
CHANGED
@@ -5,30 +5,28 @@ import { usePathname, useRouter, useSearchParams } from "next/navigation"
|
|
5 |
import { DndProvider } from "react-dnd"
|
6 |
import { HTML5Backend } from "react-dnd-html5-backend"
|
7 |
|
8 |
-
import { SceneRenderer } from "@/
|
9 |
-
|
10 |
-
import {
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
SelectTrigger,
|
15 |
-
SelectValue,
|
16 |
-
} from "@/components/ui/select"
|
17 |
-
import { Switch } from "@/components/ui/switch"
|
18 |
-
import { Label } from "@/components/ui/label"
|
19 |
-
|
20 |
-
import { newRender, getRender } from "./render"
|
21 |
-
import { InventoryEvent, InventoryItem, OnInventoryEvent, RenderedScene, SceneEvent } from "./types"
|
22 |
import { Game, GameType } from "./games/types"
|
23 |
-
import { defaultGame,
|
24 |
import { getBackground } from "@/app/queries/getBackground"
|
25 |
import { getDialogue } from "@/app/queries/getDialogue"
|
26 |
import { getActionnables } from "@/app/queries/getActionnables"
|
27 |
-
|
28 |
import { normalizeActionnables } from "@/lib/normalizeActionnables"
|
29 |
-
import { Inventory } from "@/
|
30 |
import { store } from "./store"
|
31 |
import { defaultActionnables } from "@/lib/defaultActionnables"
|
|
|
|
|
|
|
|
|
|
|
|
|
32 |
|
33 |
const getInitialRenderedScene = (): RenderedScene => ({
|
34 |
renderId: "",
|
@@ -59,6 +57,7 @@ export default function Main() {
|
|
59 |
const [situation, setSituation] = useState("")
|
60 |
|
61 |
const [dialogue, setDialogue] = useState("")
|
|
|
62 |
|
63 |
/*
|
64 |
const [grabbedItem, setGrabbedItem] = useState<InventoryItem>()
|
@@ -173,8 +172,8 @@ export default function Main() {
|
|
173 |
|
174 |
|
175 |
const askGameMasterForEpicDialogue = async (lastEvent: string) => {
|
176 |
-
|
177 |
await startTransition(async () => {
|
|
|
178 |
// const game = getGame(gameRef.current)
|
179 |
let newDialogue = ""
|
180 |
try {
|
@@ -186,6 +185,7 @@ export default function Main() {
|
|
186 |
} catch (err) {
|
187 |
console.log(`failed to generate dialogue.. again (but it's only a nice to have, so..)`)
|
188 |
setDialogue("")
|
|
|
189 |
return
|
190 |
}
|
191 |
}
|
@@ -194,6 +194,7 @@ export default function Main() {
|
|
194 |
newDialogue = newDialogue.split("As the player")[0]
|
195 |
newDialogue = newDialogue.split("As they use")[0]
|
196 |
setDialogue(newDialogue)
|
|
|
197 |
})
|
198 |
}
|
199 |
|
@@ -343,7 +344,7 @@ export default function Main() {
|
|
343 |
if (event === "ClickOnActionnable" || event === "ClickOnNothing") {
|
344 |
setBusy(busyRef.current = true) // this will be set to false once the scene finish loading
|
345 |
|
346 |
-
askGameMasterForEpicDialogue(newEventString)
|
347 |
askGameMasterForEpicBackground(newEventString)
|
348 |
}
|
349 |
}
|
@@ -397,95 +398,63 @@ export default function Main() {
|
|
397 |
askGameMasterForEpicBackground(newEventString)
|
398 |
}
|
399 |
}
|
|
|
|
|
|
|
400 |
|
401 |
-
|
402 |
-
// determine when to show the spinner
|
403 |
-
const isLoading = isBusy || rendered.status === "pending"
|
404 |
-
|
405 |
return (
|
406 |
<div
|
407 |
className="flex flex-col w-full max-w-5xl"
|
408 |
>
|
409 |
-
<
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
<SelectContent>
|
419 |
-
{Object.entries(games).map(([key, game]) =>
|
420 |
-
<SelectItem key={key} value={key}>{game.title}</SelectItem>
|
421 |
-
)}
|
422 |
-
</SelectContent>
|
423 |
-
</Select>
|
424 |
-
</div>
|
425 |
-
<div className="flex flex-row items-center space-x-3 font-mono">
|
426 |
-
<Switch
|
427 |
-
checked={debug}
|
428 |
-
onCheckedChange={handleToggleDebug}
|
429 |
-
// we won't disable it, so we can occupy our using while loading
|
430 |
-
// disabled={isLoading}
|
431 |
-
/>
|
432 |
-
<Label>Debug</Label>
|
433 |
-
</div>
|
434 |
-
<div className="flex flex-row items-center space-x-3 font-mono">
|
435 |
-
<Label className="flex text-sm">Rendering engine:</Label>
|
436 |
-
<Select
|
437 |
-
defaultValue={engine.type}
|
438 |
-
onValueChange={(value) => { handleSelectEngine(value as EngineType) }}>
|
439 |
-
<SelectTrigger className="w-[300px]">
|
440 |
-
<SelectValue className="text-sm" placeholder="Type" />
|
441 |
-
</SelectTrigger>
|
442 |
-
<SelectContent>
|
443 |
-
{Object.entries(engines)
|
444 |
-
.filter(([_, engine]) => engine.visible)
|
445 |
-
.map(([key, engine]) =>
|
446 |
-
<SelectItem key={key} value={key} disabled={!engine.enabled}>{engine.label} ({engine.modelName})</SelectItem>
|
447 |
-
)}
|
448 |
-
</SelectContent>
|
449 |
-
</Select>
|
450 |
-
</div>
|
451 |
-
</div>
|
452 |
|
453 |
<DndProvider backend={HTML5Backend}>
|
454 |
-
<div className={
|
455 |
"flex flex-col w-full pt-4 space-y-3 text-gray-50 dark:text-gray-50",
|
456 |
getGame(gameRef.current).className // apply the game theme
|
457 |
-
|
458 |
>
|
459 |
-
<div className="flex flex-row">
|
460 |
-
<div className="text-xl px-2">{lastEvent}</div>
|
461 |
-
</div>
|
462 |
-
<Inventory game={game} onEvent={handleInventoryEvent} />
|
463 |
-
<div className="flex flex-row">
|
464 |
-
<div className="text-xl mr-2">
|
465 |
-
{rendered.segments.length
|
466 |
-
? <span>π‘ Try to click on:</span>
|
467 |
-
: <span>β Generating areas for clicks and drag & drop, please wait..</span>
|
468 |
-
}
|
469 |
-
</div>
|
470 |
-
{clickables.map((clickable, i) =>
|
471 |
-
<div key={i} className="flex flex-row text-xl mr-2">
|
472 |
-
<div className="">{clickable}</div>
|
473 |
-
{i < (clickables.length - 1) ? <div>,</div> : null}
|
474 |
-
</div>)}
|
475 |
-
</div>
|
476 |
<SceneRenderer
|
477 |
rendered={rendered}
|
478 |
onEvent={handleSceneEvent}
|
479 |
-
isLoading={
|
480 |
game={game}
|
481 |
engine={engine}
|
482 |
debug={debug}
|
483 |
/>
|
484 |
-
<
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
489 |
</div>
|
490 |
</DndProvider>
|
491 |
</div>
|
|
|
5 |
import { DndProvider } from "react-dnd"
|
6 |
import { HTML5Backend } from "react-dnd-html5-backend"
|
7 |
|
8 |
+
import { SceneRenderer } from "@/app/interface/renderer"
|
9 |
+
|
10 |
+
import { newRender, getRender } from "@/app/engine/render"
|
11 |
+
import { Engine, EngineType, defaultEngine, getEngine } from "@/app/engine/engines"
|
12 |
+
|
13 |
+
import { OnInventoryEvent, RenderedScene, SceneEvent } from "@/types"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
import { Game, GameType } from "./games/types"
|
15 |
+
import { defaultGame, getGame } from "./games"
|
16 |
import { getBackground } from "@/app/queries/getBackground"
|
17 |
import { getDialogue } from "@/app/queries/getDialogue"
|
18 |
import { getActionnables } from "@/app/queries/getActionnables"
|
19 |
+
|
20 |
import { normalizeActionnables } from "@/lib/normalizeActionnables"
|
21 |
+
import { Inventory } from "@/app/interface/inventory"
|
22 |
import { store } from "./store"
|
23 |
import { defaultActionnables } from "@/lib/defaultActionnables"
|
24 |
+
import { TopMenu } from "./interface/top-menu"
|
25 |
+
import { LastEvent } from "./interface/last-event"
|
26 |
+
import { Help } from "./interface/help"
|
27 |
+
import { Dialogue } from "./interface/dialogue"
|
28 |
+
import { Progress } from "./interface/progress"
|
29 |
+
import { cn } from "@/lib/utils"
|
30 |
|
31 |
const getInitialRenderedScene = (): RenderedScene => ({
|
32 |
renderId: "",
|
|
|
57 |
const [situation, setSituation] = useState("")
|
58 |
|
59 |
const [dialogue, setDialogue] = useState("")
|
60 |
+
const [isLoadingDialogue, setLoadingDialogue] = useState(false)
|
61 |
|
62 |
/*
|
63 |
const [grabbedItem, setGrabbedItem] = useState<InventoryItem>()
|
|
|
172 |
|
173 |
|
174 |
const askGameMasterForEpicDialogue = async (lastEvent: string) => {
|
|
|
175 |
await startTransition(async () => {
|
176 |
+
setLoadingDialogue(true)
|
177 |
// const game = getGame(gameRef.current)
|
178 |
let newDialogue = ""
|
179 |
try {
|
|
|
185 |
} catch (err) {
|
186 |
console.log(`failed to generate dialogue.. again (but it's only a nice to have, so..)`)
|
187 |
setDialogue("")
|
188 |
+
setLoadingDialogue(false)
|
189 |
return
|
190 |
}
|
191 |
}
|
|
|
194 |
newDialogue = newDialogue.split("As the player")[0]
|
195 |
newDialogue = newDialogue.split("As they use")[0]
|
196 |
setDialogue(newDialogue)
|
197 |
+
setLoadingDialogue(false)
|
198 |
})
|
199 |
}
|
200 |
|
|
|
344 |
if (event === "ClickOnActionnable" || event === "ClickOnNothing") {
|
345 |
setBusy(busyRef.current = true) // this will be set to false once the scene finish loading
|
346 |
|
347 |
+
const dialogue = askGameMasterForEpicDialogue(newEventString)
|
348 |
askGameMasterForEpicBackground(newEventString)
|
349 |
}
|
350 |
}
|
|
|
398 |
askGameMasterForEpicBackground(newEventString)
|
399 |
}
|
400 |
}
|
401 |
+
const isLoadingScene = isBusy || rendered.status === "pending"
|
402 |
+
const isLoadingSegments = !rendered.segments.length
|
403 |
+
const isLoading = isLoadingScene || isLoadingSegments || isLoadingDialogue
|
404 |
|
405 |
+
const addBottomMarginForPhotoSphereMenu = engine.type.startsWith("spherical")
|
|
|
|
|
|
|
406 |
return (
|
407 |
<div
|
408 |
className="flex flex-col w-full max-w-5xl"
|
409 |
>
|
410 |
+
<TopMenu
|
411 |
+
engine={engine}
|
412 |
+
game={game}
|
413 |
+
defaultGame={gameRef.current}
|
414 |
+
onToggleDebug={handleToggleDebug}
|
415 |
+
onChangeGame={handleSelectGame}
|
416 |
+
onChangeEngine={handleSelectEngine}
|
417 |
+
debug={debug}
|
418 |
+
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
419 |
|
420 |
<DndProvider backend={HTML5Backend}>
|
421 |
+
<div className={cn(
|
422 |
"flex flex-col w-full pt-4 space-y-3 text-gray-50 dark:text-gray-50",
|
423 |
getGame(gameRef.current).className // apply the game theme
|
424 |
+
)}
|
425 |
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
426 |
<SceneRenderer
|
427 |
rendered={rendered}
|
428 |
onEvent={handleSceneEvent}
|
429 |
+
isLoading={isLoadingScene}
|
430 |
game={game}
|
431 |
engine={engine}
|
432 |
debug={debug}
|
433 |
/>
|
434 |
+
<LastEvent>{lastEvent}</LastEvent>
|
435 |
+
<Inventory game={game} isLoading={!rendered.segments.length} onEvent={handleInventoryEvent} />
|
436 |
+
{/*
|
437 |
+
<Help
|
438 |
+
clickables={clickables}
|
439 |
+
isLoading={!rendered.segments.length}
|
440 |
+
/>
|
441 |
+
*/}
|
442 |
+
<Dialogue
|
443 |
+
className={
|
444 |
+
addBottomMarginForPhotoSphereMenu ? `bottom-16`: `bottom-6`
|
445 |
+
}
|
446 |
+
isLoading={isLoadingDialogue}>{
|
447 |
+
dialogue
|
448 |
+
? dialogue
|
449 |
+
: <Help clickables={clickables} isLoading={isLoadingSegments} />
|
450 |
+
}</Dialogue>
|
451 |
+
<Progress
|
452 |
+
isLoading={isLoading}
|
453 |
+
resetKey={[
|
454 |
+
rendered?.segments?.length,
|
455 |
+
rendered?.assetUrl,
|
456 |
+
].join("&&")}
|
457 |
+
/>
|
458 |
</div>
|
459 |
</DndProvider>
|
460 |
</div>
|
src/app/queries/getDialogue.ts
CHANGED
@@ -1,3 +1,5 @@
|
|
|
|
|
|
1 |
import { Game } from "@/app/games/types"
|
2 |
import { createLlamaPrompt } from "@/lib/createLlamaPrompt"
|
3 |
|
@@ -71,5 +73,8 @@ Here is the original situation, which will inform you about the general game moo
|
|
71 |
}
|
72 |
}
|
73 |
|
74 |
-
|
|
|
|
|
|
|
75 |
}
|
|
|
1 |
+
import sbd from "sbd"
|
2 |
+
|
3 |
import { Game } from "@/app/games/types"
|
4 |
import { createLlamaPrompt } from "@/lib/createLlamaPrompt"
|
5 |
|
|
|
73 |
}
|
74 |
}
|
75 |
|
76 |
+
// llama-2 is too chatty, let's keep 3 sentences at most
|
77 |
+
const sentences = sbd.sentences(result).slice(0, 3).join(" ")
|
78 |
+
|
79 |
+
return sentences
|
80 |
}
|
src/app/store.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
"use client"
|
2 |
|
3 |
-
import { InventoryItem } from "
|
4 |
|
5 |
// could also be Zustand or something
|
6 |
export const store: {
|
|
|
1 |
"use client"
|
2 |
|
3 |
+
import { InventoryItem } from "../types"
|
4 |
|
5 |
// could also be Zustand or something
|
6 |
export const store: {
|
src/components/icons/full-screen.tsx
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export function FullScreenIcon() {
|
2 |
+
return (
|
3 |
+
<svg version="1.1" viewBox="0 0 14 14" width="24px" height="24px" xmlns="http://www.w3.org/2000/svg">
|
4 |
+
<title/>
|
5 |
+
<desc/>
|
6 |
+
<defs/>
|
7 |
+
<g fill="none" fill-rule="evenodd" id="Page-1" stroke="none" stroke-width="1">
|
8 |
+
<g fill="currentColor" id="Core" transform="translate(-215.000000, -257.000000)">
|
9 |
+
<g id="fullscreen" transform="translate(215.000000, 257.000000)">
|
10 |
+
<path d="M2,9 L0,9 L0,14 L5,14 L5,12 L2,12 L2,9 L2,9 Z M0,5 L2,5 L2,2 L5,2 L5,0 L0,0 L0,5 L0,5 Z M12,12 L9,12 L9,14 L14,14 L14,9 L12,9 L12,12 L12,12 Z M9,0 L9,2 L12,2 L12,5 L14,5 L14,0 L9,0 L9,0 Z" id="Shape"/>
|
11 |
+
</g>
|
12 |
+
</g>
|
13 |
+
</g>
|
14 |
+
</svg>
|
15 |
+
)
|
16 |
+
}
|
src/components/inventory/index.tsx
DELETED
@@ -1,24 +0,0 @@
|
|
1 |
-
import { Game } from "@/app/games/types"
|
2 |
-
import { DraggableItem } from "./draggable-item"
|
3 |
-
import { OnInventoryEvent } from "@/app/types"
|
4 |
-
|
5 |
-
export function Inventory({
|
6 |
-
game,
|
7 |
-
onEvent
|
8 |
-
}: {
|
9 |
-
game: Game;
|
10 |
-
onEvent: OnInventoryEvent;
|
11 |
-
}) {
|
12 |
-
return (
|
13 |
-
<div className="grid grid-cols-6 sm:grid-cols-8 md:grid-cols-10 lg:grid-cols-12 gap-4 w-full bg-stone-500 p-4 rounded-xl">
|
14 |
-
{game.inventory.map(item => (
|
15 |
-
<DraggableItem
|
16 |
-
key={item.name}
|
17 |
-
game={game}
|
18 |
-
item={item}
|
19 |
-
onEvent={onEvent}
|
20 |
-
/>
|
21 |
-
))}
|
22 |
-
</div>
|
23 |
-
)
|
24 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/components/ui/switch.tsx
CHANGED
@@ -11,7 +11,7 @@ const Switch = React.forwardRef<
|
|
11 |
>(({ className, ...props }, ref) => (
|
12 |
<SwitchPrimitives.Root
|
13 |
className={cn(
|
14 |
-
"peer inline-flex h-[24px] w-[44px] shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-
|
15 |
className
|
16 |
)}
|
17 |
{...props}
|
@@ -19,7 +19,7 @@ const Switch = React.forwardRef<
|
|
19 |
>
|
20 |
<SwitchPrimitives.Thumb
|
21 |
className={cn(
|
22 |
-
"pointer-events-none block h-5 w-5 rounded-full bg-white shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0 dark:bg-
|
23 |
)}
|
24 |
/>
|
25 |
</SwitchPrimitives.Root>
|
|
|
11 |
>(({ className, ...props }, ref) => (
|
12 |
<SwitchPrimitives.Root
|
13 |
className={cn(
|
14 |
+
"peer inline-flex h-[24px] w-[44px] shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-stone-400 focus-visible:ring-offset-2 focus-visible:ring-offset-white disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-stone-900 data-[state=unchecked]:bg-stone-200 dark:focus-visible:ring-stone-800 dark:focus-visible:ring-offset-stone-950 dark:data-[state=checked]:bg-stone-50 dark:data-[state=unchecked]:bg-stone-800",
|
15 |
className
|
16 |
)}
|
17 |
{...props}
|
|
|
19 |
>
|
20 |
<SwitchPrimitives.Thumb
|
21 |
className={cn(
|
22 |
+
"pointer-events-none block h-5 w-5 rounded-full bg-white shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0 dark:bg-stone-950"
|
23 |
)}
|
24 |
/>
|
25 |
</SwitchPrimitives.Root>
|
src/components/ui/tooltip.tsx
CHANGED
@@ -19,7 +19,7 @@ const TooltipContent = React.forwardRef<
|
|
19 |
ref={ref}
|
20 |
sideOffset={sideOffset}
|
21 |
className={cn(
|
22 |
-
"z-50 overflow-hidden rounded-md border border-
|
23 |
className
|
24 |
)}
|
25 |
{...props}
|
|
|
19 |
ref={ref}
|
20 |
sideOffset={sideOffset}
|
21 |
className={cn(
|
22 |
+
"z-50 overflow-hidden rounded-md border border-stone-200 bg-white px-3 py-1.5 text-sm text-stone-950 shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-stone-800 dark:bg-stone-950 dark:text-stone-50",
|
23 |
className
|
24 |
)}
|
25 |
{...props}
|
src/{app/types.ts β types.ts}
RENAMED
File without changes
|