Files
ESP32-WijiBoard/web/js/sections/home.js
T

179 lines
8.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* WijiBoard Home Section
* web/js/sections/home.js
*
* A minimal placeholder home section.
*/
import BLE from '../ble.js';
const HomeSection = {
_cleanup: [],
mount(container) {
container.innerHTML = `
<div class="section-page fade-in">
<div class="section-header">
<h2>WijiBoard</h2>
<p>Bluetooth Spirit Board — SCARA arm controller. Select a section from the sidebar to get started.</p>
</div>
<div class="grid-2" style="margin-bottom:16px">
<!-- Status card -->
<div class="card">
<div class="card-header">
<span class="card-title">Connection</span>
</div>
<div class="status-row" style="margin-bottom:10px">
<span class="status-label">BLE</span>
<span class="status-value" id="home-ble-status">
${BLE.isConnected() ? `✓ ${BLE.getDeviceName()}` : 'Not connected'}
</span>
</div>
<p style="font-size:0.8rem;color:var(--text-muted)">
Use the <strong style="color:var(--text-secondary)">Connect</strong> button
in the top bar to pair with the WijiBoard via Bluetooth.
The device name is <code style="color:var(--accent-blue)">WijiBoard</code>.
</p>
</div>
<!-- About card -->
<div class="card">
<div class="card-header">
<span class="card-title">About</span>
</div>
<p style="font-size:0.82rem;line-height:1.8;color:var(--text-secondary)">
This dashboard controls a two-arm SCARA robot that moves a planchette
across a spirit board. It uses <strong style="color:var(--text-primary)">Web Bluetooth</strong>
to communicate with an ESP32-C3 running two 28BYJ-48 stepper motors
with AccelStepper.
</p>
</div>
</div>
<!-- Sections overview -->
<div class="card">
<div class="card-header">
<span class="card-title">Sections</span>
</div>
<div class="grid-2">
<button class="sidebar-nav-item" data-route="board-control"
onclick="document.getElementById('nav-board-control').click()"
style="padding:14px 16px;background:var(--surface-2);text-align:left;border:none;cursor:pointer;width:100%;">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 20h9M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/>
</svg>
<div>
<div style="font-weight:600;font-size:0.875rem;color:var(--text-primary)">Board Control</div>
<div style="font-size:0.75rem;color:var(--text-muted);margin-top:2px">Interactive map navigation</div>
</div>
</button>
<button class="sidebar-nav-item" data-route="input-text"
onclick="document.getElementById('nav-input-text').click()"
style="padding:14px 16px;background:var(--surface-2);text-align:left;border:none;cursor:pointer;width:100%;">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/>
</svg>
<div>
<div style="font-weight:600;font-size:0.875rem;color:var(--text-primary)">Input Text</div>
<div style="font-size:0.75rem;color:var(--text-muted);margin-top:2px">Spell out words automatically</div>
</div>
</button>
<button class="sidebar-nav-item" data-route="sequence-editor"
onclick="document.getElementById('nav-sequence-editor').click()"
style="padding:14px 16px;background:var(--surface-2);text-align:left;border:none;cursor:pointer;width:100%;">
<svg class="nav-icon"><use href="web/assets/icons.svg#icon-sequence"></use></svg>
<div>
<div style="font-weight:600;font-size:0.875rem;color:var(--text-primary)">Sequence Editor</div>
<div style="font-size:0.75rem;color:var(--text-muted);margin-top:2px">Build and test custom sequences</div>
</div>
</button>
<button class="sidebar-nav-item" data-route="stepper-test"
onclick="document.getElementById('nav-stepper-test').click()"
style="padding:14px 16px;background:var(--surface-2);text-align:left;border:none;cursor:pointer;width:100%;">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="3"/><path d="M12 1v4M12 19v4M4.22 4.22l2.83 2.83M16.95 16.95l2.83 2.83M1 12h4M19 12h4M4.22 19.78l2.83-2.83M16.95 7.05l2.83-2.83"/>
</svg>
<div>
<div style="font-weight:600;font-size:0.875rem;color:var(--text-primary)">Stepper Test</div>
<div style="font-size:0.75rem;color:var(--text-muted);margin-top:2px">Manually jog each motor, visualize arm position</div>
</div>
</button>
</div>
</div>
<!-- PWA Install App card (hidden by default) -->
<div class="card" id="home-pwa-install-card" style="display: none; margin-top: 16px;">
<div class="card-header">
<span class="card-title">Install App</span>
</div>
<p style="font-size: 0.82rem; color: var(--text-secondary); margin-bottom: 12px; line-height: 1.5;">
Install WijiBoard to your device for a native app experience and full-screen control.
</p>
<button class="btn btn-primary" id="home-btn-install-pwa" style="width: 100%; justify-content: center;">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 8px;">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="7 10 12 15 17 10"></polyline>
<line x1="12" y1="15" x2="12" y2="3"></line>
</svg>
Install WijiBoard
</button>
</div>
</div>
`;
// Keep home BLE status label in sync
const statusEl = container.querySelector('#home-ble-status');
const update = () => {
if (statusEl) statusEl.textContent = BLE.isConnected()
? `✓ ${BLE.getDeviceName()}`
: 'Not connected';
};
BLE.on('connected', update);
BLE.on('disconnected', update);
// PWA Install Logic
const pwaCard = container.querySelector('#home-pwa-install-card');
const pwaBtn = container.querySelector('#home-btn-install-pwa');
const checkPwaStatus = () => {
if (window.deferredPrompt && pwaCard) {
pwaCard.style.display = 'block';
}
};
// Check initially in case the event fired before we mounted
checkPwaStatus();
window.addEventListener('pwa-installable', checkPwaStatus);
if (pwaBtn) {
pwaBtn.addEventListener('click', async () => {
if (!window.deferredPrompt) return;
window.deferredPrompt.prompt();
const { outcome } = await window.deferredPrompt.userChoice;
console.log(`User response to the install prompt: ${outcome}`);
window.deferredPrompt = null;
pwaCard.style.display = 'none';
});
}
this._cleanup = [
() => BLE.off('connected', update),
() => BLE.off('disconnected', update),
() => window.removeEventListener('pwa-installable', checkPwaStatus),
];
},
unmount() {
this._cleanup.forEach(fn => fn());
this._cleanup = [];
},
};
export default HomeSection;