fix(frontend): embed viewer bugfixes & speed improvements

This commit is contained in:
Fabians
2022-06-15 12:14:47 +03:00
parent 8d7f97b2cd
commit 79708b9754
20 changed files with 531 additions and 366 deletions
+2 -1
View File
@@ -36,7 +36,8 @@
"resolutions": {
"tslib": "^2.3.1",
"core-js": "3.22.4",
"vue-cli-plugin-apollo/graphql": "^15"
"vue-cli-plugin-apollo/graphql": "^15",
"typescript": "^4.5.4"
},
"config": {
"commitizen": {
+2
View File
@@ -68,6 +68,8 @@
"@mdi/font": "^5.8.55",
"@rushstack/eslint-patch": "^1.1.3",
"@types/lodash": "^4.14.180",
"@types/mixpanel-browser": "^2.38.0",
"@types/node": "^17.0.43",
"@typescript-eslint/eslint-plugin": "^5.21.0",
"@typescript-eslint/parser": "^5.21.0",
"@vue/cli": "^4.5.17",
+136 -244
View File
@@ -1,138 +1,66 @@
<template>
<!--
HIC SVNT DRACONES
this needs some cleanup, possibly even moving to the main app,
and ensuring local storage absence handling is properly done
-->
<v-app
:class="`no-scrollbar ${
:class="`embed-viewer no-scrollbar ${
$vuetify.theme.dark ? 'background-dark' : 'background-light'
}`"
>
<div v-if="!error" style="z-index: 10">
<div
class="top-left bottom-left pa-4"
style="right: 0px; position: fixed; z-index: 100000"
>
<span v-show="!drawer" class="caption d-inline-flex align-center">
<img src="@/assets/logo.svg" height="18" />
<span style="margin-top: 2px" class="primary--text">
<a href="https://speckle.xyz" target="_blank" class="text-decoration-none">
<b>Powered by Speckle</b>
</a>
</span>
</span>
<br />
</div>
<div v-show="!drawer && loadedModel" class="caption grey--text pa-2">
<v-btn fab small @click="drawer = true">
<v-icon>mdi-menu</v-icon>
</v-btn>
</div>
<div
class="pa-2 d-flex align-center justify-space-between caption"
style="position: fixed; bottom: 0; width: 100%"
>
<portal to="viewercontrols">
<v-btn
v-if="stream && serverInfo"
v-tooltip="'View extra details in Speckle!'"
icon
dark
large
class="elevation-5 primary pa-0 ma-o"
:href="goToServerUrl"
target="blank"
>
<v-icon dark small>mdi-open-in-new</v-icon>
</v-btn>
</portal>
</div>
<div
:style="`width: 100%; bottom: 12px; left: 0px; position: ${
$isMobile() ? 'fixed' : 'absolute'
}; z-index: 20`"
:class="`d-flex justify-center`"
>
<viewer-controls v-show="loadedModel" />
</div>
</div>
<!-- BG image -->
<div
v-if="!loadedModel"
v-if="!isModelLoaded"
ref="cover"
class="d-flex fullscreen align-center justify-center bg-img"
class="viewer-image-overlay d-flex fullscreen align-center justify-center bg-img"
/>
<!-- Play button -->
<div
v-if="!loadedModel && loadProgress > 0"
class="d-flex fullscreen align-center justify-center"
v-if="!isModelLoaded"
class="viewer-play d-flex fullscreen align-center justify-center"
>
<v-progress-linear
v-model="loadProgress"
:indeterminate="loadProgress >= 99 && !loadedModel"
<v-btn
id="viewer-play-btn"
:disabled="showPlayLoader"
fab
color="primary"
style="max-width: 30%"
></v-progress-linear>
</div>
<div
v-if="!loadedModel && loadProgress === 0"
class="d-flex fullscreen align-center justify-center"
>
<v-btn fab color="primary" class="elevation-20 hover-tada" @click="load()">
<v-icon>mdi-play</v-icon>
class="elevation-4 hover-tada"
@click="load()"
>
<v-icon v-if="!showPlayLoader">mdi-play</v-icon>
<v-icon v-else class="spinning-icon">mdi-loading</v-icon>
</v-btn>
</div>
<v-navigation-drawer
ref="drawer"
v-model="drawer"
app
floating
style="z-index: 10000"
>
<div class="mx-1 mt-4 pr-2" style="height: 100%; width: 100%">
<!-- Views display -->
<views-display v-if="views.length !== 0" :views="views" :sticky-top="false" />
<!-- Filters display -->
<viewer-filters
:props="objectProperties"
style="width: 100%"
:sticky-top="false"
/>
</div>
</v-navigation-drawer>
<div style="position: fixed" class="no-scrollbar">
<speckle-viewer @load-progress="captureProgress" />
</div>
<!-- Async loaded viewer -->
<embed-viewer-core
v-if="shouldLoadHeavyDeps"
:input="input"
:object-url="objectUrl"
@model-loaded="onModelLoaded"
@error="onError"
/>
</v-app>
</template>
<script>
import SpeckleViewer from '@/main/components/common/SpeckleViewer.vue'
import { getCommit, getLatestBranchCommit, getServerInfo } from '@/embed/speckleUtils'
<script lang="ts">
import { Nullable } from '@/helpers/typeHelpers'
import { getCommit, getLatestBranchCommit } from '@/embed/speckleUtils'
import Vue from 'vue'
export default {
/**
* TODO:
* - Move EmbedViewer back to main app? The main app has a lot of global dependencies
* that this endpoint doesn't need, tho...
* - Make speckle-viewer configurable through props/events not through window.__viewer
*/
export default Vue.extend({
name: 'EmbedViewer',
components: {
SpeckleViewer,
ViewerControls: () => import('@/main/components/viewer/ViewerControls'),
ViewsDisplay: () => import('@/main/components/viewer/ViewsDisplay'),
ViewerFilters: () => import('@/main/components/viewer/ViewerFilters.vue')
},
filters: {
truncate(str, n = 20) {
return str.length > n ? str.substr(0, n - 3) + '...' : str
}
EmbedViewerCore: () => import('@/embed/EmbedViewerCore.vue')
},
data() {
return {
drawer: false,
loadedModel: false,
loadProgress: 0,
error: null,
objectId: this.$route.query.object,
views: [],
objectProperties: null,
isModelLoaded: false,
error: null as Nullable<Error>,
input: {
stream: this.$route.query.stream,
object: this.$route.query.object,
@@ -141,19 +69,13 @@ export default {
overlay: this.$route.query.overlay,
camera: this.$route.query.c,
filter: this.$route.query.filter
},
lastCommit: null,
specificCommit: null,
serverInfo: null
} as Record<string, string>,
isInitialized: false as boolean,
shouldLoadHeavyDeps: false as boolean
}
},
computed: {
isSmall() {
return (
this.$vuetify.breakpoint.name === 'xs' || this.$vuetify.breakpoint.name === 'sm'
)
},
displayType() {
displayType(): string {
if (!this.input.stream) {
return 'error'
}
@@ -164,150 +86,95 @@ export default {
return 'stream'
},
stream() {
return this.lastCommit || this.specificCommit
objectUrl(): string {
return `${window.location.protocol}//${window.location.host}/streams/${this.input.stream}/objects/${this.input.object}`
},
objectUrl() {
return `${window.location.protocol}//${window.location.host}/streams/${this.input.stream}/objects/${this.objectId}`
},
goToServerUrl() {
const stream = this.input.stream
const base = `${window.location.origin}/streams/${stream}/`
const commit = this.input.commit
if (commit) return base + `commits/${commit}`
const object = this.objectId
if (object) return base + `objects/${object}`
const branch = this.input.branch
if (branch) return base + `branches/${encodeURI(branch)}`
return base
showPlayLoader(): boolean {
return !this.isInitialized || (this.shouldLoadHeavyDeps && !this.isModelLoaded)
}
},
watch: {
displayType(oldVal, newVal) {
if (newVal === 'error') this.error = 'Provided details were invalid'
else {
this.error = null
}
displayType(_oldVal: string, newVal: string) {
this.error =
newVal === 'error' ? new Error('Provided details were invalid') : null
},
error(newVal: Error | null) {
if (newVal) console.error(newVal)
}
},
async beforeMount() {
try {
const serverInfoResponse = await getServerInfo()
this.serverInfo = serverInfoResponse.data.serverInfo
} catch (e) {
this.error = e.message
return
}
if (this.displayType === 'commit') {
try {
const res = await getCommit(this.input.stream, this.input.commit)
const data = res.data
const latestCommit = data.stream.commit
if (this.input.object === undefined)
this.objectId = latestCommit.referencedObject
this.specificCommit = data.stream
} catch (e) {
this.error = e.message
return
// Initialize base data, which can potentially change input.object
await (this.displayType === 'commit'
? this.initializeForCommit()
: this.initializeForStream())
// Load BG image
await this.getPreviewImage()
// Mark as initialized (enable play button)
this.isInitialized = true
},
methods: {
onError(e: Error) {
this.error = e
},
onModelLoaded() {
this.isModelLoaded = true
},
load() {
if (!this.isInitialized || this.shouldLoadHeavyDeps || this.isModelLoaded) return
this.shouldLoadHeavyDeps = true
this.$mixpanel.track('Embedded Model Load', {
type: 'action'
})
},
async getPreviewImage(angle?: number) {
angle = angle || 0
const previewUrl = this.objectUrl.replace('streams', 'preview') + '/' + angle
const res = await fetch(previewUrl)
const blob = await res.blob()
const imgUrl = URL.createObjectURL(blob)
if (this.$refs.cover) {
;(this.$refs.cover as HTMLElement).style.backgroundImage = `url('${imgUrl}')`
}
} else {
},
async initializeForStream() {
try {
const res = await getLatestBranchCommit(this.input.stream, this.input.branch)
const data = res.data
const latestCommit =
data.stream.branch.commits.items[0] || data.stream.branch.commit
if (!latestCommit) {
this.error = 'No commit for this branch'
this.lastCommit = data.stream
this.error = new Error('No commit for this branch')
return
}
if (this.input.object === undefined)
this.objectId = latestCommit.referencedObject
else this.objectId = this.input.object
this.lastCommit = data.stream
} catch (e) {
this.error = e.message
console.log(e)
return
}
}
this.getPreviewImage()
},
mounted() {},
methods: {
async load() {
this.$mixpanel.track('Embedded Model Load', {
step: this.onboarding,
type: 'action'
})
await window.__viewer.loadObject(this.objectUrl)
if (this.input.overlay) {
const resIds = this.input.overlay.split(',')
for (const res of resIds) {
console.log(res)
if (res.length !== 10) {
await window.__viewer.loadObject(
`${window.location.protocol}//${window.location.host}/streams/${this.input.stream}/objects/${res}`
)
} else {
const { data } = await getCommit(this.input.stream, res)
await window.__viewer.loadObject(
`${window.location.protocol}//${window.location.host}/streams/${this.input.stream}/objects/${data.stream.commit.referencedObject}`
)
}
// Updating input.object
if (this.input.object === undefined) {
this.input.object = latestCommit.referencedObject
}
}
window.__viewer.zoomExtents(undefined, true)
this.loadedModel = true
this.views.push(...window.__viewer.sceneManager.views)
this.objectProperties = await window.__viewer.getObjectsProperties()
if (this.input.filter) {
const parsedFilter = JSON.parse(this.input.filter)
setTimeout(() => {
this.$store.commit('setFilterDirect', { filter: parsedFilter })
}, 1000)
}
if (this.input.camera) {
const cam = JSON.parse(this.input.camera)
window.__viewer.interactions.setLookAt(
{ x: cam[0], y: cam[1], z: cam[2] }, // position
{ x: cam[3], y: cam[4], z: cam[5] } // target
)
} catch (e: unknown) {
this.error = e instanceof Error ? e : new Error('An unexpected error occurred')
}
},
captureProgress(args) {
this.loadProgress = args.progress * 100
},
async getPreviewImage(angle) {
angle = angle || 0
const previewUrl = this.objectUrl.replace('streams', 'preview') + '/' + angle
let token = undefined
async initializeForCommit() {
try {
token = localStorage.getItem('AuthToken')
} catch (e) {
console.warn('Sanboxed mode, only public streams will fetch properly.')
const res = await getCommit(this.input.stream, this.input.commit)
const data = res.data
const latestCommit = data.stream.commit
// Updating input.object
if (this.input.object === undefined) {
this.input.object = latestCommit.referencedObject
}
} catch (e: unknown) {
this.error = e instanceof Error ? e : new Error('An unexpected error occurred')
}
const res = await fetch(previewUrl, {
headers: token ? { Authorization: `Bearer ${token}` } : {}
})
const blob = await res.blob()
const imgUrl = URL.createObjectURL(blob)
if (this.$refs.cover) this.$refs.cover.style.backgroundImage = `url('${imgUrl}')`
this.hasImg = true
}
}
}
})
</script>
<style lang="scss">
@@ -319,26 +186,51 @@ body::-webkit-scrollbar {
height: 100vh !important;
width: 100vw !important;
position: fixed;
&::-webkit-scrollbar {
display: none;
}
top: 0;
left: 0;
z-index: 10;
}
.no-scrollbar {
width: 100vw;
height: 100vh;
overflow: hidden;
&::-webkit-scrollbar {
display: none;
}
}
.bg-img {
background-position: center;
background-repeat: no-repeat;
/*background-attachment: fixed;*/
filter: blur(2px);
}
.no-events {
pointer-events: none;
#viewer-play-btn {
@keyframes spinner-spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
// kinda hacky, but vuetify renders the disabled state in a stupid way that relies
// on the background color of the thing behind the button
&.v-btn--fab.v-btn--disabled {
background-color: #242424 !important;
}
.spinning-icon {
animation: spinner-spin 0.5s linear infinite;
}
}
</style>
@@ -0,0 +1,269 @@
<template>
<div class="embed-viewer-core">
<!-- Viewer navbar (position fixed) -->
<div v-if="!error" style="z-index: 10" class="viewer-navbar">
<div
class="top-left bottom-left pa-4"
style="right: 0px; position: fixed; z-index: 100000"
>
<span v-show="!drawer" class="caption d-inline-flex align-center">
<img src="@/assets/logo.svg" height="18" />
<span style="margin-top: 2px" class="primary--text">
<a href="https://speckle.xyz" target="_blank" class="text-decoration-none">
<b>Powered by Speckle</b>
</a>
</span>
</span>
<br />
</div>
<div v-show="!drawer && loadedModel" class="caption grey--text pa-2">
<v-btn fab small @click="drawer = true">
<v-icon>mdi-menu</v-icon>
</v-btn>
</div>
<div
class="pa-2 d-flex align-center justify-space-between caption"
style="position: fixed; bottom: 0; width: 100%"
>
<portal to="viewercontrols">
<v-btn
v-tooltip="'View extra details in Speckle!'"
icon
dark
large
class="elevation-5 primary pa-0 ma-o"
:href="goToServerUrl"
target="blank"
>
<v-icon dark small>mdi-open-in-new</v-icon>
</v-btn>
</portal>
</div>
<div
:style="`width: 100%; bottom: 12px; left: 0px; position: ${
$isMobile() ? 'fixed' : 'absolute'
}; z-index: 20`"
:class="`d-flex justify-center`"
>
<viewer-controls v-show="loadedModel" />
</div>
</div>
<!-- Model loading progress bar -->
<div
v-if="!loadedModel && loadProgress > 0"
class="viewer-loader d-flex fullscreen align-center justify-center"
>
<v-progress-linear
v-model="loadProgress"
:indeterminate="loadProgress >= 99 && !loadedModel"
color="primary"
style="max-width: 30%"
></v-progress-linear>
</div>
<!-- Viewer filters panel / sidebar -->
<v-navigation-drawer
v-model="drawer"
class="viewer-controls-drawer"
app
floating
style="z-index: 10000"
>
<div class="px-1 pt-1 d-flex flex-column" style="height: 100%; width: 100%">
<!-- Drawer closer -->
<v-btn icon small class="align-self-end mb-2" @click="drawer = false">
<v-icon x-small>mdi-close</v-icon>
</v-btn>
<!-- Views display -->
<views-display v-if="views.length !== 0" :views="views" :sticky-top="false" />
<!-- Filters display -->
<viewer-filters
:props="objectProperties"
style="width: 100%"
:sticky-top="false"
/>
</div>
</v-navigation-drawer>
<!-- Actual viewer -->
<div style="position: fixed" class="viewer-wrapper no-scrollbar">
<speckle-viewer @load-progress="captureProgress" @viewer-init="onViewerInit" />
</div>
</div>
</template>
<script lang="ts">
import { Nullable } from '@/helpers/typeHelpers'
import Vue, { PropType } from 'vue'
import { getCommit } from '@/embed/speckleUtils'
import SpeckleViewer from '@/main/components/common/SpeckleViewer.vue'
import ViewerControls from '@/main/components/viewer/ViewerControls.vue'
import ViewsDisplay from '@/main/components/viewer/ViewsDisplay.vue'
import ViewerFilters from '@/main/components/viewer/ViewerFilters.vue'
/**
* Core embed viewer functionality with all of the heavy JS dependencies has been extracted to this component,
* so that we can lazy-load it only when the user clicks on the "play" button
*
* Make sure this component isn't initialized until it's actually needed and don't put any heavy deps
* inside EmbedViewer.vue (or asynchronize them)
*/
type UnknownObject = Record<string, unknown>
type EmbedViewerInput = {
stream: string
object: Nullable<string>
branch: Nullable<string>
commit: Nullable<string>
overlay: Nullable<string>
camera: Nullable<string>
filter: Nullable<string>
}
export default Vue.extend({
name: 'EmbedViewerCore',
components: {
SpeckleViewer,
ViewerControls,
ViewsDisplay,
ViewerFilters
},
props: {
input: {
type: Object as PropType<EmbedViewerInput>,
required: true
},
objectUrl: {
type: String,
required: true
}
},
data() {
return {
error: null as Nullable<Error>,
drawer: false,
loadedModel: false,
loadProgress: 0,
views: [] as Array<UnknownObject>,
objectProperties: null as Nullable<UnknownObject>
}
},
computed: {
goToServerUrl(): string {
const stream = this.input.stream
const base = `${window.location.origin}/streams/${stream}/`
const commit = this.input.commit
if (commit) return base + `commits/${commit}`
const object = this.input.object
if (object) return base + `objects/${object}`
const branch = this.input.branch
if (branch) return base + `branches/${encodeURI(branch)}`
return base
}
},
watch: {
error(newVal: Error | null) {
if (newVal) this.$emit('error', newVal)
}
},
methods: {
captureProgress(args: { progress: number; id: number; url: string }) {
this.loadProgress = args.progress * 100
},
markModelLoaded() {
this.loadedModel = true
this.$emit('model-loaded')
},
async onViewerInit() {
if (!window.__viewer) {
throw new Error('Viewer instance unavailable')
}
await window.__viewer.loadObject(this.objectUrl)
const overlayPromises = []
if (this.input.overlay) {
const resIds = this.input.overlay.split(',')
for (const res of resIds) {
if (res.length !== 10) {
overlayPromises.push(
window.__viewer.loadObject(
`${window.location.protocol}//${window.location.host}/streams/${this.input.stream}/objects/${res}`
)
)
} else {
overlayPromises.push(
getCommit(this.input.stream, res).then(({ data }) => {
return window.__viewer!.loadObject(
`${window.location.protocol}//${window.location.host}/streams/${this.input.stream}/objects/${data.stream.commit.referencedObject}`
)
})
)
}
}
}
await Promise.all(overlayPromises)
window.__viewer.zoomExtents(undefined, true)
this.markModelLoaded()
this.views.push(...window.__viewer.sceneManager.views)
this.objectProperties = await window.__viewer.getObjectsProperties()
if (this.input.filter) {
const parsedFilter = JSON.parse(this.input.filter)
setTimeout(() => {
this.$store.commit('setFilterDirect', { filter: parsedFilter })
}, 1000)
}
if (this.input.camera) {
const cam = JSON.parse(this.input.camera)
window.__viewer.interactions.setLookAt(
{ x: cam[0], y: cam[1], z: cam[2] }, // position
{ x: cam[3], y: cam[4], z: cam[5] } // target
)
}
}
}
})
</script>
<style lang="scss">
.embed-viewer-core {
position: relative;
width: 100%;
height: 100%;
.fullscreen {
height: 100vh !important;
width: 100vw !important;
position: fixed;
&::-webkit-scrollbar {
display: none;
}
top: 0;
left: 0;
z-index: 10;
}
.no-scrollbar {
width: 100vw;
height: 100vh;
overflow: hidden;
&::-webkit-scrollbar {
display: none;
}
}
}
</style>
+2 -16
View File
@@ -1,27 +1,13 @@
import Vue from 'vue'
import '@/vueBootstrapper'
import App from './EmbedApp.vue'
import vuetify from './embedVuetify'
import router from './embedRouter'
// process.env.NODE_ENV is injected by Webpack
// eslint-disable-next-line no-undef
Vue.config.productionTip = process.env.NODE_ENV === 'development'
import VueMixpanel from 'vue-mixpanel'
Vue.use(VueMixpanel, {
token: 'acd87c5a50b56df91a795e999812a3a4',
config: {
// eslint-disable-next-line camelcase
api_host: 'https://analytics.speckle.systems'
}
})
import '@/plugins/helpers'
import store from '@/main/store'
import PortalVue from 'portal-vue'
Vue.use(PortalVue)
new Vue({
router,
vuetify,
+1 -26
View File
@@ -1,7 +1,5 @@
import Vue from 'vue'
// Event hub
Vue.prototype.$eventHub = new Vue()
import '@/vueBootstrapper'
import App from '@/main/App.vue'
import store from '@/main/store'
@@ -16,13 +14,6 @@ import {
import router from '@/main/router/index'
import vuetify from '@/plugins/vuetify'
// process.env.NODE_ENV is injected by Webpack
// eslint-disable-next-line no-undef
Vue.config.productionTip = process.env.NODE_ENV === 'development'
import PortalVue from 'portal-vue'
Vue.use(PortalVue)
import VueTimeago from 'vue-timeago'
Vue.use(VueTimeago, { locale: 'en' })
@@ -37,22 +28,6 @@ import 'vue2-perfect-scrollbar/dist/vue2-perfect-scrollbar.css'
Vue.use(PerfectScrollbar)
import VTooltip from 'v-tooltip'
Vue.use(VTooltip, {
defaultDelay: 300,
defaultBoundariesElement: document.body,
defaultHtml: false
})
import VueMixpanel from 'vue-mixpanel'
Vue.use(VueMixpanel, {
token: 'acd87c5a50b56df91a795e999812a3a4',
config: {
// eslint-disable-next-line camelcase
api_host: 'https://analytics.speckle.systems'
}
})
// Async HistogramSlider load
Vue.component('HistogramSlider', async () => {
await import(
@@ -0,0 +1,6 @@
declare module 'vue-mixpanel' {
declare const test: string
declare const plugin: import('vue').PluginFunction<unknown>
export default plugin
export { test }
}
-12
View File
@@ -1,12 +0,0 @@
/* eslint-disable @typescript-eslint/no-empty-interface */
import Vue, { VNode } from 'vue'
declare global {
namespace JSX {
interface Element extends VNode {}
interface ElementClass extends Vue {}
interface IntrinsicElements {
[elem: string]: unknown
}
}
}
+7
View File
@@ -0,0 +1,7 @@
declare module 'vue/types/vue' {
export interface Vue {
$mixpanel: import('mixpanel-browser').OverridedMixpanel
}
}
export {}
+10
View File
@@ -0,0 +1,10 @@
export {}
declare global {
interface Window {
/**
* Initialized in SpeckleViewer.vue
*/
__viewer?: import('@speckle/viewer').Viewer
}
}
+30
View File
@@ -0,0 +1,30 @@
import Vue from 'vue'
import VTooltip from 'v-tooltip'
import VueMixpanel from 'vue-mixpanel'
import PortalVue from 'portal-vue'
/**
* Global Vue bootstrapping that is used in all of the frontend apps (main/embed)
*/
// process.env.NODE_ENV is injected by Webpack
Vue.config.productionTip = process.env.NODE_ENV === 'development'
Vue.use(VTooltip, {
defaultDelay: 300,
defaultBoundariesElement: document.body,
defaultHtml: false
})
Vue.use(VueMixpanel, {
token: 'acd87c5a50b56df91a795e999812a3a4',
config: {
// eslint-disable-next-line camelcase
api_host: 'https://analytics.speckle.systems'
}
})
Vue.use(PortalVue)
// Event hub
Vue.prototype.$eventHub = new Vue()
+5 -1
View File
@@ -11,6 +11,7 @@
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"allowJs": true,
"noEmit": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
@@ -21,7 +22,10 @@
"vue-apollo-smart-ops",
"vue-infinite-loading",
"type-fest",
"vue"
"vue",
"@types/node",
"@types/mixpanel-browser",
"vuetify"
]
},
"include": [
+1 -1
View File
@@ -14,7 +14,7 @@
"node": ">=14.0.0 <17.0.0"
},
"scripts": {
"dev": "PG_CONNECTION_STRING=postgresql://localhost:5432/speckle2_dev DEBUG='preview-service:*' nodemon --trace-deprecation ./bin/www",
"dev": "DEBUG='preview-service:*' nodemon --trace-deprecation ./bin/www",
"build": "webpack --env dev --config webpack.config.render_page.js && webpack --env build --config webpack.config.render_page.js",
"lint": "eslint . --ext .js,.ts"
},
-15
View File
@@ -1,15 +0,0 @@
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "entry",
"corejs": "3",
"targets": {
"node": "12"
}
}
]
],
"ignore": ["node_modules/**/*"]
}
+7 -1
View File
@@ -1,3 +1,5 @@
const path = require('path')
/**
* Extends repo root config, only put changes here that are scoped to this specific package
* (if you're already are - evaluate whether you really need package scoped linting rules)
@@ -8,8 +10,12 @@ const config = {
env: {
browser: true
},
parser: '@babel/eslint-parser',
parserOptions: {
sourceType: 'module'
sourceType: 'module',
babelOptions: {
configFile: path.resolve(__dirname, './babel.config.js')
}
},
rules: {
'no-console': ['warn', { allow: ['warn', 'error'] }]
+15
View File
@@ -0,0 +1,15 @@
module.exports = {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'entry',
corejs: '3',
targets: {
node: '11'
}
}
]
],
ignore: ['node_modules/**/*']
}
+1
View File
@@ -49,6 +49,7 @@
},
"devDependencies": {
"@babel/core": "^7.18.2",
"@babel/eslint-parser": "^7.18.2",
"@rollup/plugin-babel": "^5.3.1",
"@types/three": "^0.136.0",
"@typescript-eslint/eslint-plugin": "^5.21.0",
@@ -13,10 +13,11 @@ import { getConversionFactor } from './converter/Units'
* Manages objects and provides some convenience methods to focus on the entire scene, or one specific object.
*/
export default class SceneObjectManager {
views = []
constructor(viewer, skipPostLoad = false) {
this.viewer = viewer
this.scene = viewer.scene
this.views = []
this.sceneObjects = new SceneObjects(viewer)
+1 -5
View File
@@ -270,11 +270,7 @@ export class Viewer extends EventEmitter implements IViewer {
this.cameraHandler.toggleCameras()
}
public async loadObject(
url: string,
token: string | undefined,
enableCaching = true
) {
public async loadObject(url: string, token?: string, enableCaching = true) {
try {
if (++this.inProgressOperations === 1) (this as EventEmitter).emit('busy', true)
+34 -43
View File
@@ -310,6 +310,20 @@ __metadata:
languageName: node
linkType: hard
"@babel/eslint-parser@npm:^7.18.2":
version: 7.18.2
resolution: "@babel/eslint-parser@npm:7.18.2"
dependencies:
eslint-scope: ^5.1.1
eslint-visitor-keys: ^2.1.0
semver: ^6.3.0
peerDependencies:
"@babel/core": ">=7.11.0"
eslint: ^7.5.0 || ^8.0.0
checksum: dc9328cf3304b25c9029682e6b6196761e18d3ab80d66c3085a69c6f240fa2db91b824a61672e94139e73683b7ceeefe9ff58acac1ee89fe73274007b16e43d5
languageName: node
linkType: hard
"@babel/generator@npm:7.17.10, @babel/generator@npm:^7.17.10":
version: 7.17.10
resolution: "@babel/generator@npm:7.17.10"
@@ -3906,6 +3920,8 @@ __metadata:
"@tiptap/vue-2": ^2.0.0-beta.79
"@tryghost/content-api": ^1.5.12
"@types/lodash": ^4.14.180
"@types/mixpanel-browser": ^2.38.0
"@types/node": ^17.0.43
"@typescript-eslint/eslint-plugin": ^5.21.0
"@typescript-eslint/parser": ^5.21.0
"@vue/cli": ^4.5.17
@@ -4134,6 +4150,7 @@ __metadata:
resolution: "@speckle/viewer@workspace:packages/viewer"
dependencies:
"@babel/core": ^7.18.2
"@babel/eslint-parser": ^7.18.2
"@rollup/plugin-babel": ^5.3.1
"@speckle/objectloader": "workspace:^"
"@types/three": ^0.136.0
@@ -4785,6 +4802,13 @@ __metadata:
languageName: node
linkType: hard
"@types/mixpanel-browser@npm:^2.38.0":
version: 2.38.0
resolution: "@types/mixpanel-browser@npm:2.38.0"
checksum: 1ade271188446005644cf10b65af554a9a75bd2008b827db4cdcfb2eedf858e2426a4d3d21ccf5b0a25a8590f8c3095da25549cfb7765077002e03e64afa9f0a
languageName: node
linkType: hard
"@types/mocha@npm:^7.0.2":
version: 7.0.2
resolution: "@types/mocha@npm:7.0.2"
@@ -4823,6 +4847,13 @@ __metadata:
languageName: node
linkType: hard
"@types/node@npm:^17.0.43":
version: 17.0.43
resolution: "@types/node@npm:17.0.43"
checksum: 7fd84b1e37dc406c1c73a1461ccd6a1a38a5a335563788dd25e0aa75476d6948f815d0e9e4c2201f785a36a72269e6aaf0d851ca75c1e44b92e3a9d504e04a1e
languageName: node
linkType: hard
"@types/normalize-package-data@npm:^2.4.0":
version: 2.4.1
resolution: "@types/normalize-package-data@npm:2.4.1"
@@ -12329,7 +12360,7 @@ __metadata:
languageName: node
linkType: hard
"eslint-visitor-keys@npm:^2.0.0":
"eslint-visitor-keys@npm:^2.0.0, eslint-visitor-keys@npm:^2.1.0":
version: 2.1.0
resolution: "eslint-visitor-keys@npm:2.1.0"
checksum: e3081d7dd2611a35f0388bbdc2f5da60b3a3c5b8b6e928daffff7391146b434d691577aa95064c8b7faad0b8a680266bcda0a42439c18c717b80e6718d7e267d
@@ -25896,7 +25927,7 @@ __metadata:
languageName: node
linkType: hard
"typescript@npm:^4.4.3, typescript@npm:^4.5.4":
"typescript@npm:^4.5.4":
version: 4.6.4
resolution: "typescript@npm:4.6.4"
bin:
@@ -25906,27 +25937,7 @@ __metadata:
languageName: node
linkType: hard
"typescript@npm:^4.4.4":
version: 4.7.3
resolution: "typescript@npm:4.7.3"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
checksum: fd13a1ce53790a36bb8350e1f5e5e384b5f6cb9b0635114a6d01d49cb99916abdcfbc13c7521cdae2f2d3f6d8bc4a8ae7625edf645a04ee940588cd5e7597b2f
languageName: node
linkType: hard
"typescript@npm:~4.1.5":
version: 4.1.6
resolution: "typescript@npm:4.1.6"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
checksum: 54aed909f94b16178c8a8d8911871b4e1c04454a3e6c82166715e28083e7ce6271e4d1df6f82c89544a4759b07aec780785032534e9c93b254e2107a18712c05
languageName: node
linkType: hard
"typescript@patch:typescript@^4.4.3#~builtin<compat/typescript>, typescript@patch:typescript@^4.5.4#~builtin<compat/typescript>":
"typescript@patch:typescript@npm%3A^4.5.4#~builtin<compat/typescript>":
version: 4.6.4
resolution: "typescript@patch:typescript@npm%3A4.6.4#~builtin<compat/typescript>::version=4.6.4&hash=bda367"
bin:
@@ -25936,26 +25947,6 @@ __metadata:
languageName: node
linkType: hard
"typescript@patch:typescript@^4.4.4#~builtin<compat/typescript>":
version: 4.7.3
resolution: "typescript@patch:typescript@npm%3A4.7.3#~builtin<compat/typescript>::version=4.7.3&hash=bda367"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
checksum: 8257ce7ecbbf9416da60045a76a99d473698ca9e973fa0ddab7137cacb1587255431176cbbcc801a650938c4dc8109ab88355774829a714fabe56a53a2fe4524
languageName: node
linkType: hard
"typescript@patch:typescript@~4.1.5#~builtin<compat/typescript>":
version: 4.1.6
resolution: "typescript@patch:typescript@npm%3A4.1.6#~builtin<compat/typescript>::version=4.1.6&hash=bda367"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
checksum: 3bd9915f236817e4e2d32dd0d90e8902875929f014bb87a478000e32adda91d12f0425931ee6f9d6a2bc7d0c9242588fcee1050ac294497dfabf27d3d73b335c
languageName: node
linkType: hard
"typical@npm:^4.0.0":
version: 4.0.0
resolution: "typical@npm:4.0.0"