How TUUI Manages Global State with Pinia Store Architecture

TUUI leverages Pinia enhanced with a state-persistence plugin to create a reactive, singleton-based store system where domain-specific stores communicate directly to orchestrate chat inference, MCP tool execution, and UI state across the Electron-Vue application.

TUUI is an open-source Electron application that provides a chat interface for Large Language Models and Model-Context-Protocol (MCP) tools. According to the ai-ql/tuui source code, the application implements a modular Pinia architecture that serves as a single source of truth, enabling seamless data flow between the renderer process, background workers, and the main thread while automatically persisting critical state to localStorage.

Pinia Initialization and State Persistence

The global state layer is bootstrapped in the renderer entry point at src/renderer/main.ts. Here, the application creates a single Pinia instance and enhances it with the createStatePersistence plugin to ensure stores survive page reloads and application restarts.

import { createApp } from 'vue'
import { createPinia } from 'pinia'
import { createStatePersistence } from 'pinia-plugin-state-persistence'
import App from '@/renderer/App.vue'

const app = createApp(App)
const pinia = createPinia()

pinia.use(createStatePersistence())  // Enables automatic localStorage persistence

app.use(pinia).mount('#app')

The persistence plugin automatically serializes each store's state to localStorage under unique keys, restoring data when the app launches. This mechanism requires no additional configuration beyond the plugin registration, making stores like layoutStore and localeStore instantly resilient to reloads.

Domain-Specific Store Architecture

TUUI organizes its global state into logical domains under src/renderer/store/, with each store defined using Pinia's defineStore convention. This separation of concerns allows the messageStore to handle active chat sessions while delegating history management and tool execution to specialized stores.

Message Store

The src/renderer/store/message.ts file defines the core chat logic through useMessageStore. This store maintains the active conversation, userMessage input, and generating flags, while exposing actions like sendMessage(), startInference(), and processInference() that orchestrate the full inference lifecycle.

History Store

Located at src/renderer/store/history.ts, the useHistoryStore maintains a registry of all chat sessions. It provides CRUD helpers including init(), find(), save(), and remove() that the message store invokes to persist completed conversations or retrieve prior context for new sessions.

MCP Store

The src/renderer/store/mcp.ts wraps the Model-Context-Protocol server APIs via useMcpStore. It dynamically merges MCP server definitions with user-defined STDIO configurations and exposes listServerTools(), loadServerTools(), and callTool() methods. When an LLM returns a tool call, the message store invokes mcpStore.callTool() to execute external functions and process results.

UI State Stores

TUUI manages interface state through dedicated stores that follow the same architectural pattern:

  • snackbar.ts – Global notification system for info, warning, and error messages
  • layout.ts – Sidebar and panel visibility toggles that persist user preferences
  • locale.ts – Internationalization state and current language selection
  • counter.ts – Simple reactive demonstration store for component testing

Cross-Store Communication Patterns

Because all stores register on the same Pinia instance, TUUI implements direct store-to-store communication without event buses or global emitters. Stores reference each other within actions to coordinate complex workflows.

// Inside messageStore actions
const historyStore = useHistoryStore()
const mcpStore = useMcpStore()

// Persist conversation after generation
historyStore.save(this.conversation)

// Execute MCP tool when LLM requests it
const toolResult = await mcpStore.callTool(toolName, args)
this.postToolCall(toolResult)

This pattern ensures that history updates, tool executions, and UI notifications remain synchronized. For example, when sendMessage() triggers an inference that requires an MCP tool, the message store awaits mcpStore.callTool() and then updates the conversation state, with all changes propagating reactively to subscribed components.

Consuming State in Vue Components

Components interact with global state by importing the composable store hooks and binding directly to reactive properties. The following pattern demonstrates how the message input

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →