@geode/opengeodeweb-front 10.19.0 → 10.20.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/app/components/ViewToolbar.vue +1 -1
  2. package/app/components/Viewer/ContextMenu/CenterButton.vue +119 -0
  3. package/app/components/Viewer/ContextMenu/CircularItems.vue +86 -0
  4. package/app/components/Viewer/{ContextMenu.vue → ContextMenu/ContextMenu.vue} +67 -71
  5. package/app/components/Viewer/{ContextMenuItem.vue → ContextMenu/ContextMenuItem.vue} +104 -4
  6. package/app/components/Viewer/ContextMenu/InfoCard.vue +184 -0
  7. package/app/components/Viewer/EdgedCurve/SpecificEdgesOptions.vue +1 -1
  8. package/app/components/Viewer/Generic/Mesh/CellsOptions.vue +1 -1
  9. package/app/components/Viewer/Generic/Mesh/EdgesOptions.vue +1 -1
  10. package/app/components/Viewer/Generic/Mesh/PointsOptions.vue +1 -1
  11. package/app/components/Viewer/Generic/Mesh/PolygonsOptions.vue +1 -1
  12. package/app/components/Viewer/Generic/Mesh/PolyhedraOptions.vue +1 -1
  13. package/app/components/Viewer/Generic/Model/EdgesOptions.vue +1 -1
  14. package/app/components/Viewer/Generic/Model/ModelStyleOptions.vue +1 -1
  15. package/app/components/Viewer/Generic/Model/PointsOptions.vue +1 -1
  16. package/app/components/Viewer/OverlappingObjectsPicker.vue +96 -0
  17. package/app/components/Viewer/PointSet/SpecificPointsOptions.vue +1 -1
  18. package/app/components/Viewer/Solid/SpecificPolyhedraOptions.vue +1 -1
  19. package/app/components/Viewer/Surface/PolygonsOptions.vue +1 -1
  20. package/app/components/Viewer/Ui.vue +38 -24
  21. package/app/composables/project_manager.js +3 -3
  22. package/app/composables/use_adaptive_styles.js +50 -3
  23. package/app/composables/use_overlapping_picker.js +153 -0
  24. package/app/stores/data.js +5 -0
  25. package/app/utils/local/microservices.js +2 -2
  26. package/app/utils/name_cleaner.js +20 -0
  27. package/internal/stores/hybrid_viewer.js +6 -0
  28. package/package.json +3 -3
  29. package/tests/unit/composables/project_manager.nuxt.test.js +0 -1
