@popmelt.com/core 0.21.1 → 0.22.0

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/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  # Popmelt
6
6
 
7
- Popmelt is a tiny, powerful design layer for humans and AI. Turn any React repo into a design sandbox with the AI tools you already love.
7
+ Popmelt is a tiny, powerful design layer for humans and AI. Turn your local web app into a design sandbox with the AI tools you already love.
8
8
 
9
9
  Run your app locally and Popmelt appears as a toolbar in the browser. Point to any part of your UI, describe what you have in mind, or steer the design directly. Work through the result with Codex or Claude Code in a thread right on the page. Stack as many threads as you want so different ideas can move forward without losing their context.
10
10
 
@@ -14,9 +14,9 @@ Runs locally, learns naturally. Your annotations, screenshots, threads, and Impr
14
14
 
15
15
  ## What you need
16
16
 
17
- - A local React project
18
- - Node.js 18 or newer
19
- - React 18 or newer
17
+ - A local web project: React is the established integration; vanilla HTML/JS with Vite, Vue, Nuxt, Svelte/SvelteKit, and Astro have experimental DOM integrations
18
+ - Node.js 18 or newer, or a newer version required by your framework
19
+ - React 18 or newer for a React host; non-React integrations still require the renderer peers listed in their setup instructions
20
20
  - The Codex CLI or Claude Code CLI installed, available in your terminal, and signed in
21
21
 
22
22
  The Codex and Claude Code desktop apps work too. Open your local app in their built-in browser and use Popmelt there just as you would in any other browser. The corresponding CLI still needs to be installed and signed in because it runs the threads.
@@ -27,7 +27,11 @@ From inside your project, give your coding agent this prompt:
27
27
 
28
28
  > Let's install `@popmelt.com/core` in this project
29
29
 
