add function for extracting files from editor

This commit is contained in:
Moritz Hölting 2024-06-21 08:07:38 +02:00
parent 05cdc423fc
commit d04f61752d
4 changed files with 105 additions and 20 deletions

View File

@ -29,7 +29,8 @@
"shiki": "^1.7.0", "shiki": "^1.7.0",
"starlight-links-validator": "^0.7.1", "starlight-links-validator": "^0.7.1",
"tm-themes": "^1.4.3", "tm-themes": "^1.4.3",
"typescript": "^5.4.5" "typescript": "^5.4.5",
"use-immer": "^0.10.0"
}, },
"devDependencies": { "devDependencies": {
"sass": "^1.77.6" "sass": "^1.77.6"

View File

@ -65,6 +65,9 @@ dependencies:
typescript: typescript:
specifier: ^5.4.5 specifier: ^5.4.5
version: 5.4.5 version: 5.4.5
use-immer:
specifier: ^0.10.0
version: 0.10.0(immer@10.1.1)(react@18.3.1)
devDependencies: devDependencies:
sass: sass:
@ -3278,6 +3281,10 @@ packages:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
dev: false dev: false
/immer@10.1.1:
resolution: {integrity: sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==}
dev: false
/immutable@4.3.6: /immutable@4.3.6:
resolution: {integrity: sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==} resolution: {integrity: sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==}
@ -5490,6 +5497,16 @@ packages:
picocolors: 1.0.1 picocolors: 1.0.1
dev: false dev: false
/use-immer@0.10.0(immer@10.1.1)(react@18.3.1):
resolution: {integrity: sha512-/eVwNR4TG9Tm/dd+aHYLLaI0FLfYKlkTqKMkn78Ah/EYVzWd/zJIgpkdoFEKbhQJOGo8XN7/mWrTx0exp1c+Ug==}
peerDependencies:
immer: '>=8.0.0'
react: ^16.8.0 || ^17.0.1 || ^18.0.0
dependencies:
immer: 10.1.1
react: 18.3.1
dev: false
/util-deprecate@1.0.2: /util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
dev: false dev: false

View File

@ -1,13 +1,13 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useMonaco, type Monaco } from "@monaco-editor/react"; import { useMonaco, type Monaco } from "@monaco-editor/react";
import FileView from "./playground/FileView"; import { useImmer, type ImmerHook, type Updater } from "use-immer";
import Editor from "./playground/Editor";
import "@styles/playground.scss"; import "@styles/playground.scss";
import mainFileContent from "@assets/playground/main.shu?raw"; import mainFileContent from "@assets/playground/main.shu?raw";
import packTomlContent from "@assets/playground/pack.toml?raw"; import packTomlContent from "@assets/playground/pack.toml?raw";
import FileView from "./playground/FileView";
import Editor from "./playground/Editor";
import Header from "./playground/Header"; import Header from "./playground/Header";
export type File = { export type File = {
@ -21,7 +21,7 @@ export type Directory = {
export type SetState<T> = React.Dispatch<React.SetStateAction<T>>; export type SetState<T> = React.Dispatch<React.SetStateAction<T>>;
export default function Playground() { export default function Playground() {
const rootDir: Directory = { const [rootDir, updateRootDir] = useImmer({
dirs: { dirs: {
src: { src: {
files: { files: {
@ -46,15 +46,29 @@ export default function Playground() {
language: "toml", language: "toml",
}, },
}, },
}; } as Directory);
const [fileName, setFileName] = useState("src/main.shu"); const [fileName, setFileName] = useState("src/main.shu");
const file = getFile(rootDir, fileName)!; const file = getFile(rootDir, fileName)!;
const onBuild: () => void = () => {
if (monaco) {
console.log(getFiles(monaco));
} else {
console.error("monaco has not loaded");
}
};
const onZip: () => void = () => {
if (monaco) {
loadFile(monaco, updateRootDir, {content: "zip"}, "dist/pack.zip");
} else {
console.error("onZip not set");}
};
const monaco = useMonaco(); const monaco = useMonaco();
useEffect(() => { useEffect(() => {
if (monaco) { if (monaco) {
loadFiles(monaco, rootDir); loadFiles(monaco, updateRootDir, rootDir);
} }
}, [monaco]); }, [monaco]);
@ -68,7 +82,7 @@ export default function Playground() {
marginTop: "0.5cm", marginTop: "0.5cm",
}} }}
> >
<Header /> <Header onBuild={onBuild} onZip={onZip} />
<FileView <FileView
className="file-view" className="file-view"
root={rootDir} root={rootDir}
@ -81,6 +95,38 @@ export default function Playground() {
); );
} }
function getFiles(monaco: Monaco): Directory {
const files: Directory = {};
for (const model of monaco.editor.getModels()) {
const parts = model.uri.path.slice(1).split("/");
const name = parts.pop()!;
let dir = files;
for (const part of parts) {
if (!dir.dirs) {
dir.dirs = {};
}
if (!dir.dirs[part]) {
dir.dirs[part] = {};
}
dir = dir.dirs[part].files ?? {};
}
if (!dir.files) {
dir.files = {};
}
dir.files[name] = {
content: model.getValue(),
language: model.getLanguageId(),
};
}
return files;
}
function getFile(root: Directory, path: string): File | null { function getFile(root: Directory, path: string): File | null {
if (path.includes("/")) { if (path.includes("/")) {
let dir = root; let dir = root;
@ -101,19 +147,46 @@ function getFile(root: Directory, path: string): File | null {
return root.files?.[path] ?? null; return root.files?.[path] ?? null;
} }
function loadFiles(monaco: Monaco, dir: Directory, prefix = "") { function loadFiles(monaco: Monaco, updater: Updater<Directory>, dir: Directory, prefix = "") {
for (const [name, d] of Object.entries(dir.dirs ?? {})) { for (const [name, d] of Object.entries(dir.dirs ?? {})) {
loadFiles(monaco, d, prefix + name + "/"); loadFiles(monaco, updater, d, prefix + name + "/");
updater(dir => {
let current = dir;
for(const part of [...prefix.split("/").filter(s => s !== ""), name]) {
if (!current.dirs) {
current.dirs = {};
}
current = current.dirs[part];
}
});
} }
for (const [name, file] of Object.entries(dir.files ?? {})) { for (const [name, file] of Object.entries(dir.files ?? {})) {
loadFile(monaco, file, prefix + name); loadFile(monaco, updater, file, prefix + name);
} }
} }
function loadFile(monaco: Monaco, file: File, name: string) { function loadFile(monaco: Monaco, updater: Updater<Directory>, file: File, name: string) {
const uri = monaco.Uri.parse(name); const uri = monaco.Uri.parse(name);
if (!monaco.editor.getModel(uri)) { if (!monaco.editor.getModel(uri)) {
monaco.editor.createModel(file.content, file.language, uri); monaco.editor.createModel(file.content, file.language, uri);
} }
updater(dir => {
let current = dir;
const parts = name.split("/").filter(s => s !== "");
const last = parts.pop()!;
for(const part of parts) {
console.log(part);
if (!current.dirs) {
current.dirs = {};
}
current = current.dirs[part];
}
if (!current.files) {
current.files = {};
}
current.files[last] = file;
});
} }

View File

@ -1,12 +1,6 @@
import SplitButton from "./SplitButton"; import SplitButton from "./SplitButton";
export default function Header() { export default function Header({onBuild, onZip}: {onBuild: () => void; onZip: () => void;}){
const clickBuild = () => {
console.log("build");
}
const clickZip = () => {
console.log("zip");
}
return ( return (
<header style={{ <header style={{
@ -15,7 +9,7 @@ export default function Header() {
marginBottom: "0.5cm", marginBottom: "0.5cm",
}}> }}>
<h1 id="_top">Playground</h1> <h1 id="_top">Playground</h1>
<SplitButton onClick={clickBuild} options={[["Download zip", clickZip]]}>Build</SplitButton> <SplitButton onClick={onBuild} options={[["Download zip", onZip]]}>Build</SplitButton>
</header> </header>
); );
} }