@@ -0,0 +1,184 @@
1
+ <script setup>
2
+ import GlassCard from "@ogw_front/components/GlassCard";
3
+ import { middleTruncate } from "@ogw_front/utils/string";
4
+ import { useDataStore } from "@ogw_front/stores/data";
5
+ import { useMenuStore } from "@ogw_front/stores/menu";
6
+
7
+ const { show, metaData } = defineProps({
8
+ show: { type: Boolean, required: true },
9
+ metaData: { type: Object, required: true },
10
+ });
11
+
12
+ const emit = defineEmits(["update:show"]);
13
+
14
+ const COPIED_TIMEOUT = 1500;
15
+ const MAX_SHORT_ID_LENGTH = 15;
16
+ const ID_SLICE_START = 8;
17
+ const ID_SLICE_END_OFFSET = 7;
18
+ const TRUNCATE_MAX_LENGTH = 22;
19
+ const TRUNCATE_START_CHARS = 12;
20
+ const TRUNCATE_END_CHARS = 7;
21
+
22
+ const menuStore = useMenuStore();
23
+ const dataStore = useDataStore();
24
+
25
+ const componentName = ref("");
26
+
27
+ watch(
28
+ () => menuStore.current_meta_data,
29
+ async (newMeta) => {
30
+ componentName.value = "";
31
+ if (!newMeta) {
32
+ return;
33
+ }
34
+
35
+ if (newMeta.viewer_type === "model_component" && newMeta.modelId && newMeta.pickedComponentId) {
36
+ try {
37
+ const comp = await dataStore.getComponentByViewerId(
38
+ newMeta.modelId,
39
+ newMeta.pickedComponentId,
40
+ );
41
+ if (comp && comp.name) {
42
+ componentName.value = comp.name;
43
+ } else {
44
+ componentName.value = newMeta.pickedComponentId;
45
+ }
46
+ } catch {
47
+ componentName.value = newMeta.pickedComponentId;
48
+ }
49
+ }
50
+ },
51
+ { immediate: true },
52
+ );
53
+
54
+ const cleanName = computed(() => {
55
+ const meta = menuStore.current_meta_data;
56
+ if (!meta) {
57
+ return "Unnamed Object";
58
+ }
59
+ if (componentName.value) {
60
+ return componentName.value;
61
+ }
62
+ return meta.name || "Unnamed Object";
63
+ });
64
+
65
+ const displayTitle = computed(() => {
66
+ const name = cleanName.value;
67
+ if (!name) {
68
+ return "";
69
+ }
70
+ return middleTruncate(name, TRUNCATE_MAX_LENGTH, TRUNCATE_START_CHARS, TRUNCATE_END_CHARS);
71
+ });
72
+
73
+ const copied = ref(false);
74
+ async function copyId(targetId) {
75
+ if (!targetId) {
76
+ return;
77
+ }
78
+ try {
79
+ await navigator.clipboard.writeText(targetId);
80
+ copied.value = true;
81
+ setTimeout(() => {
82
+ copied.value = false;
83
+ }, COPIED_TIMEOUT);
84
+ } catch (error) {
85
+ console.error("Failed to copy ID:", error);
86
+ }
87
+ }
88
+
89
+ const formattedId = computed(() => {
90
+ const metaId = metaData?.id;
91
+ if (!metaId) {
92
+ return "";
93
+ }
94
+ if (metaId.length <= MAX_SHORT_ID_LENGTH) {
95
+ return metaId;
96
+ }
97
+ return `${metaId.slice(0, ID_SLICE_START)}...${metaId.slice(metaId.length - ID_SLICE_END_OFFSET)}`;
98
+ });
99
+ </script>
100
+
101
+ <template>
102
+ <v-fade-transition>
103
+ <v-sheet v-if="show" class="object-name-popover bg-transparent" @mousedown.stop @click.stop>
104
+ <GlassCard
105
+ variant="panel"
106
+ padding="pa-2 px-3"
107
+ rounded="lg"
108
+ class="elevation-12 text-center border-thin"
109
+ min-width="140"
110
+ max-width="250"
111
+ >
112
+ <v-row no-gutters class="flex-column align-center">
113
+ <v-col
114
+ class="text-subtitle-2 font-weight-bold text-truncate text-white w-100 pa-0"
115
+ cols="auto"
116
+ style="line-height: 1.3"
117
+ >
118
+ {{ displayTitle }}
119
+ </v-col>
120
+ <v-col
121
+ class="text-caption font-weight-black text-uppercase text-secondary pa-0"
122
+ style="font-size: 0.68rem; line-height: 1.2"
123
+ >
124
+ {{ metaData.geode_object_type }}
125
+ </v-col>
126
+ <v-col
127
+ v-if="metaData.id"
128
+ class="id-badge-container mt-1 d-inline-flex align-center px-2 py-0.5"
129
+ @click.stop="copyId(metaData.id)"
130
+ >
131
+ <span class="id-text">
132
+ {{ copied ? "COPIED!" : formattedId }}
133
+ </span>
134
+ <v-icon
135
+ :icon="copied ? 'mdi-check' : 'mdi-content-copy'"
136
+ size="10"
137
+ :color="copied ? 'success' : 'white'"
138
+ class="ml-1"
139
+ />
140
+ </v-col>
141
+ </v-row>
142
+ </GlassCard>
143
+ </v-sheet>
144
+ </v-fade-transition>
145
+ </template>
146
+
147
+ <style scoped>
148
+ .id-badge-container {
149
+ font-family: monospace;
150
+ font-size: 0.6rem;
151
+ background: rgba(255, 255, 255, 0.08);
152
+ border: 1px solid rgba(255, 255, 255, 0.15);
153
+ border-radius: 4px;
154
+ cursor: pointer;
155
+ user-select: none;
156
+ transition: all 0.2s ease;
157
+ opacity: 0.8;
158
+ }
159
+
160
+ .id-badge-container:hover {
161
+ background: rgba(255, 255, 255, 0.15);
162
+ border-color: rgba(255, 255, 255, 0.35);
163
+ opacity: 1;
164
+ transform: scale(1.03);
165
+ }
166
+
167
+ .id-badge-container:active {
168
+ transform: scale(0.97);
169
+ }
170
+
171
+ .id-text {
172
+ letter-spacing: 0.5px;
173
+ color: rgba(255, 255, 255, 0.85);
174
+ }
175
+
176
+ .object-name-popover {
177
+ position: absolute;
178
+ bottom: 110px; /* Positions it beautifully above the circular menu */
179
+ left: 50%;
180
+ transform: translateX(-50%);
181
+ z-index: 100;
182
+ pointer-events: auto;
183
+ }
184
+ </style>
@@ -1,6 +1,6 @@
1
1
  <script setup>
