@happyvertical/smrt-playground

Shared playground discovery, runtime helpers, and host components for SMRT UI packages. Discovers per-package ./playground modules and renders them under a unified host.

v0.29.34Playground HostUI DiscoveryVite Plugin

Overview

smrt-playground is the canonical playground host for SMRT UI packages. Each UI-shipping package exports a ./playground subpath; the host discovers them at build time, merges them into a unified registry, and renders the entries through a shared PlaygroundHost Svelte component.

Installation

bash
npm install @happyvertical/smrt-playground

The ./playground Subpath Contract

Each UI-shipping package opts in by exporting a ./playground subpath that resolves to a module shaped like SmrtPlaygroundModule:

typescript
// packages/<name>/src/svelte/playground.ts
import type { SmrtPlaygroundModule } from '@happyvertical/smrt-playground';

const playground: SmrtPlaygroundModule = {
  packageName: '@happyvertical/smrt-<name>',
  // Optional: displayName, description, moduleMeta
  entries: [
    {
      id: '<name>:component-id',
      title: 'Human-readable title',
      description: 'Optional short description.',
      loadComponent: () => import('./components/MyComponent.svelte'),
      // Optional:
      order: 1,
      props: { /* default props passed to the component */ },
      // modes is a map keyed by 'mock' | 'live'
      modes: { mock: { label: 'Mock' } },
    },
  ],
};

export default playground;

The package's package.json exports map must point ./playground at the compiled module:

json
{
  "exports": {
    "./playground": {
      "types": "./dist/playground.d.ts",
      "import": "./dist/playground.js"
    }
  }
}

Quick Start (Vite Plugin)

typescript
// vite.config.ts in your playground host app
import { smrtPlaygroundVitePlugin } from '@happyvertical/smrt-playground/vite';
import { sveltekit } from '@sveltejs/kit/vite';

export default {
  plugins: [
    sveltekit(),
    smrtPlaygroundVitePlugin({
      // All options are optional.
      // mode: 'auto' (default) | 'workspace' | 'consumer'
      mode: 'auto',
      // workspaceRoot, packagesPattern, localPlaygroundPath
    }),
  ],
};

Render the Host

svelte
<!-- src/routes/+page.svelte -->
<script lang="ts">
  import { PlaygroundHost } from '@happyvertical/smrt-playground/svelte';
  import { playgroundModules } from 'virtual:smrt-playground/modules';
</script>

<PlaygroundHost modules={playgroundModules} />

CLI Commands

The smrt playground commands are owned by @happyvertical/smrt-cli but delegate to this runtime:

bash
# Scaffold a playground host app
npx smrt playground init

# Run the playground in dev mode
npx smrt playground dev

# List discovered playground entries
npx smrt playground list

Module Anatomy

  • discovery.ts — finds package-owned ./playground modules across workspace and node_modules
  • runtime.ts — coerces and merges discovered modules into a unified registry consumed by the host
  • templates.ts — generates Svelte route templates for app-level and package-level playground hosts
  • svelte/PlaygroundHost.svelte — the host component that renders discovered playground entries
  • vite.ts — Vite plugin used by the SvelteKit playground host to wire everything together

UI Surface Levels

smrt-playground is one of three UI surfaces a SMRT package may expose. From the monorepo's docs/ui-surfaces.md:

  • ./svelte — reusable components (ContentEditor, ArticleCard, etc.)
  • ./playground — preview metadata for the shared playground host (the standard for SMRT packages with Svelte components)
  • ./routes — package-owned page/workflow surfaces (not standardized for this release)

smrt-playground only discovers and renders. Component code and theming live in individual packages (smrt-content/svelte, smrt-jobs/svelte, etc.) or in smrt-svelte for shared primitives.

Gotchas

DOs

  • Keep playground modules tiny — only metadata + dynamic load()
  • Use the id prefix convention <package-name>:<component> for uniqueness
  • Provide fixtures for components that require non-trivial input
  • Expose ./playground in package.json exports — discovery relies on it

DON'Ts

  • Don't ship component source from smrt-playground — it's a host, not a component library
  • Don't import smrt-playground internals from individual packages — only the type surface is public
  • Don't rely on the private host/ directory — it's a Playwright e2e harness, not published
  • Don't conflate ./playground with ./routes — the former is preview metadata, the latter would be reusable app surfaces

Related Modules