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

@happyvertical/smrt-svelte

v0.51.9

Published

Svelte 5 components for SMRT user management - auth, users, tenants, roles, permissions, groups

Readme

@happyvertical/smrt-svelte

Board

@happyvertical/smrt-svelte/board provides a generic, accessible Svelte 5 Kanban-style board. It has no project, sales, or support dependency: supply your own cards, columns, card-to-column getter/setter, and typed card snippet. cards is controlled; defaultCards enables local state. onmove receives a typed intent and can be async—rejections restore the previous view. Set optimistic to present controlled moves while persistence is pending. Set allowSameColumnReorder={false} when an adapter persists only lane/status transitions rather than a position within a lane.

<script lang="ts">
  import { Board } from '@happyvertical/smrt-svelte/board';
</script>

<Board
  {columns}
  {cards}
  getCardColumnId={(card) => card.stage}
  setCardColumnId={(card, stage) => ({ ...card, stage })}
  getCardLabel={(card) => card.subject}
  card={cardSnippet}
  onmove={({ card, target }) => save({ ...card, stage: target.columnId })}
/>

The same primitive can represent support queues (card.queue) or a sales pipeline (opportunity.stage) without importing @happyvertical/smrt-projects.

Svelte 5 component library for the s-m-r-t framework. Provides UI components, browser AI integration (STT/TTS/LLM with warm cache), a theme system, permission-aware rendering, and module UI registry for agent admin panels.

Installation

pnpm add @happyvertical/smrt-svelte

Data-surface browser bridge security

The browser bridge is a transport adapter, not an authentication system. Configure it only with a session/source binding established by the server and use a transport that supplies verified peer metadata. It accepts commands only from the configured server peer and emits acknowledgements/events only on the bound route; wire sessionId and source fields must never be treated as proof of identity.

The adapter canonicalizes requests and applies the shared identifier limit from @happyvertical/smrt-ui/data-surface along with bounded envelopes before calling the registry. The registry remains the authority for command authorization and execution. Command IDs are idempotent while their bounded replay entries are retained; concurrent same-signature requests coalesce, a conflicting signature is rejected, and replay-capacity exhaustion is reported explicitly. Malformed requests and unauthenticated peers are ignored before an acknowledgement; valid requests that expire or encounter disconnect and transport failures produce bounded protocol outcomes without exposing registry state.

Usage

Query-backed data surfaces

Use the web binding when a table should render one remote page instead of hydrating its whole collection. It exposes rows, page, total, loading, refreshing, stale, error, retry, lastUpdated, and the latest applied result. The result getter also follows query-scoped live replacements, so consumers can keep envelope metadata in sync with the rows.

<script lang="ts">
  import { remoteQuery } from '@happyvertical/smrt-svelte/web';
  const view = remoteQuery(collection, transport);
  // Failures remain available as view.error for reactive rendering.
  void view.execute(request).catch(() => undefined);
</script>

