FE2 - Embedding (#1979)
* Add Dialog * Add options to embed dialog * Min Height of Clipboard Input multiline to 3 lines * Check for visibility * Link to change access of project * Rename to guided mode * Change icon when user clicks copy button * Update Menu styles based on agi feedback * Update graphql.ts * Embed Options as hashState * Auto grow Clipboard Input * embed state and more options * Tidyups * Footer only shows when !embedOptions.isTransparent * Add auto/manual Load * Add Pre setup component * WIP Button Group mobile * Updates around manual load * Viewer Share nav * Add embed dialog to project page * Minor fixes * Check for federated * Responsive Tidyups * Responsive Fixes. Fix console issues * Add Alert to Version Embed * Disable Zoom * GQL updates * Comment Slideshow * GraphQl changes * Fix visibility * Build fix * Revert "Build fix" This reverts commit 0e706cbd9fde78204032bb1ec4421b1742d023ac. * remove unneeded change, revert yarn.lock * Test Commit * Remove commit test * Fix build * Update Tailwind. Add base url env * fix for portal scope issue * useLogger * useLogger * chore(fe2): include NUXT_PUBLIC_BASE_URL in deployment manifests * lazy load optimization * lint fixes * Updates * Re-add guided open Dialog sections * Prevent login popup on embed * Tidy up mobile combined button group * Tidy up embed Dialogs * Small styling issues * Update scrolling in embed dialog * Move selection info when embed * Testing fixes * Discuss in Speckle * Responsive Dialog Changes * Fix bug * WIP Manual Load * Fix nuxt errors * Fix nuxt logger issue * Fix embed dialog overflows * New Dialog layout * Responsive Breakpoint change * Preview Image * Fix bug with dialogSection * Hide selection info on mobile when thread is open * Footer Model Name * Overflow on ClipboardInput * Style fixes * Tidy ups * Responsive updates * Responsive fixes * Update button * Changes from testing * Fix embed height with footer * Fix Dialog Section * Fixes from testing * Move "reset filters" on embed * Small fixes * Updates from CR 1 * CR Comments 2 * Updates from CR * Add deserializeEmbedOptions helper * DialogSection changes * Revert changes in TextArea * Updates from CR * Only check for noscroll in watch * Update useRoute * Comment Slideshow mode * Changes from testing * Fix mobile share button * onMounted warn fixes * Updates from testing * Remove nesting of ManualLoad * Keep Speckle text on mobile * minor cleanup & bugfixes * Add target prop to Logo * navbar flash fix + more cleanup * Fix urls * Footer Logo changes * Remove viewer-transparent from layout * Add Reply in Speckle * Remove Anchored Points from embed * Final changes pre CR * Fix Anchored Points * Update packages/frontend-2/components/project/model-page/dialog/embed/Embed.vue Co-authored-by: Kristaps Fabians Geikins <fabians@speckle.systems> * Fixes from CR * Updates from cr * Changes WIP * Fix for dialog opening * Changes from PR * Updates to check embed in activity * fix(fe2): project settings dialog error * Make Team open section on click of "Manage" * Fixes from merge * Changes from cr * Compare old to new in watch * Fix logo in footer of embed * Fixes from merge * Fix build. Fix lazy load * Updates from Benjamin * Fix transparent bg --------- Co-authored-by: Kristaps Fabians Geikins <fabis94@live.com> Co-authored-by: Iain Sproat <68657+iainsproat@users.noreply.github.com> Co-authored-by: Kristaps Fabians Geikins <fabians@speckle.systems>
This commit is contained in:
committed by
GitHub
parent
ec95ebdfb3
commit
ff6433128a
@@ -23,6 +23,7 @@ services:
|
||||
environment:
|
||||
NUXT_PUBLIC_SERVER_NAME: 'local'
|
||||
NUXT_PUBLIC_API_ORIGIN: 'http://127.0.0.1'
|
||||
NUXT_PUBLIC_BASE_URL: 'http://127.0.0.1'
|
||||
NUXT_PUBLIC_BACKEND_API_ORIGIN: 'http://speckle-server:3000'
|
||||
NUXT_REDIS_URL: 'redis://redis'
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ NUXT_PUBLIC_LOG_PRETTY=true
|
||||
|
||||
NUXT_PUBLIC_API_ORIGIN=http://127.0.0.1:3000
|
||||
|
||||
NUXT_PUBLIC_BASE_URL=http://127.0.0.1:8081
|
||||
|
||||
NUXT_PUBLIC_MIXPANEL_TOKEN_ID=acd87c5a50b56df91a795e999812a3a4
|
||||
NUXT_PUBLIC_MIXPANEL_API_HOST=https://analytics.speckle.systems
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ const config = {
|
||||
],
|
||||
'no-alert': 'error',
|
||||
eqeqeq: ['error', 'always', { null: 'always' }],
|
||||
'no-console': 'off',
|
||||
'no-console': 'error',
|
||||
'no-var': 'error'
|
||||
},
|
||||
overrides: [
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<template>
|
||||
<div id="speckle" class="bg-foundation-page text-foreground">
|
||||
<div
|
||||
id="speckle"
|
||||
class="bg-foundation-page text-foreground has-[.viewer-transparent]:!bg-transparent"
|
||||
>
|
||||
<NuxtLayout>
|
||||
<NuxtPage />
|
||||
</NuxtLayout>
|
||||
@@ -20,7 +23,8 @@ useHead({
|
||||
lang: 'en'
|
||||
},
|
||||
bodyAttrs: {
|
||||
class: 'simple-scrollbar bg-foundation-page text-foreground'
|
||||
class:
|
||||
'simple-scrollbar bg-foundation-page text-foreground has-[.viewer-transparent]:!bg-transparent'
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<nav class="fixed z-20 top-0 h-14 bg-foundation shadow hover:shadow-md transition">
|
||||
<div class="flex items-center justify-between h-full w-screen px-4">
|
||||
<div class="flex gap-4 items-center justify-between h-full w-screen px-4">
|
||||
<div class="flex items-center truncate">
|
||||
<HeaderLogoBlock :active="false" to="/" />
|
||||
<HeaderNavLink
|
||||
@@ -12,7 +12,7 @@
|
||||
/>
|
||||
<PortalTarget name="navigation"></PortalTarget>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<div class="flex items-center gap-1.5 sm:gap-2">
|
||||
<PortalTarget name="secondary-actions"></PortalTarget>
|
||||
<PortalTarget name="primary-actions"></PortalTarget>
|
||||
<!-- Notifications dropdown -->
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
</div>
|
||||
|
||||
<UserAvatar v-if="!menuOpen" no-bg size="lg" hover-effect>
|
||||
<BellIcon class="text-foreground w-5 h-5" />
|
||||
<BellIcon class="text-primary sm:text-foreground w-5 h-5" />
|
||||
</UserAvatar>
|
||||
<UserAvatar v-else size="lg" hover-effect no-bg>
|
||||
<XMarkIcon class="text-foreground w-5 h-5" />
|
||||
<XMarkIcon class="text-primary sm:text-foreground w-5 h-5" />
|
||||
</UserAvatar>
|
||||
</div>
|
||||
</div>
|
||||
@@ -46,7 +46,7 @@
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/vue'
|
||||
import { XMarkIcon, BellIcon } from '@heroicons/vue/24/solid'
|
||||
import { XMarkIcon, BellIcon } from '@heroicons/vue/24/outline'
|
||||
import { useActiveUser } from '~~/lib/auth/composables/activeUser'
|
||||
|
||||
const { activeUser } = useActiveUser()
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
<template>
|
||||
<Menu
|
||||
as="div"
|
||||
class="flex items-center relative sm:border-r border-outline-1 sm:pr-4"
|
||||
>
|
||||
<MenuButton as="div">
|
||||
<FormButton class="hidden sm:flex" outlined :icon-right="ChevronDownIcon">
|
||||
Share
|
||||
</FormButton>
|
||||
<button class="sm:hidden mt-1.5">
|
||||
<ShareIcon class="h-5 w-5 text-primary" />
|
||||
</button>
|
||||
</MenuButton>
|
||||
<Transition
|
||||
enter-active-class="transition ease-out duration-200"
|
||||
enter-from-class="transform opacity-0 scale-95"
|
||||
enter-to-class="transform opacity-100 scale-100"
|
||||
leave-active-class="transition ease-in duration-75"
|
||||
leave-from-class="transform opacity-100 scale-100"
|
||||
leave-to-class="transform opacity-0 scale-95"
|
||||
>
|
||||
<MenuItems
|
||||
class="absolute z-50 flex flex-col gap-1 right-0 sm:right-4 top-12 min-w-max w-full sm:w-44 p-1 origin-top-right bg-foundation-2 outline outline-2 outline-primary-muted rounded-md shadow-lg overflow-hidden text-sm"
|
||||
>
|
||||
<MenuItem v-slot="{ active }">
|
||||
<div
|
||||
:class="[
|
||||
active ? 'bg-foundation-focus' : '',
|
||||
'flex gap-2 items-center px-2 py-1.5 text-sm text-foreground cursor-pointer transition rounded'
|
||||
]"
|
||||
@click="handleCopyLink"
|
||||
@keypress="keyboardClick(handleCopyLink)"
|
||||
>
|
||||
<LinkIcon class="w-5 h-5" />
|
||||
Copy Link
|
||||
</div>
|
||||
</MenuItem>
|
||||
<MenuItem v-if="!isFederated" v-slot="{ active }">
|
||||
<div
|
||||
:class="[
|
||||
active ? 'bg-foundation-focus' : '',
|
||||
'flex gap-2 items-center px-2 py-1.5 text-sm text-foreground cursor-pointer transition rounded'
|
||||
]"
|
||||
@click="handleCopyId"
|
||||
@keypress="keyboardClick(handleCopyId)"
|
||||
>
|
||||
<FingerPrintIcon class="w-5 h-5" />
|
||||
Copy ID
|
||||
</div>
|
||||
</MenuItem>
|
||||
<MenuItem v-slot="{ active }">
|
||||
<div
|
||||
:class="[
|
||||
active ? 'bg-foundation-focus' : '',
|
||||
'flex gap-2 items-center px-2 py-1.5 text-sm text-foreground cursor-pointer transition rounded'
|
||||
]"
|
||||
@click="handleEmbed"
|
||||
@keypress="keyboardClick(handleEmbed)"
|
||||
>
|
||||
<CodeBracketIcon class="w-5 h-5" />
|
||||
Embed Model
|
||||
</div>
|
||||
</MenuItem>
|
||||
</MenuItems>
|
||||
</Transition>
|
||||
<ProjectModelPageDialogEmbed
|
||||
v-model:open="embedDialogOpen"
|
||||
:project-id="projectId"
|
||||
:visibility="visibility"
|
||||
/>
|
||||
</Menu>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue'
|
||||
import {
|
||||
ChevronDownIcon,
|
||||
LinkIcon,
|
||||
FingerPrintIcon,
|
||||
CodeBracketIcon,
|
||||
ShareIcon
|
||||
} from '@heroicons/vue/24/outline'
|
||||
import { SpeckleViewer } from '@speckle/shared'
|
||||
import { keyboardClick } from '@speckle/ui-components'
|
||||
import type { ProjectVisibility } from '~/lib/common/generated/gql/graphql'
|
||||
import { useCopyModelLink } from '~~/lib/projects/composables/modelManagement'
|
||||
|
||||
const props = defineProps<{
|
||||
projectId: string
|
||||
resourceIdString: string
|
||||
visibility: ProjectVisibility
|
||||
}>()
|
||||
|
||||
const { copy } = useClipboard()
|
||||
const copyModelLink = useCopyModelLink()
|
||||
|
||||
const embedDialogOpen = ref(false)
|
||||
|
||||
const parsedResourceIds = computed(() =>
|
||||
SpeckleViewer.ViewerRoute.parseUrlParameters(props.resourceIdString)
|
||||
)
|
||||
|
||||
const firstResource = computed(() => parsedResourceIds.value[0] || {})
|
||||
|
||||
const versionId = computed(() => {
|
||||
if (SpeckleViewer.ViewerRoute.isModelResource(firstResource.value)) {
|
||||
return firstResource.value.versionId
|
||||
}
|
||||
return ''
|
||||
})
|
||||
|
||||
const modelId = computed(() => {
|
||||
if (SpeckleViewer.ViewerRoute.isModelResource(firstResource.value)) {
|
||||
return firstResource.value.modelId // Assuming your firstResource object has a modelId property
|
||||
}
|
||||
return ''
|
||||
})
|
||||
|
||||
const isFederated = computed(() => parsedResourceIds.value.length > 1)
|
||||
|
||||
const handleCopyId = () => {
|
||||
copy(props.resourceIdString, { successMessage: 'ID copied to clipboard' })
|
||||
}
|
||||
|
||||
const handleCopyLink = () => {
|
||||
const modelIdValue = modelId.value
|
||||
const versionIdValue = versionId.value ? versionId.value : undefined
|
||||
copyModelLink(props.projectId, modelIdValue, versionIdValue)
|
||||
}
|
||||
|
||||
const handleEmbed = () => {
|
||||
embedDialogOpen.value = true
|
||||
}
|
||||
</script>
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<Menu as="div" class="ml-2 flex items-center">
|
||||
<Menu as="div" class="flex items-center">
|
||||
<MenuButton v-slot="{ open: userOpen }">
|
||||
<span class="sr-only">Open user menu</span>
|
||||
<UserAvatar v-if="!userOpen" size="lg" :user="activeUser" hover-effect />
|
||||
@@ -23,7 +23,7 @@
|
||||
<NuxtLink
|
||||
:class="[
|
||||
active ? 'bg-foundation-focus' : '',
|
||||
'flex gap-3 border-b border-primary items-center px-3 py-3 text-sm text-primary cursor-pointer transition'
|
||||
'flex gap-3 border-b border-primary items-center px-3 py-3 text-sm text-primary cursor-pointer transition mb-1'
|
||||
]"
|
||||
@click="goToConnectors()"
|
||||
>
|
||||
@@ -35,7 +35,7 @@
|
||||
<NuxtLink
|
||||
:class="[
|
||||
active ? 'bg-foundation-focus' : '',
|
||||
'flex gap-2.5 items-center px-3 py-2.5 text-sm text-foreground cursor-pointer transition'
|
||||
'flex gap-2.5 items-center px-3 py-2.5 text-sm text-foreground cursor-pointer transition mx-1 rounded'
|
||||
]"
|
||||
@click="() => (showProfileEditDialog = true)"
|
||||
>
|
||||
@@ -47,7 +47,7 @@
|
||||
<NuxtLink
|
||||
:class="[
|
||||
active ? 'bg-foundation-focus' : '',
|
||||
'flex gap-3.5 items-center px-3 py-2.5 text-sm text-foreground cursor-pointer transition'
|
||||
'flex gap-3.5 items-center px-3 py-2.5 text-sm text-foreground cursor-pointer transition mx-1 rounded'
|
||||
]"
|
||||
@click="goToServerManagement()"
|
||||
>
|
||||
@@ -59,7 +59,7 @@
|
||||
<NuxtLink
|
||||
:class="[
|
||||
active ? 'bg-foundation-focus' : '',
|
||||
'flex gap-3.5 items-center px-3 py-2.5 text-sm text-foreground cursor-pointer transition'
|
||||
'flex gap-3.5 items-center px-3 py-2.5 text-sm text-foreground cursor-pointer transition mx-1 rounded'
|
||||
]"
|
||||
@click="onThemeClick"
|
||||
>
|
||||
@@ -71,7 +71,7 @@
|
||||
<NuxtLink
|
||||
:class="[
|
||||
active ? 'bg-foundation-focus' : '',
|
||||
'flex gap-3.5 items-center px-3 py-2.5 text-sm text-foreground cursor-pointer transition'
|
||||
'flex gap-3.5 items-center px-3 py-2.5 text-sm text-foreground cursor-pointer transition mx-1 rounded'
|
||||
]"
|
||||
@click="toggleInviteDialog"
|
||||
>
|
||||
@@ -83,7 +83,7 @@
|
||||
<NuxtLink
|
||||
:class="[
|
||||
active ? 'bg-foundation-focus' : '',
|
||||
'flex gap-3.5 items-center px-3 py-2.5 text-sm text-foreground cursor-pointer transition'
|
||||
'flex gap-3.5 items-center px-3 py-2.5 text-sm text-foreground cursor-pointer transition mx-1 rounded'
|
||||
]"
|
||||
target="_blank"
|
||||
to="https://docs.google.com/forms/d/e/1FAIpQLSeTOU8i0KwpgBG7ONimsh4YMqvLKZfSRhWEOz4W0MyjQ1lfAQ/viewform"
|
||||
@@ -97,7 +97,7 @@
|
||||
<NuxtLink
|
||||
:class="[
|
||||
active ? 'bg-foundation-focus' : '',
|
||||
'flex gap-3.5 items-center px-3 py-2.5 text-sm text-danger cursor-pointer transition'
|
||||
'flex gap-3.5 items-center px-3 py-2.5 text-sm text-danger cursor-pointer transition mx-1 rounded'
|
||||
]"
|
||||
@click="logout"
|
||||
>
|
||||
@@ -109,7 +109,7 @@
|
||||
<NuxtLink
|
||||
:class="[
|
||||
active ? 'bg-foundation-focus' : '',
|
||||
'flex gap-3.5 items-center px-3 py-2.5 text-sm text-primary cursor-pointer transition'
|
||||
'flex gap-3.5 items-center px-3 py-2.5 text-sm text-primary cursor-pointer transition mx-1 rounded'
|
||||
]"
|
||||
:to="loginUrl"
|
||||
>
|
||||
|
||||
@@ -1,21 +1,26 @@
|
||||
<template>
|
||||
<Portal to="navigation">
|
||||
<HeaderNavLink :to="projectRoute(project.id)" :name="project.name"></HeaderNavLink>
|
||||
<HeaderNavLink
|
||||
v-if="props.project.model"
|
||||
:to="modelVersionsRoute(project.id, props.project.model.id)"
|
||||
:name="props.project.model.name"
|
||||
></HeaderNavLink>
|
||||
</Portal>
|
||||
<div>
|
||||
<Portal to="navigation">
|
||||
<HeaderNavLink
|
||||
:to="projectRoute(project.id)"
|
||||
:name="project.name"
|
||||
></HeaderNavLink>
|
||||
<HeaderNavLink
|
||||
v-if="props.project.model"
|
||||
:to="modelVersionsRoute(project.id, props.project.model.id)"
|
||||
:name="props.project.model.name"
|
||||
></HeaderNavLink>
|
||||
</Portal>
|
||||
|
||||
<CommonEditableTitleDescription
|
||||
:title="titleState"
|
||||
:description="descriptionState"
|
||||
:can-edit="canEdit"
|
||||
:is-disabled="anyMutationsLoading"
|
||||
@update:title="handleUpdateTitle"
|
||||
@update:description="handleUpdateDescription"
|
||||
/>
|
||||
<CommonEditableTitleDescription
|
||||
:title="titleState"
|
||||
:description="descriptionState"
|
||||
:can-edit="canEdit"
|
||||
:is-disabled="anyMutationsLoading"
|
||||
@update:title="handleUpdateTitle"
|
||||
@update:description="handleUpdateDescription"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
</div>
|
||||
<div
|
||||
v-if="items?.length && project.model"
|
||||
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3 mt-4 relative z-0"
|
||||
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3 mt-4 relative z-10"
|
||||
>
|
||||
<!-- Decrementing z-index necessary for the actions menu to render correctly. Each card has its own stacking context because of the scale property -->
|
||||
<template v-for="(item, i) in items" :key="item.id">
|
||||
@@ -40,6 +40,7 @@
|
||||
:selection-disabled="disabledSelections[item.id]"
|
||||
@select="onSelect(item)"
|
||||
@chosen="onSingleActionChosen($event, item)"
|
||||
@embed="handleEmbed(item.id)"
|
||||
/>
|
||||
<ProjectModelPageVersionsCard
|
||||
v-else
|
||||
@@ -79,6 +80,13 @@
|
||||
:version="editMessageDialogVersion"
|
||||
@fully-closed="dialogState = null"
|
||||
/>
|
||||
<ProjectModelPageDialogEmbed
|
||||
v-model:open="embedDialogOpen"
|
||||
:visibility="project.visibility"
|
||||
:project-id="project.id"
|
||||
:version-id="currentVersionId"
|
||||
:model-id="project.model.id"
|
||||
/>
|
||||
<div class="py-12">
|
||||
<!-- Some padding to deal with a card menu potentially opening at the bottom of the page -->
|
||||
</div>
|
||||
@@ -102,6 +110,7 @@ type SingleVersion = NonNullable<Get<typeof versions.value, 'items[0]'>>
|
||||
graphql(`
|
||||
fragment ProjectModelPageVersionsPagination on Project {
|
||||
id
|
||||
visibility
|
||||
model(id: $modelId) {
|
||||
id
|
||||
versions(limit: 16, cursor: $versionsCursor) {
|
||||
@@ -167,6 +176,9 @@ const importArea = ref(
|
||||
}>
|
||||
)
|
||||
|
||||
const currentVersionId = ref<string | undefined>(undefined)
|
||||
const embedDialogOpen = ref(false)
|
||||
|
||||
const selectedItems = computed({
|
||||
get: () =>
|
||||
(realVersionItems.value || []).filter((i) => !!itemsSelectedState.value[i.id]),
|
||||
@@ -273,4 +285,9 @@ const onBatchDelete = () => {
|
||||
items: selectedItems.value.slice()
|
||||
}
|
||||
}
|
||||
|
||||
const handleEmbed = (versionId: string) => {
|
||||
currentVersionId.value = versionId
|
||||
embedDialogOpen.value = true
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,260 @@
|
||||
<template>
|
||||
<LayoutDialog
|
||||
v-model:open="isOpen"
|
||||
:max-width="visibility == ProjectVisibility.Private ? 'sm' : 'md'"
|
||||
:buttons="
|
||||
visibility == ProjectVisibility.Private
|
||||
? nonDiscoverableButtons
|
||||
: discoverableButtons
|
||||
"
|
||||
>
|
||||
<template #header>Embed Model</template>
|
||||
<div v-if="visibility === ProjectVisibility.Private">
|
||||
<p>
|
||||
<strong>Model embedding only works if the project is “Discoverable”.</strong>
|
||||
</p>
|
||||
<p class="mt-5">
|
||||
To change this setting you must be logged in as a user with the
|
||||
<strong>Owner</strong>
|
||||
project permission.
|
||||
</p>
|
||||
<p>
|
||||
Go to
|
||||
<strong>“Project Dashboard > Manage > Access”</strong>
|
||||
and choose
|
||||
<strong>“Discoverable”</strong>
|
||||
from the drop-down list.
|
||||
</p>
|
||||
</div>
|
||||
<div v-else>
|
||||
<CommonAlert v-if="multipleVersionedResources" class="mb-4 sm:-mt-4" color="info">
|
||||
<template #title>You are about embedding a specific version</template>
|
||||
<template #description>
|
||||
<p>
|
||||
This means that any changes you made after this version will not be included
|
||||
in the embedded model.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Tip:</strong>
|
||||
If you want to share the latest version of your model, go back to the
|
||||
project dashboard and start the embedding process from there.
|
||||
</p>
|
||||
</template>
|
||||
</CommonAlert>
|
||||
|
||||
<div class="flex flex-col lg:flex-row gap-8 mb-6">
|
||||
<div class="flex-1 order-1 lg:order-2">
|
||||
<h4 class="font-bold text-sm text-foreground-2 mb-2 ml-0.5">Embed Code</h4>
|
||||
<FormClipboardInput :value="iframeCode" is-multiline />
|
||||
<p class="text-sm sm:text-base text-foreground-2 mt-2 mb-5 ml-0.5">
|
||||
Copy this code to embed your model in a webpage or document.
|
||||
</p>
|
||||
<LayoutDialogSection border-b border-t title="Options">
|
||||
<template #icon>
|
||||
<Cog6ToothIcon class="h-full w-full" />
|
||||
</template>
|
||||
<div
|
||||
class="flex flex-col gap-1.5 sm:gap-2 ml-5 sm:ml-7 text-sm cursor-default"
|
||||
>
|
||||
<div v-for="option in embedDialogOptions" :key="option.id">
|
||||
<label
|
||||
:for="`option-${option.id}`"
|
||||
class="flex items-center gap-1 cursor-pointer max-w-max"
|
||||
>
|
||||
<FormCheckbox
|
||||
:id="`option-${option.id}`"
|
||||
:model-value="option.value.value"
|
||||
:name="option.label"
|
||||
hide-label
|
||||
class="cursor-pointer"
|
||||
@update:model-value="
|
||||
(newValue) => updateOption(option.value, newValue)
|
||||
"
|
||||
/>
|
||||
<span>{{ option.label }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</LayoutDialogSection>
|
||||
<LayoutDialogSection
|
||||
v-if="!isSmallerOrEqualSm"
|
||||
lazy-load
|
||||
border-b
|
||||
title="Preview"
|
||||
>
|
||||
<template #icon>
|
||||
<EyeIcon class="h-full w-full" />
|
||||
</template>
|
||||
<ProjectModelPageDialogEmbedIframe
|
||||
v-if="!isSmallerOrEqualSm"
|
||||
:src="updatedUrl"
|
||||
title="Preview"
|
||||
width="600"
|
||||
height="400"
|
||||
class="shrink-0 w-[600px] h-[400px] mx-auto"
|
||||
/>
|
||||
</LayoutDialogSection>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</LayoutDialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { Cog6ToothIcon, EyeIcon } from '@heroicons/vue/24/outline'
|
||||
import { ProjectVisibility } from '~~/lib/common/generated/gql/graphql'
|
||||
import { useClipboard } from '~~/composables/browser'
|
||||
import { SpeckleViewer } from '@speckle/shared'
|
||||
import { projectRoute } from '~~/lib/common/helpers/route'
|
||||
|
||||
const props = defineProps<{
|
||||
visibility?: ProjectVisibility
|
||||
projectId: string
|
||||
modelId?: string
|
||||
versionId?: string
|
||||
}>()
|
||||
|
||||
const isOpen = defineModel<boolean>('open', { required: true })
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const { copy } = useClipboard()
|
||||
const {
|
||||
public: { baseUrl }
|
||||
} = useRuntimeConfig()
|
||||
|
||||
const { isSmallerOrEqualSm } = useIsSmallerOrEqualThanBreakpoint()
|
||||
|
||||
const transparentBackground = ref(false)
|
||||
const hideViewerControls = ref(false)
|
||||
const hideSelectionInfo = ref(false)
|
||||
const preventScrolling = ref(false)
|
||||
const manuallyLoadModel = ref(false)
|
||||
|
||||
const routeModelId = computed(() => route.params.modelId as string)
|
||||
|
||||
const parsedResources = computed(() =>
|
||||
SpeckleViewer.ViewerRoute.parseUrlParameters(routeModelId.value)
|
||||
)
|
||||
|
||||
const multipleVersionedResources = computed(() => {
|
||||
return (
|
||||
parsedResources.value.filter(
|
||||
(resource) =>
|
||||
SpeckleViewer.ViewerRoute.isModelResource(resource) &&
|
||||
resource.versionId !== undefined
|
||||
).length > 1
|
||||
)
|
||||
})
|
||||
|
||||
const updatedUrl = computed(() => {
|
||||
const url = new URL(`/projects/${encodeURIComponent(props.projectId)}`, baseUrl)
|
||||
|
||||
url.pathname += '/models/'
|
||||
|
||||
// Use props.modelId and props.versionId if provided
|
||||
if (props.modelId) {
|
||||
let modelPath = encodeURIComponent(props.modelId)
|
||||
if (props.versionId) {
|
||||
modelPath += `@${encodeURIComponent(props.versionId)}`
|
||||
}
|
||||
url.pathname += modelPath
|
||||
} else {
|
||||
// Otherwise, use routeModelId directly
|
||||
url.pathname += routeModelId.value
|
||||
}
|
||||
|
||||
// Construct the embed options as a hash fragment
|
||||
const embedOptions: Record<string, boolean> = { isEnabled: true }
|
||||
embedDialogOptions.forEach((option) => {
|
||||
if (option.value.value) {
|
||||
embedOptions[option.id] = true
|
||||
}
|
||||
})
|
||||
|
||||
// Serialize the embedOptions into a hash fragment
|
||||
const hashFragment = encodeURIComponent(JSON.stringify(embedOptions))
|
||||
url.hash = `embed=${hashFragment}`
|
||||
|
||||
return url.toString()
|
||||
})
|
||||
|
||||
const iframeCode = computed(() => {
|
||||
return `<iframe title="Speckle" src="${updatedUrl.value}" width="600" height="400" frameborder="0"></iframe>`
|
||||
})
|
||||
|
||||
const discoverableButtons = computed(() => [
|
||||
{
|
||||
text: 'Cancel',
|
||||
props: { color: 'invert', fullWidth: true, outline: true },
|
||||
onClick: () => {
|
||||
isOpen.value = false
|
||||
}
|
||||
},
|
||||
{
|
||||
text: 'Copy Embed Code',
|
||||
props: { color: 'primary', fullWidth: true },
|
||||
onClick: () => {
|
||||
handleEmbedCodeCopy(iframeCode.value)
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
const nonDiscoverableButtons = computed(() => [
|
||||
{
|
||||
text: 'Close',
|
||||
props: { color: 'invert', fullWidth: true, outline: true },
|
||||
onClick: () => {
|
||||
isOpen.value = false
|
||||
}
|
||||
},
|
||||
{
|
||||
text: 'Change Access',
|
||||
props: { color: 'primary', fullWidth: true },
|
||||
onClick: () => {
|
||||
isOpen.value = false
|
||||
router.push(`${projectRoute(props.projectId)}?settings=access`)
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
const handleEmbedCodeCopy = async (value: string) => {
|
||||
await copy(value, {
|
||||
successMessage: 'Embed code copied to clipboard',
|
||||
failureMessage: 'Failed to copy embed code to clipboard'
|
||||
})
|
||||
}
|
||||
|
||||
const updateOption = (optionRef: Ref<boolean>, newValue: unknown) => {
|
||||
optionRef.value = newValue === undefined ? false : !!newValue
|
||||
}
|
||||
|
||||
const embedDialogOptions = [
|
||||
{
|
||||
id: 'isTransparent',
|
||||
label: 'Transparent background',
|
||||
value: transparentBackground
|
||||
},
|
||||
{
|
||||
id: 'hideControls',
|
||||
label: 'Hide viewer controls',
|
||||
value: hideViewerControls
|
||||
},
|
||||
{
|
||||
id: 'hideSelectionInfo',
|
||||
label: 'Hide the Selection Info panel',
|
||||
value: hideSelectionInfo
|
||||
},
|
||||
{
|
||||
id: 'noScroll',
|
||||
label: 'Prevent scrolling (zooming)',
|
||||
value: preventScrolling
|
||||
},
|
||||
{
|
||||
id: 'manualLoad',
|
||||
label: 'Load model manually',
|
||||
value: manuallyLoadModel
|
||||
}
|
||||
]
|
||||
</script>
|
||||
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<iframe
|
||||
:src="src"
|
||||
:title="title"
|
||||
:width="width"
|
||||
:height="height"
|
||||
frameborder="0"
|
||||
scrolling="no"
|
||||
></iframe>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
src: string
|
||||
title: string
|
||||
width?: string | number
|
||||
height?: string | number
|
||||
}>()
|
||||
</script>
|
||||
@@ -79,6 +79,7 @@
|
||||
:selection-disabled="selectionDisabled"
|
||||
@select="onSelect"
|
||||
@chosen="$emit('chosen', $event)"
|
||||
@embed="$emit('embed')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -122,6 +123,7 @@ const emit = defineEmits<{
|
||||
(e: 'select'): void
|
||||
(e: 'update:selected', val: boolean): void
|
||||
(e: 'chosen', val: VersionActionTypes): void
|
||||
(e: 'embed'): void
|
||||
}>()
|
||||
|
||||
const props = defineProps<{
|
||||
|
||||
@@ -21,7 +21,8 @@ import {
|
||||
LinkIcon,
|
||||
FingerPrintIcon,
|
||||
ArrowRightOnRectangleIcon,
|
||||
CursorArrowRaysIcon
|
||||
CursorArrowRaysIcon,
|
||||
CodeBracketIcon
|
||||
} from '@heroicons/vue/24/outline'
|
||||
import type { LayoutMenuItem } from '~~/lib/layout/helpers/components'
|
||||
import { useCopyModelLink } from '~~/lib/projects/composables/modelManagement'
|
||||
@@ -31,6 +32,7 @@ const emit = defineEmits<{
|
||||
(e: 'update:open', v: boolean): void
|
||||
(e: 'select'): void
|
||||
(e: 'chosen', v: VersionActionTypes): void
|
||||
(e: 'embed'): void
|
||||
}>()
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -81,7 +83,8 @@ const actionsItems = computed<LayoutMenuItem<VersionActionTypes>[][]>(() => [
|
||||
],
|
||||
[
|
||||
{ title: 'Copy Link', id: VersionActionTypes.Share, icon: LinkIcon },
|
||||
{ title: 'Copy ID', id: VersionActionTypes.CopyId, icon: FingerPrintIcon }
|
||||
{ title: 'Copy ID', id: VersionActionTypes.CopyId, icon: FingerPrintIcon },
|
||||
{ title: 'Embed Model', id: VersionActionTypes.EmbedModel, icon: CodeBracketIcon }
|
||||
],
|
||||
[
|
||||
{
|
||||
@@ -108,6 +111,9 @@ const onActionChosen = (params: { item: LayoutMenuItem<VersionActionTypes> }) =>
|
||||
case VersionActionTypes.CopyId:
|
||||
copy(props.versionId, { successMessage: 'Version ID copied to clipboard' })
|
||||
break
|
||||
case VersionActionTypes.EmbedModel:
|
||||
emit('embed')
|
||||
break
|
||||
}
|
||||
|
||||
emit('chosen', item.id)
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
<template>
|
||||
<Portal to="navigation">
|
||||
<HeaderNavLink :to="projectRoute(project.id)" :name="project.name"></HeaderNavLink>
|
||||
</Portal>
|
||||
<div>
|
||||
<Portal to="navigation">
|
||||
<HeaderNavLink
|
||||
:to="projectRoute(project.id)"
|
||||
:name="project.name"
|
||||
></HeaderNavLink>
|
||||
</Portal>
|
||||
|
||||
<CommonEditableTitleDescription
|
||||
:title="titleState"
|
||||
:description="descriptionState"
|
||||
:can-edit="canEdit"
|
||||
:is-disabled="anyMutationsLoading"
|
||||
@update:title="handleUpdateTitle"
|
||||
@update:description="handleUpdateDescription"
|
||||
/>
|
||||
<CommonEditableTitleDescription
|
||||
:title="titleState"
|
||||
:description="descriptionState"
|
||||
:can-edit="canEdit"
|
||||
:is-disabled="anyMutationsLoading"
|
||||
@update:title="handleUpdateTitle"
|
||||
@update:description="handleUpdateDescription"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
@@ -95,6 +95,7 @@ graphql(`
|
||||
fragment ProjectPageLatestItemsModels on Project {
|
||||
id
|
||||
role
|
||||
visibility
|
||||
modelCount: models(limit: 0) {
|
||||
totalCount
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
<template>
|
||||
<div :class="['p-4', 'flex flex-col justify-center items-center space-y-8']">
|
||||
<div :class="['p-4', 'flex flex-col justify-center items-center gap-2 sm:gap-6']">
|
||||
<div
|
||||
:class="`w-42 h-42 group transition-[margin-right] mr-0 hover:mr-12 ${
|
||||
:class="`hidden sm:block w-42 h-42 group transition-[margin-right] mr-0 hover:sm:mr-12 ${
|
||||
small ? 'scale-75' : ''
|
||||
}`"
|
||||
>
|
||||
<template v-if="!isDarkTheme">
|
||||
<img
|
||||
src="~~/assets/images/discussions/d-w-1.png"
|
||||
class="opacity-80 w-36 h-auto shadow-md relative transition grayscale blur-[1px] group-hover:blur-[2px] group-hover:grayscale-0 group-hover:-translate-x-10 group-hover:-translate-y-3 group-hover:scale-105"
|
||||
class="opacity-80 w-36 h-auto shadow-md relative transition grayscale blur-[1px] group-hover:blur-[2px] group-hover:sm:grayscale-0 group-hover:sm:-translate-x-10 group-hover:sm:-translate-y-3 group-hover:sm:scale-105"
|
||||
alt="discussions image"
|
||||
/>
|
||||
<img
|
||||
src="~~/assets/images/discussions/d-w-2.png"
|
||||
class="w-36 shadow-md relative ml-10 -mt-20 transition grayscale group-hover:grayscale-0 group-hover:translate-x-5 group-hover:scale-150 group-hover:shadow-xl"
|
||||
class="w-36 shadow-md relative ml-10 -mt-20 transition grayscale group-hover:sm:grayscale-0 group-hover:sm:translate-x-5 group-hover:sm:scale-150 group-hover:sm:shadow-xl"
|
||||
alt="discussions image"
|
||||
/>
|
||||
</template>
|
||||
@@ -30,13 +30,17 @@
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
<div class="text-foreground text-center">
|
||||
<div class="text-foreground text-center text-xs sm:text-sm">
|
||||
<div>Speckle allows for real time discussions straight in your 3D model.</div>
|
||||
<div v-if="!small" class="text-xs text-foreground-2">
|
||||
Head over to a model and start coordinating right away!
|
||||
</div>
|
||||
<div v-else class="mt-2">
|
||||
<FormButton :icon-left="PlusIcon" @click="() => $emit('new-discussion')">
|
||||
<div v-else class="mt-3">
|
||||
<FormButton
|
||||
size="sm"
|
||||
:icon-left="PlusIcon"
|
||||
@click="() => $emit('new-discussion')"
|
||||
>
|
||||
New discussion
|
||||
</FormButton>
|
||||
</div>
|
||||
|
||||
@@ -23,11 +23,20 @@
|
||||
:project-id="projectId"
|
||||
@deleted="$emit('model-updated')"
|
||||
/>
|
||||
<ProjectModelPageDialogEmbed
|
||||
v-model:open="embedDialogOpen"
|
||||
:project-id="projectId"
|
||||
:visibility="visibility"
|
||||
:model-id="model.id"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type { Nullable } from '@speckle/shared'
|
||||
import type { ProjectPageModelsActionsFragment } from '~~/lib/common/generated/gql/graphql'
|
||||
import type {
|
||||
ProjectPageModelsActionsFragment,
|
||||
ProjectVisibility
|
||||
} from '~~/lib/common/generated/gql/graphql'
|
||||
import type { LayoutMenuItem } from '~~/lib/layout/helpers/components'
|
||||
import { useCopyModelLink } from '~~/lib/projects/composables/modelManagement'
|
||||
import { EllipsisVerticalIcon } from '@heroicons/vue/24/solid'
|
||||
@@ -36,7 +45,8 @@ import {
|
||||
PencilIcon,
|
||||
LinkIcon,
|
||||
FingerPrintIcon,
|
||||
ArrowUpTrayIcon
|
||||
ArrowUpTrayIcon,
|
||||
CodeBracketIcon
|
||||
} from '@heroicons/vue/24/outline'
|
||||
import { graphql } from '~~/lib/common/generated/gql'
|
||||
import { useMixpanel } from '~~/lib/core/composables/mp'
|
||||
@@ -53,13 +63,15 @@ enum ActionTypes {
|
||||
Delete = 'delete',
|
||||
Share = 'share',
|
||||
UploadVersion = 'upload-version',
|
||||
CopyId = 'copy-id'
|
||||
CopyId = 'copy-id',
|
||||
Embed = 'embed'
|
||||
}
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:open', v: boolean): void
|
||||
(e: 'model-updated'): void
|
||||
(e: 'upload-version'): void
|
||||
(e: 'embed'): void
|
||||
}>()
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -67,6 +79,7 @@ const props = defineProps<{
|
||||
model: ProjectPageModelsActionsFragment
|
||||
projectId: string
|
||||
canEdit?: boolean
|
||||
visibility?: ProjectVisibility
|
||||
}>()
|
||||
|
||||
const copyModelLink = useCopyModelLink()
|
||||
@@ -74,6 +87,7 @@ const { copy } = useClipboard()
|
||||
|
||||
const showActionsMenu = ref(false)
|
||||
const openDialog = ref(null as Nullable<ActionTypes>)
|
||||
const embedDialogOpen = ref(false)
|
||||
|
||||
const isMain = computed(() => props.model.name === 'main')
|
||||
const actionsItems = computed<LayoutMenuItem[][]>(() => [
|
||||
@@ -93,7 +107,8 @@ const actionsItems = computed<LayoutMenuItem[][]>(() => [
|
||||
],
|
||||
[
|
||||
{ title: 'Copy Link', id: ActionTypes.Share, icon: LinkIcon },
|
||||
{ title: 'Copy ID', id: ActionTypes.CopyId, icon: FingerPrintIcon }
|
||||
{ title: 'Copy ID', id: ActionTypes.CopyId, icon: FingerPrintIcon },
|
||||
{ title: 'Embed Model', id: ActionTypes.Embed, icon: CodeBracketIcon }
|
||||
],
|
||||
[
|
||||
{
|
||||
@@ -135,6 +150,9 @@ const onActionChosen = (params: { item: LayoutMenuItem; event: MouseEvent }) =>
|
||||
case ActionTypes.CopyId:
|
||||
copy(props.model.id, { successMessage: 'Copied model ID to clipboard' })
|
||||
break
|
||||
case ActionTypes.Embed:
|
||||
embedDialogOpen.value = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,22 +40,26 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-12 flex items-center px-2 py-1 space-x-1">
|
||||
<NuxtLink class="min-w-0 cursor-pointer" :href="finalModelUrl">
|
||||
<div
|
||||
class="h-auto sm:h-12 flex flex-col sm:flex-row sm:items-center px-2 py-1 gap-x-1"
|
||||
>
|
||||
<NuxtLink class="min-w-0 max-w-full cursor-pointer" :href="finalModelUrl">
|
||||
<div
|
||||
v-if="nameParts[0]"
|
||||
class="text-xs text-foreground-2 relative -mb-1 truncate"
|
||||
>
|
||||
{{ nameParts[0] }}
|
||||
</div>
|
||||
<div class="font-bold truncate text-foreground flex-shrink min-w-0">
|
||||
<div
|
||||
class="font-bold text-sm sm:text-base truncate text-foreground flex-shrink min-w-0"
|
||||
>
|
||||
{{ nameParts[1] }}
|
||||
</div>
|
||||
</NuxtLink>
|
||||
<div class="grow" />
|
||||
<div class="hidden sm:flex grow" />
|
||||
<div class="flex items-center">
|
||||
<div
|
||||
:class="`text-xs w-full text-foreground-2 mr-1 truncate transition ${
|
||||
:class="`text-xs w-full text-foreground-2 sm:mr-1 truncate transition ${
|
||||
hovered ? 'sm:w-auto' : 'sm:w-0'
|
||||
}`"
|
||||
>
|
||||
@@ -81,6 +85,7 @@
|
||||
v-model:open="showActionsMenu"
|
||||
:model="model"
|
||||
:project-id="projectId"
|
||||
:visibility="project?.visibility"
|
||||
:can-edit="canEdit"
|
||||
@click.stop.prevent
|
||||
@upload-version="triggerVersionUpload"
|
||||
@@ -136,6 +141,7 @@ graphql(`
|
||||
fragment ProjectPageModelsCardProject on Project {
|
||||
id
|
||||
role
|
||||
visibility
|
||||
}
|
||||
`)
|
||||
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
<template>
|
||||
<template v-if="itemsCount">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3">
|
||||
<div
|
||||
class="relative z-10 grid gap-3"
|
||||
:class="
|
||||
smallView
|
||||
? 'grid-cols-2 sm:grid-cols-3 md:grid-cols-4'
|
||||
: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-4'
|
||||
"
|
||||
>
|
||||
<!-- Decrementing z-index necessary for the actions menu to render correctly. Each card has its own stacking context because of the scale property -->
|
||||
<ProjectPageModelsCard
|
||||
v-for="(item, i) in items"
|
||||
@@ -10,6 +17,7 @@
|
||||
:project="project"
|
||||
:show-actions="showActions"
|
||||
:show-versions="showVersions"
|
||||
height="h-32 sm:h-64"
|
||||
:disable-default-link="disableDefaultLinks"
|
||||
:style="`z-index: ${items.length - i};`"
|
||||
@click="($event) => $emit('model-clicked', { id: item.id, e: $event })"
|
||||
@@ -70,6 +78,7 @@ const props = withDefaults(
|
||||
disablePagination?: boolean
|
||||
sourceApps?: SourceAppDefinition[]
|
||||
contributors?: FormUsersSelectItemFragment[]
|
||||
smallView?: boolean
|
||||
}>(),
|
||||
{
|
||||
showActions: true,
|
||||
|
||||
@@ -24,19 +24,25 @@
|
||||
<div class="flex items-center justify-between mt-3">
|
||||
<UserAvatarGroup :users="teamUsers" class="max-w-[104px]" />
|
||||
<div v-if="activeUser">
|
||||
<FormButton class="ml-2" @click="dialogOpen = true">
|
||||
<FormButton class="ml-2" @click="onButtonClick">
|
||||
{{ project.role === 'stream:owner' ? 'Manage' : 'View' }}
|
||||
</FormButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #default>
|
||||
<ProjectPageTeamDialog v-model:open="dialogOpen" :project="project" />
|
||||
<ProjectPageTeamDialog
|
||||
v-model:open="dialogOpen"
|
||||
:project="project"
|
||||
:open-section="openSection"
|
||||
/>
|
||||
</template>
|
||||
</ProjectPageStatsBlock>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { Cog6ToothIcon, UsersIcon } from '@heroicons/vue/24/outline'
|
||||
import type { Optional } from '@speckle/shared'
|
||||
import { OpenSectionType } from '~/lib/projects/helpers/components'
|
||||
import { useActiveUser } from '~~/lib/auth/composables/activeUser'
|
||||
import { graphql } from '~~/lib/common/generated/gql'
|
||||
import type { ProjectPageStatsBlockTeamFragment } from '~~/lib/common/generated/gql/graphql'
|
||||
@@ -61,6 +67,51 @@ const props = defineProps<{
|
||||
}>()
|
||||
|
||||
const dialogOpen = ref(false)
|
||||
const openSection = ref<OpenSectionType | undefined>()
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const teamUsers = computed(() => props.project.team.map((t) => t.user))
|
||||
|
||||
const readDialogStateFromQuery = async () => {
|
||||
const newSettings = route.query.settings as Optional<string | true>
|
||||
let shouldShow = false
|
||||
|
||||
if (!newSettings) {
|
||||
shouldShow = false
|
||||
} else if (newSettings === 'invite') {
|
||||
shouldShow = true
|
||||
openSection.value = OpenSectionType.Invite
|
||||
} else if (newSettings === 'access') {
|
||||
shouldShow = true
|
||||
openSection.value = OpenSectionType.Access
|
||||
} else {
|
||||
shouldShow = true
|
||||
openSection.value = OpenSectionType.Team
|
||||
}
|
||||
|
||||
if (shouldShow) {
|
||||
dialogOpen.value = true
|
||||
await router.replace({ query: { ...route.query, settings: undefined } })
|
||||
}
|
||||
}
|
||||
|
||||
const onButtonClick = () => {
|
||||
openSection.value = OpenSectionType.Team
|
||||
dialogOpen.value = true
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
readDialogStateFromQuery()
|
||||
})
|
||||
|
||||
watch(
|
||||
() => route.query.settings,
|
||||
(newSettings, oldSettings) => {
|
||||
if (newSettings !== oldSettings) {
|
||||
readDialogStateFromQuery()
|
||||
}
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
@@ -2,12 +2,19 @@
|
||||
<LayoutDialog v-model:open="isOpen" max-width="sm">
|
||||
<template #header>Manage Project</template>
|
||||
<div class="flex flex-col text-foreground">
|
||||
<ProjectPageTeamDialogManageUsers always-open :project="project" />
|
||||
<ProjectPageTeamDialogManageUsers
|
||||
:always-open="openSection === OpenSectionType.Team"
|
||||
:project="project"
|
||||
/>
|
||||
<ProjectPageTeamDialogInviteUser
|
||||
v-if="isOwner && !isServerGuest"
|
||||
:project="project"
|
||||
:default-open="openSection === OpenSectionType.Invite"
|
||||
/>
|
||||
<ProjectPageTeamDialogManagePermissions
|
||||
:project="project"
|
||||
:default-open="openSection === OpenSectionType.Access"
|
||||
/>
|
||||
<ProjectPageTeamDialogManagePermissions :project="project" />
|
||||
<ProjectPageTeamDialogWebhooks v-if="isOwner" :project="project" />
|
||||
<ProjectPageTeamDialogDangerZones
|
||||
v-if="isOwner || canLeaveProject"
|
||||
@@ -20,6 +27,7 @@
|
||||
import type { ProjectPageTeamDialogFragment } from '~~/lib/common/generated/gql/graphql'
|
||||
import { graphql } from '~~/lib/common/generated/gql'
|
||||
import { useTeamDialogInternals } from '~~/lib/projects/composables/team'
|
||||
import { OpenSectionType } from '~~/lib/projects/helpers/components'
|
||||
|
||||
graphql(`
|
||||
fragment ProjectPageTeamDialog on Project {
|
||||
@@ -55,6 +63,7 @@ const emit = defineEmits<{
|
||||
const props = defineProps<{
|
||||
open: boolean
|
||||
project: ProjectPageTeamDialogFragment
|
||||
openSection?: OpenSectionType
|
||||
}>()
|
||||
|
||||
const { isOwner, isServerGuest, canLeaveProject } = useTeamDialogInternals({
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
<template>
|
||||
<LayoutDialogSection allow-overflow border-b border-t title="Invite">
|
||||
<LayoutDialogSection
|
||||
allow-overflow
|
||||
border-b
|
||||
border-t
|
||||
title="Invite"
|
||||
:always-open="defaultOpen"
|
||||
>
|
||||
<template #icon>
|
||||
<UserPlusIcon class="h-full w-full" />
|
||||
</template>
|
||||
@@ -67,6 +73,7 @@ type InvitableUser = UserSearchItem | string
|
||||
|
||||
const props = defineProps<{
|
||||
project: ProjectPageTeamDialogFragment
|
||||
defaultOpen: boolean
|
||||
}>()
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
<template>
|
||||
<LayoutDialogSection allow-overflow border-b title="Access">
|
||||
<LayoutDialogSection
|
||||
allow-overflow
|
||||
border-b
|
||||
title="Access"
|
||||
:always-open="defaultOpen"
|
||||
>
|
||||
<template #icon>
|
||||
<LockOpenIcon
|
||||
v-if="project.visibility === ProjectVisibility.Public"
|
||||
@@ -45,6 +50,7 @@ import { useMixpanel } from '~~/lib/core/composables/mp'
|
||||
|
||||
const props = defineProps<{
|
||||
project: ProjectPageTeamDialogFragment
|
||||
defaultOpen: boolean
|
||||
}>()
|
||||
|
||||
const { isOwner, isServerGuest } = useTeamDialogInternals({
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div
|
||||
class="relative max-w-4xl w-screen h-[100dvh] flex items-center justify-center z-50"
|
||||
>
|
||||
<TourSegmentation v-if="tourState.showSegmentation && step === 0" @next="step++" />
|
||||
<TourSegmentation v-if="showSegmentation && step === 0" @next="step++" />
|
||||
<TourSlideshow v-if="step === 1" @next="step++" />
|
||||
<!-- <OnboardingDialogManager v-if="step === 2" allow-escape @cancel="step++" /> -->
|
||||
<div
|
||||
@@ -27,6 +27,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { useViewerTour } from '~/lib/viewer/composables/tour'
|
||||
import { useSynchronizedCookie } from '~~/lib/common/composables/reactiveCookie'
|
||||
import { useMixpanel } from '~~/lib/core/composables/mp'
|
||||
|
||||
@@ -38,7 +39,7 @@ const hasCompletedChecklistV1 = useSynchronizedCookie<boolean>(
|
||||
{ default: () => false }
|
||||
)
|
||||
|
||||
const tourState = useTourStageState()
|
||||
const { showSegmentation } = useViewerTour()
|
||||
|
||||
const mp = useMixpanel()
|
||||
watch(step, (val) => {
|
||||
|
||||
@@ -73,6 +73,7 @@ import {
|
||||
import type { OnboardingState } from '~~/lib/auth/helpers/onboarding'
|
||||
import { useProcessOnboarding } from '~~/lib/auth/composables/onboarding'
|
||||
import { useCameraUtilities } from '~~/lib/viewer/composables/ui'
|
||||
import { useViewerTour } from '~/lib/viewer/composables/tour'
|
||||
|
||||
const { setMixpanelSegments } = useProcessOnboarding()
|
||||
const {
|
||||
@@ -84,7 +85,7 @@ const {
|
||||
const onboardingState = ref<OnboardingState>({ industry: undefined, role: undefined })
|
||||
|
||||
const { activeUser } = useActiveUser()
|
||||
const tourState = useTourStageState()
|
||||
const tourState = useViewerTour()
|
||||
|
||||
const emit = defineEmits(['next'])
|
||||
|
||||
@@ -102,7 +103,7 @@ function setRole(val: OnboardingRole) {
|
||||
nextView()
|
||||
// NOTE: workaround for being able to view this in storybook
|
||||
if (activeUser.value?.id) setMixpanelSegments(onboardingState.value)
|
||||
tourState.value.showSegmentation = false
|
||||
tourState.showSegmentation.value = false
|
||||
emit('next')
|
||||
}
|
||||
|
||||
|
||||
@@ -60,10 +60,11 @@ import BasicViewerNavigation from '~~/components/tour/content/BasicViewerNavigat
|
||||
import OverlayModel from '~~/components/tour/content/OverlayModel.vue'
|
||||
import { useCameraUtilities } from '~~/lib/viewer/composables/ui'
|
||||
import { useMixpanel } from '~~/lib/core/composables/mp'
|
||||
import { useViewerTour } from '~/lib/viewer/composables/tour'
|
||||
|
||||
const emit = defineEmits(['next'])
|
||||
|
||||
const tourStage = useTourStageState()
|
||||
const tourStage = useViewerTour()
|
||||
const { zoom, setView } = useCameraUtilities()
|
||||
|
||||
// Drives the amount of slideshow items
|
||||
@@ -148,8 +149,8 @@ useViewerAnchoredPoints({
|
||||
const finishSlideshow = () => {
|
||||
zoom()
|
||||
setView('left')
|
||||
tourStage.value.showNavbar = true
|
||||
tourStage.value.showViewerControls = true
|
||||
tourStage.showNavbar.value = true
|
||||
tourStage.showControls.value = true
|
||||
emit('next')
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
const state = useTourStageState()
|
||||
import { useViewerTour } from '~/lib/viewer/composables/tour'
|
||||
|
||||
state.value.showNavbar = true
|
||||
const state = useViewerTour()
|
||||
|
||||
state.value.showViewerControls = true
|
||||
state.showNavbar.value = true
|
||||
state.showControls.value = true
|
||||
</script>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
>
|
||||
<!-- Add new thread bubble -->
|
||||
<ViewerAnchoredPointNewThread
|
||||
v-if="canPostComment && !isEmbedEnabled"
|
||||
v-model="buttonState"
|
||||
:can-post-comment="canPostComment"
|
||||
class="z-[13]"
|
||||
@@ -26,13 +27,15 @@
|
||||
@login="showLoginDialog = true"
|
||||
/>
|
||||
|
||||
<!-- Active users -->
|
||||
<ViewerAnchoredPointUser
|
||||
v-for="user in Object.values(users)"
|
||||
:key="user.state.sessionId"
|
||||
:user="user"
|
||||
class="z-[10]"
|
||||
/>
|
||||
<div v-if="!isEmbedEnabled">
|
||||
<!-- Active users -->
|
||||
<ViewerAnchoredPointUser
|
||||
v-for="user in Object.values(users)"
|
||||
:key="user.state.sessionId"
|
||||
:user="user"
|
||||
class="z-[10]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<AuthLoginPanel
|
||||
v-model:open="showLoginDialog"
|
||||
@@ -46,7 +49,7 @@
|
||||
<ViewerScope :state="state">
|
||||
<div
|
||||
v-if="usersWithAvatars.length > 0"
|
||||
class="px-1 py-1 flex space-x-1 items-center"
|
||||
class="scale-90 flex space-x-1 items-center"
|
||||
>
|
||||
<!-- <UserAvatarGroup :users="activeUserAvatars" :overlap="false" hover-effect /> -->
|
||||
<template v-for="user in usersWithAvatars" :key="user.id">
|
||||
@@ -71,8 +74,16 @@
|
||||
|
||||
<!-- Active user tracking cancel & Follower count display -->
|
||||
<div
|
||||
v-if="(spotlightUserSessionId && spotlightUser) || followers.length !== 0"
|
||||
class="absolute w-screen mt-[3.5rem] h-[calc(100dvh-3.5rem)] z-10 p-1"
|
||||
v-if="
|
||||
(!isEmbedEnabled && spotlightUserSessionId && spotlightUser) ||
|
||||
followers.length !== 0
|
||||
"
|
||||
class="absolute w-screen z-10 p-1"
|
||||
:class="
|
||||
isEmbedEnabled
|
||||
? 'h-[calc(100dvh-3.5rem)]'
|
||||
: 'h-[calc(100dvh-3.5rem)] mt-[3.5rem]'
|
||||
"
|
||||
>
|
||||
<div
|
||||
class="w-full h-full outline -outline-offset-0 outline-8 rounded-md outline-blue-500/40"
|
||||
@@ -104,6 +115,7 @@
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type { Nullable } from '@speckle/shared'
|
||||
import { useEmbed } from '~/lib/viewer/composables/setup/embed'
|
||||
import { useActiveUser } from '~~/lib/auth/composables/activeUser'
|
||||
import type { LimitedUser } from '~~/lib/common/generated/gql/graphql'
|
||||
import type { SetFullyRequired } from '~~/lib/common/helpers/type'
|
||||
@@ -129,6 +141,8 @@ const { isOpenThread, open } = useThreadUtilities()
|
||||
|
||||
const canPostComment = useCheckViewerCommentingAccess()
|
||||
|
||||
const { isEnabled: isEmbedEnabled } = useEmbed()
|
||||
|
||||
const followers = computed(() => {
|
||||
if (!isLoggedIn.value) return []
|
||||
const res = [] as LimitedUser[]
|
||||
|
||||
@@ -1,24 +1,31 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="showControls">
|
||||
<div
|
||||
class="absolute z-20 flex h-[100dvh] flex-col space-y-2 bg-green-300/0 px-2 pt-[4.2rem]"
|
||||
class="absolute z-20 flex max-h-screen simple-scrollbar flex-col space-y-1 md:space-y-2 bg-green-300/0 px-2"
|
||||
:class="
|
||||
showNavbar && !isEmbedEnabled
|
||||
? 'pt-[4.2rem]'
|
||||
: isTransparent
|
||||
? 'pt-2'
|
||||
: 'pt-2 pb-16'
|
||||
"
|
||||
>
|
||||
<!-- Models -->
|
||||
<ViewerControlsButtonToggle
|
||||
v-tippy="modelsShortcut"
|
||||
v-tippy="isSmallerOrEqualSm ? undefined : modelsShortcut"
|
||||
:active="activeControl === 'models'"
|
||||
@click="toggleActiveControl('models')"
|
||||
>
|
||||
<CubeIcon class="h-5 w-5" />
|
||||
<CubeIcon class="h-4 w-4 md:h-5 md:w-5" />
|
||||
</ViewerControlsButtonToggle>
|
||||
|
||||
<!-- Explorer -->
|
||||
<ViewerControlsButtonToggle
|
||||
v-tippy="explorerShortcut"
|
||||
v-tippy="isSmallerOrEqualSm ? undefined : explorerShortcut"
|
||||
:active="activeControl === 'explorer'"
|
||||
@click="toggleActiveControl('explorer')"
|
||||
>
|
||||
<IconFileExplorer class="h-5 w-5" />
|
||||
<IconFileExplorer class="h-4 w-4 md:h-5 md:w-5" />
|
||||
</ViewerControlsButtonToggle>
|
||||
|
||||
<!-- TODO -->
|
||||
@@ -31,17 +38,17 @@
|
||||
|
||||
<!-- Comment threads -->
|
||||
<ViewerControlsButtonToggle
|
||||
v-tippy="discussionsShortcut"
|
||||
v-tippy="isSmallerOrEqualSm ? undefined : discussionsShortcut"
|
||||
:active="activeControl === 'discussions'"
|
||||
@click="toggleActiveControl('discussions')"
|
||||
>
|
||||
<ChatBubbleLeftRightIcon class="h-5 w-5" />
|
||||
<ChatBubbleLeftRightIcon class="h-4 w-4 md:h-5 md:w-5" />
|
||||
</ViewerControlsButtonToggle>
|
||||
|
||||
<!-- Automateeeeeeee FTW -->
|
||||
<ViewerControlsButtonToggle
|
||||
v-if="allAutomationRuns.length !== 0"
|
||||
v-tippy="summary.longSummary"
|
||||
v-tippy="isSmallerOrEqualSm ? undefined : summary.longSummary"
|
||||
:active="activeControl === 'automate'"
|
||||
class="p-2"
|
||||
@click="toggleActiveControl('automate')"
|
||||
@@ -56,68 +63,89 @@
|
||||
|
||||
<!-- Measurements -->
|
||||
<ViewerControlsButtonToggle
|
||||
v-tippy="measureShortcut"
|
||||
v-tippy="isSmallerOrEqualSm ? undefined : measureShortcut"
|
||||
:active="activeControl === 'measurements'"
|
||||
@click="toggleMeasurements"
|
||||
>
|
||||
<IconMeasurements class="h-5 w-5" />
|
||||
<IconMeasurements class="h-4 w-4 md:h-5 md:w-5" />
|
||||
</ViewerControlsButtonToggle>
|
||||
|
||||
<!-- Standard viewer controls -->
|
||||
<ViewerControlsButtonGroup>
|
||||
<!-- Views -->
|
||||
<ViewerViewsMenu v-tippy="'Views'" />
|
||||
<!-- Zoom extents -->
|
||||
<ViewerControlsButtonToggle
|
||||
v-tippy="zoomExtentsShortcut"
|
||||
flat
|
||||
@click="trackAndzoomExtentsOrSelection()"
|
||||
<div class="w-8 flex gap-2">
|
||||
<div class="md:hidden">
|
||||
<ViewerControlsButtonToggle
|
||||
:active="activeControl === 'mobileOverflow'"
|
||||
@click="toggleActiveControl('mobileOverflow')"
|
||||
>
|
||||
<ChevronDoubleRightIcon
|
||||
class="h-4 w-4 md:h-5 md:w-5 transition"
|
||||
:class="activeControl === 'mobileOverflow' ? 'rotate-180' : ''"
|
||||
/>
|
||||
</ViewerControlsButtonToggle>
|
||||
</div>
|
||||
<div
|
||||
class="-mt-28 md:mt-0 bg-foundation md:bg-transparent md:gap-2 shadow-md md:shadow-none flex flex-col rounded-lg transition-all *:shadow-none *:py-0 *:md:shadow-md *:md:py-2"
|
||||
:class="[
|
||||
activeControl === 'mobileOverflow' ? '' : '-translate-x-24 md:translate-x-0'
|
||||
]"
|
||||
>
|
||||
<ArrowsPointingOutIcon class="h-5 w-5" />
|
||||
</ViewerControlsButtonToggle>
|
||||
<ViewerControlsButtonGroup>
|
||||
<!-- Views -->
|
||||
<ViewerViewsMenu v-tippy="isSmallerOrEqualSm ? undefined : 'Views'" />
|
||||
<!-- Zoom extents -->
|
||||
<ViewerControlsButtonToggle
|
||||
v-tippy="isSmallerOrEqualSm ? undefined : zoomExtentsShortcut"
|
||||
flat
|
||||
@click="trackAndzoomExtentsOrSelection()"
|
||||
>
|
||||
<ArrowsPointingOutIcon class="h-4 w-4 md:h-5 md:w-5" />
|
||||
</ViewerControlsButtonToggle>
|
||||
|
||||
<!-- Sun and lights -->
|
||||
<ViewerSunMenu v-tippy="'Light Controls'" />
|
||||
</ViewerControlsButtonGroup>
|
||||
<ViewerControlsButtonGroup>
|
||||
<!-- Projection type -->
|
||||
<!-- TODO (Question for fabs): How to persist state between page navigation? e.g., swap to iso mode, move out, move back, iso mode is still on in viewer but not in ui -->
|
||||
<ViewerControlsButtonToggle
|
||||
v-tippy="projectionShortcut"
|
||||
flat
|
||||
secondary
|
||||
:active="isOrthoProjection"
|
||||
@click="trackAndtoggleProjection()"
|
||||
>
|
||||
<IconPerspective v-if="isOrthoProjection" class="h-4 w-4" />
|
||||
<IconPerspectiveMore v-else class="h-4 w-4" />
|
||||
</ViewerControlsButtonToggle>
|
||||
<!-- Sun and lights -->
|
||||
<ViewerSunMenu
|
||||
v-tippy="isSmallerOrEqualSm ? undefined : 'Light Controls'"
|
||||
/>
|
||||
</ViewerControlsButtonGroup>
|
||||
<ViewerControlsButtonGroup>
|
||||
<!-- Projection type -->
|
||||
<!-- TODO (Question for fabs): How to persist state between page navigation? e.g., swap to iso mode, move out, move back, iso mode is still on in viewer but not in ui -->
|
||||
<ViewerControlsButtonToggle
|
||||
v-tippy="isSmallerOrEqualSm ? undefined : projectionShortcut"
|
||||
flat
|
||||
secondary
|
||||
:active="isOrthoProjection"
|
||||
@click="trackAndtoggleProjection()"
|
||||
>
|
||||
<IconPerspective v-if="isOrthoProjection" class="h-3.5 md:h-4 w-4" />
|
||||
<IconPerspectiveMore v-else class="h-3.5 md:h-4 w-4" />
|
||||
</ViewerControlsButtonToggle>
|
||||
|
||||
<!-- Section Box -->
|
||||
<ViewerControlsButtonToggle
|
||||
v-tippy="sectionBoxShortcut"
|
||||
flat
|
||||
secondary
|
||||
:active="isSectionBoxEnabled"
|
||||
@click="toggleSectionBox()"
|
||||
>
|
||||
<ScissorsIcon class="h-5 w-5" />
|
||||
</ViewerControlsButtonToggle>
|
||||
<!-- Section Box -->
|
||||
<ViewerControlsButtonToggle
|
||||
v-tippy="isSmallerOrEqualSm ? undefined : sectionBoxShortcut"
|
||||
flat
|
||||
secondary
|
||||
:active="isSectionBoxEnabled"
|
||||
@click="toggleSectionBox()"
|
||||
>
|
||||
<ScissorsIcon class="h-4 w-4 md:h-5 md:w-5" />
|
||||
</ViewerControlsButtonToggle>
|
||||
|
||||
<!-- Explosion -->
|
||||
<ViewerExplodeMenu v-tippy="'Explode'" />
|
||||
<!-- Explosion -->
|
||||
<ViewerExplodeMenu v-tippy="isSmallerOrEqualSm ? undefined : 'Explode'" />
|
||||
|
||||
<!-- Settings -->
|
||||
<ViewerSettingsMenu />
|
||||
</ViewerControlsButtonGroup>
|
||||
<!-- Settings -->
|
||||
<ViewerSettingsMenu />
|
||||
</ViewerControlsButtonGroup>
|
||||
</div>
|
||||
<!-- Standard viewer controls -->
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
ref="scrollableControlsContainer"
|
||||
:class="`simple-scrollbar absolute z-10 mx-14 mt-[4rem] mb-4 max-h-[calc(100dvh-5.5rem)] w-64 sm:w-72 overflow-y-auto px-[2px] py-[2px] transition ${
|
||||
:class="`simple-scrollbar absolute z-10 ml-12 md:ml-14 mb-4 max-h-[calc(100dvh-4.5rem)] w-56 md:w-72 overflow-y-auto px-[2px] py-[2px] transition ${
|
||||
activeControl !== 'none'
|
||||
? 'translate-x-0 opacity-100'
|
||||
: '-translate-x-[100%] opacity-0'
|
||||
}`"
|
||||
} ${isEmbedEnabled ? 'mt-1.5' : 'mt-[4rem]'}`"
|
||||
>
|
||||
<div v-show="activeControl.length !== 0 && activeControl === 'measurements'">
|
||||
<KeepAlive>
|
||||
@@ -185,7 +213,8 @@ import {
|
||||
ChatBubbleLeftRightIcon,
|
||||
ArrowsPointingOutIcon,
|
||||
ScissorsIcon,
|
||||
PlusIcon
|
||||
PlusIcon,
|
||||
ChevronDoubleRightIcon
|
||||
} from '@heroicons/vue/24/outline'
|
||||
import type { Nullable } from '@speckle/shared'
|
||||
import {
|
||||
@@ -213,6 +242,8 @@ const {
|
||||
import { AutomationRunStatus } from '~~/lib/common/generated/gql/graphql'
|
||||
import type { AutomationRun } from '~~/lib/common/generated/gql/graphql'
|
||||
import { useIsSmallerOrEqualThanBreakpoint } from '~~/composables/browser'
|
||||
import { useEmbed } from '~/lib/viewer/composables/setup/embed'
|
||||
import { useViewerTour } from '~/lib/viewer/composables/tour'
|
||||
|
||||
const { resourceItems, modelsAndVersionIds } = useInjectedViewerLoadedResources()
|
||||
|
||||
@@ -220,6 +251,10 @@ const { toggleSectionBox, isSectionBoxEnabled } = useSectionBoxUtilities()
|
||||
|
||||
const { enableMeasurements } = useMeasurementUtilities()
|
||||
|
||||
const { showNavbar, showControls } = useViewerTour()
|
||||
|
||||
const { isTransparent, isEnabled: isEmbedEnabled } = useEmbed()
|
||||
|
||||
const allAutomationRuns = computed(() => {
|
||||
const allAutomationStatuses = modelsAndVersionIds.value
|
||||
.map((model) => model.model.loadedVersion.items[0].automationStatus)
|
||||
@@ -290,6 +325,7 @@ type ActiveControl =
|
||||
| 'discussions'
|
||||
| 'automate'
|
||||
| 'measurements'
|
||||
| 'mobileOverflow'
|
||||
|
||||
const openAddModel = ref(false)
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<template>
|
||||
<div
|
||||
v-show="hasAnyFiltersApplied"
|
||||
class="absolute bottom-4 left-0 w-screen p-2 bg-pink-300/0 flex justify-center pointer-events-none"
|
||||
class="absolute left-0 w-screen p-2 bg-pink-300/0 flex justify-center pointer-events-none"
|
||||
:class="isEmbedEnabled ? 'bottom-16 mb-2' : 'bottom-4'"
|
||||
>
|
||||
<Transition
|
||||
enter-active-class="transform ease-out duration-300 transition"
|
||||
@@ -11,7 +12,11 @@
|
||||
leave-from-class="opacity-100"
|
||||
leave-to-class="opacity-0"
|
||||
>
|
||||
<FormButton size="sm" class="pointer-events-auto" @click="trackAndResetFilters">
|
||||
<FormButton
|
||||
:size="isEmbedEnabled ? 'xs' : 'sm'"
|
||||
class="pointer-events-auto"
|
||||
@click="trackAndResetFilters"
|
||||
>
|
||||
Reset Filters
|
||||
</FormButton>
|
||||
</Transition>
|
||||
@@ -20,11 +25,14 @@
|
||||
<script setup lang="ts">
|
||||
import { useMixpanel } from '~~/lib/core/composables/mp'
|
||||
import { useFilterUtilities } from '~~/lib/viewer/composables/ui'
|
||||
import { useEmbed } from '~/lib/viewer/composables/setup/embed'
|
||||
|
||||
const {
|
||||
resetFilters,
|
||||
filters: { hasAnyFiltersApplied }
|
||||
} = useFilterUtilities()
|
||||
|
||||
const { isEnabled: isEmbedEnabled } = useEmbed()
|
||||
const mp = useMixpanel()
|
||||
const trackAndResetFilters = () => {
|
||||
resetFilters()
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div
|
||||
v-show="viewerBusy"
|
||||
:class="`absolute w-full max-w-screen h-1 bg-blue-500/20 overflow-hidden ${
|
||||
showNavbar ? 'mt-14' : 'mt-0'
|
||||
showNavbar && !isEmbedEnabled ? 'mt-14' : 'mt-0'
|
||||
} text-xs text-foreground-on-primary z-50`"
|
||||
>
|
||||
<div class="swoosher absolute top-0 bg-blue-500/50 rounded-md"></div>
|
||||
@@ -11,9 +11,13 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { useEmbed } from '~/lib/viewer/composables/setup/embed'
|
||||
import { useViewerTour } from '~/lib/viewer/composables/tour'
|
||||
import { useInjectedViewerInterfaceState } from '~~/lib/viewer/composables/setup'
|
||||
const { isEnabled: isEmbedEnabled } = useEmbed()
|
||||
|
||||
const { viewerBusy } = useInjectedViewerInterfaceState()
|
||||
const { showNavbar } = useTourStageState().value
|
||||
const { showNavbar } = useViewerTour()
|
||||
</script>
|
||||
<style scoped>
|
||||
.swoosher {
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<div>
|
||||
<ViewerPostSetupWrapper>
|
||||
<div class="flex-1">
|
||||
<!-- Nav -->
|
||||
<Portal to="navigation">
|
||||
<ViewerScope :state="state">
|
||||
<HeaderNavLink
|
||||
:to="`/projects/${project?.id}`"
|
||||
:name="project?.name"
|
||||
></HeaderNavLink>
|
||||
<ViewerExplorerNavbarLink />
|
||||
</ViewerScope>
|
||||
</Portal>
|
||||
|
||||
<ClientOnly>
|
||||
<!-- Tour host -->
|
||||
<div
|
||||
v-if="showTour"
|
||||
class="fixed w-full h-[100dvh] flex justify-center items-center pointer-events-none z-[100]"
|
||||
>
|
||||
<TourOnboarding />
|
||||
</div>
|
||||
<!-- Viewer host -->
|
||||
<div
|
||||
class="special-gradient absolute z-10 overflow-hidden w-screen"
|
||||
:class="
|
||||
isEmbedEnabled
|
||||
? isTransparent
|
||||
? 'viewer-transparent h-[100dvh]'
|
||||
: 'h-[calc(100dvh-3.5rem)]'
|
||||
: 'h-[100dvh]'
|
||||
"
|
||||
>
|
||||
<ViewerBase />
|
||||
<Transition
|
||||
enter-from-class="opacity-0"
|
||||
enter-active-class="transition duration-1000"
|
||||
>
|
||||
<ViewerAnchoredPoints v-show="showControls" />
|
||||
</Transition>
|
||||
</div>
|
||||
|
||||
<!-- Global loading bar -->
|
||||
<ViewerLoadingBar class="z-20" />
|
||||
|
||||
<!-- Sidebar controls -->
|
||||
<Transition
|
||||
enter-from-class="opacity-0"
|
||||
enter-active-class="transition duration-1000"
|
||||
>
|
||||
<ViewerControls v-show="showControls" class="z-20" />
|
||||
</Transition>
|
||||
|
||||
<!-- Viewer Object Selection Info Display -->
|
||||
<Transition
|
||||
v-if="!hideSelectionInfo"
|
||||
enter-from-class="opacity-0"
|
||||
enter-active-class="transition duration-1000"
|
||||
>
|
||||
<div v-show="showControls">
|
||||
<ViewerSelectionSidebar class="z-20" />
|
||||
</div>
|
||||
</Transition>
|
||||
<!-- Shows up when filters are applied for an easy return to normality -->
|
||||
<ViewerGlobalFilterReset class="z-20" :embed="!!isEmbedEnabled" />
|
||||
</ClientOnly>
|
||||
</div>
|
||||
</ViewerPostSetupWrapper>
|
||||
<ViewerEmbedFooter
|
||||
:name="modelName || 'Loading...'"
|
||||
:date="lastUpdate"
|
||||
:url="route.path"
|
||||
/>
|
||||
<Portal to="primary-actions">
|
||||
<HeaderNavShare
|
||||
v-if="project"
|
||||
:resource-id-string="modelId"
|
||||
:project-id="project.id"
|
||||
:visibility="project.visibility"
|
||||
/>
|
||||
</Portal>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
useSetupViewer,
|
||||
type InjectableViewerState
|
||||
} from '~~/lib/viewer/composables/setup'
|
||||
import dayjs from 'dayjs'
|
||||
import { graphql } from '~~/lib/common/generated/gql'
|
||||
import { useEmbed } from '~/lib/viewer/composables/setup/embed'
|
||||
import { useViewerTour } from '~/lib/viewer/composables/tour'
|
||||
|
||||
const emit = defineEmits<{
|
||||
setup: [InjectableViewerState]
|
||||
}>()
|
||||
|
||||
const route = useRoute()
|
||||
const { showTour, showControls } = useViewerTour()
|
||||
|
||||
const modelId = computed(() => route.params.modelId as string)
|
||||
|
||||
const projectId = computed(() => route.params.id as string)
|
||||
|
||||
const state = useSetupViewer({
|
||||
projectId
|
||||
})
|
||||
const { isEnabled: isEmbedEnabled, hideSelectionInfo, isTransparent } = useEmbed()
|
||||
|
||||
emit('setup', state)
|
||||
|
||||
const {
|
||||
resources: {
|
||||
response: { project }
|
||||
}
|
||||
} = state
|
||||
|
||||
graphql(`
|
||||
fragment ModelPageProject on Project {
|
||||
id
|
||||
createdAt
|
||||
name
|
||||
visibility
|
||||
}
|
||||
`)
|
||||
|
||||
const title = computed(() =>
|
||||
project.value?.name.length ? `Viewer - ${project.value.name}` : ''
|
||||
)
|
||||
|
||||
const modelName = computed(() => {
|
||||
if (project.value?.models?.items && project.value.models.items.length > 0) {
|
||||
return project.value.models.items[0].name
|
||||
} else {
|
||||
return project.value?.name
|
||||
}
|
||||
})
|
||||
|
||||
const lastUpdate = computed(() => {
|
||||
if (project.value?.models?.items[0] && project.value.models.items[0].updatedAt) {
|
||||
return 'Updated ' + dayjs(project.value.models.items[0].updatedAt).fromNow()
|
||||
} else if (project.value) {
|
||||
return 'Created ' + dayjs(project.value.createdAt).fromNow()
|
||||
} else return undefined
|
||||
})
|
||||
|
||||
useHead({ title })
|
||||
</script>
|
||||
@@ -1,12 +1,18 @@
|
||||
<template>
|
||||
<slot />
|
||||
<slot v-if="!wrapper" />
|
||||
<div v-else>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { useSetupViewerScope } from '~~/lib/viewer/composables/setup'
|
||||
import type { InjectableViewerState } from '~~/lib/viewer/composables/setup'
|
||||
import {
|
||||
useSetupViewerScope,
|
||||
type InjectableViewerState
|
||||
} from '~/lib/viewer/composables/setup/core'
|
||||
|
||||
const props = defineProps<{
|
||||
state: InjectableViewerState
|
||||
wrapper?: boolean
|
||||
}>()
|
||||
|
||||
useSetupViewerScope(props.state)
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
</button>
|
||||
<ViewerCommentsPortalOrDiv to="mobileComments">
|
||||
<div
|
||||
v-if="modelValue.isExpanded"
|
||||
v-if="modelValue.isExpanded && !isEmbedEnabled"
|
||||
class="bg-foundation px-2 py-2 text-sm text-primary sm:hidden font-medium flex justify-between items-center"
|
||||
>
|
||||
Add Comment
|
||||
@@ -87,6 +87,9 @@ import {
|
||||
} from '~~/lib/viewer/helpers/comments'
|
||||
import { useMixpanel } from '~~/lib/core/composables/mp'
|
||||
import { useThreadUtilities } from '~~/lib/viewer/composables/ui'
|
||||
import { useEmbed } from '~/lib/viewer/composables/setup/embed'
|
||||
|
||||
const { isEnabled: isEmbedEnabled } = useEmbed()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', v: ViewerNewThreadBubbleModel): void
|
||||
|
||||
@@ -48,12 +48,9 @@
|
||||
]"
|
||||
>
|
||||
<div
|
||||
class="relative w-full sm:w-80 flex py-2 pl-3 pr-2 sm:px-2 bg-foundation-2"
|
||||
class="relative w-full sm:w-80 flex justify-between items-center py-2 pl-3 pr-2 sm:px-2 bg-foundation-2"
|
||||
>
|
||||
<div class="flex-grow flex items-center">
|
||||
<span class="sm:hidden text-primary text-sm font-medium">
|
||||
Discussions
|
||||
</span>
|
||||
<FormButton
|
||||
v-tippy="'Previous'"
|
||||
size="sm"
|
||||
@@ -115,7 +112,7 @@
|
||||
<div class="relative w-full sm:w-80 flex flex-col flex-1 justify-between">
|
||||
<div
|
||||
ref="commentsContainer"
|
||||
class="max-h-[calc(50vh)] sm:max-h-[300px] 2xl:max-h-[500px] pb-20 overflow-y-auto simple-scrollbar flex flex-col space-y-1 pr-1"
|
||||
class="max-h-[40vh] sm:max-h-[300px] 2xl:max-h-[500px] overflow-y-auto simple-scrollbar flex flex-col space-y-1 pr-1"
|
||||
>
|
||||
<div
|
||||
v-if="!isThreadResourceLoaded"
|
||||
@@ -147,12 +144,26 @@
|
||||
</div>
|
||||
</div>
|
||||
<ViewerAnchoredPointThreadNewReply
|
||||
v-if="!modelValue.archived && canReply"
|
||||
v-if="showNewReplyComponent"
|
||||
:model-value="modelValue"
|
||||
@submit="onNewReply"
|
||||
/>
|
||||
<div
|
||||
v-if="!canReply"
|
||||
v-if="isEmbedEnabled"
|
||||
class="flex justify-between w-full gap-2 p-2 mt-2"
|
||||
>
|
||||
<FormButton
|
||||
:icon-right="ArrowTopRightOnSquareIcon"
|
||||
full-width
|
||||
:to="getLinkToThread(projectId, props.modelValue)"
|
||||
external
|
||||
target="_blank"
|
||||
>
|
||||
Reply in Speckle
|
||||
</FormButton>
|
||||
</div>
|
||||
<div
|
||||
v-if="!canReply && !isEmbedEnabled"
|
||||
class="p-3 flex flex-col items-center justify-center bg-foundation-2"
|
||||
>
|
||||
<FormButton full-width @click="$emit('login')">Reply</FormButton>
|
||||
@@ -201,6 +212,7 @@ import {
|
||||
import { useDisableGlobalTextSelection } from '~~/lib/common/composables/window'
|
||||
import { useMixpanel } from '~~/lib/core/composables/mp'
|
||||
import { useThreadUtilities } from '~~/lib/viewer/composables/ui'
|
||||
import { useEmbed } from '~/lib/viewer/composables/setup/embed'
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', v: CommentBubbleModel): void
|
||||
@@ -214,9 +226,12 @@ const props = defineProps<{
|
||||
modelValue: CommentBubbleModel
|
||||
}>()
|
||||
|
||||
const { isEmbedEnabled } = useEmbed()
|
||||
|
||||
const threadId = computed(() => props.modelValue.id)
|
||||
const { copy } = useClipboard()
|
||||
const { activeUser } = useActiveUser()
|
||||
const { isSmallerOrEqualSm } = useIsSmallerOrEqualThanBreakpoint()
|
||||
|
||||
const archiveComment = useArchiveComment()
|
||||
const { triggerNotification } = useGlobalToast()
|
||||
@@ -248,6 +263,15 @@ const comments = computed(() => [
|
||||
...props.modelValue.replies.items.slice().reverse()
|
||||
])
|
||||
|
||||
const showNewReplyComponent = computed(() => {
|
||||
return (
|
||||
!props.modelValue.archived &&
|
||||
canReply.value &&
|
||||
!isSmallerOrEqualSm.value &&
|
||||
!isEmbedEnabled.value
|
||||
)
|
||||
})
|
||||
|
||||
// Note: conflicted with dragging styles, so took it out temporarily
|
||||
// const { style } = useExpandedThreadResponsiveLocation({
|
||||
// threadContainer,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!-- eslint-disable vuejs-accessibility/no-autofocus -->
|
||||
<template>
|
||||
<div
|
||||
class="absolute bottom-0 left-0 bg-foundation pl-4 pr-3 py-2 sm:py-1.5 rounded-b flex items-center w-full"
|
||||
class="hidden sm:flex bg-foundation pl-4 pr-3 py-2 sm:py-1.5 rounded-b items-center w-full"
|
||||
>
|
||||
<FormButton
|
||||
:icon-left="PaperClipIcon"
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
size="xs"
|
||||
:icon-left="loadedVersionsOnly ? CheckCircleIcon : CheckCircleIconOutlined"
|
||||
text
|
||||
class="!text-left"
|
||||
@click="
|
||||
loadedVersionsOnly = loadedVersionsOnly ? undefined : 'loadedVersionsOnly'
|
||||
"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<!-- eslint-disable-next-line vuejs-accessibility/click-events-have-key-events -->
|
||||
<div
|
||||
:class="`py-2 my-2 px-2 flex flex-col space-y-1 bg-foundation border-l-4 hover:shadow-lg hover:bg-primary-muted rounded transition cursor-pointer
|
||||
:class="`py-1 sm:py-2 my-1 sm:my-2 px-2 flex flex-col space-y-1 bg-foundation border-l-4 hover:shadow-lg hover:bg-primary-muted rounded transition cursor-pointer
|
||||
${isOpenInViewer ? 'border-primary' : 'border-transparent'}
|
||||
`"
|
||||
@click="open(thread.id)"
|
||||
@@ -27,7 +27,7 @@
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="truncate text-sm mb-1">
|
||||
<div class="truncate text-xs sm:text-sm mb-1">
|
||||
{{ thread.rawText }}
|
||||
</div>
|
||||
<div
|
||||
|
||||
@@ -18,6 +18,5 @@ const props = defineProps({
|
||||
})
|
||||
|
||||
const breakpoints = useBreakpoints(TailwindBreakpoints)
|
||||
|
||||
const isMobile = computed(() => breakpoints.smallerOrEqual('sm').value)
|
||||
const isMobile = breakpoints.smallerOrEqual('sm')
|
||||
</script>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<button
|
||||
:class="`bg-foundation text-foreground shadow-md rounded-lg w-10 flex flex-col justify-center space-y-1 py-1`"
|
||||
<div
|
||||
:class="`bg-foundation text-foreground shadow-md rounded-lg w-8 md:w-10 flex flex-col justify-center md:gap-1 py-1`"
|
||||
>
|
||||
<slot></slot>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<button
|
||||
:class="`transition rounded-lg w-10 h-10 flex items-center justify-center ${shadowClasses} ${colorClasses} active:scale-[0.9] outline-none`"
|
||||
:class="`transition rounded-lg w-8 md:w-10 h-8 md:h-10 shrink-0 flex items-center justify-center ${shadowClasses} ${colorClasses} active:scale-[0.9] outline-none`"
|
||||
>
|
||||
<slot></slot>
|
||||
</button>
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<ClientOnly>
|
||||
<div
|
||||
v-if="isEmbedEnabled"
|
||||
class="select-none fixed bottom-0 left-0 w-full z-20 flex gap-3 px-4 h-14 items-center"
|
||||
:class="isTransparent ? 'bg-transparent' : 'bg-foundation'"
|
||||
>
|
||||
<HeaderLogoBlock
|
||||
large-icon
|
||||
to="https://speckle.systems/"
|
||||
target="_blank"
|
||||
show-text-on-mobile
|
||||
:active="false"
|
||||
/>
|
||||
<div class="h-6 w-px bg-outline-3"></div>
|
||||
<div class="flex flex-col">
|
||||
<NuxtLink :to="url" target="_blank" class="leading-3">
|
||||
<div class="flex items-center gap-1 w-full">
|
||||
<h2 class="font-bold text-base text-sm truncate text-foreground">
|
||||
{{ name }}
|
||||
</h2>
|
||||
<ArrowTopRightOnSquareIcon class="h-3 w-3" />
|
||||
</div>
|
||||
<span v-if="date" class="text-xs text-foreground-2">
|
||||
{{ date }}
|
||||
</span>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</ClientOnly>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ArrowTopRightOnSquareIcon } from '@heroicons/vue/20/solid'
|
||||
import { useEmbed } from '~/lib/viewer/composables/setup/embed'
|
||||
|
||||
defineProps<{
|
||||
date?: string
|
||||
name?: string
|
||||
url?: string
|
||||
}>()
|
||||
|
||||
const { isEmbedEnabled, isTransparent } = useEmbed()
|
||||
</script>
|
||||
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<div>
|
||||
<button
|
||||
class="group flex items-center justify-center absolute inset-0"
|
||||
@click="$emit('play')"
|
||||
>
|
||||
<div v-if="previewUrl" class="absolute inset-0">
|
||||
<PreviewImage :preview-url="previewUrl" />
|
||||
</div>
|
||||
<div
|
||||
class="relative z-10 pointer-events-none group-hover:scale-110 group-hover:shadow-xl shadow h-14 w-14 rounded-full border border-foundation bg-primary flex items-center justify-center transition -mt-10"
|
||||
>
|
||||
<PlayIcon class="h-6 w-6 ml-[3px] text-foundation" />
|
||||
</div>
|
||||
</button>
|
||||
<ViewerEmbedFooter :url="projectUrl" name="View in Speckle" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { PlayIcon } from '@heroicons/vue/20/solid'
|
||||
|
||||
const route = useRoute()
|
||||
const {
|
||||
public: { apiOrigin }
|
||||
} = useRuntimeConfig()
|
||||
|
||||
const projectUrl = route.path
|
||||
|
||||
const projectId = route.params.id as string
|
||||
const modelId = route.params.modelId as string
|
||||
|
||||
const previewUrl = computed(() => {
|
||||
if (modelId) {
|
||||
const url = new URL(`/preview/${projectId}/commits/${modelId}`, apiOrigin)
|
||||
return url.toString()
|
||||
} else if (projectId) {
|
||||
const url = new URL(`/preview/${projectId}`, apiOrigin)
|
||||
return url.toString()
|
||||
} else return null
|
||||
})
|
||||
|
||||
defineEmits<{
|
||||
(e: 'play'): void
|
||||
}>()
|
||||
</script>
|
||||
@@ -3,7 +3,7 @@
|
||||
<PopoverButton v-slot="{ open }" as="template">
|
||||
<ViewerControlsButtonToggle flat secondary :active="open || isActive">
|
||||
<!-- <ChevronUpDownIcon class="w-5 h-5 rotate-45" /> -->
|
||||
<IconExplode class="h-5 w-5" />
|
||||
<IconExplode class="h-4 w-4 sm:h-5 sm:w-5" />
|
||||
</ViewerControlsButtonToggle>
|
||||
</PopoverButton>
|
||||
<Transition
|
||||
@@ -15,20 +15,22 @@
|
||||
leave-to-class="opacity-0"
|
||||
>
|
||||
<PopoverPanel
|
||||
class="absolute translate-x-0 left-12 top-2 p-2 bg-foundation max-h-64 simple-scrollbar overflow-y-auto outline outline-2 outline-primary-muted rounded-lg shadow-lg overflow-hidden flex flex-col space-y-2"
|
||||
class="absolute translate-x-0 left-12 top-0 p-2 bg-foundation max-h-64 simple-scrollbar overflow-y-auto outline outline-2 outline-primary-muted rounded-lg shadow-lg overflow-hidden flex flex-col space-y-2"
|
||||
>
|
||||
<div class="flex items-center space-x-1">
|
||||
<input
|
||||
id="intensity"
|
||||
v-model="explodeFactor"
|
||||
class="h-2 mr-2"
|
||||
class="w-24 sm:w-32 h-2 mr-2"
|
||||
type="range"
|
||||
name="intensity"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.01"
|
||||
/>
|
||||
<label class="text-sm text-foreground-2" for="intensity">Intensity</label>
|
||||
<label class="text-xs sm:text-sm text-foreground-2" for="intensity">
|
||||
Intensity
|
||||
</label>
|
||||
</div>
|
||||
</PopoverPanel>
|
||||
</Transition>
|
||||
|
||||
@@ -40,8 +40,8 @@
|
||||
</div>
|
||||
</template>
|
||||
<div
|
||||
:class="`relative flex flex-col space-y-2 py-2 px-2 simple-scrollbar overflow-y-scroll overflow-x-hidden shadow-inner ${
|
||||
showAllFilters ? 'h-44 visible' : 'h-0 invisible'
|
||||
:class="`relative flex flex-col space-y-2 px-2 simple-scrollbar overflow-y-scroll overflow-x-hidden shadow-inner ${
|
||||
showAllFilters ? 'h-44 visible py-2' : 'h-0 invisible py-1'
|
||||
} transition-[height] border-b-2 border-primary-muted`"
|
||||
>
|
||||
<div class="sticky top-0">
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
} ${isSelected ? 'border-primary bg-primary-muted' : 'border-transparent'}`"
|
||||
@click="setSelection()"
|
||||
>
|
||||
<div class="flex space-x-2 items-center flex-shrink truncate text-sm">
|
||||
<div class="flex space-x-2 items-center flex-shrink truncate text-xs sm:text-sm">
|
||||
<span
|
||||
v-if="color"
|
||||
class="w-3 h-3 rounded"
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
<template>
|
||||
<div class="bg-foundation sm:rounded-lg overflow-hidden shadow flex flex-col">
|
||||
<div class="bg-foundation rounded-lg overflow-hidden shadow flex flex-col">
|
||||
<div class="sticky top-0 z-50 flex flex-col bg-foundation">
|
||||
<div v-if="!hideClose" class="absolute top-2 right-2 sm:right-0 z-10">
|
||||
<div
|
||||
v-if="!hideClose"
|
||||
class="absolute top-1.5 sm:top-2 right-0.5 sm:right-0 z-10"
|
||||
>
|
||||
<FormButton size="sm" color="secondary" text @click="$emit('close')">
|
||||
<XMarkIcon class="h-4 w-4 sm:h-3 sm:w-3 text-primary sm:text-foreground" />
|
||||
</FormButton>
|
||||
</div>
|
||||
<div
|
||||
v-if="$slots.title"
|
||||
class="flex items-center h-10 px-3 border-b border-outline-3 dark:border-foundation-2 bg-foundation rounded-t"
|
||||
class="flex items-center py-2 px-3 border-b border-outline-3 dark:border-foundation-2 bg-foundation"
|
||||
>
|
||||
<div
|
||||
class="flex items-center h-full w-full pr-8 font-semibold sm:font-bold text-sm text-primary"
|
||||
@@ -21,7 +24,7 @@
|
||||
</div>
|
||||
<div
|
||||
v-if="$slots.actions"
|
||||
class="flex items-center h-8 sm:h-10 gap-2 px-2"
|
||||
class="flex items-center py-1 sm:py-2 gap-2 px-2"
|
||||
:class="
|
||||
moveActionsToBottom
|
||||
? 'order-3 border-t border-outline-3 mt-2'
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
<ViewerLayoutPanel move-actions-to-bottom @close="$emit('close')">
|
||||
<template #title>Measure Mode</template>
|
||||
<div
|
||||
class="flex items-center gap-2 text-sm px-3 py-2 border-b border-outline-3 text-foreground-2"
|
||||
class="flex items-center gap-2 text-xs sm:text-sm px-3 py-1.5 sm:py-2 border-b border-outline-3 text-foreground-2"
|
||||
>
|
||||
<InformationCircleIcon class="h-6 h-6 shrink-0" />
|
||||
<InformationCircleIcon class="h-5 w-5 sm:h-6 sm:h-6 shrink-0" />
|
||||
<span class="max-w-[210px]">
|
||||
Reloading the model will delete all measurements.
|
||||
</span>
|
||||
@@ -21,9 +21,9 @@
|
||||
Delete Selected
|
||||
</FormButton>
|
||||
</template>
|
||||
<div class="p-3 flex flex-col gap-3 border-b border-outline-3">
|
||||
<div class="px-3 py-2 sm:p-3 flex flex-col gap-3 border-b border-outline-3">
|
||||
<div>
|
||||
<h6 class="font-semibold text-sm mb-2">Measurement Type</h6>
|
||||
<h6 class="font-semibold text-xs sm:text-sm mb-2">Measurement Type</h6>
|
||||
<FormRadio
|
||||
v-for="option in measurementTypeOptions"
|
||||
:key="option.value"
|
||||
@@ -36,26 +36,28 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-3 flex items-center border-b border-outline-3">
|
||||
<div class="py-2 px-3 sm:p-3 flex items-center border-b border-outline-3">
|
||||
<FormCheckbox
|
||||
name="Snap to Objects"
|
||||
hide-label
|
||||
:model-value="measurementParams.vertexSnap"
|
||||
@update:model-value="() => toggleMeasurementsSnap()"
|
||||
/>
|
||||
<span class="font-normal text-sm">Snap to Vertices</span>
|
||||
<span class="font-normal text-xs sm:text-sm">Snap to Vertices</span>
|
||||
</div>
|
||||
<div class="p-3 flex flex-col gap-3">
|
||||
<div class="flex flex-col gap-2">
|
||||
<h6 class="font-semibold text-sm">Units</h6>
|
||||
<h6 class="font-semibold text-xs sm:text-sm">Units</h6>
|
||||
<ViewerMeasurementsUnitSelect
|
||||
v-model="selectedUnit"
|
||||
mount-menu-on-body
|
||||
@update:model-value="onChangeMeasurementUnits"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col gap-3">
|
||||
<label class="font-semibold text-sm" for="precision">Precision</label>
|
||||
<div class="flex flex-col gap-2 sm:gap-3">
|
||||
<label class="font-semibold text-xs sm:text-sm" for="precision">
|
||||
Precision
|
||||
</label>
|
||||
<div class="flex gap-2 items-center">
|
||||
<input
|
||||
id="precision"
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
{{ showRemove ? 'Done' : 'Remove' }}
|
||||
</FormButton>
|
||||
</template>
|
||||
<div class="flex flex-col space-y-2 px-1 py-2">
|
||||
<div class="flex flex-col space-y-2 px-1 py-3">
|
||||
<template v-if="resourceItems.length">
|
||||
<div
|
||||
v-for="({ model, versionId }, index) in modelsAndVersionIds"
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<div
|
||||
:class="`${
|
||||
showVersions ? 'bg-primary' : 'bg-foundation hover:bg-primary-muted'
|
||||
} group sticky cursor-pointer top-0 z-20 flex h-20 min-w-0 max-w-full items-center justify-between space-x-2 p-2 transition select-none`"
|
||||
} group sticky cursor-pointer top-0 z-20 flex h-10 sm:h-20 min-w-0 max-w-full items-center justify-between space-x-2 p-2 transition select-none`"
|
||||
@click="showVersions = !showVersions"
|
||||
>
|
||||
<div>
|
||||
@@ -23,7 +23,7 @@
|
||||
v-tippy="modelName.subheader ? model.name : null"
|
||||
:class="`${
|
||||
showVersions ? 'text-foundation' : ''
|
||||
} font-bold truncate min-w-0`"
|
||||
} text-sm sm:text-base font-bold truncate min-w-0`"
|
||||
>
|
||||
{{ modelName.header }}
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<LayoutDialog v-model:open="open" max-width="lg">
|
||||
<template #header>Add Model</template>
|
||||
<div class="flex flex-col space-y-4">
|
||||
<div class="flex flex-col gap-y-4">
|
||||
<LayoutTabs v-slot="{ activeItem }" :items="tabItems">
|
||||
<ViewerResourcesAddModelDialogModelTab
|
||||
v-if="activeItem.id === 'model'"
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<template>
|
||||
<div class="flex flex-col space-y-2">
|
||||
<div class="flex flex-col gap-y-2">
|
||||
<div class="flex justify-end">
|
||||
<FormTextInput
|
||||
v-model="search"
|
||||
name="modelsearch"
|
||||
:show-label="false"
|
||||
:size="isSmallerOrEqualSm ? 'sm' : 'base'"
|
||||
placeholder="Search"
|
||||
color="foundation"
|
||||
class="w-60"
|
||||
class="w-48 sm:w-60"
|
||||
:show-clear="search !== ''"
|
||||
auto-focus
|
||||
@change="updateSearchImmediately"
|
||||
@@ -21,6 +22,7 @@
|
||||
:project="project"
|
||||
:project-id="project.id"
|
||||
:excluded-ids="alreadyLoadedModelIds"
|
||||
:small-view="true"
|
||||
:show-actions="false"
|
||||
:show-versions="false"
|
||||
disable-default-links
|
||||
@@ -34,12 +36,14 @@
|
||||
<script setup lang="ts">
|
||||
import { debounce } from 'lodash-es'
|
||||
import { useInjectedViewerLoadedResources } from '~~/lib/viewer/composables/setup'
|
||||
import { useIsSmallerOrEqualThanBreakpoint } from '~~/composables/browser'
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'chosen', val: { modelId: string }): void
|
||||
}>()
|
||||
|
||||
const { project, resourceItems } = useInjectedViewerLoadedResources()
|
||||
const { isSmallerOrEqualSm } = useIsSmallerOrEqualThanBreakpoint()
|
||||
|
||||
const search = ref('')
|
||||
const debouncedSearch = ref('')
|
||||
|
||||
@@ -1,23 +1,29 @@
|
||||
<template>
|
||||
<div class="flex flex-col space-y-4">
|
||||
<div class="text-foreground normal">
|
||||
<div class="flex flex-col gap-y-4">
|
||||
<div class="text-foreground normal text-sm sm:text-base">
|
||||
Add objects from the current project by their IDs or an Object URL.
|
||||
</div>
|
||||
<form
|
||||
class="flex flex-col space-y-4 sm:space-y-0 sm:flex-row sm:space-x-4 w-full"
|
||||
class="flex flex-col gap-y-4 sm:space-y-0 sm:flex-row sm:space-x-4 w-full"
|
||||
@submit="onSubmit"
|
||||
>
|
||||
<FormTextInput
|
||||
name="objectIdsOrUrl"
|
||||
label="Value"
|
||||
full-width
|
||||
size="lg"
|
||||
:size="isSmallerOrEqualSm ? 'base' : 'lg'"
|
||||
:custom-icon="LinkIcon"
|
||||
:rules="[isRequired, isValidValue]"
|
||||
placeholder="Comma-delimited object IDs/URLs"
|
||||
auto-focus
|
||||
/>
|
||||
<FormButton :icon-left="PlusIcon" size="lg" submit>Add</FormButton>
|
||||
<FormButton
|
||||
:icon-left="PlusIcon"
|
||||
:size="isSmallerOrEqualSm ? 'base' : 'lg'"
|
||||
submit
|
||||
>
|
||||
Add
|
||||
</FormButton>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
@@ -29,6 +35,7 @@ import { isRequired } from '~~/lib/common/helpers/validation'
|
||||
import { isObjectId } from '~~/lib/common/helpers/resources'
|
||||
import { useInjectedViewerLoadedResources } from '~~/lib/viewer/composables/setup'
|
||||
import { difference } from 'lodash-es'
|
||||
import { useIsSmallerOrEqualThanBreakpoint } from '~~/composables/browser'
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'chosen', val: { objectIds: string[] }): void
|
||||
@@ -39,6 +46,7 @@ const urlRegexp = /\/models\/([a-zA-Z0-_9,@$]+)$/i
|
||||
|
||||
const { handleSubmit } = useForm<FormPayload>()
|
||||
const { resourceItems } = useInjectedViewerLoadedResources()
|
||||
const { isSmallerOrEqualSm } = useIsSmallerOrEqualThanBreakpoint()
|
||||
|
||||
const explodeValidatedObjectIds = (commaDelimitedIdList: string) => {
|
||||
const idParts = commaDelimitedIdList.split(',')
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<template>
|
||||
<ViewerCommentsPortalOrDiv v-if="objects.length !== 0" to="bottomPanel">
|
||||
<div
|
||||
:class="`sm:bg-foundation simple-scrollbar z-10 relative sm:fixed sm:top-16 sm:right-4 sm:top-[4rem] sm:right-4 sm:mb-4 sm:max-w-64 min-h-[4.75rem] max-h-[50vh] sm:max-h-[calc(100dvh-5.5rem)] w-full sm:w-64 overflow-y-auto sm:rounded-md sm:shadow transition ${
|
||||
:class="`sm:bg-foundation simple-scrollbar z-10 relative sm:fixed sm:right-4 sm:right-4 sm:mb-4 sm:max-w-64 min-h-[3rem] sm:min-h-[4.75rem] max-h-[50vh] sm:max-h-[calc(100vh-5.5rem)] w-full sm:w-64 overflow-y-auto sm:rounded-md sm:shadow transition ${
|
||||
objects.length !== 0
|
||||
? 'translate-x-0 opacity-100'
|
||||
: 'translate-x-[120%] opacity-0'
|
||||
} ${isEmbedEnabled ? 'sm:top-2' : 'sm:top-[4rem]'} ${
|
||||
focusedThreadId && isSmallerOrEqualSm ? 'hidden' : ''
|
||||
}`"
|
||||
>
|
||||
<ViewerLayoutPanel @close="trackAndClearSelection()">
|
||||
@@ -73,16 +75,22 @@ import { containsAll } from '~~/lib/common/helpers/utils'
|
||||
import { useFilterUtilities, useSelectionUtilities } from '~~/lib/viewer/composables/ui'
|
||||
import { uniqWith } from 'lodash-es'
|
||||
import { useMixpanel } from '~~/lib/core/composables/mp'
|
||||
import { useIsSmallerOrEqualThanBreakpoint } from '~~/composables/browser'
|
||||
import { useEmbed } from '~/lib/viewer/composables/setup/embed'
|
||||
|
||||
const {
|
||||
viewer: {
|
||||
metadata: { filteringState }
|
||||
},
|
||||
urlHashState: { focusedThreadId },
|
||||
ui: { diff }
|
||||
} = useInjectedViewerState()
|
||||
const { objects, clearSelection } = useSelectionUtilities()
|
||||
const { hideObjects, showObjects, isolateObjects, unIsolateObjects } =
|
||||
useFilterUtilities()
|
||||
const { isEmbedEnabled } = useEmbed()
|
||||
|
||||
const { isSmallerOrEqualSm } = useIsSmallerOrEqualThanBreakpoint()
|
||||
|
||||
const itemCount = ref(42)
|
||||
|
||||
|
||||
@@ -14,59 +14,67 @@
|
||||
leave-to-class="opacity-0"
|
||||
>
|
||||
<PopoverPanel
|
||||
class="absolute translate-x-0 left-12 top-2 p-2 bg-foundation max-h-64 simple-scrollbar overflow-y-auto outline outline-2 outline-primary-muted rounded-lg shadow-lg overflow-hidden flex flex-col space-y-2"
|
||||
class="absolute translate-x-0 left-10 sm:left-12 top-2 p-2 bg-foundation max-h-64 simple-scrollbar overflow-y-auto outline outline-2 outline-primary-muted rounded-lg shadow-lg overflow-hidden flex flex-col space-y-2"
|
||||
>
|
||||
<div class="flex items-center space-x-1">
|
||||
<input
|
||||
id="intensity"
|
||||
v-model="intensity"
|
||||
class="h-2 mr-2"
|
||||
class="w-24 sm:w-32 h-2 mr-2"
|
||||
type="range"
|
||||
name="intensity"
|
||||
min="1"
|
||||
max="10"
|
||||
step="0.05"
|
||||
/>
|
||||
<label class="text-sm text-foreground-2" for="intensity">Intensity</label>
|
||||
<label class="text-xs sm:text-sm text-foreground-2" for="intensity">
|
||||
Intensity
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex items-center space-x-1">
|
||||
<input
|
||||
id="elevation"
|
||||
v-model="elevation"
|
||||
class="h-2 mr-2"
|
||||
class="w-24 sm:w-32 h-2 mr-2"
|
||||
type="range"
|
||||
name="elevation"
|
||||
min="0"
|
||||
:max="Math.PI"
|
||||
step="0.05"
|
||||
/>
|
||||
<label class="text-sm text-foreground-2" for="elevation">Elevation</label>
|
||||
<label class="text-xs sm:text-sm text-foreground-2" for="elevation">
|
||||
Elevation
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex items-center space-x-1">
|
||||
<input
|
||||
id="azimuth"
|
||||
v-model="azimuth"
|
||||
class="h-2 mr-2"
|
||||
class="w-24 sm:w-32 h-2 mr-2"
|
||||
type="range"
|
||||
name="azimuth"
|
||||
:min="-Math.PI * 0.5"
|
||||
:max="Math.PI * 0.5"
|
||||
step="0.05"
|
||||
/>
|
||||
<label class="text-sm text-foreground-2" for="azimuth">Azimuth</label>
|
||||
<label class="text-xs sm:text-sm text-foreground-2" for="azimuth">
|
||||
Azimuth
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex items-center space-x-1">
|
||||
<input
|
||||
id="indirect"
|
||||
v-model="indirectLightIntensity"
|
||||
class="h-2 mr-2"
|
||||
class="w-24 sm:w-32 h-2 mr-2"
|
||||
type="range"
|
||||
name="indirect"
|
||||
min="0"
|
||||
max="5"
|
||||
step="0.05"
|
||||
/>
|
||||
<label class="text-sm text-foreground-2" for="indirect">Indirect</label>
|
||||
<label class="text-xs sm:text-sm text-foreground-2" for="indirect">
|
||||
Indirect
|
||||
</label>
|
||||
</div>
|
||||
</PopoverPanel>
|
||||
</Transition>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
leave-to-class="opacity-0"
|
||||
>
|
||||
<MenuItems
|
||||
class="absolute translate-x-0 w-32 left-12 top-2 bg-foundation max-h-64 simple-scrollbar overflow-y-auto outline outline-2 outline-primary-muted rounded-lg shadow-lg overflow-hidden flex flex-col"
|
||||
class="absolute translate-x-0 w-24 sm:w-32 left-10 sm:left-12 -top-1 sm:top-2 bg-foundation max-h-64 simple-scrollbar overflow-y-auto outline outline-2 outline-primary-muted rounded-lg shadow-lg overflow-hidden flex flex-col"
|
||||
>
|
||||
<!-- Canonical views first -->
|
||||
<MenuItem
|
||||
@@ -27,7 +27,7 @@
|
||||
:class="{
|
||||
'bg-primary text-foreground-on-primary': active,
|
||||
'text-foreground': !active,
|
||||
'text-sm py-2 transition': true
|
||||
'text-xs sm:text-sm py-2 transition': true
|
||||
}"
|
||||
@click="setView(view.name.toLowerCase() as CanonicalView)"
|
||||
>
|
||||
|
||||
@@ -43,10 +43,10 @@ export const useClipboard = () => {
|
||||
export const useIsSmallerOrEqualThanBreakpoint = () => {
|
||||
const breakpoints = useBreakpoints(TailwindBreakpoints)
|
||||
return {
|
||||
isSmallerOrEqualSm: computed(() => breakpoints.smallerOrEqual('sm').value),
|
||||
isSmallerOrEqualMd: computed(() => breakpoints.smallerOrEqual('md').value),
|
||||
isSmallerOrEqualLg: computed(() => breakpoints.smallerOrEqual('lg').value),
|
||||
isSmallerOrEqualXl: computed(() => breakpoints.smallerOrEqual('xl').value),
|
||||
isSmallerOrEqual2xl: computed(() => breakpoints.smallerOrEqual('2xl').value)
|
||||
isSmallerOrEqualSm: breakpoints.smallerOrEqual('sm'),
|
||||
isSmallerOrEqualMd: breakpoints.smallerOrEqual('md'),
|
||||
isSmallerOrEqualLg: breakpoints.smallerOrEqual('lg'),
|
||||
isSmallerOrEqualXl: breakpoints.smallerOrEqual('xl'),
|
||||
isSmallerOrEqual2xl: breakpoints.smallerOrEqual('2xl')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
export const useTextInputGlobalFocus = () =>
|
||||
useState<boolean>('text-input-focus', () => false)
|
||||
|
||||
export const useTourStageState = () =>
|
||||
useState('global-ui-element-state', () => ({
|
||||
showNavbar: true,
|
||||
showViewerControls: true,
|
||||
showTour: false,
|
||||
showSegmentation: true
|
||||
}))
|
||||
@@ -1,45 +1,48 @@
|
||||
<template>
|
||||
<div :class="`relative min-h-full`">
|
||||
<div class="relative min-h-full">
|
||||
<div
|
||||
v-if="debug"
|
||||
class="pointer-events-none fixed bottom-0 z-40 flex w-full space-x-2 p-3 text-xs"
|
||||
>
|
||||
<FormButton
|
||||
class="pointer-events-auto"
|
||||
size="xs"
|
||||
@click="tourState.showNavbar = !tourState.showNavbar"
|
||||
>
|
||||
<FormButton class="pointer-events-auto" size="xs" @click="toggleNavbar">
|
||||
nav
|
||||
</FormButton>
|
||||
<FormButton
|
||||
class="pointer-events-auto"
|
||||
size="xs"
|
||||
@click="tourState.showViewerControls = !tourState.showViewerControls"
|
||||
>
|
||||
<FormButton class="pointer-events-auto" size="xs" @click="toggleViewerControls">
|
||||
viewer ctrls
|
||||
</FormButton>
|
||||
<FormButton
|
||||
class="pointer-events-auto"
|
||||
size="xs"
|
||||
@click="tourState.showTour = !tourState.showTour"
|
||||
>
|
||||
<FormButton class="pointer-events-auto" size="xs" @click="toggleTour">
|
||||
tour ctrls
|
||||
</FormButton>
|
||||
<!-- <span>{{ tourState }}</span> -->
|
||||
</div>
|
||||
|
||||
<Transition
|
||||
enter-from-class="opacity-0"
|
||||
enter-active-class="transition duration-1000"
|
||||
>
|
||||
<HeaderNavBar v-show="tourState.showNavbar" class="relative z-20 mb-6" />
|
||||
</Transition>
|
||||
<ClientOnly>
|
||||
<Transition
|
||||
enter-from-class="opacity-0"
|
||||
enter-active-class="transition duration-1000"
|
||||
>
|
||||
<HeaderNavBar v-show="showNavbar" class="relative z-20 mb-6" />
|
||||
</Transition>
|
||||
</ClientOnly>
|
||||
<main class="absolute top-0 left-0 z-10 h-[100dvh] w-screen">
|
||||
<slot />
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
const tourState = useTourStageState()
|
||||
import { useViewerTour } from '~/lib/viewer/composables/tour'
|
||||
|
||||
const { showNavbar, showTour, showControls } = useViewerTour()
|
||||
|
||||
const debug = ref(false)
|
||||
|
||||
const toggleNavbar = () => {
|
||||
showNavbar.value = !showNavbar.value
|
||||
}
|
||||
const toggleTour = () => {
|
||||
showTour.value = !showTour.value
|
||||
}
|
||||
const toggleViewerControls = () => {
|
||||
showControls.value = !showControls.value
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
import type { CookieOptions } from '#app'
|
||||
import dayjs from 'dayjs'
|
||||
import { useScopedState } from '~~/lib/common/composables/scopedState'
|
||||
|
||||
/**
|
||||
* Makes useCookie() synchronized across the app so that a change to it from one place
|
||||
* will also update other references elsewhere
|
||||
* will also update other references elsewhere.
|
||||
*
|
||||
* Defaults to an expiration date of 1 year
|
||||
*/
|
||||
export const useSynchronizedCookie = <CookieValue = string>(
|
||||
name: string,
|
||||
opts?: CookieOptions<CookieValue>
|
||||
) =>
|
||||
useScopedState(`synchronizedCookiesState-${name}`, () =>
|
||||
useCookie<CookieValue>(name, opts)
|
||||
useCookie<CookieValue>(name, {
|
||||
expires: dayjs().add(1, 'year').toDate(),
|
||||
...(opts || {})
|
||||
})
|
||||
)
|
||||
|
||||
@@ -25,7 +25,7 @@ const documents = {
|
||||
"\n fragment ProjectDiscussionsPageHeader_Project on Project {\n id\n name\n }\n": types.ProjectDiscussionsPageHeader_ProjectFragmentDoc,
|
||||
"\n fragment ProjectDiscussionsPageResults_Project on Project {\n id\n }\n": types.ProjectDiscussionsPageResults_ProjectFragmentDoc,
|
||||
"\n fragment ProjectModelPageHeaderProject on Project {\n id\n name\n role\n model(id: $modelId) {\n id\n name\n description\n }\n }\n": types.ProjectModelPageHeaderProjectFragmentDoc,
|
||||
"\n fragment ProjectModelPageVersionsPagination on Project {\n id\n model(id: $modelId) {\n id\n versions(limit: 16, cursor: $versionsCursor) {\n cursor\n totalCount\n items {\n ...ProjectModelPageVersionsCardVersion\n }\n }\n }\n }\n": types.ProjectModelPageVersionsPaginationFragmentDoc,
|
||||
"\n fragment ProjectModelPageVersionsPagination on Project {\n id\n visibility\n model(id: $modelId) {\n id\n versions(limit: 16, cursor: $versionsCursor) {\n cursor\n totalCount\n items {\n ...ProjectModelPageVersionsCardVersion\n }\n }\n }\n }\n": types.ProjectModelPageVersionsPaginationFragmentDoc,
|
||||
"\n fragment ProjectModelPageVersionsProject on Project {\n ...ProjectPageProjectHeader\n model(id: $modelId) {\n id\n name\n pendingImportedVersions {\n ...PendingFileUpload\n }\n }\n ...ProjectModelPageVersionsPagination\n }\n": types.ProjectModelPageVersionsProjectFragmentDoc,
|
||||
"\n fragment ProjectModelPageDialogDeleteVersion on Version {\n id\n message\n }\n": types.ProjectModelPageDialogDeleteVersionFragmentDoc,
|
||||
"\n fragment ProjectModelPageDialogEditMessageVersion on Version {\n id\n message\n }\n": types.ProjectModelPageDialogEditMessageVersionFragmentDoc,
|
||||
@@ -36,9 +36,9 @@ const documents = {
|
||||
"\n fragment ProjectPageProjectHeader on Project {\n id\n role\n name\n description\n visibility\n allowPublicComments\n }\n": types.ProjectPageProjectHeaderFragmentDoc,
|
||||
"\n fragment ProjectPageLatestItemsComments on Project {\n id\n commentThreadCount: commentThreads(limit: 0) {\n totalCount\n }\n }\n": types.ProjectPageLatestItemsCommentsFragmentDoc,
|
||||
"\n fragment ProjectPageLatestItemsCommentItem on Comment {\n id\n author {\n ...FormUsersSelectItem\n }\n screenshot\n rawText\n createdAt\n updatedAt\n archived\n repliesCount: replies(limit: 0) {\n totalCount\n }\n replyAuthors(limit: 4) {\n totalCount\n items {\n ...FormUsersSelectItem\n }\n }\n }\n": types.ProjectPageLatestItemsCommentItemFragmentDoc,
|
||||
"\n fragment ProjectPageLatestItemsModels on Project {\n id\n role\n modelCount: models(limit: 0) {\n totalCount\n }\n }\n": types.ProjectPageLatestItemsModelsFragmentDoc,
|
||||
"\n fragment ProjectPageLatestItemsModels on Project {\n id\n role\n visibility\n modelCount: models(limit: 0) {\n totalCount\n }\n }\n": types.ProjectPageLatestItemsModelsFragmentDoc,
|
||||
"\n fragment ProjectPageModelsActions on Model {\n id\n name\n }\n": types.ProjectPageModelsActionsFragmentDoc,
|
||||
"\n fragment ProjectPageModelsCardProject on Project {\n id\n role\n }\n": types.ProjectPageModelsCardProjectFragmentDoc,
|
||||
"\n fragment ProjectPageModelsCardProject on Project {\n id\n role\n visibility\n }\n": types.ProjectPageModelsCardProjectFragmentDoc,
|
||||
"\n fragment ModelPreview on Model {\n previewUrl\n }\n": types.ModelPreviewFragmentDoc,
|
||||
"\n fragment SingleLevelModelTreeItem on ModelsTreeItem {\n id\n name\n fullName\n model {\n ...ProjectPageLatestItemsModelItem\n }\n hasChildren\n updatedAt\n }\n": types.SingleLevelModelTreeItemFragmentDoc,
|
||||
"\n fragment ModelCardAutomationStatus_AutomationsStatus on AutomationsStatus {\n id\n status\n statusMessage\n automationRuns {\n id\n automationId\n automationName\n createdAt\n status\n functionRuns {\n id\n functionId\n functionName\n functionLogo\n elapsed\n status\n statusMessage\n contextView\n results\n resultVersions {\n id\n model {\n id\n name\n }\n }\n }\n }\n }\n": types.ModelCardAutomationStatus_AutomationsStatusFragmentDoc,
|
||||
@@ -59,6 +59,7 @@ const documents = {
|
||||
"\n fragment UserProfileEditDialogBio_User on User {\n id\n name\n company\n bio\n ...UserProfileEditDialogAvatar_User\n }\n": types.UserProfileEditDialogBio_UserFragmentDoc,
|
||||
"\n fragment UserProfileEditDialogDeleteAccount_User on User {\n id\n email\n }\n": types.UserProfileEditDialogDeleteAccount_UserFragmentDoc,
|
||||
"\n fragment UserProfileEditDialogNotificationPreferences_User on User {\n id\n notificationPreferences\n }\n": types.UserProfileEditDialogNotificationPreferences_UserFragmentDoc,
|
||||
"\n fragment ModelPageProject on Project {\n id\n createdAt\n name\n visibility\n }\n": types.ModelPageProjectFragmentDoc,
|
||||
"\n fragment ThreadCommentAttachment on Comment {\n text {\n attachments {\n id\n fileName\n fileType\n fileSize\n }\n }\n }\n": types.ThreadCommentAttachmentFragmentDoc,
|
||||
"\n fragment ViewerCommentsListItem on Comment {\n id\n rawText\n archived\n author {\n ...LimitedUserAvatar\n }\n createdAt\n viewedAt\n replies {\n totalCount\n cursor\n items {\n ...ViewerCommentsReplyItem\n }\n }\n replyAuthors(limit: 4) {\n totalCount\n items {\n ...FormUsersSelectItem\n }\n }\n resources {\n resourceId\n resourceType\n }\n }\n": types.ViewerCommentsListItemFragmentDoc,
|
||||
"\n fragment ViewerModelVersionCardItem on Version {\n id\n message\n referencedObject\n sourceApplication\n createdAt\n previewUrl\n authorUser {\n ...LimitedUserAvatar\n }\n }\n": types.ViewerModelVersionCardItemFragmentDoc,
|
||||
@@ -167,7 +168,6 @@ const documents = {
|
||||
"\n query LegacyViewerCommitRedirectMetadata($streamId: String!, $commitId: String!) {\n stream(id: $streamId) {\n commit(id: $commitId) {\n id\n branch {\n id\n }\n }\n }\n }\n": types.LegacyViewerCommitRedirectMetadataDocument,
|
||||
"\n query ResolveCommentLink($commentId: String!, $projectId: String!) {\n comment(id: $commentId, streamId: $projectId) {\n ...LinkableComment\n }\n }\n": types.ResolveCommentLinkDocument,
|
||||
"\n fragment ProjectPageProject on Project {\n id\n createdAt\n ...ProjectPageProjectHeader\n ...ProjectPageStatsBlockTeam\n ...ProjectPageTeamDialog\n ...ProjectPageStatsBlockVersions\n ...ProjectPageStatsBlockModels\n ...ProjectPageStatsBlockComments\n ...ProjectPageLatestItemsModels\n ...ProjectPageLatestItemsComments\n }\n": types.ProjectPageProjectFragmentDoc,
|
||||
"\n fragment ModelPageProject on Project {\n id\n createdAt\n name\n }\n": types.ModelPageProjectFragmentDoc,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -235,7 +235,7 @@ export function graphql(source: "\n fragment ProjectModelPageHeaderProject on P
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n fragment ProjectModelPageVersionsPagination on Project {\n id\n model(id: $modelId) {\n id\n versions(limit: 16, cursor: $versionsCursor) {\n cursor\n totalCount\n items {\n ...ProjectModelPageVersionsCardVersion\n }\n }\n }\n }\n"): (typeof documents)["\n fragment ProjectModelPageVersionsPagination on Project {\n id\n model(id: $modelId) {\n id\n versions(limit: 16, cursor: $versionsCursor) {\n cursor\n totalCount\n items {\n ...ProjectModelPageVersionsCardVersion\n }\n }\n }\n }\n"];
|
||||
export function graphql(source: "\n fragment ProjectModelPageVersionsPagination on Project {\n id\n visibility\n model(id: $modelId) {\n id\n versions(limit: 16, cursor: $versionsCursor) {\n cursor\n totalCount\n items {\n ...ProjectModelPageVersionsCardVersion\n }\n }\n }\n }\n"): (typeof documents)["\n fragment ProjectModelPageVersionsPagination on Project {\n id\n visibility\n model(id: $modelId) {\n id\n versions(limit: 16, cursor: $versionsCursor) {\n cursor\n totalCount\n items {\n ...ProjectModelPageVersionsCardVersion\n }\n }\n }\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
@@ -279,7 +279,7 @@ export function graphql(source: "\n fragment ProjectPageLatestItemsCommentItem
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n fragment ProjectPageLatestItemsModels on Project {\n id\n role\n modelCount: models(limit: 0) {\n totalCount\n }\n }\n"): (typeof documents)["\n fragment ProjectPageLatestItemsModels on Project {\n id\n role\n modelCount: models(limit: 0) {\n totalCount\n }\n }\n"];
|
||||
export function graphql(source: "\n fragment ProjectPageLatestItemsModels on Project {\n id\n role\n visibility\n modelCount: models(limit: 0) {\n totalCount\n }\n }\n"): (typeof documents)["\n fragment ProjectPageLatestItemsModels on Project {\n id\n role\n visibility\n modelCount: models(limit: 0) {\n totalCount\n }\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
@@ -287,7 +287,7 @@ export function graphql(source: "\n fragment ProjectPageModelsActions on Model
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n fragment ProjectPageModelsCardProject on Project {\n id\n role\n }\n"): (typeof documents)["\n fragment ProjectPageModelsCardProject on Project {\n id\n role\n }\n"];
|
||||
export function graphql(source: "\n fragment ProjectPageModelsCardProject on Project {\n id\n role\n visibility\n }\n"): (typeof documents)["\n fragment ProjectPageModelsCardProject on Project {\n id\n role\n visibility\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
@@ -368,6 +368,10 @@ export function graphql(source: "\n fragment UserProfileEditDialogDeleteAccount
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n fragment UserProfileEditDialogNotificationPreferences_User on User {\n id\n notificationPreferences\n }\n"): (typeof documents)["\n fragment UserProfileEditDialogNotificationPreferences_User on User {\n id\n notificationPreferences\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n fragment ModelPageProject on Project {\n id\n createdAt\n name\n visibility\n }\n"): (typeof documents)["\n fragment ModelPageProject on Project {\n id\n createdAt\n name\n visibility\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
@@ -800,10 +804,6 @@ export function graphql(source: "\n query ResolveCommentLink($commentId: String
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n fragment ProjectPageProject on Project {\n id\n createdAt\n ...ProjectPageProjectHeader\n ...ProjectPageStatsBlockTeam\n ...ProjectPageTeamDialog\n ...ProjectPageStatsBlockVersions\n ...ProjectPageStatsBlockModels\n ...ProjectPageStatsBlockComments\n ...ProjectPageLatestItemsModels\n ...ProjectPageLatestItemsComments\n }\n"): (typeof documents)["\n fragment ProjectPageProject on Project {\n id\n createdAt\n ...ProjectPageProjectHeader\n ...ProjectPageStatsBlockTeam\n ...ProjectPageTeamDialog\n ...ProjectPageStatsBlockVersions\n ...ProjectPageStatsBlockModels\n ...ProjectPageStatsBlockComments\n ...ProjectPageLatestItemsModels\n ...ProjectPageLatestItemsComments\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n fragment ModelPageProject on Project {\n id\n createdAt\n name\n }\n"): (typeof documents)["\n fragment ModelPageProject on Project {\n id\n createdAt\n name\n }\n"];
|
||||
|
||||
export function graphql(source: string) {
|
||||
return (documents as any)[source] ?? {};
|
||||
|
||||
@@ -2948,7 +2948,7 @@ export type ProjectDiscussionsPageResults_ProjectFragment = { __typename?: 'Proj
|
||||
|
||||
export type ProjectModelPageHeaderProjectFragment = { __typename?: 'Project', id: string, name: string, role?: string | null, model: { __typename?: 'Model', id: string, name: string, description?: string | null } };
|
||||
|
||||
export type ProjectModelPageVersionsPaginationFragment = { __typename?: 'Project', id: string, model: { __typename?: 'Model', id: string, versions: { __typename?: 'VersionCollection', cursor?: string | null, totalCount: number, items: Array<{ __typename?: 'Version', id: string, message?: string | null, createdAt: string, previewUrl: string, sourceApplication?: string | null, authorUser?: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } | null, commentThreadCount: { __typename?: 'CommentCollection', totalCount: number }, automationStatus?: { __typename?: 'AutomationsStatus', id: string, status: AutomationRunStatus, statusMessage?: string | null, automationRuns: Array<{ __typename?: 'AutomationRun', id: string, automationId: string, automationName: string, createdAt: string, status: AutomationRunStatus, functionRuns: Array<{ __typename?: 'AutomationFunctionRun', id: string, functionId: string, functionName: string, functionLogo?: string | null, elapsed: number, status: AutomationRunStatus, statusMessage?: string | null, contextView?: string | null, results?: {} | null, resultVersions: Array<{ __typename?: 'Version', id: string, model: { __typename?: 'Model', id: string, name: string } }> }> }> } | null }> } } };
|
||||
export type ProjectModelPageVersionsPaginationFragment = { __typename?: 'Project', id: string, visibility: ProjectVisibility, model: { __typename?: 'Model', id: string, versions: { __typename?: 'VersionCollection', cursor?: string | null, totalCount: number, items: Array<{ __typename?: 'Version', id: string, message?: string | null, createdAt: string, previewUrl: string, sourceApplication?: string | null, authorUser?: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } | null, commentThreadCount: { __typename?: 'CommentCollection', totalCount: number }, automationStatus?: { __typename?: 'AutomationsStatus', id: string, status: AutomationRunStatus, statusMessage?: string | null, automationRuns: Array<{ __typename?: 'AutomationRun', id: string, automationId: string, automationName: string, createdAt: string, status: AutomationRunStatus, functionRuns: Array<{ __typename?: 'AutomationFunctionRun', id: string, functionId: string, functionName: string, functionLogo?: string | null, elapsed: number, status: AutomationRunStatus, statusMessage?: string | null, contextView?: string | null, results?: {} | null, resultVersions: Array<{ __typename?: 'Version', id: string, model: { __typename?: 'Model', id: string, name: string } }> }> }> } | null }> } } };
|
||||
|
||||
export type ProjectModelPageVersionsProjectFragment = { __typename?: 'Project', id: string, role?: string | null, name: string, description?: string | null, visibility: ProjectVisibility, allowPublicComments: boolean, model: { __typename?: 'Model', id: string, name: string, pendingImportedVersions: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, versions: { __typename?: 'VersionCollection', cursor?: string | null, totalCount: number, items: Array<{ __typename?: 'Version', id: string, message?: string | null, createdAt: string, previewUrl: string, sourceApplication?: string | null, authorUser?: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } | null, commentThreadCount: { __typename?: 'CommentCollection', totalCount: number }, automationStatus?: { __typename?: 'AutomationsStatus', id: string, status: AutomationRunStatus, statusMessage?: string | null, automationRuns: Array<{ __typename?: 'AutomationRun', id: string, automationId: string, automationName: string, createdAt: string, status: AutomationRunStatus, functionRuns: Array<{ __typename?: 'AutomationFunctionRun', id: string, functionId: string, functionName: string, functionLogo?: string | null, elapsed: number, status: AutomationRunStatus, statusMessage?: string | null, contextView?: string | null, results?: {} | null, resultVersions: Array<{ __typename?: 'Version', id: string, model: { __typename?: 'Model', id: string, name: string } }> }> }> } | null }> } } };
|
||||
|
||||
@@ -2962,7 +2962,7 @@ export type ProjectModelPageVersionsCardVersionFragment = { __typename?: 'Versio
|
||||
|
||||
export type ProjectModelsPageHeader_ProjectFragment = { __typename?: 'Project', id: string, name: string, sourceApps: Array<string>, role?: string | null, team: Array<{ __typename?: 'ProjectCollaborator', user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> };
|
||||
|
||||
export type ProjectModelsPageResults_ProjectFragment = { __typename?: 'Project', id: string, role?: string | null, modelCount: { __typename?: 'ModelCollection', totalCount: number } };
|
||||
export type ProjectModelsPageResults_ProjectFragment = { __typename?: 'Project', id: string, role?: string | null, visibility: ProjectVisibility, modelCount: { __typename?: 'ModelCollection', totalCount: number } };
|
||||
|
||||
export type ProjectPageProjectHeaderFragment = { __typename?: 'Project', id: string, role?: string | null, name: string, description?: string | null, visibility: ProjectVisibility, allowPublicComments: boolean };
|
||||
|
||||
@@ -2970,11 +2970,11 @@ export type ProjectPageLatestItemsCommentsFragment = { __typename?: 'Project', i
|
||||
|
||||
export type ProjectPageLatestItemsCommentItemFragment = { __typename?: 'Comment', id: string, screenshot?: string | null, rawText: string, createdAt: string, updatedAt: string, archived: boolean, author: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null }, repliesCount: { __typename?: 'CommentCollection', totalCount: number }, replyAuthors: { __typename?: 'CommentReplyAuthorCollection', totalCount: number, items: Array<{ __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null }> } };
|
||||
|
||||
export type ProjectPageLatestItemsModelsFragment = { __typename?: 'Project', id: string, role?: string | null, modelCount: { __typename?: 'ModelCollection', totalCount: number } };
|
||||
export type ProjectPageLatestItemsModelsFragment = { __typename?: 'Project', id: string, role?: string | null, visibility: ProjectVisibility, modelCount: { __typename?: 'ModelCollection', totalCount: number } };
|
||||
|
||||
export type ProjectPageModelsActionsFragment = { __typename?: 'Model', id: string, name: string };
|
||||
|
||||
export type ProjectPageModelsCardProjectFragment = { __typename?: 'Project', id: string, role?: string | null };
|
||||
export type ProjectPageModelsCardProjectFragment = { __typename?: 'Project', id: string, role?: string | null, visibility: ProjectVisibility };
|
||||
|
||||
export type ModelPreviewFragment = { __typename?: 'Model', previewUrl?: string | null };
|
||||
|
||||
@@ -3003,9 +3003,9 @@ export type ProjectPageTeamDialogFragment = { __typename?: 'Project', id: string
|
||||
export type OnUserProjectsUpdateSubscriptionVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type OnUserProjectsUpdateSubscription = { __typename?: 'Subscription', userProjectsUpdated: { __typename?: 'UserProjectsUpdatedMessage', type: UserProjectsUpdatedMessageType, id: string, project?: { __typename?: 'Project', id: string, name: string, createdAt: string, updatedAt: string, role?: string | null, models: { __typename?: 'ModelCollection', totalCount: number, items: Array<{ __typename?: 'Model', id: string, name: string, displayName: string, previewUrl?: string | null, createdAt: string, updatedAt: string, description?: string | null, versionCount: { __typename?: 'VersionCollection', totalCount: number }, commentThreadCount: { __typename?: 'CommentCollection', totalCount: number }, pendingImportedVersions: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, automationStatus?: { __typename?: 'AutomationsStatus', id: string, status: AutomationRunStatus, statusMessage?: string | null, automationRuns: Array<{ __typename?: 'AutomationRun', id: string, automationId: string, automationName: string, createdAt: string, status: AutomationRunStatus, functionRuns: Array<{ __typename?: 'AutomationFunctionRun', id: string, functionId: string, functionName: string, functionLogo?: string | null, elapsed: number, status: AutomationRunStatus, statusMessage?: string | null, contextView?: string | null, results?: {} | null, resultVersions: Array<{ __typename?: 'Version', id: string, model: { __typename?: 'Model', id: string, name: string } }> }> }> } | null }> }, pendingImportedModels: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, team: Array<{ __typename?: 'ProjectCollaborator', user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> } | null } };
|
||||
export type OnUserProjectsUpdateSubscription = { __typename?: 'Subscription', userProjectsUpdated: { __typename?: 'UserProjectsUpdatedMessage', type: UserProjectsUpdatedMessageType, id: string, project?: { __typename?: 'Project', id: string, name: string, createdAt: string, updatedAt: string, role?: string | null, visibility: ProjectVisibility, models: { __typename?: 'ModelCollection', totalCount: number, items: Array<{ __typename?: 'Model', id: string, name: string, displayName: string, previewUrl?: string | null, createdAt: string, updatedAt: string, description?: string | null, versionCount: { __typename?: 'VersionCollection', totalCount: number }, commentThreadCount: { __typename?: 'CommentCollection', totalCount: number }, pendingImportedVersions: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, automationStatus?: { __typename?: 'AutomationsStatus', id: string, status: AutomationRunStatus, statusMessage?: string | null, automationRuns: Array<{ __typename?: 'AutomationRun', id: string, automationId: string, automationName: string, createdAt: string, status: AutomationRunStatus, functionRuns: Array<{ __typename?: 'AutomationFunctionRun', id: string, functionId: string, functionName: string, functionLogo?: string | null, elapsed: number, status: AutomationRunStatus, statusMessage?: string | null, contextView?: string | null, results?: {} | null, resultVersions: Array<{ __typename?: 'Version', id: string, model: { __typename?: 'Model', id: string, name: string } }> }> }> } | null }> }, pendingImportedModels: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, team: Array<{ __typename?: 'ProjectCollaborator', user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> } | null } };
|
||||
|
||||
export type ProjectsDashboardFilledFragment = { __typename?: 'ProjectCollection', items: Array<{ __typename?: 'Project', id: string, name: string, createdAt: string, updatedAt: string, role?: string | null, models: { __typename?: 'ModelCollection', totalCount: number, items: Array<{ __typename?: 'Model', id: string, name: string, displayName: string, previewUrl?: string | null, createdAt: string, updatedAt: string, description?: string | null, versionCount: { __typename?: 'VersionCollection', totalCount: number }, commentThreadCount: { __typename?: 'CommentCollection', totalCount: number }, pendingImportedVersions: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, automationStatus?: { __typename?: 'AutomationsStatus', id: string, status: AutomationRunStatus, statusMessage?: string | null, automationRuns: Array<{ __typename?: 'AutomationRun', id: string, automationId: string, automationName: string, createdAt: string, status: AutomationRunStatus, functionRuns: Array<{ __typename?: 'AutomationFunctionRun', id: string, functionId: string, functionName: string, functionLogo?: string | null, elapsed: number, status: AutomationRunStatus, statusMessage?: string | null, contextView?: string | null, results?: {} | null, resultVersions: Array<{ __typename?: 'Version', id: string, model: { __typename?: 'Model', id: string, name: string } }> }> }> } | null }> }, pendingImportedModels: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, team: Array<{ __typename?: 'ProjectCollaborator', user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> }> };
|
||||
export type ProjectsDashboardFilledFragment = { __typename?: 'ProjectCollection', items: Array<{ __typename?: 'Project', id: string, name: string, createdAt: string, updatedAt: string, role?: string | null, visibility: ProjectVisibility, models: { __typename?: 'ModelCollection', totalCount: number, items: Array<{ __typename?: 'Model', id: string, name: string, displayName: string, previewUrl?: string | null, createdAt: string, updatedAt: string, description?: string | null, versionCount: { __typename?: 'VersionCollection', totalCount: number }, commentThreadCount: { __typename?: 'CommentCollection', totalCount: number }, pendingImportedVersions: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, automationStatus?: { __typename?: 'AutomationsStatus', id: string, status: AutomationRunStatus, statusMessage?: string | null, automationRuns: Array<{ __typename?: 'AutomationRun', id: string, automationId: string, automationName: string, createdAt: string, status: AutomationRunStatus, functionRuns: Array<{ __typename?: 'AutomationFunctionRun', id: string, functionId: string, functionName: string, functionLogo?: string | null, elapsed: number, status: AutomationRunStatus, statusMessage?: string | null, contextView?: string | null, results?: {} | null, resultVersions: Array<{ __typename?: 'Version', id: string, model: { __typename?: 'Model', id: string, name: string } }> }> }> } | null }> }, pendingImportedModels: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, team: Array<{ __typename?: 'ProjectCollaborator', user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> }> };
|
||||
|
||||
export type ProjectsInviteBannerFragment = { __typename?: 'PendingStreamCollaborator', id: string, projectId: string, projectName: string, token?: string | null, invitedBy: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } };
|
||||
|
||||
@@ -3019,6 +3019,8 @@ export type UserProfileEditDialogDeleteAccount_UserFragment = { __typename?: 'Us
|
||||
|
||||
export type UserProfileEditDialogNotificationPreferences_UserFragment = { __typename?: 'User', id: string, notificationPreferences: {} };
|
||||
|
||||
export type ModelPageProjectFragment = { __typename?: 'Project', id: string, createdAt: string, name: string, visibility: ProjectVisibility };
|
||||
|
||||
export type ThreadCommentAttachmentFragment = { __typename?: 'Comment', text: { __typename?: 'SmartTextEditorValue', attachments?: Array<{ __typename?: 'BlobMetadata', id: string, fileName: string, fileType: string, fileSize?: number | null }> | null } };
|
||||
|
||||
export type ViewerCommentsListItemFragment = { __typename?: 'Comment', id: string, rawText: string, archived: boolean, createdAt: string, viewedAt?: string | null, author: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null }, replies: { __typename?: 'CommentCollection', totalCount: number, cursor?: string | null, items: Array<{ __typename?: 'Comment', id: string, archived: boolean, rawText: string, createdAt: string, text: { __typename?: 'SmartTextEditorValue', doc?: {} | null, attachments?: Array<{ __typename?: 'BlobMetadata', id: string, fileName: string, fileType: string, fileSize?: number | null }> | null }, author: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> }, replyAuthors: { __typename?: 'CommentReplyAuthorCollection', totalCount: number, items: Array<{ __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null }> }, resources: Array<{ __typename?: 'ResourceIdentifier', resourceId: string, resourceType: ResourceType }> };
|
||||
@@ -3160,9 +3162,9 @@ export type SearchProjectsQueryVariables = Exact<{
|
||||
|
||||
export type SearchProjectsQuery = { __typename?: 'Query', activeUser?: { __typename?: 'User', projects: { __typename?: 'ProjectCollection', totalCount: number, items: Array<{ __typename?: 'Project', id: string, name: string }> } } | null };
|
||||
|
||||
export type ProjectDashboardItemNoModelsFragment = { __typename?: 'Project', id: string, name: string, createdAt: string, updatedAt: string, role?: string | null, team: Array<{ __typename?: 'ProjectCollaborator', user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> };
|
||||
export type ProjectDashboardItemNoModelsFragment = { __typename?: 'Project', id: string, name: string, createdAt: string, updatedAt: string, role?: string | null, visibility: ProjectVisibility, team: Array<{ __typename?: 'ProjectCollaborator', user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> };
|
||||
|
||||
export type ProjectDashboardItemFragment = { __typename?: 'Project', id: string, name: string, createdAt: string, updatedAt: string, role?: string | null, models: { __typename?: 'ModelCollection', totalCount: number, items: Array<{ __typename?: 'Model', id: string, name: string, displayName: string, previewUrl?: string | null, createdAt: string, updatedAt: string, description?: string | null, versionCount: { __typename?: 'VersionCollection', totalCount: number }, commentThreadCount: { __typename?: 'CommentCollection', totalCount: number }, pendingImportedVersions: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, automationStatus?: { __typename?: 'AutomationsStatus', id: string, status: AutomationRunStatus, statusMessage?: string | null, automationRuns: Array<{ __typename?: 'AutomationRun', id: string, automationId: string, automationName: string, createdAt: string, status: AutomationRunStatus, functionRuns: Array<{ __typename?: 'AutomationFunctionRun', id: string, functionId: string, functionName: string, functionLogo?: string | null, elapsed: number, status: AutomationRunStatus, statusMessage?: string | null, contextView?: string | null, results?: {} | null, resultVersions: Array<{ __typename?: 'Version', id: string, model: { __typename?: 'Model', id: string, name: string } }> }> }> } | null }> }, pendingImportedModels: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, team: Array<{ __typename?: 'ProjectCollaborator', user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> };
|
||||
export type ProjectDashboardItemFragment = { __typename?: 'Project', id: string, name: string, createdAt: string, updatedAt: string, role?: string | null, visibility: ProjectVisibility, models: { __typename?: 'ModelCollection', totalCount: number, items: Array<{ __typename?: 'Model', id: string, name: string, displayName: string, previewUrl?: string | null, createdAt: string, updatedAt: string, description?: string | null, versionCount: { __typename?: 'VersionCollection', totalCount: number }, commentThreadCount: { __typename?: 'CommentCollection', totalCount: number }, pendingImportedVersions: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, automationStatus?: { __typename?: 'AutomationsStatus', id: string, status: AutomationRunStatus, statusMessage?: string | null, automationRuns: Array<{ __typename?: 'AutomationRun', id: string, automationId: string, automationName: string, createdAt: string, status: AutomationRunStatus, functionRuns: Array<{ __typename?: 'AutomationFunctionRun', id: string, functionId: string, functionName: string, functionLogo?: string | null, elapsed: number, status: AutomationRunStatus, statusMessage?: string | null, contextView?: string | null, results?: {} | null, resultVersions: Array<{ __typename?: 'Version', id: string, model: { __typename?: 'Model', id: string, name: string } }> }> }> } | null }> }, pendingImportedModels: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, team: Array<{ __typename?: 'ProjectCollaborator', user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> };
|
||||
|
||||
export type PendingFileUploadFragment = { __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string };
|
||||
|
||||
@@ -3304,7 +3306,7 @@ export type ProjectsDashboardQueryQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ProjectsDashboardQueryQuery = { __typename?: 'Query', activeUser?: { __typename?: 'User', id: string, projects: { __typename?: 'ProjectCollection', cursor?: string | null, totalCount: number, items: Array<{ __typename?: 'Project', id: string, name: string, createdAt: string, updatedAt: string, role?: string | null, models: { __typename?: 'ModelCollection', totalCount: number, items: Array<{ __typename?: 'Model', id: string, name: string, displayName: string, previewUrl?: string | null, createdAt: string, updatedAt: string, description?: string | null, versionCount: { __typename?: 'VersionCollection', totalCount: number }, commentThreadCount: { __typename?: 'CommentCollection', totalCount: number }, pendingImportedVersions: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, automationStatus?: { __typename?: 'AutomationsStatus', id: string, status: AutomationRunStatus, statusMessage?: string | null, automationRuns: Array<{ __typename?: 'AutomationRun', id: string, automationId: string, automationName: string, createdAt: string, status: AutomationRunStatus, functionRuns: Array<{ __typename?: 'AutomationFunctionRun', id: string, functionId: string, functionName: string, functionLogo?: string | null, elapsed: number, status: AutomationRunStatus, statusMessage?: string | null, contextView?: string | null, results?: {} | null, resultVersions: Array<{ __typename?: 'Version', id: string, model: { __typename?: 'Model', id: string, name: string } }> }> }> } | null }> }, pendingImportedModels: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, team: Array<{ __typename?: 'ProjectCollaborator', user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> }> }, projectInvites: Array<{ __typename?: 'PendingStreamCollaborator', id: string, projectId: string, projectName: string, token?: string | null, invitedBy: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> } | null };
|
||||
export type ProjectsDashboardQueryQuery = { __typename?: 'Query', activeUser?: { __typename?: 'User', id: string, projects: { __typename?: 'ProjectCollection', cursor?: string | null, totalCount: number, items: Array<{ __typename?: 'Project', id: string, name: string, createdAt: string, updatedAt: string, role?: string | null, visibility: ProjectVisibility, models: { __typename?: 'ModelCollection', totalCount: number, items: Array<{ __typename?: 'Model', id: string, name: string, displayName: string, previewUrl?: string | null, createdAt: string, updatedAt: string, description?: string | null, versionCount: { __typename?: 'VersionCollection', totalCount: number }, commentThreadCount: { __typename?: 'CommentCollection', totalCount: number }, pendingImportedVersions: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, automationStatus?: { __typename?: 'AutomationsStatus', id: string, status: AutomationRunStatus, statusMessage?: string | null, automationRuns: Array<{ __typename?: 'AutomationRun', id: string, automationId: string, automationName: string, createdAt: string, status: AutomationRunStatus, functionRuns: Array<{ __typename?: 'AutomationFunctionRun', id: string, functionId: string, functionName: string, functionLogo?: string | null, elapsed: number, status: AutomationRunStatus, statusMessage?: string | null, contextView?: string | null, results?: {} | null, resultVersions: Array<{ __typename?: 'Version', id: string, model: { __typename?: 'Model', id: string, name: string } }> }> }> } | null }> }, pendingImportedModels: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, team: Array<{ __typename?: 'ProjectCollaborator', user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> }> }, projectInvites: Array<{ __typename?: 'PendingStreamCollaborator', id: string, projectId: string, projectName: string, token?: string | null, invitedBy: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> } | null };
|
||||
|
||||
export type ProjectPageQueryQueryVariables = Exact<{
|
||||
id: Scalars['String'];
|
||||
@@ -3397,14 +3399,14 @@ export type ProjectModelVersionsQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ProjectModelVersionsQuery = { __typename?: 'Query', project: { __typename?: 'Project', id: string, model: { __typename?: 'Model', id: string, versions: { __typename?: 'VersionCollection', cursor?: string | null, totalCount: number, items: Array<{ __typename?: 'Version', id: string, message?: string | null, createdAt: string, previewUrl: string, sourceApplication?: string | null, authorUser?: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } | null, commentThreadCount: { __typename?: 'CommentCollection', totalCount: number }, automationStatus?: { __typename?: 'AutomationsStatus', id: string, status: AutomationRunStatus, statusMessage?: string | null, automationRuns: Array<{ __typename?: 'AutomationRun', id: string, automationId: string, automationName: string, createdAt: string, status: AutomationRunStatus, functionRuns: Array<{ __typename?: 'AutomationFunctionRun', id: string, functionId: string, functionName: string, functionLogo?: string | null, elapsed: number, status: AutomationRunStatus, statusMessage?: string | null, contextView?: string | null, results?: {} | null, resultVersions: Array<{ __typename?: 'Version', id: string, model: { __typename?: 'Model', id: string, name: string } }> }> }> } | null }> } } } };
|
||||
export type ProjectModelVersionsQuery = { __typename?: 'Query', project: { __typename?: 'Project', id: string, visibility: ProjectVisibility, model: { __typename?: 'Model', id: string, versions: { __typename?: 'VersionCollection', cursor?: string | null, totalCount: number, items: Array<{ __typename?: 'Version', id: string, message?: string | null, createdAt: string, previewUrl: string, sourceApplication?: string | null, authorUser?: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } | null, commentThreadCount: { __typename?: 'CommentCollection', totalCount: number }, automationStatus?: { __typename?: 'AutomationsStatus', id: string, status: AutomationRunStatus, statusMessage?: string | null, automationRuns: Array<{ __typename?: 'AutomationRun', id: string, automationId: string, automationName: string, createdAt: string, status: AutomationRunStatus, functionRuns: Array<{ __typename?: 'AutomationFunctionRun', id: string, functionId: string, functionName: string, functionLogo?: string | null, elapsed: number, status: AutomationRunStatus, statusMessage?: string | null, contextView?: string | null, results?: {} | null, resultVersions: Array<{ __typename?: 'Version', id: string, model: { __typename?: 'Model', id: string, name: string } }> }> }> } | null }> } } } };
|
||||
|
||||
export type ProjectModelsPageQueryVariables = Exact<{
|
||||
projectId: Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type ProjectModelsPageQuery = { __typename?: 'Query', project: { __typename?: 'Project', id: string, name: string, sourceApps: Array<string>, role?: string | null, team: Array<{ __typename?: 'ProjectCollaborator', user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }>, modelCount: { __typename?: 'ModelCollection', totalCount: number } } };
|
||||
export type ProjectModelsPageQuery = { __typename?: 'Query', project: { __typename?: 'Project', id: string, name: string, sourceApps: Array<string>, role?: string | null, visibility: ProjectVisibility, team: Array<{ __typename?: 'ProjectCollaborator', user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }>, modelCount: { __typename?: 'ModelCollection', totalCount: number } } };
|
||||
|
||||
export type ProjectDiscussionsPageQueryVariables = Exact<{
|
||||
projectId: Scalars['String'];
|
||||
@@ -3649,7 +3651,7 @@ export type ViewerLoadedResourcesQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ViewerLoadedResourcesQuery = { __typename?: 'Query', project: { __typename?: 'Project', id: string, role?: string | null, createdAt: string, name: string, models: { __typename?: 'ModelCollection', totalCount: number, items: Array<{ __typename?: 'Model', id: string, name: string, updatedAt: string, loadedVersion: { __typename?: 'VersionCollection', items: Array<{ __typename?: 'Version', id: string, message?: string | null, referencedObject: string, sourceApplication?: string | null, createdAt: string, previewUrl: string, automationStatus?: { __typename?: 'AutomationsStatus', id: string, status: AutomationRunStatus, statusMessage?: string | null, automationRuns: Array<{ __typename?: 'AutomationRun', id: string, automationId: string, automationName: string, versionId: string, createdAt: string, updatedAt: string, status: AutomationRunStatus, functionRuns: Array<{ __typename?: 'AutomationFunctionRun', id: string, functionId: string, functionName: string, functionLogo?: string | null, elapsed: number, status: AutomationRunStatus, contextView?: string | null, statusMessage?: string | null, results?: {} | null, resultVersions: Array<{ __typename?: 'Version', id: string, referencedObject: string, model: { __typename?: 'Model', id: string, name: string } }> }> }> } | null, authorUser?: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } | null }> }, versions: { __typename?: 'VersionCollection', totalCount: number, cursor?: string | null, items: Array<{ __typename?: 'Version', id: string, message?: string | null, referencedObject: string, sourceApplication?: string | null, createdAt: string, previewUrl: string, authorUser?: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } | null }> } }> }, modelCount: { __typename?: 'ModelCollection', totalCount: number } } };
|
||||
export type ViewerLoadedResourcesQuery = { __typename?: 'Query', project: { __typename?: 'Project', id: string, role?: string | null, visibility: ProjectVisibility, createdAt: string, name: string, models: { __typename?: 'ModelCollection', totalCount: number, items: Array<{ __typename?: 'Model', id: string, name: string, updatedAt: string, loadedVersion: { __typename?: 'VersionCollection', items: Array<{ __typename?: 'Version', id: string, message?: string | null, referencedObject: string, sourceApplication?: string | null, createdAt: string, previewUrl: string, automationStatus?: { __typename?: 'AutomationsStatus', id: string, status: AutomationRunStatus, statusMessage?: string | null, automationRuns: Array<{ __typename?: 'AutomationRun', id: string, automationId: string, automationName: string, versionId: string, createdAt: string, updatedAt: string, status: AutomationRunStatus, functionRuns: Array<{ __typename?: 'AutomationFunctionRun', id: string, functionId: string, functionName: string, functionLogo?: string | null, elapsed: number, status: AutomationRunStatus, contextView?: string | null, statusMessage?: string | null, results?: {} | null, resultVersions: Array<{ __typename?: 'Version', id: string, referencedObject: string, model: { __typename?: 'Model', id: string, name: string } }> }> }> } | null, authorUser?: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } | null }> }, versions: { __typename?: 'VersionCollection', totalCount: number, cursor?: string | null, items: Array<{ __typename?: 'Version', id: string, message?: string | null, referencedObject: string, sourceApplication?: string | null, createdAt: string, previewUrl: string, authorUser?: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } | null }> } }> }, modelCount: { __typename?: 'ModelCollection', totalCount: number } } };
|
||||
|
||||
export type ViewerModelVersionsQueryVariables = Exact<{
|
||||
projectId: Scalars['String'];
|
||||
@@ -3723,8 +3725,6 @@ export type ResolveCommentLinkQuery = { __typename?: 'Query', comment?: { __type
|
||||
|
||||
export type ProjectPageProjectFragment = { __typename?: 'Project', id: string, createdAt: string, role?: string | null, name: string, description?: string | null, visibility: ProjectVisibility, allowPublicComments: boolean, team: Array<{ __typename?: 'ProjectCollaborator', role: string, user: { __typename?: 'LimitedUser', role?: string | null, id: string, name: string, avatar?: string | null } }>, invitedTeam?: Array<{ __typename?: 'PendingStreamCollaborator', id: string, title: string, inviteId: string, role: string, user?: { __typename?: 'LimitedUser', role?: string | null, id: string, name: string, avatar?: string | null } | null }> | null, versionCount: { __typename?: 'VersionCollection', totalCount: number }, modelCount: { __typename?: 'ModelCollection', totalCount: number }, commentThreadCount: { __typename?: 'ProjectCommentCollection', totalCount: number } };
|
||||
|
||||
export type ModelPageProjectFragment = { __typename?: 'Project', id: string, createdAt: string, name: string };
|
||||
|
||||
export const AuthRegisterPanelServerInfoFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AuthRegisterPanelServerInfo"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerInfo"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"inviteOnly"}}]}}]} as unknown as DocumentNode<AuthRegisterPanelServerInfoFragment, unknown>;
|
||||
export const ServerTermsOfServicePrivacyPolicyFragmentFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerTermsOfServicePrivacyPolicyFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerInfo"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"termsOfService"}}]}}]} as unknown as DocumentNode<ServerTermsOfServicePrivacyPolicyFragmentFragment, unknown>;
|
||||
export const AuthStategiesServerInfoFragmentFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AuthStategiesServerInfoFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerInfo"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"authStrategies"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"url"}}]}}]}}]} as unknown as DocumentNode<AuthStategiesServerInfoFragmentFragment, unknown>;
|
||||
@@ -3741,12 +3741,12 @@ export const ProjectModelPageDialogMoveToVersionFragmentDoc = {"kind":"Document"
|
||||
export const ModelCardAutomationStatus_AutomationsStatusFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ModelCardAutomationStatus_AutomationsStatus"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"statusMessage"}},{"kind":"Field","name":{"kind":"Name","value":"automationRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automationId"}},{"kind":"Field","name":{"kind":"Name","value":"automationName"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"functionId"}},{"kind":"Field","name":{"kind":"Name","value":"functionName"}},{"kind":"Field","name":{"kind":"Name","value":"functionLogo"}},{"kind":"Field","name":{"kind":"Name","value":"elapsed"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"statusMessage"}},{"kind":"Field","name":{"kind":"Name","value":"contextView"}},{"kind":"Field","name":{"kind":"Name","value":"results"}},{"kind":"Field","name":{"kind":"Name","value":"resultVersions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"model"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode<ModelCardAutomationStatus_AutomationsStatusFragment, unknown>;
|
||||
export const ModelCardAutomationStatus_VersionFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ModelCardAutomationStatus_Version"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Version"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automationStatus"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ModelCardAutomationStatus_AutomationsStatus"}}]}}]}}]} as unknown as DocumentNode<ModelCardAutomationStatus_VersionFragment, unknown>;
|
||||
export const ProjectModelPageVersionsCardVersionFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectModelPageVersionsCardVersion"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Version"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"authorUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"previewUrl"}},{"kind":"Field","name":{"kind":"Name","value":"sourceApplication"}},{"kind":"Field","alias":{"kind":"Name","value":"commentThreadCount"},"name":{"kind":"Name","value":"commentThreads"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectModelPageDialogDeleteVersion"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectModelPageDialogMoveToVersion"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ModelCardAutomationStatus_Version"}}]}}]} as unknown as DocumentNode<ProjectModelPageVersionsCardVersionFragment, unknown>;
|
||||
export const ProjectModelPageVersionsPaginationFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectModelPageVersionsPagination"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"model"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"modelId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"versions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"16"}},{"kind":"Argument","name":{"kind":"Name","value":"cursor"},"value":{"kind":"Variable","name":{"kind":"Name","value":"versionsCursor"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectModelPageVersionsCardVersion"}}]}}]}}]}}]}}]} as unknown as DocumentNode<ProjectModelPageVersionsPaginationFragment, unknown>;
|
||||
export const ProjectModelPageVersionsPaginationFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectModelPageVersionsPagination"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"model"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"modelId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"versions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"16"}},{"kind":"Argument","name":{"kind":"Name","value":"cursor"},"value":{"kind":"Variable","name":{"kind":"Name","value":"versionsCursor"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectModelPageVersionsCardVersion"}}]}}]}}]}}]}}]} as unknown as DocumentNode<ProjectModelPageVersionsPaginationFragment, unknown>;
|
||||
export const ProjectModelPageVersionsProjectFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectModelPageVersionsProject"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageProjectHeader"}},{"kind":"Field","name":{"kind":"Name","value":"model"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"modelId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"pendingImportedVersions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PendingFileUpload"}}]}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectModelPageVersionsPagination"}}]}}]} as unknown as DocumentNode<ProjectModelPageVersionsProjectFragment, unknown>;
|
||||
export const ProjectModelPageDialogEditMessageVersionFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectModelPageDialogEditMessageVersion"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Version"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"message"}}]}}]} as unknown as DocumentNode<ProjectModelPageDialogEditMessageVersionFragment, unknown>;
|
||||
export const FormUsersSelectItemFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FormUsersSelectItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LimitedUser"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}}]} as unknown as DocumentNode<FormUsersSelectItemFragment, unknown>;
|
||||
export const ProjectModelsPageHeader_ProjectFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectModelsPageHeader_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sourceApps"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FormUsersSelectItem"}}]}}]}}]}}]} as unknown as DocumentNode<ProjectModelsPageHeader_ProjectFragment, unknown>;
|
||||
export const ProjectPageLatestItemsModelsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageLatestItemsModels"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","alias":{"kind":"Name","value":"modelCount"},"name":{"kind":"Name","value":"models"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}}]} as unknown as DocumentNode<ProjectPageLatestItemsModelsFragment, unknown>;
|
||||
export const ProjectPageLatestItemsModelsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageLatestItemsModels"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","alias":{"kind":"Name","value":"modelCount"},"name":{"kind":"Name","value":"models"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}}]} as unknown as DocumentNode<ProjectPageLatestItemsModelsFragment, unknown>;
|
||||
export const ProjectModelsPageResults_ProjectFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectModelsPageResults_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageLatestItemsModels"}}]}}]} as unknown as DocumentNode<ProjectModelsPageResults_ProjectFragment, unknown>;
|
||||
export const ProjectPageLatestItemsCommentItemFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageLatestItemsCommentItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Comment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"author"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FormUsersSelectItem"}}]}},{"kind":"Field","name":{"kind":"Name","value":"screenshot"}},{"kind":"Field","name":{"kind":"Name","value":"rawText"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"archived"}},{"kind":"Field","alias":{"kind":"Name","value":"repliesCount"},"name":{"kind":"Name","value":"replies"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"replyAuthors"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"4"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FormUsersSelectItem"}}]}}]}}]}}]} as unknown as DocumentNode<ProjectPageLatestItemsCommentItemFragment, unknown>;
|
||||
export const ModelPreviewFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ModelPreview"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"previewUrl"}}]}}]} as unknown as DocumentNode<ModelPreviewFragment, unknown>;
|
||||
@@ -3756,7 +3756,7 @@ export const ProjectPageModelsActionsFragmentDoc = {"kind":"Document","definitio
|
||||
export const ModelCardAutomationStatus_ModelFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ModelCardAutomationStatus_Model"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"automationStatus"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ModelCardAutomationStatus_AutomationsStatus"}}]}}]}}]} as unknown as DocumentNode<ModelCardAutomationStatus_ModelFragment, unknown>;
|
||||
export const ProjectPageLatestItemsModelItemFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageLatestItemsModelItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","alias":{"kind":"Name","value":"versionCount"},"name":{"kind":"Name","value":"versions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","alias":{"kind":"Name","value":"commentThreadCount"},"name":{"kind":"Name","value":"commentThreads"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pendingImportedVersions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PendingFileUpload"}}]}},{"kind":"Field","name":{"kind":"Name","value":"previewUrl"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsCardRenameDialog"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsCardDeleteDialog"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsActions"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ModelCardAutomationStatus_Model"}}]}}]} as unknown as DocumentNode<ProjectPageLatestItemsModelItemFragment, unknown>;
|
||||
export const SingleLevelModelTreeItemFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SingleLevelModelTreeItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ModelsTreeItem"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"model"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageLatestItemsModelItem"}}]}},{"kind":"Field","name":{"kind":"Name","value":"hasChildren"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode<SingleLevelModelTreeItemFragment, unknown>;
|
||||
export const ProjectPageModelsCardProjectFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardProject"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}}]} as unknown as DocumentNode<ProjectPageModelsCardProjectFragment, unknown>;
|
||||
export const ProjectPageModelsCardProjectFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardProject"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}}]}}]} as unknown as DocumentNode<ProjectPageModelsCardProjectFragment, unknown>;
|
||||
export const ProjectDashboardItemNoModelsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectDashboardItemNoModels"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsCardProject"}}]}}]} as unknown as DocumentNode<ProjectDashboardItemNoModelsFragment, unknown>;
|
||||
export const ProjectDashboardItemFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectDashboardItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectDashboardItemNoModels"}},{"kind":"Field","name":{"kind":"Name","value":"models"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"4"}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"onlyWithVersions"},"value":{"kind":"BooleanValue","value":true}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageLatestItemsModelItem"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pendingImportedModels"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"4"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PendingFileUpload"}}]}}]}}]} as unknown as DocumentNode<ProjectDashboardItemFragment, unknown>;
|
||||
export const ProjectsDashboardFilledFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsDashboardFilled"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectCollection"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectDashboardItem"}}]}}]}}]} as unknown as DocumentNode<ProjectsDashboardFilledFragment, unknown>;
|
||||
@@ -3767,6 +3767,7 @@ export const UserProfileEditDialogAvatar_UserFragmentDoc = {"kind":"Document","d
|
||||
export const UserProfileEditDialogBio_UserFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserProfileEditDialogBio_User"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"bio"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"UserProfileEditDialogAvatar_User"}}]}}]} as unknown as DocumentNode<UserProfileEditDialogBio_UserFragment, unknown>;
|
||||
export const UserProfileEditDialogDeleteAccount_UserFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserProfileEditDialogDeleteAccount_User"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}}]}}]} as unknown as DocumentNode<UserProfileEditDialogDeleteAccount_UserFragment, unknown>;
|
||||
export const UserProfileEditDialogNotificationPreferences_UserFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserProfileEditDialogNotificationPreferences_User"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"notificationPreferences"}}]}}]} as unknown as DocumentNode<UserProfileEditDialogNotificationPreferences_UserFragment, unknown>;
|
||||
export const ModelPageProjectFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ModelPageProject"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}}]}}]} as unknown as DocumentNode<ModelPageProjectFragment, unknown>;
|
||||
export const ViewerModelVersionCardItemFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewerModelVersionCardItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Version"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"referencedObject"}},{"kind":"Field","name":{"kind":"Name","value":"sourceApplication"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"previewUrl"}},{"kind":"Field","name":{"kind":"Name","value":"authorUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}}]}}]} as unknown as DocumentNode<ViewerModelVersionCardItemFragment, unknown>;
|
||||
export const ProjectUpdatableMetadataFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectUpdatableMetadata"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"allowPublicComments"}}]}}]} as unknown as DocumentNode<ProjectUpdatableMetadataFragment, unknown>;
|
||||
export const AppAuthorAvatarFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AppAuthorAvatar"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AppAuthor"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}}]} as unknown as DocumentNode<AppAuthorAvatarFragment, unknown>;
|
||||
@@ -3783,7 +3784,6 @@ export const ProjectPageStatsBlockModelsFragmentDoc = {"kind":"Document","defini
|
||||
export const ProjectPageStatsBlockCommentsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageStatsBlockComments"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"commentThreadCount"},"name":{"kind":"Name","value":"commentThreads"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}}]} as unknown as DocumentNode<ProjectPageStatsBlockCommentsFragment, unknown>;
|
||||
export const ProjectPageLatestItemsCommentsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageLatestItemsComments"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","alias":{"kind":"Name","value":"commentThreadCount"},"name":{"kind":"Name","value":"commentThreads"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}}]} as unknown as DocumentNode<ProjectPageLatestItemsCommentsFragment, unknown>;
|
||||
export const ProjectPageProjectFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageProject"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageProjectHeader"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageStatsBlockTeam"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageTeamDialog"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageStatsBlockVersions"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageStatsBlockModels"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageStatsBlockComments"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageLatestItemsModels"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageLatestItemsComments"}}]}}]} as unknown as DocumentNode<ProjectPageProjectFragment, unknown>;
|
||||
export const ModelPageProjectFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ModelPageProject"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]} as unknown as DocumentNode<ModelPageProjectFragment, unknown>;
|
||||
export const RegisterPanelServerInviteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"RegisterPanelServerInvite"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"token"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serverInviteByToken"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"token"},"value":{"kind":"Variable","name":{"kind":"Name","value":"token"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}}]}}]}}]} as unknown as DocumentNode<RegisterPanelServerInviteQuery, RegisterPanelServerInviteQueryVariables>;
|
||||
export const EmailVerificationBannerStateDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"EmailVerificationBannerState"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"verified"}},{"kind":"Field","name":{"kind":"Name","value":"hasPendingVerification"}}]}}]}}]} as unknown as DocumentNode<EmailVerificationBannerStateQuery, EmailVerificationBannerStateQueryVariables>;
|
||||
export const RequestVerificationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RequestVerification"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"requestVerification"}}]}}]} as unknown as DocumentNode<RequestVerificationMutation, RequestVerificationMutationVariables>;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable no-console */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
|
||||
@@ -60,5 +60,12 @@ export enum VersionActionTypes {
|
||||
EditMessage = 'edit-message',
|
||||
Select = 'select',
|
||||
Share = 'share',
|
||||
CopyId = 'copy-id'
|
||||
CopyId = 'copy-id',
|
||||
EmbedModel = 'embed-model'
|
||||
}
|
||||
|
||||
export enum OpenSectionType {
|
||||
Invite = 'invite',
|
||||
Access = 'access',
|
||||
Team = 'team'
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import { useViewerAnchoredPoints } from '~~/lib/viewer/composables/anchorPoints'
|
||||
import { useOnBeforeWindowUnload } from '~~/lib/common/composables/window'
|
||||
import { ToastNotificationType, useGlobalToast } from '~~/lib/common/composables/toast'
|
||||
import { onViewerUserActivityBroadcastedSubscription } from '~~/lib/viewer/graphql/subscriptions'
|
||||
import { useEmbed } from '~/lib/viewer/composables/setup/embed'
|
||||
|
||||
import {
|
||||
StateApplyMode,
|
||||
@@ -56,7 +57,6 @@ function useCollectMainMetadata() {
|
||||
const { sessionId } = useInjectedViewerState()
|
||||
const { activeUser } = useActiveUser()
|
||||
const { serialize } = useStateSerialization()
|
||||
|
||||
return (): Omit<ViewerUserActivityMessageInput, 'status' | 'selection'> => ({
|
||||
userId: activeUser.value?.id || null,
|
||||
userName: activeUser.value?.name || 'Anonymous Viewer',
|
||||
@@ -79,9 +79,10 @@ export function useViewerUserActivityBroadcasting(
|
||||
const { isLoggedIn } = useActiveUser()
|
||||
const getMainMetadata = useCollectMainMetadata()
|
||||
const apollo = useApolloClient().client
|
||||
const { isEnabled: isEmbedEnabled } = useEmbed()
|
||||
|
||||
const invokeMutation = async (message: ViewerUserActivityMessageInput) => {
|
||||
if (!isLoggedIn.value) return false
|
||||
if (!isLoggedIn.value || isEmbedEnabled) return false
|
||||
const result = await apollo
|
||||
.mutate({
|
||||
mutation: broadcastViewerUserActivityMutation,
|
||||
@@ -143,6 +144,7 @@ export function useViewerUserActivityTracking(params: {
|
||||
const { isLoggedIn } = useActiveUser()
|
||||
const { triggerNotification } = useGlobalToast()
|
||||
const sendUpdate = useViewerUserActivityBroadcasting()
|
||||
const { isEnabled: isEmbedEnabled } = useEmbed()
|
||||
|
||||
// TODO: For some reason subscription is set up twice? Vue Apollo bug?
|
||||
const { onResult: onUserActivity } = useSubscription(
|
||||
@@ -174,7 +176,7 @@ export function useViewerUserActivityTracking(params: {
|
||||
const incomingSessionId = event.sessionId
|
||||
|
||||
if (sessionId.value === incomingSessionId) return
|
||||
if (status === ViewerUserActivityStatus.Disconnected) {
|
||||
if (!isEmbedEnabled && status === ViewerUserActivityStatus.Disconnected) {
|
||||
triggerNotification({
|
||||
description: `${users.value[incomingSessionId]?.userName || 'A user'} left.`,
|
||||
type: ToastNotificationType.Info
|
||||
@@ -206,7 +208,7 @@ export function useViewerUserActivityTracking(params: {
|
||||
lastUpdate: dayjs()
|
||||
}
|
||||
|
||||
if (!Object.keys(users.value).includes(incomingSessionId)) {
|
||||
if (!isEmbedEnabled && !Object.keys(users.value).includes(incomingSessionId)) {
|
||||
triggerNotification({
|
||||
description: `${userData.userName} joined.`,
|
||||
type: ToastNotificationType.Info
|
||||
|
||||
@@ -19,14 +19,7 @@ import type {
|
||||
} from '@speckle/viewer'
|
||||
import type { MaybeRef } from '@vueuse/shared'
|
||||
import { inject, ref, provide } from 'vue'
|
||||
import type {
|
||||
InjectionKey,
|
||||
ComputedRef,
|
||||
WritableComputedRef,
|
||||
Raw,
|
||||
Ref,
|
||||
ShallowRef
|
||||
} from 'vue'
|
||||
import type { ComputedRef, WritableComputedRef, Raw, Ref, ShallowRef } from 'vue'
|
||||
import { useScopedState } from '~~/lib/common/composables/scopedState'
|
||||
import type { MaybeNullOrUndefined, Nullable, Optional } from '@speckle/shared'
|
||||
import { SpeckleViewer, isNonNullable } from '@speckle/shared'
|
||||
@@ -68,6 +61,10 @@ import { useDiffUtilities, useFilterUtilities } from '~~/lib/viewer/composables/
|
||||
import { flatten, reduce } from 'lodash-es'
|
||||
import { setupViewerCommentBubbles } from '~~/lib/viewer/composables/setup/comments'
|
||||
import { FilteringExtension } from '@speckle/viewer'
|
||||
import {
|
||||
InjectableViewerStateKey,
|
||||
useSetupViewerScope
|
||||
} from '~/lib/viewer/composables/setup/core'
|
||||
|
||||
export type LoadedModel = NonNullable<
|
||||
Get<ViewerLoadedResourcesQuery, 'project.models.items[0]'>
|
||||
@@ -302,13 +299,6 @@ export type InitialStateWithInterface = InitialStateWithUrlHashState &
|
||||
*/
|
||||
const GlobalViewerDataKey = Symbol('GlobalViewerData')
|
||||
|
||||
/**
|
||||
* Vue injection key for the Injectable Viewer State
|
||||
*/
|
||||
const InjectableViewerStateKey: InjectionKey<InjectableViewerState> = Symbol(
|
||||
'INJECTABLE_VIEWER_STATE'
|
||||
)
|
||||
|
||||
function createViewerDataBuilder(params: { viewerDebug: boolean }) {
|
||||
return () => {
|
||||
if (process.server)
|
||||
@@ -971,17 +961,6 @@ export function useInjectedViewerInterfaceState(): InjectableViewerState['ui'] {
|
||||
return ui
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this when you want to use the viewer state outside the viewer, ie in a component that's inside a portal!
|
||||
* @param state
|
||||
*/
|
||||
export function useSetupViewerScope(
|
||||
state: InjectableViewerState
|
||||
): InjectableViewerState {
|
||||
provide(InjectableViewerStateKey, state)
|
||||
return state
|
||||
}
|
||||
|
||||
export function useResetUiState() {
|
||||
const {
|
||||
ui: { camera, sectionBox, highlightedObjectIds, lightConfig }
|
||||
@@ -998,3 +977,5 @@ export function useResetUiState() {
|
||||
endDiff()
|
||||
}
|
||||
}
|
||||
|
||||
export { InjectableViewerStateKey, useSetupViewerScope }
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { InjectableViewerState } from '~/lib/viewer/composables/setup'
|
||||
|
||||
/**
|
||||
* Keeping some core Viewer state code here so that we can import it without
|
||||
* importing the entire Viewer state related codebase. Useful in embed mode where
|
||||
* we don't want to load all of the Viewer JS before the Play button is pressed.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Vue injection key for the Injectable Viewer State
|
||||
*/
|
||||
export const InjectableViewerStateKey: InjectionKey<InjectableViewerState> = Symbol(
|
||||
'INJECTABLE_VIEWER_STATE'
|
||||
)
|
||||
|
||||
/**
|
||||
* Use this when you want to use the viewer state outside the viewer, ie in a component that's inside a portal!
|
||||
* @param state
|
||||
*/
|
||||
export function useSetupViewerScope(
|
||||
state: InjectableViewerState
|
||||
): InjectableViewerState {
|
||||
provide(InjectableViewerStateKey, state)
|
||||
return state
|
||||
}
|
||||
|
||||
export type { InjectableViewerState }
|
||||
@@ -0,0 +1,129 @@
|
||||
import { writableAsyncComputed } from '~/lib/common/composables/async'
|
||||
import { useScopedState } from '~/lib/common/composables/scopedState'
|
||||
import { ViewerHashStateKeys } from '~/lib/viewer/composables/setup/urlHashState'
|
||||
import { useConditionalViewerRendering } from '~/lib/viewer/composables/ui'
|
||||
import { useRouteHashState } from '~~/lib/common/composables/url'
|
||||
|
||||
export type EmbedOptions = {
|
||||
isEnabled?: boolean
|
||||
isTransparent?: boolean
|
||||
hideControls?: boolean
|
||||
hideSelectionInfo?: boolean
|
||||
noScroll?: boolean
|
||||
manualLoad?: boolean
|
||||
}
|
||||
|
||||
export function isEmbedOptions(obj: unknown): obj is EmbedOptions {
|
||||
if (typeof obj === 'object' && obj !== null) {
|
||||
const possibleOptions = obj as Partial<EmbedOptions>
|
||||
return Object.keys(possibleOptions).every(
|
||||
(key) =>
|
||||
[
|
||||
'isEnabled',
|
||||
'isTransparent',
|
||||
'hideControls',
|
||||
'hideSelectionInfo',
|
||||
'noScroll',
|
||||
'manualLoad'
|
||||
].includes(key) &&
|
||||
typeof possibleOptions[key as keyof EmbedOptions] === 'boolean'
|
||||
)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export function deserializeEmbedOptions(embedString: string | null): EmbedOptions {
|
||||
const logger = useLogger()
|
||||
if (!embedString) {
|
||||
return { isEnabled: false }
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed: unknown = JSON.parse(embedString)
|
||||
if (isEmbedOptions(parsed)) {
|
||||
return { ...parsed, isEnabled: true }
|
||||
}
|
||||
logger.error('Parsed object is not of type EmbedOptions')
|
||||
} catch (error) {
|
||||
logger.error(error)
|
||||
}
|
||||
|
||||
return { isEnabled: false }
|
||||
}
|
||||
|
||||
export function useEmbedState() {
|
||||
const { hashState } = useRouteHashState()
|
||||
|
||||
const embedOptions = writableAsyncComputed({
|
||||
get: () => {
|
||||
const embedString = hashState.value[ViewerHashStateKeys.EmbedOptions]
|
||||
return deserializeEmbedOptions(embedString)
|
||||
},
|
||||
set: async (newOptions) => {
|
||||
const embedString = newOptions ? JSON.stringify(newOptions) : null
|
||||
await hashState.update({
|
||||
...hashState.value,
|
||||
[ViewerHashStateKeys.EmbedOptions]: embedString
|
||||
})
|
||||
},
|
||||
initialState: null,
|
||||
asyncRead: false
|
||||
})
|
||||
|
||||
return { embedOptions }
|
||||
}
|
||||
|
||||
const embedStateScopedKey = Symbol('EmbedStateScopedKey')
|
||||
|
||||
export function useEmbed() {
|
||||
const { embedOptions } = useEmbedState()
|
||||
const { showControls } = useConditionalViewerRendering()
|
||||
|
||||
// useScopedState so that we don't keep creating new computeds
|
||||
return useScopedState(embedStateScopedKey, () => {
|
||||
const createComputed = <K extends keyof EmbedOptions>(key: K) =>
|
||||
writableAsyncComputed({
|
||||
get: () => embedOptions.value?.[key],
|
||||
set: async (newVal) => {
|
||||
await embedOptions.update({
|
||||
...(embedOptions.value ?? {}),
|
||||
...{
|
||||
[key]: newVal
|
||||
}
|
||||
})
|
||||
},
|
||||
initialState: null,
|
||||
asyncRead: false
|
||||
})
|
||||
|
||||
const isEnabled = createComputed('isEnabled')
|
||||
const isTransparent = createComputed('isTransparent')
|
||||
|
||||
const hideSelectionInfo = createComputed('hideSelectionInfo')
|
||||
const noScroll = createComputed('noScroll')
|
||||
const manualLoad = createComputed('manualLoad')
|
||||
|
||||
const showControlsNew = writableAsyncComputed({
|
||||
get: () => showControls.value,
|
||||
set: async (newVal) =>
|
||||
await embedOptions.update({
|
||||
...(embedOptions.value ?? {}),
|
||||
...{
|
||||
hideControls: !(newVal || undefined)
|
||||
}
|
||||
}),
|
||||
initialState: null,
|
||||
asyncRead: false
|
||||
})
|
||||
|
||||
return {
|
||||
isEnabled,
|
||||
isEmbedEnabled: isEnabled,
|
||||
isTransparent,
|
||||
showControls: showControlsNew,
|
||||
hideSelectionInfo,
|
||||
noScroll,
|
||||
manualLoad
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -50,6 +50,7 @@ import { setupDebugMode } from '~~/lib/viewer/composables/setup/dev'
|
||||
import { CameraController } from '@speckle/viewer'
|
||||
import type { Reference } from '@apollo/client'
|
||||
import type { Modifier } from '@apollo/client/cache'
|
||||
import { useEmbed } from '~/lib/viewer/composables/setup/embed'
|
||||
|
||||
function useViewerIsBusyEventHandler() {
|
||||
const state = useInjectedViewerState()
|
||||
@@ -744,6 +745,24 @@ function useViewerMeasurementIntegration() {
|
||||
)
|
||||
}
|
||||
|
||||
function useDisableZoomOnEmbed() {
|
||||
const { viewer } = useInjectedViewerState()
|
||||
const embedOptions = useEmbed()
|
||||
|
||||
watch(
|
||||
() => embedOptions.noScroll.value,
|
||||
(newNoScrollValue) => {
|
||||
const cameraController = viewer.instance.getExtension(CameraController)
|
||||
if (newNoScrollValue) {
|
||||
cameraController.controls.mouseButtons.wheel = 0
|
||||
} else {
|
||||
cameraController.controls.mouseButtons.wheel = 4
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
}
|
||||
|
||||
export function useViewerPostSetup() {
|
||||
if (process.server) return
|
||||
useViewerObjectAutoLoading()
|
||||
@@ -759,5 +778,6 @@ export function useViewerPostSetup() {
|
||||
useExplodeFactorIntegration()
|
||||
useDiffingIntegration()
|
||||
useViewerMeasurementIntegration()
|
||||
useDisableZoomOnEmbed()
|
||||
setupDebugMode()
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@ import { useDiffBuilderUtilities } from '~~/lib/viewer/composables/setup/diff'
|
||||
|
||||
export enum ViewerHashStateKeys {
|
||||
FocusedThreadId = 'threadId',
|
||||
Diff = 'diff'
|
||||
Diff = 'diff',
|
||||
EmbedOptions = 'embed'
|
||||
}
|
||||
|
||||
export function setupUrlHashState(): InjectableViewerState['urlHashState'] {
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { useConditionalViewerRendering } from '~/lib/viewer/composables/ui'
|
||||
|
||||
export const useTourStageState = () =>
|
||||
useState('viewer-tour-state', () => ({
|
||||
showNavbar: true,
|
||||
showViewerControls: true,
|
||||
showTour: false,
|
||||
showSegmentation: true
|
||||
}))
|
||||
|
||||
export function useViewerTour() {
|
||||
const state = useTourStageState()
|
||||
const conditionalRendering = useConditionalViewerRendering()
|
||||
|
||||
const showNavbar = computed({
|
||||
get: () => conditionalRendering.showNavbar.value,
|
||||
set: (newVal) => (state.value.showNavbar = newVal)
|
||||
})
|
||||
|
||||
const showControls = computed({
|
||||
get: () => conditionalRendering.showControls.value,
|
||||
set: (newVal) => (state.value.showViewerControls = newVal)
|
||||
})
|
||||
|
||||
const showTour = computed({
|
||||
get: () => state.value.showTour,
|
||||
set: (newVal) => (state.value.showTour = newVal)
|
||||
})
|
||||
|
||||
const showSegmentation = computed({
|
||||
get: () => state.value.showSegmentation,
|
||||
set: (newVal) => (state.value.showSegmentation = newVal)
|
||||
})
|
||||
|
||||
return {
|
||||
showNavbar,
|
||||
showControls,
|
||||
showTour,
|
||||
showSegmentation
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { CameraController } from '@speckle/viewer'
|
||||
import type { MeasurementOptions, PropertyInfo } from '@speckle/viewer'
|
||||
import { until } from '@vueuse/shared'
|
||||
import { difference, isString, uniq } from 'lodash-es'
|
||||
import { useEmbedState } from '~/lib/viewer/composables/setup/embed'
|
||||
import type { SpeckleObject } from '~~/lib/common/helpers/sceneExplorer'
|
||||
import { isNonNullable } from '~~/lib/common/helpers/utils'
|
||||
import {
|
||||
@@ -12,6 +13,7 @@ import {
|
||||
useInjectedViewerState
|
||||
} from '~~/lib/viewer/composables/setup'
|
||||
import { useDiffBuilderUtilities } from '~~/lib/viewer/composables/setup/diff'
|
||||
import { useTourStageState } from '~~/lib/viewer/composables/tour'
|
||||
|
||||
export function useSectionBoxUtilities() {
|
||||
const { instance } = useInjectedViewer()
|
||||
@@ -361,3 +363,35 @@ export function useMeasurementUtilities() {
|
||||
removeMeasurement
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Some conditional rendering values depend on multiple & overlapping states. This utility reconciles that.
|
||||
*/
|
||||
export function useConditionalViewerRendering() {
|
||||
const tourState = useTourStageState()
|
||||
const embedMode = useEmbedState()
|
||||
|
||||
const showControls = computed(() => {
|
||||
if (tourState.value.showTour && !tourState.value.showViewerControls) return false
|
||||
if (
|
||||
embedMode.embedOptions.value?.isEnabled &&
|
||||
embedMode.embedOptions.value.hideControls
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
const showNavbar = computed(() => {
|
||||
if (!showControls.value) return false
|
||||
if (tourState.value.showTour && !tourState.value.showNavbar) return false
|
||||
if (embedMode.embedOptions.value?.isEnabled) return false
|
||||
return true
|
||||
})
|
||||
|
||||
return {
|
||||
showNavbar,
|
||||
showControls
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ export default defineNuxtConfig({
|
||||
public: {
|
||||
apiOrigin: 'UNDEFINED',
|
||||
backendApiOrigin: '',
|
||||
baseUrl: '',
|
||||
mixpanelApiHost: 'UNDEFINED',
|
||||
mixpanelTokenId: 'UNDEFINED',
|
||||
logLevel: NUXT_PUBLIC_LOG_LEVEL,
|
||||
|
||||
@@ -113,7 +113,7 @@
|
||||
"stylelint-config-prettier": "^9.0.3",
|
||||
"stylelint-config-recommended-vue": "^1.4.0",
|
||||
"stylelint-config-standard": "^26.0.0",
|
||||
"tailwindcss": "^3.3.2",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"type-fest": "^3.5.1",
|
||||
"typescript": "^4.8.3",
|
||||
"vue-tsc": "1.8.22",
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { useViewerTour } from '~/lib/viewer/composables/tour'
|
||||
import {
|
||||
useProcessOnboarding,
|
||||
FIRST_MODEL_NAME
|
||||
@@ -34,7 +35,7 @@ definePageMeta({
|
||||
|
||||
const router = useRouter()
|
||||
const { createOnboardingProject, setUserOnboardingComplete } = useProcessOnboarding()
|
||||
const tourStage = useTourStageState()
|
||||
const tourStage = useViewerTour()
|
||||
|
||||
const status = ref('Setting up your account')
|
||||
|
||||
@@ -48,9 +49,9 @@ onMounted(async () => {
|
||||
await setUserOnboardingComplete()
|
||||
status.value = 'Almost done!'
|
||||
|
||||
tourStage.value.showNavbar = false
|
||||
tourStage.value.showViewerControls = false
|
||||
tourStage.value.showTour = true
|
||||
tourStage.showNavbar.value = false
|
||||
tourStage.showControls.value = false
|
||||
tourStage.showTour.value = true
|
||||
|
||||
const firstModelToLoad = project?.models.items.find(
|
||||
(model) => model.name === FIRST_MODEL_NAME
|
||||
|
||||
@@ -21,9 +21,13 @@
|
||||
<!-- No v-if=project to ensure internal queries trigger ASAP -->
|
||||
<div v-show="project" class="flex flex-col space-y-8 sm:space-y-14">
|
||||
<!-- Latest models -->
|
||||
<ProjectPageLatestItemsModels :project="project" :project-id="projectId" />
|
||||
<div class="relative z-10">
|
||||
<ProjectPageLatestItemsModels :project="project" :project-id="projectId" />
|
||||
</div>
|
||||
<!-- Latest comments -->
|
||||
<ProjectPageLatestItemsComments :project="project" :project-id="projectId" />
|
||||
<div class="relative z-0">
|
||||
<ProjectPageLatestItemsComments :project="project" :project-id="projectId" />
|
||||
</div>
|
||||
<!-- More actions -->
|
||||
<!-- <ProjectPageMoreActions /> -->
|
||||
</div>
|
||||
|
||||
@@ -1,80 +1,25 @@
|
||||
<template>
|
||||
<ViewerPostSetupWrapper>
|
||||
<div class="absolute top-0 left-0 w-screen h-[100dvh]">
|
||||
<!-- Nav -->
|
||||
<Portal to="navigation">
|
||||
<ViewerScope :state="state">
|
||||
<HeaderNavLink
|
||||
:to="`/projects/${project?.id}`"
|
||||
:name="project?.name"
|
||||
></HeaderNavLink>
|
||||
<ViewerExplorerNavbarLink />
|
||||
</ViewerScope>
|
||||
</Portal>
|
||||
|
||||
<!-- Note: commented out until we scope it properly. -->
|
||||
<!-- <Portal to="primary-actions">
|
||||
<div class="flex space-x-4">
|
||||
<FormButton :icon-left="ShareIcon">Share</FormButton>
|
||||
</div>
|
||||
</Portal> -->
|
||||
|
||||
<ClientOnly>
|
||||
<!-- Tour host -->
|
||||
<div
|
||||
v-if="tourState.showTour"
|
||||
class="fixed w-full h-[100dvh] flex justify-center items-center pointer-events-none z-[100]"
|
||||
>
|
||||
<TourOnboarding />
|
||||
</div>
|
||||
<!-- Viewer host -->
|
||||
<div class="special-gradient absolute w-screen h-[100dvh] z-10 overflow-hidden">
|
||||
<ViewerBase />
|
||||
<Transition
|
||||
enter-from-class="opacity-0"
|
||||
enter-active-class="transition duration-1000"
|
||||
>
|
||||
<ViewerAnchoredPoints v-show="tourState.showViewerControls" />
|
||||
</Transition>
|
||||
</div>
|
||||
|
||||
<!-- Global loading bar -->
|
||||
<ViewerLoadingBar class="z-20" />
|
||||
|
||||
<!-- Sidebar sketches -->
|
||||
<Transition
|
||||
enter-from-class="opacity-0"
|
||||
enter-active-class="transition duration-1000"
|
||||
>
|
||||
<ViewerControls v-show="tourState.showViewerControls" class="z-20" />
|
||||
</Transition>
|
||||
<!-- Viewer Object Selection Info Display -->
|
||||
<Transition
|
||||
enter-from-class="opacity-0"
|
||||
enter-active-class="transition duration-1000"
|
||||
>
|
||||
<div v-show="tourState.showViewerControls">
|
||||
<ViewerSelectionSidebar class="z-20 hidden sm:block" />
|
||||
</div>
|
||||
</Transition>
|
||||
<!-- Shows up when filters are applied for an easy return to normality -->
|
||||
<ViewerGlobalFilterReset class="z-20" />
|
||||
</ClientOnly>
|
||||
</div>
|
||||
</ViewerPostSetupWrapper>
|
||||
<div
|
||||
v-if="tourState.showViewerControls"
|
||||
class="sm:hidden shadow-t fixed bottom-0 left-0 max-h-[65vh] overflow-hidden w-screen z-50 transition-all duration-300 empty:-bottom-[65vh]"
|
||||
>
|
||||
<PortalTarget name="bottomPanel"></PortalTarget>
|
||||
<PortalTarget name="mobileComments"></PortalTarget>
|
||||
<div :class="isTransparent ? 'viewer-transparent' : ''">
|
||||
<ViewerEmbedManualLoad v-if="isManualLoad" @play="isManualLoad = false" />
|
||||
<LazyViewerPreSetupWrapper v-else @setup="state = $event" />
|
||||
<ClientOnly>
|
||||
<Component
|
||||
:is="state ? ViewerScope : 'div'"
|
||||
:state="state"
|
||||
wrapper
|
||||
class="fixed shadow-t bottom-0 left-0 max-h-[65vh] overflow-hidden w-screen z-50 transition-all duration-300 empty:-bottom-[65vh]"
|
||||
>
|
||||
<PortalTarget name="bottomPanel"></PortalTarget>
|
||||
<PortalTarget name="mobileComments"></PortalTarget>
|
||||
</Component>
|
||||
</ClientOnly>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { graphql } from '~~/lib/common/generated/gql'
|
||||
import { useSetupViewer } from '~~/lib/viewer/composables/setup'
|
||||
|
||||
const tourState = useTourStageState()
|
||||
<script setup lang="ts">
|
||||
import { useRoute } from 'vue-router'
|
||||
import { deserializeEmbedOptions } from '~~/lib/viewer/composables/setup/embed'
|
||||
import type { InjectableViewerState } from '~~/lib/viewer/composables/setup/core'
|
||||
|
||||
definePageMeta({
|
||||
layout: 'viewer',
|
||||
@@ -84,29 +29,29 @@ definePageMeta({
|
||||
key: '/projects/:id/models/resources' // To prevent controls flickering on resource url param changes
|
||||
})
|
||||
|
||||
const ViewerScope = resolveComponent('ViewerScope')
|
||||
|
||||
const isManualLoad = ref(false)
|
||||
const isTransparent = ref(false)
|
||||
const route = useRoute()
|
||||
const projectId = computed(() => route.params.id as string)
|
||||
const state = ref<InjectableViewerState>()
|
||||
|
||||
const state = useSetupViewer({
|
||||
projectId
|
||||
})
|
||||
const checkUrlForEmbedManualLoadSettings = () => {
|
||||
if (process.server) return
|
||||
|
||||
const {
|
||||
resources: {
|
||||
response: { project }
|
||||
}
|
||||
} = state
|
||||
const hashParams = new URLSearchParams(route.hash.substring(1))
|
||||
const embedParam = hashParams.get('embed')
|
||||
|
||||
graphql(`
|
||||
fragment ModelPageProject on Project {
|
||||
id
|
||||
createdAt
|
||||
name
|
||||
}
|
||||
`)
|
||||
const embedOptions = deserializeEmbedOptions(embedParam)
|
||||
isManualLoad.value = embedOptions.manualLoad === true
|
||||
isTransparent.value = embedOptions.isTransparent === true
|
||||
}
|
||||
|
||||
const title = computed(() =>
|
||||
project.value?.name.length ? `Viewer - ${project.value.name}` : ''
|
||||
watch(
|
||||
() => route.fullPath,
|
||||
() => {
|
||||
checkUrlForEmbedManualLoadSettings()
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
useHead({ title })
|
||||
</script>
|
||||
|
||||
@@ -48,6 +48,7 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
||||
const seqLogger = new seq.Logger({
|
||||
serverUrl: logClientApiEndpoint,
|
||||
apiKey: logClientApiToken,
|
||||
// eslint-disable-next-line no-console
|
||||
onError: console.error
|
||||
})
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="!$vuetify.breakpoint.xs || (isEmbed && commentSlideShow)"
|
||||
v-if="!$vuetify.breakpoint.xs"
|
||||
class="no-mouse py-2"
|
||||
:style="`max-width: 350px; padding-right:30px;
|
||||
${
|
||||
@@ -450,7 +450,6 @@ import { SMART_EDITOR_SCHEMA } from '@/main/lib/viewer/comments/commentsHelper'
|
||||
import { isSuccessfullyUploaded } from '@/main/lib/common/file-upload/fileUploadHelper'
|
||||
import { COMMENT_FULL_INFO_FRAGMENT } from '@/graphql/comments'
|
||||
import { useCommitObjectViewerParams } from '@/main/lib/viewer/commit-object-viewer/stateManager'
|
||||
import { useEmbedViewerQuery } from '@/main/lib/viewer/commit-object-viewer/composables/embed'
|
||||
// TODO: The template is a WET mess, need to refactor it
|
||||
|
||||
export default {
|
||||
@@ -587,12 +586,10 @@ export default {
|
||||
},
|
||||
setup() {
|
||||
const { streamId, resourceId, isEmbed } = useCommitObjectViewerParams()
|
||||
const { commentSlideShow } = useEmbedViewerQuery()
|
||||
return {
|
||||
streamId,
|
||||
resourceId,
|
||||
isEmbed,
|
||||
commentSlideShow
|
||||
isEmbed
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
||||
@@ -111,7 +111,7 @@ const containerClasses = computed(() => {
|
||||
break
|
||||
case 'default':
|
||||
default:
|
||||
classParts.push(hasDescription.value ? 'p-4' : 'p-2')
|
||||
classParts.push(hasDescription.value ? 'p-3 sm:p-4' : 'p-2')
|
||||
break
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ const descriptionWrapperClasses = computed(() => {
|
||||
break
|
||||
case 'default':
|
||||
default:
|
||||
classParts.push('mt-2 text-sm')
|
||||
classParts.push('mt-1 sm:mt-2 text-xs sm:text-sm')
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
@@ -16,10 +16,6 @@
|
||||
:class="`${iconClasses} ${hideText ? '' : 'mr-2'}`"
|
||||
/>
|
||||
<slot v-if="!hideText">Button</slot>
|
||||
<div v-else style="margin: 0 !important; width: 0.01px">
|
||||
|
||||
<!-- The point of this is to ensure text & no-text buttons have the same height -->
|
||||
</div>
|
||||
<Component
|
||||
:is="iconRight"
|
||||
v-if="iconRight || !loading"
|
||||
@@ -433,7 +429,7 @@ const decoratorClasses = computed(() => {
|
||||
const buttonClasses = computed(() => {
|
||||
const isLinkOrText = props.link || props.text
|
||||
return [
|
||||
'transition inline-flex justify-center text-center items-center space-x-2 outline-none select-none',
|
||||
'transition inline-flex justify-center text-center items-center outline-none select-none leading-[0.9rem]',
|
||||
generalClasses.value,
|
||||
sizeClasses.value,
|
||||
foregroundClasses.value,
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
<template>
|
||||
<div class="relative group bg-foundation-page p-2 rounded-lg pr-12">
|
||||
<FormTextArea
|
||||
<div
|
||||
v-if="isMultiline"
|
||||
color="transparent"
|
||||
name="contentArea"
|
||||
readonly
|
||||
:model-value="value"
|
||||
class="relative z-10 text-sm text-foreground font-mono"
|
||||
:rows="rows"
|
||||
/>
|
||||
class="relative z-10 text-xs sm:text-sm text-foreground font-mono break-all p-2 pl-3 max-h-[4.8rem] simple-scrollbar overflow-y-auto"
|
||||
@keypress="keyboardClick(selectAllText)"
|
||||
>
|
||||
{{ value }}
|
||||
</div>
|
||||
<FormTextInput
|
||||
v-else
|
||||
color="transparent"
|
||||
@@ -21,7 +19,7 @@
|
||||
<FormButton
|
||||
color="invert"
|
||||
size="sm"
|
||||
:icon-left="ClipboardDocumentIcon"
|
||||
:icon-left="copied ? ClipboardDocumentCheckIcon : ClipboardDocumentIcon"
|
||||
hide-text
|
||||
@click="handleCopy"
|
||||
></FormButton>
|
||||
@@ -31,8 +29,13 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useClipboard } from '@vueuse/core'
|
||||
import { ClipboardDocumentIcon } from '@heroicons/vue/24/outline'
|
||||
import { FormTextArea, FormTextInput, FormButton } from '~~/src/lib'
|
||||
import {
|
||||
ClipboardDocumentIcon,
|
||||
ClipboardDocumentCheckIcon
|
||||
} from '@heroicons/vue/24/outline'
|
||||
import { FormTextInput, FormButton } from '~~/src/lib'
|
||||
import { ref } from 'vue'
|
||||
import { keyboardClick } from '~~/src/helpers/global/accessibility'
|
||||
|
||||
type Props = {
|
||||
value: string
|
||||
@@ -48,10 +51,29 @@ const emit = defineEmits<{ (e: 'copy', val: string): void }>()
|
||||
|
||||
const { copy } = useClipboard({ legacy: true })
|
||||
|
||||
const copied = ref(false)
|
||||
|
||||
const handleCopy = async () => {
|
||||
if (props.value) {
|
||||
await copy(props.value)
|
||||
copied.value = true
|
||||
emit('copy', props.value)
|
||||
|
||||
setTimeout(() => {
|
||||
copied.value = false
|
||||
}, 2000)
|
||||
}
|
||||
}
|
||||
|
||||
const selectAllText = (event: Event) => {
|
||||
const textElement = event.target as HTMLElement
|
||||
|
||||
const selection = window.getSelection()
|
||||
if (selection) {
|
||||
const range = document.createRange()
|
||||
range.selectNodeContents(textElement)
|
||||
selection.removeAllRanges()
|
||||
selection.addRange(range)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -19,14 +19,17 @@
|
||||
@change="onChange"
|
||||
/>
|
||||
</div>
|
||||
<div class="text-sm" :class="inlineDescription ? 'flex gap-2 items-center' : ''">
|
||||
<div
|
||||
class="text-xs sm:text-sm"
|
||||
:class="inlineDescription ? 'flex gap-2 items-center' : ''"
|
||||
>
|
||||
<label
|
||||
:for="finalId"
|
||||
class="text-foreground flex gap-2 items-center"
|
||||
:class="{ 'sr-only': hideLabel }"
|
||||
>
|
||||
<div v-if="icon" class="text-sm">
|
||||
<component :is="icon" class="h-10 w-10"></component>
|
||||
<component :is="icon" class="h-8 sm:h-10 w-8 sm:w-10"></component>
|
||||
</div>
|
||||
<span>{{ title }}</span>
|
||||
<span v-if="showRequired" class="text-danger ml-1">*</span>
|
||||
|
||||
@@ -35,10 +35,10 @@
|
||||
:as="isForm ? 'form' : 'div'"
|
||||
@submit.prevent="onSubmit"
|
||||
>
|
||||
<div :class="scrolledFromTop && 'relative z-10 shadow-lg'">
|
||||
<div :class="scrolledFromTop && 'relative z-20 shadow-lg'">
|
||||
<div
|
||||
v-if="hasTitle"
|
||||
class="flex items-center justify-start rounded-t-lg shrink-0 min-h-[4rem] py-2 px-4 sm:px-8 truncate text-xl sm:text-2xl font-bold"
|
||||
class="flex items-center justify-start rounded-t-lg shrink-0 min-h-[2rem] sm:min-h-[4rem] py-2 px-4 sm:px-8 truncate text-lg sm:text-2xl font-bold"
|
||||
>
|
||||
<div class="w-full truncate pr-12">
|
||||
{{ title }}
|
||||
@@ -49,22 +49,22 @@
|
||||
|
||||
<button
|
||||
v-if="!hideCloser"
|
||||
class="absolute z-20 right-4 bg-foundation rounded-full p-1"
|
||||
:class="hasTitle ? 'top-4' : 'top-3'"
|
||||
class="absolute z-20 bg-foundation rounded-full p-1"
|
||||
:class="hasTitle ? 'top-2 right-3 sm:top-4' : 'right-4 top-3'"
|
||||
@click="open = false"
|
||||
>
|
||||
<XMarkIcon class="h-6 w-6" />
|
||||
<XMarkIcon class="h-5 sm:h-6 w-5 sm:w-6" />
|
||||
</button>
|
||||
<div
|
||||
class="flex-1 simple-scrollbar overflow-y-auto"
|
||||
:class="hasTitle ? 'p-4 sm:py-6 sm:px-8' : 'p-10'"
|
||||
:class="hasTitle ? 'p-3 sm:py-6 sm:px-8' : 'p-10'"
|
||||
@scroll="onScroll"
|
||||
>
|
||||
<slot>Put your content here!</slot>
|
||||
</div>
|
||||
<div
|
||||
v-if="hasButtons"
|
||||
class="flex px-4 py-2 sm:py-4 sm:px-6 gap-2 shrink-0"
|
||||
class="relative z-50 flex px-4 py-2 sm:py-4 sm:px-6 gap-2 shrink-0 bg-foundation"
|
||||
:class="!scrolledToBottom && 'shadow-t'"
|
||||
>
|
||||
<template v-if="buttons">
|
||||
@@ -156,7 +156,7 @@ const maxWidthWeight = computed(() => {
|
||||
})
|
||||
|
||||
const widthClasses = computed(() => {
|
||||
const classParts: string[] = ['w-full', 'sm:w-full sm:max-w-xl']
|
||||
const classParts: string[] = ['w-full', 'sm:w-full sm:max-w-2xl']
|
||||
|
||||
if (maxWidthWeight.value >= 1) {
|
||||
classParts.push('md:max-w-2xl')
|
||||
|
||||
@@ -63,15 +63,27 @@
|
||||
: `max-height: ${isExpanded ? contentHeight + 'px' : '0px'}`
|
||||
"
|
||||
>
|
||||
<div ref="content" class="rounded-md text-sm pb-3 px-2 mt-1">
|
||||
<slot>Panel contents</slot>
|
||||
</div>
|
||||
<template v-if="props.lazyLoad">
|
||||
<div
|
||||
v-if="isExpanded || props.alwaysOpen"
|
||||
ref="content"
|
||||
class="rounded-md text-sm pb-3 px-2 mt-1"
|
||||
>
|
||||
<slot>Panel contents</slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<div ref="content" class="rounded-md text-sm pb-3 px-2 mt-1">
|
||||
<slot>Panel contents</slot>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, unref, computed } from 'vue'
|
||||
import { ref, unref, computed, nextTick } from 'vue'
|
||||
import type { Ref } from 'vue'
|
||||
import { ChevronDownIcon } from '@heroicons/vue/24/outline'
|
||||
import { FormButton } from '~~/src/lib'
|
||||
@@ -109,7 +121,11 @@ const props = defineProps({
|
||||
onClick?: () => void
|
||||
}
|
||||
| undefined,
|
||||
alwaysOpen: Boolean
|
||||
alwaysOpen: Boolean,
|
||||
lazyLoad: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const content: Ref<HTMLElement | null> = ref(null)
|
||||
@@ -147,9 +163,11 @@ const titleClasses = computed(() => {
|
||||
}
|
||||
})
|
||||
|
||||
const toggleExpansion = () => {
|
||||
const toggleExpansion = async () => {
|
||||
isExpanded.value = !isExpanded.value
|
||||
|
||||
if (isExpanded.value) {
|
||||
await nextTick()
|
||||
contentHeight.value = (unref(content)?.scrollHeight || 0) + 64
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ const buildButtonClassses = (params: {
|
||||
]
|
||||
|
||||
if (active && !color) {
|
||||
classParts.push('bg-primary text-foreground-on-primary')
|
||||
classParts.push('bg-foundation-focus text-foreground')
|
||||
} else if (disabled) {
|
||||
classParts.push('text-foreground-disabled')
|
||||
} else if (color === 'danger' && active) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="flex flex-col space-y-4">
|
||||
<div class="flex space-x-6">
|
||||
<div class="flex flex-col gap-y-0 sm:gap-y-4">
|
||||
<div class="flex gap-x-6">
|
||||
<FormButton
|
||||
v-for="item in items"
|
||||
:key="item.id"
|
||||
|
||||
@@ -144,7 +144,7 @@ export class RenderTree {
|
||||
public getRenderableNodes(...types: SpeckleType[]): TreeNode[] {
|
||||
return this.root.all((node: TreeNode): boolean => {
|
||||
return (
|
||||
node.model.renderView !== null &&
|
||||
node.model.renderView &&
|
||||
(node.model.renderView.hasGeometry || node.model.renderView.hasMetadata) &&
|
||||
types.includes(node.model.renderView.renderData.speckleType)
|
||||
)
|
||||
|
||||
@@ -161,6 +161,7 @@ def main():
|
||||
fe2env = yml_doc["services"]["speckle-frontend-2"]["environment"]
|
||||
fe2env["NUXT_PUBLIC_SERVER_NAME"] = DoubleQuotedScalarString(canonical_url)
|
||||
fe2env["NUXT_PUBLIC_API_ORIGIN"] = DoubleQuotedScalarString(canonical_url)
|
||||
fe2env["NUXT_PUBLIC_BASE_URL"] = DoubleQuotedScalarString(canonical_url)
|
||||
|
||||
with open(os.path.join(FILE_PATH, "docker-compose.yml"), "w") as f:
|
||||
f.write("# This file was generated by SpeckleServer setup.\n")
|
||||
|
||||
@@ -44,6 +44,7 @@ services:
|
||||
environment:
|
||||
NUXT_PUBLIC_SERVER_NAME: 'TODO: change' # e.g. 'my-speckle-server'
|
||||
NUXT_PUBLIC_API_ORIGIN: 'TODO: change' # e.g. 'http://127.0.0.1'
|
||||
NUXT_PUBLIC_BASE_URL: 'TODO: change' # e.g. 'http://127.0.0.1'
|
||||
NUXT_PUBLIC_BACKEND_API_ORIGIN: 'http://speckle-server:3000'
|
||||
NUXT_REDIS_URL: 'redis://redis'
|
||||
|
||||
|
||||
@@ -66,6 +66,8 @@ spec:
|
||||
value: {{ .Values.file_size_limit_mb | quote }}
|
||||
- name: NUXT_PUBLIC_API_ORIGIN
|
||||
value: {{ .Values.ssl_canonical_url | ternary (printf "https://%s" .Values.domain) (printf "http://%s" .Values.domain) }}
|
||||
- name: NUXT_PUBLIC_BASE_URL
|
||||
value: {{ .Values.ssl_canonical_url | ternary (printf "https://%s" .Values.domain) (printf "http://%s" .Values.domain) }}
|
||||
- name: NUXT_PUBLIC_BACKEND_API_ORIGIN
|
||||
value: {{ printf "http://%s.%s.svc.cluster.local.:3000" (include "server.name" $) .Values.namespace }}
|
||||
- name: NUXT_PUBLIC_MIXPANEL_TOKEN_ID
|
||||
|
||||
@@ -13669,7 +13669,7 @@ __metadata:
|
||||
stylelint-config-recommended-vue: ^1.4.0
|
||||
stylelint-config-standard: ^26.0.0
|
||||
subscriptions-transport-ws: ^0.11.0
|
||||
tailwindcss: ^3.3.2
|
||||
tailwindcss: ^3.4.1
|
||||
type-fest: ^3.5.1
|
||||
typescript: ^4.8.3
|
||||
vee-validate: ^4.7.0
|
||||
@@ -43441,6 +43441,39 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tailwindcss@npm:^3.4.1":
|
||||
version: 3.4.1
|
||||
resolution: "tailwindcss@npm:3.4.1"
|
||||
dependencies:
|
||||
"@alloc/quick-lru": ^5.2.0
|
||||
arg: ^5.0.2
|
||||
chokidar: ^3.5.3
|
||||
didyoumean: ^1.2.2
|
||||
dlv: ^1.1.3
|
||||
fast-glob: ^3.3.0
|
||||
glob-parent: ^6.0.2
|
||||
is-glob: ^4.0.3
|
||||
jiti: ^1.19.1
|
||||
lilconfig: ^2.1.0
|
||||
micromatch: ^4.0.5
|
||||
normalize-path: ^3.0.0
|
||||
object-hash: ^3.0.0
|
||||
picocolors: ^1.0.0
|
||||
postcss: ^8.4.23
|
||||
postcss-import: ^15.1.0
|
||||
postcss-js: ^4.0.1
|
||||
postcss-load-config: ^4.0.1
|
||||
postcss-nested: ^6.0.1
|
||||
postcss-selector-parser: ^6.0.11
|
||||
resolve: ^1.22.2
|
||||
sucrase: ^3.32.0
|
||||
bin:
|
||||
tailwind: lib/cli.js
|
||||
tailwindcss: lib/cli.js
|
||||
checksum: ef5a587dd32bb4e91e1549ead6162f85f0b78d3e6ffd8b4e8eeb15585b7b886cb3af6ae9df5092ed8ccb7e590608d1b3eec79ca08c862b07cd9ff7e72f73104b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tapable@npm:^1.0.0":
|
||||
version: 1.1.3
|
||||
resolution: "tapable@npm:1.1.3"
|
||||
|
||||
Reference in New Issue
Block a user