2
2
  import EdgedCurveEdges from "@ogw_front/assets/viewer_svgs/edged_curve_edges.svg";
3
- import ViewerContextMenuItem from "@ogw_front/components/Viewer/ContextMenuItem";
3
+ import ViewerContextMenuItem from "@ogw_front/components/Viewer/ContextMenu/ContextMenuItem";
4
4
  import ViewerOptionsColoringTypeSelector from "@ogw_front/components/Viewer/Options/ColoringTypeSelector";
5
5
  import ViewerOptionsVisibilitySwitch from "@ogw_front/components/Viewer/Options/VisibilitySwitch";
6
6
  import ViewerOptionsWidthSlider from "@ogw_front/components/Viewer/Options/Sliders/Width";
@@ -1,5 +1,5 @@
1
1
  <script setup>
2
- import ViewerContextMenuItem from "@ogw_front/components/Viewer/ContextMenuItem";
2
+ import ViewerContextMenuItem from "@ogw_front/components/Viewer/ContextMenu/ContextMenuItem";
3
3
  import ViewerOptionsColoringTypeSelector from "@ogw_front/components/Viewer/Options/ColoringTypeSelector";
4
4
  import ViewerOptionsVisibilitySwitch from "@ogw_front/components/Viewer/Options/VisibilitySwitch";
5
5
 
@@ -1,5 +1,5 @@
1
1
  <script setup>
2
- import ViewerContextMenuItem from "@ogw_front/components/Viewer/ContextMenuItem";
2
+ import ViewerContextMenuItem from "@ogw_front/components/Viewer/ContextMenu/ContextMenuItem";
3
3
  import ViewerOptionsColoringTypeSelector from "@ogw_front/components/Viewer/Options/ColoringTypeSelector";
4
4
  import ViewerOptionsVisibilitySwitch from "@ogw_front/components/Viewer/Options/VisibilitySwitch";
5
5
  import ViewerOptionsWidthSlider from "@ogw_front/components/Viewer/Options/Sliders/Width";
@@ -1,5 +1,5 @@
1
1
  <script setup>
2
- import ViewerContextMenuItem from "@ogw_front/components/Viewer/ContextMenuItem";
2
+ import ViewerContextMenuItem from "@ogw_front/components/Viewer/ContextMenu/ContextMenuItem";
3
3
  import ViewerOptionsColoringTypeSelector from "@ogw_front/components/Viewer/Options/ColoringTypeSelector";
4
4
  import ViewerOptionsSizeSlider from "@ogw_front/components/Viewer/Options/Sliders/Size";
5
5
  import ViewerOptionsVisibilitySwitch from "@ogw_front/components/Viewer/Options/VisibilitySwitch";
@@ -1,5 +1,5 @@
1
1
  <script setup>
2
- import ViewerContextMenuItem from "@ogw_front/components/Viewer/ContextMenuItem";
2
+ import ViewerContextMenuItem from "@ogw_front/components/Viewer/ContextMenu/ContextMenuItem";
3
3
  import ViewerOptionsColoringTypeSelector from "@ogw_front/components/Viewer/Options/ColoringTypeSelector";
