Skip to content

Commit

Permalink
restructure shell
Browse files Browse the repository at this point in the history
  • Loading branch information
takase1121 committed May 11, 2024
1 parent 88502dd commit 2f2e14a
Show file tree
Hide file tree
Showing 11 changed files with 913 additions and 483 deletions.
11 changes: 4 additions & 7 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,11 @@ jobs:
- name: Checkout this repository
uses: actions/checkout@v4

- uses: actions/setup-go@v5
- uses: actions/setup-node@v4
with:
go-version: '1.22.2'
node-version: lts
cache: npm
cache-dependency-path: shell/package-lock.json

- name: Setup Python
uses: actions/setup-python@v5
Expand All @@ -60,11 +62,6 @@ jobs:
- name: Build
run: bash build.sh --ref ${{ github.event.inputs.version }}

- name: Minify
run: |
go install github.com/tdewolff/minify/v2/cmd/minify@latest
minify -r -o dist/ dist/
- name: Create Artifact
uses: actions/upload-pages-artifact@v3
with:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ core/build
core/install

shell/node_modules
shell/dist

lite-xl/build
lite-xl/.git
20 changes: 16 additions & 4 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
set -euo pipefail
IFS=$'\n\t'

if ! [[ -f "shell/index.html" ]]; then
if ! [[ -f "shell/package.json" ]]; then
echo "Please run this script from the root directory of the repository."
exit 1
fi
Expand Down Expand Up @@ -256,16 +256,28 @@ main() {
--js-output=bundle.wasm.js --use-preload-cache --no-node --no-force \
--use-preload-plugins --quiet

popd

# build the shell
pushd shell
if [[ -n ${GITHUB_ACTIONS-} ]]; then
npm ci
else
npm i -D
fi
npm run build
popd

# create the distribution
if [[ -d "$output" ]]; then
rm -r "$output"
fi
mkdir "$output"

# copy all the files
cp -r "$rootdir/shell/css" "$rootdir/shell/js" "$rootdir/shell/"*.html "$output"
cp "lite-xl/lite-xl.js" "lite-xl/lite-xl.wasm" "$output"
cp bundle.wasm.js bundle.wasm "$output"
cp -r "$rootdir/shell/dist/." "$output"
cp "$xldir/lite-xl/lite-xl.js" "$xldir/lite-xl/lite-xl.wasm" "$output/js"
cp "$xldir/bundle.wasm.js" "$xldir/bundle.wasm" "$output/js"
}

main "$@"
2 changes: 0 additions & 2 deletions shell/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ canvas {
position: fixed;
top: 50%;
left: 50%;
-webkit-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}

Expand Down
5 changes: 2 additions & 3 deletions shell/index.html → shell/html/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@
<meta property="og:description" content="Try Lite XL in your Browser!" />

<link rel="stylesheet" href="./css/main.css" />
<script src="https://unpkg.com/[email protected]"></script>
<script src="./js/main.js"></script>
<script src="./bundle.wasm.js"></script>
<script src="./lite-xl.js"></script>
<script src="./js/bundle.wasm.js"></script>
<script src="./js/lite-xl.js"></script>
</head>

<body>
Expand Down
202 changes: 202 additions & 0 deletions shell/js/fs.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
import { zip } from "fflate"

/**
* Splits the path into different segments.
* @param {string} path The path.
*/
function pathSegments(path) {
// if the first path segment is empty, it's probably the root
// if other path segments are empty, it's just //bla and you can assume it's . in the middle
const segments = path
.split("/")
.map((x, i) => (x === "" ? (i === 0 ? "" : ".") : x));
if (segments[segments.length - 1] === ".") segments.pop();

return segments.map(
(x, i, a) => a.slice(0, i + 1).join("/") + (x === "" ? "/" : "")
);
}

/**
* Creates a directory.
* @param {string} dir The directory.
*/
export function mkdirp(dir) {
for (const segment of pathSegments(dir)) {
try {
FS.mkdir(segment);
} catch (e) {
if (e.code !== "EEXIST") throw e;
}
}
}

