@vibes.diy/prompts 14.3.86 → 14.3.88
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.
- package/llms/index.d.ts +2 -1
- package/llms/index.js +3 -0
- package/llms/index.js.map +1 -1
- package/llms/p5.js +1 -1
- package/llms/p5.js.map +1 -1
- package/llms/physics.js +1 -1
- package/llms/physics.js.map +1 -1
- package/llms/three-js.js +1 -1
- package/llms/three-js.js.map +1 -1
- package/llms/three-js.md +107 -20
- package/llms/touch-games.d.ts +2 -0
- package/llms/touch-games.js +13 -0
- package/llms/touch-games.js.map +1 -0
- package/llms/touch-games.md +409 -0
- package/llms/voxel.js +1 -1
- package/llms/voxel.js.map +1 -1
- package/llms/voxel.md +11 -0
- package/llms/worlds.js +1 -1
- package/llms/worlds.js.map +1 -1
- package/package.json +5 -5
- package/system-prompt-initial-oneshot.md +1 -0
- package/system-prompt-initial.md +1 -0
- package/system-prompt.md +1 -0
package/llms/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export { d3Config } from "./d3.js";
|
|
|
9
9
|
export { threeJsConfig } from "./three-js.js";
|
|
10
10
|
export { p5Config } from "./p5.js";
|
|
11
11
|
export { voxelConfig } from "./voxel.js";
|
|
12
|
+
export { touchGamesConfig } from "./touch-games.js";
|
|
12
13
|
export { webxrConfig } from "./webxr.js";
|
|
13
14
|
export { useViewerConfig } from "./use-viewer.js";
|
|
14
15
|
export { useVibeConfig } from "./use-vibe.js";
|
|
@@ -53,5 +54,5 @@ export { poufLandingConfig } from "./pouf-landing.js";
|
|
|
53
54
|
export { poufCalendarConfig } from "./pouf-calendar.js";
|
|
54
55
|
export { poufTestimonialsConfig } from "./pouf-testimonials.js";
|
|
55
56
|
export type { LlmConfig } from "./types.js";
|
|
56
|
-
export declare const allConfigs: readonly [import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig];
|
|
57
|
+
export declare const allConfigs: readonly [import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig];
|
|
57
58
|
export declare const FAST_SKILLS: readonly ["callai", "image-gen", "fireproof", "three-js", "d3", "webxr", "calendar", "spotify"];
|
package/llms/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import { d3Config } from "./d3.js";
|
|
|
9
9
|
import { threeJsConfig } from "./three-js.js";
|
|
10
10
|
import { p5Config } from "./p5.js";
|
|
11
11
|
import { voxelConfig } from "./voxel.js";
|
|
12
|
+
import { touchGamesConfig } from "./touch-games.js";
|
|
12
13
|
import { webxrConfig } from "./webxr.js";
|
|
13
14
|
import { useViewerConfig } from "./use-viewer.js";
|
|
14
15
|
import { useVibeConfig } from "./use-vibe.js";
|
|
@@ -63,6 +64,7 @@ export { d3Config } from "./d3.js";
|
|
|
63
64
|
export { threeJsConfig } from "./three-js.js";
|
|
64
65
|
export { p5Config } from "./p5.js";
|
|
65
66
|
export { voxelConfig } from "./voxel.js";
|
|
67
|
+
export { touchGamesConfig } from "./touch-games.js";
|
|
66
68
|
export { webxrConfig } from "./webxr.js";
|
|
67
69
|
export { useViewerConfig } from "./use-viewer.js";
|
|
68
70
|
export { useVibeConfig } from "./use-vibe.js";
|
|
@@ -114,6 +116,7 @@ export const allConfigs = [
|
|
|
114
116
|
threeJsConfig,
|
|
115
117
|
p5Config,
|
|
116
118
|
voxelConfig,
|
|
119
|
+
touchGamesConfig,
|
|
117
120
|
fireproofConfig,
|
|
118
121
|
webxrConfig,
|
|
119
122
|
useViewerConfig,
|
package/llms/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../jsr/llms/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../jsr/llms/index.ts"],"names":[],"mappings":"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"}
|
package/llms/p5.js
CHANGED
|
@@ -7,7 +7,7 @@ export const p5Config = {
|
|
|
7
7
|
"gallery renders with noLoop, and fragment shaders. Covers instance mode in React (one " +
|
|
8
8
|
"useEffect, instance.remove() on cleanup), the version-pinned esm.sh import, sizing from " +
|
|
9
9
|
"the host element, pixelDensity, randomSeed/noiseSeed determinism, trails, and touch. " +
|
|
10
|
-
"p5, p5.js, processing, sketch, creative coding, generative art, canvas art, noise field",
|
|
10
|
+
"p5, p5.js, processing, sketch, creative coding, generative art, canvas art, noise field. Anything played or steered also loads touch-games for phone controls",
|
|
11
11
|
cues: [
|
|
12
12
|
"p5",
|
|
13
13
|
"p5.js",
|
package/llms/p5.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"p5.js","sourceRoot":"","sources":["../../jsr/llms/p5.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,QAAQ,GAAc;IACjC,IAAI,EAAE,IAAI;IACV,KAAK,EAAE,OAAO;IACd,WAAW,EACT,uFAAuF;QACvF,wFAAwF;QACxF,sFAAsF;QACtF,wFAAwF;QACxF,0FAA0F;QAC1F,uFAAuF;QACvF
|
|
1
|
+
{"version":3,"file":"p5.js","sourceRoot":"","sources":["../../jsr/llms/p5.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,QAAQ,GAAc;IACjC,IAAI,EAAE,IAAI;IACV,KAAK,EAAE,OAAO;IACd,WAAW,EACT,uFAAuF;QACvF,wFAAwF;QACxF,sFAAsF;QACtF,wFAAwF;QACxF,0FAA0F;QAC1F,uFAAuF;QACvF,+JAA+J;IACjK,IAAI,EAAE;QACJ,IAAI;QACJ,OAAO;QACP,YAAY;QACZ,QAAQ;QACR,gBAAgB;QAChB,iBAAiB;QACjB,YAAY;QACZ,cAAc;QACd,gBAAgB;QAChB,cAAc;QACd,aAAa;KACd;IAID,YAAY,EAAE,2BAA2B;IACzC,UAAU,EAAE,IAAI;IAChB,UAAU,EAAE,SAAS;CACtB,CAAC"}
|
package/llms/physics.js
CHANGED
|
@@ -10,7 +10,7 @@ export const physicsConfig = {
|
|
|
10
10
|
"constraint with pixelRatio for touch, pieces as Fireproof documents rebuilt into bodies, " +
|
|
11
11
|
"seeded replays, labelled collisions and sensors for scoring, and the Render.create ordering trap. " +
|
|
12
12
|
"physics, gravity, bounce, bouncy, collide, collision, tumble, fall, marble run, rube goldberg, " +
|
|
13
|
-
"plinko, pachinko, catapult, slingshot, dominoes, knock down the tower, pinball, ragdoll, soft body, matter.js",
|
|
13
|
+
"plinko, pachinko, catapult, slingshot, dominoes, knock down the tower, pinball, ragdoll, soft body, matter.js. Anything played or steered also loads touch-games for phone controls",
|
|
14
14
|
cues: [
|
|
15
15
|
"physics",
|
|
16
16
|
"gravity",
|
package/llms/physics.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"physics.js","sourceRoot":"","sources":["../../jsr/llms/physics.ts"],"names":[],"mappings":"AAKA,MAAM,CAAC,MAAM,aAAa,GAAc;IACtC,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,qBAAqB;IAC5B,WAAW,EACT,kGAAkG;QAClG,+FAA+F;QAC/F,4FAA4F;QAC5F,gGAAgG;QAChG,6FAA6F;QAC7F,2FAA2F;QAC3F,2FAA2F;QAC3F,oGAAoG;QACpG,iGAAiG;QACjG
|
|
1
|
+
{"version":3,"file":"physics.js","sourceRoot":"","sources":["../../jsr/llms/physics.ts"],"names":[],"mappings":"AAKA,MAAM,CAAC,MAAM,aAAa,GAAc;IACtC,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,qBAAqB;IAC5B,WAAW,EACT,kGAAkG;QAClG,+FAA+F;QAC/F,4FAA4F;QAC5F,gGAAgG;QAChG,6FAA6F;QAC7F,2FAA2F;QAC3F,2FAA2F;QAC3F,oGAAoG;QACpG,iGAAiG;QACjG,qLAAqL;IACvL,IAAI,EAAE;QACJ,SAAS;QACT,SAAS;QACT,QAAQ;QACR,QAAQ;QACR,UAAU;QACV,SAAS;QACT,WAAW;QACX,YAAY;QACZ,QAAQ;QACR,YAAY;QACZ,QAAQ;QACR,eAAe;QACf,QAAQ;QACR,UAAU;QACV,UAAU;QACV,WAAW;QACX,QAAQ;QACR,UAAU;QACV,YAAY;QACZ,YAAY;QACZ,SAAS;QACT,SAAS;QACT,WAAW;QACX,WAAW;QACX,WAAW;QACX,UAAU;QACV,YAAY;KACb;CACF,CAAC"}
|
package/llms/three-js.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const threeJsConfig = {
|
|
2
2
|
name: "three-js",
|
|
3
3
|
label: "Three.js",
|
|
4
|
-
description: "Three.js 3D graphics library for WebGL rendering: mesh geometry, materials, lighting, animation, cameras, textures, GLSL shaders, GLTF/OBJ/FBX model loading, physics, particle systems, post-processing, visual effects, 3js",
|
|
4
|
+
description: "Three.js 3D graphics library for WebGL rendering: mesh geometry, materials, lighting, animation, cameras, textures, GLSL shaders, GLTF/OBJ/FBX model loading, physics, particle systems, post-processing, visual effects, 3js. Anything played or steered also loads touch-games for phone controls",
|
|
5
5
|
cues: ["3d", "three.js", "threejs", "3js", "webgl", "shader", "shaders", "mesh", "gltf", "3d model", "3d scene"],
|
|
6
6
|
importModule: "three",
|
|
7
7
|
importName: "THREE",
|
package/llms/three-js.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"three-js.js","sourceRoot":"","sources":["../../jsr/llms/three-js.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,aAAa,GAAc;IACtC,IAAI,EAAE,UAAU;IAChB,KAAK,EAAE,UAAU;IACjB,WAAW,EACT
|
|
1
|
+
{"version":3,"file":"three-js.js","sourceRoot":"","sources":["../../jsr/llms/three-js.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,aAAa,GAAc;IACtC,IAAI,EAAE,UAAU;IAChB,KAAK,EAAE,UAAU;IACjB,WAAW,EACT,qSAAqS;IACvS,IAAI,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,CAAC;IAChH,YAAY,EAAE,OAAO;IACrB,UAAU,EAAE,OAAO;IACnB,UAAU,EAAE,WAAW;CACxB,CAAC"}
|
package/llms/three-js.md
CHANGED
|
@@ -75,7 +75,9 @@ export default function App() {
|
|
|
75
75
|
};
|
|
76
76
|
}, []);
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
// select-none touch-none: a drag on a phone steers the scene instead of
|
|
79
|
+
// selecting nearby text, opening the copy callout, or scrolling the page.
|
|
80
|
+
return <div ref={mountRef} className="w-full h-dvh select-none touch-none" />;
|
|
79
81
|
}
|
|
80
82
|
```
|
|
81
83
|
|
|
@@ -84,6 +86,91 @@ cube for real geometry, add `OrbitControls` or your own camera rig, load models,
|
|
|
84
86
|
etc. — but the lifecycle above (one `useEffect`, one loop, dispose on unmount)
|
|
85
87
|
stays the spine of every Three.js vibe.
|
|
86
88
|
|
|
89
|
+
## Full screen by default; a smaller view only when asked
|
|
90
|
+
|
|
91
|
+
A 3D game or scene you move through fills the screen: the canvas is the whole view
|
|
92
|
+
(`relative h-dvh w-full` root, `absolute inset-0` canvas — `h-dvh` tracks the phone's real
|
|
93
|
+
height as the address bar slides). A quiet HUD (a score, a hint) stays in a top corner; the
|
|
94
|
+
title, settings, scores, sharing and every other panel live behind **one** menu button in the
|
|
95
|
+
top-right corner (`IconButton`, never the bottom-right, which the platform owns) that toggles an
|
|
96
|
+
overlay — see the Sky Glider example below. The game stays in view the moment the overlay closes.
|
|
97
|
+
|
|
98
|
+
When the person asks for a smaller view — a model on a product page, a scene inside a card, a
|
|
99
|
+
viewer beside a form — give the viewport a box instead and keep the page around it:
|
|
100
|
+
|
|
101
|
+
```jsx
|
|
102
|
+
<Card>
|
|
103
|
+
<Stack gap={3}>
|
|
104
|
+
<Heading level={2}>Chair, in 3D</Heading>
|
|
105
|
+
<div ref={mountRef} className="w-full aspect-video overflow-hidden rounded-lg select-none touch-none" />
|
|
106
|
+
<Text size="sm" muted>
|
|
107
|
+
Drag to turn it around.
|
|
108
|
+
</Text>
|
|
109
|
+
</Stack>
|
|
110
|
+
</Card>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Size the renderer from the box (`mount.clientWidth`, `mount.clientHeight`) and watch it with a
|
|
114
|
+
`ResizeObserver`, since the box changes size without the window resizing.
|
|
115
|
+
|
|
116
|
+
## Touch controls on a phone
|
|
117
|
+
|
|
118
|
+
On a phone, every drag across the canvas and every press of an on-screen
|
|
119
|
+
button is a touch the browser would otherwise claim for itself: it selects
|
|
120
|
+
the nearest text, pops the copy / look-up callout, scrolls the page, or zooms.
|
|
121
|
+
Hand those touches to the scene:
|
|
122
|
+
|
|
123
|
+
- Put `select-none touch-none` on the mount `<div>` / `<canvas>` **and** on the
|
|
124
|
+
container that holds the HUD, labels and on-screen controls — a long press
|
|
125
|
+
on a joystick selects the label next to it if only the canvas is covered.
|
|
126
|
+
- Drive input with Pointer Events (`pointerdown` / `pointermove` / `pointerup`
|
|
127
|
+
/ `pointercancel`): one path for mouse, pen and finger. `touch-none` is what
|
|
128
|
+
lets the move events keep flowing during a drag.
|
|
129
|
+
- A button someone **holds** (thrust, fire, accelerate, a D-pad arrow) tracks
|
|
130
|
+
its own pressed state from the pointer events, captures the pointer so a
|
|
131
|
+
finger sliding off still releases cleanly, and cancels the long-press menu:
|
|
132
|
+
|
|
133
|
+
```jsx
|
|
134
|
+
import { IconButton } from "@vibes.diy/pouf";
|
|
135
|
+
|
|
136
|
+
function HoldButton({ icon, label, onChange }) {
|
|
137
|
+
const set = (on) => (e) => {
|
|
138
|
+
e.preventDefault();
|
|
139
|
+
if (on) e.currentTarget.setPointerCapture(e.pointerId);
|
|
140
|
+
onChange(on);
|
|
141
|
+
};
|
|
142
|
+
return (
|
|
143
|
+
<IconButton
|
|
144
|
+
icon={icon}
|
|
145
|
+
label={label}
|
|
146
|
+
size="lg"
|
|
147
|
+
variant="solid"
|
|
148
|
+
onPointerDown={set(true)}
|
|
149
|
+
onPointerUp={set(false)}
|
|
150
|
+
onPointerCancel={set(false)}
|
|
151
|
+
onContextMenu={(e) => e.preventDefault()}
|
|
152
|
+
/>
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// The wrapper carries select-none touch-none, and its buttons inherit both —
|
|
157
|
+
// the whole control area belongs to the finger:
|
|
158
|
+
<div className="relative h-dvh w-full select-none touch-none">
|
|
159
|
+
<div ref={mountRef} className="absolute inset-0" />
|
|
160
|
+
<div className="absolute bottom-6 left-6 flex gap-3">
|
|
161
|
+
<HoldButton icon="◀" label="Steer left" onChange={(on) => (input.current.left = on)} />
|
|
162
|
+
<HoldButton icon="▶" label="Steer right" onChange={(on) => (input.current.right = on)} />
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Read the held state (`input.current`) inside the animation loop, so a press
|
|
168
|
+
moves the scene every frame it's down. `OrbitControls` already speaks pointer
|
|
169
|
+
events; it still needs `touch-none` on its element to receive the drag.
|
|
170
|
+
|
|
171
|
+
For a game's control scheme — swipes that fire mid-move, a ghost stick under the thumb,
|
|
172
|
+
hold-to-boost, pull-and-release — load the `touch-games` skill beside this one.
|
|
173
|
+
|
|
87
174
|
## Core Setup
|
|
88
175
|
|
|
89
176
|
### Scene Graph Hierarchy
|
|
@@ -1213,7 +1300,7 @@ Structure your component in this order:
|
|
|
1213
1300
|
import React, { useState, useEffect, useRef, useCallback } from "react";
|
|
1214
1301
|
import { useFireproof } from "use-fireproof";
|
|
1215
1302
|
import * as THREE from "three";
|
|
1216
|
-
import { Card, Heading, Stack, Text } from "@vibes.diy/pouf";
|
|
1303
|
+
import { Card, Heading, IconButton, Stack, Text } from "@vibes.diy/pouf";
|
|
1217
1304
|
|
|
1218
1305
|
export default function SkyGlider() {
|
|
1219
1306
|
const { database, useLiveQuery } = useFireproof("skyGliderScores");
|
|
@@ -1239,6 +1326,7 @@ export default function SkyGlider() {
|
|
|
1239
1326
|
});
|
|
1240
1327
|
|
|
1241
1328
|
const [currentScore, setCurrentScore] = useState(0);
|
|
1329
|
+
const [menuOpen, setMenuOpen] = useState(false);
|
|
1242
1330
|
const { docs: scoreData } = useLiveQuery("type", { key: "score" }) || {
|
|
1243
1331
|
docs: [],
|
|
1244
1332
|
};
|
|
@@ -1611,25 +1699,24 @@ export default function SkyGlider() {
|
|
|
1611
1699
|
}, [initThreeJS, handleKeyDown, handleKeyUp, handleResize]);
|
|
1612
1700
|
|
|
1613
1701
|
return (
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
<
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
WASD/Arrows: Fly, Space: Thrust
|
|
1624
|
-
</Text>
|
|
1625
|
-
</Stack>
|
|
1626
|
-
</Card>
|
|
1702
|
+
// Full screen by default: the game is the whole view. The score stays as a quiet HUD; the title,
|
|
1703
|
+
// the controls legend and the high scores live behind ONE menu button, so nothing covers play.
|
|
1704
|
+
<div className="relative h-dvh w-full overflow-hidden bg-background text-foreground font-sans select-none touch-none">
|
|
1705
|
+
<canvas ref={canvasRef} className="absolute inset-0 select-none touch-none" />
|
|
1706
|
+
<div className="absolute top-3 left-3 pointer-events-none">
|
|
1707
|
+
<Text num>Score: {currentScore}</Text>
|
|
1708
|
+
</div>
|
|
1709
|
+
<div className="absolute top-3 right-3">
|
|
1710
|
+
<IconButton icon="☰" label={menuOpen ? "Hide menu" : "Show menu"} onClick={() => setMenuOpen(!menuOpen)} />
|
|
1627
1711
|
</div>
|
|
1628
|
-
{
|
|
1629
|
-
<div className="absolute top-
|
|
1712
|
+
{menuOpen && (
|
|
1713
|
+
<div className="absolute top-16 right-3 w-72">
|
|
1630
1714
|
<Card>
|
|
1631
1715
|
<Stack gap={2}>
|
|
1632
|
-
<Heading level={2}>
|
|
1716
|
+
<Heading level={2}>Sky Glider</Heading>
|
|
1717
|
+
<Text size="sm" muted>
|
|
1718
|
+
WASD/Arrows: Fly, Space: Thrust
|
|
1719
|
+
</Text>
|
|
1633
1720
|
{scoreData
|
|
1634
1721
|
.sort((a, b) => b.value - a.value)
|
|
1635
1722
|
.slice(0, 3)
|
|
@@ -2010,8 +2097,8 @@ export default function HalftoneArtStudio() {
|
|
|
2010
2097
|
};
|
|
2011
2098
|
|
|
2012
2099
|
return (
|
|
2013
|
-
<div className="relative h-
|
|
2014
|
-
<canvas ref={canvasRef} className="absolute inset-0" />
|
|
2100
|
+
<div className="relative h-dvh w-full overflow-hidden bg-background text-foreground font-sans">
|
|
2101
|
+
<canvas ref={canvasRef} className="absolute inset-0 select-none touch-none" />
|
|
2015
2102
|
|
|
2016
2103
|
{/* One control panel. The pouf `Card` IS the panel — it takes no className,
|
|
2017
2104
|
so a plain wrapper carries its placement; the cushion, corner and ink are its own. */}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const touchGamesConfig = {
|
|
2
|
+
name: "touch-games",
|
|
3
|
+
label: "Touch games",
|
|
4
|
+
description: "Phone-first controls for anything played or steered in real time — load it beside three-js, voxel, " +
|
|
5
|
+
"physics, p5, worlds or pouf-game for every game, 3D scene you move through, or canvas toy: the play area " +
|
|
6
|
+
"is the controller (select-none touch-none surface, Pointer Events with pointer capture), swipes that fire " +
|
|
7
|
+
"mid-move with chaining, tap vs swipe on one surface, a floating ghost stick under the thumb, hold-to-boost " +
|
|
8
|
+
"with drag steering, pull-back-and-release aiming, gesture trails and feedback, keyboard twins for desktop. " +
|
|
9
|
+
"game, mobile game, touch controls, swipe, joystick, runner, racer, flyer, shooter, snake, slingshot, " +
|
|
10
|
+
"platformer, arcade, one thumb",
|
|
11
|
+
cues: ["swipe", "touch controls", "mobile game", "phone game", "joystick", "runner", "arcade", "thumb"],
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=touch-games.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"touch-games.js","sourceRoot":"","sources":["../../jsr/llms/touch-games.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,gBAAgB,GAAc;IACzC,IAAI,EAAE,aAAa;IACnB,KAAK,EAAE,aAAa;IACpB,WAAW,EACT,qGAAqG;QACrG,2GAA2G;QAC3G,4GAA4G;QAC5G,6GAA6G;QAC7G,6GAA6G;QAC7G,uGAAuG;QACvG,+BAA+B;IACjC,IAAI,EAAE,CAAC,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC;CACxG,CAAC"}
|
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
# Touch games — the play area is the controller
|
|
2
|
+
|
|
3
|
+
Load this beside whatever draws the game — `three-js`, `voxel`, `physics`, `p5`, `worlds`,
|
|
4
|
+
`pouf-game`, a plain `<canvas>` — whenever the app is something you play or steer in real time:
|
|
5
|
+
a runner, a racer, a flyer, a shooter, a snake, a launcher, a platformer, a pinball table, a
|
|
6
|
+
marble maze, a 3D scene you move through, a toy you drag around. Most people open a vibe on a
|
|
7
|
+
phone, one-handed. The game that feels great there is the one where the thumb rests **on the
|
|
8
|
+
play area itself** and swipes, holds and pulls, with nothing to hunt for.
|
|
9
|
+
|
|
10
|
+
The play area is the controller: the whole game surface carries `select-none touch-none`, and no fixed on-screen buttons or joystick stand in for the thumb.
|
|
11
|
+
|
|
12
|
+
The game fills the screen: the play surface is a `relative h-dvh w-full` root, never a card on a scrolling page — scores, records and leaderboards go behind the menu button, not below the game.
|
|
13
|
+
|
|
14
|
+
Three things make that true, and each has one home:
|
|
15
|
+
|
|
16
|
+
1. **The surface belongs to the finger.** `select-none touch-none` on the full-screen container
|
|
17
|
+
and the `<canvas>` (and the HUD inside it), plus `onContextMenu` cancelled, so a press or drag
|
|
18
|
+
steers the game while nearby text stays put — no selection highlight, no copy callout, no
|
|
19
|
+
page scroll, no pinch zoom, no pull-to-refresh.
|
|
20
|
+
2. **One input path for every pointer.** Input runs on Pointer Events with `setPointerCapture` on one tracked `pointerId`, so mouse, pen and finger share one path.
|
|
21
|
+
3. **The game says what it understood.** A short fading trail under the finger, plus an arrow,
|
|
22
|
+
ghost stick or aim line, so the player sees which gesture the game read.
|
|
23
|
+
|
|
24
|
+
## Pick the scheme from the game
|
|
25
|
+
|
|
26
|
+
| The game | The scheme | Feels like |
|
|
27
|
+
| --- | --- | --- |
|
|
28
|
+
| Moves in steps: lanes, a grid, a snake, 2048, a turn-based dodge | **Flick** — each swipe is one move | Subway runner, snake |
|
|
29
|
+
| Steers freely in 2D: an arena, a top-down racer, a fish tank | **Floating stick** — a ghost stick appears under the thumb | twin-stick lite |
|
|
30
|
+
| Flies or drives forward: a glider, a ship, a car | **Hold + steer** — thumb down = throttle, offset = steer | canyon flyer |
|
|
31
|
+
| Aims then commits: a launcher, golf, pool, basketball | **Pull back + release** | slingshot |
|
|
32
|
+
| Moves in steps AND has one action | **Flick + tap** on the same surface | snake with a dash |
|
|
33
|
+
|
|
34
|
+
A desktop visitor gets the same game: the mouse drives the same pointer path, and arrow keys /
|
|
35
|
+
WASD / Space call the same action functions the gestures call.
|
|
36
|
+
|
|
37
|
+
## The surface
|
|
38
|
+
|
|
39
|
+
```jsx
|
|
40
|
+
export default function App() {
|
|
41
|
+
const rootRef = React.useRef(null);
|
|
42
|
+
const canvasRef = React.useRef(null);
|
|
43
|
+
// … engine in one useEffect([]) — see the input sections below …
|
|
44
|
+
return (
|
|
45
|
+
<div
|
|
46
|
+
ref={rootRef}
|
|
47
|
+
className="relative h-dvh w-full overflow-hidden bg-background select-none touch-none"
|
|
48
|
+
onContextMenu={(e) => e.preventDefault()}
|
|
49
|
+
>
|
|
50
|
+
<canvas ref={canvasRef} className="absolute inset-0 block w-full h-full select-none touch-none" />
|
|
51
|
+
<div className="absolute top-3 left-3 pointer-events-none">
|
|
52
|
+
<p className="text-2xl font-bold text-foreground">{score}</p>
|
|
53
|
+
<p className="text-sm text-muted-foreground">swipe ← → to switch lanes · ↑ jump · ↓ slide</p>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
- **Full screen is the default.** The game is the whole view: a `relative h-dvh w-full` root
|
|
61
|
+
with the canvas `absolute inset-0`. Everything that is not play — the title, settings, high
|
|
62
|
+
scores, sharing, instructions beyond the one-line hint — lives behind **one** menu button in
|
|
63
|
+
the top-right corner that toggles an overlay (the worked example below shows it). When the
|
|
64
|
+
person asks for a smaller view, `three-js` shows the boxed viewport.
|
|
65
|
+
- **Listen on the root**, not the canvas, so a thumb that lands on the HUD still steers. The HUD
|
|
66
|
+
is `pointer-events-none`.
|
|
67
|
+
- **The bottom-right corner belongs to the platform** (the Vibes logo sits there). Keep the HUD
|
|
68
|
+
top-left and leave the lower third open — that is where the thumb rests, and where a launcher's
|
|
69
|
+
pull starts.
|
|
70
|
+
- **One line of hint text** names the gestures in the HUD; that line is the whole tutorial.
|
|
71
|
+
- **HUD ink contrasts with what the canvas paints.** A neon game painted near-black needs light
|
|
72
|
+
HUD text: either paint the canvas from the theme (`bg-background`, ink read from the root's
|
|
73
|
+
computed `color`, as the worked example does) or give the HUD a `Card` / `bg-background/80`
|
|
74
|
+
backing so the score reads on any frame.
|
|
75
|
+
- **Start on the first touch.** The touch that starts a run only starts it (it does not also
|
|
76
|
+
jump or dash); a tap restarts after game over, after a short grace period so a frantic swipe
|
|
77
|
+
at the moment of the crash does not skip the score screen.
|
|
78
|
+
|
|
79
|
+
## Flick: a swipe fires during the move
|
|
80
|
+
|
|
81
|
+
A swipe fires during the move, the moment the finger has travelled past the threshold, never on finger-up. That single choice is what makes a lane change feel instant. The rest of the recogniser:
|
|
82
|
+
|
|
83
|
+
- **Dominant axis with a ratio guard.** Fire only when one axis beats the other by about 1.35×;
|
|
84
|
+
a diagonal keeps waiting until the stroke commits.
|
|
85
|
+
- **Chaining.** After a swipe fires, the anchor follows the finger while it keeps going the
|
|
86
|
+
same way, so a long stroke fires once — and when the finger bends, it fires again. One
|
|
87
|
+
L-shaped stroke is "lane, then jump"; the thumb never has to lift.
|
|
88
|
+
- **A tap is short and small.** Under ~12px of travel and ~250ms. Travel between the tap limit
|
|
89
|
+
and the swipe threshold fires nothing: that gap is what keeps taps and swipes from ever
|
|
90
|
+
misfiring as each other.
|
|
91
|
+
- **Buffer what can't run yet.** A swipe up made mid-jump queues for ~380ms and runs on landing.
|
|
92
|
+
|
|
93
|
+
The worked example at the end of this doc carries the whole recogniser as `attachSwipe` — copy it
|
|
94
|
+
as written. The two lines that are easiest to lose are the ones that matter most: `g.last` and the
|
|
95
|
+
anchor that follows the finger along the last direction. Without them a long stroke fires a move
|
|
96
|
+
every 26px — one swipe up becomes four jumps.
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
A grid game ignores a swipe straight back into itself (a snake never reverses) and keeps a
|
|
100
|
+
two-turn queue so a fast thumb never loses a turn.
|
|
101
|
+
|
|
102
|
+
## Floating stick: the stick appears under the thumb
|
|
103
|
+
|
|
104
|
+
Wherever the thumb lands becomes the stick's centre. The offset from that point is direction
|
|
105
|
+
and throttle: nothing inside a small dead zone, full speed a thumb-width out, on a curve that
|
|
106
|
+
gives fine control near the centre. Past the leash, the centre drifts after the thumb, so a long
|
|
107
|
+
drag never runs out of stick. Release lets the ship coast down instead of stopping dead.
|
|
108
|
+
|
|
109
|
+
```jsx
|
|
110
|
+
const STICK = { deadZone: 8, fullAt: 60, leash: 74, curve: 1.35 };
|
|
111
|
+
|
|
112
|
+
// stick = { cx, cy } set on pointerdown at the touch point; call on every pointermove.
|
|
113
|
+
function readStick(stick, x, y) {
|
|
114
|
+
let dx = x - stick.cx;
|
|
115
|
+
let dy = y - stick.cy;
|
|
116
|
+
let d = Math.hypot(dx, dy);
|
|
117
|
+
if (d > STICK.leash) {
|
|
118
|
+
const k = (d - STICK.leash) / d; // drag the centre along behind the thumb
|
|
119
|
+
stick.cx += dx * k;
|
|
120
|
+
stick.cy += dy * k;
|
|
121
|
+
dx = x - stick.cx;
|
|
122
|
+
dy = y - stick.cy;
|
|
123
|
+
d = STICK.leash;
|
|
124
|
+
}
|
|
125
|
+
if (d < STICK.deadZone) return { x: 0, y: 0, power: 0 };
|
|
126
|
+
const lin = Math.min(1, (d - STICK.deadZone) / (STICK.fullAt - STICK.deadZone));
|
|
127
|
+
const power = Math.pow(lin, STICK.curve);
|
|
128
|
+
return { x: (dx / d) * power, y: (dy / d) * power, power };
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Draw the ghost stick at the centre — a faint ring at `fullAt`, a knob at the thumb, an arc that
|
|
133
|
+
brightens at full power — and fade it out over ~170ms on release. Smooth the velocity toward the
|
|
134
|
+
stick's target (~85ms to accelerate, ~500ms to coast) so movement feels buttery, not twitchy.
|
|
135
|
+
|
|
136
|
+
## Hold + steer: thumb down is the throttle
|
|
137
|
+
|
|
138
|
+
For anything that flies or drives forward. Thumb down anywhere = boost; lift = glide (slow, sink,
|
|
139
|
+
level the wings). While held, the thumb's offset from where it landed banks and pitches, like a
|
|
140
|
+
trackpad, with the same leash as the floating stick (~85px for full bank, a 6px dead zone,
|
|
141
|
+
smoothed). A quick flick while holding — over ~34px at ≥1 px/ms within ~90ms — is the one special
|
|
142
|
+
move (a barrel roll, a dodge); re-anchor the touch point after it so the flick does not also leave
|
|
143
|
+
the ship pitched. Sell the throttle with the camera: widen the field of view (66° → 86°) and let
|
|
144
|
+
the chase camera lag while boosting.
|
|
145
|
+
|
|
146
|
+
Hitting a wall grinds — bounce, lose speed, reset the combo, throw sparks — so one steering
|
|
147
|
+
mistake costs momentum rather than the run.
|
|
148
|
+
|
|
149
|
+
## Pull back + release
|
|
150
|
+
|
|
151
|
+
The pull starts **anywhere** on the play area — the thumb never has to find the projectile. The
|
|
152
|
+
launch goes opposite the drag, power is the drag length up to a cap (~120px), and a dotted arc
|
|
153
|
+
previews the shot while the thumb is down. Release fires; dragging back inside a small ring at
|
|
154
|
+
the start point (~24px) cancels, and the ring says so. The thumb usually starts in the lower
|
|
155
|
+
third, so put the ground at about two-thirds of the screen height and leave room below it to
|
|
156
|
+
pull. Preview only part of the arc (about 0.85s of flight) so aiming stays a skill.
|
|
157
|
+
|
|
158
|
+
## Teach the gesture once, then get out of the way
|
|
159
|
+
|
|
160
|
+
- **A ghost thumb on first run.** Before the first touch, a translucent thumb with a short motion
|
|
161
|
+
streak plays the game's one gesture on a loop, with a short caption ("hold to fly · slide to
|
|
162
|
+
steer"). The first touch on the play area dismisses it for good; a tap on the menu button does
|
|
163
|
+
not.
|
|
164
|
+
- **The menu pauses the game.** Opening the menu freezes the clock (and the screen shake); closing
|
|
165
|
+
it resumes. The menu button stops `pointerdown` from reaching the root, so pressing it never
|
|
166
|
+
steers, jumps or fires.
|
|
167
|
+
|
|
168
|
+
## Feedback the player can read
|
|
169
|
+
|
|
170
|
+
Every game draws the finger trail and the what-fired cue; they are how a player on a phone learns
|
|
171
|
+
the controls without a tutorial.
|
|
172
|
+
|
|
173
|
+
- **Finger trail:** keep the last ~260ms of pointer positions and draw them fading, on a canvas
|
|
174
|
+
layer over the game.
|
|
175
|
+
- **What fired:** a short arrow flash at the finger for a swipe, a pulse ring for a tap, an
|
|
176
|
+
outline arrow when an action is queued.
|
|
177
|
+
- **Juice on every action:** a few particles, a small screen shake on impacts,
|
|
178
|
+
`navigator.vibrate?.(10)` where the phone supports it, and WebAudio beeps created on the first
|
|
179
|
+
gesture (audio can only start from a user gesture).
|
|
180
|
+
|
|
181
|
+
## The engine around it
|
|
182
|
+
|
|
183
|
+
One `useEffect(() => {…}, [])` owns the loop, the listeners and the cleanup; the loop is one
|
|
184
|
+
`requestAnimationFrame` with `dt` clamped to 1/30s so a backgrounded tab does not teleport the
|
|
185
|
+
player. Size the canvas from the root's box with `devicePixelRatio` capped at 2, re-size on
|
|
186
|
+
`resize`, and remove every listener and dispose every GPU object on unmount. Keep the gesture
|
|
187
|
+
numbers in one tunables object at the top of `App.jsx` with a comment on each, so the feel can be
|
|
188
|
+
tuned in one place.
|
|
189
|
+
|
|
190
|
+
## Worked example: a three-lane dodge, start to finish
|
|
191
|
+
|
|
192
|
+
The whole shape in one file: the surface, the mid-move swipe recogniser, a keyboard twin, a
|
|
193
|
+
finger trail, and an engine that cleans up after itself. Grow it into a runner, a racer or a
|
|
194
|
+
snake by changing what `move()` does.
|
|
195
|
+
|
|
196
|
+
### `App.jsx`
|
|
197
|
+
```jsx
|
|
198
|
+
import React from "react";
|
|
199
|
+
import { Card, Heading, IconButton, Stack, Text } from "@vibes.diy/pouf";
|
|
200
|
+
|
|
201
|
+
const TUNE = {
|
|
202
|
+
swipePx: 26, // travel from the anchor before a swipe fires (raise to 30–34 if a resting thumb misfires)
|
|
203
|
+
axisRatio: 1.35, // dominant axis must beat the other by this much
|
|
204
|
+
chainCooldownMs: 60, // min gap between two swipes inside one touch
|
|
205
|
+
tapPx: 12, // a touch that travels less than this…
|
|
206
|
+
tapMs: 250, // …and lifts within this is a tap
|
|
207
|
+
trailMs: 260, // finger trail fade
|
|
208
|
+
laneMs: 120, // lane-change tween
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// Pointer Events on the root: fires a swipe mid-move, chains when the stroke bends, taps on a short touch.
|
|
212
|
+
function attachSwipe(root, { onSwipe, onTap, onTrail }) {
|
|
213
|
+
const g = { id: null, ax: 0, ay: 0, sx: 0, sy: 0, t0: 0, last: null, lastAt: 0, travel: 0 };
|
|
214
|
+
const DIRS = { left: [-1, 0], right: [1, 0], up: [0, -1], down: [0, 1] };
|
|
215
|
+
const at = (e) => {
|
|
216
|
+
const r = root.getBoundingClientRect();
|
|
217
|
+
return [e.clientX - r.left, e.clientY - r.top];
|
|
218
|
+
};
|
|
219
|
+
const down = (e) => {
|
|
220
|
+
if (g.id !== null) return;
|
|
221
|
+
g.id = e.pointerId;
|
|
222
|
+
root.setPointerCapture(e.pointerId);
|
|
223
|
+
const [x, y] = at(e);
|
|
224
|
+
Object.assign(g, { ax: x, ay: y, sx: x, sy: y, t0: performance.now(), last: null, lastAt: 0, travel: 0 });
|
|
225
|
+
onTrail(x, y);
|
|
226
|
+
};
|
|
227
|
+
const move = (e) => {
|
|
228
|
+
if (e.pointerId !== g.id) return;
|
|
229
|
+
const [x, y] = at(e);
|
|
230
|
+
onTrail(x, y);
|
|
231
|
+
g.travel = Math.max(g.travel, Math.hypot(x - g.sx, y - g.sy));
|
|
232
|
+
if (g.last) {
|
|
233
|
+
const [ux, uy] = DIRS[g.last];
|
|
234
|
+
const along = (x - g.ax) * ux + (y - g.ay) * uy;
|
|
235
|
+
if (along > 0) (g.ax += ux * along), (g.ay += uy * along);
|
|
236
|
+
}
|
|
237
|
+
const dx = x - g.ax;
|
|
238
|
+
const dy = y - g.ay;
|
|
239
|
+
if (Math.hypot(dx, dy) < TUNE.swipePx) return;
|
|
240
|
+
const ax = Math.abs(dx);
|
|
241
|
+
const ay = Math.abs(dy);
|
|
242
|
+
const dir =
|
|
243
|
+
ax >= ay * TUNE.axisRatio ? (dx < 0 ? "left" : "right") : ay >= ax * TUNE.axisRatio ? (dy < 0 ? "up" : "down") : null;
|
|
244
|
+
if (!dir) return;
|
|
245
|
+
const now = performance.now();
|
|
246
|
+
g.ax = x;
|
|
247
|
+
g.ay = y;
|
|
248
|
+
if (dir === g.last || now - g.lastAt < TUNE.chainCooldownMs) return;
|
|
249
|
+
g.last = dir;
|
|
250
|
+
g.lastAt = now;
|
|
251
|
+
onSwipe(dir);
|
|
252
|
+
};
|
|
253
|
+
const up = (e) => {
|
|
254
|
+
if (e.pointerId !== g.id) return;
|
|
255
|
+
g.id = null;
|
|
256
|
+
const quick = performance.now() - g.t0 < TUNE.tapMs;
|
|
257
|
+
if (e.type === "pointerup" && !g.last && g.travel < TUNE.tapPx && quick) onTap();
|
|
258
|
+
};
|
|
259
|
+
root.addEventListener("pointerdown", down);
|
|
260
|
+
root.addEventListener("pointermove", move);
|
|
261
|
+
root.addEventListener("pointerup", up);
|
|
262
|
+
root.addEventListener("pointercancel", up);
|
|
263
|
+
return () => {
|
|
264
|
+
root.removeEventListener("pointerdown", down);
|
|
265
|
+
root.removeEventListener("pointermove", move);
|
|
266
|
+
root.removeEventListener("pointerup", up);
|
|
267
|
+
root.removeEventListener("pointercancel", up);
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export default function App() {
|
|
272
|
+
const rootRef = React.useRef(null);
|
|
273
|
+
const canvasRef = React.useRef(null);
|
|
274
|
+
const [score, setScore] = React.useState(0);
|
|
275
|
+
const [over, setOver] = React.useState(false);
|
|
276
|
+
const [menuOpen, setMenuOpen] = React.useState(false);
|
|
277
|
+
|
|
278
|
+
React.useEffect(() => {
|
|
279
|
+
const root = rootRef.current;
|
|
280
|
+
const canvas = canvasRef.current;
|
|
281
|
+
const ctx = canvas.getContext("2d");
|
|
282
|
+
const ink = getComputedStyle(root).color; // text-foreground, so the drawing follows the theme
|
|
283
|
+
const s = { lane: 1, x: 1, rocks: [], t: 0, spawn: 0, dead: false, trail: [] };
|
|
284
|
+
let w = 0;
|
|
285
|
+
let h = 0;
|
|
286
|
+
|
|
287
|
+
const resize = () => {
|
|
288
|
+
const dpr = Math.min(window.devicePixelRatio || 1, 2);
|
|
289
|
+
w = root.clientWidth;
|
|
290
|
+
h = root.clientHeight;
|
|
291
|
+
canvas.width = w * dpr;
|
|
292
|
+
canvas.height = h * dpr;
|
|
293
|
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
294
|
+
};
|
|
295
|
+
const restart = () => {
|
|
296
|
+
Object.assign(s, { lane: 1, x: 1, rocks: [], t: 0, spawn: 0, dead: false });
|
|
297
|
+
setScore(0);
|
|
298
|
+
setOver(false);
|
|
299
|
+
};
|
|
300
|
+
const move = (dir) => {
|
|
301
|
+
if (s.dead) return;
|
|
302
|
+
if (dir === "left") s.lane = Math.max(0, s.lane - 1);
|
|
303
|
+
if (dir === "right") s.lane = Math.min(2, s.lane + 1);
|
|
304
|
+
navigator.vibrate?.(10);
|
|
305
|
+
};
|
|
306
|
+
const keys = (e) => {
|
|
307
|
+
const dir = { ArrowLeft: "left", ArrowRight: "right", a: "left", d: "right" }[e.key];
|
|
308
|
+
if (dir) move(dir);
|
|
309
|
+
if (e.key === " " && s.dead) restart();
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
const detach = attachSwipe(root, {
|
|
313
|
+
onSwipe: move,
|
|
314
|
+
onTap: () => s.dead && restart(),
|
|
315
|
+
onTrail: (x, y) => s.trail.push({ x, y, t: performance.now() }),
|
|
316
|
+
});
|
|
317
|
+
window.addEventListener("keydown", keys);
|
|
318
|
+
window.addEventListener("resize", resize);
|
|
319
|
+
resize();
|
|
320
|
+
|
|
321
|
+
let raf = 0;
|
|
322
|
+
let last = performance.now();
|
|
323
|
+
const frame = (now) => {
|
|
324
|
+
const dt = Math.min((now - last) / 1000, 1 / 30);
|
|
325
|
+
last = now;
|
|
326
|
+
const laneW = w / 3;
|
|
327
|
+
if (!s.dead) {
|
|
328
|
+
s.t += dt;
|
|
329
|
+
s.x += (s.lane - s.x) * Math.min(1, (dt * 1000) / TUNE.laneMs);
|
|
330
|
+
s.spawn -= dt;
|
|
331
|
+
if (s.spawn <= 0) {
|
|
332
|
+
s.rocks.push({ lane: Math.floor(Math.random() * 3), y: -40 });
|
|
333
|
+
s.spawn = Math.max(0.35, 0.9 - s.t * 0.01);
|
|
334
|
+
}
|
|
335
|
+
const speed = 260 + s.t * 8;
|
|
336
|
+
for (const r of s.rocks) r.y += speed * dt;
|
|
337
|
+
s.rocks = s.rocks.filter((r) => r.y < h + 40);
|
|
338
|
+
const py = h * 0.78;
|
|
339
|
+
if (s.rocks.some((r) => Math.abs(r.lane - s.x) < 0.5 && Math.abs(r.y - py) < 30)) {
|
|
340
|
+
s.dead = true;
|
|
341
|
+
navigator.vibrate?.(40);
|
|
342
|
+
setOver(true);
|
|
343
|
+
}
|
|
344
|
+
setScore(Math.floor(s.t * 10));
|
|
345
|
+
}
|
|
346
|
+
ctx.clearRect(0, 0, w, h);
|
|
347
|
+
ctx.strokeStyle = ink;
|
|
348
|
+
ctx.fillStyle = ink;
|
|
349
|
+
ctx.globalAlpha = 0.15;
|
|
350
|
+
for (let i = 1; i < 3; i++) ctx.fillRect(laneW * i - 1, 0, 2, h);
|
|
351
|
+
ctx.globalAlpha = 1;
|
|
352
|
+
for (const r of s.rocks) ctx.fillRect(laneW * r.lane + laneW / 2 - 18, r.y - 18, 36, 36);
|
|
353
|
+
ctx.beginPath();
|
|
354
|
+
ctx.arc(laneW * s.x + laneW / 2, h * 0.78, 20, 0, Math.PI * 2);
|
|
355
|
+
ctx.fill();
|
|
356
|
+
s.trail = s.trail.filter((p) => now - p.t < TUNE.trailMs);
|
|
357
|
+
s.trail.forEach((p, i) => {
|
|
358
|
+
ctx.globalAlpha = 1 - (now - p.t) / TUNE.trailMs;
|
|
359
|
+
ctx.beginPath();
|
|
360
|
+
ctx.arc(p.x, p.y, 3 + i * 0.2, 0, Math.PI * 2);
|
|
361
|
+
ctx.fill();
|
|
362
|
+
});
|
|
363
|
+
ctx.globalAlpha = 1;
|
|
364
|
+
raf = requestAnimationFrame(frame);
|
|
365
|
+
};
|
|
366
|
+
raf = requestAnimationFrame(frame);
|
|
367
|
+
|
|
368
|
+
return () => {
|
|
369
|
+
cancelAnimationFrame(raf);
|
|
370
|
+
detach();
|
|
371
|
+
window.removeEventListener("keydown", keys);
|
|
372
|
+
window.removeEventListener("resize", resize);
|
|
373
|
+
};
|
|
374
|
+
}, []);
|
|
375
|
+
|
|
376
|
+
return (
|
|
377
|
+
<div
|
|
378
|
+
ref={rootRef}
|
|
379
|
+
className="relative h-dvh w-full overflow-hidden bg-background text-foreground select-none touch-none"
|
|
380
|
+
onContextMenu={(e) => e.preventDefault()}
|
|
381
|
+
>
|
|
382
|
+
<canvas ref={canvasRef} className="absolute inset-0 block w-full h-full select-none touch-none" />
|
|
383
|
+
<div className="absolute top-3 left-3 pointer-events-none">
|
|
384
|
+
<p className="text-2xl font-bold">{score}</p>
|
|
385
|
+
<p className="text-sm text-muted-foreground">
|
|
386
|
+
{over ? "tap to run again" : "swipe ← → to switch lanes"}
|
|
387
|
+
</p>
|
|
388
|
+
</div>
|
|
389
|
+
{/* One button holds everything that is not play; pointerdown stops here so a tap on it never steers. */}
|
|
390
|
+
<div className="absolute top-3 right-3" onPointerDown={(e) => e.stopPropagation()}>
|
|
391
|
+
<IconButton icon="☰" label={menuOpen ? "Hide menu" : "Show menu"} onClick={() => setMenuOpen(!menuOpen)} />
|
|
392
|
+
</div>
|
|
393
|
+
{menuOpen && (
|
|
394
|
+
<div className="absolute top-16 right-3 w-72" onPointerDown={(e) => e.stopPropagation()}>
|
|
395
|
+
<Card>
|
|
396
|
+
<Stack gap={2}>
|
|
397
|
+
<Heading level={2}>Lane Dash</Heading>
|
|
398
|
+
<Text size="sm">Swipe left or right to change lanes. Arrow keys work too.</Text>
|
|
399
|
+
<Text size="sm" muted>
|
|
400
|
+
Best run so far: {score}
|
|
401
|
+
</Text>
|
|
402
|
+
</Stack>
|
|
403
|
+
</Card>
|
|
404
|
+
</div>
|
|
405
|
+
)}
|
|
406
|
+
</div>
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
```
|
package/llms/voxel.js
CHANGED
|
@@ -11,7 +11,7 @@ export const voxelConfig = {
|
|
|
11
11
|
"armor, mob combat, hunger, dimensions, per-player inventory, world reset) and the flat " +
|
|
12
12
|
"multi-file project split for bigger games. " +
|
|
13
13
|
"minecraft, voxel, block world, sandbox, blocks, mining, building, first-person, 3d game, " +
|
|
14
|
-
"procedural world, chunks, terrain, overworld, creative mode, survival, crafting, nether",
|
|
14
|
+
"procedural world, chunks, terrain, overworld, creative mode, survival, crafting, nether. Anything played or steered also loads touch-games for phone controls",
|
|
15
15
|
cues: ["voxel", "voxels", "minecraft", "block world", "blocky", "sandbox game"],
|
|
16
16
|
importModule: "three",
|
|
17
17
|
importName: "THREE",
|
package/llms/voxel.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"voxel.js","sourceRoot":"","sources":["../../jsr/llms/voxel.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,WAAW,GAAc;IACpC,IAAI,EAAE,OAAO;IACb,KAAK,EAAE,OAAO;IACd,WAAW,EACT,yFAAyF;QACzF,6FAA6F;QAC7F,4FAA4F;QAC5F,yFAAyF;QACzF,uFAAuF;QACvF,gGAAgG;QAChG,2FAA2F;QAC3F,yFAAyF;QACzF,6CAA6C;QAC7C,2FAA2F;QAC3F
|
|
1
|
+
{"version":3,"file":"voxel.js","sourceRoot":"","sources":["../../jsr/llms/voxel.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,WAAW,GAAc;IACpC,IAAI,EAAE,OAAO;IACb,KAAK,EAAE,OAAO;IACd,WAAW,EACT,yFAAyF;QACzF,6FAA6F;QAC7F,4FAA4F;QAC5F,yFAAyF;QACzF,uFAAuF;QACvF,gGAAgG;QAChG,2FAA2F;QAC3F,yFAAyF;QACzF,6CAA6C;QAC7C,2FAA2F;QAC3F,+JAA+J;IACjK,IAAI,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,EAAE,cAAc,CAAC;IAC/E,YAAY,EAAE,OAAO;IACrB,UAAU,EAAE,OAAO;IACnB,UAAU,EAAE,WAAW;CACxB,CAAC"}
|
package/llms/voxel.md
CHANGED
|
@@ -470,8 +470,19 @@ to the container on entry and exit. Keep fullscreen and pointer lock as
|
|
|
470
470
|
separate controls; reacquire pointer lock through the click-to-play overlay
|
|
471
471
|
when needed. Remove the fullscreen listener on unmount.
|
|
472
472
|
|
|
473
|
+
### Full screen, with one menu button
|
|
474
|
+
|
|
475
|
+
The world fills the screen (the `CanvasStage` above is `absolute inset-0` inside a
|
|
476
|
+
`relative h-dvh w-full` root). Keep the crosshair, the hotbar and the touch controls on screen;
|
|
477
|
+
put the title, settings, crafting recipes, the world-reset control and the player list behind
|
|
478
|
+
**one** top-right menu button (`IconButton`) that toggles an overlay. The bottom-right corner
|
|
479
|
+
belongs to the platform. A smaller boxed world is only for when the person asks for one.
|
|
480
|
+
|
|
473
481
|
### No text selection, and bind touch as well as mouse
|
|
474
482
|
|
|
483
|
+
The phone control schemes themselves (swipes, a ghost stick under the thumb, hold-to-move)
|
|
484
|
+
live in the `touch-games` skill; load it beside this one.
|
|
485
|
+
|
|
475
486
|
Two things separate "works on my laptop" from "works", and both are easy to
|
|
476
487
|
forget because the desktop build hides them:
|
|
477
488
|
|
package/llms/worlds.js
CHANGED
|
@@ -9,7 +9,7 @@ export const worldsConfig = {
|
|
|
9
9
|
"through ImageData in the app's theme colours, dials for the rates, a census on a slow clock, and " +
|
|
10
10
|
"kept moments as recipes (seed, dials, tick, recorded taps) that replay into a living gallery. " +
|
|
11
11
|
"ecosystem, terrarium, ant farm, game of life, cellular automaton, falling sand, forest fire, " +
|
|
12
|
-
"boids, flocking, simulation, petri dish, predator prey, evolution, artificial life, screensaver world",
|
|
12
|
+
"boids, flocking, simulation, petri dish, predator prey, evolution, artificial life, screensaver world. Anything played or steered also loads touch-games for phone controls",
|
|
13
13
|
cues: [
|
|
14
14
|
"ecosystem",
|
|
15
15
|
"terrarium",
|
package/llms/worlds.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worlds.js","sourceRoot":"","sources":["../../jsr/llms/worlds.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,MAAM,YAAY,GAAc;IACrC,IAAI,EAAE,QAAQ;IACd,KAAK,EAAE,mBAAmB;IAC1B,WAAW,EACT,kGAAkG;QAClG,mGAAmG;QACnG,mGAAmG;QACnG,oGAAoG;QACpG,oGAAoG;QACpG,mGAAmG;QACnG,gGAAgG;QAChG,+FAA+F;QAC/F,
|
|
1
|
+
{"version":3,"file":"worlds.js","sourceRoot":"","sources":["../../jsr/llms/worlds.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,MAAM,YAAY,GAAc;IACrC,IAAI,EAAE,QAAQ;IACd,KAAK,EAAE,mBAAmB;IAC1B,WAAW,EACT,kGAAkG;QAClG,mGAAmG;QACnG,mGAAmG;QACnG,oGAAoG;QACpG,oGAAoG;QACpG,mGAAmG;QACnG,gGAAgG;QAChG,+FAA+F;QAC/F,6KAA6K;IAC/K,IAAI,EAAE;QACJ,WAAW;QACX,WAAW;QACX,UAAU;QACV,YAAY;QACZ,cAAc;QACd,QAAQ;QACR,oBAAoB;QACpB,mBAAmB;QACnB,WAAW;QACX,cAAc;QACd,iBAAiB;QACjB,aAAa;QACb,OAAO;QACP,UAAU;QACV,UAAU;QACV,YAAY;QACZ,UAAU;QACV,MAAM;QACN,iBAAiB;QACjB,QAAQ;QACR,YAAY;QACZ,UAAU;QACV,QAAQ;QACR,eAAe;QACf,cAAc;QACd,WAAW;QACX,SAAS;QACT,SAAS;QACT,oBAAoB;KACrB;CACF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibes.diy/prompts",
|
|
3
|
-
"version": "14.3.
|
|
3
|
+
"version": "14.3.88",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"exports": {
|
|
@@ -39,10 +39,10 @@
|
|
|
39
39
|
"license": "Apache-2.0",
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@adviser/cement": "~0.5.34",
|
|
42
|
-
"@vibes.diy/api-types": "14.3.
|
|
43
|
-
"@vibes.diy/call-ai-v2": "14.3.
|
|
44
|
-
"@vibes.diy/identity": "14.3.
|
|
45
|
-
"@vibes.diy/use-vibes-types": "14.3.
|
|
42
|
+
"@vibes.diy/api-types": "14.3.88",
|
|
43
|
+
"@vibes.diy/call-ai-v2": "14.3.88",
|
|
44
|
+
"@vibes.diy/identity": "14.3.88",
|
|
45
|
+
"@vibes.diy/use-vibes-types": "14.3.88",
|
|
46
46
|
"arktype": "~2.2.6",
|
|
47
47
|
"json-schema-faker": "~0.6.3"
|
|
48
48
|
},
|
|
@@ -93,6 +93,7 @@ Every feature you described above should work when this one block lands. Don't l
|
|
|
93
93
|
**Split into companion files by default.** Whenever the app has more than a couple of distinct features — and always when the finished app would push `App.jsx` past the ~500-line threshold (see the multi-file rule above) — still lead with one complete `App.jsx` block, but as the composition root: imports, the `classNames` object, layout chrome, and the default `App` export composing the features. Then emit each extracted feature component as its own complete file block (its path heading first, e.g. `components/Feed.jsx`) right after the `App.jsx` block. Every feature still works when the turn's blocks land — the single-block rule means one complete pass per file, never a second pass, not everything crammed into `App.jsx`. Only a genuinely small app (one screen, one or two features) stays single-file.
|
|
94
94
|
- When a write surface needs gating, destructure `useVibe` for the database it writes to — `const { can, ready } = useVibe("<dbName>");`. Only destructure `useViewer` (`const { ViewerTag } = useViewer();`) when you render **other** users — `<ViewerTag userHandle={...} />` for authors/rosters. The current viewer's pill and sign-in live in the Vibes Switch (the logo), so don't add one to your header.
|
|
95
95
|
- **Be creative with the layout, but respect mobile idioms.** Thumb-reachable primary actions, generous tap targets (`min-h-11`), scrollable lists, no hover-only interactions.
|
|
96
|
+
- **Touch controls belong to the finger on a phone.** A 3D or canvas scene, a joystick or D-pad, a drag surface, or any button someone presses and holds carries `select-none touch-none` on the control area **and** its container (the HUD and labels around it too), so a press or drag steers the app while nearby text stays put — no selection highlight, no copy callout, no page scroll or zoom. Drive holds with `onPointerDown` / `onPointerUp` / `onPointerCancel` (mouse and touch in one path), and give a hold button `onContextMenu={(e) => e.preventDefault()}` so a long press keeps holding.
|
|
96
97
|
- **For chat or log panels, keep autoscroll on the panel itself.** Use a bounded `max-h-64 overflow-y-auto` container with a ref and move it to the end when entries change:
|
|
97
98
|
```jsx
|
|
98
99
|
const chatRef = useRef(null);
|
package/system-prompt-initial.md
CHANGED
|
@@ -89,6 +89,7 @@ The sandbox serves raw ES modules, so `App.jsx` can import local `.js`/`.jsx` fi
|
|
|
89
89
|
- A default-exported `App` function composing them inside `<main id="app">` with `<header id="app-header">`.
|
|
90
90
|
- When a write surface needs gating, destructure `useVibe` for the database it writes to — `const { can, ready } = useVibe("<dbName>");`. Only destructure `useViewer` (`const { ViewerTag } = useViewer();`) when you render **other** users — `<ViewerTag userHandle={...} />` for authors/rosters. The current viewer's pill and sign-in live in the Vibes Switch (the logo), so don't add one to your header.
|
|
91
91
|
- **Be creative with the layout, but respect mobile idioms.** Thumb-reachable primary actions, generous tap targets (`min-h-11`), scrollable lists, no hover-only interactions.
|
|
92
|
+
- **Touch controls belong to the finger on a phone.** A 3D or canvas scene, a joystick or D-pad, a drag surface, or any button someone presses and holds carries `select-none touch-none` on the control area **and** its container (the HUD and labels around it too), so a press or drag steers the app while nearby text stays put — no selection highlight, no copy callout, no page scroll or zoom. Drive holds with `onPointerDown` / `onPointerUp` / `onPointerCancel` (mouse and touch in one path), and give a hold button `onContextMenu={(e) => e.preventDefault()}` so a long press keeps holding.
|
|
92
93
|
- **For chat or log panels, keep autoscroll on the panel itself.** Use a bounded `max-h-64 overflow-y-auto` container with a ref and move it to the end when entries change:
|
|
93
94
|
```jsx
|
|
94
95
|
const chatRef = useRef(null);
|
package/system-prompt.md
CHANGED
|
@@ -98,6 +98,7 @@ The sandbox serves raw ES modules, so `App.jsx` can import local `.js`/`.jsx` fi
|
|
|
98
98
|
- When a write surface needs gating, destructure `useVibe` for the database it writes to — `const { can, ready } = useVibe("<dbName>");`. Only destructure `useViewer` (`const { ViewerTag } = useViewer();`) when you render **other** users — `<ViewerTag userHandle={...} />` for authors/rosters. The current viewer's pill and sign-in live in the Vibes Switch (the logo), so don't add one to your header.
|
|
99
99
|
- NO hooks beyond `useVibe`/`useViewer`, NO data wiring — those land in the feature edits
|
|
100
100
|
- **Be creative with the layout, but respect mobile idioms.** Thumb-reachable primary actions, generous tap targets (`min-h-11`), scrollable lists, no hover-only interactions.
|
|
101
|
+
- **Touch controls belong to the finger on a phone.** A 3D or canvas scene, a joystick or D-pad, a drag surface, or any button someone presses and holds carries `select-none touch-none` on the control area **and** its container (the HUD and labels around it too), so a press or drag steers the app while nearby text stays put — no selection highlight, no copy callout, no page scroll or zoom. Drive holds with `onPointerDown` / `onPointerUp` / `onPointerCancel` (mouse and touch in one path), and give a hold button `onContextMenu={(e) => e.preventDefault()}` so a long press keeps holding.
|
|
101
102
|
- **For chat or log panels, keep autoscroll on the panel itself.** Use a bounded `max-h-64 overflow-y-auto` container with a ref and move it to the end when entries change:
|
|
102
103
|
```jsx
|
|
103
104
|
const chatRef = useRef(null);
|