Get started
Examples

Assemble a full studio

Assemble a full studio

Every surface here dispatches through one tool bus. createStudioToolBus() registers the studio's own handler for every verb, and Studio handlers shows how to replace one. Reach for it when a product edits itineraries rather than showing them.

import { createStaticViewer, createUiStore, defaultStudioConfig } from "@mountaya/studio";
import type { StudioToolContext } from "@mountaya/studio";
import { createStudioToolBus } from "@mountaya/studio/handlers";
import { browserSaveFile } from "@mountaya/studio/seams";
import { StudioControls } from "@mountaya/studio-react";
import { MountayaStudioProvider } from "@mountaya/studio-react/headless";
import { useMemo } from "react";

const CAPABILITIES = ["authenticated", "editor", "embedding"] as const;

export const Studio = () => {
  const { bus, context, store } = useMemo(() => {
    const ui = createUiStore();
    const ctx: StudioToolContext = {
      capabilities: new Set(CAPABILITIES),
      map: mapClient,
      saveFile: browserSaveFile,
      ui,
      viewer: createStaticViewer({ getUserId: () => userId }),
    };

    return { bus: createStudioToolBus(), context: ctx, store: ui };
  }, []);

  return (
    <MountayaStudioProvider
      client={client}
      data={{ bus, capabilities: CAPABILITIES, context, store }}
      map={map}
    >
      <StudioControls config={defaultStudioConfig} fallbackItineraryId={itineraryId} />
    </MountayaStudioProvider>
  );
};

Build the bus and its context in one useMemo: a live bus refuses a second registration.

The context is typed as StudioToolContext, so a member a handler reads and the literal leaves out fails to compile instead of throwing inside a dispatch. ui is the store the bus writes. map is the MapClient the map tools draw on, the one EditorMapView hands to onClientReady, or null while no map is mounted. saveFile is where an export goes: browserSaveFile in a page, inertStudioSaveFile where nothing may be written. viewer says who is reading: createStaticViewer answers from the values you pass and denies every entitlement you leave out.

Grant embedding alongside editor. The capabilities are a flat set rather than a hierarchy, and the map and panel tools behind StudioControls are gated on embedding, so a stack missing it renders enabled, dispatches, and is refused.

Next steps