On this page

@miosa/svelte v0.2.0 provides four Svelte 5 components for embedding MIOSA sandboxes. All components use Svelte 5 runes ($props, $state, $derived, $effect) and work with SvelteKit out of the box.

Install

npm install @miosa/svelte
# xterm peer deps  -  required only if you use MiosaTerminal
npm install @xterm/xterm xterm-addon-fit

Styles are scoped inside each .svelte file - no global stylesheet import required.

MiosaPreview

Renders a sandboxed <iframe> showing the HTTP preview URL for a running sandbox.

<script lang="ts">
  import { MiosaPreview } from '@miosa/svelte';

  // previewToken should be fetched from your backend  -  never hard-code your API key here
  let { previewToken } = await loadTokenFromServer();
</script>

<MiosaPreview
  sandboxId="sb_abc123"
  {previewToken}
  theme="dark"
  on:error={(e) => console.error(e.detail)}
/>

Props

PropTypeDefaultDescription
sandboxIdstring-Required. The sandbox ID to preview.
previewTokenstring-Short-lived preview token from your backend. Mutually exclusive with apiKey.
apiKeystring-Your MIOSA API key. Use only in +page.server.svelte or trusted SSR contexts.
theme"light" \| "dark""light"Visual theme.
classstring""CSS class forwarded to the root <div>.

Events

EventDetailDescription
errorErrorDispatched when the preview URL cannot be resolved.

Exactly one of previewToken or apiKey must be provided.

The component re-resolves the URL reactively whenever sandboxId changes (via $effect).


MiosaTerminal

Renders a full PTY terminal connected to a sandbox over WebSocket, powered by xterm.js.

<script lang="ts">
  import { MiosaTerminal } from '@miosa/svelte';
  import { env } from '$env/dynamic/private'; // SvelteKit server-only env
</script>

<MiosaTerminal
  sandboxId="sb_abc123"
  apiKey={env.MIOSA_API_KEY}
  theme="dark"
  on:resize={(e) => console.log(e.detail.cols, e.detail.rows)}
  on:error={(e) => console.error(e.detail)}
  style="height: 400px"
/>

Props

PropTypeDefaultDescription
sandboxIdstring-Required.
apiKeystring-Required.
theme"light" \| "dark""dark"Sets the xterm.js color scheme.

Events

EventDetailDescription
resize{ cols: number; rows: number }Dispatched when the terminal dimensions change.
errorErrorDispatched on WebSocket errors or xterm load failure.

The component auto-fits to its container via ResizeObserver and syncs resize events to the PTY. Cleanup (WebSocket close + terminal dispose) runs in onDestroy.


MiosaFileTree

Renders a flat-list file browser backed by the sandbox filesystem. Directories expand inline on click; files dispatch a select event with the file path.

<script lang="ts">
  import { MiosaFileTree } from '@miosa/svelte';

  let selectedPath = $state<string | null>(null);
</script>

<MiosaFileTree
  sandboxId="sb_abc123"
  apiKey={env.MIOSA_API_KEY}
  showHidden={false}
  defaultExpanded={false}
  on:select={(e) => (selectedPath = e.detail)}
  on:change={(e) => console.log(e.detail.path, e.detail.type)}
/>

Props

PropTypeDefaultDescription
sandboxIdstring-Required.
apiKeystring-Required.
showHiddenbooleanfalseInclude dotfiles and hidden directories.
defaultExpandedbooleanfalseWhen true, all directories start expanded.

Events

EventDetailDescription
selectstringAbsolute path of the clicked file, e.g. /workspace/src/index.ts.
change{ path: string; type: "created" \| "deleted" \| "modified" }Dispatched on filesystem changes.

The tree uses $derived to recompute the flat render list whenever a directory is toggled - no recursive component needed.


MiosaUsage

Renders a three-column usage summary (compute seconds, storage GB·hours, egress GB) for a specific end-user.

<script lang="ts">
  import { MiosaUsage } from '@miosa/svelte';
</script>

<MiosaUsage
  externalUserId="user_xyz"
  apiKey={env.MIOSA_API_KEY}
  period="last_30"
  theme="dark"
/>

Props

PropTypeDefaultDescription
externalUserIdstring-Required. Your platform’s user ID, matching the external_user_id used when creating sandboxes.
apiKeystring-Required.
period"current" \| "last_30" \| "last_7""current"Billing period. "current" is the active billing cycle; "last_30" / "last_7" are rolling windows.
theme"light" \| "dark""light"Visual theme.

Reactively reloads whenever externalUserId or period changes via $effect. No events are dispatched.


createMiosaClient store

The package also exports a createMiosaClient helper for creating a shared client store:

import { createMiosaClient } from '@miosa/svelte';

const client = createMiosaClient({ apiKey: 'msk_...' });

This is intended for advanced use cases where you need direct SDK access alongside the components.


End-to-end example: SvelteKit

src/routes/workbench/+page.server.ts - load preview token server-side

import type { PageServerLoad } from './$types';
import Miosa from '@miosa/sdk';
import { env } from '$env/dynamic/private';

const SANDBOX_ID = 'sb_abc123';

export const load: PageServerLoad = async () => {
  const client = new Miosa({ apiKey: env.MIOSA_API_KEY });
  const sandbox = await client.sandboxes.get(SANDBOX_ID);
  // @ts-expect-error
  const { token } = await sandbox.previewToken(3600);
  return { sandboxId: SANDBOX_ID, previewToken: token };
};

src/routes/workbench/+page.svelte

<script lang="ts">
  import type { PageData } from './$types';
  import { MiosaPreview, MiosaTerminal, MiosaFileTree } from '@miosa/svelte';
  import { env } from '$env/dynamic/private';

  let { data }: { data: PageData } = $props();

  let selectedFile = $state<string | null>(null);
</script>

<div class="workbench">
  <aside class="workbench__sidebar">
    <MiosaFileTree
      sandboxId={data.sandboxId}
      apiKey={env.MIOSA_API_KEY}
      on:select={(e) => (selectedFile = e.detail)}
    />
  </aside>

  <main class="workbench__preview">
    {#if selectedFile}
      <div class="workbench__filepath">{selectedFile}</div>
    {/if}
    <MiosaPreview
      sandboxId={data.sandboxId}
      previewToken={data.previewToken}
      theme="dark"
    />

  </main>

  <section class="workbench__terminal">
    <MiosaTerminal
      sandboxId={data.sandboxId}
      apiKey={env.MIOSA_API_KEY}
      theme="dark"
      on:error={(e) => console.error('[terminal]', e.detail)}
    />
  </section>
</div>

<style>
  .workbench {
    display: grid;
    grid-template-columns: 240px 1fr 1fr;
    height: 100vh;
    overflow: hidden;
  }
</style>

See also

Was this helpful?