@popmelt.com/core 0.21.0 → 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 +225 -180
- package/claude-plugin/.claude-plugin/plugin.json +11 -0
- package/claude-plugin/.mcp.json +10 -0
- package/claude-plugin/hooks/hooks.json +60 -0
- package/claude-plugin/scripts/environment.mjs +10 -0
- package/claude-plugin/scripts/hook.mjs +35 -0
- package/claude-plugin/scripts/locate-cli.mjs +44 -0
- package/claude-plugin/scripts/mcp.mjs +13 -0
- package/claude-plugin/skills/popmelt-core/SKILL.md +23 -0
- package/codex-plugin/.codex-plugin/plugin.json +1 -1
- package/dist/bridge-entry.mjs +65 -65
- package/dist/{browser-runtime-OVKRUB6I.mjs → browser-runtime-ARYJ6VBJ.mjs} +1 -1
- package/dist/browser.mjs +1 -1
- package/dist/chunk-4XEZ4YQA.mjs +448 -0
- package/dist/{chunk-OY63KT72.mjs → chunk-DBKGRJ7N.mjs} +1 -1
- package/dist/{chunk-V25ZE6LB.mjs → chunk-K5IIMTQE.mjs} +1 -1
- package/dist/cli.mjs +103 -106
- package/dist/index.mjs +1 -1
- package/dist/plugin-astro.mjs +1 -1
- package/dist/plugin-nuxt.mjs +1 -1
- package/dist/plugin-vite.mjs +1 -1
- package/dist/{server-J34NXDOX.mjs → server-BLXPOSA6.mjs} +67 -67
- package/dist/server.mjs +67 -67
- package/package.json +2 -1
- package/dist/chunk-UU5DZDRD.mjs +0 -448
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
50
|
-
this in your browser entry:
|
|
45
|
+
Pick one thing in your own app that you already want to improve:
|
|
51
46
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
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
|
|
|
@@ -246,7 +98,7 @@ Imprint opens the project's shared understanding. Use its filters to review grou
|
|
|
246
98
|
- Press Escape to close the open thread
|
|
247
99
|
- Press Backspace or Delete with a thread open, or with one of its historical annotations selected, to archive that thread
|
|
248
100
|
- Right-click a draft comment or staged Steer change in its annotation rail to remove only that draft item
|
|
249
|
-
- Right-click a replied rail to dismiss it from the canvas without deleting its history or reverting applied edits.
|
|
101
|
+
- Right-click a replied rail to dismiss it from the canvas without deleting its history or reverting applied edits. Press **Command/Control+Z** outside a text editor to restore it before reloading. Rails with fresh drafts or active runs are protected.
|
|
250
102
|
- When a reply is queued, use its pencil or trash button to edit or delete that individual message before it starts. Save an edit with the checkmark or Command/Control+Enter; Escape cancels the edit
|
|
251
103
|
- Codex permission requests appear chronologically in the thread as a compact action card, while the canvas and toolbar show **Approval needed**. Expand the card to inspect the exact command or file changes, then choose **Allow once** or **Deny**. Answered requests become an inline tool icon and label, retained in the thread history. Chat text is not a permission grant; Popmelt does not create remembered approvals. Your existing Codex permission rules still apply, so previously allowed commands may run without a new card. After 10 seconds without a response, a run waiting solely on approval yields to other queued jobs. Approving it while parked moves it to the front of the waiting queue, without interrupting active work. The exact action resumes only after reacquiring a slot; unanswered requests expire on resumption (or at 10 seconds if the run cannot safely yield). Same-thread follow-ups cannot overtake a parked run, and disconnected runs cannot be approved.
|
|
252
104
|
|
|
@@ -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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
448
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
519
|
+
### If something is not ready
|
|
475
520
|
|
|
476
|
-
-
|
|
477
|
-
-
|
|
478
|
-
-
|
|
479
|
-
-
|
|
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
|
|
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,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);
|