feat: Add /tools page with centralized tool registry management
- Add ToolsPage for managing MCP tools activation and persistence - Centralize all tool handlers in services/tools/handlers/ - toolRegistry.ts is now the single source of truth for tool state - Add tools store for pinned tools (persisted in localStorage) - Tools can be pinned to stay active across page navigation - Remove old individual tool files, replaced by centralized handlers
This commit is contained in:
170
frontend/src/stores/tools.ts
Normal file
170
frontend/src/stores/tools.ts
Normal file
@@ -0,0 +1,170 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
export interface ToolDefinition {
|
||||
name: string
|
||||
description: string
|
||||
category: 'global' | 'canvas' | 'component' | 'theme' | 'database' | 'source' | 'project'
|
||||
schema: object
|
||||
handler: Function
|
||||
}
|
||||
|
||||
export const useToolsStore = defineStore('tools', () => {
|
||||
// All available tool definitions
|
||||
const toolDefinitions = ref<Map<string, ToolDefinition>>(new Map())
|
||||
|
||||
// Pinned tools persist across page changes (use array for reactivity)
|
||||
const pinnedToolsArray = ref<string[]>([])
|
||||
|
||||
// Currently active tools (use array for reactivity)
|
||||
const activeToolsArray = ref<string[]>([])
|
||||
|
||||
// Computed
|
||||
const allTools = computed(() => Array.from(toolDefinitions.value.values()))
|
||||
|
||||
const activeToolsDefs = computed(() =>
|
||||
allTools.value.filter(t => activeToolsArray.value.includes(t.name))
|
||||
)
|
||||
|
||||
const inactiveToolsDefs = computed(() =>
|
||||
allTools.value.filter(t => !activeToolsArray.value.includes(t.name))
|
||||
)
|
||||
|
||||
const pinnedToolsDefs = computed(() =>
|
||||
allTools.value.filter(t => pinnedToolsArray.value.includes(t.name))
|
||||
)
|
||||
|
||||
const toolsByCategory = computed(() => {
|
||||
const categories: Record<string, { active: ToolDefinition[], inactive: ToolDefinition[] }> = {}
|
||||
|
||||
for (const tool of allTools.value) {
|
||||
if (!categories[tool.category]) {
|
||||
categories[tool.category] = { active: [], inactive: [] }
|
||||
}
|
||||
|
||||
if (activeToolsArray.value.includes(tool.name)) {
|
||||
categories[tool.category].active.push(tool)
|
||||
} else {
|
||||
categories[tool.category].inactive.push(tool)
|
||||
}
|
||||
}
|
||||
|
||||
return categories
|
||||
})
|
||||
|
||||
// Actions
|
||||
function registerToolDefinition(tool: ToolDefinition) {
|
||||
toolDefinitions.value.set(tool.name, tool)
|
||||
}
|
||||
|
||||
function registerToolDefinitions(tools: ToolDefinition[]) {
|
||||
for (const tool of tools) {
|
||||
toolDefinitions.value.set(tool.name, tool)
|
||||
}
|
||||
}
|
||||
|
||||
function setToolActive(name: string, active: boolean) {
|
||||
const index = activeToolsArray.value.indexOf(name)
|
||||
if (active && index === -1) {
|
||||
activeToolsArray.value.push(name)
|
||||
} else if (!active && index !== -1) {
|
||||
activeToolsArray.value.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
function pinTool(name: string) {
|
||||
if (!pinnedToolsArray.value.includes(name)) {
|
||||
pinnedToolsArray.value.push(name)
|
||||
savePinnedTools()
|
||||
}
|
||||
}
|
||||
|
||||
function unpinTool(name: string) {
|
||||
const index = pinnedToolsArray.value.indexOf(name)
|
||||
if (index !== -1) {
|
||||
pinnedToolsArray.value.splice(index, 1)
|
||||
savePinnedTools()
|
||||
}
|
||||
}
|
||||
|
||||
function togglePin(name: string) {
|
||||
if (pinnedToolsArray.value.includes(name)) {
|
||||
unpinTool(name)
|
||||
} else {
|
||||
pinTool(name)
|
||||
}
|
||||
}
|
||||
|
||||
function isToolPinned(name: string): boolean {
|
||||
return pinnedToolsArray.value.includes(name)
|
||||
}
|
||||
|
||||
function isToolActive(name: string): boolean {
|
||||
return activeToolsArray.value.includes(name)
|
||||
}
|
||||
|
||||
function getPinnedToolNames(): string[] {
|
||||
return [...pinnedToolsArray.value]
|
||||
}
|
||||
|
||||
function getToolDefinition(name: string): ToolDefinition | undefined {
|
||||
return toolDefinitions.value.get(name)
|
||||
}
|
||||
|
||||
// Persistence
|
||||
function savePinnedTools() {
|
||||
localStorage.setItem('pinnedTools', JSON.stringify(pinnedToolsArray.value))
|
||||
}
|
||||
|
||||
function loadPinnedTools() {
|
||||
try {
|
||||
const saved = localStorage.getItem('pinnedTools')
|
||||
if (saved) {
|
||||
const parsed = JSON.parse(saved)
|
||||
pinnedToolsArray.value = parsed
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[ToolsStore] Failed to load pinned tools:', e)
|
||||
}
|
||||
}
|
||||
|
||||
function clearActiveTools() {
|
||||
activeToolsArray.value = []
|
||||
}
|
||||
|
||||
function setActiveTools(names: string[]) {
|
||||
activeToolsArray.value = [...names]
|
||||
}
|
||||
|
||||
// Initialize
|
||||
loadPinnedTools()
|
||||
|
||||
return {
|
||||
// State (reactive arrays)
|
||||
activeTools: activeToolsArray,
|
||||
pinnedTools: pinnedToolsArray,
|
||||
toolDefinitions,
|
||||
|
||||
// Computed
|
||||
allTools,
|
||||
activeToolsDefs,
|
||||
inactiveToolsDefs,
|
||||
pinnedToolsDefs,
|
||||
toolsByCategory,
|
||||
|
||||
// Actions
|
||||
registerToolDefinition,
|
||||
registerToolDefinitions,
|
||||
setToolActive,
|
||||
pinTool,
|
||||
unpinTool,
|
||||
togglePin,
|
||||
isToolPinned,
|
||||
isToolActive,
|
||||
getPinnedToolNames,
|
||||
getToolDefinition,
|
||||
clearActiveTools,
|
||||
setActiveTools,
|
||||
loadPinnedTools
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user