diff --git a/public/fonts/InterVariable.woff2 b/public/fonts/InterVariable.woff2 new file mode 100644 index 0000000..22a12b0 Binary files /dev/null and b/public/fonts/InterVariable.woff2 differ diff --git a/src/index.tsx b/src/index.tsx index 166f7f4..0af1d28 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -61,7 +61,7 @@ window.addEventListener('load', async () => { registerCustomAppHeightCSSProperty(); - const fs = await createEditorFS({prefix: '/', allowPersistence: isInStandaloneMode}); + const fs = await createEditorFS({prefix: '/libraries/', allowPersistence: isInStandaloneMode}); await registerOpenSCADLanguage(fs, '/', zipArchives); diff --git a/src/runner/openscad-worker.ts b/src/runner/openscad-worker.ts index cdaae72..aba0d6d 100644 --- a/src/runner/openscad-worker.ts +++ b/src/runner/openscad-worker.ts @@ -93,6 +93,11 @@ addEventListener('message', async (e) => { if (inputs) { for (const source of inputs) { try { + if (source.path.startsWith('/libraries/')) { + console.log(`Skipping ${source.path}`); + continue; + } + console.log(`Writing ${source.path}`); instance.FS.writeFile(source.path, await fetchSource(source)); } catch (e) { console.trace(e); diff --git a/src/state/fragment-state.ts b/src/state/fragment-state.ts index 03227c2..9e87631 100644 --- a/src/state/fragment-state.ts +++ b/src/state/fragment-state.ts @@ -57,6 +57,10 @@ export async function readStateFromFragment(): Promise { // For testing const src = decodeURIComponent(serialized.substring('src:'.length)); return createInitialState(null, src); + } else if (serialized.startsWith('testpath:')) { + // For testing + const path = decodeURIComponent(serialized.substring('testpath:'.length)); + return createInitialState(null, '...', path); } let obj; try { diff --git a/src/state/initial-state.ts b/src/state/initial-state.ts index 66d7170..26cd638 100644 --- a/src/state/initial-state.ts +++ b/src/state/initial-state.ts @@ -6,7 +6,7 @@ import { State } from './app-state'; export const defaultSourcePath = '/playground.scad'; export const defaultModelColor = '#f9d72c'; -export function createInitialState(state: State | null, content: string = defaultScad): State { +export function createInitialState(state: State | null, content = defaultScad, activePath = defaultSourcePath): State { type Mode = State['view']['layout']['mode']; const mode: Mode = window.matchMedia("(min-width: 768px)").matches @@ -14,8 +14,8 @@ export function createInitialState(state: State | null, content: string = defaul const initialState: State = { params: { - activePath: defaultSourcePath, - sources: [{path: defaultSourcePath, content}], + activePath: activePath, + sources: [{path: activePath, content}], features: [], exportFormat2D: 'svg', exportFormat3D: 'glb', diff --git a/src/state/model.ts b/src/state/model.ts index 7ea8ce5..85dbaeb 100644 --- a/src/state/model.ts +++ b/src/state/model.ts @@ -118,17 +118,24 @@ export class Model { } openFile(path: string) { - // alert(`TODO: open ${path}`); + // console.log(`openFile: ${path}`); if (this.mutate(s => { if (s.params.activePath != path) { - s.params.activePath = path; - if (!s.params.sources.find(src => src.path === path)) { - let content = ''; + const readSource = (path: string) => { try { - content = new TextDecoder("utf-8").decode(this.fs.readFileSync(path)); + return new TextDecoder("utf-8").decode(this.fs.readFileSync(path)); } catch (e) { console.error('Error while reading file:', e); + return ''; } + }; + // Remove source of previous active path if it's unmodified + const activePathContent = readSource(s.params.activePath); + s.params.sources = s.params.sources.filter(src => src.path !== s.params.activePath || src.content != activePathContent); + + s.params.activePath = path; + if (!s.params.sources.find(src => src.path === path)) { + const content = readSource(path); s.params.sources = [...s.params.sources, {path, content}]; } s.lastCheckerRun = undefined; @@ -307,6 +314,7 @@ export class Model { } async render({isPreview, mountArchives, now, retryInOtherDim}: {isPreview: boolean, mountArchives?: boolean, now: boolean, retryInOtherDim?: boolean}) { + // console.log(JSON.stringify(this.state, null, 2)); mountArchives ??= true; retryInOtherDim ??= true; const setRendering = (s: State, value: boolean) => { diff --git a/tests/e2e.test.js b/tests/e2e.test.js index 8f7ea65..de3eb94 100644 --- a/tests/e2e.test.js +++ b/tests/e2e.test.js @@ -21,7 +21,8 @@ beforeEach(async () => { afterEach(async () => { // console.log('Messages:', JSON.stringify(messages, null, 2)); - console.log('Messages:', JSON.stringify(messages.map(({text}) => text), null, 2)); + const testName = expect.getState().currentTestName; + console.log(`[${testName}] Messages:`, JSON.stringify(messages.map(({text}) => text), null, 2)); const errors = messages.filter(msg => msg.type === 'error' && @@ -34,6 +35,9 @@ afterEach(async () => { function loadSrc(src) { return page.goto(url + '#src:' + encodeURIComponent(src)); } +function loadPath(path) { + return page.goto(url + '#testpath:' + encodeURIComponent(path)); +} async function waitForViewer() { await page.waitForSelector('model-viewer'); await page.waitForFunction(() => { @@ -102,6 +106,12 @@ describe('e2e', () => { expect3DManifold(); }, longTimeout); + test('load a demo by path', async () => { + await loadPath('/libraries/closepoints/demo_3D_art.scad'); + await waitForViewer(); + expect3DPolySet(); + }, longTimeout); + test('customizer & windows line endings work', async () => { await loadSrc([ 'myVar = 10;',