/**
* Reads a file and write it to the filesystem.
* @param {File} file The file to read.
* @returns {Promise<void>}
*/
function writeFile(file) {
return new Promise((res, rej) => {
const reader = new FileReader();
reader.onload = () => {
try {
FS.writeFile(file.destination, new Uint8Array(reader.result));
res();
} catch (e) {
console.log(e);
rej(new Error(`${file.destination}: ${e.code ? e.code : e}`));
}
};
reader.onerror = rej;
reader.readAsArrayBuffer(file);
});
}

/**
* Gets the file from the user and uploads it to a destination directory.
* @param {string} dest The destination.
* @param {boolean} [dir] Set this to true to accept directories.
* @returns {Promise<number>}
*/
export function uploadFiles(dest, dir) {
return new Promise((res, rej) => {
// create a file input, trigger it
const fileInput = document.createElement("input");
fileInput.type = "file";
fileInput.multiple = true;
if (dir) fileInput.webkitdirectory = true;
fileInput.onchange = () => {
// make file.name actually good
const inputFiles = Array.from(fileInput.files);
inputFiles.forEach(
(f) =>
(f.destination = `${dest}/${
f.webkitRelativePath === "" ? f.name : f.webkitRelativePath
}`)
);

// create the directory structure needed
new Set(
inputFiles
.filter((f) => f.webkitRelativePath !== "")
.map((f) => {
const segments = pathSegments(f.destination);
return segments[segments.length - 2];
})
.sort((a, b) => a.localeCompare(b))
).forEach(mkdirp);

// create the files
Promise.all(inputFiles.map(writeFile))
.then((v) => res(v.length))
.catch(rej);
};
fileInput.oncancel = () => res();
fileInput.click();
});
}

/**
* Automatically downloads a file.
* @param {File} file The file object.
*/
function downloadFile(file) {
const url = URL.createObjectURL(file);
const elem = document.createElement("a");
elem.style.display = "none";
elem.href = url;
document.body.appendChild(elem);
elem.download = file.name;
elem.click();
document.body.removeChild(elem);
// revoke in 1 minute
setTimeout(() => {
URL.revokeObjectURL(url);
}, 60000);
}

/**
* Recursively zips a directory.
* @param {string} path The path to the directory.
* @returns {Promise<number>}
*/
async function fsDownloadDirectory(path) {
if (!FS.isDir(FS.stat(path).mode)) {
throw new Error("cannot download non-directories");
}
const filename = path.split("/").pop();
const relPath = path.split("/").slice(0, -1).join("/");

// recursively read all files into a directory with a DFS
let count = 0;
const filesObj = {};
const stack = [path];
while (stack.length > 0) {
const currentEntry = stack.pop();
const entries = FS.readdir(currentEntry);

for (const entry of entries) {
if (entry === "." || entry == "..") continue;
const fullPath = `${currentEntry}/${entry}`;
const stat = FS.stat(fullPath);
if (FS.isDir(stat.mode)) {
// insert into stack
stack.push(fullPath);
} else if (FS.isFile(stat.mode)) {
const zipPath = fullPath.slice(relPath.length + 1);
filesObj[zipPath] = [FS.readFile(fullPath), { mtime: stat.mtime }];
count++;
}
// ignore other files
}
}

// create the zip file
const file = await new Promise((res, rej) => {
zip(filesObj, { consume: true, level: 9 }, (err, out) => {
if (err) {
rej(err);
} else {
res(out);
}
});
});

// download it
downloadFile(new File([file], filename, { type: "application/zip" }));

return count;
}

/**
* Downloads a file.
* @param {string} path The path.
* @returns {}
*/
async function fsDownloadFile(path) {
const filename = path.split("/").pop();
console.log(path, filename);
const content = FS.readFile(path);
downloadFile(
new File([content], filename, {
type: "application/octet-stream",
})
);
return 1;
}

/**
* Downloads a file or directory.
* @param {string} path The path to the file or directory.
* @returns the number of files downloaded.
*/
export async function downloadFiles(path) {
const stat = FS.stat(path);
if (FS.isDir(stat.mode)) {
return await fsDownloadDirectory(path);
} else if (FS.isFile(stat.mode)) {
return await fsDownloadFile(path);
}
return 0;
}
Loading

0 comments on commit 2f2e14a

Please sign in to comment.