Skip to content

Commit

Permalink
feature for sharing url (#95)
Browse files Browse the repository at this point in the history
implement share url function to export current script as gzipped data
url

inital lame version adds a menu item to export

![image](https://github.com/hrgdavor/jscadui/assets/2480762/3c24e210-2562-4623-8f24-6c34c685ff1c)

copies the url to clipboard, and writes url to console

todo
- [x] ui feedback 
- [x] will not work for multifile projects a warnign to user should be
displayed ...
- [x] we also need some kind of state to know if current script is
multifile
  • Loading branch information
hrgdavor authored Jun 24, 2024
1 parent cce7c98 commit 7f8fe04
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 29 deletions.
84 changes: 56 additions & 28 deletions apps/jscad-web/main.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {
addToCache,
analyzeProject,
clearFs,
extractEntries,
analyzeProject,
fileDropped,
getFile,
getFileContent,
Expand All @@ -12,14 +12,17 @@ import { Gizmo } from '@jscadui/html-gizmo'
import { OrbitControl } from '@jscadui/orbit'
import { genParams } from '@jscadui/params'
import { messageProxy } from '@jscadui/postmessage'
import { gzipSync } from 'fflate'

import { runMain } from '../../packages/worker/worker.js'
import defaultCode from './examples/jscad.example.js'
import * as editor from './src/editor.js'
import * as engine from './src/engine.js'
import * as exporter from './src/exporter.js'
import * as menu from './src/menu.js'
import * as remote from './src/remote.js'
import { formatStacktrace } from './src/stacktrace.js'
import { str2ab } from './src/str2ab.js'
import { ViewState } from './src/viewState.js'
import * as welcome from './src/welcome.js'

Expand Down Expand Up @@ -81,9 +84,9 @@ async function initFs() {
})
sw.defProjectName = 'jscad'
sw.onfileschange = files => {
if(files.includes('/package.json')){
if (files.includes('/package.json')) {
reloadProject()
}else{
} else {
workerApi.jscadClearFileCache({ files })
editor.filesChanged(files)
if (sw.fileToRun) jscadScript({ url: sw.fileToRun, base: sw.base })
Expand Down Expand Up @@ -116,7 +119,7 @@ document.body.ondrop = async ev => {
}
}

async function reloadProject(){
async function reloadProject() {
saveMap = {}
sw.filesToCheck = []
const { alias, script } = await analyzeProject(sw)
Expand Down Expand Up @@ -165,6 +168,26 @@ function save(blob, filename) {
}

const exportModel = async (format, extension) => {
if (format === 'scriptUrl') {
if(editor.getEditorFiles().length > 1) {
alert('Can not export multifile projects as url')
return
}
let src = editor.getSource()
let gzipped = gzipSync(str2ab(src))
let str = String.fromCharCode.apply(null, gzipped)
let url = document.location.origin + '#data:application/gzip;base64,' + btoa(str)
console.log('url\n', url)
try {
await navigator.clipboard.writeText(url)
alert('URL with gzipped script was succesfully copied to clipboard')
} catch (err) {
console.error('Failed to copy: ', err)
alert('failed to copy to clipboard\n'+err.message)
}
return
}

const { data } = (await workerApi.jscadExportData({ format })) || {}
if (data) {
save(new Blob([data], { type: 'text/plain' }), `${projectName}.${extension}`)
Expand All @@ -188,23 +211,23 @@ const handlers = {
viewState.setModel((model = entities))
console.log('Main execution:', mainTime?.toFixed(2), ', jscad mesh -> gl:', convertTime?.toFixed(2))
setError(undefined)
onProgress(undefined, mainTime?.toFixed(2)+' ms')
onProgress(undefined, mainTime?.toFixed(2) + ' ms')
},
onProgress,
}

/** @type {JscadWorker} */
const workerApi = globalThis.workerApi = messageProxy(worker, handlers, { onJobCount: trackJobs })
const workerApi = (globalThis.workerApi = messageProxy(worker, handlers, { onJobCount: trackJobs }))

const progress = byId('progress').querySelector('progress')
const progressText = byId('progressText')
const progressText = byId('progressText')
let jobs = 0
let firstJobTimer

function trackJobs(jobs) {
if (jobs === 1) {
// do not show progress for fast renders
clearTimeout(firstJobTimer)
clearTimeout(firstJobTimer)
firstJobTimer = setTimeout(() => {
onProgress()
progress.style.display = 'block'
Expand All @@ -219,13 +242,13 @@ function trackJobs(jobs) {
const jscadScript = async ({ script, url = './jscad.model.js', base = currentBase, root }) => {
currentBase = base
loadDefault = false // don't load default model if something else was loaded
try{
try {
const result = await workerApi.jscadScript({ script, url, base, root, smooth: viewState.smoothRender })
genParams({ target: byId('paramsDiv'), params: result.def || {}, callback: paramChangeCallback })
lastRunParams = result.params
handlers.entities(result)
}catch(err){
setError(err)
} catch (err) {
setError(err)
}
}

Expand All @@ -236,9 +259,6 @@ const bundles = {
}

await workerApi.jscadInit({ bundles })
if (loadDefault) {
jscadScript({ script: defaultCode, smooth: viewState.smoothRender })
}

let working
let lastParams
Expand Down Expand Up @@ -329,21 +349,29 @@ editor.init(
)
menu.init()
welcome.init()
remote.init(
(script, url) => {
// run remote script
editor.setSource(script, url)
jscadScript({ script, base: url })
welcome.dismiss()
},
err => {
// show remote script error
loadDefault = false
setError(err)
welcome.dismiss()
},
)
let hasRemoteScript
try{
hasRemoteScript = await remote.init(
(script, url) => {
// run remote script
editor.setSource(script, url)
jscadScript({ script, base: url })
welcome.dismiss()
},
err => {
// show remote script error
loadDefault = false
setError(err)
welcome.dismiss()
},
)
}catch(e){
console.error(e)
}
exporter.init(exportModel)
if (loadDefault && !hasRemoteScript) {
jscadScript({ script: defaultCode, smooth: viewState.smoothRender })
}

try {
await initFs()
Expand Down
7 changes: 7 additions & 0 deletions apps/jscad-web/src/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ export const init = (defaultCode, fn, _saveFn, _getFileFn) => {
})
}

export const getSource = () => view.state.doc.toString()

export const setSource = (source, path = '/index.js') => {
view.dispatch({ changes: { from: 0, to: view.state.doc.length, insert: source } })
currentFile = path
Expand All @@ -111,8 +113,13 @@ async function readSource(file, currentFile){
setSource(await readAsText(file), currentFile)
}

let editorFilesArr = []

export const getEditorFiles = ()=>editorFilesArr

export const setFiles = (files) => {
const editorFiles = document.getElementById('editor-files')
editorFilesArr = files
if (files.length < 2) {
editorNav.classList.remove('visible')
} else {
Expand Down
1 change: 1 addition & 0 deletions apps/jscad-web/src/exporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const exportFormats = [
{ name: 'x3d', extension: 'x3d', label: 'X3D' },
{ name: 'svg', extension: 'svg', label: 'SVG' },
{ name: '3mf', extension: '3mf', label: '3MF' },
{ name: 'scriptUrl', label: 'Copy to clipboard script url' },
]

export const init = (exportFn) => {
Expand Down
3 changes: 2 additions & 1 deletion apps/jscad-web/src/remote.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ const gzipPrefix = 'data:application/gzip;base64,'

export const init = (compileFn, setError) => {
const load = loadFromUrl(compileFn, setError)
load() // on load
window.addEventListener('hashchange', load) // on change
return load() // on load
}

/**
Expand All @@ -19,6 +19,7 @@ export const loadFromUrl = (compileFn, setError) => async () => {
try {
const script = await fetchUrl(url)
compileFn(script, url)
return true
} catch (err) {
setError(err)
}
Expand Down
8 changes: 8 additions & 0 deletions apps/jscad-web/src/str2ab.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export function str2ab(str) {
var arrBuff = new ArrayBuffer(str.length);
var bytes = new Uint8Array(arrBuff);
for (var iii = 0; iii < str.length; iii++) {
bytes[iii] = str.charCodeAt(iii);
}
return bytes;
}

0 comments on commit 7f8fe04

Please sign in to comment.