Skip to content

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).

ActionTypes

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 }

Clone this wiki locally