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

@arbiterflow/editor

v1.0.0

Published

Embeddable React editor for ArbiterFlow.

Downloads

732

Readme

@arbiterflow/editor

Embeddable React workflow tooling for ArbiterFlow — the same canvas and workflow list that power the hosted dashboard, shipped as components you can drop into your own app.

Drag nodes from a palette, wire them together, configure them through a schema-driven inspector, then save, publish, and watch runs execute live over the graph.

Install

npm install @arbiterflow/editor

React 18 or 19 is a peer dependency and is not bundled.

Usage

The fastest complete integration is ArbiterFlowStudio: the workflow list and the editor together, with navigation between them.

import { ArbiterFlowStudio } from "@arbiterflow/editor";
import "@arbiterflow/editor/styles.css";

export function WorkflowsPage() {
  return (
    <ArbiterFlowStudio
      projectId="7f3c…"
      authMode="proxy"
      serverUrl="/api/arbiterflow"
      height="100vh"
    />
  );
}

That is the whole workflow-management surface — browse, search, create, open, edit, publish, run, delete — in one component. The stylesheet is a separate import; it is not injected by the JavaScript bundle.

For a full walkthrough — requirements, the proxy route, framework wiring, and troubleshooting — see the integration guide.

Choosing a component

| Component | Renders | Use when | |---|---|---| | ArbiterFlowStudio | List + editor, with its own navigation | You want workflow management on one route and do not care about the URL of the open workflow | | ArbiterFlowWorkflowList | Just the list | You already have routing and want the list and editor on separate pages | | ArbiterFlowEditor | Just the canvas | You pick the workflow yourself, or you only ever edit one |

All three take the same connection props (projectId, serverUrl, authMode, apiKey), so moving between them is a rename.

Two routes instead of one

ArbiterFlowStudio owns navigation internally. If you would rather the open workflow live in your URL, use the list and the editor directly — the list never routes, it just hands you back the workflow that was clicked.

// app/admin/workflows/page.tsx
<ArbiterFlowWorkflowList
  projectId={projectId}
  authMode="proxy"
  serverUrl="/api/arbiterflow"
  onSelectWorkflow={(workflow) => router.push(`/admin/workflows/${workflow.slug}`)}
/>

// app/admin/workflows/[slug]/page.tsx
<ArbiterFlowEditor
  projectId={projectId}
  authMode="proxy"
  serverUrl="/api/arbiterflow"
  workflowSlug={slug}
/>

ArbiterFlowStudio can also be URL-driven, if you want its top bar but your own routing — pass openWorkflowSlug and onOpenWorkflowChange to control it.

Authentication

Pick one of three modes.

authMode="proxy" — how you should embed this. Point serverUrl at a catch-all route in your own app. That route authenticates the visitor however you already do, then forwards to ArbiterFlow with x-api-key injected server-side. No credential reaches the browser, and you get to enforce your own permissions on the way through. Every request the editor makes is ${serverUrl}/v1/projects/${projectId}/…, so a single catch-all covers it.

authMode="apiKey" sends the apiKey prop from the browser. Anyone who opens devtools can read it, so only use it where every visitor is already entitled to that key — a local tool, or an internal page behind your own login where the key is scoped to a single project.

authMode="session" is the ArbiterFlow dashboard's own cookie surface. It is not available to other origins.

Props

Connection (all components)

| Prop | Type | Default | Description | |---|---|---|---| | projectId | string | — | Required. Project to operate on. | | serverUrl | string | https://arbiterflow-api.solopress.ai | Base URL of the ArbiterFlow API, or your proxy route. | | apiKey | string | — | Sent as x-api-key when authMode is "apiKey". | | authMode | "proxy" \| "apiKey" \| "session" | "apiKey" | See Authentication. | | theme | "dark" \| "light" \| "system" | "dark" | Palette to paint with. See Theming. |

ArbiterFlowStudio

