From 7843b65829e5251669102f426f2fcf0bd053b877 Mon Sep 17 00:00:00 2001 From: Pham Le Gia Dai Date: Thu, 26 Jun 2025 00:55:44 +0700 Subject: [PATCH 1/5] feat: update new ui with pseudo cpu, ram & network usage --- .gitignore | 2 + index.html | 131 +++++++++++------- public/static/css/app.css | 9 ++ public/static/css/base/_base.css | 22 +++ public/static/css/layout/_layout.css | 8 ++ public/static/css/modules/_modules.css | 6 + public/static/css/modules/app-xterm.css | 33 +++++ public/static/css/modules/footer-stats.css | 41 ++++++ public/static/css/modules/progress-bar.css | 20 +++ .../static/css/modules/terminal-content.css | 32 +++++ public/static/css/modules/terminal-window.css | 20 +++ public/static/css/modules/title-bar.css | 53 +++++++ public/static/css/state/_state.css | 2 + public/static/css/theme/_theme.css | 9 ++ public/{ => static}/img/github.svg | 0 public/{ => static}/img/linkedin.svg | 0 public/{ => static}/img/opendev.svg | 0 public/{ => static}/img/upload.svg | 0 src/{js => }/main.js | 88 ++++++------ src/styles/app-modal.css | 112 --------------- src/styles/app.css | 33 ----- src/styles/serial.css | 17 --- src/styles/terminal-toolbar.css | 34 ----- src/styles/terminal.css | 86 ------------ src/{js => }/terminal.js | 19 ++- src/{js => }/toolbar.js | 0 src/{js => }/upload-files.js | 0 27 files changed, 392 insertions(+), 385 deletions(-) create mode 100644 public/static/css/app.css create mode 100644 public/static/css/base/_base.css create mode 100644 public/static/css/layout/_layout.css create mode 100644 public/static/css/modules/_modules.css create mode 100644 public/static/css/modules/app-xterm.css create mode 100644 public/static/css/modules/footer-stats.css create mode 100644 public/static/css/modules/progress-bar.css create mode 100644 public/static/css/modules/terminal-content.css create mode 100644 public/static/css/modules/terminal-window.css create mode 100644 public/static/css/modules/title-bar.css create mode 100644 public/static/css/state/_state.css create mode 100644 public/static/css/theme/_theme.css rename public/{ => static}/img/github.svg (100%) rename public/{ => static}/img/linkedin.svg (100%) rename public/{ => static}/img/opendev.svg (100%) rename public/{ => static}/img/upload.svg (100%) rename src/{js => }/main.js (51%) delete mode 100644 src/styles/app-modal.css delete mode 100644 src/styles/app.css delete mode 100644 src/styles/serial.css delete mode 100644 src/styles/terminal-toolbar.css delete mode 100644 src/styles/terminal.css rename src/{js => }/terminal.js (78%) rename src/{js => }/toolbar.js (100%) rename src/{js => }/upload-files.js (100%) diff --git a/.gitignore b/.gitignore index e84f5d9..d76fe94 100644 --- a/.gitignore +++ b/.gitignore @@ -808,3 +808,5 @@ FodyWeavers.xsd # Additional files built by Visual Studio # End of https://www.toptal.com/developers/gitignore/api/visualstudio,visualstudiocode,eclipse,intellij,webstorm,node +.idea +*.iso \ No newline at end of file diff --git a/index.html b/index.html index bbf0070..8c5cfc1 100644 --- a/index.html +++ b/index.html @@ -1,74 +1,101 @@ - + - - - + + + + + BitBox - Browser VM - - + + - - - + + - BitBox - Browser VM + + + + + + + + +
- -
-
- BitBox - -
- - GitHub - daipham3213 - - - OpenDev - daipham.3213@gmail.com - - - LinkedIn - daipham-3213 - - +
+
+
+ + + +
+
+ + + +
+
+ + +
-
-
-
+
BitBox — Browser VM
+
+ +
+
+
+
Loading resources, please wait...
-
- - - diff --git a/public/static/css/modules/_modules.css b/public/static/css/modules/_modules.css index 03e4a35..702053c 100644 --- a/public/static/css/modules/_modules.css +++ b/public/static/css/modules/_modules.css @@ -4,4 +4,6 @@ @import "terminal-content.css"; @import "title-bar.css"; @import "app-xterm.css"; -@import "tooltip.css"; \ No newline at end of file +@import "tooltip.css"; +@import "button.css"; +@import "file-input.css"; diff --git a/public/static/css/modules/button.css b/public/static/css/modules/button.css new file mode 100644 index 0000000..9f9faf2 --- /dev/null +++ b/public/static/css/modules/button.css @@ -0,0 +1,54 @@ +.m-button { + background-color: transparent; + border: none; + color: #61AFEF; + /* Default text color for this button type */ + cursor: pointer; + padding: 0.25rem; + border-radius: 0.25rem; + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + transition: background-color 0.2s ease, box-shadow 0.2s ease, transform 0.3s ease-in-out; + min-width: 180px; +} + +.m-button__content { + position: relative; + z-index: 2; + /* Ensures content is on top of pseudo-elements */ + color: #ffffff; + /* White text/icon color for content */ + display: flex; + /* For icon and text alignment */ + align-items: center; + gap: 0.5rem; + padding: 12px 24px; + /* Visual padding for the clickable area */ +} + +.m-button__icon { + font-size: 1.25rem; +} + +.m-button:hover { + background-color: rgba(97, 175, 239, 0.1); + transform: scale(1.05); +} + +.m-button:focus { + outline: none; + box-shadow: 0 0 0 2px rgba(97, 175, 239, 0.5); +} + +.m-button:active { + background-color: rgba(97, 175, 239, 0.2); +} + +@media (max-width: 600px) { + .m-button { + min-width: unset; + width: 90%; + } +} \ No newline at end of file diff --git a/public/static/css/modules/file-input.css b/public/static/css/modules/file-input.css new file mode 100644 index 0000000..72e9327 --- /dev/null +++ b/public/static/css/modules/file-input.css @@ -0,0 +1,58 @@ +.m-file-input { + display: inline-flex; + align-items: center; + justify-content: center; + cursor: pointer; + min-width: 36px; + padding: 0.25rem; + border-radius: 0.25rem; + background-color: #1f2937; + /* Dark gray background */ + color: #ffffff; + /* White text/icon color */ + transition: background-color 0.2s ease, box-shadow 0.2s ease, transform 0.3s ease-in-out; +} + +.m-file-input:hover { + transform: scale(1.05); + background-color: rgba(97, 175, 239, 0.1); +} + +.m-file-input__input { + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + opacity: 0; + /* Hide the default file input */ + cursor: pointer; + z-index: 2; + /* Ensure it's clickable */ +} + +.m-file-input__label { + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + color: #ffffff; + position: relative; + z-index: 1; + /* Above input but below pseudo-elements for border */ + padding: 4px 8px; + /* Visual padding for the clickable area */ + width: 100%; + height: 100%; +} + +.m-file-input__icon { + font-size: 1.25rem; +} + +@media (max-width: 600px) { + .m-file-input { + min-width: unset; + width: 90%; + } +} \ No newline at end of file diff --git a/public/static/css/modules/footer-stats.css b/public/static/css/modules/footer-stats.css index c2ad83a..1827183 100644 --- a/public/static/css/modules/footer-stats.css +++ b/public/static/css/modules/footer-stats.css @@ -38,124 +38,5 @@ .m-footer-stats__network-speed { /* Base style */ + min-width: 0; } - -.m-footer-stats__item--file-picker { - display: flex; - align-items: center; - flex-direction: row; - justify-content: end; - gap: 0.5rem; -} - -.m-footer-stats__item--button { - position: relative; - overflow: hidden; - z-index: 1; - - background-color: transparent; - border: none; - color: #61AFEF; - cursor: pointer; - padding: 0.25rem; - border-radius: 0.25rem; - display: flex; - align-items: center; - justify-content: center; - gap: 0.5rem; - transition: background-color 0.2s ease, box-shadow 0.2s ease, transform 0.3s ease-in-out; - min-width: 24px; - box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); -} - -/* Spinning gradient pseudo-element */ -.m-footer-stats__item--button::before { - content: ''; - position: absolute; - top: -50%; - left: -50%; - width: 200%; - height: 200%; - background: conic-gradient(from 0deg, - #ff0000, - /* Red */ - #ffa500, - /* Orange */ - #ffff00, - /* Yellow */ - #008000, - /* Green */ - #0000ff, - /* Blue */ - #4b0082, - /* Indigo */ - #ee82ee, - /* Violet */ - #ff0000 - /* Red (to complete the loop) */ - ); - animation: spin 4s linear infinite; - z-index: -1; - /* Place behind the button's inner content */ -} - -.m-footer-stats__item--button::after { - content: ''; - position: absolute; - top: 2px; - left: 2px; - right: 2px; - bottom: 2px; - background-color: #1f2937; - border-radius: 0.25rem; - z-index: 0; -} - -/* Ensure button content is on top */ -.m-footer-stats__item--button span, -.m-footer-stats__item--button i { - position: relative; - z-index: 2; - color: #ffffff; - /* Ensure text/icon color is white for contrast */ -} - -.m-footer-stats__item--button:hover { - background-color: rgba(97, 175, 239, 0.1); - transform: scale(1.05); -} - -.m-footer-stats__item--button:focus { - outline: none; - box-shadow: 0 0 0 2px rgba(97, 175, 239, 0.5); -} - -.m-footer-stats__item--button:active { - background-color: rgba(97, 175, 239, 0.2); -} - -.m-footer-stats__item--button i { - font-size: 1.25rem; -} - -/* Spinning animation */ -@keyframes spin { - 0% { - transform: rotate(0deg); - } - - 100% { - transform: rotate(360deg); - } -} - -@media (max-width: 600px) { - .m-footer-stats__item--button { - min-width: unset; - width: 90%; - } - - .button-container { - padding: 0 10px; - } -} \ No newline at end of file diff --git a/public/static/css/modules/tooltip.css b/public/static/css/modules/tooltip.css index 94c8ce9..443607b 100644 --- a/public/static/css/modules/tooltip.css +++ b/public/static/css/modules/tooltip.css @@ -1,14 +1,12 @@ -/* Tooltip styles */ -.tooltip { +.m-tooltip { position: relative; - /* Allows positioning of tooltip text relative to this container */ display: inline-block; - /* Ensures it wraps content and allows proper positioning */ } -.tooltip .tooltip-text { +.m-tooltip__text { visibility: hidden; opacity: 0; + width: 120px; background-color: #555; color: #fff; text-align: center; @@ -16,40 +14,31 @@ padding: 5px 0; position: absolute; z-index: 3; - /* Above button content */ + /* Above button/input content */ bottom: 125%; - /* Position the tooltip above the button */ + /* Position above the element */ left: 50%; - margin-left: -16px; + transform: translateX(-50%); /* Center the tooltip */ transition: opacity 0.3s, visibility 0.3s; - font-size: 0.6rem; - /* Smaller font for tooltip */ + font-size: 0.5rem; + margin-left: 0; + /* Reset margin-left from previous version */ } -.tooltip .tooltip-text::after { +.m-tooltip__text::after { content: ""; position: absolute; top: 100%; - /* At the bottom of the tooltip */ left: 50%; - margin-left: -5px; + transform: translateX(-50%); + /* Center the arrow */ border-width: 5px; border-style: solid; border-color: #555 transparent transparent transparent; } -.tooltip:hover .tooltip-text { +.m-tooltip:hover .m-tooltip__text { visibility: visible; opacity: 1; -} - -@media (max-width: 600px) { - .tooltip .tooltip-text { - left: 50%; - transform: translateX(-50%); - /* Ensure centering on smaller screens */ - margin-left: 0; - /* Reset margin-left */ - } } \ No newline at end of file diff --git a/public/static/css/theme/_theme.css b/public/static/css/theme/_theme.css index e0a941a..e0f6455 100644 --- a/public/static/css/theme/_theme.css +++ b/public/static/css/theme/_theme.css @@ -1,9 +1,3 @@ /* Theme styles: Colors and typography related to the specific theme */ -.t-prompt-green { - color: #98C379; -} - -.t-command-yellow { - color: #E5C07B; -} +@import "spinning-border.css"; \ No newline at end of file diff --git a/public/static/css/theme/spinning-border.css b/public/static/css/theme/spinning-border.css new file mode 100644 index 0000000..f3a23a5 --- /dev/null +++ b/public/static/css/theme/spinning-border.css @@ -0,0 +1,53 @@ +.o-spinning-border { + position: relative; + overflow: hidden; + z-index: 1; + /* Ensures content is above pseudo-elements */ +} + +.o-spinning-border::before { + content: ''; + position: absolute; + top: -50%; + left: -50%; + width: 200%; + height: 200%; + background: conic-gradient(from 0deg, + #ff0000, #ffa500, #ffff00, #008000, #0000ff, #4b0082, #ee82ee, #ff0000); + animation: spin 4s linear infinite; + z-index: -1; + /* Place behind the inner content */ +} + +.o-spinning-border::after { + content: ''; + position: absolute; + top: 2px; + /* Border thickness */ + left: 2px; + /* Border thickness */ + right: 2px; + /* Border thickness */ + bottom: 2px; + /* Border thickness */ + background-color: #1f2937; + /* Inner background color */ + border-radius: 0.25rem; + /* Matches button/container border-radius */ + z-index: 0; + /* Above ::before but below content */ +} + +.o-shadow { + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); +} + +@keyframes spin { + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } +} \ No newline at end of file diff --git a/src/main.js b/src/main.js index 2d58c51..c31f820 100644 --- a/src/main.js +++ b/src/main.js @@ -9,10 +9,7 @@ const state = { emulator_uptime: $('emulator-uptime'), emulator_state: $('emulator-state'), emulator_state_progress: $('emulator-state-progress'), - emulator_ram_usage: $('emulator-ram-usage'), - emulator_ram_usage_bar: $('emulator-ram-usage-bar'), - emulator_cpu_usage: $('emulator-cpu-usage'), - emulator_cpu_usage_bar: $('emulator-cpu-usage-bar'), + send_file_button: $('emulator-9p-files-send-btn'), network_up: $('emulator-nw-up'), network_down: $('emulator-nw-down'), emulator: null, @@ -106,9 +103,22 @@ const onStarted = () => { state.bytes_transmitted[0], state.bytes_transmitted[0], ]; +}; - // ram usage - // window.emulator.v86.cpu.memory_size; +const onInitFilesystem = () => { + console.log('9p filesystem initialized'); + state.send_file_button.addEventListener('change', (event) => { + Array.from(event.target.files).forEach((file) => { + const loader = new FileReader(); + loader.onload = (f) => { + const buffer = f.target.result; + const path = `/daiplg/${file.name}`; + return state.emulator.create_file(path, new Uint8Array(buffer)); + }; + loader.readAsArrayBuffer(file); + }); + event.target.blur(); + }); }; const entry = () => { @@ -168,6 +178,8 @@ const entry = () => { emulator.add_listener('eth-transmit-end', (args) => { state.bytes_transmitted[0] += args[0]; }); + + emulator.add_listener('9p-attach', onInitFilesystem); }; entry(); diff --git a/src/upload-files.js b/src/upload-files.js deleted file mode 100644 index 4f7d286..0000000 --- a/src/upload-files.js +++ /dev/null @@ -1,84 +0,0 @@ -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) { - selectedFilenameSpan.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 = `/daiplg/${_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;