Frontend Guide

This guide covers frontend development for IDAH plugins using SvelteKit and Svelte 5.

Overview

IDAH plugin frontends are built with SvelteKit and Svelte 5, providing a modern, reactive UI framework. Plugins are compiled to UMD bundles and loaded dynamically by the IDAH platform. The Activity Context API provides access to datasets, annotations, tools, and workflow management.

Technology Stack:

  • Framework: SvelteKit with Svelte 5
  • Language: TypeScript
  • Build Tool: Vite
  • Package Manager: pnpm (recommended) or npm

File Structure

<plugin_name>/
   └─ frontend/
      ├─ README.md                           # Frontend quick start guide
      ├─ package.json                        # npm dependencies and scripts
      ├─ pnpm-lock.yaml                      # Locked dependencies
      ├─ pnpm-workspace.yaml                 # pnpm workspace configuration
      ├─ svelte.config.js                    # SvelteKit configuration
      ├─ vite.config.ts                      # Vite build configuration
      ├─ tsconfig.json                       # TypeScript configuration
      ├─ .gitignore                          # Git ignore rules
      ├─ .npmrc                              # npm configuration
      ├─ src/
      │  ├─ app.html                            # HTML template
      │  ├─ app.d.ts                            # TypeScript declarations
      │  ├─ lib/
      │  │  ├─ index.ts                            # Library exports
      │  │  ├─ context.ts                          # Activity Context interface
      │  │  ├─ assets/
      │  │  │  └─ favicon.svg                         # Favicon
      │  │  └─ plugin/
      │  │     ├─ plugin.svelte                       # Main plugin component (your code here!)
      │  │     └─ plugin.css                          # Plugin styles
      │  └─ routes/
      │     ├─ +layout.svelte                      # Root layout
      │     ├─ +page.svelte                        # Main page (loads plugin)
      │     └─ test_context.ts                     # Mock Activity Context for development
      └─ static/
         └─ robots.txt                          # Robots.txt for SEO

Key Files:

  • src/lib/plugin/plugin.svelte - Your main plugin component - develop your UI here
  • src/lib/context.ts - Activity Context TypeScript interface
  • src/routes/test_context.ts - Mock context for local development
  • vite.config.ts - Build configuration (UMD output for IDAH)

Getting Started

Prerequisites

  • Node.js 18+
  • pnpm (recommended) or npm

Installation

pnpm install pnpm dev pnpm build pnpm preview

Development Workflow

1. Develop Your Plugin

Edit src/lib/plugin/plugin.svelte - this is your main plugin component:

src/lib/plugin/plugin.svelte
<script lang="ts">
  import { onMount } from 'svelte'
  import type { IActivityContext } from "$lib/context";

  let { context }: { context: IActivityContext } = $props();

  onMount(() => {
    console.log('Plugin mounted!', context)
  })
</script>

<div class="plugin-container">
  <h1>My Plugin</h1>
  <p>Your plugin description here</p>

  <!-- Your plugin UI here -->
</div>

<style>
  .plugin-container {
    padding: 1rem;
  }
</style>

2. Use the Activity Context

Activity Context API Reference

The IActivityContext interface provides these core APIs:

Basic Properties

context.id              // Current entry ID
context.type            // Activity type
context.workflowStep    // 'start' | 'annotate' | 'review' | 'done' | 'export'
context.status          // Entry status
context.config          // Dataset configuration
context.mediaUrl        // Root media URL
context.user            // Current user info
context.userRole        // User role

Annotations Driver

await context.annotations.create(
  id,           // unique ID (optional, defaults to: uuidv7())
  dimensions,   // shape/dimension data
  annotation,   // annotation values
  metadata      // optional metadata
)

await context.annotations.update({
  id,
  dimensions,
  annotation,
  metadata
})

await context.annotations.delete(id)

const results = await context.annotations.list(
  { entry_id: context.id },
  { page: 1, itemsPerPage: 100 }
)

context.annotations.flush()

Commands (Undo/Redo System)

context.commands.on('commandName', (props) => {
  return {
    name: 'Human-readable name',
    async apply() {
      // Do the action
    },
    async undo() {
      // Undo the action
    },
    isCombinable: () => false,
    combine: (cmd) => cmd
  }
})

context.commands.run('commandName', { /* props */ })

context.commands.undo()
context.commands.redo()

Tools (Header Bar Tools)