{#if view.loading}<p>Loading…</p>{/if}
{#each view.rows as row (row.id)}<div>{row.name}</div>{/each}

Provider Setup

<script>
  import { Provider } from '@happyvertical/smrt-svelte';
  let { children } = $props();
</script>

<Provider user={data.user} permissions={data.permissions}
  ai={{ preload: 'idle', stt: { type: 'whisper-cpp' } }}>
  {@render children()}
</Provider>

The Provider can own generated WebMCP tools for the same lifecycle. Its policy is identical to registerWebMcpTools; omitted effects exposes reads only:

<Provider webmcp={{
  definitions,
  effects: ['read', 'write'],
  namespace: 'workspace',
  maxTools: 24
}}>
  {@render children()}
</Provider>

This controls capability exposure, not authorization. Tool execution still crosses the authenticated REST boundary and must retain its auth and tenancy guards. The effects, filter, and filterTool policy applies to generated data/model tools only; the fixed mounted-UI adapter has the separate controls described below.

WebMCP test doubles and polyfills should implement the browser's promise-returning document.modelContext.registerTool() contract; declare the function async when migrating older void-returning fixtures.

Mounted UI through WebMCP

<Provider webmcp> registers six fixed smrt_ui_* tools for the mounted UI: list, inspect, and execute for form controls and data surfaces. The tool set does not change as components mount and unmount; each call reads the current transport-neutral registries instead of inspecting or simulating the DOM.

Forms automatically join the Provider's control registry. An explicit Form interactionRegistry still takes precedence. Pass the same data-surface registry used by DataTable or CollectionToolbar when those mounted surfaces should be discoverable:

<script lang="ts">
  import { createDataSurfaceRegistry } from '@happyvertical/smrt-ui/data';
  import { Provider } from '@happyvertical/smrt-svelte';

  const surfaces = createDataSurfaceRegistry();
</script>

<Provider webmcp={{ ui: { dataSurfaceRegistry: surfaces } }}>
  <!-- pass {surfaces} to mounted data-surface components -->
  {@render children()}
</Provider>

A page with its own hand-rolled list markup — not DataTable — that already mirrors a headless DataTableController's search/filters/sort/page/selection can register that same registry in one call with mountListDataSurface (@happyvertical/smrt-svelte/web) instead of hand-writing the registration:

import { createDataTableController } from '@happyvertical/smrt-ui/data';
import { mountListDataSurface } from '@happyvertical/smrt-svelte/web';

const controller = createDataTableController();
const handle = mountListDataSurface({
  registry: surfaces,
  descriptor: myListDescriptor,
  controller,
  context: { totalRows, queryFingerprint },
  refresh: () => reload(),
});

// Only `controller` is observed automatically. App-owned `context` is a
// one-time snapshot at mount — publish a fresh one whenever totalRows,
// queryFingerprint, or freshness changes (an $effect keyed on those values
// is the usual place):
handle.update({ totalRows, queryFingerprint });

// on unmount:
handle.destroy();

It mirrors controller into the registry, translates visible table commands back into controller.dispatch() calls, and routes the fixed refresh/retry/focus/reveal/highlight controls to callbacks; any other controlId goes through an onControl escape hatch (denied by default) — except a canonical table-control id (set-filters, reset, set-page, … the full DATA_TABLE_SURFACE_CONTROL_IDS list from @happyvertical/smrt-ui/data), which is always intercepted first and never reaches onControl, even under a custom label. See docs/data-surface-conformance.md for the full contract.

The default prefix is smrt_ui_. Configure ui.prefix when multiple Providers must coexist in one document; the same prefix cannot be registered twice. The six derived names are reserved through the document-global tool-name lock, so a generated model tool, a view intent, or a useWebMcpTool tool that would take one of them under your prefix fails at registration with a message naming the tool and its current owner, rather than being silently dropped by the browser. ui: false disables only the fixed UI adapter while leaving generated model tools enabled. For compatibility, an object config that omits ui continues to enable only generated model tools; use webmcp={true} or provide ui: {} to enable the mounted-UI adapter.

Form commands always run with source: 'agent'. WebMCP input cannot assert confirmation: staging is allowed by the registry policy, while apply, clear, and undo require a separate human-confirmed path. Secret control values and hidden data-surface columns are not serialized. Read responses are marked as untrusted content. Bespoke useWebMcpTool and <Form webmcp> tools retain their existing lifecycle and submit behavior.

Custom rich fields may continue to call registerField(field) and later unregisterField(name). New code should retain and invoke the disposer returned by registerField: it is bound to that exact registration, so cleanup cannot remove a same-name replacement. The return value is additive; legacy form contexts whose registerField returns void remain supported. Context accessors bind legacy name-based cleanup to registrations made by that caller, so overlapping same-name fields can unmount in either order without retaining a detached control.

Form Components

<script>
  import { TextInput, Select, MoneyInput, DateTimeInput, Toggle } from '@happyvertical/smrt-svelte/forms';
</script>

<TextInput label="Name" bind:value={name} />
<MoneyInput label="Price" bind:value={price} currency="USD" />
<DateTimeInput label="Launch Date" bind:value={date} />
<Toggle label="Active" bind:checked={active} />

UI Foundation

<script>
  import { Button, Card, Badge, Pagination } from '@happyvertical/smrt-ui/ui';
  import { DataTable } from '@happyvertical/smrt-ui/data';
</script>

<Card>
  <DataTable columns={cols} data={rows} pageSize={20} />
  <Pagination currentPage={1} totalPages={5} />
</Card>

Permission-Aware Rendering

<script>
  import { PermissionCheck, permission } from '@happyvertical/smrt-ui';
</script>

<PermissionCheck requires="admin:write">
  <button>Admin Action</button>
</PermissionCheck>

<!-- Or as a Svelte action -->
<div use:permission={{ slug: 'admin:read', permissions: userPermissions }}>
  Protected content
</div>

Theme System

<script>
  import { ThemeProvider } from '@happyvertical/smrt-ui/themes';
</script>

<ThemeProvider preset="glass" colorScheme="system">
  {@render children()}
</ThemeProvider>

Admin Workspace

<script lang="ts">
  import { manifest } from '$lib/smrt-manifest';
  import {
    AdminShell,
    TenantNav,
    tenantNavFromManifest,
  } from '@happyvertical/smrt-svelte/workspace';

  let { children } = $props();

  const sections = tenantNavFromManifest(manifest, {
    sectionHints: {
      '@happyvertical/smrt-content': 'Content',
      '@happyvertical/smrt-profiles': 'Profiles',
    },
  });

</script>

<AdminShell title="Admin">
  {#snippet tenantPanel()}
    <TenantNav items={sections} currentHref="/admin/articles" />
  {/snippet}

  {@render children?.()}
</AdminShell>

Filter the same manifest by role permissions when only a subset of resources should be visible:

import { tenantNavFromManifest } from '@happyvertical/smrt-svelte/workspace';

const editorSections = tenantNavFromManifest(manifest, {
  permittedResources: [
    '@happyvertical/smrt-content:Article',
    '@happyvertical/smrt-content:Document',
  ],
  sectionHints: {
    '@happyvertical/smrt-content': 'Content',
  },
});

In SvelteKit, build the nav in a +layout.server.ts (server-side, no client fetch) and mount AdminShell in +layout.svelte. The template-sveltekit scaffold adopts AdminShell as its default chrome exactly this way; copy its src/routes/+layout.server.ts / +layout.svelte / settings/+page.svelte.

  • Migration guide (first-generation WorkspaceShell/RoleShellAdminShell; adoption is additive and non-breaking): src/components/workspace/MIGRATION.md
  • Playground demos: playground/src/routes/admin-shell exercises all four scopes, focus tools, and activities; admin-shell-activity-feed and admin-shell-system-feed show live feeds.

Exports

Entry Points

This is the complete exports map of this package. Anything not listed is not importable, even if it appears in dist/.

| Import Path | Contents | |-------------|----------| | @happyvertical/smrt-svelte | Provider, hooks (useAppState, useAuth, useLLM, useSocket, useSTT, useTheme, useTTS), app state/context, ModulePanel, and the form components below | | @happyvertical/smrt-svelte/forms | Form inputs (TextInput, Select, MoneyInput, DateTimeInput, Toggle, etc.) | | @happyvertical/smrt-svelte/settings | Server-paged settings search, selection, and list/detail layout (SettingsCatalog, paginateSettingsCatalog) | | @happyvertical/smrt-svelte/workspace | AdminShell, ShellState, tenant nav, focus tools, settings, activities, and system/app panels | | @happyvertical/smrt-svelte/workspace/legacy | Opt-in ToolsDock compatibility surface for applications migrating to AdminShell | | @happyvertical/smrt-svelte/workspace/server | Server-side workspace helpers (Node only) | | @happyvertical/smrt-svelte/workspace/live | systemFeed — the AdminShell system scope (jobs/schedules/dispatch) polled from an app status endpoint; deliberately carries no smrt-web dependency | | @happyvertical/smrt-svelte/browser-ai | Browser AI client (STT/TTS/LLM adapters, capability detection) | | @happyvertical/smrt-svelte/browser-ai/svelte | Svelte AI components (VoiceInput, CapabilityGate, etc.) | | @happyvertical/smrt-svelte/web | smrt-web live-query bindings (liveCollection, activityFeed, useUpdateAvailable) plus mountListDataSurface (custom-list data-surface registration) | | @happyvertical/smrt-svelte/i18n/server | Server-side i18n resolver (Node only) |

Domain-agnostic UI lives in @happyvertical/smrt-ui. There is no ui, layout, calendar, data, chat, feedback, registry, themes, i18n, or styles/tokens.css subpath on smrt-svelte, so those specifiers only resolve against smrt-ui:

| Import Path | Contents | |-------------|----------| | @happyvertical/smrt-ui | PermissionCheck, permission / hasPermission / hasAnyPermission / hasAllPermissions | | @happyvertical/smrt-ui/ui | UI primitives (Button, Card, Badge, Pagination) | | @happyvertical/smrt-ui/layout | Layout (Container, Grid, Header, Footer, Masthead, etc.) | | @happyvertical/smrt-ui/calendar | Calendar and DayView | | @happyvertical/smrt-ui/data | DataTable, CollectionList/ContentList, CollectionToolbar | | @happyvertical/smrt-ui/feedback | Modal, ConfirmDialog, LoadingOverlay, ProgressBar | | @happyvertical/smrt-ui/chat | Message bubble, reaction picker, typing indicator | | @happyvertical/smrt-ui/registry | ModuleUIRegistry for agent admin panels | | @happyvertical/smrt-ui/themes | Canonical ThemeProvider, Material/Glass/Studio/s-m-r-t/HappyVertical presets, CSS generation | | @happyvertical/smrt-ui/i18n | Client i18n (useI18n, Trans) — the counterpart to smrt-svelte's /i18n/server | | @happyvertical/smrt-ui/styles/tokens.css | Design tokens CSS |

forms is the one name on both: @happyvertical/smrt-ui/forms holds the Provider-free primitives (Input, Select, Textarea, Toggle, FormGroup), and @happyvertical/smrt-svelte/forms re-exports those and adds the Provider-backed inputs, so it stays the one-stop barrel for applications.

The first-generation WorkspaceShell, RoleShell, NavTree, and Breadcrumbs have no entry point at all. Their .svelte files are copied into dist/ but no export subpath or barrel names them, so they cannot be imported from an installed package — see the migration guide. AdminShell supersedes them.

Legacy ToolsDock availability is presentation-only, not authorization. fetchAvailability failures intentionally keep controls usable using the current context's last-known-good result, or registered-tool metadata after a context change. Every tool operation and server endpoint must independently enforce permissions. Consumers can surface current-context degraded state through dock.availabilityError; a context change or later valid refresh clears it.

Components by Category

Forms: AddressInput, CheckboxInput, DateRangeInput, DateTimeInput, FileUpload, Form, FormGroup, FormMicButton, Input, MeasurementInput, MoneyInput, NumberInput, PhoneInput, SearchInput, Select, SelectInput, Textarea, TextareaInput, TextInput, Toggle

Layout: Container, EmptyState, Footer, Grid, Header, Masthead, PageHeader, SummaryCard

UI: Badge, Button, Card, Pagination

Display (from @happyvertical/smrt-ui): ConfidenceBadge, CurrencyDisplay, DateDisplay, Icon, StatusBadge

CurrencyDisplay accepts Commerce-compatible string currency fields. It trims and uppercases ISO 4217 codes before formatting, defaults to CAD, and renders an accessible inline error for malformed or unsupported codes instead of throwing and interrupting the surrounding collection render. The historical unit="cents" option means ISO minor units, so currencies with zero or three minor digits are scaled correctly. Minor-unit amounts must be finite safe integers; fractional or unsafe numeric values render an accessible inline error. unit="dollars" means major units. ISO codes whose minor unit is N.A. require unit="dollars"; minor-unit mode renders an accessible inline error for those codes, while major-unit mode uses a stable two-digit display policy. CAD and USD retain their symbol display; all other currencies render their ISO code for deterministic SSR hydration.

Feedback: ConfirmDialog, LoadingOverlay, Modal, ProgressBar

Navigation: FilterChips, Tabs

Data: DataTable

Permissions: PermissionCheck, RoleBadge, RoleSelector

Other: Calendar, DayView, MembershipCard, MembershipList, ModulePanel

The agent-admin shells (AgentAdminPanel, AgentAdminTabs, AgentSettingsShell) moved to @happyvertical/smrt-agents/svelte (#1589).

Browser AI: AILoadingOverlay, CapabilityGate, DownloadProgress, STTTest, VoiceInput

Hooks

useAuth, useSocket, useAppState, useSTT, useTTS, useLLM, useTheme

Functions & Actions

hasPermission, hasAnyPermission, hasAllPermissions, permission (action), ripple (action)

Cache API

getCachedSTT, getCachedTTS, getCachedLLM, getCacheStats, clearAllCaches

Dependencies

  • @happyvertical/smrt-types -- shared type definitions
  • Peer: svelte >=5.18.2, @happyvertical/smrt-jobs, @happyvertical/smrt-profiles, @happyvertical/smrt-users (all optional)