feat: Add floating window system for canvas components
- Add WindowContainer.vue with Liquid Glass styling, drag, resize, close - Add windows store for managing window state (position, size, z-index) - Modify dynamicComponents.ts to wrap Vue components in floating windows - Add MCP tools: move_window, resize_window, close_window, list_windows - Add isolated Claude profiles (ejecutor, nucleo000) with versioned configs
This commit is contained in:
125
frontend/src/stores/windows.ts
Normal file
125
frontend/src/stores/windows.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
export interface WindowState {
|
||||
id: string
|
||||
title: string
|
||||
x: number
|
||||
y: number
|
||||
width: number
|
||||
height: number
|
||||
zIndex: number
|
||||
}
|
||||
|
||||
export const useWindowsStore = defineStore('windows', () => {
|
||||
const windows = ref<Map<string, WindowState>>(new Map())
|
||||
const maxZIndex = ref(100)
|
||||
const windowOffset = ref(0)
|
||||
|
||||
// Getters
|
||||
const windowsList = computed(() => Array.from(windows.value.values()))
|
||||
const windowCount = computed(() => windows.value.size)
|
||||
|
||||
// Obtener siguiente posición para ventana nueva (cascada)
|
||||
function getNextPosition() {
|
||||
const offset = windowOffset.value * 30
|
||||
windowOffset.value = (windowOffset.value + 1) % 10
|
||||
return { x: 50 + offset, y: 50 + offset }
|
||||
}
|
||||
|
||||
// Registrar nueva ventana
|
||||
function register(id: string, state: Partial<WindowState> = {}) {
|
||||
const pos = state.x !== undefined && state.y !== undefined
|
||||
? { x: state.x, y: state.y }
|
||||
: getNextPosition()
|
||||
|
||||
windows.value.set(id, {
|
||||
id,
|
||||
title: state.title ?? id,
|
||||
x: pos.x,
|
||||
y: pos.y,
|
||||
width: state.width ?? 400,
|
||||
height: state.height ?? 300,
|
||||
zIndex: ++maxZIndex.value
|
||||
})
|
||||
|
||||
return windows.value.get(id)!
|
||||
}
|
||||
|
||||
// Traer ventana al frente
|
||||
function bringToFront(id: string) {
|
||||
const win = windows.value.get(id)
|
||||
if (win) {
|
||||
win.zIndex = ++maxZIndex.value
|
||||
}
|
||||
}
|
||||
|
||||
// Actualizar posición
|
||||
function updatePosition(id: string, x: number, y: number) {
|
||||
const win = windows.value.get(id)
|
||||
if (win) {
|
||||
win.x = x
|
||||
win.y = y
|
||||
}
|
||||
}
|
||||
|
||||
// Actualizar tamaño
|
||||
function updateSize(id: string, width: number, height: number) {
|
||||
const win = windows.value.get(id)
|
||||
if (win) {
|
||||
win.width = width
|
||||
win.height = height
|
||||
}
|
||||
}
|
||||
|
||||
// Actualizar título
|
||||
function updateTitle(id: string, title: string) {
|
||||
const win = windows.value.get(id)
|
||||
if (win) {
|
||||
win.title = title
|
||||
}
|
||||
}
|
||||
|
||||
// Eliminar ventana
|
||||
function remove(id: string) {
|
||||
windows.value.delete(id)
|
||||
}
|
||||
|
||||
// Obtener ventana por ID
|
||||
function get(id: string) {
|
||||
return windows.value.get(id)
|
||||
}
|
||||
|
||||
// Verificar si existe
|
||||
function has(id: string) {
|
||||
return windows.value.has(id)
|
||||
}
|
||||
|
||||
// Limpiar todas las ventanas
|
||||
function clear() {
|
||||
windows.value.clear()
|
||||
windowOffset.value = 0
|
||||
}
|
||||
|
||||
return {
|
||||
// State
|
||||
windows,
|
||||
maxZIndex,
|
||||
|
||||
// Getters
|
||||
windowsList,
|
||||
windowCount,
|
||||
|
||||
// Actions
|
||||
register,
|
||||
bringToFront,
|
||||
updatePosition,
|
||||
updateSize,
|
||||
updateTitle,
|
||||
remove,
|
||||
get,
|
||||
has,
|
||||
clear,
|
||||
getNextPosition
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user