From a44a194f7a526bf6a466be58ac3a6fabb6da5b62 Mon Sep 17 00:00:00 2001 From: Pham Le Gia Dai Date: Wed, 2 Apr 2025 00:12:59 +0700 Subject: [PATCH] feat: support fs9p --- README.md | 1 - index.html | 27 ++++++++-- public/contents.json | 1 + src/js/main.js | 10 +++- src/js/terminal.js | 7 +-- src/js/upload-files.js | 84 +++++++++++++++++++++++++++++ src/styles/app-modal.css | 112 +++++++++++++++++++++++++++++++++++++++ src/styles/app.css | 1 + 8 files changed, 231 insertions(+), 12 deletions(-) create mode 100644 public/contents.json create mode 100644 src/js/upload-files.js create mode 100644 src/styles/app-modal.css diff --git a/README.md b/README.md index cf1069a..07bbc73 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,6 @@ repository. - [v86](https://github.com/copy/v86) - [xterm.js](https://github.com/xtermjs/xterm.js) -- [tailwindcss](https://tailwindcss.com/) - [bun](https://bun.sh) - [vite](https://vitejs.dev/) diff --git a/index.html b/index.html index 05f26d9..018da3f 100644 --- a/index.html +++ b/index.html @@ -30,12 +30,17 @@ GitHub - daipham3213 - - OpenDev - daipham.3213@gmail.com + + OpenDev - daipham.3213@gmail.com - - LinkedIn - daipham-3213 + + LinkedIn - daipham-3213 +
@@ -46,6 +51,20 @@
Loading resources, please wait...
+ diff --git a/public/contents.json b/public/contents.json new file mode 100644 index 0000000..9306609 --- /dev/null +++ b/public/contents.json @@ -0,0 +1 @@ +{"fsroot":[["daiplg",4096,1743524547,16877,0,0,[]]],"version":3,"size":4096} \ No newline at end of file diff --git a/src/js/main.js b/src/js/main.js index ce8e7b5..cb00c6d 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -1,4 +1,5 @@ -import { override, loadAddons } from './terminal'; +import { loadAddons, override } from './terminal'; +import registerUploadFilesModal from './upload-files'; override(); @@ -22,7 +23,13 @@ const emulator = new window.V86({ url: import.meta.env.VITE_ROOTFS_ISO, async: false, }, + filesystem: { + basefs: '/contents.json', + baseurl: '/flat', + }, autostart: true, + disable_keyboard: true, + disable_speaker: true, network_relay_url: import.meta.env.VITE_NETWORK_RELAY, }); @@ -41,6 +48,7 @@ emulator.add_listener('serial0-output-byte', (byte) => { state.textContent = ''; loadAddons(emulator.serial_adapter.term); + registerUploadFilesModal({ emulator }); terminalElement.focus(); } }); diff --git a/src/js/terminal.js b/src/js/terminal.js index 5c4918a..7cd7f88 100644 --- a/src/js/terminal.js +++ b/src/js/terminal.js @@ -18,9 +18,6 @@ const override = () => { black: '#21222C', brightBlack: '#6272A4', }, - // Tell xterm how many rows/cols (FitAddon will adjust this) - rows: 30, - cols: 80, allowProposedApi: true, }; super({ ...defaultOptions, ...options }); @@ -66,9 +63,7 @@ const loadAddons = (terminal) => { terminal.loadAddon(unicode11Addon); terminal.loadAddon(clipboardAddon); - document.addEventListener('DOMContentLoaded', () => { - fitAddon.fit(); - }); + fitAddon.fit(); // eslint-disable-next-line no-param-reassign terminal.unicode.activeVersion = '11'; diff --git a/src/js/upload-files.js b/src/js/upload-files.js new file mode 100644 index 0000000..07c517f --- /dev/null +++ b/src/js/upload-files.js @@ -0,0 +1,84 @@ +const registerUploadFilesModal = ({ emulator }) => { + const uploadButton = document.getElementById('upload-button'); + const modalOverlay = document.getElementById('modal-overlay'); + const modalCloseButton = document.getElementById('modal-close-button'); + const selectFileButton = document.getElementById('select-file-button'); + const fileInput = document.getElementById('file-input'); + const selectedFilenameSpan = document.getElementById('selected-filename'); + + const openModal = () => { + if (modalOverlay) { + modalOverlay.classList.add('modal-visible'); + } + }; + + const closeModal = () => { + if (modalOverlay) { + modalOverlay.classList.remove('modal-visible'); + if (selectedFilenameSpan) { + selectedFilenameSpan.textContent = 'No file selected'; + } + if (fileInput) { + fileInput.value = ''; + } + } + }; + + if (uploadButton) { + uploadButton.addEventListener('click', openModal); + } + + if (modalCloseButton) { + modalCloseButton.addEventListener('click', closeModal); + } + if (modalOverlay) { + modalOverlay.addEventListener('click', (event) => { + if (event.target === modalOverlay) { + closeModal(); + } + }); + } + + document.addEventListener('keydown', (event) => { + if ( + event.key === 'Escape' && + modalOverlay && + modalOverlay.classList.contains('modal-visible') + ) { + closeModal(); + } + }); + + if (selectFileButton && fileInput) { + selectFileButton.addEventListener('click', () => { + fileInput.click(); + }); + } + + if (fileInput && selectedFilenameSpan) { + fileInput.addEventListener('change', () => { + if (fileInput.files.length > 0) { + selectFileButton.textContent = Array.from(fileInput.files).reduce( + (acc, file, i) => { + const separator = i === 0 ? '' : ', '; + return `${separator}${file.name}`; + }, + ); + Array.from(fileInput.files).forEach((_file) => { + const reader = new FileReader(); + const path = `/${_file.name}`; + reader.onload = (f) => { + const data = new Uint8Array(f.target.result); + return emulator.create_file(path, data); + }; + reader.readAsArrayBuffer(_file); + }); + } else { + selectedFilenameSpan.textContent = 'No file selected'; + } + closeModal(); + }); + } +}; + +export default registerUploadFilesModal; diff --git a/src/styles/app-modal.css b/src/styles/app-modal.css new file mode 100644 index 0000000..ed3bf4a --- /dev/null +++ b/src/styles/app-modal.css @@ -0,0 +1,112 @@ +#modal-overlay { + display: none; + position: fixed; + left: 0; + top: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.7); + z-index: 1000; + + justify-content: center; + align-items: center; +} + +#modal-overlay.modal-visible { + display: flex; +} + +#upload-modal { + background-color: #3a3a3a; + padding: 25px 30px; + border-radius: 5px; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); + min-width: 350px; + max-width: 90%; + position: relative; + color: #eee; + border: 1px solid #555; +} + +#modal-close-button { + position: absolute; + top: 8px; + right: 10px; + background: none; + border: none; + font-size: 24px; + line-height: 1; + color: #aaa; + cursor: pointer; + padding: 0; +} + +#modal-close-button:hover { + color: #eee; +} + +#upload-modal h3 { + margin-top: 0; + margin-bottom: 20px; + color: #f0f0f0; + text-align: center; + font-family: sans-serif; +} + +#upload-modal .form-group { + margin-bottom: 15px; +} + +#upload-modal label { + display: block; + margin-bottom: 5px; + font-size: 14px; + color: #ccc; + font-family: sans-serif; +} + +#upload-modal input[type='text'] { + width: 100%; + padding: 10px; + background-color: #2d2d2d; + border: 1px solid #555; + color: #eee; + border-radius: 3px; + font-family: monospace; + font-size: 14px; +} + +#upload-modal input[type='text']:focus { + outline: none; + border-color: #777; + box-shadow: 0 0 5px rgba(150, 150, 150, 0.2); +} + +#upload-modal #select-file-button { + background-color: #5a5a5a; + border: 1px solid #777; + color: #eee; + padding: 8px 12px; + margin-right: 10px; + cursor: pointer; + border-radius: 3px; + font-size: 14px; + font-family: sans-serif; + transition: background-color 0.2s ease; +} + +#upload-modal #select-file-button:hover { + background-color: #6a6a6a; +} + +#upload-modal #selected-filename { + font-size: 13px; + color: #aaa; + font-family: sans-serif; + display: inline-block; + max-width: 180px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + vertical-align: middle; +} diff --git a/src/styles/app.css b/src/styles/app.css index 3f5394b..0bed1e6 100644 --- a/src/styles/app.css +++ b/src/styles/app.css @@ -3,6 +3,7 @@ @import 'terminal.css'; @import 'terminal-toolbar.css'; @import 'serial.css'; +@import 'app-modal.css'; html, body {