@vibes.diy/prompts 14.3.122 → 14.3.124
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 +1 -1
- package/llms/richtext.md +33 -0
- package/package.json +5 -5
- package/system-prompt-initial-oneshot.md +7 -3
- package/system-prompt-initial.md +7 -3
- package/system-prompt.md +1 -1
package/llms/access.md
CHANGED
|
@@ -1383,7 +1383,7 @@ function Shop() {
|
|
|
1383
1383
|
|
|
1384
1384
|
When the instruction names somebody by their handle — "let my friend priya-k see and add to my seed-swap shelf", "an expedition notebook for me and theo" — **the build's part is the rule that admits a membership and the screen that writes one; the membership itself is a record.** It is the same `member` doc the rule admits and the invite surface above writes, saved for that handle in the asker's own tree — or on the second object the ask names — once the rules are live, by whoever is driving the build acting as the maker, read back, and only then spoken of as in. An app whose first build carried the rules and no invite screen still states this shape in full, in `access.js`'s member branch: the type and fields that branch admits are the record a later save writes, and an edit that adds the screen afterwards writes that same record.
|
|
1385
1385
|
|
|
1386
|
-
So the record's shape is a contract the files state plainly. The `_id` is fixed by the place and the handle — `` `member:${me.userHandle}:${userHandle}` `` in the person's tree, `` `member:${boardId}:${userHandle}` `` on a board — so a second attempt is the same record rather than a twin, and a read by that id answers "is this person already in?" on its own. The fields are the ones the rule reads: `type`, `userHandle`, `addedBy` set to the writer's own handle, and the object's id when the membership is on a second object. The `member` rule admits an identical re-put, so a replay lands quietly. The tree's channel stays its person's because their own writes grant it, and a second object keeps its creator in the collaboration through its own record, independently of every content and invitation record. One invite handler in `App.jsx` writes exactly that shape, which is what makes the app's own screen and any later save agree on one record.
|
|
1386
|
+
So the record's shape is a contract the files state plainly. The `_id` is fixed by the place and the handle — `` `member:${me.userHandle}:${userHandle}` `` in the person's tree, `` `member:${boardId}:${userHandle}` `` on a board — so a second attempt is the same record rather than a twin, and a read by that id answers "is this person already in?" on its own. The fields are the ones the rule reads: `type`, `userHandle`, `addedBy` set to the writer's own handle, and the object's id when the membership is on a second object. The notes rules above route every record to the writer's own channel and read no tree field, so the record below carries none. When an app's rules instead make every record name its tree in a field before any type branch — `studioHandle`, refused with "a record names its tree" when absent — the member record carries the same `studioHandle` as the tree it joins, in the `can.create` preview that decides whether the add-a-friend control shows and in the record the invite saves; build both with one `memberFor(handle)` so they cannot drift, and a preview that leaves the field out is refused and hides the control for everyone. The `member` rule admits an identical re-put, so a replay lands quietly. The tree's channel stays its person's because their own writes grant it, and a second object keeps its creator in the collaboration through its own record, independently of every content and invitation record. One invite handler in `App.jsx` writes exactly that shape, which is what makes the app's own screen and any later save agree on one record.
|
|
1387
1387
|
|
|
1388
1388
|
```jsx
|
|
1389
1389
|
const { HandleInput } = useViewer();
|
package/llms/richtext.md
CHANGED
|
@@ -406,6 +406,35 @@ overlap (overlapping puts collide on the document's sequence, as in the
|
|
|
406
406
|
saving section above). The `Input` stays `onChange={setTitle}`; the effect
|
|
407
407
|
does the saving.
|
|
408
408
|
|
|
409
|
+
## The first draft stays open through its first save
|
|
410
|
+
|
|
411
|
+
**A draft the empty view opens stays open through its first save: which
|
|
412
|
+
document is open is state, and the draft joins it before its first put,
|
|
413
|
+
never by a reading of the list.** A blog, journal or recipe book whose list
|
|
414
|
+
is empty may open straight onto a blank draft, so the first post is front
|
|
415
|
+
and centre. That draft's first autosave puts it in the list, so a view that
|
|
416
|
+
chose the editor because the list was empty (`editing ?? (mine.length === 0
|
|
417
|
+
? blank(id) : null)`) flips back to the list mid-sentence. The put shows the
|
|
418
|
+
draft in the list at once, before the save is confirmed, so handing the
|
|
419
|
+
draft back once the put resolves is one render too late: the editor
|
|
420
|
+
unmounts and remounts, and the cursor is gone. Hand the editor the
|
|
421
|
+
open-state setter and call it in `write` before the put: the draft keeps
|
|
422
|
+
its `_id`, so the editor keeps its `key`, and the writer keeps typing until
|
|
423
|
+
they choose Back.
|
|
424
|
+
|
|
425
|
+
```jsx
|
|
426
|
+
const [editing, setEditing] = useState(null); // the open draft
|
|
427
|
+
const [firstId] = useState(() => crypto.randomUUID()); // the empty view's draft
|
|
428
|
+
const target = editing ?? (mine.length === 0 ? blank(firstId) : null);
|
|
429
|
+
|
|
430
|
+
// In App: the draft's first change makes it the open draft.
|
|
431
|
+
<PostEditor key={target._id} initial={target} onOpen={setEditing} onClose={() => setEditing(null)} />
|
|
432
|
+
|
|
433
|
+
// In PostEditor's write: the draft becomes the open one, then its put is queued.
|
|
434
|
+
onOpen(next);
|
|
435
|
+
queue.current = queue.current.then(() => database.put(next));
|
|
436
|
+
```
|
|
437
|
+
|
|
409
438
|
## Loading a document lands without a save
|
|
410
439
|
|
|
411
440
|
**A document arriving from the database is loaded with `setContent` and
|
|
@@ -657,6 +686,10 @@ warning.
|
|
|
657
686
|
React state and only Publish writes it. Save the title after its own
|
|
658
687
|
pause and in the close step, through the same queued `write` as the page,
|
|
659
688
|
and let the close step wait for it.
|
|
689
|
+
- **The first draft closes itself after a pause** — the empty list was all
|
|
690
|
+
that held the editor open, and the draft's first save filled the list.
|
|
691
|
+
`write` hands the draft to the open-state setter before the put
|
|
692
|
+
(`onOpen={setEditing}`); a callback after the put lands too late.
|
|
660
693
|
- **Two editors after a hot reload** — the effect's cleanup did not destroy
|
|
661
694
|
the editor.
|
|
662
695
|
- **The page has no styles** — Tiptap ships none, and a multi-file app
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibes.diy/prompts",
|
|
3
|
-
"version": "14.3.
|
|
3
|
+
"version": "14.3.124",
|
|
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.124",
|
|
43
|
+
"@vibes.diy/call-ai-v2": "14.3.124",
|
|
44
|
+
"@vibes.diy/identity": "14.3.124",
|
|
45
|
+
"@vibes.diy/use-vibes-types": "14.3.124",
|
|
46
46
|
"arktype": "~2.2.6",
|
|
47
47
|
"json-schema-faker": "~0.6.3"
|
|
48
48
|
},
|
|
@@ -248,8 +248,12 @@ function AddFriend({ place }) {
|
|
|
248
248
|
const { me, can, ready } = useVibe("studio");
|
|
249
249
|
const { HandleInput } = useViewer();
|
|
250
250
|
const where = place.loom ? place.loom._id : place.key.studioHandle;
|
|
251
|
+
// The member names the place it joins in the field the rules route on, in the preview and the
|
|
252
|
+
// saved record alike: the tree's own studioHandle, or the loom's loomId. Without it the rules
|
|
253
|
+
// refuse the preview, and the card never shows.
|
|
251
254
|
const memberFor = (handle) => ({
|
|
252
|
-
_id: "member:" + where + ":" + handle, type: "member",
|
|
255
|
+
_id: "member:" + where + ":" + handle, type: "member",
|
|
256
|
+
...(place.loom ? { loomId: place.loom._id } : { studioHandle: place.key.studioHandle }),
|
|
253
257
|
userHandle: handle, addedBy: me?.userHandle, authorHandle: me?.userHandle,
|
|
254
258
|
});
|
|
255
259
|
const invite = async (handle) => {
|
|
@@ -399,7 +403,7 @@ The control and its handler check the **same complete candidate** that `database
|
|
|
399
403
|
Write this turn's `access.js` against precisely these shapes: the person's own records route to the channel named by their handle and grant it on every write; a friend's write into that tree needs the channel a `member` record granted them; a second or shared object routes to the channel named by its own `_id` and grants its creator that channel and its admin channel; membership grants the place it names. Keep each record's type, author and place fixed across edits. A bobbin's `_id` equals `bobbin-<authorHandle>-<weavingId>`; the client takes the place and the weaving id from the displayed weaving, and the rule holds them fixed on subsequent writes. Nothing writes on load or on mount: the first screen opens on the person's tree because the channel is named by their handle, and every record exists because the person made it.
|
|
400
404
|
|
|
401
405
|
- **Keep the opening view useful.** The person arrives inside their own tree: the first screen is the app at work, named for the app, with a rename that writes the settings record when they want a name of their own. The header carries the app's name once; a separate name line becomes useful after a rename or a second object. A second-object form joins the object list when useful. Show the person's own tree, each tree a membership lets them into, and the whole readable type-query result of second objects, including ones someone else made; `creatorHandle` identifies their makers.
|
|
402
|
-
- **Invite with the actual membership candidate.** When the ask names sharing, a group or other people, include `HandleInput` from `useViewer()` and a handler that saves `{ _id: "member:" + place + ":" + handle, type: "member", studioHandle: me.userHandle, userHandle: handle, addedBy: me.userHandle, authorHandle: me.userHandle }` for the person's own tree, or the same record with `loomId` for a loom. Construct that value after the picker resolves the handle, gate it with `can.create`, then persist the same value. The `member` branch admits exactly that shape: in a tree the writer grants their own channel, on a second object its admin grants that object's channel, and the subject, place and writer stay fixed. A resolved `invite:<id>` is stored as the subject just like a handle. Await a real membership write and readback before saying a named person is in. When the app is a person's own collection, this same control is its first-screen share, with the line "Yours alone — add a friend to share it" beside it; otherwise, when sharing was not asked for, offer inviting as a closing option.
|
|
406
|
+
- **Invite with the actual membership candidate.** When the ask names sharing, a group or other people, include `HandleInput` from `useViewer()` and a handler that saves `{ _id: "member:" + place + ":" + handle, type: "member", studioHandle: me.userHandle, userHandle: handle, addedBy: me.userHandle, authorHandle: me.userHandle }` for the person's own tree, or the same record with `loomId` for a loom. Every member record carries the same `studioHandle` as the tree it joins — the field the rules require of every record in a tree — in the `can.create` preview that decides whether the card shows and in the record the invite saves, both built by one `memberFor`. Construct that value after the picker resolves the handle, gate it with `can.create`, then persist the same value. The `member` branch admits exactly that shape: in a tree the writer grants their own channel, on a second object its admin grants that object's channel, and the subject, place and writer stay fixed. A resolved `invite:<id>` is stored as the subject just like a handle. Await a real membership write and readback before saying a named person is in. When the app is a person's own collection, this same control is its first-screen share, with the line "Yours alone — add a friend to share it" beside it; otherwise, when sharing was not asked for, offer inviting as a closing option.
|
|
403
407
|
- **Persist the sharing choice.** A public/follower toggle writes the field its rules read (`isPublic`, `sharedWithFollowers`), with the full edited candidate checked before saving. Its who-can-see line sits beside that control and describes the channel the rules actually share. Follow `access.md` for follower consent and channel granularity.
|
|
404
408
|
- **Protect supporting records.** A membership record containing contact details or a capability token belongs only to its intended readers; choose its private channel accordingly while its grant admits the recipient to the place's channel. Keep contact/token-bearing types distinct from ordinary content and out of content queries and Ask inputs.
|
|
405
409
|
- **Keep examples and the file protocol consistent.** The first build writes the files and privacy rules the surrounding prompt requires. `seed.json` uses these same types, field names and place keys, with `"@owner"` in writer and tree fields (`authorHandle`, `studioHandle`), and leaves the settings record out so the demo opens named for the app. The access rules admit every example record, and the final `// Summary:` accurately describes those rules; private records begin private. Follow the existing attachment/file protocol for uploaded files and their visibility.
|
|
@@ -410,7 +414,7 @@ When the user's prompt hands you **concrete example data** — an image of items
|
|
|
410
414
|
|
|
411
415
|
**Required starting state is saved by an action, never by the app on load.** A person's own action — a tap, a submit, a pick — is what starts a write; nothing writes on load or on mount. A person's own starting state needs no record: their records live on the channel named by their handle, and the view renders its defaults from constants until the person's own action saves one. App-wide named containers or participants the request requires — the requested pottery studio and its named apprentice — are written by the agent building the app with `put_doc` as the owner during the build, once the rules that admit them are live, and read back; that save is the owner's own action. Give each one a stable identity, write parents and their membership or role grants before what depends on them, and let the opening view show them when they are there and an inviting add flow until they are. `access.js` admits exactly those records, and the app's code holds the handlers a person uses to add more. Starter content everyone shares — three starter characters, a starting set of prompts — is not required state: it is a fixed set of documents in `seed.json`, and the app's code writes no copy of it for each visitor. Adding the seed is the owner's choice, so the app works without it: until then the shared list opens empty, inviting the first person to make one. `seed.json` stays the optional example-content import: a name present only there describes an offered import, not an existing record or grant.
|
|
412
416
|
|
|
413
|
-
**Content the owner authors for everyone else ships its examples in `seed.json`.** When the owner authors what everyone else comes to see — a taco truck's menu, a florist's price list, a dog walker's weekly schedule, a plant nursery's stock — emit a `seed.json` of believable examples in that shape, whether the ask is vague or names the fields: every field a real entry carries, each item keyed to the owner's object, because the file is what the app's screenshot shows and what the owner adds to their live app with one ask. The app's own code writes nothing invented and nothing on load — only what a person's own action enters. The owner's view leads with the add flow only while it holds no entries — the form for the first entry front and centre, each entry appearing in place as they save it — and from the first entry on the list leads, with adding behind a clear button or a compact entry at the top. A specified ask that names the fields of what the owner offers opens on that empty view's add flow, with the visitor's view of that moment speaking in the shop's own voice. The same line governs every list in every app, and the seeded demo with it: with the examples in place the screenshot shows the records first and the add above them, never a form that pushes them below the fold.
|
|
417
|
+
**Content the owner authors for everyone else ships its examples in `seed.json`.** When the owner authors what everyone else comes to see — a taco truck's menu, a florist's price list, a dog walker's weekly schedule, a plant nursery's stock — emit a `seed.json` of believable examples in that shape, whether the ask is vague or names the fields: every field a real entry carries, each item keyed to the owner's object, because the file is what the app's screenshot shows and what the owner adds to their live app with one ask. The app's own code writes nothing invented and nothing on load — only what a person's own action enters. The owner's view leads with the add flow only while it holds no entries — the form for the first entry front and centre, each entry appearing in place as they save it — and from the first entry on the list leads, with adding behind a clear button or a compact entry at the top. That switch waits for the person to finish: an add flow that saves as they go — a draft that autosaves at each pause — stays open through its own first save, because which entry is open is state, set as the person starts the entry and before its first save, never a reading of the list. A specified ask that names the fields of what the owner offers opens on that empty view's add flow, with the visitor's view of that moment speaking in the shop's own voice. The same line governs every list in every app, and the seeded demo with it: with the examples in place the screenshot shows the records first and the add above them, never a form that pushes them below the fold.
|
|
414
418
|
|
|
415
419
|
**The seed dresses the opening screen.** The screenshot and the owner's first look both see one phone screen, so write the seed for that screen: the richest, most representative state of the app lives on the view it opens to, filled with just enough records to cover that one screen at the size the view draws them — a few rich cards, or a handful more compact rows — a lived-in week rather than a wall. Give that view one of each state the app knows how to show, so every feature is visible at first glance: a canoe club's trip board opens on a trip already paddled, one coming up next and one just posted with its put-in note; a sourdough starter log opens on a feed marked done, one due next, and a fresh loaf with its note. Every sample belongs to the owner, and its names, notes and numbers read like a real person's, so the first screen looks like someone has been happily using the app for a while.
|
|
416
420
|
|
package/system-prompt-initial.md
CHANGED
|
@@ -252,8 +252,12 @@ function AddFriend({ place }) {
|
|
|
252
252
|
const { me, can, ready } = useVibe("studio");
|
|
253
253
|
const { HandleInput } = useViewer();
|
|
254
254
|
const where = place.loom ? place.loom._id : place.key.studioHandle;
|
|
255
|
+
// The member names the place it joins in the field the rules route on, in the preview and the
|
|
256
|
+
// saved record alike: the tree's own studioHandle, or the loom's loomId. Without it the rules
|
|
257
|
+
// refuse the preview, and the card never shows.
|
|
255
258
|
const memberFor = (handle) => ({
|
|
256
|
-
_id: "member:" + where + ":" + handle, type: "member",
|
|
259
|
+
_id: "member:" + where + ":" + handle, type: "member",
|
|
260
|
+
...(place.loom ? { loomId: place.loom._id } : { studioHandle: place.key.studioHandle }),
|
|
257
261
|
userHandle: handle, addedBy: me?.userHandle, authorHandle: me?.userHandle,
|
|
258
262
|
});
|
|
259
263
|
const invite = async (handle) => {
|
|
@@ -403,7 +407,7 @@ The control and its handler check the **same complete candidate** that `database
|
|
|
403
407
|
Write this turn's `access.js` against precisely these shapes: the person's own records route to the channel named by their handle and grant it on every write; a friend's write into that tree needs the channel a `member` record granted them; a second or shared object routes to the channel named by its own `_id` and grants its creator that channel and its admin channel; membership grants the place it names. Keep each record's type, author and place fixed across edits. A bobbin's `_id` equals `bobbin-<authorHandle>-<weavingId>`; the client takes the place and the weaving id from the displayed weaving, and the rule holds them fixed on subsequent writes. Nothing writes on load or on mount: the first screen opens on the person's tree because the channel is named by their handle, and every record exists because the person made it.
|
|
404
408
|
|
|
405
409
|
- **Keep the opening view useful.** The person arrives inside their own tree: the first screen is the app at work, named for the app, with a rename that writes the settings record when they want a name of their own. The header carries the app's name once; a separate name line becomes useful after a rename or a second object. A second-object form joins the object list when useful. Show the person's own tree, each tree a membership lets them into, and the whole readable type-query result of second objects, including ones someone else made; `creatorHandle` identifies their makers.
|
|
406
|
-
- **Invite with the actual membership candidate.** When the ask names sharing, a group or other people, include `HandleInput` from `useViewer()` and a handler that saves `{ _id: "member:" + place + ":" + handle, type: "member", studioHandle: me.userHandle, userHandle: handle, addedBy: me.userHandle, authorHandle: me.userHandle }` for the person's own tree, or the same record with `loomId` for a loom. Construct that value after the picker resolves the handle, gate it with `can.create`, then persist the same value. The `member` branch admits exactly that shape: in a tree the writer grants their own channel, on a second object its admin grants that object's channel, and the subject, place and writer stay fixed. A resolved `invite:<id>` is stored as the subject just like a handle. Await a real membership write and readback before saying a named person is in. When the app is a person's own collection, this same control is its first-screen share, with the line "Yours alone — add a friend to share it" beside it; otherwise, when sharing was not asked for, offer inviting as a closing option.
|
|
410
|
+
- **Invite with the actual membership candidate.** When the ask names sharing, a group or other people, include `HandleInput` from `useViewer()` and a handler that saves `{ _id: "member:" + place + ":" + handle, type: "member", studioHandle: me.userHandle, userHandle: handle, addedBy: me.userHandle, authorHandle: me.userHandle }` for the person's own tree, or the same record with `loomId` for a loom. Every member record carries the same `studioHandle` as the tree it joins — the field the rules require of every record in a tree — in the `can.create` preview that decides whether the card shows and in the record the invite saves, both built by one `memberFor`. Construct that value after the picker resolves the handle, gate it with `can.create`, then persist the same value. The `member` branch admits exactly that shape: in a tree the writer grants their own channel, on a second object its admin grants that object's channel, and the subject, place and writer stay fixed. A resolved `invite:<id>` is stored as the subject just like a handle. Await a real membership write and readback before saying a named person is in. When the app is a person's own collection, this same control is its first-screen share, with the line "Yours alone — add a friend to share it" beside it; otherwise, when sharing was not asked for, offer inviting as a closing option.
|
|
407
411
|
- **Persist the sharing choice.** A public/follower toggle writes the field its rules read (`isPublic`, `sharedWithFollowers`), with the full edited candidate checked before saving. Its who-can-see line sits beside that control and describes the channel the rules actually share. Follow `access.md` for follower consent and channel granularity.
|
|
408
412
|
- **Protect supporting records.** A membership record containing contact details or a capability token belongs only to its intended readers; choose its private channel accordingly while its grant admits the recipient to the place's channel. Keep contact/token-bearing types distinct from ordinary content and out of content queries and Ask inputs.
|
|
409
413
|
- **Keep examples and the file protocol consistent.** The first build writes the files and privacy rules the surrounding prompt requires. `seed.json` uses these same types, field names and place keys, with `"@owner"` in writer and tree fields (`authorHandle`, `studioHandle`), and leaves the settings record out so the demo opens named for the app. The access rules admit every example record, and the final `// Summary:` accurately describes those rules; private records begin private. Follow the existing attachment/file protocol for uploaded files and their visibility.
|
|
@@ -414,7 +418,7 @@ When the user's prompt hands you **concrete example data** — an image of items
|
|
|
414
418
|
|
|
415
419
|
**Required starting state is saved by an action, never by the app on load.** A person's own action — a tap, a submit, a pick — is what starts a write; nothing writes on load or on mount. A person's own starting state needs no record: their records live on the channel named by their handle, and the view renders its defaults from constants until the person's own action saves one. App-wide named containers or participants the request requires — the requested pottery studio and its named apprentice — are written by the agent building the app with `put_doc` as the owner during the build, once the rules that admit them are live, and read back; that save is the owner's own action. Give each one a stable identity, write parents and their membership or role grants before what depends on them, and let the opening view show them when they are there and an inviting add flow until they are. `access.js` admits exactly those records, and the app's code holds the handlers a person uses to add more. Starter content everyone shares — three starter characters, a starting set of prompts — is not required state: it is a fixed set of documents in `seed.json`, and the app's code writes no copy of it for each visitor. Adding the seed is the owner's choice, so the app works without it: until then the shared list opens empty, inviting the first person to make one. `seed.json` stays the optional example-content import: a name present only there describes an offered import, not an existing record or grant.
|
|
416
420
|
|
|
417
|
-
**Content the owner authors for everyone else ships its examples in `seed.json`.** When the owner authors what everyone else comes to see — a taco truck's menu, a florist's price list, a dog walker's weekly schedule, a plant nursery's stock — emit a `seed.json` of believable examples in that shape, whether the ask is vague or names the fields: every field a real entry carries, each item keyed to the owner's object, because the file is what the app's screenshot shows and what the owner adds to their live app with one ask. The app's own code writes nothing invented and nothing on load — only what a person's own action enters. The owner's view leads with the add flow only while it holds no entries — the form for the first entry front and centre, each entry appearing in place as they save it — and from the first entry on the list leads, with adding behind a clear button or a compact entry at the top. A specified ask that names the fields of what the owner offers opens on that empty view's add flow, with the visitor's view of that moment speaking in the shop's own voice. The same line governs every list in every app, and the seeded demo with it: with the examples in place the screenshot shows the records first and the add above them, never a form that pushes them below the fold.
|
|
421
|
+
**Content the owner authors for everyone else ships its examples in `seed.json`.** When the owner authors what everyone else comes to see — a taco truck's menu, a florist's price list, a dog walker's weekly schedule, a plant nursery's stock — emit a `seed.json` of believable examples in that shape, whether the ask is vague or names the fields: every field a real entry carries, each item keyed to the owner's object, because the file is what the app's screenshot shows and what the owner adds to their live app with one ask. The app's own code writes nothing invented and nothing on load — only what a person's own action enters. The owner's view leads with the add flow only while it holds no entries — the form for the first entry front and centre, each entry appearing in place as they save it — and from the first entry on the list leads, with adding behind a clear button or a compact entry at the top. That switch waits for the person to finish: an add flow that saves as they go — a draft that autosaves at each pause — stays open through its own first save, because which entry is open is state, set as the person starts the entry and before its first save, never a reading of the list. A specified ask that names the fields of what the owner offers opens on that empty view's add flow, with the visitor's view of that moment speaking in the shop's own voice. The same line governs every list in every app, and the seeded demo with it: with the examples in place the screenshot shows the records first and the add above them, never a form that pushes them below the fold.
|
|
418
422
|
|
|
419
423
|
**The seed dresses the opening screen.** The screenshot and the owner's first look both see one phone screen, so write the seed for that screen: the richest, most representative state of the app lives on the view it opens to, filled with just enough records to cover that one screen at the size the view draws them — a few rich cards, or a handful more compact rows — a lived-in week rather than a wall. Give that view one of each state the app knows how to show, so every feature is visible at first glance: a canoe club's trip board opens on a trip already paddled, one coming up next and one just posted with its put-in note; a sourdough starter log opens on a feed marked done, one due next, and a fresh loaf with its note. Every sample belongs to the owner, and its names, notes and numbers read like a real person's, so the first screen looks like someone has been happily using the app for a while.
|
|
420
424
|
|
package/system-prompt.md
CHANGED
|
@@ -724,7 +724,7 @@ When the user's prompt hands you **concrete example data** — an image of items
|
|
|
724
724
|
|
|
725
725
|
**Required starting state is saved by an action, never by the app on load.** A person's own action — a tap, a submit, a pick — is what starts a write; nothing writes on load or on mount. A person's own starting state needs no record: their records live on the channel named by their handle, and the view renders its defaults from constants until the person's own action saves one. App-wide named containers or participants the request requires — the requested pottery studio and its named apprentice — are written by the agent building the app with `put_doc` as the owner during the build, once the rules that admit them are live, and read back; that save is the owner's own action. Give each one a stable identity, write parents and their membership or role grants before what depends on them, and let the opening view show them when they are there and an inviting add flow until they are. `access.js` admits exactly those records, and the app's code holds the handlers a person uses to add more. Starter content everyone shares — three starter characters, a starting set of prompts — is not required state: it is a fixed set of documents in `seed.json`, and the app's code writes no copy of it for each visitor. Adding the seed is the owner's choice, so the app works without it: until then the shared list opens empty, inviting the first person to make one. `seed.json` stays the optional example-content import: a name present only there describes an offered import, not an existing record or grant.
|
|
726
726
|
|
|
727
|
-
**Content the owner authors for everyone else ships its examples in `seed.json`.** When the owner authors what everyone else comes to see — a taco truck's menu, a florist's price list, a dog walker's weekly schedule, a plant nursery's stock — emit a `seed.json` of believable examples in that shape, whether the ask is vague or names the fields: every field a real entry carries, each item keyed to the owner's object, because the file is what the app's screenshot shows and what the owner adds to their live app with one ask. The app's own code writes nothing invented and nothing on load — only what a person's own action enters. The owner's view leads with the add flow only while it holds no entries — the form for the first entry front and centre, each entry appearing in place as they save it — and from the first entry on the list leads, with adding behind a clear button or a compact entry at the top. A specified ask that names the fields of what the owner offers opens on that empty view's add flow, with the visitor's view of that moment speaking in the shop's own voice. The same line governs every list in every app, and the seeded demo with it: with the examples in place the screenshot shows the records first and the add above them, never a form that pushes them below the fold.
|
|
727
|
+
**Content the owner authors for everyone else ships its examples in `seed.json`.** When the owner authors what everyone else comes to see — a taco truck's menu, a florist's price list, a dog walker's weekly schedule, a plant nursery's stock — emit a `seed.json` of believable examples in that shape, whether the ask is vague or names the fields: every field a real entry carries, each item keyed to the owner's object, because the file is what the app's screenshot shows and what the owner adds to their live app with one ask. The app's own code writes nothing invented and nothing on load — only what a person's own action enters. The owner's view leads with the add flow only while it holds no entries — the form for the first entry front and centre, each entry appearing in place as they save it — and from the first entry on the list leads, with adding behind a clear button or a compact entry at the top. That switch waits for the person to finish: an add flow that saves as they go — a draft that autosaves at each pause — stays open through its own first save, because which entry is open is state, set as the person starts the entry and before its first save, never a reading of the list. A specified ask that names the fields of what the owner offers opens on that empty view's add flow, with the visitor's view of that moment speaking in the shop's own voice. The same line governs every list in every app, and the seeded demo with it: with the examples in place the screenshot shows the records first and the add above them, never a form that pushes them below the fold.
|
|
728
728
|
|
|
729
729
|
**The seed dresses the opening screen.** The screenshot and the owner's first look both see one phone screen, so write the seed for that screen: the richest, most representative state of the app lives on the view it opens to, filled with just enough records to cover that one screen at the size the view draws them — a few rich cards, or a handful more compact rows — a lived-in week rather than a wall. Give that view one of each state the app knows how to show, so every feature is visible at first glance: a canoe club's trip board opens on a trip already paddled, one coming up next and one just posted with its put-in note; a sourdough starter log opens on a feed marked done, one due next, and a fresh loaf with its note. Every sample belongs to the owner, and its names, notes and numbers read like a real person's, so the first screen looks like someone has been happily using the app for a while.
|
|
730
730
|
|