@vibes.diy/prompts 14.3.38 → 14.3.40
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 +7 -1
- package/llms/callai.initial.md +2 -0
- package/llms/callai.md +2 -0
- package/llms/use-viewer.initial.md +2 -0
- package/llms/use-viewer.md +2 -0
- package/llms/web-audio.js +6 -0
- package/llms/web-audio.js.map +1 -1
- package/llms/web-audio.md +64 -0
- package/package.json +4 -4
- package/system-prompt-initial-oneshot.md +1 -0
- package/system-prompt-initial.md +1 -0
- package/system-prompt.md +1 -0
package/llms/access.md
CHANGED
|
@@ -14,7 +14,13 @@ including the intended next status. For example, cancelling a reservation constr
|
|
|
14
14
|
`const next = { ...reservation, status: "cancelled" }`, checks `can.edit(next).ok`, and
|
|
15
15
|
saves `next`. A creation control checks the complete creation draft. On a denial render a
|
|
16
16
|
sentence in the app's own words — `reason` is a machine token that informs that sentence —
|
|
17
|
-
and keep loading and signed-out states distinct from denial.
|
|
17
|
+
and keep loading and signed-out states distinct from denial. Name the invite screen in the
|
|
18
|
+
app's world — a party board says "Invite a friend" while a team workspace keeps the fitting
|
|
19
|
+
"Invite Collaborator" — and shape the denial for who is reading it: a signed-out visitor gets
|
|
20
|
+
the contextual sign-in invitation ("Sign in to add your roast", wired to `requestLogin()`); a
|
|
21
|
+
signed-in viewer outside the membership gets the membership explanation, not another sign-in
|
|
22
|
+
button. A playful occasion never widens the audience — the rules still decide who reads and
|
|
23
|
+
writes.
|
|
18
24
|
|
|
19
25
|
## Reference
|
|
20
26
|
|
package/llms/callai.initial.md
CHANGED
|
@@ -6,6 +6,8 @@ The browser runtime validates each answer against your schema. A schema mismatch
|
|
|
6
6
|
|
|
7
7
|
Use this capability when an app turns words into structured records or provides requested model-generated text. Direct controls handle known values, and a simple message saves directly when its text is the whole record.
|
|
8
8
|
|
|
9
|
+
Name what the app does in the app's own words. The function name `callAI` stays in implementation context — it never becomes a label, a badge, or a credit line in the UI (a roast board says what it makes, not what it is powered by). One plain sentence about AI is right where it helps the person understand the experience; the function name is not.
|
|
10
|
+
|
|
9
11
|
## Text to records
|
|
10
12
|
|
|
11
13
|
An ordinary tracker with several facts per entry uses sentence creation even when the person never asks for AI, pasting, or a chat interface. A packing board with item, quantity, and who is bringing it, or a potluck board with dish, servings, and contributor, starts with this composer. Keep quantity steppers, claim buttons, and toggles on existing records as direct controls; they complement sentence creation. A name-only list, a simple message, and a timer stay direct because their input already is the whole record or an explicit setting.
|
package/llms/callai.md
CHANGED
|
@@ -6,6 +6,8 @@ The browser runtime validates each answer against your schema. A schema mismatch
|
|
|
6
6
|
|
|
7
7
|
Use this capability when an app turns words into structured records or provides requested model-generated text. Direct controls handle known values, and a simple message saves directly when its text is the whole record.
|
|
8
8
|
|
|
9
|
+
Name what the app does in the app's own words. The function name `callAI` stays in implementation context — it never becomes a label, a badge, or a credit line in the UI (a roast board says what it makes, not what it is powered by). One plain sentence about AI is right where it helps the person understand the experience; the function name is not.
|
|
10
|
+
|
|
9
11
|
## Text to records
|
|
10
12
|
|
|
11
13
|
An ordinary tracker with several facts per entry uses sentence creation even when the person never asks for AI, pasting, or a chat interface. A packing board with item, quantity, and who is bringing it, or a potluck board with dish, servings, and contributor, starts with this composer. Keep quantity steppers, claim buttons, and toggles on existing records as direct controls; they complement sentence creation. A name-only list, a simple message, and a timer stay direct because their input already is the whole record or an explicit setting.
|
|
@@ -274,6 +274,8 @@ Copy that works, and copy that doesn't:
|
|
|
274
274
|
|
|
275
275
|
And never disable the app's real controls on `!viewer`. If favoriting works signed out, keep it working signed out — the platform refuses the *cloud* write on its own, calmly, while the local write still succeeds. Turning a working app into a signup wall is worse than never asking at all.
|
|
276
276
|
|
|
277
|
+
Keep the guest's composed text across sign-in. Draft input lives in component state (or a local draft the app already saves) — and signing in never clears it. The new identity arrives on its own through `viewer`; the words they typed stay where they left them, and they continue from there. A form that resets on identity change makes the guest write everything twice.
|
|
278
|
+
|
|
277
279
|
## Notes
|
|
278
280
|
|
|
279
281
|
- Never use Clerk user IDs. Only `userHandle` crosses into vibe code.
|
package/llms/use-viewer.md
CHANGED
|
@@ -265,6 +265,8 @@ Copy that works, and copy that doesn't:
|
|
|
265
265
|
|
|
266
266
|
And never disable the app's real controls on `!viewer`. If favoriting works signed out, keep it working signed out — the access function refuses the *cloud* write on its own, calmly, while the local write still succeeds. Turning a working app into a signup wall is worse than never asking at all.
|
|
267
267
|
|
|
268
|
+
Keep the guest's composed text across sign-in. Draft input lives in component state (or a local draft the app already saves) — and signing in never clears it. The new identity arrives on its own through `viewer`; the words they typed stay where they left them, and they continue from there. A form that resets on identity change makes the guest write everything twice.
|
|
269
|
+
|
|
268
270
|
## Notes
|
|
269
271
|
|
|
270
272
|
- Never use Clerk user IDs. Only `userHandle` crosses into vibe code.
|
package/llms/web-audio.js
CHANGED
|
@@ -12,9 +12,15 @@ export const webAudioConfig = {
|
|
|
12
12
|
"oscillator",
|
|
13
13
|
"metronome",
|
|
14
14
|
"drum machine",
|
|
15
|
+
"drum",
|
|
16
|
+
"drums",
|
|
15
17
|
"midi",
|
|
16
18
|
"melody",
|
|
17
19
|
"web audio",
|
|
20
|
+
"samples",
|
|
21
|
+
"sampled",
|
|
22
|
+
"sampled sounds",
|
|
23
|
+
"audio engine",
|
|
18
24
|
],
|
|
19
25
|
draftVariant: true,
|
|
20
26
|
};
|
package/llms/web-audio.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web-audio.js","sourceRoot":"","sources":["../../jsr/llms/web-audio.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,MAAM,cAAc,GAAc;IACvC,IAAI,EAAE,WAAW;IACjB,KAAK,EAAE,eAAe;IACtB,WAAW,EACT,2NAA2N;IAC7N,IAAI,EAAE;QACJ,OAAO;QACP,OAAO;QACP,QAAQ;QACR,OAAO;QACP,OAAO;QACP,aAAa;QACb,YAAY;QACZ,WAAW;QACX,cAAc;QACd,MAAM;QACN,QAAQ;QACR,WAAW;
|
|
1
|
+
{"version":3,"file":"web-audio.js","sourceRoot":"","sources":["../../jsr/llms/web-audio.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,MAAM,cAAc,GAAc;IACvC,IAAI,EAAE,WAAW;IACjB,KAAK,EAAE,eAAe;IACtB,WAAW,EACT,2NAA2N;IAC7N,IAAI,EAAE;QACJ,OAAO;QACP,OAAO;QACP,QAAQ;QACR,OAAO;QACP,OAAO;QACP,aAAa;QACb,YAAY;QACZ,WAAW;QACX,cAAc;QACd,MAAM;QACN,OAAO;QACP,MAAM;QACN,QAAQ;QACR,WAAW;QACX,SAAS;QACT,SAAS;QACT,gBAAgB;QAChB,cAAc;KACf;IAGD,YAAY,EAAE,IAAI;CACnB,CAAC"}
|
package/llms/web-audio.md
CHANGED
|
@@ -289,4 +289,68 @@ The shape that works (filenames illustrative — split by responsibility, matche
|
|
|
289
289
|
- Never autoplay audio on load; playback must follow a user gesture (the `unlockAudio()` gate in §1).
|
|
290
290
|
- Wire brief feedback to the app's one or two satisfying core actions — the "it worked" beats (a point scored, a post landing, a match made) — and route it through the resume-on-user-gesture unlock so it sounds on mobile. Quiet/utility apps skip audio entirely.
|
|
291
291
|
|
|
292
|
+
## 8) Heavyweight audio engines arrive by runtime import
|
|
293
|
+
|
|
294
|
+
When the app needs a serious sound engine — sampled drums, a full synth framework — rather than hand-built oscillators, import the engine with its full pinned esm.sh URL in a memoized module-level loader, and call that loader only from a user gesture. Laziness is doing two jobs here: a body-position `await import("engine-name")` with a bare specifier ships unrewritten — the import rewrite covers only the top-of-file import region — and dies at runtime with `Failed to resolve module specifier`, so the specifier is the complete versioned URL; and an engine plus its sample bank stays off first paint entirely.
|
|
295
|
+
|
|
296
|
+
The gesture handler unlocks synchronously FIRST, before any await: call `unlockAudio()` (§1) at the top of the handler and keep its return — the context the gesture unlocked. Then await the loader, and bind the engine to that SAME context before creating any voice: `Tone.setContext(ctx)` drives the engine on the already-running context, and `Tone.getContext().rawContext === ctx` verifies the identity. Without the bind, `Tone.start()` resumes a context the engine created lazily after the await — a different context from the one the gesture unlocked. Verified on desktop Chromium; on mobile the engine's first sound still follows the loader's await, so claim only what desktop evidence shows (iOS counts a synchronous start inside the gesture, and a lazily imported engine starts after an await).
|
|
297
|
+
|
|
298
|
+
A stop control silences everything — pending playback, retained voices, the transport and its future callbacks. Plays and stops share one generation counter: Stop increments it, and a playback still awaiting the loader checks it after every await and stays silent when it moved — otherwise Play → Stop → load-resolves creates and plays a voice after Stop cleared the set. Then release the retained voices and stop/cancel the transport: stopping the transport alone leaves sounding voices droning on.
|
|
299
|
+
|
|
300
|
+
Check the engine's license first: reach for a permissively licensed engine (the worked sample below is Tone.js 15.1.22, MIT), credit it visibly in the UI, and load it at runtime from the CDN — its code lives at its URL, not in the app.
|
|
301
|
+
|
|
302
|
+
```js
|
|
303
|
+
// Memoized module-level loader — one network fetch no matter how many pads play it.
|
|
304
|
+
let tonePromise;
|
|
305
|
+
function loadTone() {
|
|
306
|
+
tonePromise ??= import("https://esm.sh/tone@15.1.22");
|
|
307
|
+
return tonePromise;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Every voice the app created, so Stop can silence retained voices too.
|
|
311
|
+
const liveVoices = new Set();
|
|
312
|
+
// Plays AND stops count here: a playback still awaiting the loader belongs to
|
|
313
|
+
// an older generation once Stop runs, and must never sound.
|
|
314
|
+
let playEpoch = 0;
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
```js
|
|
318
|
+
async function handlePlay() {
|
|
319
|
+
const ctx = unlockAudio(); // §1 gate — synchronous, in the gesture
|
|
320
|
+
const myTurn = playEpoch;
|
|
321
|
+
const Tone = await loadTone();
|
|
322
|
+
if (myTurn !== playEpoch) return; // Stop ran while loading — stay silent
|
|
323
|
+
// The engine drives the context the gesture unlocked: bind once per context,
|
|
324
|
+
// then verify the identity before creating any voice.
|
|
325
|
+
if (Tone.getContext().rawContext !== ctx) Tone.setContext(ctx);
|
|
326
|
+
const synth = new Tone.MembraneSynth().toDestination();
|
|
327
|
+
if (myTurn !== playEpoch) {
|
|
328
|
+
synth.dispose?.(); // Stop landed between the bind and here — no sound
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
liveVoices.add(synth);
|
|
332
|
+
synth.triggerAttackRelease("C2", "8n");
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function handleStop() {
|
|
336
|
+
playEpoch++; // cancel any playback still waiting on the loader
|
|
337
|
+
// Silence retained voices first, then the transport and its future callbacks.
|
|
338
|
+
for (const voice of liveVoices) {
|
|
339
|
+
try {
|
|
340
|
+
voice.triggerRelease?.();
|
|
341
|
+
voice.dispose?.();
|
|
342
|
+
} catch {
|
|
343
|
+
/* a voice that already ended needs nothing */
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
liveVoices.clear();
|
|
347
|
+
loadTone().then((Tone) => {
|
|
348
|
+
Tone.getTransport().stop();
|
|
349
|
+
Tone.getTransport().cancel();
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
The Play button names what it plays ("Play the drums"), the Stop button is beside it from the first sound, and a small line credits the engine ("Sounds by Tone.js (MIT)") — the audience hears drums, not infrastructure.
|
|
355
|
+
|
|
292
356
|
— End —
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibes.diy/prompts",
|
|
3
|
-
"version": "14.3.
|
|
3
|
+
"version": "14.3.40",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"exports": {
|
|
@@ -37,9 +37,9 @@
|
|
|
37
37
|
"license": "Apache-2.0",
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@adviser/cement": "~0.5.34",
|
|
40
|
-
"@vibes.diy/call-ai-v2": "14.3.
|
|
41
|
-
"@vibes.diy/identity": "14.3.
|
|
42
|
-
"@vibes.diy/use-vibes-types": "14.3.
|
|
40
|
+
"@vibes.diy/call-ai-v2": "14.3.40",
|
|
41
|
+
"@vibes.diy/identity": "14.3.40",
|
|
42
|
+
"@vibes.diy/use-vibes-types": "14.3.40",
|
|
43
43
|
"arktype": "~2.2.3",
|
|
44
44
|
"json-schema-faker": "~0.6.3"
|
|
45
45
|
},
|
|
@@ -174,6 +174,7 @@ A second visitor should arrive at a party, not a parking lot: within five second
|
|
|
174
174
|
- **Generated images as content, not decoration.** Where pictures naturally belong (avatars, covers, cards, monsters, prizes), render them with `<ImgGen>` and a vivid specific prompt — never a placeholder box or a generic prompt like "an image".
|
|
175
175
|
- **Motion on state changes.** Small fast transitions (150–300ms) when things appear, complete, or score — enough that actions feel acknowledged, not a light show.
|
|
176
176
|
- **Empty states invite, they don't announce.** "Draw the first monster" with the button right there, not "No data yet".
|
|
177
|
+
- **Carry the occasion into every word.** Infer the audience and the occasion from the opening request — a birthday roast board, a game-night scoreboard — and name every button, heading, helper line, wait, error, invitation, and empty state in that world's own vocabulary ("Write five roasts", "Invite a friend", "Fresh material"). Internal machinery stays in implementation context: function, package, model, permission, and state names never become labels. One plain sentence about AI is right where it helps the person understand the experience; the function name is not.
|
|
177
178
|
- **A share affordance on the FIRST screen — for apps where sharing fits.** When the app is social, collaborative, or built to be shown to others, surface its invite/share/publish action where a first-time user can see it without digging, gate it like any write surface (`can.create({...draft}).ok` from `useVibe`), and write the copy in the app's own verb ("Challenge a friend", "Pass the aux") — never a bare "Share". Private-by-nature and solo apps get NO share affordance (the guidance above about private apps omitting sharing controls wins).
|
|
178
179
|
|
|
179
180
|
**Restraint — alive never means obnoxious.** No confetti on load; no popups nagging people to share. One or two well-placed delightful touches beat a wall of noise.
|
package/system-prompt-initial.md
CHANGED
|
@@ -176,6 +176,7 @@ A second visitor should arrive at a party, not a parking lot: within five second
|
|
|
176
176
|
- **Generated images as content, not decoration.** Where pictures naturally belong (avatars, covers, cards, monsters, prizes), render them with `<ImgGen>` and a vivid specific prompt — never a placeholder box or a generic prompt like "an image".
|
|
177
177
|
- **Motion on state changes.** Small fast transitions (150–300ms) when things appear, complete, or score — enough that actions feel acknowledged, not a light show.
|
|
178
178
|
- **Empty states invite, they don't announce.** "Draw the first monster" with the button right there, not "No data yet".
|
|
179
|
+
- **Carry the occasion into every word.** Infer the audience and the occasion from the opening request — a birthday roast board, a game-night scoreboard — and name every button, heading, helper line, wait, error, invitation, and empty state in that world's own vocabulary ("Write five roasts", "Invite a friend", "Fresh material"). Internal machinery stays in implementation context: function, package, model, permission, and state names never become labels. One plain sentence about AI is right where it helps the person understand the experience; the function name is not.
|
|
179
180
|
- **A share affordance on the FIRST screen — for apps where sharing fits.** When the app is social, collaborative, or built to be shown to others, surface its invite/share/publish action where a first-time user can see it without digging, gate it like any write surface (`can.create({...draft}).ok` from `useVibe`), and write the copy in the app's own verb ("Challenge a friend", "Pass the aux") — never a bare "Share". Private-by-nature and solo apps get NO share affordance (the guidance above about private apps omitting sharing controls wins).
|
|
180
181
|
|
|
181
182
|
**Restraint — alive never means obnoxious.** No confetti on load; no popups nagging people to share. One or two well-placed delightful touches beat a wall of noise.
|
package/system-prompt.md
CHANGED
|
@@ -775,6 +775,7 @@ A second visitor should arrive at a party, not a parking lot: within five second
|
|
|
775
775
|
- **Generated images as content, not decoration.** Where pictures naturally belong (avatars, covers, cards, monsters, prizes), render them with `<ImgGen>` and a vivid specific prompt — never a placeholder box or a generic prompt like "an image".
|
|
776
776
|
- **Motion on state changes.** Small fast transitions (150–300ms) when things appear, complete, or score — enough that actions feel acknowledged, not a light show.
|
|
777
777
|
- **Empty states invite, they don't announce.** "Draw the first monster" with the button right there, not "No data yet".
|
|
778
|
+
- **Carry the occasion into every word.** Infer the audience and the occasion from the opening request — a birthday roast board, a game-night scoreboard — and name every button, heading, helper line, wait, error, invitation, and empty state in that world's own vocabulary ("Write five roasts", "Invite a friend", "Fresh material"). Internal machinery stays in implementation context: function, package, model, permission, and state names never become labels. One plain sentence about AI is right where it helps the person understand the experience; the function name is not.
|
|
778
779
|
- **A share affordance on the FIRST screen — for apps where sharing fits.** When the app is social, collaborative, or built to be shown to others, surface its invite/share/publish action where a first-time user can see it without digging, gate it like any write surface (`can.create({...draft}).ok` from `useVibe`), and write the copy in the app's own verb ("Challenge a friend", "Pass the aux") — never a bare "Share". Private-by-nature and solo apps get NO share affordance (the guidance above about private apps omitting sharing controls wins).
|
|
779
780
|
|
|
780
781
|
**Restraint — alive never means obnoxious.** No confetti on load; no popups nagging people to share. One or two well-placed delightful touches beat a wall of noise.
|