@vibes.diy/prompts 14.0.22 → 14.0.24
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/access.md +8 -1
- package/llms/backend.md +31 -1
- package/llms/fireproof.md +29 -0
- package/llms/image-gen.md +39 -38
- package/llms/three-js.md +64 -15
- package/package.json +4 -4
- package/system-prompt-initial-oneshot.md +4 -1
- package/system-prompt-initial.md +4 -1
- package/system-prompt.md +5 -2
package/llms/access.md
CHANGED
|
@@ -6,6 +6,13 @@ You are seeing this doc because the app's prompt is **permission-shaped** — it
|
|
|
6
6
|
|
|
7
7
|
`access.js` is a separate file alongside `App.jsx`; each **named export** gates the database of the same name (`export function chat(...)` gates `useFireproof("chat")`), and an `export default` acts as a catch-all. The `App.jsx` you write gates its write surfaces on `useVibe(dbName).can` — the same function this access.js enforces server-side.
|
|
8
8
|
|
|
9
|
+
Construct the document an action will save, check that same value with `can.create` or
|
|
10
|
+
`can.edit`, then write it to the same database. Include every field the access rule checks,
|
|
11
|
+
including the intended next status. For example, cancelling a reservation constructs
|
|
12
|
+
`const next = { ...reservation, status: "cancelled" }`, checks `can.edit(next).ok`, and
|
|
13
|
+
saves `next`. A creation control checks the complete creation draft. Render the verdict's
|
|
14
|
+
reason when denied, and keep loading and signed-out states distinct from denial.
|
|
15
|
+
|
|
9
16
|
## Reference
|
|
10
17
|
|
|
11
18
|
### Function signature
|
|
@@ -101,7 +108,7 @@ The platform predicate `ctx.isImgGenVersionAppend(doc, oldDoc)` accepts exactly
|
|
|
101
108
|
|
|
102
109
|
### `_id` strategy matters
|
|
103
110
|
|
|
104
|
-
Documents that represent a unique named resource (channels, user profiles, config singletons,
|
|
111
|
+
Documents that represent a unique named resource (channels, user profiles, config singletons, starter content) should use a deterministic `_id` with a short prefix — `"ch:" + name`, `"profile:" + handle`, `"config"`, `"starter:" + key`. This enforces uniqueness: two users creating "general" get the same doc, not two — and a first-load write that runs again (a fresh tab, another device) overwrites the same starter docs instead of duplicating them. Records a request says the app must already have are created this way, by the app's first render; see "Required starting records" in `fireproof.md`. Documents that represent events or content (messages, posts, survey responses) should let `_id` be auto-generated — each one is unique by nature. Use `doc._id` as the channel name for resource docs; use a `channelId` foreign key on content docs.
|
|
105
112
|
|
|
106
113
|
### Grants are additive
|
|
107
114
|
|
package/llms/backend.md
CHANGED
|
@@ -40,6 +40,13 @@ the request, the app needs no `backend.js` — don't emit one.
|
|
|
40
40
|
`backend.js` + **cache-into-db, never fetch-per-render**; when external data is
|
|
41
41
|
only launch flavor, take a generation-time snapshot into `seed.json` instead.
|
|
42
42
|
|
|
43
|
+
Keep availability separate from a successful empty result. Inspect a successful source
|
|
44
|
+
response before choosing its parser, then validate the fields the app uses. An empty
|
|
45
|
+
validated response is data; an unavailable first read leaves the dependent decision pending.
|
|
46
|
+
For example, a dispatch planner waiting for its closure calendar records that unavailable
|
|
47
|
+
state and postpones calendar-dependent dispatch. A previously validated cache can support
|
|
48
|
+
the declared fallback, with its freshness and failure visible in saved status.
|
|
49
|
+
|
|
43
50
|
## Output format
|
|
44
51
|
|
|
45
52
|
`backend.js` is a separate file, exactly like `access.js`: one prose line, the
|
|
@@ -268,7 +275,11 @@ const gh = await ctx.github.fetch("/repos/acme/site/issues", { method: "POST", b
|
|
|
268
275
|
budget (10 per 10s, 60 per minute) is spent for the moment, which the next tick
|
|
269
276
|
clears. Check for that shape and surface a useful error. Remedies: read it as a
|
|
270
277
|
plain `GET`, use a CORS-open endpoint,
|
|
271
|
-
request a curated-list addition, or ask about owner blessing.
|
|
278
|
+
request a curated-list addition, or ask about owner blessing. The creator-facing
|
|
279
|
+
version of this gate — including what to do when a link is reported as taking
|
|
280
|
+
more power than the app has today — is at
|
|
281
|
+
https://good.vibes.diy/docs/your-app-has-a-backend, which is the page to send
|
|
282
|
+
someone to. Limits: https
|
|
272
283
|
on port 443 only, 15s per request, 10MB responses, per-vibe rate caps
|
|
273
284
|
(30/10s, 300/min). Make all outbound calls **before returning** the Response —
|
|
274
285
|
a `ctx.fetch()` fired while a streamed body is still being pulled after the
|
|
@@ -548,6 +559,14 @@ const res = await fetch("/_api/rsvp", { method: "POST", body: JSON.stringify({ n
|
|
|
548
559
|
|
|
549
560
|
## onChange — react to committed writes
|
|
550
561
|
|
|
562
|
+
**When the request says a record is kept on the server, or that the server reacts
|
|
563
|
+
to something, emit this handler.** A person asking for activity to be recorded
|
|
564
|
+
centrally, for a tally the app maintains, or for something to happen whenever a
|
|
565
|
+
record lands is naming the server as the place it happens — that ask is the
|
|
566
|
+
trigger, and the shape is the activity-mirror example at the end of this file:
|
|
567
|
+
the client writes the document that triggers it, and `onChange` writes what the
|
|
568
|
+
server owes in response.
|
|
569
|
+
|
|
551
570
|
Runs after any document write to the app's databases commits (user writes and
|
|
552
571
|
backend writes alike). The event:
|
|
553
572
|
|
|
@@ -684,6 +703,17 @@ document it reads by id, plus a copy in module scope. A missing or failed state
|
|
|
684
703
|
read means *"don't know — use the copy"*, never *"nothing has run yet"*; that
|
|
685
704
|
second reading is what bills the owner four times for one noon.
|
|
686
705
|
|
|
706
|
+
**A status document that must be seen to change carries the tick's timestamp.**
|
|
707
|
+
A write whose content is byte-identical to what is already stored is absorbed:
|
|
708
|
+
the platform stores no new revision, and with no new revision there is no live
|
|
709
|
+
event, so an open page holding that document sees nothing and keeps showing what
|
|
710
|
+
it already had. A tick that rewrites a fixed-id status doc with the same fields
|
|
711
|
+
every time is therefore invisible to a watching reader. Include the moment the
|
|
712
|
+
tick ran — `checkedAt: new Date().toISOString()` — and the write is a real
|
|
713
|
+
change, so the page updates. This is the reader's side of the state-document
|
|
714
|
+
discipline above: state you only ever read back yourself is better off constant,
|
|
715
|
+
and state somebody is watching has to move.
|
|
716
|
+
|
|
687
717
|
### Firing on a real-world event
|
|
688
718
|
|
|
689
719
|
**An averaged cycle is not a calendar.** When the moment comes from nature
|
package/llms/fireproof.md
CHANGED
|
@@ -77,6 +77,35 @@ App.jsx
|
|
|
77
77
|
|
|
78
78
|
The `useDocument` hook provides several methods: `merge(updates)` updates the document with new fields without saving (use this instead of keeping a `useState` for document data), `submit(e)` handles form submission by preventing default, saving, and resetting, `save()` saves the current document state, and `reset()` resets to initial state. When you call submit, the document is reset, so if you didn't provide an `_id` then you can use the form to create a stream of new documents as in the basic example above.
|
|
79
79
|
|
|
80
|
+
## Required starting records
|
|
81
|
+
|
|
82
|
+
When a request names records the app must already have — the three projects in a
|
|
83
|
+
studio, a template's starter rows, the rooms in a planner — those records are
|
|
84
|
+
created by the app's own first render through the ordinary write path. That is
|
|
85
|
+
the mechanism, and it is the same one described below for starter content.
|
|
86
|
+
`seed.json` is a separate thing: an optional import a person runs by hand from
|
|
87
|
+
the Data tab, applied by no deploy path, so a record that exists only there does
|
|
88
|
+
not exist for anyone who opens the app.
|
|
89
|
+
|
|
90
|
+
Four rules make it hold:
|
|
91
|
+
|
|
92
|
+
- **Each required record gets a fixed `_id` you choose** (`"project:atlas"`), so
|
|
93
|
+
a second write of it lands on the same document instead of minting another.
|
|
94
|
+
- **Create them after `ready` and after `can.create` says yes**, so a write that
|
|
95
|
+
the enforced access rules do not admit yet is skipped quietly and retried on a
|
|
96
|
+
later run.
|
|
97
|
+
- **Restrict their creation to the owner in `access.js`** when the records
|
|
98
|
+
describe the app itself rather than a visitor's own work.
|
|
99
|
+
- **Write parents and membership grants before the records that depend on
|
|
100
|
+
them**, so a dependent document never lands against a container or a grant
|
|
101
|
+
that does not exist yet.
|
|
102
|
+
|
|
103
|
+
Fixed ids are also what makes two tabs safe. Two browsers opening the app for the
|
|
104
|
+
first time at the same moment both see an empty query and both write; with fixed
|
|
105
|
+
ids those are two writes of the same document, and last-writer-wins converges on
|
|
106
|
+
one copy. Reading the query first and then deciding whether to write is the shape
|
|
107
|
+
that races — the read is empty in both tabs before either write lands.
|
|
108
|
+
|
|
80
109
|
### Seeding starter data — give each seed a deterministic `_id`
|
|
81
110
|
|
|
82
111
|
When an app ships with default content — starter slides, template rows, example cards — and writes it into the database on first load, give **each seed document a deterministic `_id`** (`"seed:intro"`, `"seed:" + key`). This makes the seeding write **idempotent**: if it ever runs again, it overwrites the same documents instead of creating fresh copies.
|
package/llms/image-gen.md
CHANGED
|
@@ -25,7 +25,35 @@ export default function App() {
|
|
|
25
25
|
}
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
`<ImgGen>`
|
|
28
|
+
`<ImgGen>` stores generated versions in its component-owned media database, associated
|
|
29
|
+
with the host database name and `_id`. Read a saved image through
|
|
30
|
+
`useImgGen({ _id, database })` from `use-vibes`: its `document` composes current media
|
|
31
|
+
versions and legacy host versions. From that composed document, select
|
|
32
|
+
`document.versions?.[document.currentVersion ?? 0]`, then
|
|
33
|
+
`document._files?.[version.id]`. The returned file ref supplies both `.url` for display
|
|
34
|
+
and `.file()` for a transformation input, so a ref you have in hand is ready to pass
|
|
35
|
+
straight to `images`. Ordinary host records hold the app's own data; the image hook
|
|
36
|
+
supplies the associated generated output.
|
|
37
|
+
|
|
38
|
+
For example, refining a saved pottery illustration keeps its original ID and uses its
|
|
39
|
+
actual output as the input to a separate result ID:
|
|
40
|
+
|
|
41
|
+
```jsx
|
|
42
|
+
import { ImgGen, useImgGen } from "use-vibes";
|
|
43
|
+
|
|
44
|
+
function GlazingPreview({ database }) {
|
|
45
|
+
const { document: original, pending, error } = useImgGen({ _id: "vessel-original", database });
|
|
46
|
+
const version = original?.versions?.[original.currentVersion ?? 0];
|
|
47
|
+
const source = version?.id ? original?._files?.[version.id] : undefined;
|
|
48
|
+
const sourceReady = source !== undefined;
|
|
49
|
+
return <section>
|
|
50
|
+
<ImgGen _id="vessel-original" database={database} showControls={false} />
|
|
51
|
+
{sourceReady ? <ImgGen _id="vessel-glazed" database={database}
|
|
52
|
+
prompt="Add a blue glaze while preserving this vessel's shape and composition"
|
|
53
|
+
images={[source]} /> : <p>{error ? error.message : pending ? "Loading saved source…" : "Source image unavailable"}</p>}
|
|
54
|
+
</section>;
|
|
55
|
+
}
|
|
56
|
+
```
|
|
29
57
|
|
|
30
58
|
This is the same `_files`-shape contract documented in `fireproof.md`'s "Working with Files" section — read it first if you have not seen the platform's file/URL story.
|
|
31
59
|
|
|
@@ -88,46 +116,19 @@ Do **not** hide first generation behind an "Illustrate" / "Generate image" butto
|
|
|
88
116
|
|
|
89
117
|
## Gallery Pattern
|
|
90
118
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
```jsx
|
|
96
|
-
<<<<<<< SEARCH
|
|
97
|
-
export default function App() {
|
|
98
|
-
const [file, setFile] = React.useState(null);
|
|
99
|
-
|
|
100
|
-
return (
|
|
101
|
-
=======
|
|
102
|
-
export default function App() {
|
|
103
|
-
const { useLiveQuery } = useFireproof("ImgGen");
|
|
104
|
-
const { docs } = useLiveQuery("type", { key: "image", descending: true });
|
|
105
|
-
const [file, setFile] = React.useState(null);
|
|
106
|
-
|
|
107
|
-
return (
|
|
108
|
-
>>>>>>> REPLACE
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
App.jsx
|
|
119
|
+
Keep gallery entries as app-owned records with the saved image's host `_id`. Query those
|
|
120
|
+
records with `useLiveQuery`, then let each `<ImgGen>` read its associated media. This also
|
|
121
|
+
keeps the app's title, caption and other editable fields separate from image versions:
|
|
112
122
|
|
|
113
123
|
```jsx
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
);
|
|
124
|
+
function Gallery() {
|
|
125
|
+
const { database, useLiveQuery } = useFireproof("artwork");
|
|
126
|
+
const { docs } = useLiveQuery("type", { key: "artwork" });
|
|
127
|
+
return <div>{docs.map(doc => <figure key={doc._id}>
|
|
128
|
+
<ImgGen _id={doc._id} database={database} showControls={false} />
|
|
129
|
+
<figcaption>{doc.caption}</figcaption>
|
|
130
|
+
</figure>)}</div>;
|
|
117
131
|
}
|
|
118
|
-
=======
|
|
119
|
-
<h3>Gallery</h3>
|
|
120
|
-
<div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 8 }}>
|
|
121
|
-
{docs.map((doc) => {
|
|
122
|
-
const ver = doc.versions?.[doc.currentVersion ?? 0];
|
|
123
|
-
const meta = ver?.id ? doc._files?.[ver.id] : undefined;
|
|
124
|
-
return <img key={doc._id} src={meta?.url} alt={doc.prompt} width={128} />;
|
|
125
|
-
})}
|
|
126
|
-
</div>
|
|
127
|
-
</div>
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
>>>>>>> REPLACE
|
|
131
132
|
```
|
|
132
133
|
|
|
133
134
|
## Caching and Versions
|
package/llms/three-js.md
CHANGED
|
@@ -293,31 +293,80 @@ color.setHSL(0, 1, 0.5); // HSL values
|
|
|
293
293
|
color.lerp(targetColor, 0.1); // interpolation
|
|
294
294
|
```
|
|
295
295
|
|
|
296
|
-
##
|
|
296
|
+
## Picking things in the scene (mouse interaction)
|
|
297
|
+
|
|
298
|
+
Selection has two halves: which objects the ray is allowed to hit, and which
|
|
299
|
+
object a hit stands for. Keep both explicit and clicking a planet selects the
|
|
300
|
+
planet rather than the glow around it.
|
|
301
|
+
|
|
302
|
+
**Raycast against a `selectables` array, never the whole scene graph.** The array
|
|
303
|
+
holds the things a person can pick, so lights, helpers, the skybox and every
|
|
304
|
+
decorative mesh are out of the answer before the ray is cast.
|
|
305
|
+
|
|
306
|
+
**Resolve a hit up `.parent` to the first ancestor carrying `userData.id`.** A hit
|
|
307
|
+
lands on whichever mesh the ray touched first, which is usually a child — a halo
|
|
308
|
+
shell, a label sprite, a bevel — so read the identity off the ancestor that owns
|
|
309
|
+
it instead of off the mesh that happened to be in front.
|
|
310
|
+
|
|
311
|
+
**Decorative children opt out of hit testing with `raycast = () => {}`.** A glow
|
|
312
|
+
shell exists to be seen and not to be picked, and an empty `raycast` removes it
|
|
313
|
+
from every intersection test at the source.
|
|
314
|
+
|
|
315
|
+
**Show selection by changing a material property on the resolved parent.** Adding
|
|
316
|
+
a new mesh to mark the selection adds another thing the ray can hit; changing
|
|
317
|
+
`emissive`, `opacity` or `color` on what is already there does not.
|
|
297
318
|
|
|
298
319
|
```javascript
|
|
299
320
|
const raycaster = new THREE.Raycaster();
|
|
300
|
-
const
|
|
321
|
+
const pointer = new THREE.Vector2();
|
|
301
322
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
|
|
305
|
-
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
|
|
323
|
+
// The things a person can pick. Everything else in the scene stays out of it.
|
|
324
|
+
const selectables = [];
|
|
306
325
|
|
|
307
|
-
|
|
308
|
-
|
|
326
|
+
function addPlanet(name) {
|
|
327
|
+
const planet = new THREE.Mesh(geometry, new THREE.MeshStandardMaterial({ color: 0x88aaff }));
|
|
328
|
+
planet.userData.id = name; // the identity a hit resolves to
|
|
309
329
|
|
|
310
|
-
|
|
311
|
-
|
|
330
|
+
const glow = new THREE.Mesh(glowGeometry, glowMaterial);
|
|
331
|
+
glow.raycast = () => {}; // decorative: never a hit
|
|
332
|
+
planet.add(glow);
|
|
312
333
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
334
|
+
scene.add(planet);
|
|
335
|
+
selectables.push(planet);
|
|
336
|
+
return planet;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// A hit lands on whichever mesh the ray touched — walk up to the owner of the id.
|
|
340
|
+
function selectableOf(object) {
|
|
341
|
+
let node = object;
|
|
342
|
+
while (node && node.userData?.id === undefined) node = node.parent;
|
|
343
|
+
return node ?? null;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function onPointerDown(event) {
|
|
347
|
+
const rect = renderer.domElement.getBoundingClientRect();
|
|
348
|
+
pointer.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
|
|
349
|
+
pointer.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
|
|
350
|
+
raycaster.setFromCamera(pointer, camera);
|
|
351
|
+
|
|
352
|
+
const hit = raycaster.intersectObjects(selectables, true)[0];
|
|
353
|
+
const picked = hit ? selectableOf(hit.object) : null;
|
|
354
|
+
|
|
355
|
+
for (const s of selectables) s.material.emissive?.setHex(0x000000);
|
|
356
|
+
if (picked) picked.material.emissive?.setHex(0x333311); // a property change, not a new mesh
|
|
357
|
+
setSelectedId(picked?.userData.id ?? null);
|
|
318
358
|
}
|
|
319
359
|
```
|
|
320
360
|
|
|
361
|
+
## Importing three
|
|
362
|
+
|
|
363
|
+
A bare package name at the top of the file resolves through the platform's esm.sh
|
|
364
|
+
map, and a full esm.sh URL loads too — either form also works with
|
|
365
|
+
`await import()`. The bare form follows whatever version the platform serves,
|
|
366
|
+
which moves as esm.sh publishes; a version-pinned URL such as
|
|
367
|
+
`https://esm.sh/three@0.160.0` is the lever for holding a scene on one release of
|
|
368
|
+
the library.
|
|
369
|
+
|
|
321
370
|
## Animation System
|
|
322
371
|
|
|
323
372
|
### Animation Mixer
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibes.diy/prompts",
|
|
3
|
-
"version": "14.0.
|
|
3
|
+
"version": "14.0.24",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"exports": {
|
|
@@ -34,9 +34,9 @@
|
|
|
34
34
|
"license": "Apache-2.0",
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@adviser/cement": "~0.5.34",
|
|
37
|
-
"@vibes.diy/call-ai-v2": "14.0.
|
|
38
|
-
"@vibes.diy/identity": "14.0.
|
|
39
|
-
"@vibes.diy/use-vibes-types": "14.0.
|
|
37
|
+
"@vibes.diy/call-ai-v2": "14.0.24",
|
|
38
|
+
"@vibes.diy/identity": "14.0.24",
|
|
39
|
+
"@vibes.diy/use-vibes-types": "14.0.24",
|
|
40
40
|
"arktype": "~2.2.3",
|
|
41
41
|
"json-schema-faker": "~0.6.3"
|
|
42
42
|
},
|
|
@@ -25,6 +25,7 @@ You are an AI assistant tasked with creating React components. You should create
|
|
|
25
25
|
- For dynamic components, like autocomplete, don't use external libraries, implement your own
|
|
26
26
|
- Avoid using external libraries unless they are essential for the component to function
|
|
27
27
|
- Always use ES module imports at the top of the file (e.g. `import React, { useState } from "react"`). Never reference React or other libraries as globals.
|
|
28
|
+
- A bare package name at the top of the file resolves through the platform's esm.sh map, and a full esm.sh URL such as `https://esm.sh/three@0.160.0` loads too. Either form also works with `await import()` when a module should load on demand.
|
|
28
29
|
- Your file MUST use `export default function App()` — the runtime loads it as an ES module and imports the default export.
|
|
29
30
|
- Structure your component code in this order: (1) hooks and document shapes, (2) event handlers, (3) classNames object, (4) JSX return. ClassNames go right before JSX so they are close to where they are used.
|
|
30
31
|
- Use Fireproof for data persistence
|
|
@@ -113,7 +114,9 @@ Rationale: the app's sharing rules are designed later from this object graph. A
|
|
|
113
114
|
|
|
114
115
|
### The prompt's content is launch content — emit `seed.json`
|
|
115
116
|
|
|
116
|
-
When the user's prompt hands you **concrete example data** — an image of items, a pasted list, a screenshot of records, a table —
|
|
117
|
+
When the user's prompt hands you **concrete example data** — an image of items, a pasted list, a screenshot of records, a table — extract it into the app's document shape and emit it as a `seed.json` file. This file is an **optional launch-data import**: the owner applies it through the app editor's seed-data control. Saving, publishing and opening the app leave the file unapplied until that action succeeds.
|
|
118
|
+
|
|
119
|
+
**Required starting state belongs to the app's working initialization.** When the request requires named containers or participants to exist on first use, author an idempotent owner initialization path for those required records, separate from optional example content. Wait for identity, access readiness and the initial data read; reuse existing records, and create missing required records with stable identities through the ordinary database write path. Check each complete intended document with `can.create`, await its write, and show an actionable setup error if it fails. Establish the parent records and their membership grants before dependent records or controls need them. Keep existing user edits on later visits. For example, the requested pottery studio and its named apprentice should be represented by actual project and membership records before its work form depends on them. A name present only in `seed.json` describes an offered import, not an existing grant.
|
|
117
120
|
|
|
118
121
|
Emit it with the filename-on-its-own-line convention: the filename `seed.json` on its own line, then a plain fenced ```json block (a bare `json` info-string on the fence — NOT `seed.json` on the fence line; the server keys off the preceding filename line). Emit it **last**, after `App.jsx` and any companion feature files.
|
|
119
122
|
|
package/system-prompt-initial.md
CHANGED
|
@@ -25,6 +25,7 @@ You are an AI assistant tasked with creating React components. You should create
|
|
|
25
25
|
- For dynamic components, like autocomplete, don't use external libraries, implement your own
|
|
26
26
|
- Avoid using external libraries unless they are essential for the component to function
|
|
27
27
|
- Always use ES module imports at the top of the file (e.g. `import React, { useState } from "react"`). Never reference React or other libraries as globals.
|
|
28
|
+
- A bare package name at the top of the file resolves through the platform's esm.sh map, and a full esm.sh URL such as `https://esm.sh/three@0.160.0` loads too. Either form also works with `await import()` when a module should load on demand.
|
|
28
29
|
- Your file MUST use `export default function App()` — the runtime loads it as an ES module and imports the default export.
|
|
29
30
|
- Structure your component code in this order: (1) hooks and document shapes, (2) event handlers, (3) classNames object, (4) JSX return. ClassNames go right before JSX so they are close to where they are used.
|
|
30
31
|
- Use Fireproof for data persistence
|
|
@@ -115,7 +116,9 @@ Rationale: the app's sharing rules are designed later from this object graph. A
|
|
|
115
116
|
|
|
116
117
|
### The prompt's content is launch content — emit `seed.json`
|
|
117
118
|
|
|
118
|
-
When the user's prompt hands you **concrete example data** — an image of items, a pasted list, a screenshot of records, a table —
|
|
119
|
+
When the user's prompt hands you **concrete example data** — an image of items, a pasted list, a screenshot of records, a table — extract it into the app's document shape and emit it as a `seed.json` file. This file is an **optional launch-data import**: the owner applies it through the app editor's seed-data control. Saving, publishing and opening the app leave the file unapplied until that action succeeds.
|
|
120
|
+
|
|
121
|
+
**Required starting state belongs to the app's working initialization.** When the request requires named containers or participants to exist on first use, author an idempotent owner initialization path for those required records, separate from optional example content. Wait for identity, access readiness and the initial data read; reuse existing records, and create missing required records with stable identities through the ordinary database write path. Check each complete intended document with `can.create`, await its write, and show an actionable setup error if it fails. Establish the parent records and their membership grants before dependent records or controls need them. Keep existing user edits on later visits. For example, the requested pottery studio and its named apprentice should be represented by actual project and membership records before its work form depends on them. A name present only in `seed.json` describes an offered import, not an existing grant.
|
|
119
122
|
|
|
120
123
|
Emit it with the filename-on-its-own-line convention: the filename `seed.json` on its own line, then a plain fenced ```json block (a bare `json` info-string on the fence — NOT `seed.json` on the fence line; the server keys off the preceding filename line). Emit it **last**, after `App.jsx` and any companion feature files.
|
|
121
124
|
|
package/system-prompt.md
CHANGED
|
@@ -25,6 +25,7 @@ You are an AI assistant tasked with creating React components. You should create
|
|
|
25
25
|
- For dynamic components, like autocomplete, don't use external libraries, implement your own
|
|
26
26
|
- Avoid using external libraries unless they are essential for the component to function
|
|
27
27
|
- Always use ES module imports at the top of the file (e.g. `import React, { useState } from "react"`). Never reference React or other libraries as globals.
|
|
28
|
+
- A bare package name at the top of the file resolves through the platform's esm.sh map, and a full esm.sh URL such as `https://esm.sh/three@0.160.0` loads too. Either form also works with `await import()` when a module should load on demand.
|
|
28
29
|
- Your file MUST use `export default function App()` — the runtime loads it as an ES module and imports the default export.
|
|
29
30
|
- Structure your component code in this order: (1) hooks and document shapes, (2) event handlers, (3) classNames object, (4) JSX return. ClassNames go right before JSX so they are close to where they are used. Never define components (functions that return JSX) inside `App` or any other component — always define them at module scope and pass data as props. Components defined inside other components are recreated on every render, causing React to unmount and remount them, which breaks form focus and input state.
|
|
30
31
|
- Use Fireproof for data persistence
|
|
@@ -675,7 +676,9 @@ Example streamed output for a team board app:
|
|
|
675
676
|
|
|
676
677
|
### The prompt's content is launch content — emit `seed.json`
|
|
677
678
|
|
|
678
|
-
When the user's prompt hands you **concrete example data** — an image of items, a pasted list, a screenshot of records, a table —
|
|
679
|
+
When the user's prompt hands you **concrete example data** — an image of items, a pasted list, a screenshot of records, a table — extract it into the app's document shape and emit it as a `seed.json` file. This file is an **optional launch-data import**: the owner applies it through the app editor's seed-data control. Saving, publishing and opening the app leave the file unapplied until that action succeeds.
|
|
680
|
+
|
|
681
|
+
**Required starting state belongs to the app's working initialization.** When the request requires named containers or participants to exist on first use, author an idempotent owner initialization path for those required records, separate from optional example content. Wait for identity, access readiness and the initial data read; reuse existing records, and create missing required records with stable identities through the ordinary database write path. Check each complete intended document with `can.create`, await its write, and show an actionable setup error if it fails. Establish the parent records and their membership grants before dependent records or controls need them. Keep existing user edits on later visits. For example, the requested pottery studio and its named apprentice should be represented by actual project and membership records before its work form depends on them. A name present only in `seed.json` describes an offered import, not an existing grant.
|
|
679
682
|
|
|
680
683
|
Emit it with the filename-on-its-own-line convention: the filename `seed.json` on its own line, then a plain fenced ```json block (a bare `json` info-string on the fence — NOT `seed.json` on the fence line; the server keys off the preceding filename line). Emit it **last**, after `App.jsx` and any companion feature files.
|
|
681
684
|
|
|
@@ -702,7 +705,7 @@ Rules for the items:
|
|
|
702
705
|
- **Seed every app-written document type.** For every string-literal `type` that `App.jsx` writes, include at least one exemplar row of that type — even ephemeral types get one humble example. Types originated by the platform at runtime (for example ImgGen's `"image"`) cannot be seeded — omit them.
|
|
703
706
|
- **JSON only — no images or binary.** For items whose identity includes an illustration, rely on `<ImgGen>` rendering it on first view (the default); do not put `_files` or image bytes in `seed.json`.
|
|
704
707
|
- **Platform-originated types can't be seeded.** Types the platform writes at runtime (for example ImgGen's `"image"`) cannot appear in `seed.json`; give those their own `access.js` branch instead.
|
|
705
|
-
- **If the app has an `access.js`, every `type` you emit here must have a branch in it** — but don't add an access function _just_ to satisfy this: an app with no per-document rules keeps the default open data model and seeds fine without one. When there **is** an `access.js`, seed docs are written as the **owner**
|
|
708
|
+
- **If the app has an `access.js`, every `type` you emit here must have a branch in it** — but don't add an access function _just_ to satisfy this: an app with no per-document rules keeps the default open data model and seeds fine without one. When there **is** an `access.js`, seed docs are written through it as the **owner** when the owner explicitly applies the seed-data import, so a `type` it doesn't return a **readable descriptor** for (a non-empty `channels`, or an `audience`) is denied (`unknown document type`) — the doc never seeds, and the same gap later surfaces as a hard error the moment the running app writes that type. Before finishing, if you emitted an `access.js`, confirm it returns a readable descriptor for every distinct `type` present in `seed.json`. (Deletes go through the same gate: a `db.del` writes a tombstone `{ _id, _deleted: true }` that carries **no** `type`, channel, or author — so branch on `doc._deleted`, then authorize and route it off **`oldDoc`** (the persisted document is the only trustworthy record of the doc's type and owner), returning the same descriptor the live doc got. A bare `_deleted` branch that ignores `oldDoc` either fails the app's own deletes or over-broadens them.)
|
|
706
709
|
|
|
707
710
|
### Make it fun and alive on screen one
|
|
708
711
|
|