@vibes.diy/prompts 14.3.203 → 14.3.204
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 +4 -4
- package/llms/backend.md +1 -1
- package/llms/blockly.md +2 -2
- package/llms/bluesky.md +1 -1
- package/llms/board-games.md +3 -3
- package/llms/calendar.md +1 -1
- package/llms/callai.md +4 -4
- package/llms/connections.md +1 -1
- package/llms/create-vibe.md +3 -3
- package/llms/fireproof.md +7 -7
- package/llms/github.md +2 -2
- package/llms/image-gen.md +2 -2
- package/llms/index.d.ts +28 -28
- package/llms/index.js +84 -84
- package/llms/index.js.map +1 -1
- package/llms/maps.md +1 -1
- package/llms/match-style.md +1 -1
- package/llms/motion.md +7 -7
- package/llms/p5.md +1 -1
- package/llms/physics.md +2 -2
- package/llms/reskin.md +21 -21
- package/llms/richtext.md +5 -5
- package/llms/spotify.md +1 -1
- package/llms/stripe-subscriptions.md +1 -1
- package/llms/stripe.md +3 -3
- package/llms/strudel.md +3 -3
- package/llms/three-js.md +4 -4
- package/llms/touch-games.js +1 -1
- package/llms/touch-games.js.map +1 -1
- package/llms/touch-games.md +3 -3
- package/llms/ui-blog.d.ts +2 -0
- package/llms/ui-blog.js +7 -0
- package/llms/ui-blog.js.map +1 -0
- package/llms/{pouf-blog.md → ui-blog.md} +10 -10
- package/llms/ui-booking.d.ts +2 -0
- package/llms/ui-booking.js +7 -0
- package/llms/ui-booking.js.map +1 -0
- package/llms/{pouf-booking.md → ui-booking.md} +8 -8
- package/llms/ui-calendar.d.ts +2 -0
- package/llms/ui-calendar.js +7 -0
- package/llms/ui-calendar.js.map +1 -0
- package/llms/{pouf-calendar.md → ui-calendar.md} +5 -5
- package/llms/ui-chat.d.ts +2 -0
- package/llms/ui-chat.js +7 -0
- package/llms/ui-chat.js.map +1 -0
- package/llms/{pouf-chat.md → ui-chat.md} +4 -4
- package/llms/ui-contact.d.ts +2 -0
- package/llms/ui-contact.js +7 -0
- package/llms/ui-contact.js.map +1 -0
- package/llms/{pouf-contact.md → ui-contact.md} +7 -7
- package/llms/ui-course.d.ts +2 -0
- package/llms/ui-course.js +7 -0
- package/llms/ui-course.js.map +1 -0
- package/llms/{pouf-course.md → ui-course.md} +9 -9
- package/llms/ui-crm.d.ts +2 -0
- package/llms/ui-crm.js +7 -0
- package/llms/ui-crm.js.map +1 -0
- package/llms/{pouf-crm.md → ui-crm.md} +5 -5
- package/llms/ui-dashboard.d.ts +2 -0
- package/llms/ui-dashboard.js +7 -0
- package/llms/ui-dashboard.js.map +1 -0
- package/llms/{pouf-dashboard.md → ui-dashboard.md} +7 -7
- package/llms/ui-editorial.d.ts +2 -0
- package/llms/ui-editorial.js +7 -0
- package/llms/ui-editorial.js.map +1 -0
- package/llms/{pouf-editorial.md → ui-editorial.md} +5 -5
- package/llms/ui-event.d.ts +2 -0
- package/llms/ui-event.js +7 -0
- package/llms/ui-event.js.map +1 -0
- package/llms/{pouf-event.md → ui-event.md} +5 -5
- package/llms/ui-feed.d.ts +2 -0
- package/llms/ui-feed.js +7 -0
- package/llms/ui-feed.js.map +1 -0
- package/llms/{pouf-feed.md → ui-feed.md} +5 -5
- package/llms/ui-game.d.ts +2 -0
- package/llms/ui-game.js +7 -0
- package/llms/ui-game.js.map +1 -0
- package/llms/{pouf-game.md → ui-game.md} +6 -6
- package/llms/ui-inbox.d.ts +2 -0
- package/llms/ui-inbox.js +7 -0
- package/llms/ui-inbox.js.map +1 -0
- package/llms/{pouf-inbox.md → ui-inbox.md} +5 -5
- package/llms/ui-inventory.d.ts +2 -0
- package/llms/ui-inventory.js +7 -0
- package/llms/ui-inventory.js.map +1 -0
- package/llms/{pouf-inventory.md → ui-inventory.md} +5 -5
- package/llms/ui-kanban.d.ts +2 -0
- package/llms/ui-kanban.js +7 -0
- package/llms/ui-kanban.js.map +1 -0
- package/llms/{pouf-kanban.md → ui-kanban.md} +5 -5
- package/llms/ui-landing.d.ts +2 -0
- package/llms/ui-landing.js +7 -0
- package/llms/ui-landing.js.map +1 -0
- package/llms/{pouf-landing.md → ui-landing.md} +6 -6
- package/llms/ui-login.d.ts +2 -0
- package/llms/ui-login.js +7 -0
- package/llms/ui-login.js.map +1 -0
- package/llms/{pouf-login.md → ui-login.md} +7 -7
- package/llms/ui-music.d.ts +2 -0
- package/llms/ui-music.js +7 -0
- package/llms/ui-music.js.map +1 -0
- package/llms/{pouf-music.md → ui-music.md} +6 -6
- package/llms/ui-onboarding.d.ts +2 -0
- package/llms/ui-onboarding.js +7 -0
- package/llms/ui-onboarding.js.map +1 -0
- package/llms/{pouf-onboarding.md → ui-onboarding.md} +5 -5
- package/llms/ui-pricing.d.ts +2 -0
- package/llms/ui-pricing.js +7 -0
- package/llms/ui-pricing.js.map +1 -0
- package/llms/{pouf-pricing.md → ui-pricing.md} +5 -5
- package/llms/ui-profile.d.ts +2 -0
- package/llms/ui-profile.js +7 -0
- package/llms/ui-profile.js.map +1 -0
- package/llms/{pouf-profile.md → ui-profile.md} +4 -4
- package/llms/ui-quiz.d.ts +2 -0
- package/llms/ui-quiz.js +7 -0
- package/llms/ui-quiz.js.map +1 -0
- package/llms/{pouf-quiz.md → ui-quiz.md} +6 -6
- package/llms/ui-settings.d.ts +2 -0
- package/llms/ui-settings.js +7 -0
- package/llms/ui-settings.js.map +1 -0
- package/llms/{pouf-settings.md → ui-settings.md} +5 -5
- package/llms/ui-storefront.d.ts +2 -0
- package/llms/ui-storefront.js +7 -0
- package/llms/ui-storefront.js.map +1 -0
- package/llms/{pouf-storefront.md → ui-storefront.md} +9 -9
- package/llms/ui-support.d.ts +2 -0
- package/llms/ui-support.js +7 -0
- package/llms/ui-support.js.map +1 -0
- package/llms/{pouf-support.md → ui-support.md} +5 -5
- package/llms/ui-testimonials.d.ts +2 -0
- package/llms/ui-testimonials.js +7 -0
- package/llms/ui-testimonials.js.map +1 -0
- package/llms/{pouf-testimonials.md → ui-testimonials.md} +6 -6
- package/llms/ui-todo.d.ts +2 -0
- package/llms/ui-todo.js +7 -0
- package/llms/ui-todo.js.map +1 -0
- package/llms/{pouf-todo.md → ui-todo.md} +5 -5
- package/llms/ui-weather.d.ts +2 -0
- package/llms/ui-weather.js +7 -0
- package/llms/ui-weather.js.map +1 -0
- package/llms/{pouf-weather.md → ui-weather.md} +5 -5
- package/llms/use-vibe.md +4 -4
- package/llms/use-viewer.md +5 -5
- package/llms/voxel.md +1 -1
- package/llms/web-audio.md +12 -12
- package/llms/worlds.md +3 -3
- package/llms/youtube.md +1 -1
- package/package.json +5 -5
- package/system-prompt-initial-instant.md +3 -3
- package/system-prompt.md +3 -3
- package/themes/colorsets.js +7 -7
- package/themes/colorsets.js.map +1 -1
- package/themes/looks/pouf/brief.md +87 -87
- package/themes/looks/pouf/catalog.generated.d.ts +1 -1
- package/themes/looks/pouf/catalog.generated.js +17 -17
- package/themes/looks/pouf/catalog.generated.js.map +1 -1
- package/themes/looks/pouf/variety.js +4 -4
- package/themes/looks/pouf/variety.js.map +1 -1
- package/llms/pouf-blog.d.ts +0 -2
- package/llms/pouf-blog.js +0 -6
- package/llms/pouf-blog.js.map +0 -1
- package/llms/pouf-booking.d.ts +0 -2
- package/llms/pouf-booking.js +0 -6
- package/llms/pouf-booking.js.map +0 -1
- package/llms/pouf-calendar.d.ts +0 -2
- package/llms/pouf-calendar.js +0 -6
- package/llms/pouf-calendar.js.map +0 -1
- package/llms/pouf-chat.d.ts +0 -2
- package/llms/pouf-chat.js +0 -6
- package/llms/pouf-chat.js.map +0 -1
- package/llms/pouf-contact.d.ts +0 -2
- package/llms/pouf-contact.js +0 -6
- package/llms/pouf-contact.js.map +0 -1
- package/llms/pouf-course.d.ts +0 -2
- package/llms/pouf-course.js +0 -6
- package/llms/pouf-course.js.map +0 -1
- package/llms/pouf-crm.d.ts +0 -2
- package/llms/pouf-crm.js +0 -6
- package/llms/pouf-crm.js.map +0 -1
- package/llms/pouf-dashboard.d.ts +0 -2
- package/llms/pouf-dashboard.js +0 -6
- package/llms/pouf-dashboard.js.map +0 -1
- package/llms/pouf-editorial.d.ts +0 -2
- package/llms/pouf-editorial.js +0 -6
- package/llms/pouf-editorial.js.map +0 -1
- package/llms/pouf-event.d.ts +0 -2
- package/llms/pouf-event.js +0 -6
- package/llms/pouf-event.js.map +0 -1
- package/llms/pouf-feed.d.ts +0 -2
- package/llms/pouf-feed.js +0 -6
- package/llms/pouf-feed.js.map +0 -1
- package/llms/pouf-game.d.ts +0 -2
- package/llms/pouf-game.js +0 -6
- package/llms/pouf-game.js.map +0 -1
- package/llms/pouf-inbox.d.ts +0 -2
- package/llms/pouf-inbox.js +0 -6
- package/llms/pouf-inbox.js.map +0 -1
- package/llms/pouf-inventory.d.ts +0 -2
- package/llms/pouf-inventory.js +0 -6
- package/llms/pouf-inventory.js.map +0 -1
- package/llms/pouf-kanban.d.ts +0 -2
- package/llms/pouf-kanban.js +0 -6
- package/llms/pouf-kanban.js.map +0 -1
- package/llms/pouf-landing.d.ts +0 -2
- package/llms/pouf-landing.js +0 -6
- package/llms/pouf-landing.js.map +0 -1
- package/llms/pouf-login.d.ts +0 -2
- package/llms/pouf-login.js +0 -6
- package/llms/pouf-login.js.map +0 -1
- package/llms/pouf-music.d.ts +0 -2
- package/llms/pouf-music.js +0 -6
- package/llms/pouf-music.js.map +0 -1
- package/llms/pouf-onboarding.d.ts +0 -2
- package/llms/pouf-onboarding.js +0 -6
- package/llms/pouf-onboarding.js.map +0 -1
- package/llms/pouf-pricing.d.ts +0 -2
- package/llms/pouf-pricing.js +0 -6
- package/llms/pouf-pricing.js.map +0 -1
- package/llms/pouf-profile.d.ts +0 -2
- package/llms/pouf-profile.js +0 -6
- package/llms/pouf-profile.js.map +0 -1
- package/llms/pouf-quiz.d.ts +0 -2
- package/llms/pouf-quiz.js +0 -6
- package/llms/pouf-quiz.js.map +0 -1
- package/llms/pouf-settings.d.ts +0 -2
- package/llms/pouf-settings.js +0 -6
- package/llms/pouf-settings.js.map +0 -1
- package/llms/pouf-storefront.d.ts +0 -2
- package/llms/pouf-storefront.js +0 -6
- package/llms/pouf-storefront.js.map +0 -1
- package/llms/pouf-support.d.ts +0 -2
- package/llms/pouf-support.js +0 -6
- package/llms/pouf-support.js.map +0 -1
- package/llms/pouf-testimonials.d.ts +0 -2
- package/llms/pouf-testimonials.js +0 -6
- package/llms/pouf-testimonials.js.map +0 -1
- package/llms/pouf-todo.d.ts +0 -2
- package/llms/pouf-todo.js +0 -6
- package/llms/pouf-todo.js.map +0 -1
- package/llms/pouf-weather.d.ts +0 -2
- package/llms/pouf-weather.js +0 -6
- package/llms/pouf-weather.js.map +0 -1
package/llms/motion.md
CHANGED
|
@@ -14,7 +14,7 @@ stops when the component goes away.
|
|
|
14
14
|
|
|
15
15
|
The sibling boundary: a list whose items fade and rise as they enter uses
|
|
16
16
|
the house flourish, the `starting:` Tailwind classes, with no engine at all;
|
|
17
|
-
a canvas that redraws every frame is `p5`, `worlds` or `physics`;
|
|
17
|
+
a canvas that redraws every frame is `p5`, `worlds` or `physics`; the UI's
|
|
18
18
|
components carry their own motion (a `Card` with `motion="lift"`, the
|
|
19
19
|
toasts, the sheets). Motion is the right call when something on the page
|
|
20
20
|
responds to scroll, to time, or to touch.
|
|
@@ -52,8 +52,8 @@ before the engine lands cancels the setup (measured: a `move` stopped during
|
|
|
52
52
|
the load moved nothing). `motionScope()` is the collector.
|
|
53
53
|
|
|
54
54
|
**Every helper reads the reduced-motion setting when it runs** and lands on
|
|
55
|
-
the end state instead of animating.
|
|
56
|
-
to
|
|
55
|
+
the end state instead of animating. The UI's own reduced-motion rule is scoped
|
|
56
|
+
to the UI's elements, so an animation the app authors gets no safety net from
|
|
57
57
|
it; the helpers are the net (measured with the media feature emulated: lists
|
|
58
58
|
land visible, reveals leave everything visible, the count shows its final
|
|
59
59
|
number, the press does nothing, scroll progress still reports).
|
|
@@ -212,7 +212,7 @@ export function pressSquash(target, { scale = 0.94 } = {}) {
|
|
|
212
212
|
### `App.jsx`
|
|
213
213
|
```jsx app=story
|
|
214
214
|
import React, { useEffect, useRef } from "react";
|
|
215
|
-
import { Button, Card, Heading, Row, Spacer, Stack, Text } from "@vibes.diy/
|
|
215
|
+
import { Button, Card, Heading, Row, Spacer, Stack, Text } from "@vibes.diy/ui";
|
|
216
216
|
import { countUp, motionScope, onEnter, pressSquash, reveal, scrollProgress, sequence, staggerIn } from "./lib/motion.js";
|
|
217
217
|
|
|
218
218
|
const CHAPTERS = [
|
|
@@ -297,7 +297,7 @@ export default function App() {
|
|
|
297
297
|
```
|
|
298
298
|
|
|
299
299
|
The JSX stays class-only: positions, sizes and colours are Tailwind classes
|
|
300
|
-
and
|
|
300
|
+
and component props, and Motion writes transforms and opacity onto the elements
|
|
301
301
|
through refs and selectors. The one place a style is set in the app's own
|
|
302
302
|
code is the progress bar's `transform` inside the scroll callback, through
|
|
303
303
|
its ref, which is what a scroll binding is for.
|
|
@@ -434,7 +434,7 @@ export function pressSquash(target, { scale = 0.94 } = {}) {
|
|
|
434
434
|
import React, { useEffect, useMemo, useRef, useState } from "react";
|
|
435
435
|
import { useFireproof } from "use-fireproof";
|
|
436
436
|
import { useVibe, useViewer } from "use-vibes";
|
|
437
|
-
import { Button, Card, Field, Heading, Input, Row, Skeleton, Spacer, Stack, Text } from "@vibes.diy/
|
|
437
|
+
import { Button, Card, Field, Heading, Input, Row, Skeleton, Spacer, Stack, Text } from "@vibes.diy/ui";
|
|
438
438
|
import { motionScope, move, pressSquash } from "./lib/motion.js";
|
|
439
439
|
|
|
440
440
|
export default function App() {
|
|
@@ -544,7 +544,7 @@ is the baseline and animates nothing, every later snapshot's ids are
|
|
|
544
544
|
remembered, and only a top note nobody has seen springs in, so a board that
|
|
545
545
|
loaded with twenty notes stands still and a reorder moves nothing. (A board
|
|
546
546
|
that is empty when it opens treats its first note as the baseline too; the
|
|
547
|
-
second note is the first to spring.)
|
|
547
|
+
second note is the first to spring.) The UI's `Card motion="lift"` carries
|
|
548
548
|
the hover, so the app writes none of its own.
|
|
549
549
|
|
|
550
550
|
## Mobile
|
package/llms/p5.md
CHANGED
|
@@ -24,7 +24,7 @@ the instance so remounts do not leak a second draw loop.
|
|
|
24
24
|
```jsx
|
|
25
25
|
import React from "react";
|
|
26
26
|
import p5 from "https://esm.sh/p5@1.11.13";
|
|
27
|
-
import { Button, Row, Stack } from "@vibes.diy/
|
|
27
|
+
import { Button, Row, Stack } from "@vibes.diy/ui";
|
|
28
28
|
|
|
29
29
|
const TAU = Math.PI * 2;
|
|
30
30
|
|
package/llms/physics.md
CHANGED
|
@@ -225,7 +225,7 @@ from the pieces so the next drop replays the last one exactly.
|
|
|
225
225
|
import React, { useEffect, useMemo, useRef, useState } from "react";
|
|
226
226
|
import { useFireproof } from "use-fireproof";
|
|
227
227
|
import { useVibe, useViewer } from "use-vibes";
|
|
228
|
-
import { Badge, Button, Card, Field, Heading, Input, Row, Segmented, Skeleton, Spacer, Stack, Text } from "@vibes.diy/
|
|
228
|
+
import { Badge, Button, Card, Field, Heading, Input, Row, Segmented, Skeleton, Spacer, Stack, Text } from "@vibes.diy/ui";
|
|
229
229
|
import { createWorld } from "./lib/physics.js";
|
|
230
230
|
|
|
231
231
|
const WIDTH = 360;
|
|
@@ -495,7 +495,7 @@ button that applies a force to every body.
|
|
|
495
495
|
### `App.jsx`
|
|
496
496
|
```jsx app=tumble
|
|
497
497
|
import React, { useEffect, useRef, useState } from "react";
|
|
498
|
-
import { Button, Field, Heading, Input, Row, Spacer, Stack, Text } from "@vibes.diy/
|
|
498
|
+
import { Button, Field, Heading, Input, Row, Spacer, Stack, Text } from "@vibes.diy/ui";
|
|
499
499
|
import { Matter, createWorld, cssColor } from "./lib/physics.js";
|
|
500
500
|
|
|
501
501
|
const { Bodies, Body, Composite } = Matter;
|
package/llms/reskin.md
CHANGED
|
@@ -7,8 +7,8 @@ the app keeps its look's components, its logic and its Look tab, and wears that
|
|
|
7
7
|
appearance on top of them. Every `Card`, `Button`, `RowCard` and `Heading`
|
|
8
8
|
stays exactly where it is; what changes is how they are drawn.
|
|
9
9
|
|
|
10
|
-
This works on every look that shares the component surface (
|
|
11
|
-
looks built on its API): the same tokens, the same hook classes, the same
|
|
10
|
+
This works on every look that shares the component surface (the default look today,
|
|
11
|
+
and the looks built on its API): the same tokens, the same hook classes, the same
|
|
12
12
|
technique.
|
|
13
13
|
|
|
14
14
|
- Colour, a catalogue font or spacing alone is the Look tab (`set_app_style`);
|
|
@@ -30,24 +30,24 @@ so the wrapper's values are the ones every component reads.
|
|
|
30
30
|
The tokens: `--bg --surface --ink --text-secondary --purple --on-accent
|
|
31
31
|
--orange --info --up --down --idle --warn` (colour), `--r-card --r-control
|
|
32
32
|
--r-pill` (corners), `--lip --lip-row` (the cushion's edge), `--font-family`,
|
|
33
|
-
`--s1` … `--s7` (spacing), and the composite surfaces `--
|
|
34
|
-
--
|
|
33
|
+
`--s1` … `--s7` (spacing), and the composite surfaces `--ui-card --ui-row
|
|
34
|
+
--ui-control --ui-control-active --ui-field --ui-field-focus`. A
|
|
35
35
|
composite is resolved where it is declared, so a skin that changes the shadows
|
|
36
36
|
or the edge re-declares those composites on the wrapper too (`none` for a flat
|
|
37
37
|
skin).
|
|
38
38
|
|
|
39
39
|
**2. The look's parts are styled through their hook classes, from the wrapper.**
|
|
40
|
-
Every component carries a stable class: `.
|
|
41
|
-
.
|
|
42
|
-
.
|
|
43
|
-
rest of the package's `
|
|
40
|
+
Every component carries a stable class: `.ui-h1 .ui-h2 .ui-h3 .ui-card
|
|
41
|
+
.ui-rowcard .ui-btn .ui-blob .ui-empty .ui-stat .ui-eyebrow
|
|
42
|
+
.ui-grid .ui-input .ui-field .ui-badge .ui-tabs .ui-dialog`, and the
|
|
43
|
+
rest of the package's `ui-*` names. Write the skin as `[data-reskin] .ui-card
|
|
44
44
|
{ … }`. The components themselves take no `className` and no `style`: they keep
|
|
45
45
|
their props (`tone`, `size`, `variant`), and the skin reaches them by class.
|
|
46
46
|
|
|
47
47
|
**Portaled parts stay in the skin.** `Dialog`, `Select`, `Popover`, `Tooltip`
|
|
48
48
|
and `AlertDialog` render their open content under `<body>`, outside the
|
|
49
49
|
wrapper. So the token block is declared on `[data-reskin], body:has([data-reskin])`,
|
|
50
|
-
and a rule for a portaled part is written `body:has([data-reskin]) .
|
|
50
|
+
and a rule for a portaled part is written `body:has([data-reskin]) .ui-dialog
|
|
51
51
|
{ … }`. `body` sits below `:root`, so its tokens win for the whole page, the
|
|
52
52
|
open modal and dropdown included.
|
|
53
53
|
|
|
@@ -83,7 +83,7 @@ cushions, Chicago-like bitmap type (`Silkscreen`) over a plain grotesk body
|
|
|
83
83
|
import React, { useState } from "react";
|
|
84
84
|
import { useFireproof } from "use-fireproof";
|
|
85
85
|
import { useVibe } from "use-vibes";
|
|
86
|
-
import { Button, Card, Empty, Field, Heading, Input, Row, RowCard, Skeleton, Stack, Text } from "@vibes.diy/
|
|
86
|
+
import { Button, Card, Empty, Field, Heading, Input, Row, RowCard, Skeleton, Stack, Text } from "@vibes.diy/ui";
|
|
87
87
|
|
|
88
88
|
const SKIN = `
|
|
89
89
|
@import url('https://fonts.googleapis.com/css2?family=Silkscreen&family=Space+Grotesk:wght@400;600&display=swap');
|
|
@@ -92,8 +92,8 @@ const SKIN = `
|
|
|
92
92
|
--purple: #000000; --on-accent: #ffffff;
|
|
93
93
|
--r-card: 0px; --r-control: 0px; --r-pill: 0px; --lip: 0px; --lip-row: 0px;
|
|
94
94
|
--font-family: 'Space Grotesk', system-ui, sans-serif;
|
|
95
|
-
--
|
|
96
|
-
--
|
|
95
|
+
--ui-card: none; --ui-row: none; --ui-control: none; --ui-control-active: none;
|
|
96
|
+
--ui-field: inset 0 0 0 1px #000000; --ui-field-focus: inset 0 0 0 2px #000000;
|
|
97
97
|
min-height: 100vh; background: var(--bg); color: var(--ink); font-family: var(--font-family);
|
|
98
98
|
}
|
|
99
99
|
[data-reskin] .menubar { background: #ffffff; border-bottom: 1px solid #000000; padding: 2px 12px; font-family: 'Silkscreen', monospace; }
|
|
@@ -101,12 +101,12 @@ const SKIN = `
|
|
|
101
101
|
[data-reskin] .titlebar { height: 20px; border-bottom: 1px solid #000000; text-align: center; font-family: 'Silkscreen', monospace; font-size: 12px; line-height: 20px;
|
|
102
102
|
background: repeating-linear-gradient(#ffffff 0 1px, #000000 1px 2px); }
|
|
103
103
|
[data-reskin] .titlebar span { background: #ffffff; padding: 0 8px; }
|
|
104
|
-
[data-reskin] .
|
|
105
|
-
[data-reskin] .
|
|
106
|
-
[data-reskin] .
|
|
107
|
-
[data-reskin] .
|
|
108
|
-
[data-reskin] .
|
|
109
|
-
body:has([data-reskin]) .
|
|
104
|
+
[data-reskin] .ui-h1, [data-reskin] .ui-h2 { font-family: 'Silkscreen', monospace; letter-spacing: 0; }
|
|
105
|
+
[data-reskin] .ui-card, [data-reskin] .ui-rowcard { border: 1px solid #000000; }
|
|
106
|
+
[data-reskin] .ui-btn { border: 1px solid #000000; background: #ffffff; color: #000000; border-radius: 6px; }
|
|
107
|
+
[data-reskin] .ui-btn:active { background: #000000; color: #ffffff; }
|
|
108
|
+
[data-reskin] .ui-empty { border: 1px dashed #000000; }
|
|
109
|
+
body:has([data-reskin]) .ui-dialog { border: 1px solid #000000; border-radius: 0; box-shadow: 2px 2px 0 #000000; }
|
|
110
110
|
`;
|
|
111
111
|
|
|
112
112
|
export default function App() {
|
|
@@ -232,7 +232,7 @@ let the components edit it.
|
|
|
232
232
|
|
|
233
233
|
```jsx
|
|
234
234
|
import React, { useState } from "react";
|
|
235
|
-
import { Button, Field, Input, Stack } from "@vibes.diy/
|
|
235
|
+
import { Button, Field, Input, Stack } from "@vibes.diy/ui";
|
|
236
236
|
|
|
237
237
|
const SKIN = `
|
|
238
238
|
@import url('https://fonts.googleapis.com/css2?family=Fraunces:ital,wght@0,600;1,400&display=swap');
|
|
@@ -289,7 +289,7 @@ example.
|
|
|
289
289
|
Name the five things a stranger would recognise and set each one:
|
|
290
290
|
|
|
291
291
|
- **Ground and surfaces:** `--bg`, `--surface`, `--ink`, `--text-secondary`.
|
|
292
|
-
- **Type:** one display face for `.
|
|
292
|
+
- **Type:** one display face for `.ui-h1`/`.ui-h2`/`.ui-eyebrow`, one body
|
|
293
293
|
face on `--font-family`, the heading scale and tracking.
|
|
294
294
|
- **Edges:** corners (`--r-card`, `--r-control`, `--r-pill`), borders on the
|
|
295
295
|
hook classes, and the composite surfaces (`none` for flat, a hard offset for
|
|
@@ -302,5 +302,5 @@ Name the five things a stranger would recognise and set each one:
|
|
|
302
302
|
A newspaper is a cream ground, black ink, a serif masthead over a rule, column
|
|
303
303
|
grids and no shadows; a terminal is black, one phosphor green, a mono face and
|
|
304
304
|
a blinking cursor that starts lit; Y2K is chrome gradients, pill corners and a
|
|
305
|
-
glossy highlight on `.
|
|
305
|
+
glossy highlight on `.ui-btn`. The words on screen stay the app's own: the
|
|
306
306
|
style is borrowed, the names and logos of whoever made it famous are not.
|
package/llms/richtext.md
CHANGED
|
@@ -10,8 +10,8 @@ in Fireproof, saves after typing pauses, loads a document without echoing it
|
|
|
10
10
|
back as a save, and styles the written text with Tailwind on the host.
|
|
11
11
|
|
|
12
12
|
The sibling boundary: a short field (a title, a comment, a caption) is
|
|
13
|
-
|
|
14
|
-
of stories with stages is `
|
|
13
|
+
the UI's `Textarea` in a `Field`; a feed of posts is `ui-feed`; a pipeline
|
|
14
|
+
of stories with stages is `ui-editorial`. Rich text is the right call when
|
|
15
15
|
the person is writing a page.
|
|
16
16
|
|
|
17
17
|
The worked examples below were built and measured for this doc (Chromium,
|
|
@@ -165,7 +165,7 @@ export function createEditor(host, { content = EMPTY_DOC, placeholder = "Start w
|
|
|
165
165
|
import React, { useEffect, useMemo, useRef, useState } from "react";
|
|
166
166
|
import { useFireproof } from "use-fireproof";
|
|
167
167
|
import { useVibe, useViewer } from "use-vibes";
|
|
168
|
-
import { Badge, Button, Card, Heading, IconButton, Row, Skeleton, Spacer, Stack, Text } from "@vibes.diy/
|
|
168
|
+
import { Badge, Button, Card, Heading, IconButton, Row, Skeleton, Spacer, Stack, Text } from "@vibes.diy/ui";
|
|
169
169
|
import { EMPTY_DOC, createEditor, titleOf } from "./lib/editor.js";
|
|
170
170
|
|
|
171
171
|
// The written page is styled from the host: Tailwind reaches into the editor with arbitrary
|
|
@@ -471,7 +471,7 @@ through Tailwind's arbitrary variants**: `[&_h1]:text-2xl`,
|
|
|
471
471
|
`[&_.is-editor-empty:first-child::before]:content-[attr(data-placeholder)]`.
|
|
472
472
|
The JSX stays class-only and the theme tokens (`text-primary`,
|
|
473
473
|
`text-muted-foreground`, `bg-muted`) carry the owner's restyle into the
|
|
474
|
-
written page. Measured: the
|
|
474
|
+
written page. Measured: the design lint passes a host carrying these classes.
|
|
475
475
|
|
|
476
476
|
## Commands and the toolbar
|
|
477
477
|
|
|
@@ -553,7 +553,7 @@ export function createEditor(host, { content = EMPTY_DOC, placeholder = "Start w
|
|
|
553
553
|
import React, { useEffect, useRef, useState } from "react";
|
|
554
554
|
import { useFireproof } from "use-fireproof";
|
|
555
555
|
import { useVibe, useViewer } from "use-vibes";
|
|
556
|
-
import { Badge, Button, Card, Heading, Row, Spacer, Stack, Text } from "@vibes.diy/
|
|
556
|
+
import { Badge, Button, Card, Heading, Row, Spacer, Stack, Text } from "@vibes.diy/ui";
|
|
557
557
|
import { EMPTY_DOC, createEditor } from "./lib/editor.js";
|
|
558
558
|
|
|
559
559
|
const PAGE_ID = "page:home";
|
package/llms/spotify.md
CHANGED
|
@@ -325,7 +325,7 @@ function artistRow(a) {
|
|
|
325
325
|
```jsx app=spotify-browser
|
|
326
326
|
import React, { useEffect, useRef, useState } from "react";
|
|
327
327
|
import { apiFetch } from "use-vibes";
|
|
328
|
-
import { Badge, Button, Field, Heading, Input, Row, Stack, Text } from "@vibes.diy/
|
|
328
|
+
import { Badge, Button, Field, Heading, Input, Row, Stack, Text } from "@vibes.diy/ui";
|
|
329
329
|
|
|
330
330
|
// The setup verdict, read on mount and again whenever the app comes back into view: the owner
|
|
331
331
|
// sets the pair in another tab and returns, and search unlocks without a reload.
|
|
@@ -683,7 +683,7 @@ export function club(doc, oldDoc, user, ctx) {
|
|
|
683
683
|
import React, { useEffect, useRef, useState } from "react";
|
|
684
684
|
import { useFireproof } from "use-fireproof";
|
|
685
685
|
import { apiFetch, useViewer } from "use-vibes";
|
|
686
|
-
import { Badge, Button, Heading, Row, Stack, Text } from "@vibes.diy/
|
|
686
|
+
import { Badge, Button, Heading, Row, Stack, Text } from "@vibes.diy/ui";
|
|
687
687
|
|
|
688
688
|
// The setup verdict, read on mount and again whenever the app comes back into view: the owner
|
|
689
689
|
// connects Stripe or saves the portal in another tab and returns, and the Join buttons unlock
|
package/llms/stripe.md
CHANGED
|
@@ -570,7 +570,7 @@ export function orders(doc, oldDoc, user, ctx) {
|
|
|
570
570
|
import React, { useEffect, useRef, useState } from "react";
|
|
571
571
|
import { useFireproof } from "use-fireproof";
|
|
572
572
|
import { apiFetch, useVibe } from "use-vibes";
|
|
573
|
-
import { Badge, Button, Heading, Row, Stack, Text } from "@vibes.diy/
|
|
573
|
+
import { Badge, Button, Heading, Row, Stack, Text } from "@vibes.diy/ui";
|
|
574
574
|
|
|
575
575
|
// The setup verdict, read on mount and again whenever the app comes back into view: the owner
|
|
576
576
|
// connects Stripe in another tab and returns, and the Pay buttons unlock without a reload.
|
|
@@ -1184,8 +1184,8 @@ export function checks(doc, oldDoc, user, ctx) {
|
|
|
1184
1184
|
import React, { useEffect, useRef, useState } from "react";
|
|
1185
1185
|
import { useFireproof } from "use-fireproof";
|
|
1186
1186
|
import { apiFetch } from "use-vibes";
|
|
1187
|
-
import { Badge, Button, Field, Figure, Heading, Input, Metric, Row, Segmented, Stack, Text } from "@vibes.diy/
|
|
1188
|
-
import { Checkbox } from "@vibes.diy/
|
|
1187
|
+
import { Badge, Button, Field, Figure, Heading, Input, Metric, Row, Segmented, Stack, Text } from "@vibes.diy/ui";
|
|
1188
|
+
import { Checkbox } from "@vibes.diy/ui/interactive";
|
|
1189
1189
|
|
|
1190
1190
|
const money = (cents) => `$${(cents / 100).toFixed(2)}`;
|
|
1191
1191
|
const post = (body) => ({ method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) });
|
package/llms/strudel.md
CHANGED
|
@@ -207,7 +207,7 @@ with every write gated on `can`.
|
|
|
207
207
|
import React, { useEffect, useMemo, useState } from "react";
|
|
208
208
|
import { useFireproof } from "use-fireproof";
|
|
209
209
|
import { callAI, useVibe, useViewer } from "use-vibes";
|
|
210
|
-
import { Badge, Button, Card, Field, Heading, Input, Row, Skeleton, Spacer, Stack, Text } from "@vibes.diy/
|
|
210
|
+
import { Badge, Button, Card, Field, Heading, Input, Row, Skeleton, Spacer, Stack, Text } from "@vibes.diy/ui";
|
|
211
211
|
import { EXAMPLES, onPatternError, play, stop } from "./lib/strudel.js";
|
|
212
212
|
import { rewritePrompt } from "./lib/pattern-prompt.js";
|
|
213
213
|
|
|
@@ -446,8 +446,8 @@ A groovebox whose lights follow the music, built on the same module:
|
|
|
446
446
|
import React, { useEffect, useRef, useState } from "react";
|
|
447
447
|
import { useFireproof } from "use-fireproof";
|
|
448
448
|
import { useVibe, useViewer } from "use-vibes";
|
|
449
|
-
import { Badge, Button, Card, Field, Heading, Icon, IconButton, Input, Row, Skeleton, Spacer, Stack, Text } from "@vibes.diy/
|
|
450
|
-
import { Slider } from "@vibes.diy/
|
|
449
|
+
import { Badge, Button, Card, Field, Heading, Icon, IconButton, Input, Row, Skeleton, Spacer, Stack, Text } from "@vibes.diy/ui";
|
|
450
|
+
import { Slider } from "@vibes.diy/ui/interactive";
|
|
451
451
|
import { gridToCode, play, stop } from "./lib/strudel.js";
|
|
452
452
|
|
|
453
453
|
const ROWS = ["bd", "sd", "hh", "cp"];
|
package/llms/three-js.md
CHANGED
|
@@ -1258,7 +1258,7 @@ import React, { useState, useEffect, useRef, useCallback } from "react";
|
|
|
1258
1258
|
import { useFireproof } from "use-fireproof";
|
|
1259
1259
|
import * as THREE from "three";
|
|
1260
1260
|
import { useWorldControls } from "use-vibes";
|
|
1261
|
-
import { Card, Heading, IconButton, Stack, Text } from "@vibes.diy/
|
|
1261
|
+
import { Card, Heading, IconButton, Stack, Text } from "@vibes.diy/ui";
|
|
1262
1262
|
|
|
1263
1263
|
export default function SkyGlider() {
|
|
1264
1264
|
const { database, useLiveQuery } = useFireproof("skyGliderScores");
|
|
@@ -1705,8 +1705,8 @@ import { OrbitControls } from "three/addons/controls/OrbitControls.js";
|
|
|
1705
1705
|
import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js";
|
|
1706
1706
|
import { RenderPass } from "three/addons/postprocessing/RenderPass.js";
|
|
1707
1707
|
import { HalftonePass } from "three/addons/postprocessing/HalftonePass.js";
|
|
1708
|
-
import { Button, Card, Field, Heading, Input, RowCard, Stack, Text } from "@vibes.diy/
|
|
1709
|
-
import { Checkbox, Select, Separator, Slider } from "@vibes.diy/
|
|
1708
|
+
import { Button, Card, Field, Heading, Input, RowCard, Stack, Text } from "@vibes.diy/ui";
|
|
1709
|
+
import { Checkbox, Select, Separator, Slider } from "@vibes.diy/ui/interactive";
|
|
1710
1710
|
|
|
1711
1711
|
export default function HalftoneArtStudio() {
|
|
1712
1712
|
const { database, useLiveQuery } = useFireproof("halftoneStudio");
|
|
@@ -2061,7 +2061,7 @@ export default function HalftoneArtStudio() {
|
|
|
2061
2061
|
<div className="relative h-dvh w-full overflow-hidden bg-background text-foreground font-sans">
|
|
2062
2062
|
<canvas ref={canvasRef} className="absolute inset-0 select-none touch-none" />
|
|
2063
2063
|
|
|
2064
|
-
{/* One control panel. The
|
|
2064
|
+
{/* One control panel. The UI's `Card` IS the panel — it takes no className,
|
|
2065
2065
|
so a plain wrapper carries its placement; the cushion, corner and ink are its own. */}
|
|
2066
2066
|
<div className="absolute inset-y-4 left-4 w-80 overflow-y-auto">
|
|
2067
2067
|
<Card>
|
package/llms/touch-games.js
CHANGED
|
@@ -2,7 +2,7 @@ export const touchGamesConfig = {
|
|
|
2
2
|
name: "touch-games",
|
|
3
3
|
label: "Touch games",
|
|
4
4
|
description: "Phone-first controls for anything played or steered in real time — load it beside three-js, voxel, " +
|
|
5
|
-
"physics, p5, worlds or
|
|
5
|
+
"physics, p5, worlds or ui-game for every game, 3D scene you move through, or canvas toy: the play area " +
|
|
6
6
|
"is the controller (select-none touch-none surface, Pointer Events with pointer capture), swipes that fire " +
|
|
7
7
|
"mid-move with chaining, tap vs swipe on one surface, a floating ghost stick under the thumb, hold-to-boost " +
|
|
8
8
|
"with drag steering, pull-back-and-release aiming, gesture trails and feedback, keyboard twins for desktop, " +
|
package/llms/touch-games.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"touch-games.js","sourceRoot":"","sources":["../../jsr/llms/touch-games.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,gBAAgB,GAAc;IACzC,IAAI,EAAE,aAAa;IACnB,KAAK,EAAE,aAAa;IACpB,WAAW,EACT,qGAAqG;QACrG,
|
|
1
|
+
{"version":3,"file":"touch-games.js","sourceRoot":"","sources":["../../jsr/llms/touch-games.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,gBAAgB,GAAc;IACzC,IAAI,EAAE,aAAa;IACnB,KAAK,EAAE,aAAa;IACpB,WAAW,EACT,qGAAqG;QACrG,yGAAyG;QACzG,4GAA4G;QAC5G,6GAA6G;QAC7G,6GAA6G;QAC7G,iHAAiH;QACjH,2DAA2D;QAC3D,uGAAuG;QACvG,mEAAmE;IAKrE,IAAI,EAAE;QACJ,OAAO;QACP,gBAAgB;QAChB,aAAa;QACb,YAAY;QACZ,YAAY;QACZ,aAAa;QACb,cAAc;QACd,aAAa;QACb,UAAU;QACV,QAAQ;QACR,OAAO;QACP,QAAQ;QACR,OAAO;QACP,SAAS;QACT,YAAY;QACZ,gBAAgB;QAChB,SAAS;QACT,aAAa;QACb,aAAa;QACb,OAAO;QACP,SAAS;QACT,WAAW;QACX,SAAS;QACT,WAAW;QACX,UAAU;QACV,QAAQ;QACR,YAAY;QACZ,cAAc;QACd,cAAc;QACd,aAAa;QACb,SAAS;KACV;CACF,CAAC"}
|
package/llms/touch-games.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Touch games — the play area is the controller
|
|
2
2
|
|
|
3
3
|
Load this beside whatever draws the game — `three-js`, `voxel`, `physics`, `p5`, `worlds`,
|
|
4
|
-
`
|
|
4
|
+
`ui-game`, a plain `<canvas>` — whenever the app is something you play or steer in real time:
|
|
5
5
|
a runner, a racer, a flyer, a shooter, a snake, a launcher, a platformer, a pinball table, a
|
|
6
6
|
marble maze, a 3D scene you move through, a toy you drag around. Most people open a vibe on a
|
|
7
7
|
phone, one-handed. The game that feels great there is the one where the thumb rests **on the
|
|
@@ -208,7 +208,7 @@ orbits, a click is `onTap`, a right-click is `onHold`, and the wheel zooms.
|
|
|
208
208
|
import React from "react";
|
|
209
209
|
import * as THREE from "three";
|
|
210
210
|
import { useWorldControls } from "use-vibes";
|
|
211
|
-
import { Button, Card, Heading, IconButton, Stack, Text } from "@vibes.diy/
|
|
211
|
+
import { Button, Card, Heading, IconButton, Stack, Text } from "@vibes.diy/ui";
|
|
212
212
|
|
|
213
213
|
const VIEWS = ["Free cam", "First person", "Shoulder view"];
|
|
214
214
|
const HINT = {
|
|
@@ -407,7 +407,7 @@ snake by changing what `move()` does.
|
|
|
407
407
|
### `App.jsx`
|
|
408
408
|
```jsx
|
|
409
409
|
import React from "react";
|
|
410
|
-
import { Card, Heading, IconButton, Stack, Text } from "@vibes.diy/
|
|
410
|
+
import { Card, Heading, IconButton, Stack, Text } from "@vibes.diy/ui";
|
|
411
411
|
|
|
412
412
|
const TUNE = {
|
|
413
413
|
swipePx: 26, // travel from the anchor before a swipe fires (raise to 30–34 if a resting thumb misfires)
|
package/llms/ui-blog.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-blog.js","sourceRoot":"","sources":["../../jsr/llms/ui-blog.ts"],"names":[],"mappings":"AAMA,MAAM,CAAC,MAAM,YAAY,GAAc;IACrC,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,CAAC,WAAW,CAAC;IACtB,KAAK,EAAE,YAAY;IACnB,WAAW,EAAE,gEAAgE;CAC9E,CAAC"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Blog
|
|
2
2
|
|
|
3
3
|
A blog with a front page and a reading view: a bakery's recipe notes, a travel journal, a club's match reports, a studio's news. Reach for it whenever the app's core is posts written by a few people and read by many, newest first, sorted into a handful of categories. Each post is its own document, so the blog is there on the next visit and on every device, and a new post appears for every reader as soon as it is published.
|
|
4
4
|
|
|
5
|
-
Built from @vibes.diy/
|
|
5
|
+
Built from @vibes.diy/ui components; persisted state lives in Fireproof. Every write surface is gated on `useVibe(dbName).can`.
|
|
6
6
|
|
|
7
7
|
## The block
|
|
8
8
|
|
|
9
|
-
Two views, one job each. The front page opens with an `Eyebrow`, the `Heading` and a muted post count beside a `Write a post` button, the front page's one lead, which opens a `Dialog` with the writing form (title, category, body, each in its own `Field` with its own error). The newest post leads on a `Card` laid out with `Grid cols="sidebar"` beside a decorative `orange` `
|
|
9
|
+
Two views, one job each. The front page opens with an `Eyebrow`, the `Heading` and a muted post count beside a `Write a post` button, the front page's one lead, which opens a `Dialog` with the writing form (title, category, body, each in its own `Field` with its own error). The newest post leads on a `Card` laid out with `Grid cols="sidebar"` beside a decorative `orange` `IconTile`; under it a `Segmented` category filter (`tone="info"`) narrows a two-column `Grid` of post `Card`s, each with an `info` category `Badge`, the title, an excerpt in muted `Text`, the writer as a `ViewerTag` with a muted date, and a quiet `Read` button; `Pagination` pages through them and an `Empty` stands in when a category has nothing. Opening a post swaps in the reading view: a quiet `IconButton` back, the title as the `Heading`, the byline and the body as `Text` paragraphs, with a quiet `Delete post` (toned `down`) behind a `Confirm` wearing `tone="down"`, both showing `loading` while the delete lands. Every write waits on its `can` verdict from `useVibe("blog")`, the database the posts live in: the write button gives way to a `Skeleton` inside an `aria-busy` wrapper while the verdict resolves, to a quiet `Sign in to write` wired to `useViewer().requestLogin()` for a visitor who has not signed in, and to a sentence in the app's own words when writing is closed; `Delete post` renders only where `can.delete(post).ok`.
|
|
10
10
|
|
|
11
11
|
### `App.jsx`
|
|
12
12
|
```jsx
|
|
@@ -15,7 +15,7 @@ import { useFireproof } from "use-fireproof";
|
|
|
15
15
|
import { useViewer, useVibe } from "use-vibes";
|
|
16
16
|
import {
|
|
17
17
|
Badge,
|
|
18
|
-
|
|
18
|
+
IconTile,
|
|
19
19
|
Button,
|
|
20
20
|
Card,
|
|
21
21
|
Empty,
|
|
@@ -33,8 +33,8 @@ import {
|
|
|
33
33
|
Stack,
|
|
34
34
|
Text,
|
|
35
35
|
Textarea,
|
|
36
|
-
} from "@vibes.diy/
|
|
37
|
-
import { Confirm, Dialog, Select, Toaster, toast } from "@vibes.diy/
|
|
36
|
+
} from "@vibes.diy/ui";
|
|
37
|
+
import { Confirm, Dialog, Select, Toaster, toast } from "@vibes.diy/ui/interactive";
|
|
38
38
|
|
|
39
39
|
const CATEGORIES = [
|
|
40
40
|
{ value: "recipes", label: "Recipes" },
|
|
@@ -164,7 +164,7 @@ export default function App() {
|
|
|
164
164
|
</Row>
|
|
165
165
|
)}
|
|
166
166
|
</Stack>
|
|
167
|
-
<div className="
|
|
167
|
+
<div className="ui-toasts">
|
|
168
168
|
<Toaster />
|
|
169
169
|
</div>
|
|
170
170
|
</main>
|
|
@@ -246,7 +246,7 @@ export default function App() {
|
|
|
246
246
|
</Button>
|
|
247
247
|
</Row>
|
|
248
248
|
</Stack>
|
|
249
|
-
<
|
|
249
|
+
<IconTile icon="sparkle" tone="orange" size="lg" />
|
|
250
250
|
</Grid>
|
|
251
251
|
</Card>
|
|
252
252
|
|
|
@@ -286,7 +286,7 @@ export default function App() {
|
|
|
286
286
|
</>
|
|
287
287
|
)}
|
|
288
288
|
</Stack>
|
|
289
|
-
<div className="
|
|
289
|
+
<div className="ui-toasts">
|
|
290
290
|
<Toaster />
|
|
291
291
|
</div>
|
|
292
292
|
</main>
|
|
@@ -298,7 +298,7 @@ export default function App() {
|
|
|
298
298
|
|
|
299
299
|
- Swap `CATEGORIES` for the blog's own sections; the filter and the form's `Select` both read from it, so a new section appears in both. Keep the category `Badge`s on one neutral tone (`info`): a category is a label, not a verdict.
|
|
300
300
|
- Keep one document per post with its `authorHandle`; the byline is always a `ViewerTag`, never a typed name.
|
|
301
|
-
- A cover photo is a `_files` attachment on the post, shown with `Figure` in place of the featured `
|
|
301
|
+
- A cover photo is a `_files` attachment on the post, shown with `Figure` in place of the featured `IconTile`; drafts that aren't ready yet are a `status` field filtered out of the front page (the editorial block walks posts through that pipeline).
|
|
302
302
|
- Editing a post is the same form, prefilled, writing behind `can.edit(post)`; keep `Delete post` quiet and behind its `Confirm`.
|
|
303
303
|
- A new write surface gets its own verdict: `can.create(draft)` with the fields the write stamps, `can.edit(doc)` or `can.delete(doc)` on the document it changes, always on `useVibe` of the database it writes.
|
|
304
304
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-booking.js","sourceRoot":"","sources":["../../jsr/llms/ui-booking.ts"],"names":[],"mappings":"AAMA,MAAM,CAAC,MAAM,eAAe,GAAc;IACxC,IAAI,EAAE,YAAY;IAClB,OAAO,EAAE,CAAC,cAAc,CAAC;IACzB,KAAK,EAAE,eAAe;IACtB,WAAW,EAAE,iEAAiE;CAC/E,CAAC"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Booking
|
|
2
2
|
|
|
3
3
|
An appointment booker: pick a service, a day and a free time, add a name, review, confirm. It fits a massage practice's sessions, a tutor's lessons, a studio's portrait slots or a repair café's drop-in times. Reach for it whenever the app's core is people claiming time slots that only one person can hold. Every booking persists as its own document and availability is worked out from them, so a taken time disappears for everyone and the booker finds their sessions on every visit.
|
|
4
4
|
|
|
5
|
-
Built from @vibes.diy/
|
|
5
|
+
Built from @vibes.diy/ui components; persisted state lives in Fireproof. Every write surface is gated on `useVibe(dbName).can`.
|
|
6
6
|
|
|
7
7
|
## The block
|
|
8
8
|
|
|
9
|
-
Two cards beside each other in a `Grid cols="sidebar"`. The main side opens with an `Eyebrow`, the `Heading` and a muted promise, then the person's own bookings on a `Card`. Each booking shows a `Badge`: `up` when confirmed, `warn` when someone else got the time first. Each one has a quiet `Cancel` behind a `Confirm` wearing `tone="down"`, offered only where `can.delete(booking).ok`. The side holds the wizard, one step at a time under an `Eyebrow` and a `Progress` bar (`tone="info"`): each service is a clickable `RowCard` with a decorative `orange` `
|
|
9
|
+
Two cards beside each other in a `Grid cols="sidebar"`. The main side opens with an `Eyebrow`, the `Heading` and a muted promise, then the person's own bookings on a `Card`. Each booking shows a `Badge`: `up` when confirmed, `warn` when someone else got the time first. Each one has a quiet `Cancel` behind a `Confirm` wearing `tone="down"`, offered only where `can.delete(booking).ok`. The side holds the wizard, one step at a time under an `Eyebrow` and a `Progress` bar (`tone="info"`): each service is a clickable `RowCard` with a decorative `orange` `IconTile`; the day is a `Segmented` (`tone="info"`) and the time a `RadioGroup` of only the free times, with a muted line when a day is full; the details are a name `Input` in a `Field` with its own error (nothing private, because every booking is readable so free times can be worked out); and the review ends in `Confirm booking`, that step's one lead, which shows `loading` while the write lands. Each booking is its own document, never a flag on a shared calendar document. Two people can confirm the same time at once, and sync keeps both documents, so availability is derived: a slot belongs to its earliest booking (by `createdAt`, then `_id`). A later one is shown to its booker as a clash, with a `Pick another time` button that removes it and keeps their details. Confirming waits on its `can` verdict from `useVibe("bookings")`: a `Skeleton` inside an `aria-busy` wrapper while it resolves, and a sentence in the app's own words when booking is closed. For a visitor who has not signed in, the lead is `Sign in to confirm`, wired to `useViewer().requestLogin()`, and their choices stay in place. Sign-in is required here, unlike a local-first note, because a signed-out write stays on the device until sign-in: a booking made that way would never reach the practice or hold the slot against anyone else. Between the write and the live query catching up, the wizard shows a confirming `Skeleton` rather than the review again, so the lead can't book twice; cancelling the booking just made resets the wizard.
|
|
10
10
|
|
|
11
11
|
### `App.jsx`
|
|
12
12
|
```jsx
|
|
@@ -15,7 +15,7 @@ import { useFireproof } from "use-fireproof";
|
|
|
15
15
|
import { useViewer, useVibe } from "use-vibes";
|
|
16
16
|
import {
|
|
17
17
|
Badge,
|
|
18
|
-
|
|
18
|
+
IconTile,
|
|
19
19
|
Button,
|
|
20
20
|
Card,
|
|
21
21
|
Empty,
|
|
@@ -31,8 +31,8 @@ import {
|
|
|
31
31
|
Spacer,
|
|
32
32
|
Stack,
|
|
33
33
|
Text,
|
|
34
|
-
} from "@vibes.diy/
|
|
35
|
-
import { Confirm, Progress, RadioGroup, Toaster, toast } from "@vibes.diy/
|
|
34
|
+
} from "@vibes.diy/ui";
|
|
35
|
+
import { Confirm, Progress, RadioGroup, Toaster, toast } from "@vibes.diy/ui/interactive";
|
|
36
36
|
|
|
37
37
|
const SERVICES = [
|
|
38
38
|
{ id: "relax", name: "Relaxation massage", blurb: "A slow, full-body session to unwind.", minutes: 60, price: 80, icon: "sparkle" },
|
|
@@ -227,7 +227,7 @@ export default function App() {
|
|
|
227
227
|
}}
|
|
228
228
|
>
|
|
229
229
|
<Row gap={3} wrap={false} align="top">
|
|
230
|
-
<
|
|
230
|
+
<IconTile icon={s.icon} tone="orange" size="sm" />
|
|
231
231
|
<Stack gap={1}>
|
|
232
232
|
<Text>{s.name}</Text>
|
|
233
233
|
<Text size="sm" muted>
|
|
@@ -378,7 +378,7 @@ export default function App() {
|
|
|
378
378
|
</Stack>
|
|
379
379
|
<Card>{wizard}</Card>
|
|
380
380
|
</Grid>
|
|
381
|
-
<div className="
|
|
381
|
+
<div className="ui-toasts">
|
|
382
382
|
<Toaster />
|
|
383
383
|
</div>
|
|
384
384
|
</main>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-calendar.js","sourceRoot":"","sources":["../../jsr/llms/ui-calendar.ts"],"names":[],"mappings":"AAOA,MAAM,CAAC,MAAM,gBAAgB,GAAc;IACzC,IAAI,EAAE,aAAa;IACnB,OAAO,EAAE,CAAC,eAAe,CAAC;IAC1B,KAAK,EAAE,gBAAgB;IACvB,WAAW,EAAE,uEAAuE;CACrF,CAAC"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Calendar
|
|
2
2
|
|
|
3
3
|
An on-screen calendar of timed events: a studio's class timetable, a family's appointments, a club's practice schedule, a shared room's bookings for the week. Reach for it when the app shows a month at a glance and one day's agenda in time order, with the free gaps between events and a line where "now" falls, and lets people add and remove events. This is the calendar people look at inside the app; a subscribable phone-calendar feed (webcal or `.ics`) is a different job, served from the backend. Each event is its own document, so everyone who opens the app sees the same schedule.
|
|
4
4
|
|
|
5
|
-
Built from @vibes.diy/
|
|
5
|
+
Built from @vibes.diy/ui components; persisted state lives in Fireproof. Every write surface is gated on `useVibe(dbName).can`.
|
|
6
6
|
|
|
7
7
|
## The block
|
|
8
8
|
|
|
@@ -13,8 +13,8 @@ One view with one job: see what is on and add to it. The `Heading` and a muted l
|
|
|
13
13
|
import React, { useEffect, useState } from "react";
|
|
14
14
|
import { useFireproof } from "use-fireproof";
|
|
15
15
|
import { useViewer, useVibe } from "use-vibes";
|
|
16
|
-
import { Badge, Button, Card, Empty, Field, Grid, Heading, Icon, IconButton, Input, Row, RowCard, Skeleton, Stack, Text } from "@vibes.diy/
|
|
17
|
-
import { Confirm, Dialog, Select, Toaster, toast } from "@vibes.diy/
|
|
16
|
+
import { Badge, Button, Card, Empty, Field, Grid, Heading, Icon, IconButton, Input, Row, RowCard, Skeleton, Stack, Text } from "@vibes.diy/ui";
|
|
17
|
+
import { Confirm, Dialog, Select, Toaster, toast } from "@vibes.diy/ui/interactive";
|
|
18
18
|
|
|
19
19
|
const pad = (n) => String(n).padStart(2, "0");
|
|
20
20
|
// Dates are kept as local YYYY-MM-DD strings, so an event never drifts a day across time zones.
|
|
@@ -332,7 +332,7 @@ export default function App() {
|
|
|
332
332
|
</Stack>
|
|
333
333
|
</Card>
|
|
334
334
|
</Stack>
|
|
335
|
-
<div className="
|
|
335
|
+
<div className="ui-toasts">
|
|
336
336
|
<Toaster />
|
|
337
337
|
</div>
|
|
338
338
|
</main>
|
package/llms/ui-chat.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-chat.js","sourceRoot":"","sources":["../../jsr/llms/ui-chat.ts"],"names":[],"mappings":"AAMA,MAAM,CAAC,MAAM,YAAY,GAAc;IACrC,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,CAAC,WAAW,CAAC;IACtB,KAAK,EAAE,YAAY;IACnB,WAAW,EAAE,4DAA4D;CAC1E,CAAC"}
|