State Management
UI state is managed by Pinia, split across two stores. This guide describes the store architecture, the key state each holds, and how the apis/ layer interacts with them.
Store setup
packages/webview/src/stores/index.js creates a single Pinia instance and exports setupStore(app) to install it. The stores follow a strict two-store separation (documented in stores/AGENTS.md):
- Viewer store — "how to look at the document" (UI / view state).
- Document store — "what's in the document & how to draw on it" (data + tool styles).
Both stores are referenced by ~102 files across components/ and apis/.
Viewer store — modules/viewer.js (useViewerStore, ~1770 lines)
Holds UI / view state. Highlights:
| Category | Fields |
|---|---|
| View / navigation | currentPage, scale, oldScale, newScale, zoomLevel (preset array), scrollMode, pageMode, pageLabels. |
| Theme / display | theme ('light'/'dark'), fullScreen, language, languages[]. |
| Tool mode | toolMode ('view' default), activeHand, activeStickNote, activeActiveMeasure. |
| Elements open/close | activeElements{} (boolean map keyed by DataElements.*), activeElementsTab{}, panelSpace{left,right}, openedPanelsNum{left,right}. |
| Disabled elements | disabledElements{} — { disabled, style } per element. |
| Toolbar definition | headers{} (per group), rightHeaders[], toolItems{}, documentEditorHeaders, toolbarGroup, activeHeaderGroup. |
| Signature config | signatureDialogTabs, signatureDialogProperties{}, enableSignatureTemplates, digitalSignature{}, selectedSignatureField, disableSignatureTool, electronicSignaturesList[], digitalSignaturesList[]. |
| Stamps | customStamps[], stampPanelTabs[], importStamps[]. |
| Compare | compareStatus, compareMode, showCompareTip. |
| Popups | textPopup[], cropPagePopup[], contentEditorPopup[], annotationPopup[], showAnnotationPopup, popoverChanged. |
| Form board | formBoardFold, formBoardPosition{}, enableFillForm, skipNoRequired, consecutiveType. |
| Misc flags | license, verified, readOnly, userAdmin, isDisabledHeader, downloading, upload, uploadLoading, customLoading, documentEditorSaveStatus, documentEditorSaveAsStatus, webviewerMode. |
| Fonts | customFonts[], fontSizes[]. |
| Panels config | genericPanels[], customModals[]. |
| Annotation flags | annotation{isAnnotationExport, isAnnotationImport}. |
Key actions
openElement(dataElement, capability?)— setsactiveElements[de]=true; closes sibling panels on the same side; onLEFT_PANELtogglescore.toggleSidebar(); onSEARCH_PANELalso opensLEFT_PANELand switches its tab.closeElement/openElements/closeElements/toggleElement/resetPanels— variants. ClosingLEFT_PANELalso callscore.webViewerPageMode('none')and closesSEARCH_PANEL.setActiveElementTab(dataElement, tab)— mapsThumbnailsButton→core.webViewerPageMode('thumbs'),OutlinesButton→'outline'.setActiveToolMode(mode)— guardsreadOnlyand mobile (blocksseparation/measurement); closes context panels; callscore.setToolMode(mode).setToolbarGroup(value)— mobile guard for Separation/Measurement.disableElement/enableElement/disableElements/enableElements(de, key?)— toggle disabled flags.- Stamp/signature list mutators persist to
localStorage. dispatch(action)/getState(key)— explicitly the API-access channel; expose store actions/state toapis/.
Document store — modules/document.js (useDocumentStore, ~600 lines)
Holds document data + tool configuration. Highlights:
| Category | Fields |
|---|---|
| Document meta | totalPages, loadingProgress, annotationsCount, info, currentPdfData{}, fileHasPwd, pwd. |
| Annotations | annotations (shallowReactive), selected{}, selectedAnnotation, outlines[], activeOutlineId, searchResults[], activeSearchResult, layers[], selectedLayer. |
| Current tool | activeTool, markupTool (default HIGHLIGHT), shapeTool (default SQUARE). |
| Tool style presets | per-tool { color, opacity?, stroke? } (highlight, underline, squiggly, strikeout, square, arc, polygon, polyline, circle, arrow, line, ink, freetext, text, form fields, redaction, remove). |
| Property panel | propertyPanel{} — form-field/editor property model. |
| Compare | compareFiles{}, compareSettings{}, compareResult. |
| Signatures | signatures[], selectedSignature, trustedCertificateLists[], pkcs12, author, annotator, highlightLink, highlightForm, autoJumpNextSign, disableDigitalSignature, disableElectronicSignature. |
| Measure | measure{scale, precision}. |
| Document editor | documentEditor{operationList[], pageList[], selectedPageList[]}. |
| Undo/redo | canUndo{content, annotation}, canRedo{content, annotation}. |
Key actions
setActiveToolColor(color)— writesthis[activeTool].colorand callscore.setTool(activeTool).setToolState(tool)— toggles off if same; otherwise looks upmap[tool], callscore.setTool(mapTool.tool, mapTool.toolName), setsactiveTool, and for form tools callscore.setInitProperty().setActiveToolColor/setPropertyPanel/setCurrentUser/setHighlightLink/setHighlightForm/setAutoJumpNextSign— push the value intocorein addition to storing it.initAnnotations/setAnnotations(annotation, type)— manage the annotation array by page index.dispatch(action)/getState(key)— API access.
How apis/ interacts with the stores
Each apis/*.js module is a factory (stores...) => (...args) => result. Two access patterns:
- Via getters for reads:
getActiveTool.js→store.getActiveTool. - Via
dispatchfor actions:closeElement.js→store.dispatch('closeElement')(dataElement).
Some APIs (openElement, closeElements, enableElements, disableElements, addPanel, getPanels, isElementOpen, getPassword) use dispatch/getState; others call getters directly. api_helpers/createEncapsulatedStore.js builds the encapsulated viewerStore / documentStore wrappers (exposing only dispatch/getState) used by the popup APIs, getPassword, and getPanels.
Persistence
localStorage['language']— selected language.localStorage['customStamps'],['electronicSignatures'],['digitalSignatures']— stamp/signature lists.
Related
- Architecture — the store↔engine mirroring pattern.
- Core Bridge
- API Reference