@vibes.diy/prompts 14.1.1 → 14.1.3

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/backend.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export const backendConfig = {
2
2
  name: "backend",
3
3
  label: "backend-js",
4
- description: "server-side backend.js file: answer HTTP/webhook requests at the app's /_api URL, react to committed data changes, and run scheduled jobs — its writes go through the app's own access rules",
4
+ description: "server-side backend.js file: answer HTTP/webhook requests at the app's /_api URL, react to committed data changes, and run scheduled jobs — its writes go through the app's own access rules; sends a record on to another service through a Zapier Catch Hook when the person asks for Zapier",
5
+ cues: ["zapier", "catch hook"],
5
6
  };
6
7
  //# sourceMappingURL=backend.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"backend.js","sourceRoot":"","sources":["../../jsr/llms/backend.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,MAAM,aAAa,GAAc;IACtC,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,YAAY;IACnB,WAAW,EACT,8LAA8L;CACjM,CAAC"}
1
+ {"version":3,"file":"backend.js","sourceRoot":"","sources":["../../jsr/llms/backend.ts"],"names":[],"mappings":"AAUA,MAAM,CAAC,MAAM,aAAa,GAAc;IACtC,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,YAAY;IACnB,WAAW,EACT,gSAAgS;IAMlS,IAAI,EAAE,CAAC,QAAQ,EAAE,YAAY,CAAC;CAC/B,CAAC"}
package/llms/backend.md CHANGED
@@ -231,12 +231,17 @@ const gh = await ctx.github.fetch("/repos/acme/site/issues", { method: "POST", b
231
231
  reads it unfiltered, anonymous callers included, and owes the authorization
232
232
  check itself. Nothing changes for writes, or for any lane or database you
233
233
  didn't name.
234
- - `ctx.secrets` carries the owner's per-vibe secrets, set in the vibe's settings
235
- page or with `vibes-diy secrets set KEY`. Use it to verify inbound credentials —
236
- a webhook's shared secret or token — or to authenticate outbound calls
237
- (`Authorization: Bearer ${ctx.secrets.KEY}` to an admitted API). Never write a
238
- value anywhere users can read it (a doc, a response body). Only the owner can
239
- set or rotate secrets; handlers see the current values on every invocation.
234
+ - `ctx.secrets` carries the **owner's own** per-vibe secrets, set in the vibe's
235
+ settings page or with `vibes-diy secrets set KEY`. Use it to verify inbound
236
+ credentials — a webhook's shared secret or token — or to authenticate outbound
237
+ calls to a service the owner holds a key for (`Authorization: Bearer
238
+ ${ctx.secrets.KEY}` to an admitted API). Never write a value anywhere users
239
+ can read it (a doc, a response body). Only the owner can set or rotate
240
+ secrets; handlers see the current values on every invocation. **A visitor's
241
+ own account (their GitHub, Threads, Instagram) is never a secret and never a
242
+ pasted token — it is a connection**: declare it in `config.connections`, let
243
+ the platform hold the credential, and read it through `@vibes.diy/social` or
244
+ `ctx.connections` (the `connections` skill).
240
245
  - **Make every outbound HTTP call with `ctx.fetch(...)`, never a bare `fetch(...)`.**
241
246
  It takes the same arguments as the web `fetch` and returns a `Response`. Why not
242
247
  bare `fetch`: a backend that exports a `fetch` handler creates a module-scoped
@@ -268,6 +273,45 @@ const gh = await ctx.github.fetch("/repos/acme/site/issues", { method: "POST", b
268
273
  CORS headers (`api.github.com` is seeded; additions are a PR to the platform
269
274
  repo).
270
275
 
276
+ **Sending a record to another service goes through Zapier.**
277
+ `hooks.zapier.com` is on that list for a `POST` to a Catch Hook, so an app can
278
+ hand a new record to any tool Zapier reaches — Clio, Slack, Google Sheets,
279
+ email — without holding that tool's credentials. The owner makes a Zap whose
280
+ trigger is "Webhooks by Zapier → Catch Hook", saves the hook URL as a secret
281
+ (`vibes-diy secrets set ZAPIER_HOOK_URL ...`), and the backend posts JSON to
282
+ `ctx.secrets.ZAPIER_HOOK_URL`. The URL **is** the credential: never write it
283
+ into a document or a response body. Treat any 2xx as accepted and mark the
284
+ record sent; on anything else **throw** — a handler that returns normally has
285
+ acknowledged the event, so the platform will not call it again for that write,
286
+ while a thrown `onChange` is re-delivered with backoff (a handful of tries over
287
+ a few minutes) and a run that never succeeds lands in the app's logs.
288
+
289
+ **Zapier is the last door, never the first.**
290
+ Reach for it only when the person names Zapier — a Zap, a Catch Hook, a hook URL
291
+ they already made — or when the destination has no other admitted door. A public
292
+ read, a CORS-open endpoint, the connected GitHub repo, or simply keeping the
293
+ record in this app and showing it on the owner's screen all come first, and a
294
+ record the app already holds needs no hook at all. On a build that was just made,
295
+ say what the app does on its own and leave the Zap as a next step the owner can
296
+ ask for.
297
+
298
+ ```js
299
+ export async function onChange(event, ctx) {
300
+ const doc = event.doc;
301
+ if (event.deleted || doc.type !== "intake" || doc.sentToZapier) return;
302
+ const res = await ctx.fetch(ctx.secrets.ZAPIER_HOOK_URL, {
303
+ method: "POST",
304
+ headers: { "content-type": "application/json" },
305
+ body: JSON.stringify({ type: doc.type, name: doc.name, email: doc.email }),
306
+ });
307
+ if (!res.ok) {
308
+ ctx.log("warn", "zapier hook refused", { status: res.status });
309
+ throw new Error(`zapier hook ${res.status}`); // re-delivered with backoff
310
+ }
311
+ await ctx.db.put({ ...doc, sentToZapier: true }, { db: event.dbName });
312
+ }
313
+ ```
314
+
271
315
  A call outside all three returns
272
316
  `403 {"vibesEgressDenied":true,"gate":"floor"|"owner-blocked"|"cors"|"read-rate"|"rate-limit","host":...}`,
273
317
  and `gate` says which lane refused: `cors` is a write or a credentialed call to
@@ -0,0 +1,2 @@
1
+ import type { LlmConfig } from "./types.js";
2
+ export declare const connectionsConfig: LlmConfig;
@@ -0,0 +1,23 @@
1
+ export const connectionsConfig = {
2
+ name: "connections",
3
+ label: "Connections",
4
+ description: "Connect a visitor's own GitHub, Threads or Instagram account with one click and read it from backend.js — config.connections, useConnection, @vibes.diy/social",
5
+ cues: [
6
+ "connect my",
7
+ "sign in with github",
8
+ "sign in with instagram",
9
+ "sign in with threads",
10
+ "my instagram",
11
+ "my threads",
12
+ "my github",
13
+ "my posts",
14
+ "my followers",
15
+ "social media login",
16
+ "instagram",
17
+ "threads",
18
+ "github account",
19
+ ],
20
+ importModule: "use-vibes",
21
+ importName: "useConnection",
22
+ };
23
+ //# sourceMappingURL=connections.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connections.js","sourceRoot":"","sources":["../../jsr/llms/connections.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,iBAAiB,GAAc;IAC1C,IAAI,EAAE,aAAa;IACnB,KAAK,EAAE,aAAa;IACpB,WAAW,EACT,gKAAgK;IAClK,IAAI,EAAE;QACJ,YAAY;QACZ,qBAAqB;QACrB,wBAAwB;QACxB,sBAAsB;QACtB,cAAc;QACd,YAAY;QACZ,WAAW;QACX,UAAU;QACV,cAAc;QACd,oBAAoB;QACpB,WAAW;QACX,SAAS;QACT,gBAAgB;KACjB;IACD,YAAY,EAAE,WAAW;IACzB,UAAU,EAAE,eAAe;CAC5B,CAAC"}
@@ -0,0 +1,151 @@
1
+ # Connections — a visitor's own account, spent by your app
2
+
3
+ A **connection** lets a visitor link their own third-party account (GitHub,
4
+ Threads, Instagram) to your app in one click, and lets your `backend.js` read
5
+ from that account as them. The platform shows the consent screen, runs the
6
+ sign-in, holds the credential, refreshes it, and warns the person before it
7
+ expires. Your app contains **no** client id, secret, redirect route, token
8
+ field, or refresh code.
9
+
10
+ **A visitor's account is never a pasted token, a `ctx.secrets` value, or a
11
+ document — the platform holds the credential and your code never sees it.**
12
+ `ctx.secrets` is for the _owner's own_ API keys (a weather API, a webhook
13
+ shared secret). Anything that belongs to the person using the app is a
14
+ connection. Never render an input that asks for an access token, and never
15
+ send anyone to a developer console to mint one — that is the flow this exists
16
+ to replace.
17
+
18
+ ## When to use it — signal words
19
+
20
+ "connect my Instagram", "sign in with GitHub", "my Threads posts", "show my
21
+ profile from …", "which of my videos got the most likes", "my followers on …".
22
+ A request that names one of the providers below and wants **their** data is a
23
+ connection. A request for public data nobody has to log in for (a public feed,
24
+ a public profile) is an ordinary `ctx.fetch` and needs no connection.
25
+
26
+ ## The three moving parts
27
+
28
+ **Declare the provider in `config.connections`, render the Connect button from
29
+ `useConnection`, and read the account through `@vibes.diy/social` or
30
+ `ctx.connections` in `backend.js`.** All three, every time.
31
+
32
+ ### 1. Declare it — `backend.js`
33
+
34
+ ```js
35
+ export const config = {
36
+ connections: [
37
+ {
38
+ provider: "instagram",
39
+ scopes: ["instagram_business_basic"],
40
+ why: "Rank your recent posts by likes and comments.",
41
+ },
42
+ ],
43
+ };
44
+ ```
45
+
46
+ `provider` and `scopes` are checked when the app is pushed; an unknown value
47
+ refuses the push. `why` is **required** and is shown to the visitor verbatim on
48
+ the consent card, quoted as the app's own words — write it for them ("Show your
49
+ GitHub profile next to your entries."), never as a note to yourself.
50
+
51
+ | provider | scope | what it buys (read-only) |
52
+ | ----------- | -------------------------- | ----------------------------------------------------------- |
53
+ | `github` | `read:user` | the account's profile (`github(ctx).me()`) |
54
+ | `threads` | `threads_basic` | the account's profile and its own posts |
55
+ | `instagram` | `instagram_business_basic` | the account's profile and its own media with like/comment counts |
56
+
57
+ Every connection is **read-only today**: nothing posts, replies, or writes on
58
+ the person's behalf, and there is no scope for that. Instagram views and plays
59
+ (the `insights` edge) are not available yet either. Build the app around what
60
+ the table gives you; do not add a token field for the rest.
61
+
62
+ ### 2. Ask for it — `App.jsx`
63
+
64
+ ```jsx
65
+ import { useConnection } from "use-vibes";
66
+
67
+ function ConnectInstagram() {
68
+ const { state, consented, accountLabel, link, refresh } = useConnection("instagram");
69
+ if (state === "pending") return null; // still resolving — never flash a Connect button
70
+ if (state === "absent" || !consented)
71
+ return <button onClick={link}>Connect Instagram</button>;
72
+ if (state === "dead") return <p>Your Instagram connection expired. Reconnect it in Settings.</p>;
73
+ return <p>Connected as {accountLabel}</p>;
74
+ }
75
+ ```
76
+
77
+ `useConnection(provider)` returns `state` (`pending` | `absent` | `live` |
78
+ `expiring` | `dead`), `consented` (whether _this_ app has been approved — a
79
+ `live` account with `consented: false` is a one-click ask, not a full
80
+ sign-in), `accountLabel` and `expiresAt` for display, `link()` to open the
81
+ platform's consent card, and `refresh()` to re-read. `link()` resolves when the
82
+ flow **starts**, not when it finishes — poll `refresh()` a few times while the
83
+ surface is visible if you want to react to completion. Disconnecting lives in
84
+ the platform's Settings page, not in your app.
85
+
86
+ ### 3. Read it — `backend.js`
87
+
88
+ ```js
89
+ import { instagram, connectionState } from "@vibes.diy/social";
90
+
91
+ export async function fetch(request, ctx) {
92
+ const url = new URL(request.url);
93
+ if (url.pathname.endsWith("/top-posts")) {
94
+ const conn = await connectionState(ctx, "instagram");
95
+ if (conn.state !== "live") return Response.json({ connection: conn.state }, { status: 409 });
96
+ const ig = instagram(ctx);
97
+ const page = await ig.media({ limit: 50 });
98
+ const ranked = [...page.items].sort((a, b) => (b.likeCount ?? 0) - (a.likeCount ?? 0));
99
+ return Response.json({ posts: ranked.slice(0, 10) });
100
+ }
101
+ return new Response("not found", { status: 404 });
102
+ }
103
+ ```
104
+
105
+ `@vibes.diy/social` is the only import a `backend.js` can make. It wraps every
106
+ provider call as a typed request with a typed response, so there is no
107
+ `fields=` string to get wrong and no JSON key to guess:
108
+
109
+ - `connectionState(ctx, provider)` → `{ state: "live" | "needs_consent" | "dead" | "error" }`.
110
+ Spends nothing. `needs_consent` means render the Connect button; `dead` means
111
+ tell the person to reconnect in Settings; `error` carries a `reason`.
112
+ - `github(ctx).me()` → `{ id, login, name, bio, avatarUrl, htmlUrl }`.
113
+ - `threads(ctx).me()` → `{ id, username }`; `threads(ctx).posts({ limit, cursor })` →
114
+ `{ items: [{ id, text, permalink, timestamp, mediaType }], cursor? }`.
115
+ - `instagram(ctx).me()` → `{ id, username }`; `instagram(ctx).media({ limit, cursor })` →
116
+ `{ items: [{ id, caption, mediaType, permalink, timestamp, likeCount, commentsCount }], cursor? }`
117
+ (`likeCount` is `null` when the person hides likes, never `0`).
118
+
119
+ Page with `cursor` until it is absent. A provider error surfaces as a thrown
120
+ `ProviderResponseError` with `status`; a connection problem surfaces as a
121
+ thrown error with `reason` (`needs_consent`, `connection_dead`,
122
+ `provider_not_declared`, …) — branch on `reason`, never on the message text.
123
+
124
+ Underneath the library sit the raw primitives, for a path the library does not
125
+ wrap: `await ctx.connections.fetch(provider, path)` returns a normal `Response`
126
+ with the credential attached by the platform, and
127
+ `await ctx.connections.status(provider)` returns
128
+ `{ state, consented, expiresAt?, accountLabel? }` without spending a call.
129
+
130
+ **Whose account?** In a `fetch` handler it is the signed-in visitor who called
131
+ the route; in `scheduled` it is the app owner's. A `scheduled` tick can only
132
+ ever read the owner's connection — an app that ranks _each visitor's_ posts
133
+ does that work in `fetch`, when they are there.
134
+
135
+ ## Ticks that have nothing to do
136
+
137
+ **A `scheduled` handler that finds nothing to do says so with `ctx.log`, so an
138
+ idle tick is never silent.** A tick that ranks posts every 15 minutes but has
139
+ no connection yet should log `"instagram not connected, nothing to rank"` and
140
+ return — 155 silent ticks look identical to 155 working ones, and nobody can
141
+ tell the app is waiting.
142
+
143
+ ## What not to build
144
+
145
+ - No token, API key, password or "paste your login" input, for any provider.
146
+ - No `ctx.secrets.INSTAGRAM_TOKEN`-style keys for a visitor's account.
147
+ - No OAuth redirect route, no `client_id`, no `refresh_access_token` loop —
148
+ the platform owns all of it.
149
+ - No `Authorization: Bearer …` header built in app code; `ctx.connections`
150
+ attaches the credential.
151
+ - No posting: `POST`/`PUT`/`DELETE` through `ctx.connections` is refused.
package/llms/index.d.ts CHANGED
@@ -12,5 +12,6 @@ export { useViewerConfig } from "./use-viewer.js";
12
12
  export { useVibeConfig } from "./use-vibe.js";
13
13
  export { createVibeConfig } from "./create-vibe.js";
14
14
  export { accessConfig } from "./access.js";
15
+ export { connectionsConfig } from "./connections.js";
15
16
  export type { LlmConfig } from "./types.js";
16
- export declare const allConfigs: readonly [import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig];
17
+ export declare const allConfigs: readonly [import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig, import("./types.js").LlmConfig];
package/llms/index.js CHANGED
@@ -12,6 +12,7 @@ import { useViewerConfig } from "./use-viewer.js";
12
12
  import { useVibeConfig } from "./use-vibe.js";
13
13
  import { createVibeConfig } from "./create-vibe.js";
14
14
  import { accessConfig } from "./access.js";
15
+ import { connectionsConfig } from "./connections.js";
15
16
  export { backendConfig } from "./backend.js";
16
17
  export { calendarConfig } from "./calendar.js";
17
18
  export { callaiConfig } from "./callai.js";
@@ -26,6 +27,7 @@ export { useViewerConfig } from "./use-viewer.js";
26
27
  export { useVibeConfig } from "./use-vibe.js";
27
28
  export { createVibeConfig } from "./create-vibe.js";
28
29
  export { accessConfig } from "./access.js";
30
+ export { connectionsConfig } from "./connections.js";
29
31
  export const allConfigs = [
30
32
  callaiConfig,
31
33
  imageGenConfig,
@@ -41,5 +43,6 @@ export const allConfigs = [
41
43
  backendConfig,
42
44
  calendarConfig,
43
45
  accessConfig,
46
+ connectionsConfig,
44
47
  ];
45
48
  //# sourceMappingURL=index.js.map
package/llms/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../jsr/llms/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAI3C,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,YAAY;IACZ,cAAc;IACd,cAAc;IACd,QAAQ;IACR,aAAa;IACb,WAAW;IACX,eAAe;IACf,WAAW;IACX,eAAe;IACf,aAAa;IACb,gBAAgB;IAChB,aAAa;IACb,cAAc;IACd,YAAY;CACJ,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../jsr/llms/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAErD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAIrD,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,YAAY;IACZ,cAAc;IACd,cAAc;IACd,QAAQ;IACR,aAAa;IACb,WAAW;IACX,eAAe;IACf,WAAW;IACX,eAAe;IACf,aAAa;IACb,gBAAgB;IAChB,aAAa;IACb,cAAc;IACd,YAAY;IACZ,iBAAiB;CACT,CAAC"}
package/llms/three-js.md CHANGED
@@ -361,12 +361,21 @@ function onPointerDown(event) {
361
361
  ## Importing three
362
362
 
363
363
  A bare package name at the top of the file resolves through the platform's esm.sh
364
- map, and a full esm.sh URL loads too — either form also works with
365
- `await import()`. The bare form follows whatever version the platform serves,
366
- which moves as esm.sh publishes; a version-pinned URL such as
364
+ map, and a full esm.sh URL loads too. The bare form follows whatever version the
365
+ platform serves, which moves as esm.sh publishes; a version-pinned URL such as
367
366
  `https://esm.sh/three@0.160.0` is the lever for holding a scene on one release of
368
367
  the library.
369
368
 
369
+ `await import()` defers a library until it is needed. Give it a full esm.sh URL,
370
+ which the browser resolves on its own — for a third-party library like three,
371
+ that is the form that both resolves and defers.
372
+
373
+ A bare specifier resolves through the page's import map, which carries the
374
+ packages the platform serves plus the ones your files import at the top. So a
375
+ bare name loads on demand for a platform package; for three, a name your file
376
+ also imports at the top is already loaded by the time the call runs, and a name
377
+ that appears only inside `await import()` has nothing to resolve through.
378
+
370
379
  ## Animation System
371
380
 
372
381
  ### Animation Mixer
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibes.diy/prompts",
3
- "version": "14.1.1",
3
+ "version": "14.1.3",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "exports": {
@@ -34,9 +34,9 @@
34
34
  "license": "Apache-2.0",
35
35
  "dependencies": {
36
36
  "@adviser/cement": "~0.5.34",
37
- "@vibes.diy/call-ai-v2": "14.1.1",
38
- "@vibes.diy/identity": "14.1.1",
39
- "@vibes.diy/use-vibes-types": "14.1.1",
37
+ "@vibes.diy/call-ai-v2": "14.1.3",
38
+ "@vibes.diy/identity": "14.1.3",
39
+ "@vibes.diy/use-vibes-types": "14.1.3",
40
40
  "arktype": "~2.2.3",
41
41
  "json-schema-faker": "~0.6.3"
42
42
  },
@@ -25,7 +25,7 @@ You are an AI assistant tasked with creating React components. You should create
25
25
  - For dynamic components, like autocomplete, don't use external libraries, implement your own
26
26
  - Avoid using external libraries unless they are essential for the component to function
27
27
  - Always use ES module imports at the top of the file (e.g. `import React, { useState } from "react"`). Never reference React or other libraries as globals.
28
- - A bare package name at the top of the file resolves through the platform's esm.sh map, and a full esm.sh URL such as `https://esm.sh/three@0.160.0` loads too. Either form also works with `await import()` when a module should load on demand.
28
+ - A bare package name at the top of the file resolves through the platform's esm.sh map, and a full esm.sh URL such as `https://esm.sh/three@0.160.0` loads too. To defer a library until it is needed, `await import()` the full URL.
29
29
  - Your file MUST use `export default function App()` — the runtime loads it as an ES module and imports the default export.
30
30
  - Structure your component code in this order: (1) hooks and document shapes, (2) event handlers, (3) classNames object, (4) JSX return. ClassNames go right before JSX so they are close to where they are used.
31
31
  - Use Fireproof for data persistence
@@ -25,7 +25,7 @@ You are an AI assistant tasked with creating React components. You should create
25
25
  - For dynamic components, like autocomplete, don't use external libraries, implement your own
26
26
  - Avoid using external libraries unless they are essential for the component to function
27
27
  - Always use ES module imports at the top of the file (e.g. `import React, { useState } from "react"`). Never reference React or other libraries as globals.
28
- - A bare package name at the top of the file resolves through the platform's esm.sh map, and a full esm.sh URL such as `https://esm.sh/three@0.160.0` loads too. Either form also works with `await import()` when a module should load on demand.
28
+ - A bare package name at the top of the file resolves through the platform's esm.sh map, and a full esm.sh URL such as `https://esm.sh/three@0.160.0` loads too. To defer a library until it is needed, `await import()` the full URL.
29
29
  - Your file MUST use `export default function App()` — the runtime loads it as an ES module and imports the default export.
30
30
  - Structure your component code in this order: (1) hooks and document shapes, (2) event handlers, (3) classNames object, (4) JSX return. ClassNames go right before JSX so they are close to where they are used.
31
31
  - Use Fireproof for data persistence
package/system-prompt.md CHANGED
@@ -25,7 +25,7 @@ You are an AI assistant tasked with creating React components. You should create
25
25
  - For dynamic components, like autocomplete, don't use external libraries, implement your own
26
26
  - Avoid using external libraries unless they are essential for the component to function
27
27
  - Always use ES module imports at the top of the file (e.g. `import React, { useState } from "react"`). Never reference React or other libraries as globals.
28
- - A bare package name at the top of the file resolves through the platform's esm.sh map, and a full esm.sh URL such as `https://esm.sh/three@0.160.0` loads too. Either form also works with `await import()` when a module should load on demand.
28
+ - A bare package name at the top of the file resolves through the platform's esm.sh map, and a full esm.sh URL such as `https://esm.sh/three@0.160.0` loads too. To defer a library until it is needed, `await import()` the full URL.
29
29
  - Your file MUST use `export default function App()` — the runtime loads it as an ES module and imports the default export.
30
30
  - Structure your component code in this order: (1) hooks and document shapes, (2) event handlers, (3) classNames object, (4) JSX return. ClassNames go right before JSX so they are close to where they are used. Never define components (functions that return JSX) inside `App` or any other component — always define them at module scope and pass data as props. Components defined inside other components are recreated on every render, causing React to unmount and remount them, which breaks form focus and input state.
31
31
  - Use Fireproof for data persistence