diff --git a/packages/fileimport-service/ifc-dotnet/ifc-converter.csproj b/packages/fileimport-service/ifc-dotnet/ifc-converter.csproj
index 9c8c46297..3183ad96c 100644
--- a/packages/fileimport-service/ifc-dotnet/ifc-converter.csproj
+++ b/packages/fileimport-service/ifc-dotnet/ifc-converter.csproj
@@ -10,7 +10,7 @@
-
+
diff --git a/packages/frontend-2/components/automate/runs/StatusBadge.vue b/packages/frontend-2/components/automate/runs/StatusBadge.vue
index 3e4b2084d..c14e12ef4 100644
--- a/packages/frontend-2/components/automate/runs/StatusBadge.vue
+++ b/packages/frontend-2/components/automate/runs/StatusBadge.vue
@@ -1,5 +1,9 @@
-
+
{{ run.status.toUpperCase() }}
diff --git a/packages/frontend-2/components/form/select/ServerRoles.vue b/packages/frontend-2/components/form/select/ServerRoles.vue
index 25028ebd8..9aa4c2bce 100644
--- a/packages/frontend-2/components/form/select/ServerRoles.vue
+++ b/packages/frontend-2/components/form/select/ServerRoles.vue
@@ -7,7 +7,7 @@
:disabled-item-tooltip="
!allowGuest ? 'The Guest role isn\'t enabled on the server' : ''
"
- name="serverRoles"
+ :name="name ?? 'serverRoles'"
label="Role"
:show-label="showLabel"
class="min-w-[110px]"
@@ -75,7 +75,8 @@ const props = defineProps({
allowAdmin: Boolean,
allowArchived: Boolean,
fullyControlValue: Boolean,
- showLabel: Boolean
+ showLabel: Boolean,
+ name: String
})
const elementToWatchForChanges = ref(null as Nullable)
diff --git a/packages/frontend-2/components/global/icon/ViewModes.vue b/packages/frontend-2/components/global/icon/ViewModes.vue
new file mode 100644
index 000000000..1de72d6e3
--- /dev/null
+++ b/packages/frontend-2/components/global/icon/ViewModes.vue
@@ -0,0 +1,45 @@
+
+
+
diff --git a/packages/frontend-2/components/header/NavUserMenu.vue b/packages/frontend-2/components/header/NavUserMenu.vue
index 04a59f813..4083b7035 100644
--- a/packages/frontend-2/components/header/NavUserMenu.vue
+++ b/packages/frontend-2/components/header/NavUserMenu.vue
@@ -124,7 +124,7 @@
-
+
+
+ Invite to Speckle
+
+
+
+
diff --git a/packages/frontend-2/components/onboarding/checklist/v1.vue b/packages/frontend-2/components/onboarding/checklist/v1.vue
index 0dd619fb6..73e647a22 100644
--- a/packages/frontend-2/components/onboarding/checklist/v1.vue
+++ b/packages/frontend-2/components/onboarding/checklist/v1.vue
@@ -213,7 +213,7 @@
>
Your first upload
- (!v ? markComplete(3) : '')"
/>
diff --git a/packages/frontend-2/components/project/page/settings/general/block/Delete/Delete.vue b/packages/frontend-2/components/project/page/settings/general/block/Delete/Delete.vue
index a71df99ef..195209419 100644
--- a/packages/frontend-2/components/project/page/settings/general/block/Delete/Delete.vue
+++ b/packages/frontend-2/components/project/page/settings/general/block/Delete/Delete.vue
@@ -39,9 +39,6 @@ graphql(`
workspace {
slug
}
- automations(limit: 0) {
- totalCount
- }
}
`)
diff --git a/packages/frontend-2/components/project/page/settings/general/block/Delete/Dialog.vue b/packages/frontend-2/components/project/page/settings/general/block/Delete/Dialog.vue
index 7edc0d420..30859528b 100644
--- a/packages/frontend-2/components/project/page/settings/general/block/Delete/Dialog.vue
+++ b/packages/frontend-2/components/project/page/settings/general/block/Delete/Dialog.vue
@@ -7,10 +7,6 @@
delete “{{ project.name }}”
and all its contents, including
{{ project.models.totalCount }} {{ modelText }}
-
- and
- {{ project.automations.totalCount }} {{ automationText }}
-
and
{{ project.commentThreads.totalCount }} {{ discussionText }}
@@ -61,9 +57,6 @@ const modelText = computed(() =>
const discussionText = computed(() =>
props.project.commentThreads.totalCount === 1 ? 'discussion' : 'discussions'
)
-const automationText = computed(() =>
- props.project.automations.totalCount === 1 ? 'automation' : 'automations'
-)
const dialogButtons = computed(() => [
{
diff --git a/packages/frontend-2/components/settings/server/ActiveUsers.vue b/packages/frontend-2/components/settings/server/ActiveUsers.vue
index 3d9150edc..da61c5a23 100644
--- a/packages/frontend-2/components/settings/server/ActiveUsers.vue
+++ b/packages/frontend-2/components/settings/server/ActiveUsers.vue
@@ -98,7 +98,7 @@
:user="userToModify"
/>
-
+
diff --git a/packages/frontend-2/components/settings/server/PendingInvitations.vue b/packages/frontend-2/components/settings/server/PendingInvitations.vue
index 2644c0156..844e2801b 100644
--- a/packages/frontend-2/components/settings/server/PendingInvitations.vue
+++ b/packages/frontend-2/components/settings/server/PendingInvitations.vue
@@ -70,7 +70,7 @@
@infinite="onInfiniteLoad"
/>
-
+
diff --git a/packages/frontend-2/components/settings/server/user/InviteDialog.vue b/packages/frontend-2/components/settings/server/user/InviteDialog.vue
deleted file mode 100644
index b8e38bf71..000000000
--- a/packages/frontend-2/components/settings/server/user/InviteDialog.vue
+++ /dev/null
@@ -1,136 +0,0 @@
-
-
- Get your colleagues in!
-
-
-
-
diff --git a/packages/frontend-2/components/settings/workspaces/Billing.vue b/packages/frontend-2/components/settings/workspaces/Billing.vue
index 73074c6b6..dbd2838ac 100644
--- a/packages/frontend-2/components/settings/workspaces/Billing.vue
+++ b/packages/frontend-2/components/settings/workspaces/Billing.vue
@@ -49,30 +49,38 @@
-
- {{
- statusIsTrial
- ? 'Expected bill'
- : subscription?.billingInterval === BillingInterval.Yearly
- ? 'Annual bill'
- : 'Monthly bill'
- }}
-
-
- {{ billValue }} per
- {{
- subscription?.billingInterval === BillingInterval.Yearly
- ? 'year'
- : 'month'
- }}
-
-
- {{ billDescription }}
-
-
+
+
+ {{
+ statusIsTrial
+ ? 'Expected bill'
+ : subscription?.billingInterval === BillingInterval.Yearly
+ ? 'Annual bill'
+ : 'Monthly bill'
+ }}
+
+
+ {{ billValue }} per
+ {{
+ subscription?.billingInterval === BillingInterval.Yearly
+ ? 'year'
+ : 'month'
+ }}
+
+
+ {{ billDescription }}
+
+
+
+
+ Expected bill
+
+ {{ isAcademiaPlan ? 'Free' : 'Not applicable' }}
+
+
@@ -123,6 +131,7 @@
class="pt-4"
/>
currentPlan.value?.name === WorkspacePlans.Academia
+)
const isPurchasablePlan = computed(() => isPaidPlan(currentPlan.value?.name))
const seatPrice = computed(() =>
currentPlan.value && subscription.value
diff --git a/packages/frontend-2/components/singleton/ToastManager.vue b/packages/frontend-2/components/singleton/ToastManager.vue
index 43499b8f7..616aea412 100644
--- a/packages/frontend-2/components/singleton/ToastManager.vue
+++ b/packages/frontend-2/components/singleton/ToastManager.vue
@@ -1,6 +1,6 @@
-
+
@@ -8,13 +8,13 @@
import { useGlobalToastManager } from '~~/lib/common/composables/toast'
import { GlobalToastRenderer } from '@speckle/ui-components'
-const { currentNotification, dismiss } = useGlobalToastManager()
+const { currentNotifications, dismissAll, dismiss } = useGlobalToastManager()
-const notification = computed({
- get: () => currentNotification.value,
+const notifications = computed({
+ get: () => currentNotifications.value,
set: (newVal) => {
if (!newVal) {
- dismiss()
+ dismissAll()
}
}
})
diff --git a/packages/frontend-2/components/viewer/Controls.vue b/packages/frontend-2/components/viewer/Controls.vue
index 7953afa27..4e483ae3e 100644
--- a/packages/frontend-2/components/viewer/Controls.vue
+++ b/packages/frontend-2/components/viewer/Controls.vue
@@ -13,27 +13,27 @@
>
@@ -42,8 +42,8 @@
@@ -58,8 +58,8 @@
@@ -67,27 +67,37 @@
+
+ toggleActiveControl(value ? 'viewModes' : 'none')"
+ />
-
+ toggleActiveControl(value ? 'views' : 'none')"
+ />
@@ -96,14 +106,15 @@
toggleActiveControl(value ? 'sun' : 'none')"
/>
-
-
+ toggleActiveControl(value ? 'explode' : 'none')"
+ />
@@ -135,9 +149,9 @@
-
+
-
+
-
+
-
+
-
+
-
+
@@ -258,17 +272,12 @@ import { isNonNullable, type Nullable } from '@speckle/shared'
import {
useCameraUtilities,
useSectionBoxUtilities,
- useMeasurementUtilities
+ useMeasurementUtilities,
+ useViewerShortcuts
} from '~~/lib/viewer/composables/ui'
-import {
- onKeyboardShortcut,
- ModifierKeys,
- getKeyboardShortcutTitle
-} from '@speckle/ui-components'
import {
useInjectedViewerLoadedResources,
- useInjectedViewerInterfaceState,
- useInjectedViewerState
+ useInjectedViewerInterfaceState
} from '~~/lib/viewer/composables/setup'
import { useMixpanel } from '~~/lib/core/composables/mp'
import { useIsSmallerOrEqualThanBreakpoint } from '~~/composables/browser'
@@ -283,17 +292,27 @@ import {
import { useFunctionRunsStatusSummary } from '~/lib/automate/composables/runStatus'
import { TailwindBreakpoints } from '~~/lib/common/helpers/tailwind'
-const isGendoEnabled = useIsGendoModuleEnabled()
+type ActivePanel =
+ | 'none'
+ | 'models'
+ | 'explorer'
+ | 'discussions'
+ | 'automate'
+ | 'measurements'
+ | 'gendo'
+ | 'mobileOverflow'
-enum ViewerKeyboardActions {
- ToggleModels = 'ToggleModels',
- ToggleExplorer = 'ToggleExplorer',
- ToggleDiscussions = 'ToggleDiscussions',
- ToggleMeasurements = 'ToggleMeasurements',
- ToggleProjection = 'ToggleProjection',
- ToggleSectionBox = 'ToggleSectionBox',
- ZoomExtentsOrSelection = 'ZoomExtentsOrSelection'
-}
+type ActiveControl =
+ | 'none'
+ | 'viewModes'
+ | 'views'
+ | 'sun'
+ | 'projection'
+ | 'sectionBox'
+ | 'explode'
+ | 'settings'
+
+const isGendoEnabled = useIsGendoModuleEnabled()
const width = ref(360)
const scrollableControlsContainer = ref(null as Nullable
)
@@ -336,17 +355,6 @@ if (import.meta.client) {
})
}
-type ActiveControl =
- | 'none'
- | 'models'
- | 'explorer'
- | 'filters'
- | 'discussions'
- | 'automate'
- | 'measurements'
- | 'mobileOverflow'
- | 'gendo'
-
const { resourceItems, modelsAndVersionIds } = useInjectedViewerLoadedResources()
const {
resetSectionBox,
@@ -364,8 +372,11 @@ const {
toggleProjection,
camera: { isOrthoProjection }
} = useCameraUtilities()
+const { registerShortcuts, getShortcutDisplayText, shortcuts } = useViewerShortcuts()
-const { ui } = useInjectedViewerState()
+const {
+ diff: { enabled }
+} = useInjectedViewerInterfaceState()
const breakpoints = useBreakpoints(TailwindBreakpoints)
const isMobile = breakpoints.smaller('sm')
@@ -389,100 +400,47 @@ const { summary } = useFunctionRunsStatusSummary({
const openAddModel = ref(false)
-const activeControl = ref('models')
-
-const {
- diff: { enabled }
-} = useInjectedViewerInterfaceState()
-
-const map: Record = {
- [ViewerKeyboardActions.ToggleModels]: [[ModifierKeys.Shift], 'M'],
- [ViewerKeyboardActions.ToggleExplorer]: [[ModifierKeys.Shift], 'E'],
- [ViewerKeyboardActions.ToggleDiscussions]: [[ModifierKeys.Shift], 'T'],
- [ViewerKeyboardActions.ToggleMeasurements]: [[ModifierKeys.Shift], 'R'],
- [ViewerKeyboardActions.ToggleProjection]: [[ModifierKeys.Shift], 'P'],
- [ViewerKeyboardActions.ToggleSectionBox]: [[ModifierKeys.Shift], 'B'],
- [ViewerKeyboardActions.ZoomExtentsOrSelection]: [[ModifierKeys.Shift], 'space']
-}
-
-const getShortcutTitle = (action: ViewerKeyboardActions) =>
- `(${getKeyboardShortcutTitle([...map[action][0], map[action][1]])})`
-
-const modelsShortcut = ref(
- `Models ${getShortcutTitle(ViewerKeyboardActions.ToggleModels)}`
-)
-const explorerShortcut = ref(
- `Scene explorer ${getShortcutTitle(ViewerKeyboardActions.ToggleExplorer)}`
-)
-const discussionsShortcut = ref(
- `Discussions ${getShortcutTitle(ViewerKeyboardActions.ToggleDiscussions)}`
-)
-const zoomExtentsShortcut = ref(
- `Fit to screen ${getShortcutTitle(ViewerKeyboardActions.ZoomExtentsOrSelection)}`
-)
-const projectionShortcut = ref(
- `Projection ${getShortcutTitle(ViewerKeyboardActions.ToggleProjection)}`
-)
-const sectionBoxShortcut = ref(
- `Section box ${getShortcutTitle(ViewerKeyboardActions.ToggleSectionBox)}`
-)
-const measureShortcut = ref(
- `Measure mode ${getShortcutTitle(ViewerKeyboardActions.ToggleMeasurements)}`
-)
-
-const isTypingComment = computed(() => {
- const isNewThreadEditorOpen = ui.threads.openThread.newThreadEditor.value
- const isExistingThreadEditorOpen = !!ui.threads.openThread.thread.value
- return isNewThreadEditorOpen || isExistingThreadEditorOpen
-})
-
-const handleKeyboardAction = (action: ViewerKeyboardActions) => {
- if (isTypingComment.value) {
- return
- }
- switch (action) {
- case ViewerKeyboardActions.ToggleModels:
- toggleActiveControl('models')
- break
- case ViewerKeyboardActions.ToggleExplorer:
- toggleActiveControl('explorer')
- break
- case ViewerKeyboardActions.ToggleDiscussions:
- toggleActiveControl('discussions')
- break
- case ViewerKeyboardActions.ToggleMeasurements:
- toggleMeasurements()
- break
- case ViewerKeyboardActions.ToggleProjection:
- trackAndtoggleProjection()
- break
- case ViewerKeyboardActions.ToggleSectionBox:
- toggleSectionBox()
- break
- case ViewerKeyboardActions.ZoomExtentsOrSelection:
- trackAndzoomExtentsOrSelection()
- break
- }
-}
-
-Object.entries(map).forEach(([actionKey, [modifiers, key]]) => {
- const action = actionKey as ViewerKeyboardActions
- onKeyboardShortcut(modifiers, key, () => handleKeyboardAction(action))
-})
+const activeControl = ref('none')
+const activePanel = ref('none')
const { isSmallerOrEqualSm } = useIsSmallerOrEqualThanBreakpoint()
-const toggleActiveControl = (control: ActiveControl) => {
- const isMeasurementsActive = activeControl.value === 'measurements'
- if (isMeasurementsActive && control !== 'measurements') {
+const toggleActivePanel = (panel: ActivePanel) => {
+ const isMeasurementsActive = activePanel.value === 'measurements'
+ if (isMeasurementsActive && panel !== 'measurements') {
enableMeasurements(false)
}
+
+ // Special handling for mobile overflow
+ if (isSmallerOrEqualSm.value && panel === 'mobileOverflow') {
+ activePanel.value =
+ activePanel.value === 'mobileOverflow' ? 'none' : 'mobileOverflow'
+ } else {
+ activePanel.value = activePanel.value === panel ? 'none' : panel
+ }
+}
+
+const toggleActiveControl = (control: ActiveControl) => {
activeControl.value = activeControl.value === control ? 'none' : control
}
+registerShortcuts({
+ ToggleModels: () => toggleActivePanel('models'),
+ ToggleExplorer: () => toggleActivePanel('explorer'),
+ ToggleDiscussions: () => toggleActivePanel('discussions'),
+ ToggleMeasurements: () => toggleMeasurements(),
+ ToggleProjection: () => trackAndtoggleProjection(),
+ ToggleSectionBox: () => toggleSectionBox(),
+ ZoomExtentsOrSelection: () => trackAndzoomExtentsOrSelection()
+})
+
const mp = useMixpanel()
watch(activeControl, (newVal) => {
- mp.track('Viewer Action', { type: 'action', name: 'controls-toggle', action: newVal })
+ mp.track('Viewer Action', {
+ type: 'action',
+ name: 'controls-toggle',
+ action: newVal
+ })
})
const trackAndzoomExtentsOrSelection = () => {
@@ -506,30 +464,32 @@ const scrollControlsToBottom = () => {
}
const toggleMeasurements = () => {
- const isMeasurementsActive = activeControl.value === 'measurements'
+ const isMeasurementsActive = activePanel.value === 'measurements'
enableMeasurements(!isMeasurementsActive)
- activeControl.value = isMeasurementsActive ? 'none' : 'measurements'
+ activePanel.value = isMeasurementsActive ? 'none' : 'measurements'
}
-onMounted(() => {
- activeControl.value = isSmallerOrEqualSm.value ? 'none' : 'models'
-})
-
onKeyStroke('Escape', () => {
const isActiveMeasurement = getActiveMeasurement()
if (isActiveMeasurement) {
removeMeasurement()
} else {
- if (activeControl.value === 'measurements') {
+ if (activePanel.value === 'measurements') {
toggleMeasurements()
}
+ activePanel.value = 'none'
activeControl.value = 'none'
}
})
+onMounted(() => {
+ // Set initial panel state after component is mounted
+ activePanel.value = isSmallerOrEqualSm.value ? 'none' : 'models'
+})
+
watch(isSmallerOrEqualSm, (newVal) => {
- activeControl.value = newVal ? 'none' : 'models'
+ activePanel.value = newVal ? 'none' : 'models'
})
watch(isSectionBoxEnabled, (val) => {
@@ -547,4 +507,25 @@ watch(isSectionBoxVisible, (val) => {
status: val
})
})
+
+const viewModesOpen = computed({
+ get: () => activeControl.value === 'viewModes',
+ set: (value) => {
+ activeControl.value = value ? 'viewModes' : 'none'
+ }
+})
+
+const viewsOpen = computed({
+ get: () => activeControl.value === 'views',
+ set: (value) => {
+ activeControl.value = value ? 'views' : 'none'
+ }
+})
+
+const explodeOpen = computed({
+ get: () => activeControl.value === 'explode',
+ set: (value) => {
+ activeControl.value = value ? 'explode' : 'none'
+ }
+})
diff --git a/packages/frontend-2/components/viewer/explode/Menu.vue b/packages/frontend-2/components/viewer/explode/Menu.vue
index 494ddc143..c8112352f 100644
--- a/packages/frontend-2/components/viewer/explode/Menu.vue
+++ b/packages/frontend-2/components/viewer/explode/Menu.vue
@@ -1,53 +1,46 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/packages/frontend-2/components/viewer/menu/Menu.vue b/packages/frontend-2/components/viewer/menu/Menu.vue
new file mode 100644
index 000000000..cc8587f97
--- /dev/null
+++ b/packages/frontend-2/components/viewer/menu/Menu.vue
@@ -0,0 +1,48 @@
+
+
+
+
+
diff --git a/packages/frontend-2/components/viewer/sun/Menu.vue b/packages/frontend-2/components/viewer/sun/Menu.vue
index a7029b344..8ae8aed37 100644
--- a/packages/frontend-2/components/viewer/sun/Menu.vue
+++ b/packages/frontend-2/components/viewer/sun/Menu.vue
@@ -1,105 +1,111 @@
-
+
+
+
+
+
+
+
+
+ Not available in current view mode.
+
+
+
+
+
+ Sun shadows
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/frontend-2/components/viewer/views/Menu.vue b/packages/frontend-2/components/viewer/views/Menu.vue
index 3280bebad..5c6bbfdcb 100644
--- a/packages/frontend-2/components/viewer/views/Menu.vue
+++ b/packages/frontend-2/components/viewer/views/Menu.vue
@@ -1,52 +1,48 @@
+
-
-
+
+
-
-
+
+
+
diff --git a/packages/frontend-2/lib/automate/composables/runs.ts b/packages/frontend-2/lib/automate/composables/runs.ts
index 68189ba89..4c7db0119 100644
--- a/packages/frontend-2/lib/automate/composables/runs.ts
+++ b/packages/frontend-2/lib/automate/composables/runs.ts
@@ -94,19 +94,19 @@ export const useAutomationRunDetailsFns = () => {
switch (status) {
case AutomateRunStatus.Pending:
case AutomateRunStatus.Initializing:
- classParts.push('bg-warning-lighter text-warning-darker')
+ classParts.push('bg-warning-lighter')
break
case AutomateRunStatus.Running:
- classParts.push('bg-info-lighter text-info-darker')
+ classParts.push('bg-info-lighter')
break
case AutomateRunStatus.Failed:
case AutomateRunStatus.Exception:
case AutomateRunStatus.Canceled:
case AutomateRunStatus.Timeout:
- classParts.push('bg-danger-lighter text-danger-darker')
+ classParts.push('bg-danger-lighter')
break
case AutomateRunStatus.Succeeded:
- classParts.push('bg-success-lighter text-success-darker')
+ classParts.push('bg-success-lighter')
break
}
diff --git a/packages/frontend-2/lib/billing/composables/actions.ts b/packages/frontend-2/lib/billing/composables/actions.ts
index b8b230d4f..fed3dbd58 100644
--- a/packages/frontend-2/lib/billing/composables/actions.ts
+++ b/packages/frontend-2/lib/billing/composables/actions.ts
@@ -199,12 +199,24 @@ export const useBillingActions = () => {
type: ToastNotificationType.Danger,
title: 'Your payment was canceled'
})
+
+ mixpanel.track('Workspace Upgrade Cancelled', {
+ // eslint-disable-next-line camelcase
+ workspace_id: workspace.id
+ })
} else {
triggerNotification({
type: ToastNotificationType.Success,
title: 'Your workspace plan was successfully updated'
})
+ mixpanel.track('Workspace Upgraded', {
+ plan: workspace.plan?.name,
+ cycle: workspace.subscription?.billingInterval,
+ // eslint-disable-next-line camelcase
+ workspace_id: workspace.id
+ })
+
if (import.meta.server) {
await sendWebhook(defaultZapierWebhookUrl, {
workspaceId: workspace.id,
diff --git a/packages/frontend-2/lib/common/composables/toast.ts b/packages/frontend-2/lib/common/composables/toast.ts
index 657afacac..a2b5911c6 100644
--- a/packages/frontend-2/lib/common/composables/toast.ts
+++ b/packages/frontend-2/lib/common/composables/toast.ts
@@ -1,60 +1,93 @@
-import { useTimeoutFn } from '@vueuse/core'
import type { Optional } from '@speckle/shared'
import type { ToastNotification } from '@speckle/ui-components'
+import { useTimeoutFn } from '@vueuse/core'
import { ToastNotificationType } from '@speckle/ui-components'
import { useSynchronizedCookie } from '~/lib/common/composables/reactiveCookie'
+import { nanoid } from 'nanoid'
/**
* Persisting toast state between reqs and between CSR & SSR loads so that we can trigger
* toasts anywhere and anytime
*/
const useGlobalToastState = () =>
- useSynchronizedCookie>('global-toast-state')
+ useSynchronizedCookie>('global-toast-state')
/**
* Set up a new global toast manager/renderer (don't use this in multiple components that live at the same time)
*/
export function useGlobalToastManager() {
- const stateNotification = useGlobalToastState()
-
- const currentNotification = ref(stateNotification.value)
- const readOnlyNotification = computed(() => currentNotification.value)
-
- const dismiss = () => {
- currentNotification.value = undefined
- stateNotification.value = undefined
+ type Timeout = {
+ id: string
+ stop: () => void
}
- const { start, stop } = useTimeoutFn(() => {
- dismiss()
- }, 4000)
+ const stateNotification = useGlobalToastState()
+
+ const timeouts = ref([])
+ const currentNotifications = ref(
+ Array.isArray(stateNotification.value) ? stateNotification.value : []
+ )
+ const readOnlyNotification = computed(() => currentNotifications.value)
+
+ // Remove a specific notification from the state
+ const removeNotification = (id: string) => {
+ const index = currentNotifications.value.findIndex((n) => n.id === id)
+ if (index !== -1) {
+ currentNotifications.value.splice(index, 1)
+ // Clean up timeout
+ timeouts.value = timeouts.value.filter((t) => t.id !== id)
+ }
+ }
+
+ // Create a timeout for a notification
+ const createTimeout = (notification: ToastNotification) => {
+ const { stop } = useTimeoutFn(() => {
+ if (notification.id) {
+ removeNotification(notification.id)
+ }
+ }, 4000)
+ return stop
+ }
watch(
stateNotification,
(newVal) => {
if (!newVal) return
- if (import.meta.server) {
- currentNotification.value = newVal
- return
+ currentNotifications.value = newVal
+
+ // Create timeout for the new notification
+ const index = currentNotifications.value.length - 1
+ const lastNotification = newVal[index]
+
+ if (lastNotification && !lastNotification.autoClose) {
+ timeouts.value.push({
+ id: lastNotification.id as string,
+ stop: createTimeout(lastNotification)
+ })
}
-
- // First dismiss old notification, then set a new one on next tick
- // this is so that the old one actually disappears from the screen for the user,
- // instead of just having its contents replaced
- dismiss()
-
- nextTick(() => {
- currentNotification.value = newVal
-
- // (re-)init timeout
- stop()
- if (newVal.autoClose !== false) start()
- })
},
{ deep: true, immediate: true }
)
- return { currentNotification: readOnlyNotification, dismiss }
+ // Function to dismiss a specific notification
+ const dismiss = (notification: ToastNotification) => {
+ if (!notification.id) return
+
+ const targetTimeout = timeouts.value.find((t) => t.id === notification.id)
+ if (targetTimeout) {
+ targetTimeout.stop()
+ }
+ removeNotification(notification.id as string)
+ }
+
+ // Dismiss all notifications
+ const dismissAll = () => {
+ timeouts.value.forEach((timeout) => timeout.stop())
+ timeouts.value = []
+ currentNotifications.value = []
+ }
+
+ return { currentNotifications: readOnlyNotification, dismiss, dismissAll }
}
/**
@@ -68,7 +101,11 @@ export function useGlobalToast() {
* Trigger a new toast notification
*/
const triggerNotification = (notification: ToastNotification) => {
- stateNotification.value = notification
+ const newNotification = { ...notification, id: nanoid() }
+
+ stateNotification.value
+ ? stateNotification.value.push(newNotification)
+ : (stateNotification.value = [newNotification])
if (import.meta.server) {
logger.info('Queued SSR toast notification', notification)
diff --git a/packages/frontend-2/lib/common/generated/gql/gql.ts b/packages/frontend-2/lib/common/generated/gql/gql.ts
index 22f9359ce..84941e186 100644
--- a/packages/frontend-2/lib/common/generated/gql/gql.ts
+++ b/packages/frontend-2/lib/common/generated/gql/gql.ts
@@ -83,7 +83,7 @@ const documents = {
"\n query ProjectPageSettingsCollaboratorsWorkspace($workspaceId: String!) {\n workspace(id: $workspaceId) {\n ...ProjectPageTeamInternals_Workspace\n }\n }\n": types.ProjectPageSettingsCollaboratorsWorkspaceDocument,
"\n query ProjectPageSettingsGeneral($projectId: String!) {\n project(id: $projectId) {\n id\n role\n ...ProjectPageSettingsGeneralBlockProjectInfo_Project\n ...ProjectPageSettingsGeneralBlockAccess_Project\n ...ProjectPageSettingsGeneralBlockDiscussions_Project\n ...ProjectPageSettingsGeneralBlockLeave_Project\n ...ProjectPageSettingsGeneralBlockDelete_Project\n ...ProjectPageTeamInternals_Project\n }\n }\n": types.ProjectPageSettingsGeneralDocument,
"\n fragment ProjectPageSettingsGeneralBlockAccess_Project on Project {\n id\n visibility\n }\n": types.ProjectPageSettingsGeneralBlockAccess_ProjectFragmentDoc,
- "\n fragment ProjectPageSettingsGeneralBlockDelete_Project on Project {\n id\n name\n role\n models(limit: 0) {\n totalCount\n }\n commentThreads(limit: 0) {\n totalCount\n }\n workspace {\n slug\n }\n automations(limit: 0) {\n totalCount\n }\n }\n": types.ProjectPageSettingsGeneralBlockDelete_ProjectFragmentDoc,
+ "\n fragment ProjectPageSettingsGeneralBlockDelete_Project on Project {\n id\n name\n role\n models(limit: 0) {\n totalCount\n }\n commentThreads(limit: 0) {\n totalCount\n }\n workspace {\n slug\n }\n }\n": types.ProjectPageSettingsGeneralBlockDelete_ProjectFragmentDoc,
"\n fragment ProjectPageSettingsGeneralBlockDiscussions_Project on Project {\n id\n visibility\n allowPublicComments\n }\n": types.ProjectPageSettingsGeneralBlockDiscussions_ProjectFragmentDoc,
"\n fragment ProjectPageSettingsGeneralBlockLeave_Project on Project {\n id\n name\n role\n team {\n role\n user {\n ...LimitedUserAvatar\n role\n }\n }\n workspace {\n id\n }\n }\n": types.ProjectPageSettingsGeneralBlockLeave_ProjectFragmentDoc,
"\n fragment ProjectPageSettingsGeneralBlockProjectInfo_Project on Project {\n id\n role\n name\n description\n }\n": types.ProjectPageSettingsGeneralBlockProjectInfo_ProjectFragmentDoc,
@@ -674,7 +674,7 @@ export function graphql(source: "\n fragment ProjectPageSettingsGeneralBlockAcc
/**
* 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 ProjectPageSettingsGeneralBlockDelete_Project on Project {\n id\n name\n role\n models(limit: 0) {\n totalCount\n }\n commentThreads(limit: 0) {\n totalCount\n }\n workspace {\n slug\n }\n automations(limit: 0) {\n totalCount\n }\n }\n"): (typeof documents)["\n fragment ProjectPageSettingsGeneralBlockDelete_Project on Project {\n id\n name\n role\n models(limit: 0) {\n totalCount\n }\n commentThreads(limit: 0) {\n totalCount\n }\n workspace {\n slug\n }\n automations(limit: 0) {\n totalCount\n }\n }\n"];
+export function graphql(source: "\n fragment ProjectPageSettingsGeneralBlockDelete_Project on Project {\n id\n name\n role\n models(limit: 0) {\n totalCount\n }\n commentThreads(limit: 0) {\n totalCount\n }\n workspace {\n slug\n }\n }\n"): (typeof documents)["\n fragment ProjectPageSettingsGeneralBlockDelete_Project on Project {\n id\n name\n role\n models(limit: 0) {\n totalCount\n }\n commentThreads(limit: 0) {\n totalCount\n }\n workspace {\n slug\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
diff --git a/packages/frontend-2/lib/common/generated/gql/graphql.ts b/packages/frontend-2/lib/common/generated/gql/graphql.ts
index 848196eb2..b44de5b36 100644
--- a/packages/frontend-2/lib/common/generated/gql/graphql.ts
+++ b/packages/frontend-2/lib/common/generated/gql/graphql.ts
@@ -4757,11 +4757,11 @@ export type ProjectPageSettingsGeneralQueryVariables = Exact<{
}>;
-export type ProjectPageSettingsGeneralQuery = { __typename?: 'Query', project: { __typename?: 'Project', id: 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 } }>, workspace?: { __typename?: 'Workspace', id: string, slug: string } | null, models: { __typename?: 'ModelCollection', totalCount: number }, commentThreads: { __typename?: 'ProjectCommentCollection', totalCount: number }, automations: { __typename?: 'AutomationCollection', totalCount: number }, invitedTeam?: Array<{ __typename?: 'PendingStreamCollaborator', id: string, title: string, role: string, inviteId: string, user?: { __typename?: 'LimitedUser', role?: string | null, id: string, name: string, avatar?: string | null } | null }> | null } };
+export type ProjectPageSettingsGeneralQuery = { __typename?: 'Query', project: { __typename?: 'Project', id: 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 } }>, workspace?: { __typename?: 'Workspace', id: string, slug: string } | null, models: { __typename?: 'ModelCollection', totalCount: number }, commentThreads: { __typename?: 'ProjectCommentCollection', totalCount: number }, invitedTeam?: Array<{ __typename?: 'PendingStreamCollaborator', id: string, title: string, role: string, inviteId: string, user?: { __typename?: 'LimitedUser', role?: string | null, id: string, name: string, avatar?: string | null } | null }> | null } };
export type ProjectPageSettingsGeneralBlockAccess_ProjectFragment = { __typename?: 'Project', id: string, visibility: ProjectVisibility };
-export type ProjectPageSettingsGeneralBlockDelete_ProjectFragment = { __typename?: 'Project', id: string, name: string, role?: string | null, models: { __typename?: 'ModelCollection', totalCount: number }, commentThreads: { __typename?: 'ProjectCommentCollection', totalCount: number }, workspace?: { __typename?: 'Workspace', slug: string } | null, automations: { __typename?: 'AutomationCollection', totalCount: number } };
+export type ProjectPageSettingsGeneralBlockDelete_ProjectFragment = { __typename?: 'Project', id: string, name: string, role?: string | null, models: { __typename?: 'ModelCollection', totalCount: number }, commentThreads: { __typename?: 'ProjectCommentCollection', totalCount: number }, workspace?: { __typename?: 'Workspace', slug: string } | null };
export type ProjectPageSettingsGeneralBlockDiscussions_ProjectFragment = { __typename?: 'Project', id: string, visibility: ProjectVisibility, allowPublicComments: boolean };
@@ -6411,7 +6411,7 @@ export const ProjectPageModelsActionsFragmentDoc = {"kind":"Document","definitio
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":"Field","name":{"kind":"Name","value":"automationsStatus"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatus_TriggeredAutomationsStatus"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FunctionRunStatusForSummary"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunctionRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TriggeredAutomationsStatusSummary"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automationRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"FunctionRunStatusForSummary"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogFunctionRun_AutomateFunctionRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunctionRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"results"}},{"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":"function"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomationsStatusOrderedRuns_AutomationRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogRunsRows_AutomateRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogFunctionRun_AutomateFunctionRun"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomationsStatusOrderedRuns_AutomationRun"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialog_TriggeredAutomationsStatus"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automationRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogRunsRows_AutomateRun"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PendingFileUpload"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FileUpload"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"modelName"}},{"kind":"Field","name":{"kind":"Name","value":"convertedStatus"}},{"kind":"Field","name":{"kind":"Name","value":"convertedMessage"}},{"kind":"Field","name":{"kind":"Name","value":"uploadDate"}},{"kind":"Field","name":{"kind":"Name","value":"convertedLastUpdate"}},{"kind":"Field","name":{"kind":"Name","value":"fileType"}},{"kind":"Field","name":{"kind":"Name","value":"fileName"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardRenameDialog"},"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":"description"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardDeleteDialog"},"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":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsActions"},"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":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatus_TriggeredAutomationsStatus"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"TriggeredAutomationsStatusSummary"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialog_TriggeredAutomationsStatus"}}]}}]} as unknown as DocumentNode;
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"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PendingFileUpload"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FileUpload"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"modelName"}},{"kind":"Field","name":{"kind":"Name","value":"convertedStatus"}},{"kind":"Field","name":{"kind":"Name","value":"convertedMessage"}},{"kind":"Field","name":{"kind":"Name","value":"uploadDate"}},{"kind":"Field","name":{"kind":"Name","value":"convertedLastUpdate"}},{"kind":"Field","name":{"kind":"Name","value":"fileType"}},{"kind":"Field","name":{"kind":"Name","value":"fileName"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardRenameDialog"},"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":"description"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardDeleteDialog"},"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":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsActions"},"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":"FragmentDefinition","name":{"kind":"Name","value":"FunctionRunStatusForSummary"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunctionRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TriggeredAutomationsStatusSummary"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automationRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"FunctionRunStatusForSummary"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogFunctionRun_AutomateFunctionRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunctionRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"results"}},{"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":"function"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomationsStatusOrderedRuns_AutomationRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogRunsRows_AutomateRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogFunctionRun_AutomateFunctionRun"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomationsStatusOrderedRuns_AutomationRun"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialog_TriggeredAutomationsStatus"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automationRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogRunsRows_AutomateRun"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatus_TriggeredAutomationsStatus"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"TriggeredAutomationsStatusSummary"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialog_TriggeredAutomationsStatus"}}]}},{"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":"Field","name":{"kind":"Name","value":"automationsStatus"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatus_TriggeredAutomationsStatus"}}]}}]}}]} as unknown as DocumentNode;
export const ProjectPageSettingsGeneralBlockAccess_ProjectFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageSettingsGeneralBlockAccess_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":"visibility"}}]}}]} as unknown as DocumentNode;
-export const ProjectPageSettingsGeneralBlockDelete_ProjectFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageSettingsGeneralBlockDelete_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":"role"}},{"kind":"Field","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"}}]}},{"kind":"Field","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":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"slug"}}]}},{"kind":"Field","name":{"kind":"Name","value":"automations"},"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;
+export const ProjectPageSettingsGeneralBlockDelete_ProjectFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageSettingsGeneralBlockDelete_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":"role"}},{"kind":"Field","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"}}]}},{"kind":"Field","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":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"slug"}}]}}]}}]} as unknown as DocumentNode;
export const ProjectPageSettingsGeneralBlockDiscussions_ProjectFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageSettingsGeneralBlockDiscussions_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":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"allowPublicComments"}}]}}]} as unknown as DocumentNode;
export const ProjectPageSettingsGeneralBlockLeave_ProjectFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageSettingsGeneralBlockLeave_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":"role"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LimitedUserAvatar"},"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;
export const ProjectPageSettingsGeneralBlockProjectInfo_ProjectFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageSettingsGeneralBlockProjectInfo_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":"role"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}}]} as unknown as DocumentNode;
@@ -6525,7 +6525,7 @@ export const RequestVerificationDocument = {"kind":"Document","definitions":[{"k
export const AutomationCreateDialogFunctionsSearchDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AutomationCreateDialogFunctionsSearch"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"search"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}},"defaultValue":{"kind":"NullValue"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspace"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"automateFunctions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"20"}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"search"},"value":{"kind":"Variable","name":{"kind":"Name","value":"search"}}}]}},{"kind":"Argument","name":{"kind":"Name","value":"cursor"},"value":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}}}],"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":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateAutomationCreateDialog_AutomateFunction"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomationsFunctionsCard_AutomateFunction"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"isFeatured"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"repo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"owner"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateAutomationCreateDialogFunctionParametersStep_AutomateFunction"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"releases"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"inputSchema"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateAutomationCreateDialog_AutomateFunction"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomationsFunctionsCard_AutomateFunction"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateAutomationCreateDialogFunctionParametersStep_AutomateFunction"}}]}}]} as unknown as DocumentNode;
export const ProjectPageSettingsCollaboratorsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectPageSettingsCollaborators"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageTeamInternals_Project"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageInviteDialog_Project"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LimitedUserAvatar"},"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"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageTeamInternals_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":"role"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"inviteId"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageInviteDialog_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":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"defaultProjectRole"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"role"}},{"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":"bio"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"verified"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}}]}}]}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageTeamInternals_Project"}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domainBasedMembershipProtectionEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"domains"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"domain"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode;
export const ProjectPageSettingsCollaboratorsWorkspaceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectPageSettingsCollaboratorsWorkspace"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspace"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageTeamInternals_Workspace"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageTeamInternals_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]}}]} as unknown as DocumentNode;
-export const ProjectPageSettingsGeneralDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectPageSettingsGeneral"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageSettingsGeneralBlockProjectInfo_Project"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageSettingsGeneralBlockAccess_Project"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageSettingsGeneralBlockDiscussions_Project"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageSettingsGeneralBlockLeave_Project"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageSettingsGeneralBlockDelete_Project"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageTeamInternals_Project"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LimitedUserAvatar"},"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"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageSettingsGeneralBlockProjectInfo_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":"role"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageSettingsGeneralBlockAccess_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":"visibility"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageSettingsGeneralBlockDiscussions_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":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"allowPublicComments"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageSettingsGeneralBlockLeave_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":"role"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageSettingsGeneralBlockDelete_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":"role"}},{"kind":"Field","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"}}]}},{"kind":"Field","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":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"slug"}}]}},{"kind":"Field","name":{"kind":"Name","value":"automations"},"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":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageTeamInternals_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":"role"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"inviteId"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}}]}}]}}]} as unknown as DocumentNode;
+export const ProjectPageSettingsGeneralDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectPageSettingsGeneral"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageSettingsGeneralBlockProjectInfo_Project"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageSettingsGeneralBlockAccess_Project"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageSettingsGeneralBlockDiscussions_Project"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageSettingsGeneralBlockLeave_Project"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageSettingsGeneralBlockDelete_Project"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageTeamInternals_Project"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LimitedUserAvatar"},"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"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageSettingsGeneralBlockProjectInfo_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":"role"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageSettingsGeneralBlockAccess_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":"visibility"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageSettingsGeneralBlockDiscussions_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":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"allowPublicComments"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageSettingsGeneralBlockLeave_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":"role"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageSettingsGeneralBlockDelete_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":"role"}},{"kind":"Field","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"}}]}},{"kind":"Field","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":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"slug"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageTeamInternals_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":"role"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"inviteId"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}}]}}]}}]} as unknown as DocumentNode;
export const OnUserProjectsUpdateDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"subscription","name":{"kind":"Name","value":"OnUserProjectsUpdate"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userProjectsUpdated"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"project"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectDashboardItem"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsPageTeamDialogManagePermissions_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":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsModelPageEmbed_Project"},"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":"ProjectsPageTeamDialogManagePermissions_Project"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsActions_Project"},"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":"ProjectsModelPageEmbed_Project"}}]}},{"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"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsActions_Project"}}]}},{"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":"id"}},{"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"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PendingFileUpload"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FileUpload"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"modelName"}},{"kind":"Field","name":{"kind":"Name","value":"convertedStatus"}},{"kind":"Field","name":{"kind":"Name","value":"convertedMessage"}},{"kind":"Field","name":{"kind":"Name","value":"uploadDate"}},{"kind":"Field","name":{"kind":"Name","value":"convertedLastUpdate"}},{"kind":"Field","name":{"kind":"Name","value":"fileType"}},{"kind":"Field","name":{"kind":"Name","value":"fileName"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardRenameDialog"},"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":"description"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardDeleteDialog"},"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":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsActions"},"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":"FragmentDefinition","name":{"kind":"Name","value":"FunctionRunStatusForSummary"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunctionRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TriggeredAutomationsStatusSummary"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automationRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"FunctionRunStatusForSummary"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogFunctionRun_AutomateFunctionRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunctionRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"results"}},{"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":"function"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomationsStatusOrderedRuns_AutomationRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogRunsRows_AutomateRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogFunctionRun_AutomateFunctionRun"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomationsStatusOrderedRuns_AutomationRun"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialog_TriggeredAutomationsStatus"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automationRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogRunsRows_AutomateRun"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatus_TriggeredAutomationsStatus"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"TriggeredAutomationsStatusSummary"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialog_TriggeredAutomationsStatus"}}]}},{"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":"Field","name":{"kind":"Name","value":"automationsStatus"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatus_TriggeredAutomationsStatus"}}]}}]}},{"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"}}],"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":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}}]}},{"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;
export const ProjectsMoveToWorkspaceDialogDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectsMoveToWorkspaceDialog"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsMoveToWorkspaceDialog_User"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceHasCustomDataResidency_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"defaultRegion"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsWorkspaceSelect_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsMoveToWorkspaceDialog_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceHasCustomDataResidency_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsWorkspaceSelect_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsMoveToWorkspaceDialog_User"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsMoveToWorkspaceDialog_Workspace"}}]}}]}}]}}]} as unknown as DocumentNode;
export const SettingsServerRegionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"SettingsServerRegions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serverInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"multiRegion"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"regions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsServerRegionsTable_ServerRegionItem"}}]}},{"kind":"Field","name":{"kind":"Name","value":"availableKeys"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsServerRegionsTable_ServerRegionItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerRegionItem"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}}]} as unknown as DocumentNode;
diff --git a/packages/frontend-2/lib/invites/helpers/constants.ts b/packages/frontend-2/lib/invites/helpers/constants.ts
new file mode 100644
index 000000000..d31c358f6
--- /dev/null
+++ b/packages/frontend-2/lib/invites/helpers/constants.ts
@@ -0,0 +1,8 @@
+import type { InviteServerItem } from '~~/lib/invites/helpers/types'
+import { Roles } from '@speckle/shared'
+
+export const emptyInviteServerItem: InviteServerItem = {
+ email: '',
+ serverRole: Roles.Server.User,
+ project: undefined
+}
diff --git a/packages/frontend-2/lib/invites/helpers/types.ts b/packages/frontend-2/lib/invites/helpers/types.ts
new file mode 100644
index 000000000..aeae0c16a
--- /dev/null
+++ b/packages/frontend-2/lib/invites/helpers/types.ts
@@ -0,0 +1,12 @@
+import type { ServerRoles } from '@speckle/shared'
+import type { FormSelectProjects_ProjectFragment } from '~~/lib/common/generated/gql/graphql'
+
+export type InviteServerItem = {
+ email: string
+ serverRole: ServerRoles
+ project?: FormSelectProjects_ProjectFragment
+}
+
+export interface InviteServerForm {
+ fields: InviteServerItem[]
+}
diff --git a/packages/frontend-2/lib/projects/composables/projectManagement.ts b/packages/frontend-2/lib/projects/composables/projectManagement.ts
index d4520d467..3049df31f 100644
--- a/packages/frontend-2/lib/projects/composables/projectManagement.ts
+++ b/packages/frontend-2/lib/projects/composables/projectManagement.ts
@@ -308,13 +308,19 @@ export function useInviteUserToProject() {
if (err) {
triggerNotification({
type: ToastNotificationType.Danger,
- title: 'Invitation failed',
+ title:
+ input.length > 1
+ ? "Couldn't send invites"
+ : `Coudldn't send invite to ${input[0].email}`,
description: err
})
} else {
triggerNotification({
type: ToastNotificationType.Success,
- title: 'Invite successfully sent'
+ title:
+ input.length > 1
+ ? 'Invites successfully send'
+ : `Invite successfully sent to ${input[0].email}`
})
}
diff --git a/packages/frontend-2/lib/server/composables/invites.ts b/packages/frontend-2/lib/server/composables/invites.ts
index c0b73713d..cd18c3222 100644
--- a/packages/frontend-2/lib/server/composables/invites.ts
+++ b/packages/frontend-2/lib/server/composables/invites.ts
@@ -55,13 +55,19 @@ export function useInviteUserToServer() {
if (res?.data?.serverInviteBatchCreate) {
triggerNotification({
type: ToastNotificationType.Success,
- title: `Server invite${finalInput.length > 1 ? 's' : ''} sent`
+ title:
+ finalInput.length > 1
+ ? 'Server invites sent'
+ : `Server invite sent to ${finalInput[0].email}`
})
} else {
const errMsg = getFirstErrorMessage(res?.errors)
triggerNotification({
type: ToastNotificationType.Danger,
- title: `Couldn't send invite${finalInput.length > 1 ? 's' : ''}`,
+ title:
+ finalInput.length > 1
+ ? "Couldn't send invites"
+ : `Couldn't send invite to ${finalInput[0].email}`,
description: errMsg
})
}
diff --git a/packages/frontend-2/lib/viewer/composables/serialization.ts b/packages/frontend-2/lib/viewer/composables/serialization.ts
index 7b3e769f7..5b240f8a1 100644
--- a/packages/frontend-2/lib/viewer/composables/serialization.ts
+++ b/packages/frontend-2/lib/viewer/composables/serialization.ts
@@ -10,7 +10,7 @@ import {
useFilterUtilities,
useSelectionUtilities
} from '~~/lib/viewer/composables/ui'
-import { CameraController } from '@speckle/viewer'
+import { CameraController, ViewMode } from '@speckle/viewer'
import type { NumericPropertyInfo } from '@speckle/viewer'
type SerializedViewerState = SpeckleViewer.ViewerState.SerializedViewerState
@@ -105,6 +105,7 @@ export function useStateSerialization() {
isOrthoProjection: state.ui.camera.isOrthoProjection.value,
zoom: (get(camControls, '_zoom') as number) || 1 // kinda hacky, _zoom is a protected prop
},
+ viewMode: state.ui.viewMode.value,
sectionBox: state.ui.sectionBox.value
? {
min: box.min.toArray(),
@@ -141,7 +142,8 @@ export function useApplySerializedState() {
highlightedObjectIds,
explodeFactor,
lightConfig,
- diff
+ diff,
+ viewMode
},
resources: {
request: { resourceIdString }
@@ -282,6 +284,13 @@ export function useApplySerializedState() {
await endDiff()
}
+ // Restore view mode
+ if (state.ui.viewMode) {
+ viewMode.value = state.ui.viewMode
+ } else {
+ viewMode.value = ViewMode.DEFAULT
+ }
+
explodeFactor.value = state.ui.explodeFactor
lightConfig.value = {
...lightConfig.value,
diff --git a/packages/frontend-2/lib/viewer/composables/setup.ts b/packages/frontend-2/lib/viewer/composables/setup.ts
index 1ea069ea1..cd3705fb2 100644
--- a/packages/frontend-2/lib/viewer/composables/setup.ts
+++ b/packages/frontend-2/lib/viewer/composables/setup.ts
@@ -6,16 +6,17 @@ import {
MeasurementType,
FilteringExtension
} from '@speckle/viewer'
-import type {
- FilteringState,
- PropertyInfo,
- SunLightConfiguration,
- SpeckleView,
- MeasurementOptions,
- DiffResult,
- Viewer,
- WorldTree,
- VisualDiffMode
+import {
+ type FilteringState,
+ type PropertyInfo,
+ type SunLightConfiguration,
+ type SpeckleView,
+ type MeasurementOptions,
+ type DiffResult,
+ type Viewer,
+ type WorldTree,
+ type VisualDiffMode,
+ ViewMode
} from '@speckle/viewer'
import type { MaybeRef } from '@vueuse/shared'
import { inject, ref, provide } from 'vue'
@@ -66,7 +67,6 @@ import {
import { useSynchronizedCookie } from '~~/lib/common/composables/reactiveCookie'
import { buildManualPromise } from '@speckle/ui-components'
import { PassReader } from '../extensions/PassReader'
-import { ViewModesKeys } from '../extensions/ViewModesKeys'
export type LoadedModel = NonNullable<
Get
@@ -259,6 +259,7 @@ export type InjectableViewerState = Readonly<{
target: Ref
isOrthoProjection: Ref
}
+ viewMode: Ref
diff: {
newVersion: ComputedRef
oldVersion: ComputedRef
@@ -338,7 +339,6 @@ function createViewerDataBuilder(params: { viewerDebug: boolean }) {
verbose: !!(import.meta.client && params.viewerDebug)
})
viewer.createExtension(PassReader)
- viewer.createExtension(ViewModesKeys)
const initPromise = viewer.init()
return {
@@ -931,6 +931,7 @@ function setupInterfaceState(
if (explodeFactor.value !== 0) return true
return false
})
+ const viewMode = ref(ViewMode.DEFAULT)
const highlightedObjectIds = ref([] as string[])
const spotlightUserSessionId = ref(null as Nullable)
@@ -993,6 +994,7 @@ function setupInterfaceState(
target,
isOrthoProjection
},
+ viewMode,
sectionBox: ref(null as Nullable),
sectionBoxContext: {
visible: ref(false),
@@ -1078,7 +1080,7 @@ export function useInjectedViewerInterfaceState(): InjectableViewerState['ui'] {
export function useResetUiState() {
const {
- ui: { camera, sectionBox, highlightedObjectIds, lightConfig }
+ ui: { camera, sectionBox, highlightedObjectIds, lightConfig, viewMode }
} = useInjectedViewerState()
const { resetFilters } = useFilterUtilities()
const { endDiff } = useDiffUtilities()
@@ -1088,6 +1090,7 @@ export function useResetUiState() {
sectionBox.value = null
highlightedObjectIds.value = []
lightConfig.value = { ...DefaultLightConfiguration }
+ viewMode.value = ViewMode.DEFAULT
resetFilters()
endDiff()
}
diff --git a/packages/frontend-2/lib/viewer/composables/setup/postSetup.ts b/packages/frontend-2/lib/viewer/composables/setup/postSetup.ts
index 849de3955..26ff1f704 100644
--- a/packages/frontend-2/lib/viewer/composables/setup/postSetup.ts
+++ b/packages/frontend-2/lib/viewer/composables/setup/postSetup.ts
@@ -1,4 +1,10 @@
import { difference, flatten, isEqual, uniq } from 'lodash-es'
+import {
+ ViewMode,
+ type PropertyInfo,
+ type StringPropertyInfo,
+ type SunLightConfiguration
+} from '@speckle/viewer'
import {
ViewerEvent,
VisualDiffMode,
@@ -6,12 +12,9 @@ import {
UpdateFlags,
SectionOutlines,
SectionToolEvent,
- SectionTool
-} from '@speckle/viewer'
-import type {
- PropertyInfo,
- StringPropertyInfo,
- SunLightConfiguration
+ SectionTool,
+ ViewModes,
+ ViewModeEvent
} from '@speckle/viewer'
import { useAuthCookie } from '~~/lib/auth/composables/auth'
import type {
@@ -636,6 +639,44 @@ function useViewerFiltersIntegration() {
)
}
+function useViewerViewModeIntegration() {
+ const {
+ ui: { viewMode },
+ viewer: { instance }
+ } = useInjectedViewerState()
+
+ const viewModes = instance.getExtension(ViewModes)
+ const onViewModeChanged = (mode: ViewMode) => {
+ viewMode.value = mode
+ }
+
+ onMounted(() => {
+ if (!viewMode.value) {
+ viewMode.value = ViewMode.DEFAULT
+ }
+ viewModes.on(ViewModeEvent.Changed, onViewModeChanged)
+ })
+
+ onBeforeUnmount(() => {
+ // Reset view mode to default
+ viewModes.setViewMode(ViewMode.DEFAULT)
+ viewMode.value = ViewMode.DEFAULT
+
+ // Clean up event listener
+ viewModes.removeListener(ViewModeEvent.Changed, onViewModeChanged)
+ })
+
+ watch(
+ () => viewMode.value,
+ (newMode) => {
+ if (viewModes && newMode) {
+ viewModes.setViewMode(newMode)
+ }
+ },
+ { immediate: true }
+ )
+}
+
function useLightConfigIntegration() {
const {
ui: { lightConfig },
@@ -874,6 +915,7 @@ export function useViewerPostSetup() {
useViewerSectionBoxIntegration()
useViewerCameraIntegration()
useViewerFiltersIntegration()
+ useViewerViewModeIntegration()
useLightConfigIntegration()
useExplodeFactorIntegration()
useDiffingIntegration()
diff --git a/packages/frontend-2/lib/viewer/composables/ui.ts b/packages/frontend-2/lib/viewer/composables/ui.ts
index b105335a2..c38a63a50 100644
--- a/packages/frontend-2/lib/viewer/composables/ui.ts
+++ b/packages/frontend-2/lib/viewer/composables/ui.ts
@@ -1,9 +1,14 @@
import { SpeckleViewer, timeoutAt } from '@speckle/shared'
-import type { TreeNode, MeasurementOptions, PropertyInfo } from '@speckle/viewer'
-import { MeasurementsExtension } from '@speckle/viewer'
+import type {
+ TreeNode,
+ MeasurementOptions,
+ PropertyInfo,
+ ViewMode
+} from '@speckle/viewer'
+import { MeasurementsExtension, ViewModes } from '@speckle/viewer'
import { until } from '@vueuse/shared'
import { difference, isString, uniq } from 'lodash-es'
-import { useEmbedState } from '~/lib/viewer/composables/setup/embed'
+import { useEmbedState, useEmbed } from '~/lib/viewer/composables/setup/embed'
import type { SpeckleObject } from '~/lib/viewer/helpers/sceneExplorer'
import { isNonNullable } from '~~/lib/common/helpers/utils'
import {
@@ -15,6 +20,13 @@ import {
import { useDiffBuilderUtilities } from '~~/lib/viewer/composables/setup/diff'
import { useTourStageState } from '~~/lib/viewer/composables/tour'
import { Vector3, Box3 } from 'three'
+import { getKeyboardShortcutTitle, onKeyboardShortcut } from '@speckle/ui-components'
+import { ViewerShortcuts } from '~/lib/viewer/helpers/shortcuts/shortcuts'
+import type {
+ ViewerShortcut,
+ ViewerShortcutAction
+} from '~/lib/viewer/helpers/shortcuts/types'
+import { useActiveElement } from '@vueuse/core'
export function useSectionBoxUtilities() {
const { instance } = useInjectedViewer()
@@ -475,3 +487,93 @@ export function useHighlightedObjectsUtilities() {
clearHighlightedObjects
}
}
+
+export function useViewModeUtilities() {
+ const { instance } = useInjectedViewer()
+ const { viewMode } = useInjectedViewerInterfaceState()
+
+ const currentViewMode = computed(() => viewMode.value)
+
+ const setViewMode = (mode: ViewMode) => {
+ const viewModes = instance.getExtension(ViewModes)
+ if (viewModes) {
+ viewModes.setViewMode(mode)
+ }
+ }
+
+ return {
+ currentViewMode,
+ setViewMode
+ }
+}
+
+export function useViewerShortcuts() {
+ const { ui } = useInjectedViewerState()
+ const { isSmallerOrEqualSm } = useIsSmallerOrEqualThanBreakpoint()
+ const { isEnabled: isEmbedEnabled } = useEmbed()
+ const activeElement = useActiveElement()
+
+ const isTypingComment = computed(() => {
+ if (
+ activeElement.value &&
+ (activeElement.value.tagName.toLowerCase() === 'input' ||
+ activeElement.value.tagName.toLowerCase() === 'textarea' ||
+ activeElement.value.getAttribute('contenteditable') === 'true')
+ ) {
+ return true
+ }
+
+ // Check thread editor states
+ const isNewThreadEditorOpen = ui.threads.openThread.newThreadEditor.value
+ const isExistingThreadEditorOpen = !!ui.threads.openThread.thread.value
+
+ return isNewThreadEditorOpen || isExistingThreadEditorOpen
+ })
+
+ const formatKey = (key: string) => {
+ if (key.startsWith('Digit')) {
+ return key.slice(5)
+ }
+ return key
+ }
+
+ const getShortcutDisplayText = (
+ shortcut: ViewerShortcut,
+ options?: { hideName?: boolean }
+ ) => {
+ if (isSmallerOrEqualSm.value) return undefined
+ if (isEmbedEnabled.value) return undefined
+
+ const shortcutText = getKeyboardShortcutTitle([
+ ...shortcut.modifiers,
+ formatKey(shortcut.key)
+ ])
+
+ if (!options?.hideName) {
+ return `${shortcut.name} (${shortcutText})`
+ }
+
+ return shortcutText
+ }
+
+ const disableShortcuts = computed(() => isTypingComment.value || isEmbedEnabled.value)
+
+ const registerShortcuts = (
+ handlers: Partial void>>
+ ) => {
+ Object.values(ViewerShortcuts).forEach((shortcut) => {
+ const handler = handlers[shortcut.action as ViewerShortcutAction]
+ if (handler) {
+ onKeyboardShortcut([...shortcut.modifiers], shortcut.key, () => {
+ if (!disableShortcuts.value) handler()
+ })
+ }
+ })
+ }
+
+ return {
+ shortcuts: ViewerShortcuts,
+ registerShortcuts,
+ getShortcutDisplayText
+ }
+}
diff --git a/packages/frontend-2/lib/viewer/extensions/ViewModesKeys.ts b/packages/frontend-2/lib/viewer/extensions/ViewModesKeys.ts
deleted file mode 100644
index df714c2a6..000000000
--- a/packages/frontend-2/lib/viewer/extensions/ViewModesKeys.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import {
- Extension,
- InputEvent,
- ViewMode,
- ViewModes,
- type IViewer
-} from '@speckle/viewer'
-
-export class ViewModesKeys extends Extension {
- public get inject() {
- return [ViewModes]
- }
-
- constructor(viewer: IViewer, protected viewModes: ViewModes) {
- super(viewer)
- const renderer = viewer.getRenderer()
-
- renderer.input.on(InputEvent.KeyUp, (arg: KeyboardEvent) => {
- // Dont trigger on inputs, textareas or contenteditable elements
- // We should handle this more gracefully but it works for now
- if (
- arg.target &&
- ((arg.target as HTMLElement).tagName.toLowerCase() === 'input' ||
- (arg.target as HTMLElement).tagName.toLowerCase() === 'textarea' ||
- (arg.target as HTMLElement).getAttribute('contenteditable') === 'true')
- )
- return
-
- switch (arg.key) {
- case '1':
- viewModes.setViewMode(ViewMode.DEFAULT)
- break
- case '2':
- viewModes.setViewMode(ViewMode.DEFAULT_EDGES)
- break
- case '3':
- viewModes.setViewMode(ViewMode.SHADED)
- break
- case '4':
- viewModes.setViewMode(ViewMode.PEN)
- break
- case '5':
- viewModes.setViewMode(ViewMode.ARCTIC)
- break
- }
- })
- }
-}
diff --git a/packages/frontend-2/lib/viewer/helpers/shortcuts/shortcuts.ts b/packages/frontend-2/lib/viewer/helpers/shortcuts/shortcuts.ts
new file mode 100644
index 000000000..31757dc0d
--- /dev/null
+++ b/packages/frontend-2/lib/viewer/helpers/shortcuts/shortcuts.ts
@@ -0,0 +1,153 @@
+import { ModifierKeys } from '@speckle/ui-components'
+import { ViewMode } from '@speckle/viewer'
+
+export const PanelShortcuts = {
+ ToggleModels: {
+ name: 'Models',
+ description: 'Toggle models panel',
+ modifiers: [ModifierKeys.Shift],
+ key: 'M',
+ action: 'ToggleModels'
+ },
+ ToggleExplorer: {
+ name: 'Scene explorer',
+ description: 'Toggle scene explorer panel',
+ modifiers: [ModifierKeys.Shift],
+ key: 'E',
+ action: 'ToggleExplorer'
+ },
+ ToggleDiscussions: {
+ name: 'Discussions',
+ description: 'Toggle discussions panel',
+ modifiers: [ModifierKeys.Shift],
+ key: 'D',
+ action: 'ToggleDiscussions'
+ }
+} as const
+
+export const ToolShortcuts = {
+ ToggleMeasurements: {
+ name: 'Measure',
+ description: 'Toggle measurement mode',
+ modifiers: [ModifierKeys.Shift],
+ key: 'R',
+ action: 'ToggleMeasurements'
+ },
+ ToggleProjection: {
+ name: 'Projection',
+ description: 'Toggle between orthographic and perspective projection',
+ modifiers: [ModifierKeys.Shift],
+ key: 'P',
+ action: 'ToggleProjection'
+ },
+ ToggleSectionBox: {
+ name: 'Section',
+ description: 'Toggle section box',
+ modifiers: [ModifierKeys.Shift],
+ key: 'B',
+ action: 'ToggleSectionBox'
+ },
+ ZoomExtentsOrSelection: {
+ name: 'Fit',
+ description: 'Zoom to fit selection or entire model',
+ modifiers: [ModifierKeys.Shift],
+ key: 'space',
+ action: 'ZoomExtentsOrSelection'
+ }
+} as const
+
+export const ViewModeShortcuts = {
+ SetViewModeDefault: {
+ name: 'Rendered',
+ description: 'Set view mode to Rendered',
+ modifiers: [ModifierKeys.Shift],
+ key: 'Digit1',
+ action: 'SetViewModeDefault',
+ viewMode: ViewMode.DEFAULT
+ },
+ SetViewModeDefaultEdges: {
+ name: 'Rendered + Edges',
+ description: 'Set view mode to Rendered + Edges',
+ modifiers: [ModifierKeys.Shift],
+ key: 'Digit2',
+ action: 'SetViewModeDefaultEdges',
+ viewMode: ViewMode.DEFAULT_EDGES
+ },
+ SetViewModeShaded: {
+ name: 'Solid',
+ description: 'Set view mode to Solid',
+ modifiers: [ModifierKeys.Shift],
+ key: 'Digit3',
+ action: 'SetViewModeShaded',
+ viewMode: ViewMode.SHADED
+ },
+ SetViewModePen: {
+ name: 'Pen',
+ description: 'Set view mode to Pen',
+ modifiers: [ModifierKeys.Shift],
+ key: 'Digit4',
+ action: 'SetViewModePen',
+ viewMode: ViewMode.PEN
+ },
+ SetViewModeArctic: {
+ name: 'Arctic',
+ description: 'Set view mode to Arctic',
+ modifiers: [ModifierKeys.Shift],
+ key: 'Digit5',
+ action: 'SetViewModeArctic',
+ viewMode: ViewMode.ARCTIC
+ },
+ SetViewModeColors: {
+ name: 'Shaded',
+ description: 'Set view mode to Shaded',
+ modifiers: [ModifierKeys.Shift],
+ key: 'Digit6',
+ action: 'SetViewModeColors',
+ viewMode: ViewMode.COLORS
+ }
+} as const
+
+export const ViewShortcuts = {
+ SetViewTop: {
+ name: 'Top',
+ description: 'Set view to Top',
+ modifiers: [ModifierKeys.AltOrOpt],
+ key: 'Digit1',
+ action: 'SetViewTop'
+ },
+ SetViewFront: {
+ name: 'Front',
+ description: 'Set view to Front',
+ modifiers: [ModifierKeys.AltOrOpt],
+ key: 'Digit2',
+ action: 'SetViewFront'
+ },
+ SetViewLeft: {
+ name: 'Left',
+ description: 'Set view to Left',
+ modifiers: [ModifierKeys.AltOrOpt],
+ key: 'Digit3',
+ action: 'SetViewLeft'
+ },
+ SetViewBack: {
+ name: 'Back',
+ description: 'Set view to Back',
+ modifiers: [ModifierKeys.AltOrOpt],
+ key: 'Digit4',
+ action: 'SetViewBack'
+ },
+ SetViewRight: {
+ name: 'Right',
+ description: 'Set view to Right',
+ modifiers: [ModifierKeys.AltOrOpt],
+ key: 'Digit5',
+ action: 'SetViewRight'
+ }
+} as const
+
+export const ViewerShortcuts = {
+ ...ViewModeShortcuts,
+ ...PanelShortcuts,
+ ...ToolShortcuts,
+ ...ViewShortcuts
+} as const
diff --git a/packages/frontend-2/lib/viewer/helpers/shortcuts/types.ts b/packages/frontend-2/lib/viewer/helpers/shortcuts/types.ts
new file mode 100644
index 000000000..80ce73ad5
--- /dev/null
+++ b/packages/frontend-2/lib/viewer/helpers/shortcuts/types.ts
@@ -0,0 +1,18 @@
+import type { ModifierKeys } from '@speckle/ui-components'
+import type { ViewMode } from '@speckle/viewer'
+import type { ViewerShortcuts } from '~/lib/viewer/helpers/shortcuts/shortcuts'
+
+export type BaseShortcut = {
+ name: string
+ description: string
+ modifiers: readonly ModifierKeys[]
+ key: string
+ action: string
+}
+
+export type ViewModeShortcut = BaseShortcut & {
+ viewMode: ViewMode
+}
+
+export type ViewerShortcut = (typeof ViewerShortcuts)[keyof typeof ViewerShortcuts]
+export type ViewerShortcutAction = keyof typeof ViewerShortcuts
diff --git a/packages/server/app.ts b/packages/server/app.ts
index eb398e768..c6d69f130 100644
--- a/packages/server/app.ts
+++ b/packages/server/app.ts
@@ -399,12 +399,13 @@ export async function init() {
const app = express()
app.disable('x-powered-by')
- Logging(app)
-
// Moves things along automatically on restart.
// Should perhaps be done manually?
await migrateDbToLatest({ region: 'main', db: knex })
+ // Logging relies on 'regions' table in the database, so much be initialized after migrations
+ await Logging(app)
+
app.use(cookieParser())
app.use(DetermineRequestIdMiddleware)
app.use(determineClientIpAddressMiddleware)
diff --git a/packages/server/logging/highFrequencyMetrics/highfrequencyMonitoring.ts b/packages/server/logging/highFrequencyMetrics/highfrequencyMonitoring.ts
index a38c80bb5..b11bfe6cd 100644
--- a/packages/server/logging/highFrequencyMetrics/highfrequencyMonitoring.ts
+++ b/packages/server/logging/highFrequencyMetrics/highfrequencyMonitoring.ts
@@ -14,7 +14,9 @@ type MetricConfig = {
prefix?: string
labels?: Record
buckets?: Record
- knex: Knex
+ getDbClients: () => Promise<
+ Array<{ client: Knex; isMain: boolean; regionKey: string }>
+ >
}
type HighFrequencyMonitor = {
diff --git a/packages/server/logging/highFrequencyMetrics/knexConnectionPool.ts b/packages/server/logging/highFrequencyMetrics/knexConnectionPool.ts
index 72cf4b89c..6a6afc5fd 100644
--- a/packages/server/logging/highFrequencyMetrics/knexConnectionPool.ts
+++ b/packages/server/logging/highFrequencyMetrics/knexConnectionPool.ts
@@ -31,16 +31,17 @@ type MetricConfig = {
prefix?: string
labels?: Record
buckets?: Record
- knex: Knex
+ getDbClients: () => Promise<
+ Array<{ client: Knex; isMain: boolean; regionKey: string }>
+ >
}
export const knexConnections = (registry: Registry, config: MetricConfig): Metric => {
const registers = registry ? [registry] : undefined
const namePrefix = config.prefix ?? ''
const labels = config.labels ?? {}
- const labelNames = Object.keys(labels)
+ const labelNames = [...Object.keys(labels), 'region']
const buckets = { ...DEFAULT_KNEX_TOTAL_BUCKETS, ...config.buckets }
- const knex = config.knex
const knexConnectionsFree = new Histogram({
name: namePrefix + KNEX_CONNECTIONS_FREE,
@@ -91,15 +92,24 @@ export const knexConnections = (registry: Registry, config: MetricConfig): Metri
})
return {
- collect: () => {
- const connPool = knex.client.pool
+ collect: async () => {
+ for (const dbClient of await config.getDbClients()) {
+ const labelsAndRegion = { ...labels, region: dbClient.regionKey }
+ const connPool = dbClient.client.client.pool
- knexConnectionsFree.observe(labels, connPool.numFree())
- knexConnectionsUsed.observe(labels, connPool.numUsed())
- knexPendingAcquires.observe(labels, connPool.numPendingAcquires())
- knexPendingCreates.observe(labels, connPool.numPendingCreates())
- knexPendingValidations.observe(labels, connPool.numPendingValidations())
- knexRemainingCapacity.observe(labels, numberOfFreeConnections(knex))
+ knexConnectionsFree.observe(labelsAndRegion, connPool.numFree())
+ knexConnectionsUsed.observe(labelsAndRegion, connPool.numUsed())
+ knexPendingAcquires.observe(labelsAndRegion, connPool.numPendingAcquires())
+ knexPendingCreates.observe(labelsAndRegion, connPool.numPendingCreates())
+ knexPendingValidations.observe(
+ labelsAndRegion,
+ connPool.numPendingValidations()
+ )
+ knexRemainingCapacity.observe(
+ labelsAndRegion,
+ numberOfFreeConnections(dbClient.client)
+ )
+ }
}
}
}
diff --git a/packages/server/logging/index.ts b/packages/server/logging/index.ts
index 6489ae634..c1020813e 100644
--- a/packages/server/logging/index.ts
+++ b/packages/server/logging/index.ts
@@ -4,14 +4,14 @@ import promBundle from 'express-prom-bundle'
import { initKnexPrometheusMetrics } from '@/logging/knexMonitoring'
import { initHighFrequencyMonitoring } from '@/logging/highFrequencyMetrics/highfrequencyMonitoring'
-import knex from '@/db/knex'
import { highFrequencyMetricsCollectionPeriodMs } from '@/modules/shared/helpers/envHelper'
import { startupLogger as logger } from '@/logging/logging'
import type express from 'express'
+import { getAllRegisteredDbClients } from '@/modules/multiregion/utils/dbSelector'
let prometheusInitialized = false
-export default function (app: express.Express) {
+export default async function (app: express.Express) {
if (!prometheusInitialized) {
prometheusInitialized = true
prometheusClient.register.clear()
@@ -24,14 +24,14 @@ export default function (app: express.Express) {
register: prometheusClient.register,
collectionPeriodMilliseconds: highFrequencyMetricsCollectionPeriodMs(),
config: {
- knex
+ getDbClients: getAllRegisteredDbClients
}
})
highfrequencyMonitoring.start()
- initKnexPrometheusMetrics({
+ await initKnexPrometheusMetrics({
register: prometheusClient.register,
- db: knex,
+ getAllDbClients: getAllRegisteredDbClients,
logger
})
const expressMetricsMiddleware = promBundle({
diff --git a/packages/server/logging/knexMonitoring.ts b/packages/server/logging/knexMonitoring.ts
index 5d327ca42..f2d087b90 100644
--- a/packages/server/logging/knexMonitoring.ts
+++ b/packages/server/logging/knexMonitoring.ts
@@ -5,150 +5,228 @@ import { Logger } from 'pino'
import { toNDecimalPlaces } from '@/modules/core/utils/formatting'
import { omit } from 'lodash'
-export const initKnexPrometheusMetrics = (params: {
- db: Knex
+let metricQueryDuration: prometheusClient.Summary
+let metricQueryErrors: prometheusClient.Counter
+let metricConnectionAcquisitionDuration: prometheusClient.Histogram
+let metricConnectionPoolErrors: prometheusClient.Counter
+let metricConnectionInUseDuration: prometheusClient.Histogram
+let metricConnectionPoolReapingDuration: prometheusClient.Histogram
+const initializedRegions: string[] = []
+let initializedPollingMetrics = false
+
+export const initKnexPrometheusMetrics = async (params: {
+ getAllDbClients: () => Promise<
+ Array<{ client: Knex; isMain: boolean; regionKey: string }>
+ >
register: Registry
logger: Logger
}) => {
- const normalizeSqlMethod = (sqlMethod: string) => {
- if (!sqlMethod) return 'unknown'
- switch (sqlMethod.toLocaleLowerCase()) {
- case 'first':
- return 'select'
- default:
- return sqlMethod.toLocaleLowerCase()
- }
+ if (!initializedPollingMetrics) {
+ initializedPollingMetrics = true
+ new prometheusClient.Gauge({
+ registers: [params.register],
+ name: 'speckle_server_knex_free',
+ labelNames: ['region'],
+ help: 'Number of free DB connections',
+ async collect() {
+ for (const dbClient of await params.getAllDbClients()) {
+ this.set(
+ { region: dbClient.regionKey },
+ dbClient.client.client.pool.numFree()
+ )
+ }
+ }
+ })
+
+ new prometheusClient.Gauge({
+ registers: [params.register],
+ name: 'speckle_server_knex_used',
+ labelNames: ['region'],
+ help: 'Number of used DB connections',
+ async collect() {
+ for (const dbClient of await params.getAllDbClients()) {
+ this.set(
+ { region: dbClient.regionKey },
+ dbClient.client.client.pool.numUsed()
+ )
+ }
+ }
+ })
+
+ new prometheusClient.Gauge({
+ registers: [params.register],
+ name: 'speckle_server_knex_pending',
+ labelNames: ['region'],
+ help: 'Number of pending DB connection aquires',
+ async collect() {
+ for (const dbClient of await params.getAllDbClients()) {
+ this.set(
+ { region: dbClient.regionKey },
+ dbClient.client.client.pool.numPendingAcquires()
+ )
+ }
+ }
+ })
+
+ new prometheusClient.Gauge({
+ registers: [params.register],
+ name: 'speckle_server_knex_pending_creates',
+ labelNames: ['region'],
+ help: 'Number of pending DB connection creates',
+ async collect() {
+ for (const dbClient of await params.getAllDbClients()) {
+ this.set(
+ { region: dbClient.regionKey },
+ dbClient.client.client.pool.numPendingCreates()
+ )
+ }
+ }
+ })
+
+ new prometheusClient.Gauge({
+ registers: [params.register],
+ name: 'speckle_server_knex_pending_validations',
+ labelNames: ['region'],
+ help: 'Number of pending DB connection validations. This is a state between pending acquisition and acquiring a connection.',
+ async collect() {
+ for (const dbClient of await params.getAllDbClients()) {
+ this.set(
+ { region: dbClient.regionKey },
+ dbClient.client.client.pool.numPendingValidations()
+ )
+ }
+ }
+ })
+
+ new prometheusClient.Gauge({
+ registers: [params.register],
+ name: 'speckle_server_knex_remaining_capacity',
+ labelNames: ['region'],
+ help: 'Remaining capacity of the DB connection pool',
+ async collect() {
+ for (const dbClient of await params.getAllDbClients()) {
+ this.set(
+ { region: dbClient.regionKey },
+ numberOfFreeConnections(dbClient.client)
+ )
+ }
+ }
+ })
+
+ metricQueryDuration = new prometheusClient.Summary({
+ registers: [params.register],
+ labelNames: ['sqlMethod', 'sqlNumberBindings', 'region'],
+ name: 'speckle_server_knex_query_duration',
+ help: 'Summary of the DB query durations in seconds'
+ })
+
+ metricQueryErrors = new prometheusClient.Counter({
+ registers: [params.register],
+ labelNames: ['sqlMethod', 'sqlNumberBindings', 'region'],
+ name: 'speckle_server_knex_query_errors',
+ help: 'Number of DB queries with errors'
+ })
+
+ metricConnectionAcquisitionDuration = new prometheusClient.Histogram({
+ registers: [params.register],
+ name: 'speckle_server_knex_connection_acquisition_duration',
+ labelNames: ['region'],
+ help: 'Summary of the DB connection acquisition duration, from request to acquire connection from pool until successfully acquired, in seconds'
+ })
+
+ metricConnectionPoolErrors = new prometheusClient.Counter({
+ registers: [params.register],
+ name: 'speckle_server_knex_connection_acquisition_errors',
+ labelNames: ['region'],
+ help: 'Number of DB connection pool acquisition errors'
+ })
+
+ metricConnectionInUseDuration = new prometheusClient.Histogram({
+ registers: [params.register],
+ name: 'speckle_server_knex_connection_usage_duration',
+ labelNames: ['region'],
+ help: 'Summary of the DB connection duration, from successful acquisition of connection from pool until release back to pool, in seconds'
+ })
+
+ metricConnectionPoolReapingDuration = new prometheusClient.Histogram({
+ registers: [params.register],
+ name: 'speckle_server_knex_connection_pool_reaping_duration',
+ labelNames: ['region'],
+ help: 'Summary of the DB connection pool reaping duration, in seconds. Reaping is the process of removing idle connections from the pool.'
+ })
}
+ // configure hooks on knex
+ for (const dbClient of await params.getAllDbClients()) {
+ if (initializedRegions.includes(dbClient.regionKey)) continue
+ initKnexPrometheusMetricsForRegionEvents({
+ logger: params.logger,
+ region: dbClient.regionKey,
+ db: dbClient.client
+ })
+ initializedRegions.push(dbClient.regionKey)
+ }
+}
+
+const normalizeSqlMethod = (sqlMethod: string) => {
+ if (!sqlMethod) return 'unknown'
+ switch (sqlMethod.toLocaleLowerCase()) {
+ case 'first':
+ return 'select'
+ default:
+ return sqlMethod.toLocaleLowerCase()
+ }
+}
+
+interface QueryEvent extends Knex.Sql {
+ __knexUid: string
+ __knexTxId: string
+ __knexQueryUid: string
+}
+
+const initKnexPrometheusMetricsForRegionEvents = async (params: {
+ region: string
+ db: Knex
+ logger: Logger
+}) => {
+ const { region, db } = params
const queryStartTime: Record = {}
const connectionAcquisitionStartTime: Record = {}
const connectionInUseStartTime: Record = {}
- new prometheusClient.Gauge({
- registers: [params.register],
- name: 'speckle_server_knex_free',
- help: 'Number of free DB connections',
- collect() {
- this.set(params.db.client.pool.numFree())
- }
- })
-
- new prometheusClient.Gauge({
- registers: [params.register],
- name: 'speckle_server_knex_used',
- help: 'Number of used DB connections',
- collect() {
- this.set(params.db.client.pool.numUsed())
- }
- })
-
- new prometheusClient.Gauge({
- registers: [params.register],
- name: 'speckle_server_knex_pending',
- help: 'Number of pending DB connection aquires',
- collect() {
- this.set(params.db.client.pool.numPendingAcquires())
- }
- })
-
- new prometheusClient.Gauge({
- registers: [params.register],
- name: 'speckle_server_knex_pending_creates',
- help: 'Number of pending DB connection creates',
- collect() {
- this.set(params.db.client.pool.numPendingCreates())
- }
- })
-
- new prometheusClient.Gauge({
- registers: [params.register],
- name: 'speckle_server_knex_pending_validations',
- help: 'Number of pending DB connection validations. This is a state between pending acquisition and acquiring a connection.',
- collect() {
- this.set(params.db.client.pool.numPendingValidations())
- }
- })
-
- new prometheusClient.Gauge({
- registers: [params.register],
- name: 'speckle_server_knex_remaining_capacity',
- help: 'Remaining capacity of the DB connection pool',
- collect() {
- this.set(numberOfFreeConnections(params.db))
- }
- })
-
- const metricQueryDuration = new prometheusClient.Summary({
- registers: [params.register],
- labelNames: ['sqlMethod', 'sqlNumberBindings'],
- name: 'speckle_server_knex_query_duration',
- help: 'Summary of the DB query durations in seconds'
- })
-
- const metricQueryErrors = new prometheusClient.Counter({
- registers: [params.register],
- labelNames: ['sqlMethod', 'sqlNumberBindings'],
- name: 'speckle_server_knex_query_errors',
- help: 'Number of DB queries with errors'
- })
-
- const metricConnectionAcquisitionDuration = new prometheusClient.Histogram({
- registers: [params.register],
- name: 'speckle_server_knex_connection_acquisition_duration',
- help: 'Summary of the DB connection acquisition duration, from request to acquire connection from pool until successfully acquired, in seconds'
- })
-
- const metricConnectionPoolErrors = new prometheusClient.Counter({
- registers: [params.register],
- name: 'speckle_server_knex_connection_acquisition_errors',
- help: 'Number of DB connection pool acquisition errors'
- })
-
- const metricConnectionInUseDuration = new prometheusClient.Histogram({
- registers: [params.register],
- name: 'speckle_server_knex_connection_usage_duration',
- help: 'Summary of the DB connection duration, from successful acquisition of connection from pool until release back to pool, in seconds'
- })
-
- const metricConnectionPoolReapingDuration = new prometheusClient.Histogram({
- registers: [params.register],
- name: 'speckle_server_knex_connection_pool_reaping_duration',
- help: 'Summary of the DB connection pool reaping duration, in seconds. Reaping is the process of removing idle connections from the pool.'
- })
-
- // configure hooks on knex
-
- params.db.on('query', (data) => {
+ db.on('query', (data: QueryEvent) => {
const queryId = data.__knexQueryUid + ''
queryStartTime[queryId] = performance.now()
})
- params.db.on('query-response', (_data, querySpec) => {
- const queryId = querySpec.__knexQueryUid + ''
+ db.on('query-response', (_response: unknown, data: QueryEvent) => {
+ const queryId = data.__knexQueryUid + ''
const durationMs = performance.now() - queryStartTime[queryId]
const durationSec = toNDecimalPlaces(durationMs / 1000, 2)
delete queryStartTime[queryId]
if (!isNaN(durationSec))
metricQueryDuration
.labels({
- sqlMethod: normalizeSqlMethod(querySpec.method),
- sqlNumberBindings: querySpec.bindings?.length || -1
+ region,
+ sqlMethod: normalizeSqlMethod(data.method),
+ sqlNumberBindings: data.bindings?.length || -1
})
.observe(durationSec)
params.logger.debug(
{
- sql: querySpec.sql,
- sqlMethod: normalizeSqlMethod(querySpec.method),
+ region,
+ sql: data.sql,
+ sqlMethod: normalizeSqlMethod(data.method),
sqlQueryId: queryId,
sqlQueryDurationMs: toNDecimalPlaces(durationMs, 0),
- sqlNumberBindings: querySpec.bindings?.length || -1
+ sqlNumberBindings: data.bindings?.length || -1
},
"DB query successfully completed, for method '{sqlMethod}', after {sqlQueryDurationMs}ms"
)
})
- params.db.on('query-error', (err, querySpec) => {
- const queryId = querySpec.__knexQueryUid + ''
+ db.on('query-error', (err: unknown, data: QueryEvent) => {
+ const queryId = data.__knexQueryUid + ''
const durationMs = performance.now() - queryStartTime[queryId]
const durationSec = toNDecimalPlaces(durationMs / 1000, 2)
delete queryStartTime[queryId]
@@ -156,25 +234,27 @@ export const initKnexPrometheusMetrics = (params: {
if (!isNaN(durationSec))
metricQueryDuration
.labels({
- sqlMethod: normalizeSqlMethod(querySpec.method),
- sqlNumberBindings: querySpec.bindings?.length || -1
+ region,
+ sqlMethod: normalizeSqlMethod(data.method),
+ sqlNumberBindings: data.bindings?.length || -1
})
.observe(durationSec)
metricQueryErrors.inc()
params.logger.warn(
{
- err: omit(err, 'detail'),
- sql: querySpec.sql,
- sqlMethod: normalizeSqlMethod(querySpec.method),
+ err: typeof err === 'object' ? omit(err, 'detail') : err,
+ region,
+ sql: data.sql,
+ sqlMethod: normalizeSqlMethod(data.method),
sqlQueryId: queryId,
sqlQueryDurationMs: toNDecimalPlaces(durationMs, 0),
- sqlNumberBindings: querySpec.bindings?.length || -1
+ sqlNumberBindings: data.bindings?.length || -1
},
'DB query errored for {sqlMethod} after {sqlQueryDurationMs}ms'
)
})
- const pool = params.db.client.pool
+ const pool = db.client.pool
// configure hooks on knex connection pool
pool.on('acquireRequest', (eventId: number) => {
@@ -190,7 +270,8 @@ export const initKnexPrometheusMetrics = (params: {
const now = performance.now()
const durationMs = now - connectionAcquisitionStartTime[eventId]
delete connectionAcquisitionStartTime[eventId]
- if (!isNaN(durationMs)) metricConnectionAcquisitionDuration.observe(durationMs)
+ if (!isNaN(durationMs))
+ metricConnectionAcquisitionDuration.labels({ region }).observe(durationMs)
// successful acquisition is the start of usage, so record that start time
let knexUid: string | undefined = undefined
@@ -234,7 +315,8 @@ export const initKnexPrometheusMetrics = (params: {
const now = performance.now()
const durationMs = now - connectionInUseStartTime[knexUid]
- if (!isNaN(durationMs)) metricConnectionInUseDuration.observe(durationMs)
+ if (!isNaN(durationMs))
+ metricConnectionInUseDuration.labels({ region }).observe(durationMs)
// params.logger.debug(
// {
// knexUid,
@@ -263,7 +345,8 @@ export const initKnexPrometheusMetrics = (params: {
pool.on('stopReaping', () => {
if (!reapingStartTime) return
const durationMs = performance.now() - reapingStartTime
- if (!isNaN(durationMs)) metricConnectionPoolReapingDuration.observe(durationMs)
+ if (!isNaN(durationMs))
+ metricConnectionPoolReapingDuration.labels({ region }).observe(durationMs)
reapingStartTime = undefined
})
diff --git a/packages/server/modules/comments/services/data.ts b/packages/server/modules/comments/services/data.ts
index 44737e3c7..cd887e2da 100644
--- a/packages/server/modules/comments/services/data.ts
+++ b/packages/server/modules/comments/services/data.ts
@@ -156,6 +156,7 @@ export const convertLegacyDataToStateFactory =
isOrthoProjection: !!data.camPos?.[6],
zoom: data.camPos?.[7] || 1
},
+ viewMode: 0,
sectionBox: sectionBox
? {
min: (sectionBox.min as number[]) || [0, 0, 0],
diff --git a/packages/server/test/notificationsHelper.ts b/packages/server/test/notificationsHelper.ts
index 762456fab..2d70d10ae 100644
--- a/packages/server/test/notificationsHelper.ts
+++ b/packages/server/test/notificationsHelper.ts
@@ -57,7 +57,7 @@ export function buildNotificationsStateTracker() {
* Wait for an acknowledgement of a specific msg
*/
waitForMsgAck: async (msgId: JobId, timeout = 2000) => {
- let timeoutRef: NodeJS.Timer
+ let timeoutRef: NodeJS.Timeout
let eventEmitterHandler: (e: AckEvent) => void
return new Promise((resolve, reject) => {
// Set ack cb for notifications event handler
diff --git a/packages/shared/src/viewer/helpers/state.ts b/packages/shared/src/viewer/helpers/state.ts
index f62f40190..2d9237372 100644
--- a/packages/shared/src/viewer/helpers/state.ts
+++ b/packages/shared/src/viewer/helpers/state.ts
@@ -74,6 +74,7 @@ export type SerializedViewerState = {
isOrthoProjection: boolean
zoom: number
}
+ viewMode: number
sectionBox: Nullable<{
min: number[]
max: number[]
@@ -198,6 +199,7 @@ const initializeMissingData = (state: UnformattedState): SerializedViewerState =
isOrthoProjection: state.ui?.camera?.isOrthoProjection || false,
zoom: state.ui?.camera?.zoom || 1
},
+ viewMode: state.ui?.viewMode || 0,
sectionBox:
state.ui?.sectionBox?.min?.length && state.ui?.sectionBox.max?.length
? {
diff --git a/packages/ui-components/src/components/form/select/Base.vue b/packages/ui-components/src/components/form/select/Base.vue
index 3f988eb63..5c72ffc5f 100644
--- a/packages/ui-components/src/components/form/select/Base.vue
+++ b/packages/ui-components/src/components/form/select/Base.vue
@@ -127,7 +127,7 @@
ref="searchInput"
v-model="searchValue"
type="text"
- class="py-1 pl-7 w-full bg-foundation placeholder:font-normal normal placeholder:text-foreground-2 text-[13px]"
+ class="py-1 pl-7 w-full bg-foundation placeholder:font-normal normal placeholder:text-foreground-2 text-[13px] focus-visible:[box-shadow:none] rounded-md hover:border-outline-5 focus-visible:border-outline-4"
:placeholder="searchPlaceholder"
@keydown.stop
/>
diff --git a/packages/ui-components/src/components/global/ToastRenderer.stories.ts b/packages/ui-components/src/components/global/ToastRenderer.stories.ts
index ce1e1fe54..fe7b69219 100644
--- a/packages/ui-components/src/components/global/ToastRenderer.stories.ts
+++ b/packages/ui-components/src/components/global/ToastRenderer.stories.ts
@@ -7,7 +7,7 @@ import { ToastNotificationType } from '~~/src/helpers/global/toast'
import type { ToastNotification } from '~~/src/helpers/global/toast'
import { useGlobalToast } from '~~/src/stories/composables/toast'
-type StoryType = StoryObj<{ notification: ToastNotification }>
+type StoryType = StoryObj<{ notifications: ToastNotification[] }>
export default {
component: ToastRenderer,
@@ -20,12 +20,11 @@ export default {
}
},
argTypes: {
- notification: {
- description: 'ToastNotification type object, nullable'
+ notifications: {
+ description: 'ToastNotification array, nullable'
},
- 'update:notification': {
- description:
- "Notification prop update event. Enables two-way binding through 'v-model:notification'"
+ dismiss: {
+ description: 'Dismiss event for a notification'
}
}
} as Meta
@@ -35,11 +34,11 @@ export const Default: StoryType = {
components: { ToastRenderer, FormButton },
setup() {
const { triggerNotification } = useGlobalToast()
- const notification = ref(null as Nullable)
+ const notifications = ref(null as Nullable)
const onClick = () => {
- triggerNotification(args.notification)
+ triggerNotification(args.notifications[0])
}
- return { args, onClick, notification }
+ return { args, onClick, notifications }
},
template: `
@@ -50,30 +49,34 @@ export const Default: StoryType = {
parameters: {
docs: {
source: {
- code: '
'
+ code: '
'
}
}
},
args: {
- notification: {
- type: ToastNotificationType.Info,
- title: 'Title',
- description: 'Description',
+ notifications: [
+ {
+ type: ToastNotificationType.Info,
+ title: 'Title',
+ description: 'Description',
- cta: {
- title: 'CTA'
+ cta: {
+ title: 'CTA'
+ }
}
- }
+ ]
}
}
export const WithManualClose: StoryType = {
...Default,
args: {
- notification: {
- ...Default.args!.notification!,
- autoClose: false
- }
+ notifications: [
+ {
+ ...Default.args!.notifications![0],
+ autoClose: false
+ }
+ ]
}
}
@@ -81,23 +84,20 @@ export const NoCtaOrDescription: StoryObj = {
render: (args) => ({
components: { ToastRenderer, FormButton },
setup() {
- const notification = ref(null as Nullable
)
+ const { triggerNotification } = useGlobalToast()
+ const notifications = ref(null as Nullable)
const onClick = () => {
- // Update notification without cta or description
- notification.value = {
+ triggerNotification({
type: ToastNotificationType.Info,
title: 'Displays a toast notification'
- }
-
- // Clear after 2s
- setTimeout(() => (notification.value = null), 2000)
+ })
}
- return { args, onClick, notification }
+ return { args, onClick, notifications }
},
template: `
Trigger Title Only
-
+
`
}),
@@ -108,8 +108,8 @@ export const NoCtaOrDescription: StoryObj = {
},
source: {
code: `
-Trigger Title Only
-
+ Trigger Title Only
+
`
}
}
diff --git a/packages/ui-components/src/components/global/ToastRenderer.vue b/packages/ui-components/src/components/global/ToastRenderer.vue
index 01bdc1a82..41d68a501 100644
--- a/packages/ui-components/src/components/global/ToastRenderer.vue
+++ b/packages/ui-components/src/components/global/ToastRenderer.vue
@@ -5,7 +5,7 @@
>
-
@@ -60,7 +61,7 @@
color="subtle"
:to="notification.cta.url"
size="sm"
- @click="onCtaClick"
+ @click="(e: MouseEvent) => onCtaClick(notification, e)"
>
{{ notification.cta.title }}
@@ -70,7 +71,7 @@
-
+
@@ -91,29 +92,24 @@ import {
InformationCircleIcon,
XMarkIcon
} from '@heroicons/vue/20/solid'
-import { computed } from 'vue'
import type { MaybeNullOrUndefined } from '@speckle/shared'
import { ToastNotificationType } from '~~/src/helpers/global/toast'
import type { ToastNotification } from '~~/src/helpers/global/toast'
const emit = defineEmits<{
- (e: 'update:notification', val: MaybeNullOrUndefined): void
+ (e: 'dismiss', val: ToastNotification): void
}>()
-const props = defineProps<{
- notification: MaybeNullOrUndefined
+defineProps<{
+ notifications: MaybeNullOrUndefined
}>()
-const isTitleOnly = computed(
- () => !props.notification?.description && !props.notification?.cta
-)
-
-const dismiss = () => {
- emit('update:notification', null)
+const dismiss = (notification: ToastNotification) => {
+ emit('dismiss', notification)
}
-const onCtaClick = (e: MouseEvent) => {
- props.notification?.cta?.onClick?.(e)
- dismiss()
+const onCtaClick = (notification: ToastNotification, e: MouseEvent) => {
+ notification.cta?.onClick?.(e)
+ dismiss(notification)
}
diff --git a/packages/ui-components/src/helpers/form/input.ts b/packages/ui-components/src/helpers/form/input.ts
index 8f04485d0..27537860b 100644
--- a/packages/ui-components/src/helpers/form/input.ts
+++ b/packages/ui-components/src/helpers/form/input.ts
@@ -9,14 +9,14 @@ export enum ModifierKeys {
export const clientOs = getClientOperatingSystem()
export const ModifierKeyTitles: Record = {
- [ModifierKeys.CtrlOrCmd]: clientOs === OperatingSystem.Mac ? 'Cmd' : 'Ctrl',
- [ModifierKeys.AltOrOpt]: clientOs === OperatingSystem.Mac ? 'Opt' : 'Alt',
- [ModifierKeys.Shift]: 'Shift'
+ [ModifierKeys.CtrlOrCmd]: clientOs === OperatingSystem.Mac ? '⌘' : '⌃',
+ [ModifierKeys.AltOrOpt]: clientOs === OperatingSystem.Mac ? '⌥' : 'Alt',
+ [ModifierKeys.Shift]: '⇧'
}
export function getKeyboardShortcutTitle(keys: Array) {
const isModifierKey = (k: string): k is ModifierKeys =>
(Object.values(ModifierKeys) as string[]).includes(k)
- return keys.map((v) => (isModifierKey(v) ? ModifierKeyTitles[v] : v)).join('+')
+ return keys.map((v) => (isModifierKey(v) ? ModifierKeyTitles[v] : v)).join('')
}
diff --git a/packages/ui-components/src/helpers/global/toast.ts b/packages/ui-components/src/helpers/global/toast.ts
index 7052f86a2..4c070cf69 100644
--- a/packages/ui-components/src/helpers/global/toast.ts
+++ b/packages/ui-components/src/helpers/global/toast.ts
@@ -25,4 +25,5 @@ export type ToastNotification = {
* Defaults to true
*/
autoClose?: boolean
+ id?: string
}
diff --git a/packages/ui-components/src/stories/components/GlobalToast.vue b/packages/ui-components/src/stories/components/GlobalToast.vue
index 38b795ea8..401583d09 100644
--- a/packages/ui-components/src/stories/components/GlobalToast.vue
+++ b/packages/ui-components/src/stories/components/GlobalToast.vue
@@ -1,18 +1,18 @@
-
+