@vibes.diy/prompts 14.3.20 → 14.3.22

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.
@@ -47,7 +47,8 @@ Wire these visible actions in the first working app, using its own record names.
47
47
 
48
48
  With the callai skill available, submitting words extracts and normalizes every task they name, then immediately saves one document per task. The saved task is the confirmation and remains editable, with Undo beside the intake after each save. The parent keeps the ids from the last creation or the previous version from an edit, so intake and saved-detail editors share one Undo action. `useLiveQuery` keeps saved tasks visible; `useDocument` supplies the detail editor only after a record exists. For a plain message whose text is already the whole record, save that text directly.
49
49
 
50
- ```jsx file=App.jsx
50
+ ### `App.jsx`
51
+ ```jsx
51
52
  import React, { useState } from "react";
52
53
  import { callAI } from "call-ai";
53
54
  import { useFireproof } from "use-fireproof";
@@ -350,8 +351,7 @@ Don't stamp `Date.now()` or other changing values into seed documents — that m
350
351
 
351
352
  To update an existing document from a click handler or callback, use `database.put()` directly. Never call `useDocument` inside an event handler — that violates React's Rules of Hooks. Adding a toggle to list items:
352
353
 
353
- App.jsx
354
-
354
+ ### `App.jsx`
355
355
  ```jsx
356
356
  <<<<<<< SEARCH
357
357
  const { useDocument, useLiveQuery } = useFireproof("myLedger");
@@ -360,8 +360,7 @@ App.jsx
360
360
  >>>>>>> REPLACE
361
361
  ```
362
362
 
