Skip to content
This repository has been archived by the owner on Jun 27, 2024. It is now read-only.

Commit

Permalink
feat(Updates): (#12)
Browse files Browse the repository at this point in the history
* chore(pkg): updated pkg.json with more build & general settings
* refactor(ui): moved main html output out to seperate module
* refactor(actions): moved actions to seperate folder
* refactor(state): extracted export file path determination to simple function
* initial output format now determined correctly
* feat(error display): added basic display of errors & updated internal
plumbing accordingly
* fix(design loading):
* seperated setting design path from design loading
* moved file system interaction to fsWrapper side effect
* modified fileWatcher to return the file contents
* added potential fix for fileWatcher issues thanks to the above
* modified actions & state accordingly
* refactor(loadScript): moved out fs.readFileSync, now takes script content & path as input
* chore(pkg): added dev launch command for window
* chore(ui): modified export message
* chore(experiments): added experiments for other useful side effects
* closes #14 
* closes #13 
* closes #11 
* closes #10
  • Loading branch information
kaosat-dev authored Jan 15, 2018
1 parent 7581451 commit d25c349
Show file tree
Hide file tree
Showing 13 changed files with 214 additions and 123 deletions.
7 changes: 7 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,16 @@
user-select: none;
}
#busy{
position: absolute;
color: black;
top: 50px;
}
#status{
position: absolute;
color: red;
top: 50px;
left: 100px;
z-index: 100;
}
#container{
z-index: 100;
Expand Down
20 changes: 17 additions & 3 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ function createWindow () {
}))

// Open the DevTools.
// mainWindow.webContents.openDevTools()
mainWindow.webContents.openDevTools()

