Plugin System
Triiiceratops features a flexible, component-based plugin system that allows you to extend the viewer's functionality by adding custom panels to the left or right sidebars.
Quick Start
Load the viewer and plugin bundles via script tags, then assign plugins to the viewer:
<!DOCTYPE html>
<html>
<head>
<!-- Load the viewer (self-contained, includes all dependencies) -->
<script src="https://unpkg.com/triiiceratops/dist/triiiceratops-element.iife.js"></script>
<!-- Load plugins -->
<script src="https://unpkg.com/triiiceratops/dist/triiiceratops-plugin-image-manipulation.iife.js"></script>
</head>
<body>
<triiiceratops-viewer manifest-id="https://example.com/manifest.json"></triiiceratops-viewer>
<script>
customElements.whenDefined('triiiceratops-viewer').then(() => {
const viewer = document.querySelector('triiiceratops-viewer');
viewer.plugins = [window.TriiiceratopsPlugins.ImageManipulation];
});
</script>
</body>
</html>
Import the viewer and plugin, then pass plugins via the plugins prop:
<script>
import { TriiiceratopsViewer } from 'triiiceratops';
import 'triiiceratops/style.css';
import { ImageManipulationPlugin } from 'triiiceratops/plugins/image-manipulation';
</script>
<div style="height: 600px;">
<TriiiceratopsViewer
manifestId="https://example.com/manifest.json"
plugins={[ImageManipulationPlugin]}
/>
</div>
How the Plugin System Works
Build System Overview
Triiiceratops provides two distribution formats to support different use cases:
| Format | Use Case | Plugins Loaded Via |
|---|---|---|
| IIFE Bundles | Static HTML pages, no build step | Script tags + window.TriiiceratopsPlugins |
| ES Modules | Vite/Svelte projects with bundlers | import statements |
IIFE Bundles (Script Tags)
The IIFE (Immediately Invoked Function Expression) bundles are self-contained JavaScript files that work in any browser without a build system.
How it works:
-
triiiceratops-element.iife.js(~700KB) bundles:- The
<triiiceratops-viewer>custom element - Svelte runtime
- OpenSeadragon image viewer
- All core dependencies
- Exposes a shared Svelte runtime for plugins
- The
-
Plugin IIFE bundles (e.g.,
triiiceratops-plugin-image-manipulation.iife.js):- Bundle the plugin's Svelte components and icons
- Use the shared Svelte runtime from the main element bundle
- Register themselves on
window.TriiiceratopsPlugins
Svelte Runtime Sharing
The viewer exposes its Svelte runtime on window.__TriiiceratopsSvelteRuntime, which plugins use to ensure getContext() and other Svelte features work correctly across bundle boundaries.
ES Module Exports
For projects using Vite, SvelteKit, or other bundlers, Triiiceratops exports ES modules that enable tree-shaking and integration with your existing build pipeline:
// Main Svelte component
import { TriiiceratopsViewer } from 'triiiceratops';
// Plugin components for manual assembly
import {
ImageManipulationController,
SlidersIcon,
} from 'triiiceratops/plugins/image-manipulation';
Plugin Definition
A plugin is defined by a PluginDef object:
import type { Component } from 'svelte';
interface PluginDef {
id?: string; // Unique identifier (auto-generated if not provided)
name: string; // Title shown in tooltips/headers
icon: Component; // Icon component (e.g., from phosphor-svelte)
panel: Component; // The Svelte component to render in the sidebar
position?: 'left' | 'right' | 'bottom' | 'overlay'; // Panel position (default: 'left')
props?: Record<string, unknown>; // Optional props to pass to the panel
}
Adding Plugins
When using the <triiiceratops-viewer> custom element, you cannot pass complex objects via HTML attributes. Instead, set the plugins property using JavaScript after the element is defined.
Script Tags (No Build Required):
<!-- Load the viewer -->
<script src="https://unpkg.com/triiiceratops/dist/triiiceratops-element.iife.js"></script>
<!-- Load plugins -->
<script src="https://unpkg.com/triiiceratops/dist/triiiceratops-plugin-image-manipulation.iife.js"></script>
<triiiceratops-viewer
manifest-id="https://iiif.wellcomecollection.org/presentation/v3/b18035723"
></triiiceratops-viewer>
<script>
customElements.whenDefined('triiiceratops-viewer').then(() => {
const viewer = document.querySelector('triiiceratops-viewer');
// Plugins are pre-configured objects on window.TriiiceratopsPlugins
viewer.plugins = [
window.TriiiceratopsPlugins.ImageManipulation
];
});
</script>
With a Build System:
If you're using the web component in a project with a bundler (Vite, Webpack, etc.):
When using the TriiiceratopsViewer Svelte component directly, pass plugins via the plugins prop.
<script>
import { TriiiceratopsViewer } from 'triiiceratops';
import 'triiiceratops/style.css';
import { ImageManipulationPlugin } from 'triiiceratops/plugins/image-manipulation';
</script>
<div style="height: 600px;">
<TriiiceratopsViewer
manifestId="https://example.com/manifest.json"
plugins={[ImageManipulationPlugin]}
/>
</div>
Multiple Plugins:
<script>
import { TriiiceratopsViewer } from 'triiiceratops';
import 'triiiceratops/style.css';
import { ImageManipulationPlugin } from 'triiiceratops/plugins/image-manipulation';
import MyCustomPlugin from './MyCustomPlugin.svelte';
import CustomIcon from './CustomIcon.svelte';
const plugins = [
ImageManipulationPlugin,
{
name: 'My Custom Feature',
icon: CustomIcon,
panel: MyCustomPlugin,
position: 'right'
}
];
</script>
<div style="height: 600px;">
<TriiiceratopsViewer manifestId="..." {plugins} />
</div>
Customizing Built-in Plugins:
If you need to customize the name, position, or other properties of a built-in plugin, you can import the individual components:
<script>
import { TriiiceratopsViewer } from 'triiiceratops';
import 'triiiceratops/style.css';
import { ImageManipulationController, SlidersIcon } from 'triiiceratops/plugins/image-manipulation';
const plugins = [
{
name: 'Custom Name',
icon: SlidersIcon,
panel: ImageManipulationController,
position: 'right' // Override default position
}
];
</script>
<div style="height: 600px;">
<TriiiceratopsViewer manifestId="..." {plugins} />
</div>
Available Plugins
Image Manipulation
Provides brightness, contrast, saturation, invert, and grayscale controls for the displayed image.
Creating Custom Plugins
Custom plugins are Svelte components that receive props from the plugin system and can access the viewer's state via Svelte context.
Plugin Component Props
Your plugin component receives these props:
| Prop | Type | Description |
|---|---|---|
isOpen |
boolean |
Whether the plugin panel is currently open |
close |
() => void |
Function to close the plugin panel |
Accessing Viewer State
Use Svelte's getContext to access the viewer's reactive state:
<script>
import { getContext } from 'svelte';
// Props from the plugin system
let { isOpen, close } = $props();
// Access the viewer's reactive state
const viewerState = getContext('triiiceratops:viewerState');
function handleZoomIn() {
viewerState.osdViewer?.viewport.zoomBy(1.5);
}
function handleNextCanvas() {
viewerState.nextCanvas();
}
</script>
<div class="p-4">
<h3>My Custom Plugin</h3>
<p>Current canvas: {viewerState.canvasId}</p>
<div class="flex gap-2">
<button onclick={handleZoomIn}>Zoom In</button>
<button onclick={handleNextCanvas}>Next Canvas</button>
<button onclick={close}>Close</button>
</div>
</div>
ViewerState Properties
The viewerState context provides access to:
| Property | Type | Description |
|---|---|---|
manifestId |
string \| null |
Current manifest URL |
canvasId |
string \| null |
Current canvas ID |
currentCanvasIndex |
number |
Index of current canvas (-1 if none) |
canvases |
any[] |
Array of canvas objects from manifest |
osdViewer |
OpenSeadragon.Viewer \| null |
OpenSeadragon instance |
showAnnotations |
boolean |
Whether annotations are visible |
showThumbnailGallery |
boolean |
Whether the thumbnail gallery is open |
showSearchPanel |
boolean |
Whether the search panel is open |
searchQuery |
string |
Current search query |
searchResults |
any[] |
Array of search results |
isSearching |
boolean |
Whether a search is in progress |
isFullScreen |
boolean |
Whether the viewer is in fullscreen mode |
dockSide |
string |
Current dock side for gallery |
hasNext |
boolean |
Whether there is a next canvas |
hasPrevious |
boolean |
Whether there is a previous canvas |
And methods:
| Method | Description |
|---|---|
nextCanvas() |
Navigate to the next canvas |
previousCanvas() |
Navigate to the previous canvas |
setCanvas(id) |
Navigate to a specific canvas by ID |
setManifest(id) |
Load a new manifest |
toggleFullScreen() |
Toggle fullscreen mode |
toggleAnnotations() |
Toggle annotation visibility |
toggleThumbnailGallery() |
Toggle thumbnail gallery |
toggleSearchPanel() |
Toggle search panel |
search(query) |
Perform a search (async) |
Registering Your Custom Plugin
Custom plugins for script-tag usage require building your own IIFE bundle that:
- Uses Svelte from
window.__TriiiceratopsSvelteRuntime - Registers on
window.TriiiceratopsPlugins
See vite.config.plugins-iife.ts in the Triiiceratops source for an example build configuration.
Simply import your custom component and add it to the plugins array:
<script>
import { TriiiceratopsViewer } from 'triiiceratops';
import 'triiiceratops/style.css';
import MyCustomPanel from './MyCustomPanel.svelte';
import MyIcon from 'phosphor-svelte/lib/Star'; // or your own icon
const plugins = [
{
name: 'My Custom Plugin',
icon: MyIcon,
panel: MyCustomPanel,
position: 'right'
}
];
</script>
<div style="height: 600px;">
<TriiiceratopsViewer manifestId="..." {plugins} />
</div>
Package Exports Reference
| Export Path | Description |
|---|---|
triiiceratops |
Main Svelte component and utilities |
triiiceratops/element |
Web component IIFE bundle |
triiiceratops/plugins/image-manipulation |
Image manipulation plugin (ES module) |
triiiceratops/plugins/image-manipulation.iife |
Image manipulation plugin (IIFE bundle) |
triiiceratops/style.css |
Stylesheet (for Svelte component usage) |