| Prop | Type | Default | Description | |---|---|---|---| | openWorkflowSlug | string \| null | — | Controls which workflow is open. Omit to let the studio track it. | | onOpenWorkflowChange | (slug: string \| null) => void | — | Fires on open and on back, for syncing to your URL. | | height | string \| number | "100%" | Height of the root. | | title / description | ReactNode | — | Header copy on the list screen. | | canCreate / canDelete | boolean | true | Hide list controls to match your RBAC. | | canPublish / canRun / canDatabaseWrite | boolean | — | Passed through to the editor. | | customNodes | Record<string, ComponentType<CustomNodeProps>> | — | Passed through to the editor. |

ArbiterFlowWorkflowList

| Prop | Type | Default | Description | |---|---|---|---| | onSelectWorkflow | (w: WorkflowListItem) => void | — | Required. A row was clicked; you decide what that means. | | onWorkflowCreated | (w: WorkflowListItem) => void | onSelectWorkflow | Fires after creation, before the list refreshes. | | title | ReactNode | "Workflows" | Pass null to drop the header entirely. | | description | ReactNode | — | Sub-heading copy. | | headerActions | ReactNode | — | Rendered beside the create button. | | canCreate / canDelete | boolean | true | Hide controls to match your RBAC. | | height | string \| number | "100%" | Height of the root. | | className | string | — | Applied to the root. |

ArbiterFlowEditor

| Prop | Type | Default | Description | |---|---|---|---| | workflowSlug | string | "default-routing" | Which workflow to load. | | mode | "edit" \| "runView" \| "graphView" | "edit" | runView replays a run; graphView renders a static definition. | | viewRunId | string | — | Run to display when mode is "runView". | | initialDefinition | WorkflowDefinition | — | Graph to render before (or instead of) fetching. | | height | string \| number | "100vh" | Height of the editor root. | | customNodes | Record<string, ComponentType<CustomNodeProps>> | {} | Override or extend node renderers by node type. | | onControlsChange | (c: EditorControls \| null) => void | — | Hands save/publish/run out so the host can render its own chrome. | | onRunStatusChange | (status) => void | — | Fires on run status transitions in run view. | | canPublish / canRun / canDatabaseWrite | boolean | true | Hide controls to match your RBAC. |

Permissions

The components render the controls you allow and nothing more, but they cannot enforce anything — the browser is not a trust boundary. Gate canCreate, canDelete, canPublish and canRun to keep the UI honest, and enforce the same rules in your proxy route, which is the only place a check counts.

Next.js

The bundle carries a "use client" directive, so App Router consumers can import it directly from a server component — no wrapper module needed. It reads no browser APIs at module scope, so next/dynamic with ssr: false is not required either.

Styling

Import the stylesheet anywhere, including a root layout:

import "@arbiterflow/editor/styles.css";

Every rule in it — including Tailwind's reset and utility classes — is confined to the editor's root element, so it cannot reach the rest of your page. You do not need to wrap it in a cascade layer, scope the import to a subtree, or worry about it colliding with your own Tailwind build.

The scoping prefix carries no specificity of its own, so your theme is not demoted beneath the editor's and the editor's is not demoted beneath yours.

Theming

Every component takes a theme prop — "dark" (default), "light", or "system" to follow the OS. To follow your own switch instead, pass the mode you already track:

const { resolvedTheme } = useTheme(); // next-themes
<ArbiterFlowStudio theme={resolvedTheme === "light" ? "light" : "dark"} … />

Switching does not remount the canvas — the palette is CSS custom properties, so unsaved edits and scroll position survive.

Using your own colours

Colours resolve through --color-af-* properties, and the utilities are indirections onto them (.bg-af-bg { background-color: var(--color-af-bg) }). Redefine one, scoped to .arbiterflow-editor, and everything using it follows:

.arbiterflow-editor {
  --color-af-accent: #0ea5e9;
}

Yours wins: the shipped values are emitted at zero specificity.

shadcn/ui

The mapping is already written. Import it after the base stylesheet:

import "@arbiterflow/editor/styles.css";
import "@arbiterflow/editor/shadcn.css";

It points the editor's tokens at the variables shadcn already defines (--background, --card, --primary, --border, --destructive, --chart-1--chart-5), so the editor adopts your palette — and, because it maps to your variables rather than to fixed values, it keeps up when you switch themes and follows your light/dark class automatically. Still pass theme, so the canvas and code editors know which mode is active.

The full token list and the reasoning behind the mapping are in the integration guide.

License

MIT