// Emitted when the window is closed.
mainWindow.on('closed', function () {
Expand All @@ -47,9 +47,7 @@ app.on('ready', createWindow)
app.on('window-all-closed', function () {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
// if (process.platform !== 'darwin') {
app.quit()
// }
})

app.on('activate', function () {
Expand All @@ -62,3 +60,19 @@ app.on('activate', function () {

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
electron.ipcMain.on('get-file-data', function (event) {
let data = null
if (process.platform === 'win32' && process.argv.length >= 2) {
var openFilePath = process.argv[1]
data = openFilePath
}
console.log('here', data, event, process.argv)
event.sender.send('asynchronous-reply', {data, event, args: process.argv})
event.returnValue = data
})
console.log('foo')

electron.ipcMain.on('asynchronous-message', (event, arg) => {
console.log(arg) // prints "ping"
event.sender.send('asynchronous-reply', 'pong')
})
18 changes: 14 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@
"name": "jscad-desktop",
"version": "0.0.1",
"description": "jscad desktop application",
"author": "jscad core team/ Mark Moissette",
"license": "MIT",
"repository": "",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "node node_modules/.bin/electron .",
"dev-win": "node_modules/.bin/electron.cmd .",
"pack": "electron-builder --dir",
"dist": "electron-builder"
},
"author": "Mark 'kaosat-dev' Moissette",
"license": "MIT",
"dependencies": {
"@jscad/amf-serializer": "^0.1.1",
"@jscad/csg": "^0.3.7",
Expand Down Expand Up @@ -40,7 +42,15 @@
"build": {
"appId": "jscad.id",
"mac": {
"category": "jscad.3dmodeling.type"
}
"category": "public.app-category.graphics-design"
},
"linux":{
"target": "AppImage",
"category": "Graphics"
},
"win":{
"target": "nsis"
},
"fileAssociations": [{"ext": "jscad", "name":"jscad","role":"editor"}]
}
}
28 changes: 12 additions & 16 deletions src/actions.js → src/actions/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ const path = require('path')
const most = require('most')
const {remote} = require('electron')
const {dialog} = remote
const {getScriptFile} = require('./core/scripLoading')
const {head} = require('./utils')
const {getScriptFile} = require('../core/scripLoading')
const {head} = require('../utils')

function compositeKeyFromKeyEvent (event) {
const ctrl = event.ctrlKey ? 'ctrl+' : ''
Expand Down Expand Up @@ -108,7 +108,7 @@ const makeActions = (sources) => {
const filePath = dialog.showSaveDialog({properties: ['saveFile'], title: 'export design to', defaultPath: defaultExportFilePath})//, function (filePath) {
// console.log('saving', filePath)
if (filePath !== undefined) {
const saveDataToFs = require('./io/saveDataToFs')
const saveDataToFs = require('../io/saveDataToFs')
saveDataToFs(data, exportFormat, filePath)
}
})
Expand All @@ -126,35 +126,31 @@ const makeActions = (sources) => {
.map(data => [data]),
sources.drops
.filter(drop => drop.type === 'fileOrFolder' && drop.data.length > 0)
.map(drop => drop.data.map(fileOrFolder => fileOrFolder.path)),
sources.watcher
.map(path => [path])
.map(drop => drop.data.map(fileOrFolder => fileOrFolder.path))
])
.filter(data => data !== undefined)
.debounce(300)
.debounce(50)
.multicast()

const designLoadRequested$ = designPath$
.map(data => ({type: 'designLoadRequested', data}))

const setDesignPath$ = designPath$
.map(data => ({type: 'setDesignPath', data}))
.delay(1)

const setDesignScriptContent$ = most.mergeArray([
sources.fs.filter()
const setDesignContent$ = most.mergeArray([
sources.fs.filter(data => data.operation === 'read').map(raw => raw.data),
sources.watcher// .map(content => )
])
.map(data => ({type: 'setDesignScriptContent', data}))
.map(data => ({type: 'setDesignContent', data}))

// design parameter change actions
const updateDesignFromParams$ = most.mergeArray([
sources.dom.select('#updateDesignFromParams').events('click')
.map(function () {
const controls = Array.from(document.getElementById('paramsMain').getElementsByTagName('input'))
return {paramValues: require('./core/getParamValues')(controls), origin: 'manualUpdate'}
return {paramValues: require('../core/getParamValues')(controls), origin: 'manualUpdate'}
}),
sources.paramChanges.map(function (controls) {
return {paramValues: require('./core/getParamValues')(controls), origin: 'instantUpdate'}
return {paramValues: require('../core/getParamValues')(controls), origin: 'instantUpdate'}
})
])
.map(data => ({type: 'updateDesignFromParams', data}))
Expand All @@ -172,7 +168,7 @@ const makeActions = (sources) => {
toggleInstantUpdate$,
// design
setDesignPath$,
designLoadRequested$,
setDesignContent$,
updateDesignFromParams$,
// exports
changeExportFormat$,
Expand Down
Empty file added src/actions/types.js
Empty file.
81 changes: 13 additions & 68 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const paramsCallbacktoStream = require('./observable-utils/callbackToObservable'
const { attach, stream } = proxy()
const state$ = stream
//
const actions$ = require('./actions')({
const actions$ = require('./actions/actions')({
store: storeSource$,
drops: dragAndDropSource$,
watcher: watcherSource(),
Expand Down Expand Up @@ -58,15 +58,16 @@ electronStoreSink(state$
watcherSink(
state$
.filter(state => state.design.mainPath !== '') // FIXME: disable watch if autoreload is set to false
.map(state => ({filePath: state.design.mainPath, enabled: state.autoReload}))
.skipRepeats()
.map(state => ({filePath: state.design.mainPath, enabled: state.autoReload}))
)
/* fsSink(
fsSink(
state$
.filter(state => state.design.mainPath !== '')
.map(state => ({operation: 'read', id: 'loadScript', path: state.design.mainPath}))
.map(state => state.design.mainPath)
.skipRepeats()
) */
.map(path => ({operation: 'read', id: 'loadScript', path}))
)

// viewer data
state$
Expand All @@ -90,66 +91,6 @@ state$
})

// ui updates, exports
const html = require('bel')

function dom (state) {
const formatsList = state.availableExportFormats
.map(function ({name, displayName}) {
return html`<option value=${name} selected='${state.exportFormat === name}'>${displayName}</option>`
})

const {createParamControls} = require('./ui/createParameterControls2')
const {paramValues, paramDefinitions} = state.design
const {controls} = createParamControls(paramValues, paramDefinitions, true, paramsCallbacktoStream.callback)

const output = html`
<div id='container' style='color:${state.mainTextColor}'>
<!--Ui Controls-->
<div id='controls'>
<input type="button" value="load jscad (.js or .jscad) file" id="fileLoader"/>
<label for="autoReload">Auto reload</label>
<input type="checkbox" id="autoReload" checked=${state.autoReload}/>
<label for="grid">Grid</label>
<input type="checkbox" id="grid" checked=${state.viewer.grid.show}/>
<label for="toggleAxes">Axes</label>
<input type="checkbox" id="toggleAxes" checked=${state.viewer.axes.show}/>
<label for="autoRotate">Autorotate</label>
<input type="checkbox" id="autoRotate"/>
<select id='themeSwitcher'>
<option value='dark' selected=${state.themeName === 'dark'}>Dark Theme</option>
<option value='light' selected=${state.themeName === 'light'}>Light Theme</option>
</select>
<span id='exports'>
<select id='exportFormats'>
${formatsList}
</select>
<input type='button' value="export to ${state.exportFormat}" id="exportBtn"/>
</span>
<span id='busy'>${state.busy ? 'processing, please wait' : ''}</span>
</div>
<!--Params-->
<span id='params'>
<span id='paramsMain'>
<table>
${controls}
</table>
</span>
<span id='paramsControls' style='visibility:${state.design.paramDefinitions.length === 0 ? 'hidden' : 'visible'}'>
<button id='updateDesignFromParams'>Update</button>
<label for='instantUpdate'>Instant Update</label>
<input type='checkbox' checked='${state.instantUpdate}' id='instantUpdate'/>
</span>
</span>
<canvas id='renderTarget'> </canvas>
</div>
`
return output
}

const outToDom$ = state$
.skipRepeatsWith(function (state, previousState) {
Expand All @@ -159,13 +100,17 @@ const outToDom$ = state$
const sameExportFormats = state.exportFormat === previousState.exportFormat &&
state.availableExportFormats === previousState.availableExportFormats

const sameStatus = state.busy === previousState.busy
const sameStyling = state.themeName === previousState.themeName

const sameAutoreload = state.autoReload === previousState.autoReload
return sameParamDefinitions && sameExportFormats && sameStatus && sameStyling && sameAutoreload && sameInstantUpdate

const sameError = JSON.stringify(state.error) === JSON.stringify(previousState.error)
const sameStatus = state.busy === previousState.busy

return sameParamDefinitions && sameExportFormats && sameStatus && sameStyling &&
sameAutoreload && sameInstantUpdate && sameError
})
.map(state => dom(state))
.map(state => require('./ui/main')(state, paramsCallbacktoStream))

domSink(outToDom$)

Expand Down
3 changes: 1 addition & 2 deletions src/core/scripLoading.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,8 @@ const getScriptFile = paths => {
* @param {} filePath
* @param {} csgBasePath='../../../core/' : relative path or './node_modules/@jscad'
*/
function loadScript (filePath, csgBasePath = './node_modules/@jscad') {
function loadScript (scriptAsText, filePath, csgBasePath = './node_modules/@jscad') {
console.log('loading script using jscad/csg base path at:', csgBasePath)
const scriptAsText = fs.readFileSync(filePath, 'utf8')
let jscadScript
// && !scriptAsText.includes('require(')
if ((!scriptAsText.includes('module.exports')) && scriptAsText.includes('main')) {
Expand Down
8 changes: 6 additions & 2 deletions src/sideEffects/fileWatcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ function watcherSink (toWatch$) {
watched = filename
// clear require() cache to force reload the file , otherwise it keeps reloading the cached version
requireUncached(filePath)
scriptDataFromCB.callback(filePath)
const contents = fs.readFileSync(filePath, 'utf8')
scriptDataFromCB.callback(contents)
}
})
}
Expand All @@ -44,7 +45,10 @@ function watcherSink (toWatch$) {
}

function watcherSource () {
return scriptDataFromCB.stream.multicast().debounce(1000)// debounce is very important as fs.Watch is unstable
return scriptDataFromCB.stream
.debounce(400)// debounce is very important as fs.Watch is unstable
.skipRepeats()
.multicast()
}

module.exports = {watcherSink, watcherSource}
6 changes: 3 additions & 3 deletions src/sideEffects/fsWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ const callBackToStream = require('../observable-utils/callbackToObservable')
const readFileToCB = callBackToStream()

function fsSink (out$) {
out$.forEach(function ({operation, path}) {
out$.forEach(function ({path, operation}) {
console.log('read/writing to', path, operation)
if (operation === 'read') {
fs.readFile(path, 'utf8', function (error, data) {
if (error) {
readFileToCB.callback({error})
readFileToCB.callback({path, operation, error})
} else {
readFileToCB.callback({path, data, operation})
readFileToCB.callback({path, operation, data})
}
})
}
Expand Down
18 changes: 18 additions & 0 deletions src/sideEffects/i8n.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const i18next = require('i18next')

const {create} = require('@most/create')

module.exports = getTranslations = (translationPaths) => {
return create((add, end, error) => {
i18next.init({
lng: 'en',
resources: translationPaths
}, (err, t) => {
if (err) {
error(err)
} else {
add(t)
}
})
})
}
12 changes: 12 additions & 0 deletions src/sideEffects/ipc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const {ipcRenderer} = require('electron')
var data = ipcRenderer.sendSync('get-file-data')
if (data === null) {
console.log('There is no file')
} else {
// Do something with the file.
console.log(data)
}
ipcRenderer.send('asynchronous-message', 'ping')
ipcRenderer.on('asynchronous-reply', (event, arg) => {
console.log(arg) // prints "pong"
})
Loading

0 comments on commit d25c349

Please sign in to comment.