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",
"starlight-links-validator": "^0.7.1",
"tm-themes": "^1.4.3",
"typescript": "^5.4.5"
"typescript": "^5.4.5",
"use-immer": "^0.10.0"
},
"devDependencies": {
"sass": "^1.77.6"

View File

@ -65,6 +65,9 @@ dependencies:
typescript:
specifier: ^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:
sass:
@ -3278,6 +3281,10 @@ packages:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
dev: false
/immer@10.1.1:
resolution: {integrity: sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==}
dev: false
/immutable@4.3.6:
resolution: {integrity: sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==}
@ -5490,6 +5497,16 @@ packages:
picocolors: 1.0.1
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:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
dev: false

View File

@ -1,13 +1,13 @@
import React, { useEffect, useState } from "react";
import { useMonaco, type Monaco } from "@monaco-editor/react";
import FileView from "./playground/FileView";
import Editor from "./playground/Editor";
import { useImmer, type ImmerHook, type Updater } from "use-immer";
import "@styles/playground.scss";
import mainFileContent from "@assets/playground/main.shu?raw";
import packTomlContent from "@assets/playground/pack.toml?raw";
import FileView from "./playground/FileView";
import Editor from "./playground/Editor";
import Header from "./playground/Header";
export type File = {
@ -21,7 +21,7 @@ export type Directory = {
export type SetState<T> = React.Dispatch<React.SetStateAction<T>>;
export default function Playground() {
const rootDir: Directory = {
const [rootDir, updateRootDir] = useImmer({
dirs: {
src: {
files: {
@ -46,15 +46,29 @@ export default function Playground() {
language: "toml",
},
},
};
} as Directory);
const [fileName, setFileName] = useState("src/main.shu");
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();
useEffect(() => {
if (monaco) {
loadFiles(monaco, rootDir);
loadFiles(monaco, updateRootDir, rootDir);
}
}, [monaco]);
@ -68,7 +82,7 @@ export default function Playground() {
marginTop: "0.5cm",
}}
>
<Header />
<Header onBuild={onBuild} onZip={onZip} />
<FileView
className="file-view"
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 {
if (path.includes("/")) {
let dir = root;
@ -101,19 +147,46 @@ function getFile(root: Directory, path: string): File | 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 ?? {})) {
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 ?? {})) {
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);
if (!monaco.editor.getModel(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";
export default function Header() {
const clickBuild = () => {
console.log("build");
}
const clickZip = () => {
console.log("zip");
}
export default function Header({onBuild, onZip}: {onBuild: () => void; onZip: () => void;}){
return (
<header style={{
@ -15,7 +9,7 @@ export default function Header() {
marginBottom: "0.5cm",
}}>
<h1 id="_top">Playground</h1>
<SplitButton onClick={clickBuild} options={[["Download zip", clickZip]]}>Build</SplitButton>
<SplitButton onClick={onBuild} options={[["Download zip", onZip]]}>Build</SplitButton>
</header>
);
}