363
- App.jsx
364
-
363
+ ### `App.jsx`
365
364
  ```jsx
366
365
  <<<<<<< SEARCH
367
366
  <li key={doc._id}>{doc.text}</li>
@@ -381,8 +380,7 @@ Never call hooks inside handlers — `const { doc, save } = useDocument({ _id: i
381
380
 
382
381
  A continuous control (slider, drag, color picker, live-typed text bound to one doc) fires many events in quick succession. **Never call `database.put()` or `save()` on every `onChange`** — that floods the server with rapid concurrent writes to the same `_id`, which collide on the per-document sequence and get rejected (`Failed to put document …`). Instead, `merge()` locally on each event to keep the UI live, and `save()` once when the interaction commits (`onPointerUp`, `onBlur`, `onChange` for a range input's final value, or a debounced trailing call). Editing a single doc with a slider:
383
382
 
384
- App.jsx
385
-
383
+ ### `App.jsx`
386
384
  ```jsx
387
385
  <<<<<<< SEARCH
388
386
  const { useDocument, useLiveQuery, database } = useFireproof("myLedger");
@@ -394,8 +392,7 @@ App.jsx
394
392
  >>>>>>> REPLACE
395
393
  ```
396
394
 
397
- App.jsx
398
-
395
+ ### `App.jsx`
399
396
  ```jsx
400
397
  <<<<<<< SEARCH
401
398
  <h3>Recent Documents</h3>
@@ -423,8 +420,7 @@ Data is queried by sorted indexes defined by the application. Sort by strings, n
423
420
 
424
421
  Passing a string to `useLiveQuery` will index by that field. Use the key argument to filter by a specific value, or range for bounded queries. Switching from temporal to filtered query:
425
422
 
426
- App.jsx
427
-
423
+ ### `App.jsx`
428
424
  ```jsx
429
425
  <<<<<<< SEARCH
430
426
  const { docs } = useLiveQuery("_id", { descending: true, limit: 100 });
@@ -436,8 +432,7 @@ App.jsx
436
432
 
437
433
  Or query a numeric range:
438
434
 
439
- App.jsx
440
-
435
+ ### `App.jsx`
441
436
  ```jsx
442
437
  <<<<<<< SEARCH
443
438
  const { docs } = useLiveQuery("agentName", { key: "agent-1" });
@@ -451,8 +446,7 @@ App.jsx
451
446
 
452
447
  Documents can be updated by multiple clients, and synced later. To create an event counter, don't increment a number on a single doc, instead write a small document per counted event, and query them with an index:
453
448
 
454
- App.jsx
455
-
449
+ ### `App.jsx`
456
450
  ```jsx
457
451
  <<<<<<< SEARCH
458
452
  const { docs } = useLiveQuery("agentRating", { range: [3, 5] });
@@ -477,8 +471,7 @@ This pattern ensures the count is accurate even during sync — each event is it
477
471
 
478
472
  Use a custom index function to normalize and transform document data, for instance if you have both new and old document versions in your app:
479
473
 
480
- App.jsx
481
-
474
+ ### `App.jsx`
482
475
  ```jsx
483
476
  <<<<<<< SEARCH
484
477
  // The counter's scope: the board being viewed and who is counting (useVibe from "use-vibes", as above).
@@ -507,8 +500,7 @@ App.jsx
507
500
 
508
501
  When you want to group rows easily, you can use an array index key. This is great for grouping records by year/month/day or other paths. The prefix query is a shorthand for a key range:
509
502
 
510
- App.jsx
511
-
503
+ ### `App.jsx`
512
504
  ```jsx
513
505
  <<<<<<< SEARCH
514
506
  const { docs } = useLiveQuery(
@@ -534,8 +526,7 @@ App.jsx
534
526
 
535
527
  Sortable lists are a common pattern. Use evenly spaced positions and insert between items using midpoint calculation:
536
528
 
537
- App.jsx
538
-
529
+ ### `App.jsx`
539
530
  ```jsx
540
531
  <<<<<<< SEARCH
541
532
  const { docs } = useLiveQuery(
@@ -565,6 +556,23 @@ App.jsx
565
556
  >>>>>>> REPLACE
566
557
  ```
567
558
 
559
+ #### Emit a Row Value
560
+
561
+ An index function can call `emit(key, value)` with a second argument to store a value beside the key. `rows[].value` is the value you emitted when you pass one, and the whole document when you call `emit(key)` alone. Emit a small summary when a long list renders only a field or two, and read `docs` when you want the documents themselves — `docs` holds the indexed documents whatever the rows carry.
562
+
563
+ ### `App.jsx`
564
+ ```jsx
565
+ <<<<<<< SEARCH
566
+ const { docs } = useLiveQuery((doc) => [doc.listId, doc.position], { prefix: ["xyz"] });
567
+ =======
568
+ // Rows carry just what this list renders; docs still holds the whole documents
569
+ const { rows } = useLiveQuery((doc, emit) => emit([doc.listId, doc.position], { label: doc.label }), {
570
+ prefix: ["xyz"],
571
+ });
572
+ const labels = rows.map((row) => row.value.label);
573
+ >>>>>>> REPLACE
574
+ ```
575
+
568
576
  ## Offline writes are on by default (`offlineQueue`)
569
577
 
570
578
  For signed-in users, writes are **local-first by default**: a `put`/`del` that fails on the network is durably queued on the device (it resolves, stays visible, and syncs to the cloud when you're back online) instead of rolling back. You don't opt in — every signed-in vibe gets this. A refusal or validation rejection is **not** queued: it converges **server-wins** (the local optimistic revision is overwritten by the server's version); the platform surfaces the reason only when it has one, and a reason-less rejection converges silently — so keep the UI reactive to the store rather than depending on a toast. Only transport failures queue and retry.
@@ -595,8 +603,7 @@ Data lives in a local replica (IndexedDB) that the app reads and writes instantl
595
603
 
596
604
  You can use the core API outside a React component — in a plain script or a helper module. Instead of hooks, import the core API directly:
597
605
 
598
- App.jsx
599
-
606
+ ### `App.jsx`
600
607
  ```jsx
601
608
  <<<<<<< SEARCH
602
609
  import React from "react";
@@ -634,8 +641,7 @@ A save that carries `_files` uploads the bytes before the doc lands, so unlike a
634
641
 
635
642
  Building an image uploader with `_files`:
636
643
 
637
- App.jsx
638
-
644
+ ### `App.jsx`
639
645
  ```jsx
640
646
  import React from "react";
641
647
  import { useFireproof } from "use-fireproof";
@@ -716,8 +722,7 @@ For multi-file uploads (e.g. `<input multiple>`), build the `_files` map keyed b
716
722
 
717
723
  Adding multi-file support:
718
724
 
719
- App.jsx
720
-
725
+ ### `App.jsx`
721
726
  ```jsx
722
727
  <<<<<<< SEARCH
723
728
  const onPickFile = (e) => {
@@ -745,8 +750,7 @@ App.jsx
745
750
 
746
751
  You can use React's `useState` to manage validation states and error messages. Validate inputs at the UI level before allowing submission. Adding validation to the uploader:
747
752
 
748
- App.jsx
749
-
753
+ ### `App.jsx`
750
754
  ```jsx
751
755
  <<<<<<< SEARCH
752
756
  const onSubmit = async (e) => {
package/llms/fireproof.js CHANGED
@@ -3,6 +3,7 @@ export const fireproofConfig = {
3
3
  label: "useFireproof",
4
4
  description: "cloud-backed document database with live sync",
5
5
  initialVariant: true,
6
+ draftVariant: true,
6
7
  importModule: "use-fireproof",
7
8
  importName: "useFireproof",
8
9
  };
@@ -1 +1 @@
1
- {"version":3,"file":"fireproof.js","sourceRoot":"","sources":["../../jsr/llms/fireproof.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,eAAe,GAAc;IACxC,IAAI,EAAE,WAAW;IACjB,KAAK,EAAE,cAAc;IACrB,WAAW,EAAE,+CAA+C;IAC5D,cAAc,EAAE,IAAI;IACpB,YAAY,EAAE,eAAe;IAC7B,UAAU,EAAE,cAAc;CAC3B,CAAC"}
1
+ {"version":3,"file":"fireproof.js","sourceRoot":"","sources":["../../jsr/llms/fireproof.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,eAAe,GAAc;IACxC,IAAI,EAAE,WAAW;IACjB,KAAK,EAAE,cAAc;IACrB,WAAW,EAAE,+CAA+C;IAC5D,cAAc,EAAE,IAAI;IACpB,YAAY,EAAE,IAAI;IAClB,YAAY,EAAE,eAAe;IAC7B,UAAU,EAAE,cAAc;CAC3B,CAAC"}
package/llms/fireproof.md CHANGED
@@ -47,7 +47,8 @@ Wire these visible actions in the first working app, using its own record names.
47
47
 
48
48
  With the callai skill available, submitting words extracts and normalizes every task they name, then immediately saves one document per task. The saved task is the confirmation and remains editable, with Undo beside the intake after each save. The parent keeps the ids from the last creation or the previous version from an edit, so intake and saved-detail editors share one Undo action. `useLiveQuery` keeps saved tasks visible; `useDocument` supplies the detail editor only after a record exists. For a plain message whose text is already the whole record, save that text directly.
49
49
 
50
- ```jsx file=App.jsx
50
+ ### `App.jsx`
51
+ ```jsx
51
52
  import React, { useState } from "react";
52
53
  import { callAI } from "call-ai";
53
54
  import { useFireproof } from "use-fireproof";
@@ -278,7 +279,8 @@ When an app ships with default content — starter slides, template rows, exampl
278
279
 
279
280
  The trap is the live-query empty state. `useLiveQuery` renders empty before data arrives, so `docs.length === 0` is briefly true on **every** fresh load — and a `useRef`/`useState` "already seeded" flag only guards the current session, never the next reload, a second device, or another collaborator. With **auto-generated** `_id`s, each of those re-seeds the full set and duplicate copies pile up without bound. Deterministic `_id`s collapse every re-seed back onto the same N documents.
280
281
 
281
- ```jsx file=App.jsx
282
+ ### `App.jsx`
283
+ ```jsx
282
284
  import React from "react";
283
285
  import { useFireproof } from "use-fireproof";
284
286
  import { useVibe } from "use-vibes";
@@ -319,8 +321,7 @@ Don't stamp `Date.now()` or other changing values into seed documents — that m
319
321
 
320
322
  To update an existing document from a click handler or callback, use `database.put()` directly. Never call `useDocument` inside an event handler — that violates React's Rules of Hooks. Adding a toggle to list items:
321
323
 
322
- App.jsx
323
-
324
+ ### `App.jsx`
324
325
  ```jsx
325
326
  <<<<<<< SEARCH
326
327
  const { useDocument, useLiveQuery } = useFireproof("myLedger");
@@ -329,8 +330,7 @@ App.jsx
329
330
  >>>>>>> REPLACE
330
331
  ```
331
332
 
332
- App.jsx
333
-
333
+ ### `App.jsx`
334
334
  ```jsx
335
335
  <<<<<<< SEARCH
336
336
  <li key={doc._id}>{doc.text}</li>
@@ -350,8 +350,7 @@ Never call hooks inside handlers — `const { doc, save } = useDocument({ _id: i
350
350
 
351
351
  A continuous control (slider, drag, color picker, live-typed text bound to one doc) fires many events in quick succession. **Never call `database.put()` or `save()` on every `onChange`** — that floods the server with rapid concurrent writes to the same `_id`, which collide on the per-document sequence and get rejected (`Failed to put document …`). Instead, `merge()` locally on each event to keep the UI live, and `save()` once when the interaction commits (`onPointerUp`, `onBlur`, `onChange` for a range input's final value, or a debounced trailing call). Editing a single doc with a slider:
352
352
 
353
- App.jsx
354
-
353
+ ### `App.jsx`
355
354
  ```jsx
356
355
  <<<<<<< SEARCH
357
356
  const { useDocument, useLiveQuery, database } = useFireproof("myLedger");
@@ -363,8 +362,7 @@ App.jsx
363
362
  >>>>>>> REPLACE
364
363
  ```
365
364
 
366
- App.jsx
367
-
365
+ ### `App.jsx`
368
366
  ```jsx
369
367
  <<<<<<< SEARCH
370
368
  <h3>Recent Documents</h3>
@@ -392,8 +390,7 @@ Data is queried by sorted indexes defined by the application. Sort by strings, n
392
390
 
393
391
  Passing a string to `useLiveQuery` will index by that field. Use the key argument to filter by a specific value, or range for bounded queries. Switching from temporal to filtered query:
394
392
 
395
- App.jsx
396
-
393
+ ### `App.jsx`
397
394
  ```jsx
398
395
  <<<<<<< SEARCH
399
396
  const { docs } = useLiveQuery("_id", { descending: true, limit: 100 });
@@ -405,8 +402,7 @@ App.jsx
405
402
 
406
403
  Or query a numeric range:
407
404
 
408
- App.jsx
409
-
405
+ ### `App.jsx`
410
406
  ```jsx
411
407
  <<<<<<< SEARCH
412
408
  const { docs } = useLiveQuery("agentName", { key: "agent-1" });
@@ -420,8 +416,7 @@ App.jsx
420
416
 
421
417
  Documents can be updated by multiple clients, and synced later. To create an event counter, don't increment a number on a single doc, instead write a small document per counted event, and query them with an index:
422
418
 
423
- App.jsx
424
-
419
+ ### `App.jsx`
425
420
  ```jsx
426
421
  <<<<<<< SEARCH
427
422
  const { docs } = useLiveQuery("agentRating", { range: [3, 5] });
@@ -441,8 +436,7 @@ This pattern ensures the count is accurate even during sync — each event is it
441
436
 
442
437
  Use a custom index function to normalize and transform document data, for instance if you have both new and old document versions in your app:
443
438
 
444
- App.jsx
445
-
439
+ ### `App.jsx`
446
440
  ```jsx
447
441
  <<<<<<< SEARCH
448
442
  const { docs } = useLiveQuery("counter", { key: "my-event-name" });
@@ -466,8 +460,7 @@ App.jsx
466
460
 
467
461
  When you want to group rows easily, you can use an array index key. This is great for grouping records by year/month/day or other paths. The prefix query is a shorthand for a key range:
468
462
 
469
- App.jsx
470
-
463
+ ### `App.jsx`
471
464
  ```jsx
472
465
  <<<<<<< SEARCH
473
466
  const { docs } = useLiveQuery(
@@ -493,8 +486,7 @@ App.jsx
493
486
 
494
487
  Sortable lists are a common pattern. Use evenly spaced positions and insert between items using midpoint calculation:
495
488
 
496
- App.jsx
497
-
489
+ ### `App.jsx`
498
490
  ```jsx
499
491
  <<<<<<< SEARCH
500
492
  const { docs } = useLiveQuery(
@@ -523,6 +515,23 @@ App.jsx
523
515
  >>>>>>> REPLACE
524
516
  ```
525
517
 
518
+ #### Emit a Row Value
519
+
520
+ An index function can call `emit(key, value)` with a second argument to store a value beside the key. `rows[].value` is the value you emitted when you pass one, and the whole document when you call `emit(key)` alone. Emit a small summary when a long list renders only a field or two, and read `docs` when you want the documents themselves — `docs` holds the indexed documents whatever the rows carry.
521
+
522
+ ### `App.jsx`
523
+ ```jsx
524
+ <<<<<<< SEARCH
525
+ const { docs } = useLiveQuery((doc) => [doc.list, doc.position], { prefix: ["xyz"] });
526
+ =======
527
+ // Rows carry just what this list renders; docs still holds the whole documents
528
+ const { rows } = useLiveQuery((doc, emit) => emit([doc.list, doc.position], { label: doc.label }), {
529
+ prefix: ["xyz"],
530
+ });
531
+ const labels = rows.map((row) => row.value.label);
532
+ >>>>>>> REPLACE
533
+ ```
534
+
526
535
  ## Offline writes are on by default (`offlineQueue`)
527
536
 
528
537
  For signed-in users, writes are **local-first by default**: a `put`/`del` that fails on the network is durably queued on the device (it resolves, stays visible, and syncs to the cloud when you're back online) instead of rolling back. You don't opt in — every signed-in vibe gets this. An access-denied/validation rejection is **not** queued: it converges **server-wins** (the local optimistic revision is overwritten by the server's version); the platform surfaces the reason whenever there is one, and a reason-less rejection converges silently — so keep the UI reactive to the store rather than depending on a toast. Only transport failures queue and retry.
@@ -557,8 +566,7 @@ Data lives in a local replica (IndexedDB) that the app reads and writes instantl
557
566
 
558
567
  You can use the core API in HTML or on the backend. Instead of hooks, import the core API directly:
559
568
 
560
- App.jsx
561
-
569
+ ### `App.jsx`
562
570
  ```jsx
563
571
  <<<<<<< SEARCH
564
572
  import React from "react";
@@ -608,9 +616,8 @@ A save that carries `_files` uploads the bytes before the doc lands, so unlike a
608
616
 
609
617
  Building an image uploader with `_files`:
610
618
 
611
- App.jsx
612
-
613
- ```jsx file=App.jsx
619
+ ### `App.jsx`
620
+ ```jsx
614
621
  import React from "react";
615
622
  import { useFireproof } from "use-fireproof";
616
623
  import { Button, Card, CardContent, Input, Label } from "@vibes.diy/look";
@@ -690,8 +697,7 @@ For multi-file uploads (e.g. `<input multiple>`), build the `_files` map keyed b
690
697
 
691
698
  Adding multi-file support:
692
699
 
693
- App.jsx
694
-
700
+ ### `App.jsx`
695
701
  ```jsx
696
702
  <<<<<<< SEARCH
697
703
  const onPickFile = (e) => {
@@ -719,8 +725,7 @@ App.jsx
719
725
 
720
726
  You can use React's `useState` to manage validation states and error messages. Validate inputs at the UI level before allowing submission. Adding validation to the uploader:
721
727
 
722
- App.jsx
723
-
728
+ ### `App.jsx`
724
729
  ```jsx
725
730
  <<<<<<< SEARCH
726
731
  const onSubmit = async (e) => {
@@ -747,7 +752,8 @@ App.jsx
747
752
 
748
753
  Code listing for todo tracker App.jsx. Note the code ordering: hooks, then handlers, then classNames right before JSX.
749
754
 
750
- ```js file=App.jsx
755
+ ### `App.jsx`
756
+ ```js
751
757
  import React from "react";
752
758
  import { useFireproof } from "use-fireproof";
753
759
  import { Button, Card, CardContent, Input, Label } from "@vibes.diy/look";
@@ -8,8 +8,7 @@ Generate and edit images from a text prompt. Each generated image lands as a fil
8
8
 
9
9
  Start with a minimal image generation component:
10
10
 
11
- App.jsx
12
-
11
+ ### `App.jsx`
13
12
  ```jsx
14
13
  import React from "react";
15
14
  import { useFireproof } from "use-fireproof";
@@ -61,8 +60,7 @@ This is the same `_files`-shape contract documented in `fireproof.md`'s "Working
61
60
 
62
61
  Pass a `File` object via `images` to run img2img. Adding a file picker that feeds into ImgGen:
63
62
 
64
- App.jsx
65
-
63
+ ### `App.jsx`
66
64
  ```jsx
67
65
  <<<<<<< SEARCH
68
66
  export default function App() {
package/llms/image-gen.md CHANGED
@@ -8,9 +8,8 @@ Generate and edit images from a text prompt. Each generated image lands as a fil
8
8
 
9
9
  Start with a minimal image generation component:
10
10
 
11
- App.jsx
12
-
13
- ```jsx file=App.jsx
11
+ ### `App.jsx`
12
+ ```jsx
14
13
  import React from "react";
15
14
  import { useFireproof } from "use-fireproof";
16
15
  import { ImgGen } from "use-vibes";
@@ -61,8 +60,7 @@ This is the same `_files`-shape contract documented in `fireproof.md`'s "Working
61
60
 
62
61
  Pass a `File` object via `images` to run img2img. Adding a file picker that feeds into ImgGen:
63
62
 
64
- App.jsx
65
-
63
+ ### `App.jsx`
66
64
  ```jsx
67
65
  <<<<<<< SEARCH
68
66
  export default function App() {
package/llms/p5.md CHANGED
@@ -20,9 +20,8 @@ factory function that receives the host element, the instance is built once in
20
20
  one `useEffect([])`, the canvas is sized from the host, and the cleanup removes
21
21
  the instance so remounts do not leak a second draw loop.
22
22
 
23
- App.jsx
24
-
25
- ```jsx file=App.jsx
23
+ ### `App.jsx`
24
+ ```jsx
26
25
  import React from "react";
27
26
  import p5 from "https://esm.sh/p5@1.11.13";
28
27
  import { Button } from "@vibes.diy/look";
package/llms/spotify.md CHANGED
@@ -159,7 +159,8 @@ when the route names it.
159
159
 
160
160
  ## Complete backend.js
161
161
 
162
- ```js file=backend.js app=spotify-browser
162
+ ### `backend.js`
163
+ ```js app=spotify-browser
163
164
  const TOKEN_URL = "https://accounts.spotify.com/api/token";
164
165
  const API = "https://api.spotify.com/v1";
165
166
 
@@ -302,7 +303,8 @@ function artistRow(a) {
302
303
 
303
304
  ## App.jsx sketch
304
305
 
305
- ```jsx file=App.jsx app=spotify-browser
306
+ ### `App.jsx`
307
+ ```jsx app=spotify-browser
306
308
  import React, { useState } from "react";
307
309
 
308
310
  const SETUP_STEPS = [
package/llms/three-js.md CHANGED
@@ -15,9 +15,8 @@ one `useEffect([])`, the animation loop is a `requestAnimationFrame`, resize
15
15
  keeps the camera aspect correct, and the cleanup disposes geometry, material,
16
16
  and renderer so remounts don't leak GPU memory.
17
17
 
18
- App.jsx
19
-
20
- ```jsx file=App.jsx
18
+ ### `App.jsx`
19
+ ```jsx
21
20
  import React from "react";
22
21
  import * as THREE from "three";
23
22
 
package/llms/types.d.ts CHANGED
@@ -4,6 +4,7 @@ export interface LlmConfig {
4
4
  description: string;
5
5
  cues?: string[];
6
6
  initialVariant?: boolean;
7
+ draftVariant?: boolean;
7
8
  importModule?: string;
8
9
  importName?: string;
9
10
  importType?: "named" | "namespace" | "default";
package/llms/use-vibe.md CHANGED
@@ -26,7 +26,8 @@ Never stamp author fields from `me` while the verdict is pending — `me` is nul
26
26
 
27
27
  Gate every write affordance on `can.*`. Hold it while `pending`, and write your own copy when denied. Never branch write permission on `viewer`, `access.hasRole()`/`access.hasChannel()`, or document fields — those drift from what `access.js` actually does. Rendering **other** users (authors, rosters) is `useViewer()`'s `<ViewerTag userHandle={...} />`, not `useVibe`. The current viewer's own pill and the "signed in as" label are system chrome in the Vibes Switch (the logo) — don't build them into the app. Asking an anonymous visitor to sign in is a different thing and it IS yours: `useViewer().requestLogin()`, at the moment their work becomes worth keeping (see use-viewer docs). A resolved denial is one such moment — write the sign-in copy in your app's own words and wire it to that callable, never to a sentence about the logo. (The one exception is inline avatar self-edit: a guarded no-prop `{viewer && <ViewerTag />}` lets any signed-in member change their own photo in place — see use-viewer docs.)
28
28
 
29
- ```jsx file=App.jsx
29
+ ### `App.jsx`
30
+ ```jsx
30
31
  import { useVibe } from "use-vibes";
31
32
  import { Button, Input, Skeleton } from "@vibes.diy/look";
32
33
 
@@ -12,8 +12,7 @@ Use `useViewer()` to render **other** people's identity — comment authors, ros
12
12
 
13
13
  Start with a minimal component that reads the viewer identity. You don't render a current-user pill — that lives in the Vibes Switch (click the logo). Branch on `viewer` for any welcome/empty copy. Note there is **no sign-in button here**: the ask belongs at the moment the visitor has made something worth keeping, not on arrival — see **Asking a visitor to sign in** below.
14
14
 
15
- App.jsx
16
-
15
+ ### `App.jsx`
17
16
  ```jsx
18
17
  import React from "react";
19
18
  import { useFireproof } from "use-fireproof";
@@ -50,8 +49,7 @@ export default function App() {
50
49
 
51
50
  Gate the comment form on `useVibe("comments").can` — not on `viewer`. The current viewer never needs a pill here (that's in the Vibes Switch); just render the gated form, and on a denial a sentence in your app's own words — `reason` is a machine token that informs that sentence and is never printed:
52
51
 
53
- App.jsx
54
-
52
+ ### `App.jsx`
55
53
  ```jsx
56
54
  <<<<<<< SEARCH
57
55
  import { useViewer } from "use-vibes";
@@ -60,8 +58,7 @@ import { useViewer, useVibe } from "use-vibes";
60
58
  >>>>>>> REPLACE
61
59
  ```
62
60
 
63
- App.jsx
64
-
61
+ ### `App.jsx`
65
62
  ```jsx
66
63
  <<<<<<< SEARCH
67
64
  const { viewer, isViewerPending } = useViewer();
@@ -71,8 +68,7 @@ App.jsx
71
68
  >>>>>>> REPLACE
72
69
  ```
73
70
 
74
- App.jsx
75
-
71
+ ### `App.jsx`
76
72
  ```jsx
77
73
  <<<<<<< SEARCH
78
74
  {viewer ? (
@@ -105,8 +101,7 @@ When one user writes content others will see (comments, posts, messages), **stam
105
101
 
106
102
  Wire up a full comment thread with Fireproof and viewer attribution:
107
103
 
108
- App.jsx
109
-
104
+ ### `App.jsx`
110
105
  ```jsx
111
106
  <<<<<<< SEARCH
112
107
  import { useViewer, useVibe } from "use-vibes";
@@ -12,9 +12,8 @@ Use `useViewer()` to render **other** people's identity — comment authors, ros
12
12
 
13
13
  Start with a minimal component that reads the viewer identity. You don't render a current-user pill — that lives in the Vibes Switch (click the logo). Branch on `viewer` for any welcome/empty copy. Note there is **no sign-in button here**: the ask belongs at the moment the visitor has made something worth keeping, not on arrival — see **Asking a visitor to sign in** below.
14
14
 
15
- App.jsx
16
-
17
- ```jsx file=App.jsx
15
+ ### `App.jsx`
16
+ ```jsx
18
17
  import React from "react";
19
18
  import { useFireproof } from "use-fireproof";
20
19
  import { useViewer } from "use-vibes";
@@ -51,8 +50,7 @@ export default function App() {
51
50
 
52
51
  Gate the comment form on `useVibe("comments").can` — not on `viewer`. The current viewer never needs a pill here (that's in the Vibes Switch); just render the gated form, and on a denial a sentence in your app's own words — `reason` is a machine token that informs that sentence and is never printed:
53
52
 
54
- App.jsx
55
-
53
+ ### `App.jsx`
56
54
  ```jsx
57
55
  <<<<<<< SEARCH
58
56
  import { useViewer } from "use-vibes";
@@ -61,8 +59,7 @@ import { useViewer, useVibe } from "use-vibes";
61
59
  >>>>>>> REPLACE
62
60
  ```
63
61
 
64
- App.jsx
65
-
62
+ ### `App.jsx`
66
63
  ```jsx
67
64
  <<<<<<< SEARCH
68
65
  const { viewer, isViewerPending } = useViewer();
@@ -72,8 +69,7 @@ App.jsx
72
69
  >>>>>>> REPLACE
73
70
  ```
74
71
 
75
- App.jsx
76
-
72
+ ### `App.jsx`
77
73
  ```jsx
78
74
  <<<<<<< SEARCH
79
75
  {viewer ? (
@@ -106,8 +102,7 @@ When one user writes content others will see (comments, posts, messages), **stam
106
102
 
107
103
  Wire up a full comment thread with Fireproof and viewer attribution:
108
104
 
109
- App.jsx
110
-
105
+ ### `App.jsx`
111
106
  ```jsx
112
107
  <<<<<<< SEARCH
113
108
  import { useViewer, useVibe } from "use-vibes";
package/llms/webxr.md CHANGED
@@ -417,7 +417,8 @@ for (let i = 0; i < 200; i++) {
417
417
 
418
418
  A floating galaxy of 2000 particles with a custom-shader core sphere, navigable in VR. Session data stored in Fireproof.
419
419
 
420
- ```javascript file=App.jsx
420
+ ### `App.jsx`
421
+ ```javascript
421
422
  import * as BABYLON from "@babylonjs/core";
422
423
  import React, { useEffect, useRef } from "react";
423
424
  import { useFireproof } from "use-fireproof";
package/llms/youtube.md CHANGED
@@ -250,7 +250,8 @@ nothing they can act on, and the four cases above each have a different fix.
250
250
 
251
251
  ## Complete backend.js
252
252
 
253
- ```js file=backend.js app=youtube-dashboard
253
+ ### `backend.js`
254
+ ```js app=youtube-dashboard
254
255
  export const config = {
255
256
  scheduled: { interval: "1h" },
256
257
  // The visitor path below reads the snapshot docs on the fetch lane, which
@@ -507,7 +508,8 @@ export async function scheduled(event, ctx) {
507
508
 
508
509
  ## App.jsx sketch
509
510
 
510
- ```jsx file=App.jsx app=youtube-dashboard
511
+ ### `App.jsx`
512
+ ```jsx app=youtube-dashboard
511
513
  import React, { useState } from "react";
512
514
  import { useFireproof } from "use-fireproof";
513
515
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibes.diy/prompts",
3
- "version": "14.3.20",
3
+ "version": "14.3.22",
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.20",
41
- "@vibes.diy/identity": "14.3.20",
42
- "@vibes.diy/use-vibes-types": "14.3.20",
40
+ "@vibes.diy/call-ai-v2": "14.3.22",
41
+ "@vibes.diy/identity": "14.3.22",
42
+ "@vibes.diy/use-vibes-types": "14.3.22",
43
43
  "arktype": "~2.2.3",
44
44
  "json-schema-faker": "~0.6.3"
45
45
  },
package/prompts.d.ts CHANGED
@@ -20,7 +20,7 @@ export declare function generateImportStatements(llms: LlmCatalogEntry[]): strin
20
20
  export interface MakeBaseSystemPromptOptions {
21
21
  fetch?: typeof fetch;
22
22
  pkgBaseUrl?: string;
23
- variant?: "initial" | "initial-oneshot" | "continuation";
23
+ variant?: "initial" | "initial-oneshot" | "initial-draft" | "continuation";
24
24
  suppressDefaultStylePrompt?: boolean;
25
25
  look?: boolean;
26
26
  }