Skip to main content
Glama
WorkspaceCustomizeAssets.vue8.42 kB
<!-- eslint-disable vue/no-multiple-template-root --> <template> <component :is="ResizablePanel" ref="leftResizablePanelRef" :minSize="320" rememberSizeKey="func-picker" side="left" > <template v-if="tabContentSlug === 'assets'" #subpanel1> <div class="flex flex-col h-full"> <div class="relative flex-grow"> <CustomizeTabs :tabContentSlug="tabContentSlug"> <template #assets> <AssetListPanel /> </template> </CustomizeTabs> </div> </div> </template> <template v-if="tabContentSlug === 'assets'" #subpanel2> <AssetFuncListPanel :schemaVariantId="selectedVariantId" /> </template> </component> <div class="grow overflow-hidden bg-shade-0 dark:bg-neutral-800 dark:text-shade-0 font-semi-bold relative" > <div class="absolute left-0 right-0 top-0 bottom-0"> <FuncEditor v-if=" tabContentSlug === 'assets' && selectedVariantId && selectedFuncId " :funcId="selectedFuncId" /> <AssetEditor v-else-if="tabContentSlug === 'assets' && selectedVariantId" :schemaVariantId="selectedVariantId" /> <WorkspaceCustomizeEmptyState v-else :instructions=" assetStore.selectedSchemaVariants.length > 1 ? 'You have selected multiple assets, use the right pane!' : undefined " :requestStatus="loadAssetsRequestStatus" loadingMessage="Loading assets..." /> </div> </div> <component :is="ResizablePanel" v-if="tabContentSlug === 'assets'" ref="rightResizablePanelRef" :minSize="300" rememberSizeKey="func-details" side="right" > <div class="absolute w-full flex flex-col h-full"> <AssetCard v-if="selectedVariantId" :assetId="selectedVariantId" titleCard /> <template v-if="selectedVariantId"> <FuncDetails v-if=" selectedFuncId && assetStore.selectedSchemaVariant?.schemaVariantId " :funcId="selectedFuncId" :schemaVariantId="assetStore.selectedSchemaVariant?.schemaVariantId" allowTestPanel singleModelScreen @expand-panel="rightResizablePanelRef?.maximize()" /> <!-- the key here is to force remounting so we get the proper asset request statuses --> <AssetDetailsPanel v-else :key="selectedVariantId" :schemaVariantId="selectedVariantId" /> </template> <template v-else-if="assetStore.selectedSchemaVariants.length > 1"> <div class="flex flex-col h-full w-full overflow-hidden"> <ScrollArea> <template #top> <SidebarSubpanelTitle icon="multiselect" label="Multiple Assets"> <DetailsPanelMenuIcon @click="open" /> </SidebarSubpanelTitle> <DropdownMenu ref="contextMenuRef" :items="rightClickMenuItems" /> </template> <div class="capsize p-xs mt-xs italic text-neutral-400 text-sm"> {{ assetStore.selectedSchemaVariants.length }} assets selected: </div> <Stack class="p-xs" spacing="xs"> <AssetCard v-for="assetId in assetStore.selectedSchemaVariants" :key="assetId" :assetId="assetId" :titleCard="false" /> </Stack> </ScrollArea> </div> </template> <EmptyStateCard v-else iconName="no-assets" primaryText="No Assets Selected" secondaryText="Select an asset from the list on the left panel to view its details here." /> </div> </component> </template> <script lang="ts" setup> import { onBeforeUnmount, onMounted, ref, computed, watch, onBeforeMount, } from "vue"; import { ResizablePanel, ScrollArea, Stack, DropdownMenu, DropdownMenuItemObjectDef, } from "@si/vue-lib/design-system"; import { useRoute } from "vue-router"; import { useAssetStore } from "@/store/asset.store"; import { useFuncStore } from "@/store/func/funcs.store"; import { useModuleStore } from "@/store/module.store"; import AssetCard from "../AssetCard.vue"; import AssetListPanel from "../AssetListPanel.vue"; import CustomizeTabs from "../CustomizeTabs.vue"; import AssetDetailsPanel from "../AssetDetailsPanel.vue"; import AssetFuncListPanel from "../AssetFuncListPanel.vue"; import FuncDetails from "../FuncEditor/FuncDetails.vue"; import EmptyStateCard from "../EmptyStateCard.vue"; import SidebarSubpanelTitle from "../SidebarSubpanelTitle.vue"; import DetailsPanelMenuIcon from "../DetailsPanelMenuIcon.vue"; import AssetEditor from "../AssetEditor.vue"; import FuncEditor from "../FuncEditor/FuncEditor.vue"; import WorkspaceCustomizeEmptyState from "../WorkspaceCustomizeEmptyState.vue"; const assetStore = useAssetStore(); const funcStore = useFuncStore(); const moduleStore = useModuleStore(); const selectedVariantId = computed(() => assetStore.selectedVariantId); const selectedFuncId = computed(() => funcStore.selectedFuncId); const loadAssetsRequestStatus = assetStore.getRequestStatus( "LOAD_SCHEMA_VARIANT_LIST", ); const leftResizablePanelRef = ref<InstanceType<typeof ResizablePanel>>(); const rightResizablePanelRef = ref<InstanceType<typeof ResizablePanel>>(); const contextMenuRef = ref<InstanceType<typeof DropdownMenu>>(); const open = (mouse: MouseEvent) => { contextMenuRef.value?.open(mouse, false); }; const rightClickMenuItems = computed(() => { const canContribute = []; const canUpdate = []; assetStore.selectedSchemaVariantRecords.forEach((asset) => { if (asset.canContribute) canContribute.push(asset); if (moduleStore.upgradeableModules[asset.schemaVariantId]) canUpdate.push(asset); }); const items: DropdownMenuItemObjectDef[] = [ { label: `Contribute ${ canContribute.length ? canContribute.length : "" } Assets`, icon: "cloud-upload", onSelect: () => {}, // TODO disabled: canContribute.length === 0, }, { label: `Update ${canUpdate.length ? canUpdate.length : ""} Assets`, icon: "code-deployed", onSelect: () => {}, // TODO disabled: canUpdate.length === 0, }, ]; return items; }); const onKeyDown = async (e: KeyboardEvent) => { if ( e.altKey && e.shiftKey && leftResizablePanelRef.value && rightResizablePanelRef.value ) { if ( leftResizablePanelRef.value.collapsed && rightResizablePanelRef.value.collapsed ) { // Open all panels leftResizablePanelRef.value.collapseSet(false); rightResizablePanelRef.value.collapseSet(false); leftResizablePanelRef.value.subpanelCollapseSet(false); } else { // Close all panels leftResizablePanelRef.value.collapseSet(true); rightResizablePanelRef.value.collapseSet(true); } } }; onBeforeMount(async () => { await assetStore.LOAD_SCHEMA_VARIANT_LIST(); if (Object.values(route.query).length > 0) { assetStore.syncUrlIntoSelection(); // loads selected assets/funcs } await Promise.all([moduleStore.SYNC(), funcStore.FETCH_FUNC_LIST()]); }); onMounted(() => { window.addEventListener("keydown", onKeyDown); }); onBeforeUnmount(() => { window.removeEventListener("keydown", onKeyDown); }); watch( () => assetStore.selectedSchemaVariants.length, (newVal, oldVal) => { if (newVal > 1 && oldVal < 2 && leftResizablePanelRef.value) { leftResizablePanelRef.value.subpanelCollapseSet(true); } else if (newVal === 1 && oldVal > 1 && leftResizablePanelRef.value) { leftResizablePanelRef.value.subpanelCollapseSet(false); } }, ); const route = useRoute(); // Compute the initial tab content based on the route. This is necessary because the "packages" tab is mounted on a // different parent, so moving to other tabs from it causes a remount const tabContentSlug = computed<"assets" | "newassets">(() => { const lab_type = route.name?.toString().match(/workspace-lab-(.*)/)?.[1] ?? ""; // This looks awkward because of the strict return type. We can't return a generic string from this func. switch (lab_type) { case "newassets": return "newassets"; default: return "assets"; } }); </script>

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/systeminit/si'

If you have feedback or need assistance with the MCP directory API, please join our Discord server