4
4
  import ViewerOptionsVisibilitySwitch from "@ogw_front/components/Viewer/Options/VisibilitySwitch";
5
5
 
@@ -1,5 +1,5 @@
1
1
  <script setup>
2
- import ViewerContextMenuItem from "@ogw_front/components/Viewer/ContextMenuItem";
2
+ import ViewerContextMenuItem from "@ogw_front/components/Viewer/ContextMenu/ContextMenuItem";
3
3
  import ViewerOptionsColoringTypeSelector from "@ogw_front/components/Viewer/Options/ColoringTypeSelector";
4
4
  import ViewerOptionsVisibilitySwitch from "@ogw_front/components/Viewer/Options/VisibilitySwitch";
5
5
 
@@ -1,6 +1,6 @@
1
1
  <script setup>
2
2
  import SurfaceEdges from "@ogw_front/assets/viewer_svgs/surface_edges.svg";
3
- import ViewerContextMenuItem from "@ogw_front/components/Viewer/ContextMenuItem";
3
+ import ViewerContextMenuItem from "@ogw_front/components/Viewer/ContextMenu/ContextMenuItem";
4
4
  import ViewerOptionsVisibilitySwitch from "@ogw_front/components/Viewer/Options/VisibilitySwitch";
5
5
 
6
6
  import { useDataStyleStore } from "@ogw_front/stores/data_style";
@@ -1,7 +1,7 @@
1
1
  <script setup>
2
2
  import ModelColor from "@ogw_front/assets/viewer_svgs/model_component_color.svg";
3
3
  import ModelStyleCard from "./ModelStyleCard.vue";
4
- import ViewerContextMenuItem from "@ogw_front/components/Viewer/ContextMenuItem";
4
+ import ViewerContextMenuItem from "@ogw_front/components/Viewer/ContextMenu/ContextMenuItem";
5
5
 
