From cfda844339453473768f477849491449801f8cbb Mon Sep 17 00:00:00 2001 From: ochafik Date: Sun, 26 Mar 2023 15:57:09 +0100 Subject: [PATCH] Shuffle files around --- src/App.css | 45 -- src/App.tsx | 679 --------------------- src/components/App.tsx | 159 +++++ src/components/EditorPanel.tsx | 289 +++++++++ src/components/Footer.tsx | 195 ++++++ src/components/ViewerPanel.tsx | 42 ++ src/{ => fs}/BrowserFS.d.ts | 0 src/{ => fs}/filesystem.ts | 3 + src/{ => fs}/zip-archives.ts | 0 src/index.tsx | 6 +- src/language/openscad-completions.ts | 4 +- src/language/openscad-register-language.ts | 2 +- src/{ => runner}/actions.ts | 4 +- src/{ => runner}/openscad-runner.ts | 2 +- src/{ => runner}/openscad-worker.ts | 8 +- src/{ => runner}/output-parser.ts | 0 src/{ => state}/deep-mutate.ts | 0 src/state/model.ts | 4 +- 18 files changed, 704 insertions(+), 738 deletions(-) delete mode 100644 src/App.css delete mode 100644 src/App.tsx create mode 100644 src/components/App.tsx create mode 100644 src/components/EditorPanel.tsx create mode 100644 src/components/Footer.tsx create mode 100644 src/components/ViewerPanel.tsx rename src/{ => fs}/BrowserFS.d.ts (100%) rename src/{ => fs}/filesystem.ts (97%) rename src/{ => fs}/zip-archives.ts (100%) rename src/{ => runner}/actions.ts (96%) rename src/{ => runner}/openscad-runner.ts (97%) rename src/{ => runner}/openscad-worker.ts (92%) rename src/{ => runner}/output-parser.ts (100%) rename src/{ => state}/deep-mutate.ts (100%) diff --git a/src/App.css b/src/App.css deleted file mode 100644 index 528765a..0000000 --- a/src/App.css +++ /dev/null @@ -1,45 +0,0 @@ -/* .App { - text-align: center; -} - -.App-logo { - height: 40vmin; - pointer-events: none; -} - -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } -} - -.App-header { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; -} - -.App-link { - color: #61dafb; -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - - -.logs-container { - overflow-y: scroll; - height: calc(min(200px, 30vh)); - position: relative; -} */ \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx deleted file mode 100644 index 2f0ffd8..0000000 --- a/src/App.tsx +++ /dev/null @@ -1,679 +0,0 @@ -// Portions of this file are Copyright 2021 Google LLC, and licensed under GPL2+. See COPYING. - -import React, { CSSProperties, forwardRef, useContext, useEffect, useRef, useState } from 'react'; -import {MultiLayoutComponentId, SingleLayoutComponentId, State} from './state/app-state' -import Editor, { loader, Monaco } from '@monaco-editor/react'; -// import './App.css'; -import openscadEditorOptions from './language/openscad-editor-options'; -import { Model, ModelContext } from './state/model'; -import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; -import {StlViewer} from "react-stl-viewer"; -import { Button } from 'primereact/button'; -import { ProgressBar } from 'primereact/progressbar'; -import { MenuItem } from 'primereact/menuitem'; -import { TreeSelect } from 'primereact/treeselect'; -import TreeNode from 'primereact/treenode'; -import { TabMenu } from 'primereact/tabmenu'; -import { Badge } from 'primereact/badge'; -import { Menu } from 'primereact/menu'; -import { ToggleButton } from 'primereact/togglebutton'; -import { ConfirmDialog, confirmDialog } from 'primereact/confirmdialog'; -import { Toast } from 'primereact/toast'; -import { getParentDir } from './filesystem'; -import { BreadCrumb } from 'primereact/breadcrumb'; -import { ScrollPanel } from 'primereact/scrollpanel'; -import { Splitter, SplitterPanel } from 'primereact/splitter'; -import { Toolbar } from 'primereact/toolbar'; -import { buildUrlForStateParams } from './state/fragment-state'; -import { blankProjectState } from './state/initial-state'; - -// import "primereact/resources/themes/lara-light-indigo/theme.css"; -// import "primereact/resources/primereact.min.css"; -// import "primeicons/primeicons.css"; - -let monacoInstance: Monaco -loader.init().then(mi => monacoInstance = mi); - -const isFileWritable = (path: string) => getParentDir(path) === '/home' - -const FSContext = React.createContext(undefined); - -function listFilesAsNodes(fs: FS, path: string): TreeNode[] { - const files: [string, string][] = [] - const dirs: [string, string][] = [] - for (const name of fs.readdirSync(path)) { - 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; -} - -function EditorPanel({className, style}: {className?: string, style?: CSSProperties}) { - - const model = useContext(ModelContext); - if (!model) throw new Error('No model'); - - const menu = useRef(null); - - const state = model.state; - - const fs = useContext(FSContext); - const [editor, setEditor] = useState(null as monaco.editor.IStandaloneCodeEditor | null) - // const [modelFile, setModelFile] = useState(state.params.sourcePath); - - if (editor) { - const checkerRun = state.lastCheckerRun; - const editorModel = editor.getModel(); - if (editorModel) { - if (checkerRun) { - monacoInstance.editor.setModelMarkers(editorModel, 'openscad', checkerRun.markers); - } - // if (modelFile !== state.params.sourcePath) { - // editor.setModel(monaco.editor.createModel('', 'openscad')); - // setModelFile(state.params.sourcePath) - // } - } - } - - const onMount = (editor: monaco.editor.IStandaloneCodeEditor) => { - editor.addAction({ - id: "openscad-render", - label: "Render OpenSCAD", - keybindings: [ - monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter, - monaco.KeyCode.F6, - ], - run: () => model.render({isPreview: false, now: true}) - }); - editor.addAction({ - id: "openscad-preview", - label: "Preview OpenSCAD", - keybindings: [monaco.KeyCode.F5], - run: () => model.render({isPreview: true, now: true}) - }); - setEditor(editor) - } - - // const breadCrumbsHome: MenuItem = { - // icon: 'pi pi-home' - // } - - // const breadCrumbsItems: MenuItem[] = [ - // { - // label: 'input.scad', - // command: () => alert('ok') - // } - // ] - - const fsItems = fs && listFilesAsNodes(fs, '/home') || []; - - // console.log('getParentDir(', state.params.sourcePath, ')', getParentDir(state.params.sourcePath)) - return ( -
- {/* */} -
- - window.open(buildUrlForStateParams(blankProjectState), '_blank'), - }, - { - // TODO: share text, title and rendering image - // https://developer.mozilla.org/en-US/docs/Web/API/Navigator/share - label: 'Share project', - icon: 'pi pi-share-alt', - disabled: true, - command: () => window.open('http://openscad.org/cheatsheet/', '_blank'), - }, - { - separator: true - }, - { - // TODO: popup to ask for file name - label: "New file", - icon: 'pi pi-plus', - disabled: true, - command: () => window.open('https://github.com/openscad/openscad-playground/tree/rewrite1', '_blank'), - }, - { - label: "Copy to new file", - icon: 'pi pi-clone', - disabled: true, - command: () => window.open('https://github.com/openscad/openscad-playground/tree/rewrite1', '_blank'), - }, - { - label: "Upload file(s)", - icon: 'pi pi-upload', - disabled: true, - command: () => window.open('https://github.com/openscad/openscad-playground/tree/rewrite1', '_blank'), - }, - { - label: 'Download sources', - icon: 'pi pi-download', - disabled: true, - command: () => window.open('https://github.com/revarbat/BOSL2/wiki/CheatSheet', '_blank'), - }, - { - separator: true - }, - // https://vscode-docs.readthedocs.io/en/stable/customization/keybindings/ - // { - // label: 'Undo', - // icon: 'pi pi-undo', - // // disabled: true, - // command: () => editor?.trigger(state.params.sourcePath, 'editor.action.undoAction', null), - // }, - // { - // label: 'Redo', - // icon: 'pi pi-reply', - // // disabled: true, - // command: () => editor?.trigger(state.params.sourcePath, 'editor.action.redoAction', null), - // }, - { - separator: true - }, - // { - // label: 'Copy', - // icon: 'pi pi-copy', - // // disabled: true, - // command: () => editor?.trigger(state.params.sourcePath, 'editor.action.clipboardCopyAction', null), - // }, - // { - // label: 'Cut', - // icon: 'pi pi-eraser', - // // disabled: true, - // command: () => editor?.trigger(state.params.sourcePath, 'editor.action.clipboardCutAction', null), - // }, - // { - // label: 'Paste', - // icon: 'pi pi-images', - // // disabled: true, - // command: () => editor?.trigger(state.params.sourcePath, 'editor.action.clipboardPasteAction', null), - // }, - { - label: 'Select All', - icon: 'pi pi-info-circle', - // disabled: true, - command: () => editor?.trigger(state.params.sourcePath, 'editor.action.selectAll', null), - }, - { - separator: true - }, - { - label: 'Find', - icon: 'pi pi-search', - // disabled: true, - command: () => editor?.trigger(state.params.sourcePath, 'actions.find', null), - }, - ] as MenuItem[]} popup ref={menu} /> -
- - {/* */} - {/*
*/} - {/* */} - {/* */} -
- 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" - /> -
- - {/*
- */} - {/* */} -
-
{state.lastCheckerRun?.logText ?? 'No log yet!'}
-
- {/*
*/} - {/*
-
*/} - {/*
-
*/} - {/* */} - {/*
*/} - -
- ) -} - -export function ViewerPanel({className, style}: {className?: string, style?: CSSProperties}) { - const model = useContext(ModelContext); - if (!model) throw new Error('No model'); - - const state = model.state; - - return ( -
- {state.output?.stlFileURL && - - } - -
- ) -} - -function downloadOutput(state: State) { - if (!state.output) return; - - const fileName = state.output!.isPreview ? 'preview.stl' : 'render.stl'; - const doDownload = () => { - const a = document.createElement('a') - a.href = state.output!.stlFileURL - a.download = fileName; - document.body.appendChild(a) - a.click() - document.body.removeChild(a) - }; - - if (state.output.isPreview && state.params.source.indexOf('$preview') >= 0) { - confirmDialog({ - message: "This model references the $preview variable but hasn't been rendered (F6), or its rendering is stale. You're about to download the preview result itself, which may not have the intended refinement of the render. Sure you want to proceed?", - header: 'Preview vs. Render', - icon: 'pi pi-exclamation-triangle', - accept: () => doDownload, - acceptLabel: `Download ${fileName}`, - rejectLabel: 'Cancel' - // reject: () => {} - }); - } else { - doDownload(); - } - -} - - -function Footer({style}: {style?: CSSProperties}) { - const model = useContext(ModelContext); - if (!model) throw new Error('No model'); - - const menu = useRef(null); - const toast = useRef(null); - - const state = model.state; - - const severityByMarkerSeverity = new Map([ - [monaco.MarkerSeverity.Error, 'danger'], - [monaco.MarkerSeverity.Warning, 'warning'], - [monaco.MarkerSeverity.Info, 'info'], - ]); - const markers = state.lastCheckerRun?.markers ?? []; - const getBadge = (s: monaco.MarkerSeverity) => { - const count = markers.filter(m => m.severity == s).length; - const sev = s == monaco.MarkerSeverity.Error ? 'danger' - : s == monaco.MarkerSeverity.Warning ? 'warning' - : s == monaco.MarkerSeverity.Info ? 'info' - : 'success'; - return <>{count > 0 && }; - }; - - - const maxMarkerSeverity = markers.length == 0 ? undefined : markers.map(m => m.severity).reduce((a, b) => Math.max(a, b)); - - return <> - - -
- - - {/* {state.output && - - - {state.output.formattedElapsedMillis} - } */} - - {/* {state.previewing && 'previewing... '} - {state.rendering && 'rendering... '} - {state.checkingSyntax && 'checking syntax... '} */} - - {/* */} - - - - - - - - {state.output && ( -
- -} - -export function App({initialState, fs}: {initialState: State, fs: FS}) { - const [state, setState] = useState(initialState); - - const model = new Model(fs, state, setState); - useEffect(() => model.init()); - - // const breadCrumbsItems: MenuItem[] = [ - // { - // label: 'input.scad', - // command: () => alert('ok') - - const singleTargets: {id: SingleLayoutComponentId, icon: string, label: string}[] = [ - { id: 'editor', icon: 'pi pi-pencil', label: 'Edit' }, - { id: 'viewer', icon: 'pi pi-box', label: 'View' }, - { id: 'customizer', icon: 'pi pi-sliders-h', label: 'Customize' }, - // { id: 'customizer', icon: 'pi pi-cog', label: 'Customize' }, - ]; - const multiTargets = singleTargets; - // const multiTargets: {id: MultiLayoutComponentId, icon: string, label: string}[] = [ - // { id: 'editor', icon: 'pi pi-pencil', label: 'Editor' }, - // { id: 'viewer', icon: 'pi pi-box', label: 'Viewer' }, - // { id: 'customizer', icon: 'pi pi-cog', label: 'Customizer' }, - // ]; - const zIndexOfPanelsDependingOnFocus = { - editor: { - editor: 3, - viewer: 1, - customizer: 0, - }, - viewer: { - editor: 2, - viewer: 3, - customizer: 2, - }, - customizer: { - editor: 0, - viewer: 0, - customizer: 3, - } - } - - const layout = state.view.layout - const mode = state.view.layout.mode; - function getPanelStyle(id: MultiLayoutComponentId): CSSProperties { - if (layout.mode === 'multi') { - const itemCount = (layout.editor ? 1 : 0) + (layout.viewer ? 1 : 0) + (layout.customizer ? 1 : 0) - return { - flex: 1, - maxWidth: Math.floor(100/itemCount) + '%', - display: (state.view.layout as any)[id] ? 'flex' : 'none' - } - } else { - return { - flex: 1, - zIndex: Number((zIndexOfPanelsDependingOnFocus as any)[id][layout.focus]), - } - } - } - - const isIPhone = /iPhone/.test(navigator.userAgent) - return ( - - -
- {isIPhone &&
} -
-
- - {state.view.layout.mode === 'multi' - ?
- {multiTargets.map(({icon, label, id}) => - //
- : t.id).indexOf(state.view.layout.focus)} - style={{ - flex: 1, - // justifyContent: 'center' - }} - model={singleTargets.map(({icon, label, id}) => - ({icon, label, disabled: id === 'customizer', command: () => model.changeSingleVisibility(id)}))} /> - } -
-
- -
- {/* {position: 'relative'}}> */} - - - - {/* */} -
- - {!isIPhone &&
} -
-
-
- ); -} - - // - // - // - // - // - // - // - // - //