context.tools.setTools([
  {
    type: 'visual',
    label: 'Visual',
    iconName: 'mouse-pointer-2',
    handleClick: () => context.commands.run('tools.visual')
  },
  {
    type: 'bounding-box',
    label: 'Bounding Box',
    iconName: 'vector-square',
    disabled: !['annotate', 'review'].includes(context.workflowStep),
    handleClick: () => context.commands.run('tools.boundingBox')
  }
])

context.tools.setTool('visual')

context.tools.onToolChange((tool) => {
  console.log('Tool changed:', tool)
})

Notes

context.notes.showNewNoteFeedPopup({
  anchor_type: 'annotation',  // 'entry' | 'annotation'
  position: { x: 100, y: 200, sidebar_width: 256 },
  annotation_id: 'ann-123'
})

context.notes.gotoFeed(noteFeedId, noteCommentId)

context.notes.onNoteSelected((noteFeedId, noteCommentId) => {
  // Handle note selection
})

Workflow Navigation

await context.submit({ approved: true })

context.back()

await context.error('Error message')

3. Style Your Plugin

Add styles in src/lib/plugin/plugin.css or use scoped styles within your Svelte components:

src/lib/plugin/plugin.css
.plugin-container {
  font-family: system-ui, sans-serif;
  max-width: 1200px;
  margin: 0 auto;
}

Building for Production

Build the Plugin

pnpm build

This generates:

  • build/plugin.umd.js - Main plugin bundle
  • build/plugin.css - Plugin styles
  • build/entryDetailsBuild.js - Entry details component (if applicable)
  • build/entryDetailsBuild.css - Entry details styles

Build Output

The build process creates UMD (Universal Module Definition) bundles that can be loaded by the IDAH platform. These files are referenced in your plugin's manifest.json:

{
  "entryPoints": {
    "entryPlugin": {
      "script": "/frontend/build/plugin.umd.js",
      "style": "/frontend/build/plugin.css"
    }
  }
}

TypeScript

Type Checking

pnpm check pnpm check:watch

Adding Types

Define your types in src/lib/types.ts or alongside your components:

export interface MyPluginData {
  id: string
  title: string
  metadata: Record<string, any>
}

Best Practices

1. Component Organization

Recommended Component Organization
src/lib/
   └─ plugin/
      ├─ plugin.svelte                  # Main plugin component
      ├─ components/                    # Reusable components
      │  ├─ Header.svelte
      │  ├─ DataTable.svelte
      │  └─ Chart.svelte
      ├─ stores/                        # Svelte stores for state
      │  └─ pluginState.ts
      ├─ utils/                         # Utility functions
      │  └─ formatters.ts
      └─ types/                         # TypeScript types
         └─ index.ts

2. State Management

Use Svelte stores for shared state:

src/lib/plugin/stores/pluginState.ts
import { writable } from 'svelte/store'

export const selectedItems = writable<string[]>([])
export const isLoading = writable(false)

3. Error Handling

Edit src/lib/plugin/plugin.svelte - this is your main plugin component:

src/lib/plugin/plugin.svelte
<script lang="ts">
  let error = $state<string | null>(null)

  async function loadData() {
    try {
      // ... load data
    } catch (err) {
      error = err instanceof Error ? err.message : 'Unknown error'
      console.error('Failed to load data:', err)
    }
  }
</script>

{#if error}
  <div class="error-message">{error}</div>
{/if}

4. Performance

  • Use $effect for side effects instead of onMount when appropriate
  • Lazy load heavy components
  • Optimize images and assets
  • Use virtual scrolling for large lists

5. Accessibility

  • Use semantic HTML
  • Add ARIA labels where needed
  • Ensure keyboard navigation works
  • Test with screen readers

Testing

Manual Testing

  1. Start the dev server: pnpm dev
  2. Open http://localhost:5173
  3. Modify test_context.ts to test different scenarios
  4. Test responsive design at different screen sizes

Integration Testing

Test your plugin in the full IDAH platform:

  1. Build the plugin: pnpm build
  2. Copy the plugin to IDAH's plugins directory
  3. Restart IDAH
  4. Test with real data

Troubleshooting

Port Already in Use

If port 5173 is busy, Vite will try the next available port. You can also specify a port:

pnpm dev -- --port 3000

Build Errors

Clear the build cache and reinstall:

rm -rf node_modules build .svelte-kit pnpm install pnpm build

Type Errors

Regenerate SvelteKit types:

pnpm run prepare

Resources

Next Steps

🎨 Frontend ready! Add backend services to complete your full-stack plugin.