jbilcke-hf HF staff commited on
Commit
fd2aa6b
β€’
1 Parent(s): a44c1b5
Files changed (41) hide show
  1. components.json +1 -1
  2. package-lock.json +131 -1
  3. package.json +3 -1
  4. src/app/{engines.ts β†’ engine/engines.ts} +1 -1
  5. src/app/{render.ts β†’ engine/render.ts} +3 -3
  6. src/app/games/arizona.ts +1 -1
  7. src/app/games/city.ts +1 -1
  8. src/app/games/doom.ts +1 -1
  9. src/app/games/dungeon.ts +1 -1
  10. src/app/games/enchanters.ts +1 -1
  11. src/app/games/flamenco.ts +1 -1
  12. src/app/games/nexus.ts +1 -1
  13. src/app/games/pharaoh.ts +1 -1
  14. src/app/games/pirates.ts +1 -1
  15. src/app/games/tensor.ts +1 -1
  16. src/app/games/types.ts +2 -2
  17. src/app/games/vernian.ts +1 -1
  18. src/app/interface/dialogue/index.tsx +25 -0
  19. src/app/interface/help/index.tsx +23 -0
  20. src/{components β†’ app/interface}/inventory/draggable-item.tsx +18 -13
  21. src/app/interface/inventory/index.tsx +39 -0
  22. src/app/interface/last-event/index.tsx +21 -0
  23. src/app/interface/progress/index.tsx +56 -0
  24. src/{components/misc/progress.tsx β†’ app/interface/progress/progress-bar.tsx} +0 -0
  25. src/{components β†’ app/interface}/renderer/cartesian-image.tsx +10 -12
  26. src/{components β†’ app/interface}/renderer/cartesian-video.tsx +3 -7
  27. src/app/interface/renderer/full-screen-button.tsx +18 -0
  28. src/{components β†’ app/interface}/renderer/index.tsx +31 -58
  29. src/{components β†’ app/interface}/renderer/scene-menu.tsx +8 -6
  30. src/{components β†’ app/interface}/renderer/scene-tooltip.tsx +6 -5
  31. src/{components β†’ app/interface}/renderer/spherical-image.tsx +2 -2
  32. src/{components β†’ app/interface}/renderer/types.ts +0 -0
  33. src/app/interface/top-menu/index.tsx +86 -0
  34. src/app/main.tsx +60 -91
  35. src/app/queries/getDialogue.ts +6 -1
  36. src/app/store.ts +1 -1
  37. src/components/icons/full-screen.tsx +16 -0
  38. src/components/inventory/index.tsx +0 -24
  39. src/components/ui/switch.tsx +2 -2
  40. src/components/ui/tooltip.tsx +1 -1
  41. 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": "zinc",
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 = "spherical_image"
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
- import { RenderedScene } from "./types"
 
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 : 512,
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 "../types"
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 "../types"
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 "../types"
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 "../types"
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 "../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`,
 
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 "../types"
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 "../types"
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 "../types"
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 "../types"
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 "../types"
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 { EngineType } from "../engines"
2
- import { InventoryItem } from "../types"
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 "../types"
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 "@/app/types"
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-125 border border-stone-300 shadow-2xl cursor-grabbing"
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-125 hover:border hover:border-stone-300 hover:shadow-2xl",
86
- ].join(" ")}>
87
  <div ref={drop}>
88
- <Image
89
- src={`/inventories/${game.type}/${item.name}.jpeg`}
90
- width={1024}
91
- height={1024}
92
- alt={item.title}
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
- import { MouseEventHandler } from "./types"
3
- import { RenderedScene } from "@/app/types"
 
 
 
4
 
