From 8dbd342a4035e18e5a24137e4e363e5c66a1ffc6 Mon Sep 17 00:00:00 2001 From: Kristaps Fabians Geikins Date: Thu, 28 Aug 2025 11:40:58 +0300 Subject: [PATCH] feat(fe2): view modes stored in saved views (and elsewhere) (#5320) * feat(fe2): view modes stored in saved views (and elsewhere) * lint fixes --- .../components/viewer/controls/Bottom.vue | 5 +- .../components/viewer/lightControls/Menu.vue | 4 +- .../components/viewer/view-modes/Menu.vue | 27 ++-- .../lib/viewer/composables/serialization.ts | 27 ++-- .../lib/viewer/composables/setup.ts | 41 +++--- .../lib/viewer/composables/setup/dev.ts | 8 +- .../lib/viewer/composables/setup/postSetup.ts | 14 +- .../lib/viewer/composables/setup/viewMode.ts | 127 ++++++++++++++++++ .../frontend-2/lib/viewer/composables/ui.ts | 82 +++-------- .../frontend-2/type-augmentations/window.d.ts | 1 + .../server/modules/comments/services/data.ts | 8 +- .../shared/src/viewer/helpers/state.spec.ts | 24 +++- packages/shared/src/viewer/helpers/state.ts | 27 +++- 13 files changed, 270 insertions(+), 125 deletions(-) create mode 100644 packages/frontend-2/lib/viewer/composables/setup/viewMode.ts diff --git a/packages/frontend-2/components/viewer/controls/Bottom.vue b/packages/frontend-2/components/viewer/controls/Bottom.vue index d86e36a7a..ce170fee3 100644 --- a/packages/frontend-2/components/viewer/controls/Bottom.vue +++ b/packages/frontend-2/components/viewer/controls/Bottom.vue @@ -93,7 +93,10 @@ const { const { getActiveMeasurement, removeMeasurement, enableMeasurements, hasMeasurements } = useMeasurementUtilities() const { resetExplode } = useFilterUtilities() -const { currentViewMode, setViewMode } = useViewModeUtilities() +const { + viewMode: { mode: currentViewMode }, + setViewMode +} = useViewModeUtilities() const { ui: { explodeFactor } } = useInjectedViewerState() diff --git a/packages/frontend-2/components/viewer/lightControls/Menu.vue b/packages/frontend-2/components/viewer/lightControls/Menu.vue index 02a75d454..0fd1fed6c 100644 --- a/packages/frontend-2/components/viewer/lightControls/Menu.vue +++ b/packages/frontend-2/components/viewer/lightControls/Menu.vue @@ -68,7 +68,9 @@ import { useViewModeUtilities } from '~/lib/viewer/composables/ui' import { TIME_MS } from '@speckle/shared' const mp = useMixpanel() -const { currentViewMode } = useViewModeUtilities() +const { + viewMode: { mode: currentViewMode } +} = useViewModeUtilities() const isLightingSupported = computed(() => { const supported = currentViewMode.value === ViewMode.DEFAULT diff --git a/packages/frontend-2/components/viewer/view-modes/Menu.vue b/packages/frontend-2/components/viewer/view-modes/Menu.vue index b1a4f62ea..ef17109c7 100644 --- a/packages/frontend-2/components/viewer/view-modes/Menu.vue +++ b/packages/frontend-2/components/viewer/view-modes/Menu.vue @@ -95,19 +95,15 @@ import { ViewMode } from '@speckle/viewer' import { useViewModeUtilities } from '~~/lib/viewer/composables/ui' import { ViewModeShortcuts } from '~/lib/viewer/helpers/shortcuts/shortcuts' import { FormSwitch } from '@speckle/ui-components' -import { useTheme } from '~/lib/core/composables/theme' +import { defaultEdgeColorValue } from '~/lib/viewer/composables/setup/viewMode' const { setViewMode, - currentViewMode, - edgesEnabled, toggleEdgesEnabled, setEdgesWeight, - edgesWeight, setEdgesColor, - edgesColor + viewMode: { edgesColor, edgesWeight, edgesEnabled, mode: currentViewMode } } = useViewModeUtilities() -const { isLightTheme } = useTheme() const showSettings = ref(false) @@ -115,14 +111,17 @@ const isActiveMode = (mode: ViewMode) => mode === currentViewMode.value const viewModeShortcuts = Object.values(ViewModeShortcuts) -const edgesColorOptions = computed(() => [ - isLightTheme.value || currentViewMode.value !== ViewMode.PEN ? 0x1a1a1a : 0xffffff, // black or white - 0x3b82f6, // blue-500 - 0x8b5cf6, // violet-500 - 0x65a30d, // lime-600 - 0xf97316, // orange-500 - 0xf43f5e //rose-500 -]) +const edgesColorOptions = computed( + () => + [ + defaultEdgeColorValue, // black or white + 0x3b82f6, // blue-500 + 0x8b5cf6, // violet-500 + 0x65a30d, // lime-600 + 0xf97316, // orange-500 + 0xf43f5e //rose-500 + ] as const +) const handleViewModeChange = (mode: ViewMode) => { setViewMode(mode) diff --git a/packages/frontend-2/lib/viewer/composables/serialization.ts b/packages/frontend-2/lib/viewer/composables/serialization.ts index 0d97b07ed..e23c03244 100644 --- a/packages/frontend-2/lib/viewer/composables/serialization.ts +++ b/packages/frontend-2/lib/viewer/composables/serialization.ts @@ -2,7 +2,7 @@ import { useInjectedViewerState, useResetUiState } from '~~/lib/viewer/composables/setup' -import { SpeckleViewer, TimeoutError } from '@speckle/shared' +import { isUndefinedOrVoid, SpeckleViewer, TimeoutError } from '@speckle/shared' import { get } from 'lodash-es' import { Vector3 } from 'three' import { @@ -10,7 +10,7 @@ import { useFilterUtilities, useSelectionUtilities } from '~~/lib/viewer/composables/ui' -import { CameraController, ViewMode, VisualDiffMode } from '@speckle/viewer' +import { CameraController, VisualDiffMode } from '@speckle/viewer' import type { NumericPropertyInfo } from '@speckle/viewer' import type { Merge, PartialDeep } from 'type-fest' import type { SectionBoxData } from '@speckle/shared/viewer/state' @@ -117,7 +117,13 @@ export function useStateSerialization() { isOrthoProjection: state.ui.camera.isOrthoProjection.value, zoom: (get(camControls, '_zoom') as unknown as number) || 1 // kinda hacky, _zoom is a protected prop }, - viewMode: state.ui.viewMode.value, + viewMode: { + mode: state.ui.viewMode.mode.value, + edgesEnabled: state.ui.viewMode.edgesEnabled.value, + edgesWeight: state.ui.viewMode.edgesWeight.value, + outlineOpacity: state.ui.viewMode.outlineOpacity.value, + edgesColor: state.ui.viewMode.edgesColor.value + }, sectionBox: state.ui.sectionBox.value ? box : null, lightConfig: { ...state.ui.lightConfig.value }, explodeFactor: state.ui.explodeFactor.value, @@ -361,11 +367,16 @@ export function useApplySerializedState() { } // Restore view mode - if (state.ui?.viewMode) { - viewMode.value = state.ui.viewMode - } else { - viewMode.value = ViewMode.DEFAULT - } + if (!isUndefinedOrVoid(state.ui?.viewMode?.mode)) + viewMode.mode.value = state.ui!.viewMode!.mode + if (!isUndefinedOrVoid(state.ui?.viewMode?.edgesEnabled)) + viewMode.edgesEnabled.value = state.ui!.viewMode!.edgesEnabled + if (!isUndefinedOrVoid(state.ui?.viewMode?.edgesWeight)) + viewMode.edgesWeight.value = state.ui!.viewMode!.edgesWeight + if (!isUndefinedOrVoid(state.ui?.viewMode?.outlineOpacity)) + viewMode.outlineOpacity.value = state.ui!.viewMode!.outlineOpacity + if (!isUndefinedOrVoid(state.ui?.viewMode?.edgesColor)) + viewMode.edgesColor.value = state.ui!.viewMode!.edgesColor explodeFactor.value = state.ui?.explodeFactor || 0 lightConfig.value = { diff --git a/packages/frontend-2/lib/viewer/composables/setup.ts b/packages/frontend-2/lib/viewer/composables/setup.ts index 96256ceb3..7468fde8f 100644 --- a/packages/frontend-2/lib/viewer/composables/setup.ts +++ b/packages/frontend-2/lib/viewer/composables/setup.ts @@ -6,17 +6,17 @@ import { MeasurementType, FilteringExtension } from '@speckle/viewer' -import { - type FilteringState, - type PropertyInfo, - type SunLightConfiguration, - type SpeckleView, - type MeasurementOptions, - type DiffResult, - type Viewer, - type WorldTree, - type VisualDiffMode, - ViewMode +import type { + ViewMode, + FilteringState, + PropertyInfo, + SunLightConfiguration, + SpeckleView, + MeasurementOptions, + DiffResult, + Viewer, + WorldTree, + VisualDiffMode } from '@speckle/viewer' import { inject, ref, provide } from 'vue' import type { ComputedRef, WritableComputedRef, Raw, Ref, ShallowRef } from 'vue' @@ -85,6 +85,8 @@ import { useBuildSavedViewsUIState, type SavedViewsUIState } from '~/lib/viewer/composables/savedViews/state' +import type { defaultEdgeColorValue } from '~/lib/viewer/composables/setup/viewMode' +import { useViewModesSetup } from '~/lib/viewer/composables/setup/viewMode' export type LoadedModel = NonNullable< Get @@ -313,7 +315,16 @@ export type InjectableViewerState = Readonly<{ target: Ref isOrthoProjection: Ref } - viewMode: Ref + viewMode: { + mode: Ref + edgesEnabled: Ref + edgesWeight: Ref + outlineOpacity: Ref + edgesColor: Ref + finalEdgesColor: ComputedRef + defaultEdgesColor: ComputedRef + resetViewMode: () => void + } diff: { newVersion: ComputedRef oldVersion: ComputedRef @@ -1104,7 +1115,7 @@ function setupInterfaceState( if (propertyFilter.value || isPropertyFilterApplied.value) return true return false }) - const viewMode = ref(ViewMode.DEFAULT) + const { viewMode } = useViewModesSetup() const highlightedObjectIds = ref([] as string[]) const spotlightUserSessionId = ref(null as Nullable) @@ -1143,6 +1154,7 @@ function setupInterfaceState( return { ...state, ui: { + viewMode, diff: { ...diffState }, @@ -1168,7 +1180,6 @@ function setupInterfaceState( target, isOrthoProjection }, - viewMode, sectionBox: ref(null as Nullable), sectionBoxContext: { visible: ref(false), @@ -1263,7 +1274,7 @@ export function useResetUiState() { sectionBox.value = null highlightedObjectIds.value = [] lightConfig.value = { ...DefaultLightConfiguration } - viewMode.value = ViewMode.DEFAULT + viewMode.resetViewMode() resetFilters() endDiff() } diff --git a/packages/frontend-2/lib/viewer/composables/setup/dev.ts b/packages/frontend-2/lib/viewer/composables/setup/dev.ts index 45c360bbd..8a37e02ee 100644 --- a/packages/frontend-2/lib/viewer/composables/setup/dev.ts +++ b/packages/frontend-2/lib/viewer/composables/setup/dev.ts @@ -42,12 +42,18 @@ function useDebugViewer() { // Get current viewer state window.VIEWER_STATE = () => fullViewerState - // Get serialized version of current state + // Get serialized version of current state as string window.VIEWER_SERIALIZED_STATE = (...args: Parameters) => { const serialized = serialize(...args) return JSON.stringify(serialized) } + // Get serialized version of current state as object + window.VIEWER_SERIALIZED_STATE_OBJECT = (...args: Parameters) => { + const serialized = serialize(...args) + return serialized + } + // Apply viewer state window.APPLY_VIEWER_STATE = ( state: SpeckleViewer.ViewerState.SerializedViewerState diff --git a/packages/frontend-2/lib/viewer/composables/setup/postSetup.ts b/packages/frontend-2/lib/viewer/composables/setup/postSetup.ts index b42695167..6677b8040 100644 --- a/packages/frontend-2/lib/viewer/composables/setup/postSetup.ts +++ b/packages/frontend-2/lib/viewer/composables/setup/postSetup.ts @@ -54,8 +54,7 @@ import { areVectorsLooselyEqual } from '~~/lib/viewer/helpers/three' import { SafeLocalStorage, type Nullable } from '@speckle/shared' import { useCameraUtilities, - useMeasurementUtilities, - useViewModeUtilities + useMeasurementUtilities } from '~~/lib/viewer/composables/ui' import { setupDebugMode } from '~~/lib/viewer/composables/setup/dev' import { useEmbed } from '~/lib/viewer/composables/setup/embed' @@ -64,6 +63,7 @@ import type { SectionBoxData } from '@speckle/shared/viewer/state' import { graphql } from '~/lib/common/generated/gql' import { useTreeManagement } from '~~/lib/viewer/composables/tree' import { useViewerSavedViewIntegration } from '~/lib/viewer/composables/savedViews/state' +import { useViewModesPostSetup } from '~/lib/viewer/composables/setup/viewMode' function useViewerLoadCompleteEventHandler() { const state = useInjectedViewerState() @@ -977,14 +977,6 @@ function useViewerCursorIntegration() { }) } -function useViewerViewModesIntegration() { - const { resetViewMode } = useViewModeUtilities() - - onBeforeUnmount(() => { - resetViewMode() - }) -} - export function useViewerPostSetup() { if (import.meta.server) return useViewerObjectAutoLoading() @@ -1005,6 +997,6 @@ export function useViewerPostSetup() { useDisableZoomOnEmbed() useViewerCursorIntegration() useViewerTreeIntegration() - useViewerViewModesIntegration() + useViewModesPostSetup() setupDebugMode() } diff --git a/packages/frontend-2/lib/viewer/composables/setup/viewMode.ts b/packages/frontend-2/lib/viewer/composables/setup/viewMode.ts new file mode 100644 index 000000000..0db57636a --- /dev/null +++ b/packages/frontend-2/lib/viewer/composables/setup/viewMode.ts @@ -0,0 +1,127 @@ +import { defaultViewModeEdgeColorValue } from '@speckle/shared/viewer/state' +import { ViewMode, ViewModes } from '@speckle/viewer' +import { watchTriggerable } from '@vueuse/core' +import { useTheme } from '~/lib/core/composables/theme' +import { useInjectedViewerState } from '~/lib/viewer/composables/setup' +import { useOnViewerLoadComplete } from '~/lib/viewer/composables/viewer' + +export const defaultEdgeColorValue = defaultViewModeEdgeColorValue +export const edgeColorDark = 0x1a1a1a +export const edgeColorLight = 0xffffff + +export const useViewModesSetup = () => { + const { isLightTheme } = useTheme() + + const mode = ref(ViewMode.DEFAULT) + const edgesEnabled = ref(true) + const edgesWeight = ref(1) + const outlineOpacity = ref(0.75) + const edgesColor = ref(defaultEdgeColorValue) + + const defaultEdgesColor = computed(() => { + if (mode.value === ViewMode.PEN) { + return isLightTheme.value ? edgeColorDark : edgeColorLight + } else { + return isLightTheme.value ? edgeColorLight : edgeColorDark + } + }) + + const finalEdgesColor = computed(() => { + if (edgesColor.value !== defaultEdgeColorValue) return edgesColor.value + return defaultEdgesColor.value + }) + + const resetViewMode = () => { + mode.value = ViewMode.DEFAULT + edgesEnabled.value = true + edgesWeight.value = 1 + outlineOpacity.value = 0.75 + edgesColor.value = defaultEdgeColorValue + } + + return { + viewMode: { + mode, + edgesEnabled, + edgesWeight, + outlineOpacity, + edgesColor, + finalEdgesColor, + defaultEdgesColor, + resetViewMode + } + } +} + +export const useViewModesPostSetup = () => { + const { + ui: { viewMode }, + viewer: { instance } + } = useInjectedViewerState() + const { + mode, + edgesEnabled, + edgesWeight, + outlineOpacity, + finalEdgesColor, + resetViewMode + } = viewMode + + const updateViewMode = () => { + const viewModes = instance.getExtension(ViewModes) + if (viewModes) { + viewModes.setViewMode(mode.value, { + edges: edgesEnabled.value, + outlineThickness: edgesWeight.value, + outlineOpacity: outlineOpacity.value, + outlineColor: finalEdgesColor.value + }) + } + } + + // state -> viewer + useOnViewerLoadComplete( + () => { + updateViewMode() + }, + { initialOnly: true } + ) + + const { ignoreUpdates: ignoreEdgesEnabledUpdates } = watchTriggerable( + edgesEnabled, + (newVal, oldVal) => { + if (oldVal === newVal) return + updateViewMode() + } + ) + watchTriggerable(edgesWeight, (newVal, oldVal) => { + if (oldVal === newVal) return + updateViewMode() + }) + const { ignoreUpdates: ignoreOutlineOpacityUpdates } = watchTriggerable( + outlineOpacity, + (newVal, oldVal) => { + if (oldVal === newVal) return + updateViewMode() + } + ) + watchTriggerable(finalEdgesColor, (newVal, oldVal) => { + if (oldVal === newVal) return + updateViewMode() + }) + watchTriggerable(mode, (newVal, oldVal) => { + if (oldVal === newVal) return + + if (newVal === ViewMode.PEN) { + ignoreOutlineOpacityUpdates(() => (outlineOpacity.value = 1)) + ignoreEdgesEnabledUpdates(() => (edgesEnabled.value = true)) + } else { + ignoreOutlineOpacityUpdates(() => (outlineOpacity.value = 0.75)) + } + updateViewMode() + }) + + onBeforeUnmount(() => { + resetViewMode() + }) +} diff --git a/packages/frontend-2/lib/viewer/composables/ui.ts b/packages/frontend-2/lib/viewer/composables/ui.ts index 4a85f803f..17f55d7ef 100644 --- a/packages/frontend-2/lib/viewer/composables/ui.ts +++ b/packages/frontend-2/lib/viewer/composables/ui.ts @@ -1,11 +1,11 @@ import { SpeckleViewer, TIME_MS, timeoutAt } from '@speckle/shared' -import { - type TreeNode, - type MeasurementOptions, - type PropertyInfo, +import type { + TreeNode, + MeasurementOptions, + PropertyInfo, ViewMode } from '@speckle/viewer' -import { MeasurementsExtension, ViewModes, MeasurementEvent } from '@speckle/viewer' +import { MeasurementsExtension, MeasurementEvent } from '@speckle/viewer' import { until } from '@vueuse/shared' import { useActiveElement } from '@vueuse/core' import { difference, isString, uniq } from 'lodash-es' @@ -25,9 +25,9 @@ import type { ViewerShortcut, ViewerShortcutAction } from '~/lib/viewer/helpers/shortcuts/types' -import { useTheme } from '~/lib/core/composables/theme' import { useMixpanel } from '~/lib/core/composables/mp' import { isStringPropertyInfo } from '~/lib/viewer/helpers/sceneExplorer' +import type { defaultEdgeColorValue } from '~/lib/viewer/composables/setup/viewMode' export function useSectionBoxUtilities() { const { instance } = useInjectedViewer() @@ -754,49 +754,11 @@ export function useHighlightedObjectsUtilities() { } export function useViewModeUtilities() { - const { instance } = useInjectedViewer() const { viewMode } = useInjectedViewerInterfaceState() - const { isLightTheme } = useTheme() const mp = useMixpanel() - const edgesEnabled = ref(true) - const edgesWeight = ref(1) - const outlineOpacity = ref(0.75) - const defaultColor = ref(0x1a1a1a) - const edgesColor = ref(defaultColor.value) - - const currentViewMode = computed(() => viewMode.value) - - const updateViewMode = () => { - const viewModes = instance.getExtension(ViewModes) - if (viewModes) { - viewModes.setViewMode(currentViewMode.value, { - edges: edgesEnabled.value, - outlineThickness: edgesWeight.value, - outlineOpacity: outlineOpacity.value, - outlineColor: edgesColor.value - }) - } - } - const setViewMode = (mode: ViewMode) => { - viewMode.value = mode - if (mode === ViewMode.PEN) { - outlineOpacity.value = 1 - edgesEnabled.value = true - if (edgesColor.value === defaultColor.value) { - if (!isLightTheme.value) { - edgesColor.value = 0xffffff - } - } - } else { - outlineOpacity.value = 0.75 - if (edgesColor.value === 0xffffff) { - edgesColor.value = isLightTheme.value ? 0xffffff : defaultColor.value - } - } - - updateViewMode() + viewMode.mode.value = mode mp.track('Viewer Action', { type: 'action', name: 'set-view-mode', @@ -805,28 +767,25 @@ export function useViewModeUtilities() { } const toggleEdgesEnabled = () => { - edgesEnabled.value = !edgesEnabled.value - updateViewMode() + viewMode.edgesEnabled.value = !viewMode.edgesEnabled.value mp.track('Viewer Action', { type: 'action', name: 'toggle-edges', - enabled: edgesEnabled.value + enabled: viewMode.edgesEnabled.value }) } const setEdgesWeight = (weight: number) => { - edgesWeight.value = Number(weight) - updateViewMode() + viewMode.edgesWeight.value = Number(weight) mp.track('Viewer Action', { type: 'action', name: 'set-edges-weight', - weight: edgesWeight.value + weight: viewMode.edgesWeight.value }) } - const setEdgesColor = (color: number) => { - edgesColor.value = color - updateViewMode() + const setEdgesColor = (color: number | typeof defaultEdgeColorValue) => { + viewMode.edgesColor.value = color mp.track('Viewer Action', { type: 'action', name: 'set-edges-color', @@ -834,24 +793,13 @@ export function useViewModeUtilities() { }) } - const resetViewMode = () => { - setViewMode(ViewMode.DEFAULT) - edgesEnabled.value = true - edgesWeight.value = 1 - outlineOpacity.value = 0.75 - edgesColor.value = defaultColor.value - } - return { - currentViewMode, + viewMode, setViewMode, - edgesEnabled, toggleEdgesEnabled, - edgesWeight, setEdgesWeight, setEdgesColor, - edgesColor, - resetViewMode + resetViewMode: viewMode.resetViewMode } } diff --git a/packages/frontend-2/type-augmentations/window.d.ts b/packages/frontend-2/type-augmentations/window.d.ts index 9ef805522..4aeeebfdb 100644 --- a/packages/frontend-2/type-augmentations/window.d.ts +++ b/packages/frontend-2/type-augmentations/window.d.ts @@ -14,6 +14,7 @@ declare global { VIEWER?: any VIEWER_STATE?: any VIEWER_SERIALIZED_STATE?: any + VIEWER_SERIALIZED_STATE_OBJECT?: any APPLY_VIEWER_STATE?: any APPLY_VIEWER_DD_EVENT?: any } diff --git a/packages/server/modules/comments/services/data.ts b/packages/server/modules/comments/services/data.ts index d6028d0a1..0007c7dc3 100644 --- a/packages/server/modules/comments/services/data.ts +++ b/packages/server/modules/comments/services/data.ts @@ -148,7 +148,13 @@ export const convertLegacyDataToStateFactory = isOrthoProjection: !!data.camPos?.[6], zoom: data.camPos?.[7] || 1 }, - viewMode: 0, + viewMode: { + mode: 0, + edgesColor: 0, + edgesEnabled: true, + outlineOpacity: 0.75, + edgesWeight: 1 + }, sectionBox: sectionBox ? { min: (sectionBox.min as number[]) || [0, 0, 0], diff --git a/packages/shared/src/viewer/helpers/state.spec.ts b/packages/shared/src/viewer/helpers/state.spec.ts index bd4af48e5..c2ceda6e6 100644 --- a/packages/shared/src/viewer/helpers/state.spec.ts +++ b/packages/shared/src/viewer/helpers/state.spec.ts @@ -43,7 +43,13 @@ describe('Viewer State helpers', () => { isOrthoProjection: false, zoom: 1 }, - viewMode: 0, + viewMode: { + mode: 0, + edgesColor: 0, + edgesEnabled: true, + outlineOpacity: 0, + edgesWeight: 0 + }, sectionBox: null, lightConfig: {}, explodeFactor: 0, @@ -149,7 +155,13 @@ describe('Viewer State helpers', () => { isOrthoProjection: false, zoom: 1 }, - viewMode: 0, + viewMode: { + mode: 0, + edgesColor: 0, + edgesEnabled: true, + outlineOpacity: 0, + edgesWeight: 0 + }, sectionBox: null, lightConfig: {}, explodeFactor: 0, @@ -202,7 +214,13 @@ describe('Viewer State helpers', () => { isOrthoProjection: false, zoom: 1 }, - viewMode: 0, + viewMode: { + mode: 0, + edgesColor: 0, + edgesEnabled: true, + outlineOpacity: 0, + edgesWeight: 0 + }, sectionBox: null, lightConfig: {}, explodeFactor: 0, diff --git a/packages/shared/src/viewer/helpers/state.ts b/packages/shared/src/viewer/helpers/state.ts index ef586728a..3bca59a27 100644 --- a/packages/shared/src/viewer/helpers/state.ts +++ b/packages/shared/src/viewer/helpers/state.ts @@ -1,9 +1,11 @@ -import { has, intersection, isObjectLike } from '#lodash' +import { has, intersection, isNumber, isObjectLike } from '#lodash' import type { MaybeNullOrUndefined, Nullable } from '../../core/helpers/utilityTypes.js' import type { PartialDeep } from 'type-fest' import { UnformattableSerializedViewerStateError } from '../errors/index.js' import { coerceUndefinedValuesToNull } from '../../core/index.js' +export const defaultViewModeEdgeColorValue = 'DEFAULT_EDGE_COLOR' + /** Redefining these is unfortunate. Especially since they are not part of viewer-core */ enum MeasurementType { PERPENDICULAR = 0, @@ -35,6 +37,9 @@ export interface SectionBoxData { * - ui.diff added * v1.2 -> v1.3 * - ui.filters.selectedObjectIds removed in favor of ui.filters.selectedObjectApplicationIds + * v1.3 -> 1.4 + * - ui.viewMode -> ui.viewMode.mode + * - ui.viewMode has new keys: edgesEnabled, edgesWeight, outlineOpacity, edgesColor */ export const SERIALIZED_VIEWER_STATE_VERSION = 1.3 @@ -88,7 +93,13 @@ export type SerializedViewerState = { isOrthoProjection: boolean zoom: number } - viewMode: number + viewMode: { + mode: number + edgesEnabled: boolean + edgesWeight: number + outlineOpacity: number + edgesColor: typeof defaultViewModeEdgeColorValue | number + } sectionBox: Nullable lightConfig: { intensity?: number @@ -174,6 +185,10 @@ const initializeMissingData = (state: UnformattedState): SerializedViewerState = ) } + const viewMode = isNumber(state.ui?.viewMode) + ? state.ui.viewMode + : state.ui?.viewMode?.mode + return { projectId: state.projectId || throwInvalidError('projectId'), sessionId: state.sessionId || `nullSessionId-${Math.random() * 1000}`, @@ -236,7 +251,13 @@ const initializeMissingData = (state: UnformattedState): SerializedViewerState = isOrthoProjection: state.ui?.camera?.isOrthoProjection || false, zoom: state.ui?.camera?.zoom || 1 }, - viewMode: state.ui?.viewMode || 0, + viewMode: { + mode: viewMode || 0, + edgesEnabled: state.ui?.viewMode?.edgesEnabled || false, + edgesWeight: state.ui?.viewMode?.edgesWeight || 1, + outlineOpacity: state.ui?.viewMode?.outlineOpacity || 0.75, + edgesColor: state.ui?.viewMode?.edgesColor || defaultViewModeEdgeColorValue + }, sectionBox: state.ui?.sectionBox?.min?.length && state.ui?.sectionBox.max?.length ? // Complains otherwise