@@ -214,73 +215,39 @@ export default function EditorPanel({className, style}: {className?: string, sty
] 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"
- />
-
+
+
+ model.source = s ?? ''}
+ onMount={onMount} // TODO: This looks a bit silly, does it trigger a re-render??
+ options={{
+ ...openscadEditorOptions,
+ readOnly: !isFileWritable(state.params.sourcePath)
+ }}
+ />
+
- {/*
- */}
- {/* */}
-
-
{state.lastCheckerRun?.logText ?? 'No log yet!'}
-
- {/* */}
- {/*
-
*/}
- {/*
- */}
- {/* */}
- {/*
*/}
+
+
{state.lastCheckerRun?.logText ?? 'No log yet!'}
+
)
diff --git a/src/components/FilePicker.tsx b/src/components/FilePicker.tsx
new file mode 100644
index 0000000..df37229
--- /dev/null
+++ b/src/components/FilePicker.tsx
@@ -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 (
+ model.openFile(String(e.value))}
+ // dropdownIcon="pi pi-folder-open"
+ filter
+ style={style}
+ // style={{style}}
+ options={fsItems} />
+ )
+}
diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx
index 897be92..fc284c1 100644
--- a/src/components/Footer.tsx
+++ b/src/components/Footer.tsx
@@ -9,9 +9,10 @@ import { ProgressBar } from 'primereact/progressbar';
import { MenuItem } from 'primereact/menuitem';
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 SettingsMenu from './SettingsMenu';
+import HelpMenu from './HelpMenu';
function downloadOutput(state: State) {
if (!state.output) return;
@@ -46,11 +47,11 @@ function downloadOutput(state: State) {
export default function Footer({style}: {style?: CSSProperties}) {
const model = useContext(ModelContext);
if (!model) throw new Error('No model');
+ const state = model.state;
- const menu = useRef