6
6
  const { itemProps } = defineProps({
7
7
  itemProps: { type: Object, required: true },
@@ -1,6 +1,6 @@
1
1
  <script setup>
2
2
  import SurfacePoints from "@ogw_front/assets/viewer_svgs/surface_points.svg";
3
- import ViewerContextMenuItem from "@ogw_front/components/Viewer/ContextMenuItem";
3
+ import ViewerContextMenuItem from "@ogw_front/components/Viewer/ContextMenu/ContextMenuItem";
4
4
  import ViewerOptionsSizeSlider from "@ogw_front/components/Viewer/Options/Sliders/Size";
5
5
  import ViewerOptionsVisibilitySwitch from "@ogw_front/components/Viewer/Options/VisibilitySwitch";
6
6
 
@@ -0,0 +1,96 @@
1
+ <script setup>
2
+ import GlassCard from "@ogw_front/components/GlassCard";
3
+ import { formatListId } from "@ogw_front/utils/name_cleaner";
4
+ import { geode_objects } from "@ogw_front/assets/geode_objects";
5
+
6
+ const { displayIntermediate, intermediateItems, menuStyle } = defineProps({
7
+ displayIntermediate: { type: Boolean, required: true },
8
+ intermediateItems: { type: Array, required: true },
9
+ menuStyle: { type: Object, required: true },
10
+ });
11
+
12
+ const emit = defineEmits(["select", "update:displayIntermediate"]);
13
+
14
+ function selectItem(item) {
15
+ emit("select", item);
16
+ }
17
+
18
+ function handleUpdate(val) {
19
+ emit("update:displayIntermediate", val);
20
+ }
21
+ </script>
22
+
23
+ <template>
24
+ <v-menu
25
+ :model-value="displayIntermediate"
26
+ :close-on-content-click="true"
27
+ :style="menuStyle"
28
+ :overlay="false"
29
+ @update:model-value="handleUpdate"
30
+ >
31
+ <GlassCard
32
+ variant="panel"
33
+ padding="pa-0"
34
+ rounded="lg"
35
+ class="elevation-12"
36
+ min-width="260"
37
+ max-width="340"
38
+ >
39
+ <v-card-title
40
+ class="d-flex align-center py-2 px-3 text-caption text-uppercase font-weight-black text-medium-emphasis"
41
+ >
42
+ <v-icon icon="mdi-layers-triple" size="small" class="mr-2" color="secondary" />
43
+ Overlapping objects
44
+ </v-card-title>
45
+
46
+ <v-divider />
47
+
48
+ <v-list class="py-1 bg-transparent" density="compact">
49
+ <v-list-item
50
+ v-for="item in intermediateItems"
51
+ :key="`${item.id}-${item.viewer_id}`"
52
+ class="intermediate-picker-item px-3 py-2"
53
+ @click="selectItem(item)"
54
+ >
55
+ <template #prepend>
56
+ <v-img
57
+ v-if="geode_objects[item.geode_object_type]?.image"
58
+ :src="geode_objects[item.geode_object_type].image"
59
+ height="24"
60
+ width="24"
61
+ max-width="24"
62
+ class="mr-3"
63
+ style="object-fit: contain; filter: brightness(0) invert(1)"
64
+ />
65
+ <v-icon v-else icon="mdi-cube-outline" size="24" color="white" class="mr-3" />
66
+ </template>
67
+
68
+ <v-list-item-title class="font-weight-bold text-body-2 text-truncate text-white">
69
+ {{ item.name }}
70
+ </v-list-item-title>
71
+ <v-list-item-subtitle
72
+ class="text-caption text-truncate text-medium-emphasis mt-0.5 d-flex align-center"
73
+ >
74
+ <span class="font-weight-medium mr-1">{{ item.geode_object_type }}</span>
75
+ <span style="font-family: monospace; opacity: 0.65; font-size: 0.72rem"
76
+ >&middot; {{ formatListId(item.id) }}</span
77
+ >
78
+ </v-list-item-subtitle>
79
+ </v-list-item>
80
+ </v-list>
81
+ </GlassCard>
82
+ </v-menu>
83
+ </template>
84
+
85
+ <style scoped>
86
+ .intermediate-picker-item {
87
+ transition: all 0.2s ease;
88
+ border-radius: 8px !important;
89
+ margin: 2px 6px;
90
+ }
91
+
92
+ .intermediate-picker-item:hover {
93
+ background: rgba(var(--v-theme-secondary), 0.1) !important;
94
+ transform: translateX(4px);
95
+ }
96
+ </style>
@@ -1,6 +1,6 @@
1
1
  <script setup>
2
2
  import PointSetPoints from "@ogw_front/assets/viewer_svgs/point_set_points.svg";
3
- import ViewerContextMenuItem from "@ogw_front/components/Viewer/ContextMenuItem";
3
+ import ViewerContextMenuItem from "@ogw_front/components/Viewer/ContextMenu/ContextMenuItem";
4
4
  import ViewerOptionsColoringTypeSelector from "@ogw_front/components/Viewer/Options/ColoringTypeSelector";
5
5
  import ViewerOptionsVisibilitySwitch from "@ogw_front/components/Viewer/Options/VisibilitySwitch";
6
6
 
@@ -1,6 +1,6 @@
1
1
  <script setup>
2
2
  import SolidPolyhedra from "@ogw_front/assets/viewer_svgs/solid_polyhedra.svg";
3
- import ViewerContextMenuItem from "@ogw_front/components/Viewer/ContextMenuItem";
3
+ import ViewerContextMenuItem from "@ogw_front/components/Viewer/ContextMenu/ContextMenuItem";
4
4
  import ViewerOptionsColoringTypeSelector from "@ogw_front/components/Viewer/Options/ColoringTypeSelector";
5
5
  import ViewerOptionsVisibilitySwitch from "@ogw_front/components/Viewer/Options/VisibilitySwitch";
6
6
 
@@ -1,6 +1,6 @@
1
1
  <script setup>
2
2
  import PolygonalSurfacePolygons from "@ogw_front/assets/viewer_svgs/surface_triangles.svg";
3
- import ViewerContextMenuItem from "@ogw_front/components/Viewer/ContextMenuItem";
3
+ import ViewerContextMenuItem from "@ogw_front/components/Viewer/ContextMenu/ContextMenuItem";
4
4
  import ViewerOptionsColoringTypeSelector from "@ogw_front/components/Viewer/Options/ColoringTypeSelector";
5
5
  import ViewerOptionsVisibilitySwitch from "@ogw_front/components/Viewer/Options/VisibilitySwitch";
6
6
 
@@ -1,11 +1,11 @@
1
1
  <script setup>
2
- import ViewerContextMenu from "@ogw_front/components/Viewer/ContextMenu";
2
+ import OverlappingObjectsPicker from "@ogw_front/components/Viewer/OverlappingObjectsPicker";
3
+ import ViewerContextMenu from "@ogw_front/components/Viewer/ContextMenu/ContextMenu";
3
4
  import ViewerObjectTreeLayout from "@ogw_front/components/Viewer/ObjectTree/Layout";
4
- import { useDataStore } from "@ogw_front/stores/data";
5
- import { useDataStyleStore } from "@ogw_front/stores/data_style";
5
+ import { getCurrentInstance } from "vue";
6
6
  import { useMenuStore } from "@ogw_front/stores/menu";
7
+ import { useOverlappingPicker } from "@ogw_front/composables/use_overlapping_picker";
7
8
  import { useViewerStore } from "@ogw_front/stores/viewer";
8
- import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json";
9
9
 
10
10
  const { displayMenu, containerWidth, containerHeight } = defineProps({
11
11
  displayMenu: { type: Boolean, required: true },
@@ -14,29 +14,35 @@ const { displayMenu, containerWidth, containerHeight } = defineProps({
14
14
  });
15
15
 
16
16
  const emit = defineEmits(["show-menu"]);
17
- const dataStore = useDataStore();
18
- const dataStyleStore = useDataStyleStore();
19
- const viewerStore = useViewerStore();
20
17
  const menuStore = useMenuStore();
21
- const dataItems = dataStore.refAllItems();
18
+ const viewerStore = useViewerStore();
19
+
20
+ const {
21
+ displayIntermediate,
22
+ intermediateItems,
23
+ getIntermediateMenuStyle,
24
+ selectIntermediateItem,
25
+ handleIntermediateMenuUpdate,
26
+ get_viewer_id: trigger_picker,
27
+ } = useOverlappingPicker();
22
28
 
23
- async function get_viewer_id(x, y) {
24
- const activeIds = new Set(dataItems.value.map((item) => item.id));
25
- const ids = Object.keys(dataStyleStore.styles).filter((styleId) => activeIds.has(styleId));
29
+ function get_viewer_id(x, y) {
30
+ const instance = getCurrentInstance();
31
+ const containerRect = instance?.proxy?.$el
32
+ ?.closest?.('[data-testid="hybridViewer"]')
33
+ ?.getBoundingClientRect() ||
34
+ document.querySelector('[data-testid="hybridViewer"]')?.getBoundingClientRect() || {
35
+ left: 0,
36
+ top: 0,
37
+ };
26
38
 
27
- let result = { id: undefined, viewer_id: undefined };
28
- await viewerStore.request(
29
- viewer_schemas.opengeodeweb_viewer.viewer.picked_ids,
30
- { x, y, ids },
31
- {
32
- response_function: (response) => {
33
- const { array_ids, viewer_id } = response;
34
- const [first_id] = array_ids;
35
- result = { id: first_id, viewer_id };
36
- },
37
- },
38
- );
39
- return result;
39
+ return trigger_picker({
40
+ x,
41
+ y,
42
+ containerWidth,
43
+ containerHeight,
44
+ containerRect,
45
+ });
40
46
  }
41
47
 
42
48
  defineExpose({ get_viewer_id });
@@ -56,6 +62,14 @@ defineExpose({ get_viewer_id });
56
62
  :container-height="containerHeight"
57
63
  />
58
64
 
65
+ <OverlappingObjectsPicker
66
+ :display-intermediate="displayIntermediate"
67
+ :intermediate-items="intermediateItems"
68
+ :menu-style="getIntermediateMenuStyle()"
69
+ @select="selectIntermediateItem"
70
+ @update:display-intermediate="handleIntermediateMenuUpdate"
71
+ />
72
+
59
73
  <v-fade-transition>
60
74
  <div
61
75
  v-if="viewerStore.picking_mode"
@@ -29,7 +29,7 @@ async function exportProject() {
29
29
  return { result };
30
30
  }
31
31
 
32
- async function importProject(project) {
32
+ async function importProject(file) {
33
33
  const geodeStore = useGeodeStore();
34
34
  const dataStyleStore = useDataStyleStore();
35
35
  const viewerStore = useViewerStore();
@@ -56,11 +56,11 @@ async function importProject(project) {
56
56
 
57
57
  const schemaImport = back_schemas.opengeodeweb_back.import_project;
58
58
  const form = new FormData();
59
- const originalFileName = project && project.name ? project.name : "project.vease";
59
+ const originalFileName = file && file.name ? file.name : "project.vease";
60
60
  if (!originalFileName.toLowerCase().endsWith(".vease")) {
61
61
  throw new Error("Uploaded file must be a .vease");
62
62
  }
63
- form.append("file", project, originalFileName);
63
+ form.append("file", file, originalFileName);
64
64
 
65
65
  const result = await $fetch(schemaImport.$id, {
66
66
  baseURL: geodeStore.base_url,
@@ -15,9 +15,54 @@ const MIN_BOOST = 1;
15
15
  const MAX_BOOST = 1.2;
16
16
  const ADAPTIVE_REFRESH_RATE = 150;
17
17
 
18
- export function useAdaptiveStyles(targetRef) {
18
+ function getValue(val) {
19
+ if (typeof val === "object" && val !== null && val.value !== undefined) {
20
+ return val.value;
21
+ }
22
+ return val ?? 0;
23
+ }
24
+
25
+ export function useAdaptiveStyles(target, options = {}) {
19
26
  const hybridViewerStore = useHybridViewerStore();
20
- const { x, y, width, height } = useElementBounding(targetRef);
27
+
28
+ const isCoordinates =
29
+ target &&
30
+ (typeof target === "function" ||
31
+ (target.value !== undefined && target.value !== null && target.value.x !== undefined) ||
32
+ (target.x !== undefined && target.value === undefined));
33
+
34
+ const bounding = useElementBounding(isCoordinates ? undefined : target);
35
+
36
+ const unwrapped = computed(() => {
37
+ if (isCoordinates) {
38
+ let val = undefined;
39
+ if (typeof target === "function") {
40
+ val = target();
41
+ } else if (target.value === undefined) {
42
+ val = target;
43
+ } else {
44
+ val = target.value;
45
+ }
46
+ return {
47
+ x: getValue(val?.x),
48
+ y: getValue(val?.y),
49
+ width: getValue(val?.width),
50
+ height: getValue(val?.height),
51
+ };
52
+ }
53
+ return {
54
+ x: bounding.x.value,
55
+ y: bounding.y.value,
56
+ width: bounding.width.value,
57
+ height: bounding.height.value,
58
+ };
59
+ });
60
+
61
+ const x = computed(() => unwrapped.value.x);
62
+ const y = computed(() => unwrapped.value.y);
63
+ const width = computed(() => unwrapped.value.width);
64
+ const height = computed(() => unwrapped.value.height);
65
+
21
66
  const brightness = ref(LUMINANCE_THRESHOLD);
22
67
 
23
68
  const updateBrightness = useThrottleFn(() => {
@@ -38,7 +83,9 @@ export function useAdaptiveStyles(targetRef) {
38
83
  const darkFactor = (1 - normalized) ** ADAPTIVE_EXPONENT;
39
84
 
40
85
  const blur = MIN_BLUR + darkFactor * (MAX_BLUR - MIN_BLUR);
41
- const opacity = MIN_OPACITY + darkFactor * (MAX_OPACITY - MIN_OPACITY);
86
+ const minOpacity = options.minOpacity ?? MIN_OPACITY;
87
+ const maxOpacity = options.maxOpacity ?? MAX_OPACITY;
88
+ const opacity = minOpacity + darkFactor * (maxOpacity - minOpacity);
42
89
  const brightnessBoost = MIN_BOOST + darkFactor * (MAX_BOOST - MIN_BOOST);
43
90
 
44
91
  return {