Space Plugins

Extend your workspace with custom JavaScript plugins that run during meetings, process live transcripts, and render custom file types.

Space Plugins let you extend the Stoa workspace with custom JavaScript or TypeScript. Plugins are regular project files that live in a _stoa_plugins/ directory, sync via CRDT to all participants, and can be co-edited in real time during meetings.

What plugins can do

  • Show custom widgets in the sidebar and full panel view
  • Render custom file types (e.g., display .csv files as interactive tables)
  • Process live transcript segments from your meeting in real time
  • Fetch external data from APIs and display it in the workspace

Plugins run in sandboxed Web Workers on each participant's browser. There's no deploy step. Edit the file and the output updates automatically for everyone.

Creating a plugin

From the template picker

  1. Click "Create Plugin" in the Space section of the sidebar.
  2. Choose a template:
    • Widget: sidebar card with a full-panel expanded view
    • Renderer: custom display for specific file types
    • Transcript: processes live meeting audio
    • API Dashboard: fetches and displays external data
    • Blank: empty scaffold
  3. Name your file (must end in .js or .ts).
  4. Click Create. The file opens in the editor, synced to all participants.

Manually

Create any .js or .ts file inside a _stoa_plugins/ directory in your project. The plugin system automatically discovers it.

Plugin structure

Every plugin needs a manifest header and a render() function:

// @stoa-plugin
// @name Sprint Board
// @type widget
// @icon BarChart3

module.exports = {
  render(context) {
    return {
      type: 'list',
      data: {
        items: [
          { title: 'Design review', subtitle: 'In progress' },
          { title: 'API refactor', subtitle: 'Done' }
        ]
      }
    }
  }
}

Manifest fields

FieldRequiredDescription
@stoa-pluginYesMarks the file as a plugin
@nameYesDisplay name in the sidebar
@typeYeswidget, renderer, or event
@iconNoLucide icon name (e.g., BarChart3)
@permissionsNoNetwork hosts the plugin can access (e.g., network:api.github.com)
@refreshNoAuto-refresh interval (e.g., 30s, 5m, 2h)
@filetypeNoGlob patterns for renderer plugins (e.g., *.csv,*.tsv)
@eventsNoEvent subscriptions (e.g., transcript:segment)
@runtimeNoserver to force server-side execution (default is client-side)

Plugin types

Widget

Appears in the sidebar with a compact view and an expandable full-panel view. Click "Open" on a widget to expand it.

Widget plugins receive context.mode as either 'widget' (sidebar) or 'full' (expanded panel), so you can return different layouts for each.

Renderer

Overrides how specific file types display in the editor. For example, a renderer with @filetype *.csv will display CSV files as formatted tables instead of raw text.

A banner appears above rendered files showing the plugin name, with a toggle to switch between the plugin view and the raw source.

Event (transcript)

Receives live transcript segments during a meeting. Declare @events transcript:segment in the manifest and access context.transcriptSegments in your render function:

// @stoa-plugin
// @name Meeting Tracker
// @type event
// @events transcript:segment

module.exports = {
  render(context) {
    const segments = context.transcriptSegments || []
    const speakers = [...new Set(segments.map(s => s.displayName))]
    return {
      type: 'list',
      data: {
        items: speakers.map(name => ({
          title: name,
          subtitle: `${segments.filter(s => s.displayName === name).length} segments`
        }))
      }
    }
  }
}

Each transcript segment includes text, speaker, displayName, isFinal, and timestamp.

Output types

Plugins return structured output that renders as native UI components. Supported types:

TypeDescription
markdownFormatted markdown with headings, lists, links, and code
textMonospace preformatted text
listItems with optional icons and subtitles
kvKey-value pairs
tableColumns and rows
jsonCollapsible tree viewer
treeHierarchical expandable nodes
stackVertical or horizontal composition of other outputs
tabsTabbed content switcher
htmlSandboxed iframe for custom HTML
errorError message with optional details
loadingSpinner with a message
emptyEmpty state with a message

Network access

Plugins can fetch data from external APIs using standard fetch():

// @stoa-plugin
// @name GitHub Issues
// @type widget
// @permissions network:api.github.com

module.exports = {
  async render(context) {
    const res = await fetch('https://api.github.com/repos/myorg/myrepo/issues')
    const issues = await res.json()
    return {
      type: 'list',
      data: { items: issues.map(i => ({ title: i.title, subtitle: `#${i.number}` })) }
    }
  }
}

For APIs that don't support CORS, use the built-in stoaProxy(url) helper, which routes the request through a server-side proxy with SSRF protection.

Declare which hosts your plugin needs access to in the @permissions field. Use network:* to allow all hosts.

Execution

Plugins run client-side in Web Workers by default. This means near-instant execution with no network round trip.

If you need server-side execution (e.g., for APIs that require server-only secrets), add @runtime server to your manifest. Server-side plugins run in a V8 isolate with a 5-second timeout and 64MB memory limit.

Plugins re-execute automatically when their source changes. Results are cached by source hash, so identical source returns cached output instantly.

Guest access

Guests in a session can view and interact with plugins. Both the plugin execution and proxy endpoints accept guest tokens, so plugins work the same way for guests as they do for team members.

What's Next