30
- Your agent can detect the framework, add the provider and integration, and preserve your existing development command. Restart the development server when it is finished.
30
+ Your agent can detect the framework, add the matching integration, and preserve your existing development command. Restart the development server when it is finished.
31
+
32
+ For the installing agent: finish with the [installation checks](#verify-the-installation), including the applicable ambient integration below. Report what is verified and any remaining step; adding dependencies alone is not a ready-to-use installation.
33
+
34
+ For manual instructions, see [Manual setup](#manual-setup). Experimental non-React integrations support DOM annotations and style edits, but do not provide React-specific component hierarchy or library previews.
31
35
 
32
36
  To add local object recognition to an existing Popmelt project, use:
33
37
 
@@ -36,168 +40,16 @@ To add local object recognition to an existing Popmelt project, use:
36
40
  The integration installs See and adds it to the existing Core provider's
37
41
  `extensions` array without replacing Core.
38
42
 
39
- ### Experimental standalone browser entry
40
-
41
- The first framework-independent integration is available at `@popmelt.com/core/browser`.
42
- It mounts Popmelt beside the host application, without JSX or a React provider in
43
- your application code. React still renders Popmelt internally; the package's
44
- existing React, React DOM, and Lucide React peer dependencies are still required.
45
- This is an experimental DOM integration, not a claim of Vue/Svelte/Angular feature
46
- parity. React-specific component hierarchy and library previews are not supplied
47
- for other frameworks yet.
43
+ ## Try your first change
48
44
 
49
- For a Vite host, keep the existing `@popmelt.com/core/vite` bridge plugin, then use
50
- this in your browser entry:
45
+ Pick one thing in your own app that you already want to improve:
51
46
 
52
- ```js
53
- if (import.meta.env.DEV) {
54
- const { mountPopmelt } = await import('@popmelt.com/core/browser');
55
- const popmelt = await mountPopmelt({ enabled: true });
56
- import.meta.hot?.dispose(() => popmelt.unmount());
57
- }
58
- ```
59
-
60
- Enablement is explicit: omitted/false `enabled` or a server-side call is a no-op
61
- and does not load the renderer. `unmount()` removes the UI and releases its
62
- subscriptions without deleting saved annotations or cancelling bridge jobs.
63
- Call it before mounting again; simultaneous integrations on one page are rejected.
64
- Use either this entry or `PopmeltProvider`, not both. Pass `bridgeUrl`, `projectId`,
65
- or a host-router `navigate` callback when these are not supplied by your integration.
66
- Explicit bridge/project options take effect before startup and restore prior
67
- host bindings when unmounted; no manual browser-global assignment is needed.
68
-
69
- For Vue, mount your app normally and mount Popmelt alongside it, not inside the
70
- Vue component tree. Keep your Vue Vite plugin and add `popmelt()` to that same
71
- plugin list. Connect the returned `unmount()` to your app's teardown (for example,
72
- `app.onUnmount(() => popmelt.unmount())` in Vue 3.5+). With Vue Router, pass
73
- `navigate: (url) => router.push(url)`; no router dependency is required by Core.
47
+ 1. Open your local app and double-tap Command on macOS or Control on Windows/Linux to open Popmelt.
48
+ 2. Choose Chat (`C`), click the element, and describe a concrete change—for example, “Give this card more breathing room without changing the text size.”
49
+ 3. Press Command/Control+Enter to send it, then review the result on the page.
50
+ 4. Refine it in the same thread: “A little less padding, and keep the button aligned with the text.”
74
51
 
75
- The plain HTML/JS fixture at `e2e/fixtures/browser` and Vue fixture at
76
- `e2e/fixtures/vue` share a browser workflow: author and recover an annotation
77
- draft through target replacement and page reload, submit it, receive streamed
78
- and final replies, and continue the same thread after another target replacement.
79
- Both verify durable thread history, stage a Steer edit, select Chat through the
80
- toolbar, and recover the exact edited style and completed thread after reload.
81
- Vanilla replacement creates a fresh DOM node without copying Popmelt attributes
82
- or inline styles; Vue uses keyed rerenders. Both verify teardown/remount releases
83
- Popmelt's UI and connection while preserving the host DOM and input state.
84
-
85
- Vue additionally exercises real Vite SFC template/script hot updates and Vue
86
- Router navigation/back. Hot updates must retain the same Popmelt renderer and
87
- bridge connection, without a full-page reload. The tests edit temporary fixture
88
- copies, not repository files.
89
- Only the external provider process is controlled; HTTP, SSE, queue dispatch, and
90
- durable thread storage use the real bridge. It does not run your installed agent.
91
- Run both fixtures after building:
92
-
93
- ```sh
94
- pnpm run build
95
- pnpm exec vitest run --config vitest.browser.config.ts e2e/browser-mount.browser.ts
96
- ```
97
-
98
- For a fresh tarball installation with the real Vite bridge plugin, run
99
- `pnpm run vue:smoke`. This opt-in check needs npm registry access and Chrome
100
- (`CHROME_PATH` can override its location). It installs into a temporary Vue app,
101
- verifies bridge startup/shutdown and the toolbar, and checks that the
102
- development-only import is absent from production output. It does not publish
103
- or replace the separate package-size release gate.
104
-
105
- The plain HTML/JS fixture still uses Vite and installed npm peers; this does not
106
- validate a bundler-free script tag or arbitrary static-server integration.
107
- The journeys use stable target IDs and Chrome, not a cross-browser or arbitrary
108
- DOM-identity matrix. They validate the client-side DOM boundary, not Vue component
109
- metadata. Router guards, nested routes, and hash-history mode are
110
- not yet covered. Vue and its test tooling are development dependencies only;
111
- they do not add a second Popmelt UI implementation or become runtime dependencies.
112
-
113
- ### Experimental Nuxt integration
114
-
115
- For Nuxt 4.5.2, install Popmelt and its internal renderer peers as development
116
- dependencies, then add the module:
117
-
118
- ```sh
119
- npm install -D @popmelt.com/core react react-dom lucide-react
120
- ```
121
-
122
- ```ts
123
- // nuxt.config.ts
124
- export default defineNuxtConfig({
125
- modules: ['@popmelt.com/core/nuxt'],
126
- });
127
- ```
128
-
129
- No separate Vite plugin, React provider, or manual browser mount is needed.
130
- The module starts the local bridge in development and mounts Popmelt after Nuxt
131
- has mounted the client app. It uses Nuxt's router for navigation and releases
132
- its bridge lease on graceful shutdown. When Nuxt's CLI exits immediately, the
133
- existing lease-expiry cleanup stops an idle bridge within about a minute.
134
- Production builds do not register the client
135
- plugin or start the bridge. Optional `popmelt` settings are `port`, `projectRoot`,
136
- and `bridge` (set `bridge: false` to disable automatic bridge startup).
137
-
138
- Run `pnpm run nuxt:smoke` with a Nuxt-compatible Node version (22.19+, 24.11+,
139
- or 26+) to check a fresh tarball installation. It requires npm registry access
140
- and Chrome, like the Vue smoke check. The isolated fixture checks SSR/hydration,
141
- thread recovery through navigation/back and template/script HMR, and production
142
- exclusion. It also exercises annotation submission, streaming replies, same-thread
143
- follow-ups, and staged style recovery through routing and reload. Provider responses
144
- are controlled in that journey; it never launches your coding agent.
145
- Nuxt 3, component metadata, and advanced router configurations are not yet tested.
146
-
147
- ### Experimental Svelte and SvelteKit
148
-
149
- Tested with Svelte 5.57.0, SvelteKit 2.70.3, and Vite 8.2.2. Install the same
150
- development dependencies used by the standalone entry:
151
-
152
- ```sh
153
- npm install -D @popmelt.com/core react react-dom lucide-react
154
- ```
155
-
156
- Keep your existing Vite configuration and add `popmelt()` alongside `sveltekit()`
157
- (or `svelte()` for a plain Svelte app):
158
-
159
- ```js
160
- import { sveltekit } from '@sveltejs/kit/vite';
161
- import { popmelt } from '@popmelt.com/core/vite';
162
-
163
- export default { plugins: [sveltekit(), popmelt()] };
164
- ```
165
-
166
- Mount once in your root `+layout.svelte`, without replacing its existing content:
167
-
168
- ```svelte
169
- <script>
170
- import { onMount } from 'svelte';
171
- import { goto } from '$app/navigation';
172
- import { bridgeUrl, projectId } from 'virtual:popmelt/bridge';
173
-
174
- onMount(() => {
175
- if (!import.meta.env.DEV || !bridgeUrl || !projectId) return;
176
- let disposed = false;
177
- const pending = import('@popmelt.com/core/browser').then(({ mountPopmelt }) => {
178
- if (!disposed) return mountPopmelt({ enabled: true, bridgeUrl, projectId, navigate: goto });
179
- });
180
- void pending.catch(console.error);
181
- return () => {
182
- disposed = true;
183
- void pending.then((handle) => handle?.unmount()).catch(() => {});
184
- };
185
- });
186
- </script>
187
- ```
188
-
189
- For plain Svelte, place this in `App.svelte` and omit the `goto` import and
190
- `navigate` option, or supply your own router callback. TypeScript projects can
191
- add `/// <reference types="@popmelt.com/core/vite/client" />` to their app's
192
- ambient declarations for the virtual module types.
193
-
194
- This setup is development-only, including during SSR. It does not provide
195
- Svelte component metadata or framework-specific library previews. Advanced
196
- Kit routing, form actions, and deployment adapters beyond Node are not yet tested.
197
- Run `pnpm run svelte:smoke` with Node 24.11+, npm registry access, and Chrome
198
- to check fresh Svelte and SvelteKit installs; it does not launch your coding agent.
199
- The SvelteKit check also runs the shared annotation/reply/style-edit journey,
200
- including same-thread continuation and recovery after navigation and reload.
52
+ Questions, ideas, or issues? [Email](mailto:reb@popmelt.com) or [DM on X](https://x.com/bekhovsgun).
201
53
 
202
54
  ## Use Popmelt
203
55
 
@@ -273,16 +125,70 @@ Archiving hides a thread from the active canvas without deleting its stored conv
273
125
 
274
126
  ## Ambient Popmelt in Codex
275
127
 
276
- Popmelt can also pair ordinary project-bound Codex conversations with the same local project memory. Install the bundled Codex plugin once:
128
+ Popmelt can also pair ordinary project-bound Codex conversations with the same local project memory. On supported Codex desktop or CLI surfaces, include this in setup unless the user wants toolbar-only use or has disabled the integration.
129
+
130
+ The bundled `popmelt-core` plugin installs the skill, MCP tools, and lifecycle hooks together; do not install separate copies of the skill or hooks. This is a personal installation, not just a project dependency. Explain that the hooks supply project context and record project-bound turns, and obtain any required permission before changing personal configuration. Preserve unrelated plugins, settings, and explicit opt-outs. See [OpenAI's plugin guidance](https://learn.chatgpt.com/docs/plugins) for supported surfaces and hook trust.
131
+
132
+ From the project, inspect the existing installation first:
133
+
134
+ ```bash
135
+ npx @popmelt.com/core codex status
136
+ ```
137
+
138
+ If missing or needing an update, install the bundled plugin:
277
139
 
278
140
  ```bash
279
141
  npx @popmelt.com/core codex install
280
142
  ```
281
143
 
282
- Restart Codex completely, then begin a new conversation in the project. Popmelt will orient the conversation automatically when one store applies. If multiple stores apply, Codex will ask you to choose one writable primary store and any read-only mounted stores; it never infers write authority from directory nesting, recency, or store size.
144
+ Follow the install command's restart instructions after ongoing work finishes, then begin a new Codex conversation in the project. Do not close active sessions automatically. A result of `files_installed` means plugin registration is incomplete; `installed` or `update_ready` still requires the reported restart and new conversation. Popmelt will orient the conversation automatically when one store applies. If multiple stores apply, Codex will ask you to choose one writable primary store and any read-only mounted stores; it never infers write authority from directory nesting, recency, or store size.
145
+
146
+ After restart, check that the plugin is registered and enabled and that the new session exposes the Popmelt skill, tools, and ambient context. Registration alone does not prove the hooks have loaded. If you cannot inspect the new session, report ambient activation as unverified rather than reinstalling repeatedly.
147
+
148
+ This installer is Codex-specific. Claude Code toolbar threads do not need it. For ordinary local Claude conversations, see the experimental native plugin below; do not copy Codex hooks into Claude settings.
283
149
 
284
150
  Ambient capture stays out of toolbar-owned runs, so opening a thread from the in-page toolbar still produces exactly one toolbar job. Popmelt tools remain available for intentional context expansion, decision review, and Imprint grounding.
285
151
 
152
+ ### Ambient Popmelt in Claude Code (local preview)
153
+
154
+ The package also includes an experimental native Claude plugin with the Popmelt skill, local MCP tools, and prompt/tool/completion hooks. For local Claude Code, inspect first:
155
+
156
+ ```bash
157
+ npx @popmelt.com/core claude status
158
+ ```
159
+
160
+ With the user's permission for a personal plugin installation, install or update the bundle:
161
+
162
+ ```bash
163
+ npx @popmelt.com/core claude install
164
+ ```
165
+
166
+ The installer registers `popmelt-core@popmelt-local` through Claude's native plugin commands, using a local marketplace under `CLAUDE_CONFIG_DIR` (or `~/.claude`). It preserves disabled plugins, refuses conflicting sources/scopes, and never writes execution permission settings. Partial registration is reported separately from success. After ongoing work finishes, reload plugins or start a new Claude Code session; don't interrupt active work automatically.
167
+
168
+ Then check the setup without launching an agent request or restarting a bridge:
169
+
170
+ ```bash
171
+ npx @popmelt.com/core claude doctor
172
+ ```
173
+
174
+ `infrastructureReady` covers registration, locally reported sign-in, and the identity/version of an already-running project bridge. `sessionVerified` requires recent prompt-hook, MCP-tool, and completion-hook observations from one session of that installed plugin version. `ok` requires both. Observations expire after 24 hours and do not prove the session is still live or that Claude read the skill. Missing observations are **unverified**, not a reason to reinstall repeatedly. Bridge probes cover the standard 1111–1120 range; verify a custom port separately.
175
+
176
+ In a multi-store workspace, diagnostics honor the same saved human-selected primary and read-only mounts as ambient sessions. If that choice is missing, invalid, or no longer matches the available stores, orientation remains required; diagnostics never pick a replacement or rewrite the saved choice.
177
+
178
+ For a one-session trial without changing personal settings, you can instead load the packaged plugin directly:
179
+
180
+ ```bash
181
+ claude --plugin-dir ./node_modules/@popmelt.com/core/claude-plugin
182
+ ```
183
+
184
+ This uses Claude's native [plugin](https://code.claude.com/docs/en/plugins-reference) and [hook](https://code.claude.com/docs/en/hooks) formats. Respect Claude's workspace trust, plugin/MCP consent, and existing permission settings. The plugin adds no permission policy or execution grants.
185
+
186
+ Check that the session exposes the Popmelt skill and tools and that the next ordinary prompt receives ambient context. Use the exact tool-session ID it supplies. Store orientation, mounted read stores, focus inspection, and Imprint use the same bridge as Codex. Toolbar-owned Claude runs skip ambient capture.
187
+
188
+ The adapter keeps a private, bounded current-turn journal locally, correlates Claude's hooks, and retries against the same durable turn after bridge recovery. Missing/failed hooks remain unverified; API-error and session-end events are not recorded as successful completions. Concurrent processes using the same native Claude session are not supported in this preview. No transcript copy is read or retained.
189
+
190
+ Local sessions in Desktop’s Code tab are expected to work through shared configuration. Chat, Cowork, and cloud sessions are outside this preview’s scope. Native plugin validation and isolated lifecycle checks do not prove activation in the user's session. Let the user choose the next real task; do not submit an automatic test annotation or paid request to manufacture readiness. The toolbar works independently.
191
+
286
192
  ### Point and discuss
287
193
 
288
194
  With your app running and the Popmelt canvas active, point at an element and ask Codex, “What am I pointing at?” You can also ask it to inspect a comment or explain what was discussed in an existing thread, without pointing at that item first.
@@ -300,6 +206,8 @@ npx @popmelt.com/core codex status
300
206
  npx @popmelt.com/core codex doctor
301
207
  ```
302
208
 
209
+ `status` reports plugin registration and project discovery. `doctor` can start the local project bridge and check its health, so run it only for the intended project when starting that service is appropriate. Inspect the plugin fields and any project-selection warning as well as bridge health: a healthy bridge does not prove plugin activation, browser connectivity, or provider authentication.
210
+
303
211
  ## Imprint
304
212
 
305
213
  As you work, Popmelt recognizes choices that may be useful beyond the current thread and proposes an Imprint update:
@@ -314,7 +222,13 @@ Enter Imprint mode with `I` to inspect the project's current understanding, supp
314
222
 
315
223
  ## Manual setup
316
224
 
317
- Install Popmelt and its icon peer dependency:
225
+ For React, add the provider below and follow the [Next.js](#nextjs),
226
+ [Vite](#vite), or [other development server](#other-development-servers) instructions.
227
+ For experimental non-React hosts, use the [standalone entry for vanilla HTML/JS or Vue](#experimental-standalone-browser-entry),
228
+ [Nuxt module](#experimental-nuxt-integration), [Svelte/SvelteKit setup](#experimental-svelte-and-sveltekit),
229
+ or [Astro integration](#experimental-astro-integration) instead of adding a React provider.
230
+
231
+ For a React app, install Popmelt and its icon peer dependency:
318
232
 
319
233
  ```bash
320
234
  npm install @popmelt.com/core lucide-react
@@ -416,6 +330,125 @@ export default defineConfig({
416
330
  });
417
331
  ```
418
332
 
333
+ ### Experimental standalone browser entry
334
+
335
+ The first framework-independent integration is available at `@popmelt.com/core/browser`.
336
+ It mounts Popmelt beside the host application, without JSX or a React provider in
337
+ your application code. React still renders Popmelt internally; the package's
338
+ existing React, React DOM, and Lucide React peer dependencies are still required.
339
+ This is an experimental DOM integration, not a claim of Vue/Svelte/Angular feature
340
+ parity. React-specific component hierarchy and library previews are not supplied
341
+ for other frameworks yet.
342
+
343
+ For a Vite host, keep the existing `@popmelt.com/core/vite` bridge plugin, then use
344
+ this in your browser entry:
345
+
346
+ ```js
347
+ if (import.meta.env.DEV) {
348
+ const { mountPopmelt } = await import('@popmelt.com/core/browser');
349
+ const popmelt = await mountPopmelt({ enabled: true });
350
+ import.meta.hot?.dispose(() => popmelt.unmount());
351
+ }
352
+ ```
353
+
354
+ Enablement is explicit: omitted/false `enabled` or a server-side call is a no-op
355
+ and does not load the renderer. `unmount()` removes the UI and releases its
356
+ subscriptions without deleting saved annotations or cancelling bridge jobs.
357
+ Call it before mounting again; simultaneous integrations on one page are rejected.
358
+ Use either this entry or `PopmeltProvider`, not both. Pass `bridgeUrl`, `projectId`,
359
+ or a host-router `navigate` callback when these are not supplied by your integration.
360
+ Explicit bridge/project options take effect before startup and restore prior
361
+ host bindings when unmounted; no manual browser-global assignment is needed.
362
+
363
+ For Vue, mount your app normally and mount Popmelt alongside it, not inside the
364
+ Vue component tree. Keep your Vue Vite plugin and add `popmelt()` to that same
365
+ plugin list. Connect the returned `unmount()` to your app's teardown (for example,
366
+ `app.onUnmount(() => popmelt.unmount())` in Vue 3.5+). With Vue Router, pass
367
+ `navigate: (url) => router.push(url)`; no router dependency is required by Core.
368
+
369
+ The vanilla HTML/JS integration requires Vite and installed npm peers; a
370
+ bundler-free script tag or arbitrary static-server setup is not covered.
371
+ Vue Router guards, nested routes, and hash-history mode are not yet tested.
372
+
373
+ ### Experimental Nuxt integration
374
+
375
+ For Nuxt 4.5.2, install Popmelt and its internal renderer peers as development
376
+ dependencies, then add the module:
377
+
378
+ ```sh
379
+ npm install -D @popmelt.com/core react react-dom lucide-react
380
+ ```
381
+
382
+ ```ts
383
+ // nuxt.config.ts
384
+ export default defineNuxtConfig({
385
+ modules: ['@popmelt.com/core/nuxt'],
386
+ });
387
+ ```
388
+
389
+ No separate Vite plugin, React provider, or manual browser mount is needed.
390
+ The module starts the local bridge in development and mounts Popmelt after Nuxt
391
+ has mounted the client app. It uses Nuxt's router for navigation and releases
392
+ its bridge lease on graceful shutdown. When Nuxt's CLI exits immediately, the
393
+ existing lease-expiry cleanup stops an idle bridge within about a minute.
394
+ Production builds do not register the client
395
+ plugin or start the bridge. Optional `popmelt` settings are `port`, `projectRoot`,
396
+ and `bridge` (set `bridge: false` to disable automatic bridge startup).
397
+
398
+ Use a Nuxt-compatible Node version (22.19+, 24.11+, or 26+).
399
+ Nuxt 3, component metadata, and advanced router configurations are not yet tested.
400
+
401
+ ### Experimental Svelte and SvelteKit
402
+
403
+ Tested with Svelte 5.57.0, SvelteKit 2.70.3, and Vite 8.2.2. Install the same
404
+ development dependencies used by the standalone entry:
405
+
406
+ ```sh
407
+ npm install -D @popmelt.com/core react react-dom lucide-react
408
+ ```
409
+
410
+ Keep your existing Vite configuration and add `popmelt()` alongside `sveltekit()`
411
+ (or `svelte()` for a plain Svelte app):
412
+
413
+ ```js
414
+ import { sveltekit } from '@sveltejs/kit/vite';
415
+ import { popmelt } from '@popmelt.com/core/vite';
416
+
417
+ export default { plugins: [sveltekit(), popmelt()] };
418
+ ```
419
+
420
+ Mount once in your root `+layout.svelte`, without replacing its existing content:
421
+
422
+ ```svelte
423
+ <script>
424
+ import { onMount } from 'svelte';
425
+ import { goto } from '$app/navigation';
426
+ import { bridgeUrl, projectId } from 'virtual:popmelt/bridge';
427
+
428
+ onMount(() => {
429
+ if (!import.meta.env.DEV || !bridgeUrl || !projectId) return;
430
+ let disposed = false;
431
+ const pending = import('@popmelt.com/core/browser').then(({ mountPopmelt }) => {
432
+ if (!disposed) return mountPopmelt({ enabled: true, bridgeUrl, projectId, navigate: goto });
433
+ });
434
+ void pending.catch(console.error);
435
+ return () => {
436
+ disposed = true;
437
+ void pending.then((handle) => handle?.unmount()).catch(() => {});
438
+ };
439
+ });
440
+ </script>
441
+ ```
442
+
443
+ For plain Svelte, place this in `App.svelte` and omit the `goto` import and
444
+ `navigate` option, or supply your own router callback. TypeScript projects can
445
+ add `/// <reference types="@popmelt.com/core/vite/client" />` to their app's
446
+ ambient declarations for the virtual module types.
447
+
448
+ This setup is development-only, including during SSR. It does not provide
449
+ Svelte component metadata or framework-specific library previews. Advanced
450
+ Kit routing, form actions, and deployment adapters beyond Node are not yet tested.
451
+
419
452
  ### Experimental Astro integration
420
453
 
421
454
  For Astro 7.3.1, install Popmelt and its internal renderer peers as development
@@ -444,10 +477,8 @@ If your project already mounts a Popmelt provider, use `popmelt({ client: false
444
477
  to keep that provider in charge of the UI. The existing `port`, `projectRoot`,
445
478
  `basePath`, and `bridge` options remain available.
446
479
 
447
- Run `pnpm run astro:smoke` with Node 24.11+, npm registry access, and Chrome for
448
- the fresh-install browser journey. Provider responses are controlled; it does not
449
- launch your coding agent. Astro component metadata, framework-specific island
450
- previews, and deployment adapters are not yet covered.
480
+ Astro component metadata, framework-specific island previews, and deployment
481
+ adapters are not yet covered.
451
482
 
452
483
  ### Other development servers
453
484
 
@@ -463,20 +494,34 @@ The wrapper can start Popmelt alongside another development command:
463
494
 
464
495
  ## Verify the installation
465
496
 
466
- Start the app normally:
497
+ For the installing agent, verify the user's actual app, not a separate demo or fixture. Work from the correct application directory and preserve its existing scripts and configuration.
498
+
499
+ 1. **Framework and app:** confirm the installed dependencies and matching provider/mount and framework integration. Explain any relevant experimental-support limit. Start the app using its existing development command, or restart only its own development process when needed and authorized. Do not interrupt active work or other projects.
500
+ 2. **Browser:** open the exact local URL reported by the development server. Confirm the app loads and double-tap Command or Control to check that the toolbar appears. If browser access is unavailable, report the integration as configured but browser verification as pending.
501
+ 3. **Bridge:** confirm the local bridge belongs to this application and is healthy, then check the toolbar's connection when browser access is available. A service responding on some port is not enough. Use the applicable Codex or Claude diagnostics above with their stated limits; do not treat them as a general end-to-end check.
502
+ 4. **Provider:** confirm the selected CLI is available to the bridge process, not only to an unrelated terminal, and check version compatibility and sign-in status using supported, non-generating diagnostics where available. Do not print credentials, change sandbox/approval settings to make checks pass, or submit a paid test prompt. If authentication cannot be checked safely, say so. These checks establish readiness, not successful end-to-end execution; the user's first real request verifies that.
503
+ 5. **Ambient integration, when applicable:** follow the bundled Codex setup or experimental local Claude Code setup above, keeping personal installation, registration, and activation in a new session distinct. Preserve disabled integrations and get permission before personal installation. Track a required restart or unavailable surface separately from toolbar readiness; an optional ambient gap should not prevent the user from trying a working toolbar.
504
+
505
+ To start the app manually, use its existing development command, for example:
467
506
 
468
507
  ```bash
469
508
  npm run dev
470
509
  ```
471
510
 
472
- Open the local app in a browser and double-tap Command or Control. The Popmelt toolbar should appear.
511
+ ### Finish with a useful handoff
512
+
513
+ Only describe a check as verified when you observed it. Keep the completion message brief: give the exact local app URL, distinguish configured from verified, and name each remaining gap with its next step. For example, when the relevant checks pass:
514
+
515
+ > Popmelt is ready at [the app's actual local URL]. Double-tap Command/Control, click something you want changed, and leave a comment.
516
+
517
+ If ambient setup still needs a restart, say so separately. Let the user choose the first change; do not create a test annotation or launch an agent run merely to validate installation.
473
518
 
474
- If the toolbar opens but cannot reach an AI, check that:
519
+ ### If something is not ready
475
520
 
476
- - The selected AI CLI is installed and signed in
477
- - A Next.js development script includes `popmelt wrap`
478
- - The framework plugin or config wrapper is installed
479
- - The development server was restarted after installation
521
+ - **Toolbar missing:** check the provider or browser mount, development-only enablement, framework config, and whether the running app picked up the change. In Next.js, both `withPopmelt` and `popmelt wrap` are required.
522
+ - **Bridge disconnected:** check the intended project's bridge and the toolbar's connection settings. If a process must be restarted, identify that process and check for active work first; do not restart everything.
523
+ - **Provider unavailable:** distinguish a missing executable, incompatible version, missing sign-in, or a bridge environment that differs from the terminal. Give the specific installation, sign-in, or targeted restart step supported by the evidence. A denied diagnostic is unverified, not proof the provider is broken.
524
+ - **Ambient context missing:** inspect `codex status` for registration/enabled state, follow any pending restart, and check a new project-bound session. Resolve project selection with the user when needed; do not infer the project or install duplicate hooks.
480
525
 
481
526
  ## React Three Fiber
482
527
 
@@ -626,7 +671,7 @@ Popmelt is designed for local development. As with any AI-assisted coding, your
626
671
  | `I` | Enter Imprint mode |
627
672
  | Command/Control+Enter | Send all pending work |
628
673
  | Command/Control+C | Capture a screenshot for pending work when no text is selected |
629
- | Command/Control+Z | Undo a pending change or thread dismissal |
674
+ | Command/Control+Z | Undo a pending change or thread dismissal |
630
675
  | Command/Control+Shift+Z | Redo a pending change |
631
676
  | Backspace/Delete | Archive the open or selected historical thread |
632
677
  | Command/Control+Backspace/Delete | Clear all annotations and pending changes |
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "popmelt-core",
3
+ "version": "0.22.0",
4
+ "description": "Local Popmelt context, canvas tools, and ambient turn capture for Claude Code.",
5
+ "author": {
6
+ "name": "Popmelt",
7
+ "email": "reb@popmelt.com"
8
+ },
9
+ "homepage": "https://github.com/popmelt/core",
10
+ "license": "PolyForm-Shield-1.0.0"
11
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "mcpServers": {
3
+ "popmelt-core": {
4
+ "command": "node",
5
+ "args": [
6
+ "${CLAUDE_PLUGIN_ROOT}/scripts/mcp.mjs"
7
+ ]
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,60 @@
1
+ {
2
+ "description": "Ambient Popmelt context and turn capture; no permission hooks.",
3
+ "hooks": {
4
+ "UserPromptSubmit": [
5
+ {
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hook.mjs\"",
10
+ "timeout": 12
11
+ }
12
+ ]
13
+ }
14
+ ],
15
+ "PostToolUse": [
16
+ {
17
+ "hooks": [
18
+ {
19
+ "type": "command",
20
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hook.mjs\"",
21
+ "timeout": 12
22
+ }
23
+ ]
24
+ }
25
+ ],
26
+ "Stop": [
27
+ {
28
+ "hooks": [
29
+ {
30
+ "type": "command",
31
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hook.mjs\"",
32
+ "timeout": 12
33
+ }
34
+ ]
35
+ }
36
+ ],
37
+ "StopFailure": [
38
+ {
39
+ "hooks": [
40
+ {
41
+ "type": "command",
42
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hook.mjs\"",
43
+ "timeout": 12
44
+ }
45
+ ]
46
+ }
47
+ ],
48
+ "SessionEnd": [
49
+ {
50
+ "hooks": [
51
+ {
52
+ "type": "command",
53
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hook.mjs\"",
54
+ "timeout": 12
55
+ }
56
+ ]
57
+ }
58
+ ]
59
+ }
60
+ }
@@ -0,0 +1,10 @@
1
+ import { readFileSync } from 'node:fs';
2
+
3
+ export function pluginEnvironment() {
4
+ try {
5
+ const manifest = JSON.parse(readFileSync(new URL('../.claude-plugin/plugin.json', import.meta.url), 'utf8'));
6
+ return { ...process.env, POPMELT_CLAUDE_PLUGIN_VERSION: manifest.version };
7
+ } catch {
8
+ return { ...process.env, POPMELT_CLAUDE_PLUGIN_VERSION: '' };
9
+ }
10
+ }
@@ -0,0 +1,35 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { locatePopmeltCli } from './locate-cli.mjs';
3
+ import { pluginEnvironment } from './environment.mjs';
4
+
5
+ const cli = locatePopmeltCli();
6
+ const child = spawn(cli.command, [...cli.prefix, 'claude', 'hook'], {
7
+ cwd: process.cwd(), env: pluginEnvironment(), stdio: ['pipe', 'pipe', 'ignore'],
8
+ });
9
+ let output = '';
10
+ let finished = false;
11
+ const finish = (value = '{}') => {
12
+ if (finished) return;
13
+ finished = true;
14
+ clearTimeout(timer);
15
+ process.stdin.unpipe(child.stdin);
16
+ process.stdin.pause();
17
+ process.stdout.write(value + '\n');
18
+ };
19
+ const timer = setTimeout(() => {
20
+ child.kill('SIGKILL');
21
+ finish();
22
+ }, 10_000);
23
+ child.stdout.on('data', (chunk) => {
24
+ output += chunk.toString();
25
+ if (output.length > 256_000) { child.kill('SIGKILL'); finish(); }
26
+ });
27
+ child.stdin.on('error', () => {});
28
+ child.on('error', () => finish());
29
+ child.on('close', (code) => {
30
+ try {
31
+ const value = JSON.parse(output);
32
+ finish(code === 0 && value && typeof value === 'object' ? JSON.stringify(value) : '{}');
33
+ } catch { finish(); }
34
+ });
35
+ process.stdin.pipe(child.stdin);