diff --git a/index.html b/index.html index c909032..8710248 100644 --- a/index.html +++ b/index.html @@ -10,7 +10,7 @@ - + DASFAD diff --git a/src/diagram/diagramManager.ts b/src/diagram/diagramManager.ts index 7406954..a2e10ea 100644 --- a/src/diagram/diagramManager.ts +++ b/src/diagram/diagramManager.ts @@ -102,6 +102,74 @@ export class DiagramManager { } }); + + // Chat event listeners for AI-powered diagram creation + document.addEventListener('chatCreateEntity', (event: CustomEvent) => { + const {entity} = event.detail; + this._logger.debug('chatCreateEntity', entity); + const object = new DiagramObject(this._scene, this.onDiagramEventObservable, { + diagramEntity: entity, + actionManager: this._diagramEntityActionManager + }); + this._diagramObjects.set(entity.id, object); + this.onDiagramEventObservable.notifyObservers({ + type: DiagramEventType.ADD, + entity: entity + }, DiagramEventObserverMask.TO_DB); + }); + + document.addEventListener('chatRemoveEntity', (event: CustomEvent) => { + const {target} = event.detail; + this._logger.debug('chatRemoveEntity', target); + const entity = this.findEntityByIdOrLabel(target); + if (entity) { + const diagramObject = this._diagramObjects.get(entity.id); + if (diagramObject) { + diagramObject.dispose(); + this._diagramObjects.delete(entity.id); + this.onDiagramEventObservable.notifyObservers({ + type: DiagramEventType.REMOVE, + entity: entity + }, DiagramEventObserverMask.TO_DB); + } + } + }); + + document.addEventListener('chatModifyEntity', (event: CustomEvent) => { + const {target, updates} = event.detail; + this._logger.debug('chatModifyEntity', target, updates); + const entity = this.findEntityByIdOrLabel(target); + if (entity) { + const diagramObject = this._diagramObjects.get(entity.id); + if (diagramObject) { + if (updates.text !== undefined) { + diagramObject.text = updates.text; + } + // Note: color and position updates would require additional DiagramObject methods + const updatedEntity = {...entity, ...updates}; + this.onDiagramEventObservable.notifyObservers({ + type: DiagramEventType.MODIFY, + entity: updatedEntity + }, DiagramEventObserverMask.TO_DB); + } + } + }); + + document.addEventListener('chatListEntities', () => { + this._logger.debug('chatListEntities'); + const entities = Array.from(this._diagramObjects.values()).map(obj => ({ + id: obj.diagramEntity.id, + template: obj.diagramEntity.template, + text: obj.diagramEntity.text || '', + position: obj.diagramEntity.position + })); + const responseEvent = new CustomEvent('chatListEntitiesResponse', { + detail: {entities}, + bubbles: true + }); + document.dispatchEvent(responseEvent); + }); + this._logger.debug("DiagramManager constructed"); } @@ -135,6 +203,21 @@ export class DiagramManager { return appConfigInstance; } + private findEntityByIdOrLabel(target: string): DiagramEntity | null { + // First try direct ID match + const byId = this._diagramObjects.get(target); + if (byId) { + return byId.diagramEntity; + } + // Then try label match (case-insensitive) + const targetLower = target.toLowerCase(); + for (const [, obj] of this._diagramObjects) { + if (obj.diagramEntity.text?.toLowerCase() === targetLower) { + return obj.diagramEntity; + } + } + return null; + } private onDiagramEvent(event: DiagramEvent) { let diagramObject = this._diagramObjects.get(event?.entity?.id); diff --git a/src/react/pages/vrExperience.tsx b/src/react/pages/vrExperience.tsx index 7191cf8..a67e2fe 100644 --- a/src/react/pages/vrExperience.tsx +++ b/src/react/pages/vrExperience.tsx @@ -2,7 +2,7 @@ import VrApp from '../../vrApp'; import React, {useEffect, useState} from "react"; import {Affix, Burger, Group, Menu, Alert, Button, Text} from "@mantine/core"; import VrTemplate from "../vrTemplate"; -import {IconStar, IconInfoCircle} from "@tabler/icons-react"; +import {IconStar, IconInfoCircle, IconMessageCircle} from "@tabler/icons-react"; import VrMenuItem from "../components/vrMenuItem"; import CreateDiagramModal from "./createDiagramModal"; import ManageDiagramsModal from "./manageDiagramsModal"; @@ -19,6 +19,7 @@ import {DefaultScene} from "../../defaultScene"; import VREntryPrompt from "../components/VREntryPrompt"; import ComingSoonBadge from "../components/ComingSoonBadge"; import UpgradeBadge from "../components/UpgradeBadge"; +import ChatPanel from "../components/ChatPanel"; let vrApp: VrApp = null; @@ -118,6 +119,7 @@ export default function VrExperience() { const [rerender, setRerender] = useState(0); const [dbName, setDbName] = useState(params.db); const [showVRPrompt, setShowVRPrompt] = useState(false); + const [chatOpen, setChatOpen] = useState(!isMobileVRDevice()); // Show chat by default on desktop useEffect(() => { const canvas = document.getElementById('vrCanvas'); @@ -340,10 +342,30 @@ export default function VrExperience() { onClick={getClickHandler(configState, openConfig)} availableIcon={getFeatureIndicator(configState)}/> )} + + + setChatOpen(!chatOpen)} + availableIcon={}/> - + +
+
+ +
+ {chatOpen && setChatOpen(false)}/>} +
{/* VR Entry Prompt - Rendered AFTER canvas to ensure it's on top in DOM order */} -import {defineConfig} from "vite"; +import {defineConfig, loadEnv} from "vite"; /** @type {import('vite').UserConfig} */ -export default defineConfig({ +export default defineConfig(({mode}) => { + const env = loadEnv(mode, process.cwd(), ''); + return { test: {}, define: {}, build: { @@ -23,6 +25,7 @@ export default defineConfig({ } }, server: { + allowedHosts: true, port: 3001, proxy: { '^/sync/.*': { @@ -36,6 +39,22 @@ export default defineConfig({ '^/api/images': { target: 'https://www.deepdiagram.com/', changeOrigin: true, + }, + '^/api/claude': { + target: 'https://api.anthropic.com', + changeOrigin: true, + rewrite: (path) => path.replace(/^\/api\/claude/, ''), + configure: (proxy) => { + proxy.on('proxyReq', (proxyReq) => { + + const apiKey = env.ANTHROPIC_API_KEY; + console.log(` API KEY: ${apiKey}`); + if (apiKey) { + proxyReq.setHeader('x-api-key', apiKey); + proxyReq.setHeader('anthropic-version', '2023-06-01'); + } + }); + } } } @@ -54,9 +73,24 @@ export default defineConfig({ '^/api/images': { target: 'https://www.deepdiagram.com/', changeOrigin: true, + }, + '^/api/claude': { + target: 'https://api.anthropic.com', + changeOrigin: true, + rewrite: (path) => path.replace(/^\/api\/claude/, ''), + configure: (proxy) => { + proxy.on('proxyReq', (proxyReq) => { + const apiKey = env.ANTHROPIC_API_KEY; + console.log(` API KEY: ${apiKey}`); + if (apiKey) { + proxyReq.setHeader('x-api-key', apiKey); + proxyReq.setHeader('anthropic-version', '2023-06-01'); + } + }); + } } } }, base: "/" - -}) \ No newline at end of file + }; +}); \ No newline at end of file