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:
<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:
.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
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:
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:
<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
$effectfor side effects instead ofonMountwhen 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
-
Start the dev server:
pnpm dev - Open http://localhost:5173
-
Modify
test_context.tsto test different scenarios - Test responsive design at different screen sizes
Integration Testing
Test your plugin in the full IDAH platform:
-
Build the plugin:
pnpm build - Copy the plugin to IDAH's plugins directory
- Restart IDAH
- 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
- Backend Guide - Add backend services to your plugin
- Import & Process Media - Process media files
- Export Datasets - Export and sync data
🎨 Frontend ready! Add backend services to complete your full-stack plugin.