npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@runflow-io/studio

v0.1.1

Published

Embeddable Runflow Studio. Drop it on any website via script tag or npm install.

Readme

@runflow-io/studio

Embeddable Runflow Studio for any website. Drops the production Studio UI into a <div>, talks to api.runflow.io through a proxy you control.

Install

bun add @runflow-io/studio @runflow-io/proxy @runflow-io/sdk

react@>=18 and react-dom@>=18 (or 19) are peer dependencies.

Server: mount the proxy

// app/api/runflow/[...path]/route.ts  (Next.js App Router)
import { runflowProxy } from "@runflow-io/proxy";

export const { GET, POST } = runflowProxy({
  apiKey: process.env.RUNFLOW_API_KEY!,
});

This handler covers the /v1/models/*/runs dispatch path, the /v1/runs/{id} poll path, and the asset-upload/read routes the Studio uses for workflow runs and file uploads (rf.assets.upload through the proxy's default allow-list — no extra upload endpoint needed). It does NOT cover chat, image-proxy, or sentinel — see Companion endpoints below.

Browser: mount the Studio

import { mount } from "@runflow-io/studio";

const studio = mount("#studio", {
  urls: { runflowProxy: "/api/runflow" },
  theme: "auto",
});

// Later, if you want to unmount:
studio.unmount();
<div id="studio"></div>

The Studio injects its own stylesheet into <head> on mount. Pass injectStyles: false if you ship your own CSS.

Mount options

mount(target: string | HTMLElement, options?: {
  /** Endpoint overrides. Defaults to /api/runflow/* paths. */
  urls?: {
    runflowProxy?: string;     // /api/runflow         — dispatch + poll
    runflowDevProxy?: string;  // (off by default)      — unreleased models
    imageProxy?: string;       // /api/runflow/image    — same-origin image fetch
    upload?: string;           // optional legacy multipart endpoint — by default
                               // uploads use the SDK presigned flow through
                               // runflowProxy (no extra endpoint needed)
    chat?: string;             // /api/runflow/chat     — chat agent (SSE)
    sentinel?: string;         // /api/runflow/sentinel — sentinel evaluation
  };
  /** "light" | "dark" | "auto" | { accent, bg0, ink0, ... } */
  theme?: ThemeMode | ThemeOverrides;
  /** Inject the default <style> into <head>. Default: true. */
  injectStyles?: boolean;
  /** Shell customization — see "Customizing the shell" below. */
  props?: StudioShellProps;
}): { unmount(): void };

Customizing the shell

<StudioShell> (and mount()'s props option) takes four optional props; zero props renders the stock studio.

import { StudioShell } from "@runflow-io/studio";
import { WORKFLOWS } from "@runflow-io/studio/headless";

<StudioShell
  // The workflow catalogue (Workflow[], not the SDK's ToolDefs):
  // cards, chat-agent tools, package steps.
  tools={WORKFLOWS.filter((w) => w.group === "cleanup")}
  // A starting image URL, or SampleAsset[] to replace the samples.
  source="https://cdn.example/listing.jpg"
  // Disable quality evals, or re-template their task description.
  sentinel={{ enabled: false }}
  // Brand + labels, shallow-merged over the defaults.
  copy={{ brandName: "Estates Studio", brandTag: "", assetsTitle: "Listings" }}
/>

If you need event callbacks or update() to reconfigure at runtime, those are tracked as follow-ups.

Companion endpoints

The Studio dispatches against five endpoints. @runflow-io/proxy handles two; the other three are customer-provided because they involve your storage, your Anthropic key, and (for sentinel) your evaluation service:

| URL key | What needs to live there | Provided by | |---|---|---| | runflowProxy | POST /v1/models/{model}/runs + GET /v1/runs/{id} | @runflow-io/proxy | | imageProxy | GET /?url=<external> → echo bytes, same-origin (for cross-origin source images) | You | | upload | (optional legacy) POST multipart → { url } — by default uploads use the SDK presigned flow through runflowProxy; only set this to keep a custom storage path | You | | chat | SSE chat agent (wraps Anthropic + the tool catalogue) | You | | sentinel | POST /evaluate?sync=false + GET /evaluate/{eval_id} | You |

The runflow-prototypes repo has working reference implementations under projects/demos/api/{upload,chat,sentinel,image}.mjs. If you only need dispatch + uploads (no chat, no sentinel), the proxy alone is enough — the upload/chat/sentinel-dependent UI paths gracefully degrade when the routes return 404/403.

CDN

<link rel="stylesheet" href="https://cdn.runflow.io/studio.css" />
<script src="https://cdn.runflow.io/studio.js"></script>
<div id="studio"></div>
<script>
  RunflowStudio.mount("#studio", { urls: { runflowProxy: "/api/runflow" } });
</script>

The CDN bundle inlines React (~150 KB gzipped). The npm package externalizes React — install that one if you already have React in your bundle.

Theming

mount("#studio", {
  theme: {
    accent: "#FBBF24",
    bg0: "#0a0a0b",
    ink0: "#fafafa",
    // ...
  },
});

Or pass "light" / "dark" / "auto" for the bundled defaults. All theme values become CSS custom properties (--rfs-*) on the mount root, so you can also override them in your own CSS.

Workflows that ship

Built-in single workflows: ai-edit, ai-scene, reference-inpaint, logo-fix, product-isolation, smart-resize, outpaint, background-color, background-removal, tag-removal, object-removal, model-removal, skin-fix, topaz-upscale. Plus the zalando-package bundle. They map one-to-one to the catalogue published at runflow.io/models.

Headless mode

If you want to skip the bundled UI and build your own with the same tool catalogue:

import { BUILTIN_TOOLS, findTool } from "@runflow-io/studio/headless";
import { Runflow } from "@runflow-io/sdk";

const rf = new Runflow({ baseUrl: "/api/runflow" });
const tool = findTool("background-removal")!;
const { output } = await rf.tools.run(tool, { image: "https://cdn/x.png" });

The headless entry also exports WORKFLOWS, SAMPLES, the prototype's runWorkflow dispatcher, the sentinelEvaluate client, setStudioUrls, and createMaskController — the framework-free dual-canvas brush engine the shell uses for its mask workflows:

import { createMaskController } from "@runflow-io/studio/headless";

const mask = createMaskController({ brushSize: 45 });
mask.attach(overlayCanvas); // rendered over your image
mask.syncToDisplay(rect.width, rect.height, devicePixelRatio);
// pointer events → beginStroke / strokeTo / endStroke; then:
const blob = await mask.toMaskBlob(img.naturalWidth, img.naturalHeight);
const asset = await rf.assets.upload(blob, { filename: "mask.png" });
// → feed asset.ref (stable, never expires) as mask_url to
//   runflow/reference-inpaint etc.

A React state reducer for building a custom shell is tracked as a follow-up.

License

MIT