create-three-flatland 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of create-three-flatland might be problematic. Click here for more details.

Files changed (36) hide show
  1. package/LICENSE +21 -0
  2. package/dist/index.js +1014 -0
  3. package/package.json +70 -0
  4. package/templates/react/.oxfmtrc.json +8 -0
  5. package/templates/react/.oxlintrc.json +27 -0
  6. package/templates/react/AGENTS.md +188 -0
  7. package/templates/react/CLAUDE.md +188 -0
  8. package/templates/react/_gitignore +10 -0
  9. package/templates/react/e2e/app.spec.ts +122 -0
  10. package/templates/react/index.html +146 -0
  11. package/templates/react/package.json +54 -0
  12. package/templates/react/playwright.config.ts +41 -0
  13. package/templates/react/public/sprite.svg +1866 -0
  14. package/templates/react/src/App.tsx +172 -0
  15. package/templates/react/src/interaction.test.ts +48 -0
  16. package/templates/react/src/interaction.ts +53 -0
  17. package/templates/react/src/main.tsx +9 -0
  18. package/templates/react/tsconfig.json +19 -0
  19. package/templates/react/vite.config.ts +12 -0
  20. package/templates/react/vitest.config.ts +6 -0
  21. package/templates/three/.oxfmtrc.json +8 -0
  22. package/templates/three/.oxlintrc.json +26 -0
  23. package/templates/three/AGENTS.md +167 -0
  24. package/templates/three/CLAUDE.md +167 -0
  25. package/templates/three/_gitignore +10 -0
  26. package/templates/three/e2e/app.spec.ts +122 -0
  27. package/templates/three/index.html +185 -0
  28. package/templates/three/package.json +47 -0
  29. package/templates/three/playwright.config.ts +41 -0
  30. package/templates/three/public/sprite.svg +1866 -0
  31. package/templates/three/src/interaction.test.ts +66 -0
  32. package/templates/three/src/interaction.ts +65 -0
  33. package/templates/three/src/main.ts +138 -0
  34. package/templates/three/tsconfig.json +18 -0
  35. package/templates/three/vite.config.ts +4 -0
  36. package/templates/three/vitest.config.ts +6 -0