5
  export function CartesianImage({
6
  rendered,
@@ -84,24 +87,19 @@ export function CartesianImage({
84
  return null
85
  }
86
  return (
87
- <div
88
- className={[
89
- "h-[512px]",
90
- className
91
- ].join(" ")
92
- }
93
- >
94
  <img
95
  src={rendered.assetUrl || undefined}
96
  ref={ref}
97
- className="absolute"
98
  onMouseUp={(event) => handleEvent(event, true)}
99
  onMouseMove={(event) => handleEvent(event, false)}
100
  />
101
  {debug && <img
102
  src={rendered.maskUrl || undefined}
103
- className="absolute opacity-50 pointer-events-none"
104
  />}
105
- </div>
 
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 "@/app/types"
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="absolute"
90
  muted
91
  autoPlay
92
  loop
93
- width="1024px"
94
- height="512px"
95
  />
96
  {debug && <img
97
  src={rendered.maskUrl || undefined}
98
- className="absolute opacity-50 pointer-events-none"
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 "@/app/types"
4
- import { ProgressBar } from "../misc/progress"
 
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
- import { useImageDimension } from "@/lib/useImageDimension"
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
- const timeoutRef = useRef<any>()
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
- const [progressPercent, setProcessPercent] = useState(0)
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
- useEffect(() => {
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="w-full pt-2" ref={drop}>
274
  <div
275
  ref={containerRef}
276
- className={[
277
- "relative border-2 border-gray-50 rounded-xl overflow-hidden min-h-[512px]",
278
- engine.type === "cartesian_video"
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
- ].join(" ")}>
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
- ].join(" ")}
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
- ].join(" ")}>
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
- ].join(" ")}>
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-10 fixed flex flex-col space-y-2 w-24 h-16 px-2`,
17
  `translate-x-[-50%] translate-y-[-40px]`,
18
  isVisible ? "cursor-pointer" : "",
19
  "pointer-events-none"
20
- ].join(" ")}
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
- ].join(" ")}
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 "@/app/types"
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="60vh"
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 "@/components/renderer"
9
-
10
- import {
11
- Select,
12
- SelectContent,
13
- SelectItem,
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, games, getGame } from "./games"
24
  import { getBackground } from "@/app/queries/getBackground"
25
  import { getDialogue } from "@/app/queries/getDialogue"
26
  import { getActionnables } from "@/app/queries/getActionnables"
27
- import { Engine, EngineType, defaultEngine, engines, getEngine } from "./engines"
28
  import { normalizeActionnables } from "@/lib/normalizeActionnables"
29
- import { Inventory } from "@/components/inventory"
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
- <div className="flex flex-row w-full justify-between items-center px-2 py-2 border-b-1 border-gray-50 dark:border-gray-50 bg-gray-800 dark:bg-gray-800 text-gray-50 dark:text-gray-50">
410
- <div className="flex flex-row items-center space-x-3 font-mono">
411
- <Label className="flex text-sm">Select a story:</Label>
412
- <Select
413
- defaultValue={gameRef.current}
414
- onValueChange={(value) => { handleSelectGame(value as GameType) }}>
415
- <SelectTrigger className="w-[180px]">
416
- <SelectValue className="text-sm" placeholder="Type" />
417
- </SelectTrigger>
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
- ].join(" ")}
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={isLoading}
480
  game={game}
481
  engine={engine}
482
  debug={debug}
483
  />
484
- <div
485
- className="text-xl rounded-xl backdrop-blur-sm bg-white/10 p-4"
486
- style={{
487
- textShadow: "1px 0px 2px #000000ab"
488
- }}>{dialogue}</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- return result
 
 
 
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 "./types"
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-zinc-400 focus-visible:ring-offset-2 focus-visible:ring-offset-white disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-zinc-900 data-[state=unchecked]:bg-zinc-200 dark:focus-visible:ring-zinc-800 dark:focus-visible:ring-offset-zinc-950 dark:data-[state=checked]:bg-zinc-50 dark:data-[state=unchecked]:bg-zinc-800",
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-zinc-950"
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-zinc-200 bg-white px-3 py-1.5 text-sm text-zinc-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-zinc-800 dark:bg-zinc-950 dark:text-zinc-50",
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