-
Notifications
You must be signed in to change notification settings - Fork 261
Architecture overview 4: Mirador state changes for plugin authors
Marlo Longley edited this page Oct 2, 2025
·
19 revisions
Mirador uses React for its UI and Redux for state management. If you're building a plugin for Mirador and want to respond to user actions or state changes (like opening a window, selecting a canvas, or updating layout), this info will help.
Redux uses:
- Actions – objects describing what happened
- Reducers – functions that update the state
- Selectors – helper functions to read parts of the state
- Middleware – logic that can run between actions and reducers
Plugin authors might want to respond to state changes. This is an example of an analytics plugin that wants to know when a canvas is set. Mirador emits an action, which plugin authors can "listen" for.
import { all, put, select, takeEvery } from 'redux-saga/effects'
import { ActionTypes, getManifest } from 'mirador'
/** Log canvas selection */
function* onSetCanvas({ type, windowId, canvasId }) {
const { id: manifestId } = yield select(getManifest, { windowId })
console.log('Canvas selected:', {
manifestId,
canvasId,
action: type,
})
}
/** Main saga that wires up listeners */
function* analyticsSaga() {
yield all([
takeEvery(ActionTypes.SET_CANVAS, onSetCanvas),
// Add more listeners if desired
])
}
export default {
// Mirador looks for a saga export
saga: analyticsSaga,
component: () => null, // No UI component needed
}There are two key imports here.
-
ActionTypes, which are all the actions you can listen for from Mirador. A list of all ActionTypes is listed at the end of this file. -
getManifest, which is a selector exported from Mirador for reading the state.
For a similar plugin in the real world, see Stanford's analytics plugin.
The plugin can be included in Mirador's typical plugin array on initialization (related example here).
| Action Type | Action Payload Schema |
|---|---|
| ADD_COMPANION_WINDOW | { id: string, payload: { id: string, windowId: string, content?: any, position?: any, ... }, windowId: string } |
| UPDATE_COMPANION_WINDOW | payload varies on dispatch context (see calls to updateCompanionWindow, setWindowThumbnailPosition, ) { id: string, payload: object, windowId: string }
|
| REMOVE_COMPANION_WINDOW | { id: string, windowId: string } |
| TOGGLE_TOC_NODE | { id: string, payload: { [nodeId: string]: { expanded: boolean } }, windowId: string } |
| UPDATE_WINDOW | payload varies on dispatch (see calls to updateWindow) { id: string, payload: object }
|
| REQUEST_CANVAS_ANNOTATIONS | { windowId: string, canvasId: string } |
| HOVER_ANNOTATION | { windowId: string, annotationIds: Array<string> } |
| REQUEST_ANNOTATION | { targetId: string, annotationId: string } |
| RECEIVE_ANNOTATION | { targetId: string, annotationId: string, annotationJson: object } |
| RECEIVE_ANNOTATION_FAILURE | { targetId: string, annotationId: string, error: string } |
| DESELECT_ANNOTATION | { windowId: string, annotationId: string } |
| SELECT_ANNOTATION | { windowId: string, annotationId: string } |
| TOGGLE_ANNOTATION_DISPLAY | { windowId: string } |
| FOCUS_WINDOW | { windowId: string, pan?: boolean } |
| SET_WORKSPACE_FULLSCREEN | { isFullscreenEnabled: boolean } |
| SET_WORKSPACE_VIEWPORT_POSITION | (setWorkspaceViewportPosition) { payload: { position: { x: number, y: number } } }
|
| SET_WORKSPACE_VIEWPORT_POSITION | (setWorkspaceViewportDimensions) { payload: { position: { width: number, height: number } } }
|
| ADD_MANIFEST | |
| ADD_WINDOW | { window: { ...defaultOptions, ...options }, companionWindows: Array<object>, elasticLayout: object, manifest?: object } |
| ADD_ERROR | { id: string, message: string } |
| IMPORT_CONFIG | { config: object } |
| IMPORT_MIRADOR_STATE | { state: object (with workspace.id: string) } |
| SET_CANVAS | { windowId: string, canvasId: string, visibleCanvases: Array<string>, preserveViewport: boolean, ...options } |
| MAXIMIZE_WINDOW | { windowId: string } |
| MINIMIZE_WINDOW | { windowId: string } |
| REMOVE_WINDOW | { windowId: string } |
| REQUEST_MANIFEST | { manifestId: string, properties?: object } |
| RECEIVE_MANIFEST | { manifestId: string, manifestJson: object } |
| RECEIVE_MANIFEST_FAILURE | { manifestId: string, error: string or object } |
| REMOVE_ERROR | { id: string } |
| SET_CONFIG | { config: object } |
| UPDATE_WORKSPACE | { config: Object } |
| SET_WINDOW_VIEW_TYPE | { windowId: string, viewType: string } |
| SET_WORKSPACE_ADD_VISIBILITY | { isWorkspaceAddVisible: boolean } |
| TOGGLE_WINDOW_SIDE_BAR | { windowId: string } |
| TOGGLE_DRAGGING | no payload |
| TOGGLE_ZOOM_CONTROLS | { showZoomControls: boolean } |
| UPDATE_CONFIG | { config: object } |
| REMOVE_MANIFEST | { manifestId: string } |
| REQUEST_INFO_RESPONSE | { infoId: string, imageResource: object, windowId: string } |
| RECEIVE_INFO_RESPONSE | { infoId: string, infoJson: object, ok: boolean, tokenServiceId?: string } |
| RECEIVE_DEGRADED_INFO_RESPONSE | { infoId: string, infoJson: object, ok: boolean, tokenServiceId?: string, windowId: string } |
| RECEIVE_INFO_RESPONSE_FAILURE | { infoId: string, error: string, tokenServiceId?: string } |
| REMOVE_INFO_RESPONSE | { infoId: string } |
| UPDATE_WORKSPACE_MOSAIC_LAYOUT | { layout: Object } |
| UPDATE_VIEWPORT | {windowId: string, payload: {bounds: object, flip: boolean, rotation: number, x: number ,y: number,zoom: number} } |
| UPDATE_ELASTIC_WINDOW_LAYOUT | { windowId: string, payload: { height: number, width: number, x: number, y: number } } |
| ADD_AUTHENTICATION_REQUEST | { windowId: string, id: string, profile?: string } |
| RESOLVE_AUTHENTICATION_REQUEST | { id: string, tokenServiceId: string, ...props: object } |
| REQUEST_ACCESS_TOKEN | { serviceId: string, authId: string } |
| RECEIVE_ACCESS_TOKEN | { authId: string, serviceId: string, json: object } |
| RECEIVE_ACCESS_TOKEN_FAILURE | { authId: string, serviceId: string, error: object } |
| RESET_AUTHENTICATION_STATE | { id: string, tokenServiceId: string } |
| REQUEST_SEARCH | { windowId: string, companionWindowId: string, searchId: string, query: string } |
| RECEIVE_SEARCH | { windowId: string, companionWindowId: string, searchId: string, searchJson: object } |
| RECEIVE_SEARCH_FAILURE | { windowId: string, companionWindowId: string, searchId: string, error: string } |
| REMOVE_SEARCH | { windowId: string, companionWindowId: string } |
| SET_CONTENT_SEARCH_CURRENT_ANNOTATIONS | { windowId: string, companionWindowId: string, annotationIds: Array<string> } |
| UPDATE_LAYERS | { windowId: string, canvasId: string, payload: { [layerId: string]: { opacity?: number, visibility?: boolean, index?: number } } } |
| ADD_RESOURCE | { manifestId: string, manifestJson?: object, payload?: { provider: string } |
| REMOVE_RESOURCE | { manifestId: string } |
| SHOW_COLLECTION_DIALOG | { manifestId: string, dialogCollectionPath?: Array, windowId?: string } |
| HIDE_COLLECTION_DIALOG | { windowId: string } |