Zustand Store Structure for Managing Application State in Ontology-Playground
The Ontology-Playground application uses a single Zustand store defined in src/store/appStore.ts that organizes state into four distinct slices—Ontology, UI, Quest, and Query—each with dedicated actions for immutable updates.
The Microsoft Ontology-Playground project leverages Zustand to provide lightweight, type-safe state management for its ontology visualization and quest features. Understanding the Zustand store structure is essential for extending the application or debugging state-related issues, as the entire client-side state lives within a single create<AppStore> instance exported from the store module.
Store Architecture Overview
The state management layer centers on src/store/appStore.ts, which exports the main application store. Unlike Redux or Context-based solutions, Ontology-Playground uses a single monolithic store that combines all state slices and their corresponding actions in one TypeScript interface.
The store initialization pulls from default data constants including cosmicCoffeeOntology, sampleBindings, and defaultQuests, while also hydrating theme preferences from localStorage. Two helper utilities, isDarkTheme and themeClass, derive computed values from the theme and darkMode flags to simplify component-level styling logic.
State Slices and Interface
The AppState interface defines the contract for all mutable properties, organized into four functional domains.
Ontology State
This slice manages the core data model and its bindings:
currentOntology(Ontology): The active ontology loaded into the visual editor.dataBindings(DataBinding[]): Mappings that link ontology entities to runtime data sources.
UI State
Interaction state for the graph visualization and interface controls:
selectedEntityId(string | null): Identifies the currently selected node in the graph.selectedRelationshipId(string | null): Tracks the active edge selection.highlightedEntities(string[]): Array of entity IDs emphasized during quest guidance or search results.highlightedRelationships(string[]): Array of relationship IDs currently highlighted.showDataBindings(boolean): Toggles visibility of data-binding overlay panels.theme(ThemeId): Active theme identifier (dark,light,aurora,crimson).darkMode(boolean): Boolean flag affecting canvas rendering and syntax highlighting.
Quest State
Gamification layer for guided learning experiences:
availableQuests(Quest[]): Catalog of quest definitions available to the user.activeQuest(Quest | null): The quest currently in progress, ornullif none.currentStepIndex(number): Zero-based index tracking position within the active quest.completedQuests(string[]): IDs of finished quests for progress tracking.earnedBadges({ badge: string; icon: string }[]): Collection of rewards unlocked through completion.totalPoints(number): Aggregated score across all completed activities.
Query State
SPARQL-like interaction state:
queryInput(string): Raw text entered in the query editor.queryResult(string | null): Serialized output from the last executed query.
Actions and State Mutations
All state modifications flow through action functions defined in the store creator, using Zustand's set and get parameters to ensure immutable updates.
Ontology Actions
loadOntology(ontology, bindings): ReplacescurrentOntologyanddataBindingssimultaneously.resetToDefault(): Reverts to the initialcosmicCoffeeOntologystate.exportOntology(): Serializes the current ontology for download.
UI Actions
selectEntity(id)andselectRelationship(id): Update selection markers.setHighlightedEntities(ids)andsetHighlightedRelationships(ids): Batch-update highlight arrays.setHighlights(entities, relationships): Convenience method for updating both highlight arrays atomically.toggleDataBindings(): Flips theshowDataBindingsboolean.setTheme(theme): Updates the active theme ID.toggleDarkMode(): Switches thedarkModeflag and persists tolocalStorage.
Quest Actions
startQuest(questId): InitializesactiveQuestand resetscurrentStepIndexto 0.advanceQuestStep(): IncrementscurrentStepIndexwithin bounds.completeQuest(): Transfers the quest ID tocompletedQuests, awards badges, and clearsactiveQuest.abandonQuest(): Clears active quest state without awarding points.
Query Actions
setQueryInput(input): Updates the query editor text.setQueryResult(result): Stores serialized query output.clearHighlights(): Resets both entity and relationship highlight arrays.
Usage Examples
Accessing state in React components uses the standard Zustand selector pattern:
import { useAppStore } from '@/store/appStore';
// Subscribe to specific state slices
const theme = useAppStore(state => state.theme);
const selectedEntity = useAppStore(state => state.selectedEntityId);
// Trigger actions outside React render cycle
const toggleTheme = () => {
useAppStore.getState().toggleDarkMode();
};
Loading a custom ontology with bindings:
import { useAppStore } from '@/store/appStore';
import { myOntology } from '@/data/ontology';
// Actions accept multiple parameters
useAppStore.getState().loadOntology(myOntology, [{ /* binding objects */ }]);
Managing quest lifecycle:
const store = useAppStore.getState();
// Start a guided quest
store.startQuest('quest-42');
// Progress through steps
store.advanceQuestStep();
// Complete and award badges
store.completeQuest();
Executing queries and visualizing results:
const executeQuery = (queryString: string) => {
const store = useAppStore.getState();
store.setQueryInput(queryString);
// Query execution logic against current ontology
const result = runQuery(queryString, store.currentOntology);
store.setQueryResult(result);
};
Summary
- The Zustand store structure in Ontology-Playground consolidates all state in
src/store/appStore.tsusing a singlecreate<AppState>pattern. - State is organized into four slices: Ontology (data model), UI (selections and highlights), Quest (gamification), and Query (SPARQL interactions).
- All mutations occur through typed actions that receive
setandgetfor immutable updates, with specific handlers for loading ontologies, managing quest progress, and toggling UI modes. - Default values initialize from
cosmicCoffeeOntologyandlocalStoragepersists theme preferences across sessions.
Frequently Asked Questions
How does Ontology-Playground handle theme persistence?
The store checks localStorage during initialization to hydrate the theme and darkMode values. When toggleDarkMode() or setTheme() is called, the actions update both the store state and localStorage to ensure preferences survive page refreshes. The isDarkTheme helper computes the effective dark mode state based on the current theme selection.
Can I use multiple stores instead of the single appStore?
While the primary architecture uses a single store in src/store/appStore.ts, the codebase also exports a separate src/store/designerStore.ts for visual designer-specific state. This secondary store handles graph layout and canvas interactions independently, demonstrating that the application supports store composition when domain separation is necessary.
What is the difference between selectEntity and setHighlightedEntities?
selectEntity(id) updates selectedEntityId to indicate the user's active focus in the property panel, while setHighlightedEntities(ids) populates highlightedEntities to temporarily emphasize multiple nodes during quest guidance or search results. Selection represents user intent; highlights represent transient visual cues that can be cleared via clearHighlights().
How are quest badges and points calculated?
The completeQuest() action automatically appends the quest's defined badges to earnedBadges and increments totalPoints by the quest's reward value. These values are stored in the AppState interface as plain arrays and numbers, making them serializable for future persistence implementations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →