Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon.png">
<link rel="manifest" href="site.webmanifest">
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="page-drop.css">
<script src="os/wasm-rtos.js" defer></script>
<script src="main.js" defer></script>
</head>
Expand All @@ -29,6 +30,18 @@
</nav>
</header>

<div id="pageDropOverlay" class="page-drop-overlay" role="status" aria-live="polite" aria-hidden="true">
<div class="page-drop-overlay-card">
<span class="page-drop-overlay-label">WASM task</span>
<svg class="page-drop-overlay-icon" viewBox="0 0 64 64" aria-hidden="true" focusable="false">
<path d="M32 45V15M20 27L32 15L44 27" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14 42V51H50V42" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<strong>Drop to launch</strong>
<span>Release anywhere · .wasm files only</span>
</div>
</div>

<main id="runtime" class="dashboard" aria-label="wasm-rtos task dashboard">
<div class="content-row">
<section class="left-pane" aria-label="Task controls">
Expand All @@ -52,8 +65,8 @@ <h1 id="hero-title">Run WebAssembly tasks everywhere.</h1>
<path d="M32 32V17M24 25L32 17L40 25" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<h2 id="upload-title">Drag &amp; Drop WASM App</h2>
<p id="upload-help">.wasm files only</p>
<h2 id="upload-title">Drop WASM anywhere</h2>
<p id="upload-help">or choose a .wasm file</p>
<button id="launchButton" class="primary-button" type="button">Launch Task</button>
</div>
</section>
Expand Down
60 changes: 51 additions & 9 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ const cpuValue = document.querySelector('#cpuValue');
const cpuProgress = document.querySelector('#cpuProgress');
const fileInput = document.querySelector('#wasmFile');
const dropZone = document.querySelector('#dropZone');
const pageDropOverlay = document.querySelector('#pageDropOverlay');
const launchButton = document.querySelector('#launchButton');
const newTaskButton = document.querySelector('#newTaskButton');
const fuelRateSlider = document.querySelector('#fuelRateSlider');
Expand Down Expand Up @@ -1084,6 +1085,51 @@ function handleFiles(files) {
Array.from(files).forEach((file) => addFile(file).catch((error) => appendLog(`${file.name}: ${error.message}`)));
}

let pageFileDragDepth = 0;

function dragContainsFiles(event) {
return Array.from(event.dataTransfer?.types || []).includes('Files');
}

function setPageDropActive(active) {
pageDropOverlay.classList.toggle('is-active', active);
pageDropOverlay.setAttribute('aria-hidden', String(!active));
}

function resetPageFileDrag() {
pageFileDragDepth = 0;
setPageDropActive(false);
}

function handlePageDragEnter(event) {
if (!dragContainsFiles(event)) return;
event.preventDefault();
pageFileDragDepth += 1;
setPageDropActive(true);
}

function handlePageDragOver(event) {
if (!dragContainsFiles(event)) return;
event.preventDefault();
event.dataTransfer.dropEffect = 'copy';
setPageDropActive(true);
}

function handlePageDragLeave(event) {
if (pageFileDragDepth === 0) return;
event.preventDefault();
pageFileDragDepth -= 1;
if (pageFileDragDepth === 0) setPageDropActive(false);
}

function handlePageDrop(event) {
if (!dragContainsFiles(event)) return;
event.preventDefault();
const files = event.dataTransfer.files;
resetPageFileDrag();
if (files.length > 0) handleFiles(files);
}

['click', 'keydown'].forEach((type) => {
dropZone.addEventListener(type, (event) => {
if (event.type === 'click' || event.key === 'Enter' || event.key === ' ') openPicker();
Expand All @@ -1092,15 +1138,11 @@ function handleFiles(files) {
launchButton.addEventListener('click', (event) => { event.stopPropagation(); openPicker(); });
newTaskButton.addEventListener('click', openPicker);
fileInput.addEventListener('change', () => { handleFiles(fileInput.files); fileInput.value = ''; });
['dragenter', 'dragover'].forEach((type) => dropZone.addEventListener(type, (event) => {
event.preventDefault();
dropZone.classList.add('is-dragover');
}));
['dragleave', 'drop'].forEach((type) => dropZone.addEventListener(type, (event) => {
event.preventDefault();
dropZone.classList.remove('is-dragover');
}));
dropZone.addEventListener('drop', (event) => handleFiles(event.dataTransfer.files));
document.addEventListener('dragenter', handlePageDragEnter, true);
document.addEventListener('dragover', handlePageDragOver, true);
document.addEventListener('dragleave', handlePageDragLeave, true);
document.addEventListener('drop', handlePageDrop, true);
document.addEventListener('dragend', resetPageFileDrag, true);

function refreshRuntimeState() {
if (!runtime) return;
Expand Down
72 changes: 72 additions & 0 deletions page-drop.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
.page-drop-overlay {
position: fixed;
inset: 0;
z-index: 1000;
display: grid;
place-items: center;
padding: clamp(20px, 5vw, 72px);
background: rgba(10, 10, 10, 0.82);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
opacity: 0;
visibility: hidden;
pointer-events: none;
transition: opacity 140ms ease, visibility 140ms ease;
}

.page-drop-overlay::before {
content: "";
position: absolute;
inset: clamp(20px, 5vw, 72px);
border: 2px dashed rgba(238, 238, 238, 0.72);
border-radius: clamp(18px, 2vw, 30px);
box-shadow: inset 0 0 80px rgba(255, 255, 255, 0.035);
}

.page-drop-overlay.is-active {
opacity: 1;
visibility: visible;
}

.page-drop-overlay-card {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
color: #f4f4f4;
}

.page-drop-overlay-label {
margin-bottom: 22px;
color: #9e9e9e;
font: 600 11px/1 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
letter-spacing: 0.18em;
text-transform: uppercase;
}

.page-drop-overlay-icon {
width: 76px;
height: 76px;
margin-bottom: 20px;
color: #eeeeee;
filter: drop-shadow(0 0 22px rgba(255, 255, 255, 0.14));
}

.page-drop-overlay-card strong {
margin-bottom: 10px;
font-size: clamp(34px, 5vw, 66px);
font-weight: 550;
letter-spacing: -0.045em;
}

.page-drop-overlay-card > span:last-child {
color: #b8b8b8;
font-size: clamp(14px, 1.4vw, 18px);
}

@media (prefers-reduced-motion: reduce) {
.page-drop-overlay {
transition: none;
}
}
Loading