The AI image editor used inside Bloom Editor, plus a reusable React component.
This repo produces two outputs, and they are consumed in different ways:
| Output | Built by | What it is | Consumed by |
|---|---|---|---|
dist/ (library) |
build:lib (tsup) |
The ImageToolsWorkspace React component, as an importable npm package. |
Any app that wants to import { ImageToolsWorkspace }. |
dist-app/ (hosted app) |
build:app (Vite) |
The whole standalone editor app (index.html + assets). |
Bloom Editor, which loads it in an iframe overlay. |
Bloom does not import the React component — it loads the prebuilt dist-app/ app by URL into an iframe. See How Bloom hosts this editor.
Prerequisites: Vite+ (vp). On Windows install it with irm https://vite.plus/ps1 | iex, then restart your terminal or VS Code so vp is on PATH.
Vite+ manages the Node.js runtime from .node-version and the pnpm version from packageManager in package.json.
- Install dependencies:
vp install - Run the dev demo:
vp dev - Build the npm package:
vp run build:lib - Build the demo bundle (optional):
vp build
pnpm add bloom-ai-image-toolsimport { ImageToolsWorkspace } from "bloom-ai-image-tools";
function Example() {
return <ImageToolsWorkspace persistence={...} envApiKey={...} />;
}See App.tsx for a concrete integration example.
Art-style preview thumbnails rely on bundlers that support
import.meta.glob(Vite/Rollup). Other bundlers fall back to text-only style selection.
Bloom embeds the editor as an iframe overlay inside its existing edit-tab WebView2 —
it is not a separate window, and Bloom never bundles this repo's source. The editor app
runs in the iframe and talks to its host over window.postMessage (channel
bloom-ai-image-tools); file I/O and image bytes go over HTTP to Bloom's local server.
On the Bloom side this lives in AiImageEditorApi.cs and CanvasElementContextControls.tsx.
The editor decides how it's running from the URL (App.tsx): ?mode=bloom-iframe →
BloomEmbeddedShell over createIframeBloomHostBridge(); ?mode=bloom-harness → the
fake-host BloomHostHarness (dev/e2e); no mode → the plain StandaloneShell. The host
plumbing all hides behind services/host/BloomHostBridge.ts.
Bloom's GetEditorUrl() returns http://localhost:3000/ in a DEBUG build, so the
overlay iframe loads this repo's running Vite dev server:
vp devhere (serves the editor onhttp://localhost:3000).- Run a DEBUG build of Bloom and choose "Edit with AI…" on an image. Editor edits hot-reload inside Bloom; only Bloom C# changes need a Bloom rebuild.
A Windows junction (BloomEditor → the Bloom worktree) is sometimes used to view/edit
both repos in one place; it's git-ignored and not part of the consumption path.
In a Release build GetEditorUrl() returns {ServerUrl}/bloom/aiImageEditor/index.html,
i.e. the editor served same-origin from Bloom's own server (no CORS). To wire that up:
- Publish this package (ships
dist-app/— seefilesinpackage.json). The app build bakes in--base=/bloom/aiImageEditor/so its asset URLs resolve at that mount. - Add the dependency in Bloom (
src/BloomBrowserUI/package.json):"bloom-ai-image-tools": "^x.y.z". - Copy the app into Bloom's served output at build time, exactly like the existing
bp-to-outputstep forbloom-player, e.g.:During dev you canyarn linkthis package instead of installing a published version.
Not done yet: steps 2–3 wait until the first npm publish (a
package.jsonrange pointing at an unpublished version would break Bloom's install). The editor side (step 1) is ready.
We use Changesets for semver management. Typical workflow:
- Create a changeset describing your change:
vp run changeset - Merge the generated PR. The
ReleaseGitHub Action will bump versions and publish to npm. - Manual publishing (rare):
vp run release
Ensure NPM_TOKEN is configured in the repo secrets for the workflow to succeed.
- Unit tests:
vp test - E2E (Playwright): set
BLOOM_OPENROUTER_KEY_FOR_PLAYWRIGHT_TESTSto your OpenRouter API key, then runvp run e2e.