From 8f07a70527571830d3d75fd08cb074b3ae7c5a1d Mon Sep 17 00:00:00 2001 From: Neta London Date: Mon, 16 Sep 2024 19:30:00 +0300 Subject: [PATCH 1/5] Allow switching folders in local fs --- components/src/stores/base.context.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/src/stores/base.context.ts b/components/src/stores/base.context.ts index 6b591c32f..90129100a 100644 --- a/components/src/stores/base.context.ts +++ b/components/src/stores/base.context.ts @@ -106,6 +106,9 @@ export function useBaseContext(): BaseContext { async (force = false, createFiles = false) => { if (!canUpgradeFs || (root && !force)) return; const handler = await openNand2TetrisDirectory(); + if (root) { + await removeLocalAdapterFromIndexedDB(); + } const adapter = await createAndStoreLocalAdapterInIndexedDB(handler); await setLocalFs(adapter, createFiles); }, From 36002142f408696d7dc97c9e65a2b7a1870c437d Mon Sep 17 00:00:00 2001 From: Neta London Date: Mon, 16 Sep 2024 19:43:17 +0300 Subject: [PATCH 2/5] Clear chips menu where there are no hdl projects --- components/src/stores/chip.store.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/src/stores/chip.store.ts b/components/src/stores/chip.store.ts index 3f8481e6f..740dbe5f0 100644 --- a/components/src/stores/chip.store.ts +++ b/components/src/stores/chip.store.ts @@ -309,6 +309,8 @@ export function makeChipStore( if (hdlProjects.length > 0) { await actions.setProject(sortedNames[0]); + } else { + dispatch.current({ action: "setChips", payload: [] }); } dispatch.current({ action: "clearChip" }); From dcfabf262386af9416f1512527c2739a797d5426 Mon Sep 17 00:00:00 2001 From: Neta London Date: Tue, 17 Sep 2024 14:43:13 +0300 Subject: [PATCH 3/5] Copy local storage data when downloading projects for the first time --- components/src/stores/base.context.ts | 47 +++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/components/src/stores/base.context.ts b/components/src/stores/base.context.ts index 90129100a..1bef869fa 100644 --- a/components/src/stores/base.context.ts +++ b/components/src/stores/base.context.ts @@ -21,6 +21,43 @@ import { removeLocalAdapterFromIndexedDB, } from "./base/indexDb.js"; +async function cloneTree( + sourceFs: FileSystem, + targetFs: FileSystem, + dir = "/", + pathTransform: (path: string) => string, + overwrite = false, +) { + const sourceDir = dir == "/" ? "" : dir; + const targetDir = pathTransform(sourceDir); + + const sourceItems = await sourceFs.scandir(dir); + + targetFs.mkdir(targetDir); + const targetItems = new Set( + (await targetFs.scandir(targetDir)).map((stat) => stat.name), + ); + + for (const item of sourceItems) { + if (item.isFile()) { + if (overwrite || !targetItems.has(item.name)) { + await targetFs.writeFile( + `${targetDir}/${item.name}`, + await sourceFs.readFile(`${sourceDir}/${item.name}`), + ); + } + } else { + await cloneTree( + sourceFs, + targetFs, + `${sourceDir}/${item.name}`, + pathTransform, + overwrite, + ); + } + } +} + export interface BaseContext { fs: FileSystem; localFsRoot?: string; @@ -49,8 +86,14 @@ export function useBaseContext(): BaseContext { new FileSystemAccessFileSystemAdapter(handle), ); if (createFiles) { - const loaders = await import("@nand2tetris/projects/loader.js"); - await loaders.createFiles(newFs); + if (root) { + const loaders = await import("@nand2tetris/projects/loader.js"); + await loaders.createFiles(newFs); + } else { + await cloneTree(fs, newFs, "/projects", (path: string) => + path.replace("/projects", "/").replace(/\/0*(\d+)/, "$1"), + ); + } } setFs(newFs); setRoot(handle.name); From e7d6dc08e08cbea305fc7e1a5547dc8ec01f24ff Mon Sep 17 00:00:00 2001 From: Neta London Date: Tue, 17 Sep 2024 16:06:41 +0300 Subject: [PATCH 4/5] Don't override existing files --- projects/src/full.ts | 2 +- projects/src/reset.ts | 12 ++++++++++-- web/src/shell/settings.tsx | 1 - 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/projects/src/full.ts b/projects/src/full.ts index 08a272572..e3b35a8d6 100644 --- a/projects/src/full.ts +++ b/projects/src/full.ts @@ -72,7 +72,7 @@ export const resetTests = async (fs: FileSystem, projects?: number[]) => { }; export const createFiles = async (fs: FileSystem) => { - await reset(fs, ProjectFiles); + await reset(fs, ProjectFiles, "/", false); }; export const Assignments = { diff --git a/projects/src/reset.ts b/projects/src/reset.ts index f423de30a..6f54f996e 100644 --- a/projects/src/reset.ts +++ b/projects/src/reset.ts @@ -19,11 +19,19 @@ export async function resetBySuffix( } } -export async function reset(fs: FileSystem, tree: Tree, base?: string) { +export async function reset( + fs: FileSystem, + tree: Tree, + base?: string, + override = true, +) { + const items = (await fs.scandir(base ?? "/")).map((item) => item.name); for (const [key, value] of Object.entries(tree)) { const path = `${base ? `${base}/` : ""}${key}`; if (typeof value === "string") { - await fs.writeFile(path, value); + if (override || !items.includes(key)) { + await fs.writeFile(path, value); + } } else { await fs.mkdir(path); await reset(fs, value as Tree, path); diff --git a/web/src/shell/settings.tsx b/web/src/shell/settings.tsx index 4317e9ef6..11abc1d04 100644 --- a/web/src/shell/settings.tsx +++ b/web/src/shell/settings.tsx @@ -10,7 +10,6 @@ import "../pico/button-group.scss"; import "../pico/property.scss"; import { TrackingDisclosure } from "../tracking"; import { getVersion, setVersion } from "../versions"; -// import { useDialog } from "./dialog"; const showUpgradeFs = true; From f5c0766cd21b48fd97926bc68ec08a3fa0c9ae2f Mon Sep 17 00:00:00 2001 From: Neta London Date: Tue, 1 Oct 2024 13:00:16 +0300 Subject: [PATCH 5/5] CR --- components/src/file_utils.ts | 37 ++++++++++++++++++++++++++ components/src/stores/base.context.ts | 38 +-------------------------- 2 files changed, 38 insertions(+), 37 deletions(-) diff --git a/components/src/file_utils.ts b/components/src/file_utils.ts index bf4cf39e4..9df6d7bf1 100644 --- a/components/src/file_utils.ts +++ b/components/src/file_utils.ts @@ -42,3 +42,40 @@ export function sortFiles(files: Stats[]) { } }); } + +export async function cloneTree( + sourceFs: FileSystem, + targetFs: FileSystem, + dir = "/", + pathTransform: (path: string) => string, + overwrite = false, +) { + const sourceDir = dir == "/" ? "" : dir; + const targetDir = pathTransform(sourceDir); + + const sourceItems = await sourceFs.scandir(dir); + + targetFs.mkdir(targetDir); + const targetItems = new Set( + (await targetFs.scandir(targetDir)).map((stat) => stat.name), + ); + + for (const item of sourceItems) { + if (item.isFile()) { + if (overwrite || !targetItems.has(item.name)) { + await targetFs.writeFile( + `${targetDir}/${item.name}`, + await sourceFs.readFile(`${sourceDir}/${item.name}`), + ); + } + } else { + await cloneTree( + sourceFs, + targetFs, + `${sourceDir}/${item.name}`, + pathTransform, + overwrite, + ); + } + } +} diff --git a/components/src/stores/base.context.ts b/components/src/stores/base.context.ts index 1bef869fa..10fda269e 100644 --- a/components/src/stores/base.context.ts +++ b/components/src/stores/base.context.ts @@ -11,6 +11,7 @@ import { useState, } from "react"; import { useDialog } from "../dialog.js"; +import { cloneTree } from "../file_utils.js"; import { FileSystemAccessFileSystemAdapter, openNand2TetrisDirectory, @@ -21,43 +22,6 @@ import { removeLocalAdapterFromIndexedDB, } from "./base/indexDb.js"; -async function cloneTree( - sourceFs: FileSystem, - targetFs: FileSystem, - dir = "/", - pathTransform: (path: string) => string, - overwrite = false, -) { - const sourceDir = dir == "/" ? "" : dir; - const targetDir = pathTransform(sourceDir); - - const sourceItems = await sourceFs.scandir(dir); - - targetFs.mkdir(targetDir); - const targetItems = new Set( - (await targetFs.scandir(targetDir)).map((stat) => stat.name), - ); - - for (const item of sourceItems) { - if (item.isFile()) { - if (overwrite || !targetItems.has(item.name)) { - await targetFs.writeFile( - `${targetDir}/${item.name}`, - await sourceFs.readFile(`${sourceDir}/${item.name}`), - ); - } - } else { - await cloneTree( - sourceFs, - targetFs, - `${sourceDir}/${item.name}`, - pathTransform, - overwrite, - ); - } - } -} - export interface BaseContext { fs: FileSystem; localFsRoot?: string;