Split into more components
This commit is contained in:
parent
704c6ed074
commit
0e97a288e1
11 changed files with 276 additions and 160 deletions
|
|
@ -9,10 +9,11 @@ import { MenuItem } from 'primereact/menuitem';
|
||||||
import { TreeSelect } from 'primereact/treeselect';
|
import { TreeSelect } from 'primereact/treeselect';
|
||||||
import TreeNode from 'primereact/treenode';
|
import TreeNode from 'primereact/treenode';
|
||||||
import { Menu } from 'primereact/menu';
|
import { Menu } from 'primereact/menu';
|
||||||
import { getParentDir } from '../fs/filesystem';
|
import { getParentDir, librariesFolder } from '../fs/filesystem';
|
||||||
import { buildUrlForStateParams } from '../state/fragment-state';
|
import { buildUrlForStateParams } from '../state/fragment-state';
|
||||||
import { blankProjectState } from '../state/initial-state';
|
import { blankProjectState } from '../state/initial-state';
|
||||||
import { ModelContext, FSContext } from './contexts';
|
import { ModelContext, FSContext } from './contexts';
|
||||||
|
import FilePicker, { isFileWritable } from './FilePicker';
|
||||||
|
|
||||||
// import "primereact/resources/themes/lara-light-indigo/theme.css";
|
// import "primereact/resources/themes/lara-light-indigo/theme.css";
|
||||||
// import "primereact/resources/primereact.min.css";
|
// import "primereact/resources/primereact.min.css";
|
||||||
|
|
@ -21,42 +22,45 @@ import { ModelContext, FSContext } from './contexts';
|
||||||
let monacoInstance: Monaco
|
let monacoInstance: Monaco
|
||||||
loader.init().then(mi => monacoInstance = mi);
|
loader.init().then(mi => monacoInstance = mi);
|
||||||
|
|
||||||
const isFileWritable = (path: string) => getParentDir(path) === '/home'
|
// const isFileWritable = (path: string) => getParentDir(path) === '/home'
|
||||||
|
|
||||||
function listFilesAsNodes(fs: FS, path: string): TreeNode[] {
|
// function listFilesAsNodes(fs: FS, path: string): TreeNode[] {
|
||||||
const files: [string, string][] = []
|
// const files: [string, string][] = []
|
||||||
const dirs: [string, string][] = []
|
// const dirs: [string, string][] = []
|
||||||
for (const name of fs.readdirSync(path)) {
|
// for (const name of fs.readdirSync(path)) {
|
||||||
const childPath = `${path}/${name}`;
|
// if (name.startsWith('.')) {
|
||||||
const stat = fs.lstatSync(childPath);
|
// continue;
|
||||||
const isDirectory = stat.isDirectory();
|
// }
|
||||||
if (!isDirectory && !name.endsWith('.scad')) {
|
// const childPath = `${path}/${name}`;
|
||||||
continue;
|
// const stat = fs.lstatSync(childPath);
|
||||||
}
|
// const isDirectory = stat.isDirectory();
|
||||||
(isDirectory ? dirs : files).push([name, childPath]);
|
// if (!isDirectory && !name.endsWith('.scad')) {
|
||||||
}
|
// continue;
|
||||||
[files, dirs].forEach(arr => arr.sort(([a], [b]) => a.localeCompare(b)));
|
// }
|
||||||
|
// (isDirectory ? dirs : files).push([name, childPath]);
|
||||||
|
// }
|
||||||
|
// [files, dirs].forEach(arr => arr.sort(([a], [b]) => a.localeCompare(b)));
|
||||||
|
|
||||||
const nodes: TreeNode[] = []
|
// const nodes: TreeNode[] = []
|
||||||
for (const [arr, isDirectory] of [[files, false], [dirs, true]] as [[string, string][], boolean][]) {
|
// for (const [arr, isDirectory] of [[files, false], [dirs, true]] as [[string, string][], boolean][]) {
|
||||||
for (const [name, path] of arr) {
|
// for (const [name, path] of arr) {
|
||||||
const children = isDirectory ? listFilesAsNodes(fs, path) : undefined;
|
// const children = isDirectory ? listFilesAsNodes(fs, path) : undefined;
|
||||||
if (isDirectory && children!.length == 0) {
|
// if (isDirectory && children!.length == 0) {
|
||||||
continue;
|
// continue;
|
||||||
}
|
// }
|
||||||
nodes.push({
|
// nodes.push({
|
||||||
// icon: path == '/home' ? 'pi-home' : ...
|
// // icon: path == '/home' ? 'pi-home' : ...
|
||||||
icon: isDirectory ? 'pi pi-folder' : isFileWritable(path) ? 'pi pi-file' : 'pi pi-lock',
|
// icon: isDirectory ? 'pi pi-folder' : isFileWritable(path) ? 'pi pi-file' : 'pi pi-lock',
|
||||||
label: name,
|
// label: name,
|
||||||
data: path,
|
// data: path,
|
||||||
key: path,
|
// key: path,
|
||||||
children,
|
// children,
|
||||||
selectable: !isDirectory // && (name == 'LICENSE' || name.endsWith('.scad') || name.endsWith('.scad')
|
// selectable: !isDirectory // && (name == 'LICENSE' || name.endsWith('.scad') || name.endsWith('.scad')
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return nodes;
|
// return nodes;
|
||||||
}
|
// }
|
||||||
|
|
||||||
export default function EditorPanel({className, style}: {className?: string, style?: CSSProperties}) {
|
export default function EditorPanel({className, style}: {className?: string, style?: CSSProperties}) {
|
||||||
|
|
||||||
|
|
@ -67,7 +71,7 @@ export default function EditorPanel({className, style}: {className?: string, sty
|
||||||
|
|
||||||
const state = model.state;
|
const state = model.state;
|
||||||
|
|
||||||
const fs = useContext(FSContext);
|
// const fs = useContext(FSContext);
|
||||||
const [editor, setEditor] = useState(null as monaco.editor.IStandaloneCodeEditor | null)
|
const [editor, setEditor] = useState(null as monaco.editor.IStandaloneCodeEditor | null)
|
||||||
// const [modelFile, setModelFile] = useState(state.params.sourcePath);
|
// const [modelFile, setModelFile] = useState(state.params.sourcePath);
|
||||||
|
|
||||||
|
|
@ -100,8 +104,6 @@ export default function EditorPanel({className, style}: {className?: string, sty
|
||||||
setEditor(editor)
|
setEditor(editor)
|
||||||
}
|
}
|
||||||
|
|
||||||
const fsItems = fs && listFilesAsNodes(fs, '/home') || [];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`editor-panel ${className ?? ''}`} style={{
|
<div className={`editor-panel ${className ?? ''}`} style={{
|
||||||
// maxWidth: '5 0vw',
|
// maxWidth: '5 0vw',
|
||||||
|
|
@ -111,7 +113,6 @@ export default function EditorPanel({className, style}: {className?: string, sty
|
||||||
// width: '100%', height: '100%',
|
// width: '100%', height: '100%',
|
||||||
...(style ?? {})
|
...(style ?? {})
|
||||||
}}>
|
}}>
|
||||||
{/* <BreadCrumb model={breadCrumbsItems} home={breadCrumbsHome}/> */}
|
|
||||||
<div className='flex flex-row gap-2' style={{
|
<div className='flex flex-row gap-2' style={{
|
||||||
margin: '5px',
|
margin: '5px',
|
||||||
}}>
|
}}>
|
||||||
|
|
@ -214,73 +215,39 @@ export default function EditorPanel({className, style}: {className?: string, sty
|
||||||
] as MenuItem[]} popup ref={menu} />
|
] as MenuItem[]} popup ref={menu} />
|
||||||
<Button title="Editor menu" rounded text icon="pi pi-ellipsis-h" onClick={(e) => menu.current && menu.current.toggle(e)} />
|
<Button title="Editor menu" rounded text icon="pi pi-ellipsis-h" onClick={(e) => menu.current && menu.current.toggle(e)} />
|
||||||
|
|
||||||
<TreeSelect
|
<FilePicker
|
||||||
title='OpenSCAD Playground Files'
|
|
||||||
value={state.params.sourcePath}
|
|
||||||
onChange={(e) => model.openFile(String(e.value))}
|
|
||||||
// dropdownIcon="pi pi-folder-open"
|
|
||||||
filter
|
|
||||||
style={{
|
style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
}}
|
}}/>
|
||||||
options={fsItems} />
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* <SplitPane type="vertical"> */}
|
|
||||||
{/* <div className='flex flex-column'> */}
|
<div style={{
|
||||||
{/* <Splitter style={{
|
position: 'relative',
|
||||||
// height: '300px'
|
flex: 1
|
||||||
}} layout="vertical"> */}
|
}}>
|
||||||
{/* <SplitterPanel className="flex flex-column align-items-center justify-content-center"
|
<Editor
|
||||||
style={{
|
className="openscad-editor absolute-fill"
|
||||||
// width: '100%', height: '100%'
|
defaultLanguage="openscad"
|
||||||
}}> */}
|
path={state.params.sourcePath}
|
||||||
<div style={{
|
value={state.params.source}
|
||||||
position: 'relative',
|
onChange={s => model.source = s ?? ''}
|
||||||
flex: 1
|
onMount={onMount} // TODO: This looks a bit silly, does it trigger a re-render??
|
||||||
}}>
|
options={{
|
||||||
<Editor
|
...openscadEditorOptions,
|
||||||
className="openscad-editor absolute-fill"
|
readOnly: !isFileWritable(state.params.sourcePath)
|
||||||
defaultLanguage="openscad"
|
}}
|
||||||
path={state.params.sourcePath}
|
/>
|
||||||
value={state.params.source}
|
</div>
|
||||||
onChange={s => model.source = s ?? ''}
|
|
||||||
onMount={onMount} // TODO: This looks a bit silly, does it trigger a re-render??
|
|
||||||
options={{
|
|
||||||
...openscadEditorOptions,
|
|
||||||
readOnly: !isFileWritable(state.params.sourcePath)
|
|
||||||
}}
|
|
||||||
// height="100%"
|
|
||||||
// width="100%"
|
|
||||||
// height="80vh"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* </SplitterPanel>
|
<div style={{
|
||||||
<SplitterPanel className="flex flex-column align-items-center justify-content-center" style={{position: 'relative'}}> */}
|
display: state.view.logs ? undefined : 'none',
|
||||||
{/* <ScrollPanel style={{
|
overflowY: 'scroll',
|
||||||
// width: '100%', height: '200px'
|
height: 'calc(min(200px, 30vh))',
|
||||||
}} className="custombar2"> */}
|
}}>
|
||||||
<div style={{
|
<pre><code id="logs" style={{
|
||||||
display: state.view.logs ? undefined : 'none',
|
}}>{state.lastCheckerRun?.logText ?? 'No log yet!'}</code></pre>
|
||||||
overflowY: 'scroll',
|
</div>
|
||||||
height: 'calc(min(200px, 30vh))',
|
|
||||||
// position: 'relative',
|
|
||||||
// maxWidth: '100%',
|
|
||||||
// maxWidth: '50vw',
|
|
||||||
}}>
|
|
||||||
<pre><code id="logs" style={{
|
|
||||||
// maxWidth: '200px'
|
|
||||||
}}>{state.lastCheckerRun?.logText ?? 'No log yet!'}</code></pre>
|
|
||||||
</div>
|
|
||||||
{/* </ScrollPanel> */}
|
|
||||||
{/* <div className="logs-container">
|
|
||||||
</div> */}
|
|
||||||
{/* </SplitterPanel>
|
|
||||||
</Splitter> */}
|
|
||||||
{/* </SplitPane> */}
|
|
||||||
{/* </div> */}
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
85
src/components/FilePicker.tsx
Normal file
85
src/components/FilePicker.tsx
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
// Portions of this file are Copyright 2021 Google LLC, and licensed under GPL2+. See COPYING.
|
||||||
|
|
||||||
|
import { CSSProperties, useContext } from 'react';
|
||||||
|
import { TreeSelect } from 'primereact/treeselect';
|
||||||
|
import TreeNode from 'primereact/treenode';
|
||||||
|
import { getParentDir, librariesFolder } from '../fs/filesystem';
|
||||||
|
import { ModelContext, FSContext } from './contexts';
|
||||||
|
|
||||||
|
export const isFileWritable = (path: string) => getParentDir(path) === '/home'
|
||||||
|
|
||||||
|
function listFilesAsNodes(fs: FS, path: string): TreeNode[] {
|
||||||
|
const files: [string, string][] = []
|
||||||
|
const dirs: [string, string][] = []
|
||||||
|
for (const name of fs.readdirSync(path)) {
|
||||||
|
if (name.startsWith('.')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const childPath = `${path}/${name}`;
|
||||||
|
const stat = fs.lstatSync(childPath);
|
||||||
|
const isDirectory = stat.isDirectory();
|
||||||
|
if (!isDirectory && !name.endsWith('.scad')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
(isDirectory ? dirs : files).push([name, childPath]);
|
||||||
|
}
|
||||||
|
[files, dirs].forEach(arr => arr.sort(([a], [b]) => a.localeCompare(b)));
|
||||||
|
|
||||||
|
const nodes: TreeNode[] = []
|
||||||
|
for (const [arr, isDirectory] of [[files, false], [dirs, true]] as [[string, string][], boolean][]) {
|
||||||
|
for (const [name, path] of arr) {
|
||||||
|
const children = isDirectory ? listFilesAsNodes(fs, path) : undefined;
|
||||||
|
if (isDirectory && children!.length == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
nodes.push({
|
||||||
|
// icon: path == '/home' ? 'pi-home' : ...
|
||||||
|
icon: isDirectory ? 'pi pi-folder' : isFileWritable(path) ? 'pi pi-file' : 'pi pi-lock',
|
||||||
|
label: name,
|
||||||
|
data: path,
|
||||||
|
key: path,
|
||||||
|
children,
|
||||||
|
selectable: !isDirectory // && (name == 'LICENSE' || name.endsWith('.scad') || name.endsWith('.scad')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function FilePicker({className, style}: {className?: string, style?: CSSProperties}) {
|
||||||
|
const model = useContext(ModelContext);
|
||||||
|
if (!model) throw new Error('No model');
|
||||||
|
const state = model.state;
|
||||||
|
|
||||||
|
const fs = useContext(FSContext);
|
||||||
|
|
||||||
|
const fsItems = fs && [
|
||||||
|
{
|
||||||
|
icon: 'pi pi-home',
|
||||||
|
label: 'User files',
|
||||||
|
key: '/home',
|
||||||
|
children: listFilesAsNodes(fs, '/home'),
|
||||||
|
selectable: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'pi pi-database',
|
||||||
|
label: 'Builtin libraries',
|
||||||
|
key: librariesFolder,
|
||||||
|
children: listFilesAsNodes(fs, librariesFolder),
|
||||||
|
selectable: false
|
||||||
|
},
|
||||||
|
] || [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TreeSelect
|
||||||
|
className={className}
|
||||||
|
title='OpenSCAD Playground Files'
|
||||||
|
value={state.params.sourcePath}
|
||||||
|
onChange={(e) => model.openFile(String(e.value))}
|
||||||
|
// dropdownIcon="pi pi-folder-open"
|
||||||
|
filter
|
||||||
|
style={style}
|
||||||
|
// style={{style}}
|
||||||
|
options={fsItems} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -9,9 +9,10 @@ import { ProgressBar } from 'primereact/progressbar';
|
||||||
import { MenuItem } from 'primereact/menuitem';
|
import { MenuItem } from 'primereact/menuitem';
|
||||||
import { Badge } from 'primereact/badge';
|
import { Badge } from 'primereact/badge';
|
||||||
import { Menu } from 'primereact/menu';
|
import { Menu } from 'primereact/menu';
|
||||||
import { ToggleButton } from 'primereact/togglebutton';
|
|
||||||
import { ConfirmDialog, confirmDialog } from 'primereact/confirmdialog';
|
import { ConfirmDialog, confirmDialog } from 'primereact/confirmdialog';
|
||||||
import { Toast } from 'primereact/toast';
|
import { Toast } from 'primereact/toast';
|
||||||
|
import SettingsMenu from './SettingsMenu';
|
||||||
|
import HelpMenu from './HelpMenu';
|
||||||
|
|
||||||
function downloadOutput(state: State) {
|
function downloadOutput(state: State) {
|
||||||
if (!state.output) return;
|
if (!state.output) return;
|
||||||
|
|
@ -46,11 +47,11 @@ function downloadOutput(state: State) {
|
||||||
export default function Footer({style}: {style?: CSSProperties}) {
|
export default function Footer({style}: {style?: CSSProperties}) {
|
||||||
const model = useContext(ModelContext);
|
const model = useContext(ModelContext);
|
||||||
if (!model) throw new Error('No model');
|
if (!model) throw new Error('No model');
|
||||||
|
const state = model.state;
|
||||||
|
|
||||||
const menu = useRef<Menu>(null);
|
const helpMenu = useRef<Menu>(null);
|
||||||
const toast = useRef<Toast>(null);
|
const toast = useRef<Toast>(null);
|
||||||
|
|
||||||
const state = model.state;
|
|
||||||
|
|
||||||
const severityByMarkerSeverity = new Map<monaco.MarkerSeverity, 'danger' | 'warning' | 'info'>([
|
const severityByMarkerSeverity = new Map<monaco.MarkerSeverity, 'danger' | 'warning' | 'info'>([
|
||||||
[monaco.MarkerSeverity.Error, 'danger'],
|
[monaco.MarkerSeverity.Error, 'danger'],
|
||||||
|
|
@ -147,49 +148,9 @@ export default function Footer({style}: {style?: CSSProperties}) {
|
||||||
// </a>
|
// </a>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<ToggleButton
|
<SettingsMenu />
|
||||||
style={{
|
|
||||||
// flex: 1,
|
|
||||||
}}
|
|
||||||
onIcon='pi pi-table'
|
|
||||||
offIcon='pi pi-table'
|
|
||||||
onLabel=''
|
|
||||||
offLabel=''
|
|
||||||
// onLabel='Multi'
|
|
||||||
// offLabel='Single'
|
|
||||||
title='Change between single and multiple layout'
|
|
||||||
checked={state.view.layout.mode === 'multi'}
|
|
||||||
onChange={e => model.changeLayout(e.value ? 'multi' : 'single')} />
|
|
||||||
|
|
||||||
<Menu model={[
|
<HelpMenu />
|
||||||
{
|
|
||||||
label: "openscad-playground",
|
|
||||||
icon: 'pi pi-github',
|
|
||||||
command: () => window.open('https://github.com/openscad/openscad-playground/tree/rewrite1', '_blank'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'LICENSES',
|
|
||||||
icon: 'pi pi-info-circle',
|
|
||||||
command: () => window.open('https://github.com/openscad/openscad-playground/blob/rewrite1/LICENSE.md', '_blank'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'OpenSCAD Docs',
|
|
||||||
icon: 'pi pi-book',
|
|
||||||
command: () => window.open('http://openscad.org/documentation.html', '_blank'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'OpenSCAD Cheatsheet',
|
|
||||||
icon: 'pi pi-palette',
|
|
||||||
command: () => window.open('http://openscad.org/cheatsheet/', '_blank'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'BOSL2 Cheatsheet',
|
|
||||||
icon: 'pi pi-palette',
|
|
||||||
command: () => window.open('https://github.com/revarbat/BOSL2/wiki/CheatSheet', '_blank'),
|
|
||||||
},
|
|
||||||
] as MenuItem[]} popup ref={menu} />
|
|
||||||
<Button title="Help & Licenses" rounded icon="pi pi-question-circle" onClick={(e) => menu.current && menu.current.toggle(e)} />
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
48
src/components/HelpMenu.tsx
Normal file
48
src/components/HelpMenu.tsx
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
// Portions of this file are Copyright 2021 Google LLC, and licensed under GPL2+. See COPYING.
|
||||||
|
|
||||||
|
import { useContext, useRef } from 'react';
|
||||||
|
import { Button } from 'primereact/button';
|
||||||
|
import { MenuItem } from 'primereact/menuitem';
|
||||||
|
import { Menu } from 'primereact/menu';
|
||||||
|
import { ModelContext } from './contexts';
|
||||||
|
|
||||||
|
export default function HelpMenu() {
|
||||||
|
const menuRef = useRef<Menu>(null);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Menu model={[
|
||||||
|
{
|
||||||
|
label: "openscad-playground",
|
||||||
|
icon: 'pi pi-github',
|
||||||
|
url: 'https://github.com/openscad/openscad-playground/tree/rewrite1',
|
||||||
|
target: '_blank'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'LICENSES',
|
||||||
|
icon: 'pi pi-info-circle',
|
||||||
|
url: 'https://github.com/openscad/openscad-playground/blob/rewrite1/LICENSE.md',
|
||||||
|
target: '_blank'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'OpenSCAD Docs',
|
||||||
|
icon: 'pi pi-book',
|
||||||
|
url: 'http://openscad.org/documentation.html',
|
||||||
|
target: '_blank'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'OpenSCAD Cheatsheet',
|
||||||
|
icon: 'pi pi-palette',
|
||||||
|
url: 'http://openscad.org/cheatsheet/',
|
||||||
|
target: '_blank'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'BOSL2 Cheatsheet',
|
||||||
|
icon: 'pi pi-palette',
|
||||||
|
url: 'https://github.com/revarbat/BOSL2/wiki/CheatSheet',
|
||||||
|
target: '_blank'
|
||||||
|
},
|
||||||
|
] as MenuItem[]} popup ref={menuRef} />
|
||||||
|
<Button title="Help & Licenses" rounded icon="pi pi-question-circle" onClick={(e) => menuRef.current && menuRef.current.toggle(e)} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
45
src/components/SettingsMenu.tsx
Normal file
45
src/components/SettingsMenu.tsx
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
// Portions of this file are Copyright 2021 Google LLC, and licensed under GPL2+. See COPYING.
|
||||||
|
|
||||||
|
import { useContext, useRef } from 'react';
|
||||||
|
import { Button } from 'primereact/button';
|
||||||
|
import { MenuItem } from 'primereact/menuitem';
|
||||||
|
import { Menu } from 'primereact/menu';
|
||||||
|
import { ModelContext } from './contexts';
|
||||||
|
|
||||||
|
export default function SettingsMenu() {
|
||||||
|
const model = useContext(ModelContext);
|
||||||
|
if (!model) throw new Error('No model');
|
||||||
|
const state = model.state;
|
||||||
|
|
||||||
|
const settingsMenu = useRef<Menu>(null);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Menu model={[
|
||||||
|
{
|
||||||
|
label: state.view.layout.mode === 'multi'
|
||||||
|
? 'Switch to single panel mode'
|
||||||
|
: "Switch to side-by-side mode",
|
||||||
|
icon: 'pi pi-table',
|
||||||
|
// disabled: true,
|
||||||
|
command: () => model.changeLayout(state.view.layout.mode === 'multi' ? 'single' : 'multi'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
separator: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: state.view.showAxes ? 'Hide axes' : 'Show axes',
|
||||||
|
icon: 'pi pi-box',
|
||||||
|
// disabled: true,
|
||||||
|
command: () => model.mutate(s => s.view.showAxes = !s.view.showAxes)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: state.view.showShadows ? 'Hide shadows' : 'Add shadows',
|
||||||
|
icon: 'pi pi-box',
|
||||||
|
// disabled: true,
|
||||||
|
command: () => model.mutate(s => s.view.showShadows = !s.view.showShadows)
|
||||||
|
},
|
||||||
|
] as MenuItem[]} popup ref={settingsMenu} />
|
||||||
|
<Button title="Settings menu" rounded text icon="pi pi-cog" onClick={(e) => settingsMenu.current && settingsMenu.current.toggle(e)} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -26,9 +26,9 @@ export default function ViewerPanel({className, style}: {className?: string, sty
|
||||||
// width: '100%'
|
// width: '100%'
|
||||||
}}
|
}}
|
||||||
// ref={stlModelRef}
|
// ref={stlModelRef}
|
||||||
showAxes={true}
|
showAxes={state.view.showAxes}
|
||||||
orbitControls
|
orbitControls
|
||||||
shadows
|
shadows={state.view.showShadows}
|
||||||
modelProps={{
|
modelProps={{
|
||||||
color: '#f9d72c',
|
color: '#f9d72c',
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ export type OpenSCADInvocationResults = {
|
||||||
elapsedMillis: number,
|
elapsedMillis: number,
|
||||||
}
|
}
|
||||||
|
|
||||||
export function spawnOpenSCAD({inputs, args, outputPaths}: OpenSCADInvocation): AbortablePromise<OpenSCADInvocationResults> {
|
export function spawnOpenSCAD(invocation: OpenSCADInvocation): AbortablePromise<OpenSCADInvocationResults> {
|
||||||
var worker: Worker | null;
|
var worker: Worker | null;
|
||||||
var rejection: (err: any) => void;
|
var rejection: (err: any) => void;
|
||||||
|
|
||||||
|
|
@ -55,7 +55,7 @@ export function spawnOpenSCAD({inputs, args, outputPaths}: OpenSCADInvocation):
|
||||||
resolve(e.data);
|
resolve(e.data);
|
||||||
terminate();
|
terminate();
|
||||||
}
|
}
|
||||||
worker.postMessage({inputs, args, outputPaths})
|
worker.postMessage(invocation)
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
// rejection({error: 'Terminated'});
|
// rejection({error: 'Terminated'});
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import OpenSCAD from "../wasm/openscad.js";
|
import OpenSCAD from "../wasm/openscad.js";
|
||||||
|
|
||||||
import { createEditorFS, getBrowserFSLibrariesMounts, symlinkLibraries } from "../fs/filesystem";
|
import { createEditorFS, getBrowserFSLibrariesMounts, librariesFolder, symlinkLibraries } from "../fs/filesystem";
|
||||||
import { OpenSCADInvocation, OpenSCADInvocationResults } from "./openscad-runner";
|
import { OpenSCADInvocation, OpenSCADInvocationResults } from "./openscad-runner";
|
||||||
import { zipArchives } from "../fs/zip-archives";
|
import { zipArchives } from "../fs/zip-archives";
|
||||||
declare var BrowserFS: BrowserFSInterface
|
declare var BrowserFS: BrowserFSInterface
|
||||||
|
|
@ -33,12 +33,14 @@ addEventListener('message', async (e) => {
|
||||||
console.debug('stderr: ' + text);
|
console.debug('stderr: ' + text);
|
||||||
mergedOutputs.push({ stderr: text })
|
mergedOutputs.push({ stderr: text })
|
||||||
},
|
},
|
||||||
ENV: {
|
// ENV: {
|
||||||
OPENSCADPATH: '/home'
|
// OPENSCADPATH: '/home'
|
||||||
}
|
// }
|
||||||
});
|
});
|
||||||
|
|
||||||
const fs = await createEditorFS('/home');
|
// const librariesFolder = '/home/.local/share/OpenSCAD/libraries'
|
||||||
|
const fs = await createEditorFS();
|
||||||
|
await symlinkLibraries(allArchiveNames, fs, librariesFolder, '/home');//'/home', '/home');
|
||||||
|
|
||||||
// https://github.com/emscripten-core/emscripten/issues/10061
|
// https://github.com/emscripten-core/emscripten/issues/10061
|
||||||
const BFS = new BrowserFS.EmscriptenFS(
|
const BFS = new BrowserFS.EmscriptenFS(
|
||||||
|
|
@ -49,7 +51,7 @@ addEventListener('message', async (e) => {
|
||||||
}, instance.ERRNO_CODES ?? {});
|
}, instance.ERRNO_CODES ?? {});
|
||||||
instance.FS.mount(BFS, {root: '/home'}, '/home');
|
instance.FS.mount(BFS, {root: '/home'}, '/home');
|
||||||
|
|
||||||
// await symlinkLibraries(allArchiveNames, instance.FS, '/home/libraries', '/home');
|
//await symlinkLibraries(allArchiveNames, instance.FS, '/home/libraries', '/home');
|
||||||
|
|
||||||
instance.FS.chdir('/home');
|
instance.FS.chdir('/home');
|
||||||
|
|
||||||
|
|
@ -65,7 +67,7 @@ addEventListener('message', async (e) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Calling main ', args)
|
console.log('Invoking OpenSCAD from ', workingDir, args)
|
||||||
const start = performance.now();
|
const start = performance.now();
|
||||||
const exitCode = instance.callMain(args);
|
const exitCode = instance.callMain(args);
|
||||||
const end = performance.now();
|
const end = performance.now();
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,9 @@ export interface State {
|
||||||
} | ({
|
} | ({
|
||||||
mode: 'multi',
|
mode: 'multi',
|
||||||
} & { [K in MultiLayoutComponentId]: boolean })
|
} & { [K in MultiLayoutComponentId]: boolean })
|
||||||
|
|
||||||
|
showAxes?: boolean,
|
||||||
|
showShadows?: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
lastCheckerRun?: {
|
lastCheckerRun?: {
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,9 @@ export function readStateFromFragment(): State | null {
|
||||||
editor: validateBoolean(view?.layout['editor']),
|
editor: validateBoolean(view?.layout['editor']),
|
||||||
viewer: validateBoolean(view?.layout['viewer']),
|
viewer: validateBoolean(view?.layout['viewer']),
|
||||||
customizer: validateBoolean(view?.layout['customizer']),
|
customizer: validateBoolean(view?.layout['customizer']),
|
||||||
}
|
},
|
||||||
|
showAxes: validateBoolean(view?.layout?.showAxis),
|
||||||
|
showShadows: validateBoolean(view?.layout?.showShadow),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,9 @@ export function createInitialState(fs: any, state: State | null) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initialState.view.showAxes ??= true
|
||||||
|
initialState.view.showShadows ??= true
|
||||||
|
|
||||||
fs.writeFile(initialState.params.sourcePath, initialState.params.source);
|
fs.writeFile(initialState.params.sourcePath, initialState.params.source);
|
||||||
if (initialState.params.sourcePath !== defaultSourcePath) {
|
if (initialState.params.sourcePath !== defaultSourcePath) {
|
||||||
fs.writeFile(defaultSourcePath, defaultScad);
|
fs.writeFile(defaultSourcePath, defaultScad);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue