Merge branch 'main' into dim/frontend-fiddle
This commit is contained in:
Vendored
+78
-87
@@ -1,91 +1,82 @@
|
||||
{
|
||||
"window.openFilesInNewWindow": "off",
|
||||
"explorer.confirmDelete": false,
|
||||
// "[vue]": {
|
||||
// "editor.formatOnSave": true,
|
||||
// },
|
||||
"files.associations": {
|
||||
"*.vue": "vue"
|
||||
"window.openFilesInNewWindow": "off",
|
||||
"explorer.confirmDelete": false,
|
||||
// "[vue]": {
|
||||
// "editor.formatOnSave": true,
|
||||
// },
|
||||
"files.associations": {
|
||||
"*.vue": "vue"
|
||||
},
|
||||
"editor.formatOnPaste": true,
|
||||
"editor.multiCursorModifier": "ctrlCmd",
|
||||
"editor.snippetSuggestions": "top",
|
||||
"eslint.format.enable": true,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
|
||||
},
|
||||
"[jsonc]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"vetur.format.defaultFormatterOptions": {
|
||||
"js-beautify-html": {
|
||||
"wrap_attributes": "force-expand-multiline"
|
||||
},
|
||||
"editor.formatOnPaste": true,
|
||||
"editor.multiCursorModifier": "ctrlCmd",
|
||||
"editor.snippetSuggestions": "top",
|
||||
"workbench.colorTheme": "Monokai",
|
||||
"eslint.format.enable": true,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
|
||||
},
|
||||
"[jsonc]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"vetur.format.defaultFormatterOptions": {
|
||||
"js-beautify-html": {
|
||||
"wrap_attributes": "force-expand-multiline"
|
||||
},
|
||||
"prettyhtml": {
|
||||
"printWidth": 100,
|
||||
"singleQuote": false,
|
||||
"wrapAttributes": false,
|
||||
"sortAttributes": false
|
||||
}
|
||||
},
|
||||
"cSpell.enableFiletypes": [
|
||||
"vue-html",
|
||||
"vue-postcss"
|
||||
],
|
||||
"[vue]": {
|
||||
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
|
||||
},
|
||||
"cSpell.userWords": [
|
||||
"Matomo",
|
||||
"SUPPRESSMSGBOXES",
|
||||
"matomo"
|
||||
],
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
"prettyhtml": {
|
||||
"printWidth": 100,
|
||||
"singleQuote": false,
|
||||
"wrapAttributes": false,
|
||||
"sortAttributes": false
|
||||
}
|
||||
|
||||
// "[javascript]": {
|
||||
// "editor.defaultFormatter": "dbaeumer.vscode-eslint"
|
||||
// },
|
||||
// "[jsonc]": {
|
||||
// "editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
// }
|
||||
// "eslint.codeAction.showDocumentation": {
|
||||
// "enable": true
|
||||
// },
|
||||
// "eslint.validate": ["vue"],
|
||||
// "[js]": {
|
||||
// "editor.defaultFormatter": "dbaeumer.vscode-eslint"
|
||||
// },
|
||||
// "vetur.format.defaultFormatterOptions": {
|
||||
// "prettier": {
|
||||
// // Prettier option here
|
||||
// "semi": false,
|
||||
// "trailingComma": "es5",
|
||||
// "tabWidth": 2,
|
||||
// "bracketSpacing": true,
|
||||
// "vueIndentScriptAndStyle": true,
|
||||
// "eslintIntegration": true
|
||||
// }
|
||||
// },
|
||||
// "[javascript]": {
|
||||
// "editor.defaultFormatter": "dbaeumer.vscode-eslint"
|
||||
// },
|
||||
// "[vue]": {
|
||||
// //"editor.defaultFormatter": "octref.vetur"
|
||||
// "editor.defaultFormatter": "dbaeumer.vscode-eslint"
|
||||
// },
|
||||
// "cSpell.userWords": [
|
||||
// "vetur",
|
||||
// "vuetify"
|
||||
// ],
|
||||
// "[jsonc]": {
|
||||
// "editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
// }
|
||||
},
|
||||
"cSpell.enableFiletypes": ["vue-html", "vue-postcss"],
|
||||
"[vue]": {
|
||||
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
|
||||
},
|
||||
"cSpell.userWords": ["Matomo", "SUPPRESSMSGBOXES", "matomo"],
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
}
|
||||
|
||||
|
||||
// "[javascript]": {
|
||||
// "editor.defaultFormatter": "dbaeumer.vscode-eslint"
|
||||
// },
|
||||
// "[jsonc]": {
|
||||
// "editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
// }
|
||||
// "eslint.codeAction.showDocumentation": {
|
||||
// "enable": true
|
||||
// },
|
||||
// "eslint.validate": ["vue"],
|
||||
// "[js]": {
|
||||
// "editor.defaultFormatter": "dbaeumer.vscode-eslint"
|
||||
// },
|
||||
// "vetur.format.defaultFormatterOptions": {
|
||||
// "prettier": {
|
||||
// // Prettier option here
|
||||
// "semi": false,
|
||||
// "trailingComma": "es5",
|
||||
// "tabWidth": 2,
|
||||
// "bracketSpacing": true,
|
||||
// "vueIndentScriptAndStyle": true,
|
||||
// "eslintIntegration": true
|
||||
// }
|
||||
// },
|
||||
// "[javascript]": {
|
||||
// "editor.defaultFormatter": "dbaeumer.vscode-eslint"
|
||||
// },
|
||||
// "[vue]": {
|
||||
// //"editor.defaultFormatter": "octref.vetur"
|
||||
// "editor.defaultFormatter": "dbaeumer.vscode-eslint"
|
||||
// },
|
||||
// "cSpell.userWords": [
|
||||
// "vetur",
|
||||
// "vuetify"
|
||||
// ],
|
||||
// "[jsonc]": {
|
||||
// "editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -8,6 +8,11 @@ server {
|
||||
try_files $uri $uri/ /app.html;
|
||||
}
|
||||
|
||||
location /embed {
|
||||
default_type text/html;
|
||||
alias /usr/share/nginx/html/embedApp.html;
|
||||
}
|
||||
|
||||
location ~ ^/streams/.* {
|
||||
default_type text/html;
|
||||
content_by_lua_block {
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<!-- <base href="/appname/"> -->
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title>
|
||||
<%= htmlWebpackPlugin.options.title %>
|
||||
</title>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css">
|
||||
<style type="text/css">
|
||||
body {
|
||||
background-color: #333333;
|
||||
color: #0A66FF;
|
||||
}
|
||||
|
||||
@media screen and (prefers-color-scheme: light) {
|
||||
body {
|
||||
background-color: white;
|
||||
color: #0A66FF;
|
||||
}
|
||||
}
|
||||
|
||||
.tada {
|
||||
-webkit-animation-name: tada;
|
||||
animation-name: tada;
|
||||
-webkit-animation-duration: 1s;
|
||||
animation-duration: 1s;
|
||||
-webkit-animation-fill-mode: both;
|
||||
animation-fill-mode: both;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
@-webkit-keyframes tada {
|
||||
0% {
|
||||
-webkit-transform: scale3d(1, 1, 1);
|
||||
transform: scale3d(1, 1, 1);
|
||||
}
|
||||
|
||||
10%,
|
||||
20% {
|
||||
-webkit-transform: scale3d(.8, .8, .8) rotate3d(0, 0, 1, -3deg);
|
||||
transform: scale3d(.8, .8, .8) rotate3d(0, 0, 1, -3deg);
|
||||
}
|
||||
|
||||
30%,
|
||||
50%,
|
||||
70%,
|
||||
90% {
|
||||
-webkit-transform: scale3d(1.4, 1.4, 1.4) rotate3d(0, 0, 1, 3deg);
|
||||
transform: scale3d(1.4, 1.4, 1.4) rotate3d(0, 0, 1, 3deg);
|
||||
}
|
||||
|
||||
40%,
|
||||
60%,
|
||||
80% {
|
||||
-webkit-transform: scale3d(1.4, 1.4, 1.4) rotate3d(0, 0, 1, -3deg);
|
||||
transform: scale3d(1.4, 1.4, 1.4) rotate3d(0, 0, 1, -3deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: scale3d(1, 1, 1);
|
||||
transform: scale3d(1, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes tada {
|
||||
0% {
|
||||
-webkit-transform: scale3d(1, 1, 1);
|
||||
transform: scale3d(1, 1, 1);
|
||||
}
|
||||
|
||||
10%,
|
||||
20% {
|
||||
-webkit-transform: scale3d(.8, .8, .8) rotate3d(0, 0, 1, -3deg);
|
||||
transform: scale3d(.8, .8, .8) rotate3d(0, 0, 1, -3deg);
|
||||
}
|
||||
|
||||
30%,
|
||||
50%,
|
||||
70%,
|
||||
90% {
|
||||
-webkit-transform: scale3d(1.4, 1.4, 1.4) rotate3d(0, 0, 1, 3deg);
|
||||
transform: scale3d(1.4, 1.4, 1.4) rotate3d(0, 0, 1, 3deg);
|
||||
}
|
||||
|
||||
40%,
|
||||
60%,
|
||||
80% {
|
||||
-webkit-transform: scale3d(1.4, 1.4, 1.4) rotate3d(0, 0, 1, -3deg);
|
||||
transform: scale3d(1.4, 1.4, 1.4) rotate3d(0, 0, 1, -3deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: scale3d(1, 1, 1);
|
||||
transform: scale3d(1, 1, 1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but Speckle doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app">
|
||||
<div style='
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
font-family: sans-serif !important;
|
||||
position: absolute;
|
||||
top:0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
font-weight: 400;
|
||||
font-size: 10px;
|
||||
'>
|
||||
<img src="<%= BASE_URL %>logo.svg" style="max-width: 50px" class="tada">
|
||||
</div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -352,9 +352,15 @@ export default {
|
||||
async getPreviewImage(angle) {
|
||||
angle = angle || 0
|
||||
let previewUrl = this.objectUrl.replace('streams', 'preview') + '/' + angle
|
||||
let token = undefined
|
||||
try {
|
||||
token = localStorage.getItem('AuthToken')
|
||||
}catch (e) {
|
||||
console.warn("Sanboxed mode, only public streams will fetch properly.")
|
||||
}
|
||||
const res = await fetch(previewUrl, {
|
||||
headers: localStorage.getItem('AuthToken')
|
||||
? { Authorization: `Bearer ${localStorage.getItem('AuthToken')}` }
|
||||
headers: token
|
||||
? { Authorization: `Bearer ${token}` }
|
||||
: {}
|
||||
})
|
||||
const blob = await res.blob()
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<template lang="html">
|
||||
<router-view />
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
components: {}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,11 @@
|
||||
import Vue from 'vue'
|
||||
import App from './EmbedApp.vue'
|
||||
import vuetify from './embedVuetify'
|
||||
import router from './embedRouter'
|
||||
Vue.config.productionTip = false
|
||||
|
||||
new Vue({
|
||||
router,
|
||||
vuetify,
|
||||
render: (h) => h(App)
|
||||
}).$mount('#app')
|
||||
@@ -0,0 +1,22 @@
|
||||
import Vue from 'vue'
|
||||
import VueRouter from 'vue-router'
|
||||
|
||||
Vue.use(VueRouter)
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '*',
|
||||
meta: {
|
||||
title: 'Embed View | Speckle'
|
||||
},
|
||||
component: () => import('@/views/EmbedViewer.vue')
|
||||
}
|
||||
]
|
||||
|
||||
const router = new VueRouter({
|
||||
mode: 'history',
|
||||
// base: process.env.BASE_URL,
|
||||
routes
|
||||
})
|
||||
|
||||
export default router
|
||||
@@ -0,0 +1,39 @@
|
||||
import '@mdi/font/css/materialdesignicons.css'
|
||||
import Vue from 'vue'
|
||||
import Vuetify from 'vuetify/lib'
|
||||
|
||||
Vue.use(Vuetify)
|
||||
|
||||
let darkMediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
export default new Vuetify({
|
||||
icons: {
|
||||
iconfont: 'mdi'
|
||||
},
|
||||
theme: {
|
||||
options: { customProperties: true },
|
||||
dark: darkMediaQuery ? 'dark' : null,
|
||||
themes: {
|
||||
light: {
|
||||
primary: '#047EFB', //blue
|
||||
secondary: '#7BBCFF', //light blue
|
||||
accent: '#FCF25E', //yellow
|
||||
error: '#FF5555', //red
|
||||
warning: '#FF9100', //orange
|
||||
info: '#313BCF', //dark blue
|
||||
success: '#4caf50',
|
||||
background: '#eeeeee',
|
||||
text: '#FFFFFF'
|
||||
},
|
||||
dark: {
|
||||
primary: '#047EFB', //blue
|
||||
secondary: '#7BBCFF', //light blue
|
||||
accent: '#FCF25E', //yellow
|
||||
error: '#FF5555', //red
|
||||
warning: '#FF9100', //orange
|
||||
info: '#313BCF', //dark blue
|
||||
success: '#4caf50',
|
||||
background: '#3a3b3c'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,44 @@
|
||||
import gql from "graphql-tag";
|
||||
|
||||
export const serverInfoQuery = `
|
||||
query ServerInfo {
|
||||
serverInfo {
|
||||
name
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const streamCommitQuery = `
|
||||
query Stream($id: String!, $commit: String!) {
|
||||
stream(id: $id) {
|
||||
id
|
||||
name
|
||||
description
|
||||
isPublic
|
||||
commit(id: $commit) {
|
||||
referencedObject
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const branchLastCommitQuery = `
|
||||
query Stream($id: String!, $branch: String!) {
|
||||
stream(id: $id) {
|
||||
id
|
||||
name
|
||||
description
|
||||
isPublic
|
||||
branch(name: $branch) {
|
||||
commits(limit: 1) {
|
||||
totalCount
|
||||
items {
|
||||
referencedObject
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import {
|
||||
branchLastCommitQuery,
|
||||
serverInfoQuery,
|
||||
streamCommitQuery
|
||||
} from "./speckleQueries.js";
|
||||
|
||||
export let SERVER_URL = window.location.origin;
|
||||
|
||||
// Unauthorised fetch, without token to prevent use of localStorage or exposing elsewhere.
|
||||
export async function speckleFetch(query, variables) {
|
||||
try {
|
||||
var res = await fetch(`${SERVER_URL}/graphql`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
query: query,
|
||||
variables: variables
|
||||
})
|
||||
});
|
||||
return await res.json();
|
||||
} catch (err) {
|
||||
console.error("API call failed", err);
|
||||
}
|
||||
}
|
||||
|
||||
export const getServerInfo = () => speckleFetch(serverInfoQuery);
|
||||
export const getLatestBranchCommit = (id, branch) => speckleFetch(branchLastCommitQuery, { id, branch });
|
||||
export const getCommit = (id, commit) => speckleFetch(streamCommitQuery, { id, commit });
|
||||
@@ -239,11 +239,6 @@ const routes = [
|
||||
},
|
||||
component: () => import('@/views/GettingStartedView.vue')
|
||||
},
|
||||
{
|
||||
path: '/embed',
|
||||
name: 'Embeded Viewer',
|
||||
component: () => import('@/views/EmbedViewer.vue')
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
name: 'notfound',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<template lang="html">
|
||||
<template>
|
||||
<v-app class="no-scrollbar">
|
||||
<speckle-loading v-if="!stream || error" :error="error" style="z-index: 101" />
|
||||
<div v-if="!error" class="no-scrollbar embed-view">
|
||||
@@ -87,9 +87,10 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import gql from 'graphql-tag'
|
||||
import Renderer from '../components/Renderer.vue'
|
||||
import SpeckleLoading from '../components/SpeckleLoading.vue'
|
||||
import { getCommit, getLatestBranchCommit, getServerInfo } from "@/embed/speckleUtils";
|
||||
|
||||
export default {
|
||||
name: 'EmbedViewer',
|
||||
components: { Renderer, SpeckleLoading },
|
||||
@@ -98,6 +99,45 @@ export default {
|
||||
return str.length > n ? str.substr(0, n - 3) + '...' : str
|
||||
}
|
||||
},
|
||||
async beforeMount() {
|
||||
try {
|
||||
var serverInfoResponse = await getServerInfo()
|
||||
this.serverInfo = serverInfoResponse.data.serverInfo
|
||||
} catch (e) {
|
||||
this.error = e.message
|
||||
return
|
||||
}
|
||||
if(this.displayType === 'commit'){
|
||||
try {
|
||||
var res = await getCommit(this.input.stream, this.input.commit)
|
||||
var data = res.data
|
||||
var latestCommit = data.stream.commit
|
||||
if (this.input.object === undefined) this.objectId = latestCommit.referencedObject
|
||||
this.specificCommit = data.stream
|
||||
} catch (e) {
|
||||
this.error = e.message
|
||||
return
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
var res = await getLatestBranchCommit(this.input.stream, this.input.branch)
|
||||
var data = res.data
|
||||
console.log(data)
|
||||
var latestCommit = data.stream.branch.commits.items[0] || data.stream.branch.commit
|
||||
if (!latestCommit) {
|
||||
this.error = 'No commit for this branch'
|
||||
this.lastCommit = data.stream
|
||||
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
|
||||
return
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
hideDetails: false,
|
||||
@@ -108,95 +148,10 @@ export default {
|
||||
object: this.$route.query.object,
|
||||
branch: this.$route.query.branch || 'main',
|
||||
commit: this.$route.query.commit
|
||||
}
|
||||
}
|
||||
},
|
||||
apollo: {
|
||||
lastCommit: {
|
||||
query: gql`
|
||||
query Stream($id: String!, $branch: String!) {
|
||||
stream(id: $id) {
|
||||
id
|
||||
name
|
||||
description
|
||||
isPublic
|
||||
branch(name: $branch) {
|
||||
commits(limit: 1) {
|
||||
totalCount
|
||||
items {
|
||||
referencedObject
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables() {
|
||||
return {
|
||||
id: this.input.stream,
|
||||
branch: this.input.branch
|
||||
}
|
||||
},
|
||||
error(err) {
|
||||
this.error = err.message
|
||||
},
|
||||
update(data) {
|
||||
var latestCommit = data.stream.branch.commits.items[0] || data.stream.branch.commit
|
||||
if (!latestCommit) {
|
||||
this.error = 'No commit for this branch'
|
||||
return data.stream
|
||||
}
|
||||
if (this.input.object == undefined) this.objectId = latestCommit.referencedObject
|
||||
else this.objectId = this.input.object
|
||||
return data.stream
|
||||
},
|
||||
skip() {
|
||||
return this.displayType === 'commit'
|
||||
}
|
||||
},
|
||||
specificCommit: {
|
||||
query: gql`
|
||||
query Stream($id: String!, $commit: String!) {
|
||||
stream(id: $id) {
|
||||
id
|
||||
name
|
||||
description
|
||||
isPublic
|
||||
commit(id: $commit) {
|
||||
referencedObject
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables() {
|
||||
return {
|
||||
id: this.input.stream,
|
||||
commit: this.input.commit
|
||||
}
|
||||
},
|
||||
error(err) {
|
||||
this.error = err.message
|
||||
},
|
||||
update(data) {
|
||||
var latestCommit = data.stream.commit
|
||||
if (this.input.object === undefined) this.objectId = latestCommit.referencedObject
|
||||
return data.stream
|
||||
},
|
||||
skip() {
|
||||
return this.displayType !== 'commit'
|
||||
}
|
||||
},
|
||||
serverInfo: {
|
||||
query: gql`
|
||||
query ServerInfo {
|
||||
serverInfo {
|
||||
name
|
||||
}
|
||||
}
|
||||
`,
|
||||
error(err) {
|
||||
this.error = err.message
|
||||
}
|
||||
lastCommit: null,
|
||||
specificCommit: null,
|
||||
serverInfo: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
||||
@@ -6,6 +6,12 @@ module.exports = {
|
||||
title: 'Speckle',
|
||||
template: 'public/app.html',
|
||||
filename: 'app.html'
|
||||
},
|
||||
embedApp: {
|
||||
entry: 'src/embed/embedApp.js',
|
||||
title: 'Speckle Embed Viewer',
|
||||
template: 'public/embedApp.html',
|
||||
filename: 'embedApp.html'
|
||||
}
|
||||
},
|
||||
devServer: {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Simple client that streams object info from a Speckle Server.
|
||||
* Simple client that streams object info from a Speckle Server.
|
||||
* TODO: Object construction progress reporting is weird.
|
||||
*/
|
||||
|
||||
@@ -9,7 +9,7 @@ export default class ObjectLoader {
|
||||
|
||||
/**
|
||||
* Creates a new object loader instance.
|
||||
* @param {*} param0
|
||||
* @param {*} param0
|
||||
*/
|
||||
constructor( { serverUrl, streamId, token, objectId, options = { fullyTraverseArrays: false, excludeProps: [ ] } } ) {
|
||||
this.INTERVAL_MS = 20
|
||||
@@ -18,13 +18,18 @@ export default class ObjectLoader {
|
||||
this.serverUrl = serverUrl || window.location.origin
|
||||
this.streamId = streamId
|
||||
this.objectId = objectId
|
||||
this.token = token || localStorage.getItem( 'AuthToken' )
|
||||
console.log('Object loader constructor called!!!')
|
||||
try {
|
||||
this.token = token || localStorage.getItem( 'AuthToken' )
|
||||
} catch (error) {
|
||||
// Accessing localStorage may throw when executing on sandboxed document, ignore.
|
||||
}
|
||||
|
||||
this.headers = {
|
||||
'Accept': 'text/plain'
|
||||
}
|
||||
|
||||
if( token ) {
|
||||
if( this.token ) {
|
||||
this.headers['Authorization'] = `Bearer ${this.token}`
|
||||
}
|
||||
|
||||
@@ -45,25 +50,25 @@ export default class ObjectLoader {
|
||||
|
||||
/**
|
||||
* Use this method to receive and construct the object. It will return the full, de-referenced and de-chunked original object.
|
||||
* @param {*} onProgress
|
||||
* @returns
|
||||
* @param {*} onProgress
|
||||
* @returns
|
||||
*/
|
||||
async getAndConstructObject( onProgress ) {
|
||||
|
||||
|
||||
;( await this.downloadObjectsInBuffer( onProgress ) ) // Fire and forget; PS: semicolon of doom
|
||||
|
||||
|
||||
let rootObject = await this.getObject( this.objectId )
|
||||
return this.traverseAndConstruct( rootObject, onProgress )
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function used to download all the objects in a local buffer.
|
||||
* @param {*} onProgress
|
||||
* @param {*} onProgress
|
||||
*/
|
||||
async downloadObjectsInBuffer( onProgress ) {
|
||||
let first = true
|
||||
let downloadNum = 0
|
||||
|
||||
|
||||
for await ( let obj of this.getObjectIterator() ) {
|
||||
if( first ) {
|
||||
this.totalChildrenCount = obj.totalChildrenCount
|
||||
@@ -78,9 +83,9 @@ export default class ObjectLoader {
|
||||
|
||||
/**
|
||||
* Internal function used to recursively traverse an object and populate its references and dechunk any arrays.
|
||||
* @param {*} obj
|
||||
* @param {*} onProgress
|
||||
* @returns
|
||||
* @param {*} obj
|
||||
* @param {*} onProgress
|
||||
* @returns
|
||||
*/
|
||||
async traverseAndConstruct( obj, onProgress ) {
|
||||
if( !obj ) return
|
||||
@@ -91,20 +96,20 @@ export default class ObjectLoader {
|
||||
let arr = []
|
||||
for ( let element of obj ) {
|
||||
if ( typeof element !== 'object' && ! this.options.fullyTraverseArrays ) return obj
|
||||
|
||||
|
||||
// Dereference element if needed
|
||||
let deRef = element.referencedId ? await this.getObject( element.referencedId ) : element
|
||||
if( element.referencedId && onProgress ) onProgress( { stage: 'construction', current: ++this.traversedReferencesCount > this.totalChildrenCount ? this.totalChildrenCount : this.traversedReferencesCount, total: this.totalChildrenCount } )
|
||||
|
||||
if( element.referencedId && onProgress ) onProgress( { stage: 'construction', current: ++this.traversedReferencesCount > this.totalChildrenCount ? this.totalChildrenCount : this.traversedReferencesCount, total: this.totalChildrenCount } )
|
||||
|
||||
// Push the traversed object in the array
|
||||
arr.push( await this.traverseAndConstruct( deRef, onProgress ) )
|
||||
}
|
||||
|
||||
|
||||
// De-chunk
|
||||
if( arr[0]?.speckle_type?.toLowerCase().includes('datachunk') ) {
|
||||
if( arr[0]?.speckle_type?.toLowerCase().includes('datachunk') ) {
|
||||
return arr.reduce( ( prev, curr ) => prev.concat( curr.data ), [] )
|
||||
}
|
||||
|
||||
|
||||
return arr
|
||||
}
|
||||
|
||||
@@ -113,11 +118,11 @@ export default class ObjectLoader {
|
||||
for( let ignoredProp of this.options.excludeProps ) {
|
||||
delete obj[ ignoredProp ]
|
||||
}
|
||||
|
||||
|
||||
// 2) Iterate through obj
|
||||
for( let prop in obj ) {
|
||||
for( let prop in obj ) {
|
||||
if( typeof obj[prop] !== 'object' ) continue // leave alone primitive props
|
||||
|
||||
|
||||
if( obj[prop].referencedId ) {
|
||||
obj[prop] = await this.getObject( obj[prop].referencedId )
|
||||
if( onProgress ) onProgress( { stage: 'construction', current: ++this.traversedReferencesCount > this.totalChildrenCount ? this.totalChildrenCount : this.traversedReferencesCount, total: this.totalChildrenCount } )
|
||||
@@ -131,8 +136,8 @@ export default class ObjectLoader {
|
||||
|
||||
/**
|
||||
* Internal function. Returns a promise that is resolved when the object id is loaded into the internal buffer.
|
||||
* @param {*} id
|
||||
* @returns
|
||||
* @param {*} id
|
||||
* @returns
|
||||
*/
|
||||
async getObject( id ){
|
||||
if ( this.buffer[id] ) return this.buffer[id]
|
||||
@@ -198,7 +203,7 @@ export default class ObjectLoader {
|
||||
let result = re.exec( chunk )
|
||||
if ( !result ) {
|
||||
if ( readerDone ) break
|
||||
let remainder = chunk.substr( startIndex )
|
||||
let remainder = chunk.substr( startIndex )
|
||||
;( { value: chunk, done: readerDone } = await reader.read() ) // PS: semicolon of doom
|
||||
chunk = remainder + ( chunk ? decoder.decode( chunk ) : '' )
|
||||
startIndex = re.lastIndex = 0
|
||||
|
||||
@@ -10,7 +10,12 @@ export default class ViewerObjectLoader {
|
||||
|
||||
constructor( parent, objectUrl, authToken ) {
|
||||
this.viewer = parent
|
||||
this.token = authToken || localStorage.getItem( 'AuthToken' )
|
||||
this.token = null
|
||||
try {
|
||||
this.token = authToken || localStorage.getItem( 'AuthToken' )
|
||||
} catch ( error ) {
|
||||
// Accessing localStorage may throw when executing on sandboxed document, ignore.
|
||||
}
|
||||
|
||||
if ( !this.token ) {
|
||||
console.warn( 'Viewer: no auth token present. Requests to non-public stream objects will fail.' )
|
||||
@@ -32,7 +37,7 @@ export default class ViewerObjectLoader {
|
||||
serverUrl: this.serverUrl,
|
||||
token: this.token,
|
||||
streamId: this.streamId,
|
||||
objectId: this.objectId,
|
||||
objectId: this.objectId
|
||||
} )
|
||||
|
||||
this.converter = new Converter( this.loader )
|
||||
|
||||
@@ -1,59 +1,72 @@
|
||||
# Speckle Web
|
||||
<h1 align="center">
|
||||
<img src="https://user-images.githubusercontent.com/2679513/131189167-18ea5fe1-c578-47f6-9785-3748178e4312.png" width="150px"/><br/>
|
||||
Speckle
|
||||
</h1><br/>
|
||||
<p align="center"><b>Speckle</b> is data infrastructure for the AEC industry.</p><br/>
|
||||
|
||||
[](https://twitter.com/SpeckleSystems) [](https://speckle.community) [](https://speckle.systems) [](https://speckle.guide/dev/)
|
||||
|
||||
#### Status
|
||||
|
||||
[](https://github.com/Speckle-Next/SpeckleServer/) [](https://codecov.io/gh/specklesystems/speckle-server)
|
||||
<p align="center"><a href="https://twitter.com/SpeckleSystems"><img src="https://img.shields.io/twitter/follow/SpeckleSystems?style=social" alt="Twitter Follow"></a> <a href="https://speckle.community"><img src="https://img.shields.io/discourse/users?server=https%3A%2F%2Fspeckle.community&style=flat-square&logo=discourse&logoColor=white" alt="Community forum users"></a> <a href="https://speckle.systems"><img src="https://img.shields.io/badge/https://-speckle.systems-royalblue?style=flat-square" alt="website"></a> <a href="https://speckle.guide/dev/"><img src="https://img.shields.io/badge/docs-speckle.guide-orange?style=flat-square&logo=read-the-docs&logoColor=white" alt="docs"></a></p>
|
||||
<p align="center"><a href="https://github.com/Speckle-Next/SpeckleServer/"><img src="https://circleci.com/gh/specklesystems/speckle-server.svg?style=svg&circle-token=76eabd350ea243575cbb258b746ed3f471f7ac29" alt="Speckle-Next"></a> <a href="https://codecov.io/gh/specklesystems/speckle-server"><img src="https://codecov.io/gh/specklesystems/speckle-server/branch/master/graph/badge.svg" alt="codecov"></a></p>
|
||||
|
||||
## Introduction
|
||||
|
||||
This monorepo is the home of the Speckle 2.0 web packages. If you're looking for the desktop connectors, you'll find them [here](https://github.com/specklesystems/speckle-sharp).
|
||||
What is Speckle? Check our 
|
||||
|
||||
Specifically, this monorepo contains:
|
||||
### Features
|
||||
|
||||
### ➡️ [Server](packages/server), the Speckle Server
|
||||
- **Object-based:** say goodbye to files! Speckle is the first object based platform for the AEC industry
|
||||
- **Version control:** Speckle is the Git & Hub for geometry and BIM data
|
||||
- **Collaboration:** share your designs collaborate with others
|
||||
- **3D Viewer:** see your CAD and BIM models online, share and embed them anywhere
|
||||
- **Interoperability:** get your CAD and BIM models into other software without exporting or importing
|
||||
- **Real time:** get real time updates and notifications and changes
|
||||
- **GraphQL API:** get what you need anywhere you want it
|
||||
- **Webhooks:** the base for a automation and next-gen pipelines
|
||||
- **Built for developers:** we are building Speckle with developers in mind and got tools for every stack
|
||||
- **Built for the AEC industry:** Speckle connectors are plugins for the most common software used in the industry such as Revit, Rhino, Grasshopper, AutoCAD, Civil 3D, Excel, Unreal Engine, Unity, QGIS, Blender and more!
|
||||
|
||||
The server is a nodejs app. Core external dependencies are a Redis and Postgresql db. Deploy one in a minute on Digital Ocean using the button below!
|
||||
### Try Speckle now!
|
||||
|
||||
[](https://marketplace.digitalocean.com/apps/speckle-server?refcode=947a2b5d7dc1)
|
||||
Give Speckle a try in no time by:
|
||||
|
||||
### ➡️ [Frontend](packages/frontend), the Speckle Frontend
|
||||
- using our general availability instance at ⇒ [](https://speckle.xyz)
|
||||
- deploying an instance in 1 click ⇒ [](https://marketplace.digitalocean.com/apps/speckle-server?refcode=947a2b5d7dc1)
|
||||
|
||||
The frontend is a static Vue app.
|
||||
### Resources
|
||||
|
||||
### ➡️ [Viewer](packages/viewer), the Speckle Viewer
|
||||
[](https://speckle.community) [](https://speckle.systems) [](https://speckle.guide/dev/)
|
||||
|
||||
[](https://www.npmjs.com/package/@speckle/viewer)
|
||||
|
||||
The viewer is a [threejs](https://threejs.org/) extension that allows you to display data from Speckle.
|
||||
|
||||
### ➡️ [Object Loader](packages/objectloader), a JS helper module
|
||||
## Speckle Server | Repo structure
|
||||
|
||||
[](https://www.npmjs.com/package/@speckle/objectloader)
|
||||
This monorepo is the home of the Speckle v2 web packages:
|
||||
|
||||
A small utility class that helps you stream an object and all its sub-components from the Speckle Server API.
|
||||
- [`packages/server`](https://github.com/specklesystems/speckle-server/blob/main/packages/server): the Server, a nodejs app. Core external dependencies are a Redis and Postgresql db.
|
||||
- [`packages/frontend`](https://github.com/specklesystems/speckle-server/blob/main/packages/frontend): the Frontend, a static Vue app.
|
||||
- [`packages/viewer`](https://github.com/specklesystems/speckle-server/blob/main/packages/viewer): a threejs extension that allows you to display 3D data [](https://www.npmjs.com/package/@speckle/viewer)
|
||||
- [`packages/objectloader`](https://github.com/specklesystems/speckle-server/blob/main/packages/objectloader): a small js utility class that helps you stream an object and all its sub-components from the Speckle Server API. [](https://www.npmjs.com/package/@speckle/objectloader)
|
||||
- [`packages/preview-service`](https://github.com/specklesystems/speckle-server/blob/main/packages/preview-service): generates object previews for Speckle Objects headlessly. This package is meant to be called on by the server.
|
||||
- [`webhook-service`](https://github.com/specklesystems/speckle-server/tree/main/packages/webhook-service): the Webhook service
|
||||
|
||||
### ➡️ [Preview Service](packages/preview-service), for headlessly generating images for 3d objects
|
||||
### Other repos
|
||||
|
||||
Generates object previews for Speckle Objects. This package is meant to be called on by the server.
|
||||
Make sure to also check and ⭐️ these other Speckle repositories:
|
||||
|
||||
## Documentation
|
||||
- [`speckle-sharp`](https://github.com/specklesystems/speckle-sharp) .NET tooling, connectors and interoperability
|
||||
- [`specklepy`](https://github.com/specklesystems/specklepy) Python SDK 🐍
|
||||
- and more [connectos & tooling](https://github.com/specklesystems/)!
|
||||
|
||||
Comprehensive developer and user documentation can be found in our:
|
||||
|
||||
#### 📚 [Speckle Docs website](https://speckle.guide/dev/)
|
||||
|
||||
## Usage
|
||||
To start using Speckle, it's not necessary to deploy it yourself. The easiest way is to register a free account on speckle.xyz, our general availability offering. Check [https://speckle.systems/getstarted/](https://speckle.systems/getstarted/) for more information.
|
||||
|
||||
## Developing and Debugging
|
||||
|
||||
If you want to deploy the Server, we have a detailed [guide on how to do so](https://speckle.guide/dev/server-setup.html). To get started developing locally, you can read the [run in development mode](https://speckle.guide/dev/server-setup.html#run-in-development-mode) chapter of our deployment guide.
|
||||
Have you checked our [dev docs](https://speckle.guide/dev/)?
|
||||
|
||||
## Contributing
|
||||
We have a detailed section on [deploying a Speckle server](https://speckle.guide/dev/server-setup.html). To get started developing locally, you can see the [run in development mode](https://speckle.guide/dev/server-setup.html#run-in-development-mode) chapter.
|
||||
|
||||
Please make sure you read the [contribution guidelines](CONTRIBUTING.md) for an overview of the best practices we try to follow.
|
||||
### Contributing
|
||||
|
||||
Please make sure you read the [contribution guidelines](https://github.com/specklesystems/speckle-server/blob/main/CONTRIBUTING.md) for an overview of the best practices we try to follow.
|
||||
|
||||
When pushing commits to this repo, please follow the following guidelines:
|
||||
|
||||
@@ -61,14 +74,10 @@ When pushing commits to this repo, please follow the following guidelines:
|
||||
- When ready to commit, `git cz` & follow the prompts.
|
||||
- Please use either `server` or `frontend` as the scope of your commit.
|
||||
|
||||
## Community
|
||||
### Security
|
||||
|
||||
The Speckle Community hangs out on [the forum](https://speckle.community), do join and introduce yourself & feel free to ask us questions!
|
||||
For any security vulnerabilities or concerns, please contact us directly at security[at]speckle.systems.
|
||||
|
||||
## Security
|
||||
|
||||
For any security vulnerabilities or concerns, please contact us directly at security[at]speckle.systems.
|
||||
|
||||
## License
|
||||
### License
|
||||
|
||||
Unless otherwise described, the code in this repository is licensed under the Apache-2.0 License. Please note that some modules, extensions or code herein might be otherwise licensed. This is indicated either in the root of the containing folder under a different license file, or in the respective file's header. If you have any questions, don't hesitate to get in touch with us via [email](mailto:hello@speckle.systems).
|
||||
|
||||
Reference in New Issue
Block a user