@@ -0,0 +1,146 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>three-flatland</title>
7
+ <style>
8
+ html,
9
+ body {
10
+ margin: 0;
11
+ width: 100%;
12
+ height: 100%;
13
+ overflow: hidden;
14
+ background: #16191e;
15
+ }
16
+ #root {
17
+ position: relative;
18
+ width: 100%;
19
+ height: 100%;
20
+ }
21
+ /* Loading screen overlay. React owns #root, so the overlay lives beside it
22
+ and retires itself with CSS alone: it shows only while #root has no
23
+ <canvas>. The Canvas mounts exactly when the Suspense boundary in
24
+ src/App.tsx resolves (renderer constructed, texture loaded), so this
25
+ tracks the same readiness signal — no removal code, no JS. It sits
26
+ opaque above the bare Suspense fallback. On browsers without :has()
27
+ the overlay simply never shows, which fails safe. */
28
+ .fl-stack {
29
+ display: grid;
30
+ justify-items: center;
31
+ gap: 20px;
32
+ }
33
+ .fl-mark {
34
+ width: 96px;
35
+ height: 96px;
36
+ }
37
+ /* One soft diagonal glint sweeps the mark — the same turquoise the
38
+ sprite tints on hover — then rests before the next pass. */
39
+ .fl-sweep {
40
+ animation: fl-sweep 2.6s ease-in-out infinite;
41
+ }
42
+ .fl-dot {
43
+ opacity: 0.25;
44
+ animation: fl-dot 1.6s ease-in-out infinite;
45
+ }
46
+ .fl-dot2 {
47
+ animation-delay: 0.2s;
48
+ }
49
+ .fl-dot3 {
50
+ animation-delay: 0.4s;
51
+ }
52
+ @keyframes fl-sweep {
53
+ 0% {
54
+ transform: translate(-48px, -48px);
55
+ }
56
+ 55%,
57
+ 100% {
58
+ transform: translate(48px, 48px);
59
+ }
60
+ }
61
+ @keyframes fl-dot {
62
+ 0%,
63
+ 60%,
64
+ 100% {
65
+ opacity: 0.25;
66
+ }
67
+ 30% {
68
+ opacity: 1;
69
+ }
70
+ }
71
+ /* Non-negotiable: reduced motion collapses to a static pose — the mark
72
+ unlit, all three dots on. */
73
+ @media (prefers-reduced-motion: reduce) {
74
+ .fl-sweep {
75
+ animation: none;
76
+ visibility: hidden;
77
+ }
78
+ .fl-dot {
79
+ animation: none;
80
+ opacity: 1;
81
+ }
82
+ }
83
+ /* Loading overlay — identical in both templates. Fades out once the
84
+ scene is ready, then removes itself. */
85
+ #loader {
86
+ position: fixed;
87
+ inset: 0;
88
+ z-index: 1;
89
+ display: grid;
90
+ place-items: center;
91
+ background: #16191e;
92
+ pointer-events: none;
93
+ opacity: 1;
94
+ transition: opacity 320ms ease;
95
+ }
96
+ #loader[data-loaded='true'] {
97
+ opacity: 0;
98
+ }
99
+ </style>
100
+ </head>
101
+ <body>
102
+ <div id="root"></div>
103
+
104
+ <!--
105
+ Loading screen. The pixel FL mark is the brand icon (assets/icon.svg
106
+ upstream) losslessly re-encoded as one path per colour; "loading" is
107
+ Silkscreen — the flatland wordmark face — baked to path outlines, so
108
+ there is no font file and no webfont request. Animation is pure CSS
109
+ and collapses to a static pose under prefers-reduced-motion.
110
+ -->
111
+ <div id="loader" role="status" aria-label="Loading">
112
+ <div class="fl-stack">
113
+ <svg class="fl-mark" viewBox="0 0 48 48" shape-rendering="crispEdges" aria-hidden="true">
114
+ <defs>
115
+ <linearGradient id="fl-glint" x1="0" y1="0" x2="1" y2="1">
116
+ <stop offset="0" stop-color="#47cca9" stop-opacity="0" />
117
+ <stop offset="0.5" stop-color="#47cca9" stop-opacity="0.4" />
118
+ <stop offset="1" stop-color="#47cca9" stop-opacity="0" />
119
+ </linearGradient>
120
+ <!-- Luminance mask from the mark itself: bright faces catch the
121
+ glint strongly, shadowed faces barely, voids not at all. -->
122
+ <mask id="fl-mask"><use href="#fl-mark-px" /></mask>
123
+ </defs>
124
+ <g id="fl-mark-px">
125
+ <path fill="#999999" d="M11 2h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h3v1h-3m4 -1h3v1h-3m4 -1h7v1h-7m-29 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h3v1h-3m4 -1h21v1h-21m-9 0h1v1h-1m4 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h3v1h-3m-31 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h3v1h-3m4 -1h13v1h-13m-13 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h3v1h-3m-31 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h3v1h-3m4 -1h9v1h-9m-9 0h1v1h-1m4 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-25 0h1v1h-1m4 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h3v1h-3m4 -1h1v1h-1" />
126
+ <path fill="#777777" d="M45 3h1v1h-1m-1 0h2v1h-2m-1 0h1v1h-1m2 -1h1v1h-1m-3 0h3v1h-3m-1 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-5 0h3v1h-3m4 -1h1v1h-1m-5 0h3v1h-3m4 -1h1v1h-1m2 -1h1v1h-1m-7 0h7v1h-7m0 0h2v1h-2m3 -1h1v1h-1m2 -1h1v1h-1m-5 0h5v1h-5m6 -1h1v1h-1m-6 0h4v1h-4m5 -1h1v1h-1m2 -1h1v1h-1m-7 0h3v1h-3m4 -1h1v1h-1m2 -1h1v1h-1m-6 0h2v1h-2m3 -1h1v1h-1m2 -1h1v1h-1m-5 0h5v1h-5m6 -1h1v1h-1m-6 0h2v1h-2m3 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-7 0h3v1h-3m4 -1h1v1h-1m2 -1h1v1h-1m-6 0h2v1h-2m3 -1h1v1h-1m2 -1h1v1h-1m-5 0h5v1h-5m6 -1h1v1h-1m-5 0h1v1h-1m2 -1h1v1h-1m-3 0h3v1h-3m4 -1h1v1h-1m2 -1h1v1h-1m-5 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-5 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-5 0h1v1h-1m2 -1h1v1h-1m-3 0h3v1h-3m4 -1h1v1h-1m2 -1h1v1h-1m-5 0h1v1h-1m-1 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-5 0h1v1h-1m2 -1h1v1h-1m-3 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-3 0h1v1h-1m-1 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-5 0h1v1h-1m2 -1h1v1h-1m-3 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-3 0h1v1h-1m-1 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-6 1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-3 0h1v1h-1m-1 0h1v1h-1m2 -1h1v1h-1m-2 1h1v1h-1m2 -1h1v1h-1m-2 1h1v1h-1" />
127
+ <path fill="#444444" d="M44 5h1v1h-1m1 0h1v1h-1m-3 0h1v1h-1m2 -1h1v1h-1m-1 0h1v1h-1m2 -1h1v1h-1m-3 0h1v1h-1m2 -1h1v1h-1m1 0h1v1h-1m-5 0h1v1h-1m2 -1h1v1h-1m2 -1h2v1h-2m-1 0h1v1h-1m2 -1h1v1h-1m-3 0h1v1h-1m2 -1h1v1h-1m-3 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-5 0h1v1h-1m2 -1h1v1h-1m2 -1h2v1h-2m-1 0h1v1h-1m2 -1h1v1h-1m-5 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-3 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-5 0h1v1h-1m2 -1h1v1h-1m2 -1h2v1h-2m-1 0h1v1h-1m2 -1h1v1h-1m-7 0h1v1h-1m2 -1h1v1h-1m2 -1h4v1h-4m-1 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-7 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h2v1h-2m-5 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-7 0h1v1h-1m2 -1h1v1h-1m2 -1h4v1h-4m-1 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-7 0h1v1h-1m2 -1h6v1h-6m-1 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-7 0h1v1h-1m2 -1h1v1h-1m2 -1h4v1h-4m-3 0h1v1h-1m2 -1h1v1h-1m2 -1h3v1h-3m-5 0h1v1h-1m2 -1h6v1h-6m-1 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-7 0h1v1h-1m2 -1h1v1h-1m2 -1h4v1h-4m-3 0h1v1h-1m2 -1h1v1h-1m2 -1h3v1h-3m-5 0h1v1h-1m2 -1h6v1h-6m-1 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-7 0h8v1h-8m1 0h1v1h-1m2 -1h1v1h-1m2 -1h2v1h-2m-5 0h1v1h-1m2 -1h4v1h-4m-1 0h1v1h-1m2 -1h2v1h-2m-3 0h4v1h-4m1 0h1v1h-1m-1 0h2v1h-2" />
128
+ <path fill="#E0E0E0" d="M10 2h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m4 -1h1v1h-1m4 -1h1v1h-1m-29 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m4 -1h1v1h-1m-15 0h7v1h-7m8 -1h3v1h-3m4 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-33 0h3v1h-3m4 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m4 -1h1v1h-1m-23 0h11v1h-11m12 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-33 0h3v1h-3m4 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m4 -1h1v1h-1m-27 0h19v1h-19m20 -1h3v1h-3m4 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-35 0h11v1h-11m12 -1h3v1h-3m4 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v2h-1m2 -2h1v1h-1m2 -1h1v2h-1m2 -2h1v2h-1m2 -2h1v2h-1m4 -2h1v2h-1m-2 -1h1v1h-1m-23 0h1v1h-1m4 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h2v1h-2m-13 0h1v1h-1m4 -1h1v1h-1m4 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-27 0h1v1h-1m4 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-15 0h1v1h-1m4 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-29 0h1v1h-1m4 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h3v1h-3m4 -1h2v1h-2m-17 0h1v1h-1m4 -1h1v1h-1m4 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-31 0h1v1h-1m4 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h3v1h-3m-13 0h1v1h-1m6 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-33 0h1v1h-1m14 -1h1v1h-1m2 -1h1v1h-1m6 -1h1v1h-1m2 -1h3v1h-3m4 -1h6v1h-6m-13 0h1v1h-1m8 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-35 0h1v1h-1m16 -1h1v1h-1m2 -1h1v1h-1m6 -1h1v1h-1m2 -1h1v1h-1m2 -1h3v1h-3m4 -1h4v1h-4m-21 0h1v1h-1m4 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m6 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-33 0h1v1h-1m6 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m6 -1h1v1h-1m2 -1h10v1h-10m-17 0h1v1h-1m4 -1h1v1h-1m4 -1h1v1h-1m2 -1h1v1h-1m6 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-35 0h1v1h-1m2 -1h1v1h-1m6 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m6 -1h3v1h-3m4 -1h8v1h-8m-15 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m6 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h3v1h-3m-33 0h1v1h-1m2 -1h1v1h-1m10 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h2v1h-2m6 -1h12v1h-12m-11 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m6 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-35 0h1v1h-1m2 -1h1v1h-1m10 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m6 -1h12v1h-12m-21 0h1v1h-1m6 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m6 -1h1v1h-1m2 -1h1v1h-1m2 -1h3v1h-3m4 -1h3v1h-3m-33 0h1v1h-1m2 -1h1v1h-1m6 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h3v1h-3m4 -1h2v1h-2m6 -1h12v1h-12m-23 0h1v1h-1m8 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m6 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-35 0h1v1h-1m2 -1h1v1h-1m6 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h3v1h-3m8 -1h12v1h-12m-21 0h1v1h-1m6 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m14 -1h3v1h-3m-33 0h1v1h-1m2 -1h1v1h-1m6 -1h1v1h-1m2 -1h3v1h-3m4 -1h6v1h-6m18 -1h4v1h-4m-31 0h1v1h-1m8 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m13 -1h2v1h-2m3 -1h1v1h-1m-35 0h1v1h-1m2 -1h1v1h-1m6 -1h1v1h-1m2 -1h1v1h-1m2 -1h3v1h-3m4 -1h4v1h-4m16 -1h4v1h-4m-31 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h3v1h-3m4 -1h3v1h-3m4 -1h3v1h-3m4 -1h3v1h-3m-33 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h3v1h-3m4 -1h26v1h-26m-9 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h3v1h-3m4 -1h3v1h-3m4 -1h1v1h-1m-35 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h3v1h-3m4 -1h24v1h-24m-11 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h3v1h-3m4 -1h3v1h-3m4 -1h3v1h-3m4 -1h7v1h-7m-29 0h1v1h-1m2 -1h3v1h-3m4 -1h30v1h-30m-5 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h3v1h-3m4 -1h3v1h-3m4 -1h3v1h-3m4 -1h1v1h-1m-35 0h1v1h-1m2 -1h1v1h-1m2 -1h3v1h-3m4 -1h28v1h-28" />
129
+ <path fill="#FFFFFF" d="M2 10h23v1h-23m24 -1h3v1h-3m4 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-34 0h10v1h-10m11 -1h3v1h-3m4 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-33 0h21v1h-21m22 -1h3v1h-3m4 -1h3v1h-3m4 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-34 0h8v1h-8m9 -1h3v1h-3m4 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-35 0h19v1h-19m20 -1h3v1h-3m4 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-34 0h6v1h-6m7 -1h3v1h-3m4 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m4 -1h1v1h-1m-33 0h17v1h-17m18 -1h3v1h-3m4 -1h3v1h-3m4 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-34 0h4v2h-4m5 -2h3v1h-3m4 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m4 -1h1v1h-1m-19 0h3v1h-3m8 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-34 0h2v1h-2m3 -1h1v1h-1m14 -1h1v1h-1m2 -1h1v1h-1m6 -1h1v1h-1m4 -1h1v1h-1m-29 0h4v1h-4m16 -1h1v1h-1m2 -1h2v1h-2m6 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-33 0h3v1h-3m16 -1h1v1h-1m2 -1h1v1h-1m6 -1h1v1h-1m2 -1h1v1h-1m4 -1h1v1h-1m-31 0h4v1h-4m8 -1h3v1h-3m4 -1h3v1h-3m4 -1h1v1h-1m2 -1h1v1h-1m6 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-34 0h2v1h-2m3 -1h1v1h-1m6 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m6 -1h1v1h-1m-25 0h4v1h-4m8 -1h1v1h-1m2 -1h3v1h-3m4 -1h3v1h-3m4 -1h1v1h-1m6 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-33 0h1v1h-1m2 -1h1v1h-1m6 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m8 -1h1v1h-1m-27 0h4v1h-4m12 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m6 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-31 0h1v1h-1m2 -1h1v1h-1m10 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-17 0h4v1h-4m12 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m6 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-33 0h1v1h-1m2 -1h1v1h-1m10 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-19 0h3v1h-3m8 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m6 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m4 -1h1v1h-1m-31 0h1v1h-1m2 -1h1v1h-1m6 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m4 -1h1v1h-1m-17 0h1v1h-1m2 -1h2v1h-2m6 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m6 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m-33 0h1v1h-1m2 -1h1v1h-1m6 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m4 -1h1v1h-1m-19 0h3v1h-3m8 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m14 -1h1v1h-1m-31 0h1v1h-1m2 -1h1v1h-1m6 -1h1v1h-1m4 -1h1v1h-1m-13 0h1v1h-1m2 -1h2v1h-2m6 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m16 -1h1v1h-1m-33 0h1v1h-1m2 -1h1v1h-1m6 -1h1v1h-1m2 -1h1v1h-1m4 -1h1v1h-1m-15 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m4 -1h1v1h-1m4 -1h1v1h-1m4 -1h1v1h-1m-31 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m4 -1h1v1h-1m-9 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m4 -1h1v1h-1m4 -1h1v1h-1m-33 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m4 -1h1v1h-1m-11 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m4 -1h1v1h-1m4 -1h1v1h-1m4 -1h1v1h-1m-27 0h1v1h-1m4 -1h1v1h-1m-5 0h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m2 -1h1v1h-1m4 -1h1v1h-1m4 -1h1v1h-1m4 -1h1v1h-1m-33 0h1v1h-1m2 -1h1v1h-1m4 -1h1v1h-1" />
130
+ <path fill="#000000" d="M6 18h12v4h-12m16 -4h4v16h-4m-16 -12h4v4h-4m0 0h8v4h-8m0 0h4v8h-4m16 -4h12v4h-12" />
131
+ </g>
132
+ <g mask="url(#fl-mask)">
133
+ <rect class="fl-sweep" width="48" height="48" fill="url(#fl-glint)" />
134
+ </g>
135
+ </svg>
136
+ <svg class="fl-word" viewBox="0 -13 120 14" width="120" height="14" aria-hidden="true">
137
+ <path fill="#9aa4b2" d="M10 0L2.5 0L2.5-12.5L5-12.5L5-2.5L10-2.5L10 0ZM22.5 0L17.5 0L17.5-2.5L15-2.5L15-10L17.5-10L17.5-12.5L22.5-12.5L22.5-10L25-10L25-2.5L22.5-2.5L22.5 0ZM17.5-9.9L17.5-2.6L22.5-2.6L22.5-9.9L17.5-9.9ZM32.5 0L30 0L30-10L32.5-10L32.5-12.5L37.5-12.5L37.5-10L40-10L40 0L37.5 0L37.5-5L32.5-5L32.5 0ZM32.5-9.9L32.5-7.5L37.5-7.5L37.5-9.9L32.5-9.9ZM52.5 0L45 0L45-12.5L52.5-12.5L52.5-10L55-10L55-2.5L52.5-2.5L52.5 0ZM47.5-10L47.5-2.5L52.4-2.5L52.4-10L47.5-10ZM62.5 0L60 0L60-12.5L62.5-12.5L62.5 0ZM70 0L67.5 0L67.5-12.5L70-12.5L70-10L72.5-10L72.5-7.5L75-7.5L75-5L77.5-5L77.5-12.5L80-12.5L80 0L77.5 0L77.5-2.5L75-2.5L75-5L72.5-5L72.5-7.5L70-7.5L70 0ZM95-10L87.5-10L87.5-12.5L95-12.5L95-10ZM87.5-2.5L85-2.5L85-10L87.5-10L87.5-2.5ZM95-2.5L92.5-2.5L92.5-5L90-5L90-7.5L95-7.5L95-2.5ZM92.5 0L87.5 0L87.5-2.5L92.5-2.5L92.5 0Z" />
138
+ <path class="fl-dot" fill="#47cca9" d="M102.5 0L100 0L100-2.5L102.5-2.5L102.5 0Z" />
139
+ <path class="fl-dot fl-dot2" fill="#47cca9" d="M110 0L107.5 0L107.5-2.5L110-2.5L110 0Z" />
140
+ <path class="fl-dot fl-dot3" fill="#47cca9" d="M117.5 0L115 0L115-2.5L117.5-2.5L117.5 0Z" />
141
+ </svg>
142
+ </div>
143
+ </div>
144
+ <script type="module" src="/src/main.tsx"></script>
145
+ </body>
146
+ </html>
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "flatland-template-react",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "preview": "vite preview",
10
+ "typecheck": "tsc --noEmit",
11
+ "lint": "oxlint .",
12
+ "format": "oxfmt .",
13
+ "format:check": "oxfmt --check .",
14
+ "test": "vitest run",
15
+ "test:watch": "vitest",
16
+ "test:e2e": "playwright test",
17
+ "test:e2e:install": "playwright install chromium"
18
+ },
19
+ "dependencies": {
20
+ "@react-three/fiber": "10.0.0-alpha.2",
21
+ "koota": "^0.6.5",
22
+ "react": "^19.2.0",
23
+ "react-dom": "^19.2.0",
24
+ "three": "^0.183.1",
25
+ "three-flatland": "^0.1.0-alpha.8"
26
+ },
27
+ "devDependencies": {
28
+ "@playwright/test": "^1.61.1",
29
+ "@types/react": "^19.0.2",
30
+ "@types/react-dom": "^19.0.2",
31
+ "@types/three": "^0.183.1",
32
+ "@vitejs/plugin-react": "^4.3.4",
33
+ "babel-plugin-react-compiler": "^1.0.0",
34
+ "oxfmt": "^0.59.0",
35
+ "oxlint": "^1.74.0",
36
+ "typescript": "^5.7.3",
37
+ "vite": "^7.3.6",
38
+ "vitest": "^3.2.6"
39
+ },
40
+ "nx": {
41
+ "tags": [
42
+ "type:template"
43
+ ],
44
+ "targets": {
45
+ "lint": {
46
+ "executor": "nx:run-commands",
47
+ "options": {
48
+ "command": "oxlint --type-aware .",
49
+ "cwd": "{projectRoot}"
50
+ }
51
+ }
52
+ }
53
+ }
54
+ }
@@ -0,0 +1,41 @@
1
+ import { defineConfig, devices } from '@playwright/test'
2
+
3
+ /**
4
+ * End-to-end config. `webServer` starts `npm run dev` on a fixed port and tears
5
+ * it down when the run finishes, so `npm run test:e2e` is the whole command —
6
+ * there is no separate "start the server first" step to forget.
7
+ *
8
+ * One-time setup: `npm run test:e2e:install` downloads the Chromium build
9
+ * Playwright drives.
10
+ */
11
+
12
+ const PORT = 5183
13
+ const HOST = '127.0.0.1'
14
+ const IS_CI = !!process.env.CI
15
+
16
+ export default defineConfig({
17
+ testDir: './e2e',
18
+ fullyParallel: true,
19
+ forbidOnly: IS_CI,
20
+ retries: IS_CI ? 2 : 0,
21
+ workers: IS_CI ? 1 : undefined,
22
+ reporter: 'list',
23
+ timeout: 60_000,
24
+ expect: { timeout: 15_000 },
25
+ use: {
26
+ baseURL: `http://${HOST}:${PORT}/`,
27
+ trace: 'on-first-retry',
28
+ screenshot: 'only-on-failure',
29
+ },
30
+ projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
31
+ webServer: {
32
+ // `--strictPort` so a squatter on the port fails loudly instead of Vite
33
+ // quietly picking another one and the tests hitting a stale app.
34
+ command: `npm run dev -- --port ${PORT} --strictPort --host ${HOST}`,
35
+ url: `http://${HOST}:${PORT}/`,
36
+ reuseExistingServer: !IS_CI,
37
+ timeout: 60_000,
38
+ stdout: 'ignore',
39
+ stderr: 'pipe',
40
+ },
41
+ })