diff --git a/.gitignore b/.gitignore index aef331fe..e68f8292 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,7 @@ logs vease-back* vease-viewer* VTK.txt +/data # Playwright node_modules/ diff --git a/app/components/CreateTools.vue b/app/components/CreateTools.vue index 45850255..869e0b4a 100644 --- a/app/components/CreateTools.vue +++ b/app/components/CreateTools.vue @@ -1,15 +1,24 @@ - - - - Create New Object - - - Choose a drawing tool to get started. - - + + + + + Create New Object + + + Choose a drawing tool to get started. + + + + - + + mdi-arrow-left @@ -127,31 +138,40 @@ .custom-tool-card { transition: all 0.2s ease-in-out; } + .custom-tool-card:hover { transform: scale(1.03); } - .cursor-pointer { - cursor: pointer; - } .svg-white-filter { filter: invert(100%) sepia(0%) saturate(0%) hue-rotate(0deg) brightness(100%) contrast(100%); } + .tool-title { min-height: 2em; line-height: 1.2; } - .v-sheet-tool-wrapper { - position: relative; + .tool-component-wrapper { min-height: 400px; } - .v-sheet-back-button { - position: sticky; - top: 0; - z-index: 10; - background-color: white; + .overflow-y-auto::-webkit-scrollbar { + width: 8px; + } + + .overflow-y-auto::-webkit-scrollbar-track { + background: #f1f1f1; + border-radius: 4px; + } + + .overflow-y-auto::-webkit-scrollbar-thumb { + background: #888; + border-radius: 4px; + } + + .overflow-y-auto::-webkit-scrollbar-thumb:hover { + background: #555; } diff --git a/app/components/Extension.vue b/app/components/Extension.vue new file mode 100644 index 00000000..f5dd05dd --- /dev/null +++ b/app/components/Extension.vue @@ -0,0 +1,669 @@ + + + + + Extensions + + + + Enhance your application with additional features and tools. + + + + + + + + + + + + {{ + loading + ? "Loading Extension..." + : isDragging + ? "Drop to Install" + : "Click or Drag & Drop Extension" + }} + + + + (.vext extension files) + + + + + + + + + + {{ successMessage }} + + + + + + {{ errorMessage }} + + + + + + + + Active Extensions + + {{ loadedExtensions.length }} + + + + + + + + + + + + + + + + + + + {{ getExtensionName(extension) }} + + + + {{ getExtensionDescription(extension) }} + + + + + v{{ getExtensionVersion(extension) }} + + + + mdi-tools + {{ getExtensionToolsCount(extension) }} + + + + + + {{ formatDate(extension.loadedAt) }} + + + + + + + + + + {{ + extension.enabled + ? "Disable extension" + : "Enable extension" + }} + + + + + + + Remove extension + + + + + + + + + + + + + + Tools + + + {{ getExtensionToolsCount(extension) }} + {{ + getExtensionToolsCount(extension) === 1 + ? "tool" + : "tools" + }} + + + + + + + + + + + + + + + + {{ tool.title }} + + + Disabled + + + + + {{ tool.description }} + + + + + + + + + This extension doesn't register any tools + + + + + + + + + + + + + + + + + + No extensions loaded yet + + + Upload an extension file to get started + + + + + + + + + + + Remove Extension? + + + + + Are you sure you want to remove + {{ + getExtensionName(extensionToRemove) + }}? + + + + mdi-information + + This will remove all tools registered by this extension. + + + + + + + + + Cancel + + + mdi-delete + Remove + + + + + + + + + + diff --git a/app/components/Layout/SideBar.vue b/app/components/Layout/SideBar.vue index f2d7ae5f..33f0e111 100644 --- a/app/components/Layout/SideBar.vue +++ b/app/components/Layout/SideBar.vue @@ -86,18 +86,28 @@ diff --git a/app/components/tools/CreateVOI.vue b/app/components/tools/CreateVOI.vue deleted file mode 100644 index 68d07287..00000000 --- a/app/components/tools/CreateVOI.vue +++ /dev/null @@ -1,322 +0,0 @@ - - - - - Create Volume of Interest (Bounding Box) - - - - Select an existing AOI and define Z min/max coordinates to create a 3D - volume. - - - - - - - - - - - - - - - - - - - Z Coordinates (Min / Max) - - - - sanitizeInputVOI(val, 'z_min')" - /> - - - sanitizeInputVOI(val, 'z_max')" - /> - - - - - - - - - mdi-close-circle-outline - Cancel - - - - mdi-send - Create VOI - - - - - - - - - diff --git a/app/composables/extension_manager.js b/app/composables/extension_manager.js new file mode 100644 index 00000000..44693281 --- /dev/null +++ b/app/composables/extension_manager.js @@ -0,0 +1,104 @@ +import back_schemas from "@geode/opengeodeweb-back/opengeodeweb_back_schemas.json" + +export function useExtensionManager() { + const appStore = useAppStore() + const geodeStore = useGeodeStore() + + const importExtensionFile = async function (file) { + console.log("[ExtensionManager] Importing extension file:", file.name) + + // Upload .vext to backend + const schemaImport = back_schemas.opengeodeweb_back.import_extension + const form = new FormData() + form.append("file", file, file.name) + + const result = await $fetch(schemaImport.$id, { + baseURL: geodeStore.base_url, + method: "POST", + body: form, + }) + + console.log("[ExtensionManager] Extension extracted:", result) + + const { extension_name, frontend_content, backend_path } = result + + // Create blob URL from frontend JS content + const blob = new Blob([frontend_content], { + type: "application/javascript", + }) + const blobUrl = URL.createObjectURL(blob) + + // Load the extension module + const extensionModule = await appStore.loadExtension(blobUrl, backend_path) + + console.log("[ExtensionManager] Extension loaded:", extension_name) + + // Register the store if present + if (extensionModule.metadata?.store) { + const storeFactory = extensionModule.metadata.store + const store = storeFactory() + appStore.registerStore(store) + console.log("[ExtensionManager] Store registered:", store.$id) + + // Launch the microservice if the store has a launch method + const infraStore = useInfraStore() + if (typeof store.launch === "function") { + if (infraStore.app_mode === "DESKTOP") { + console.log( + "[ExtensionManager] Launching microservice in DESKTOP mode...", + ) + await store.launch(backend_path) + await store.connect() + console.log("[ExtensionManager] Microservice connected") + } else if (infraStore.app_mode === "BROWSER") { + console.log( + "[ExtensionManager] Launching microservice in BROWSER mode...", + ) + await store.launch(backend_path) + await store.connect() + console.log("[ExtensionManager] Microservice connected") + } else { + console.log( + `[ExtensionManager] Skipping microservice launch in ${infraStore.app_mode} mode`, + ) + } + } + } + + return { + extensionModule, + extension_name, + backend_path, + } + } + + const unloadExtension = async function (extensionId) { + console.log("[ExtensionManager] Unloading extension:", extensionId) + + const extensionData = appStore.getExtension(extensionId) + if (!extensionData) { + console.warn("[ExtensionManager] Extension not found:", extensionId) + return false + } + + // Get the store if it exists + const storeFactory = extensionData.metadata?.store + if (storeFactory) { + const store = storeFactory() + // Stop the microservice if possible + if (typeof store.kill === "function") { + await store.kill() + } + } + + // Unload from AppStore + appStore.unloadExtension(extensionId) + + console.log("[ExtensionManager] Extension unloaded:", extensionId) + return true + } + + return { importExtensionFile, unloadExtension } +} + +export default useExtensionManager diff --git a/app/composables/useExtensionMetadata.js b/app/composables/useExtensionMetadata.js new file mode 100644 index 00000000..0c9a0010 --- /dev/null +++ b/app/composables/useExtensionMetadata.js @@ -0,0 +1,36 @@ +export function useExtensionMetadata() { + const UIStore = useUIStore() + + const getExtensionName = (extension) => { + if (!extension) return "Unknown Extension" + if (extension.metadata?.name) return extension.metadata.name + return extension.id || "Unknown Extension" + } + + const getExtensionDescription = (extension) => { + return extension?.metadata?.description || "Custom extension module" + } + + const getExtensionVersion = (extension) => { + return extension?.metadata?.version || null + } + + const getExtensionTools = (extension) => { + if (!extension) return [] + return UIStore.toolsDefinitions.filter( + (tool) => tool.extensionPath === extension.id, + ) + } + + const getExtensionToolsCount = (extension) => { + return getExtensionTools(extension).length + } + + return { + getExtensionName, + getExtensionDescription, + getExtensionVersion, + getExtensionTools, + getExtensionToolsCount, + } +} diff --git a/app/layouts/default.vue b/app/layouts/default.vue index 4599e0ae..401c94e4 100644 --- a/app/layouts/default.vue +++ b/app/layouts/default.vue @@ -75,15 +75,24 @@ indeterminate color="white" /> + + +
- Z Coordinates (Min / Max) -