Better layout and code organization

pull/3/head
Drew Bednar 8 months ago
parent 99222d0524
commit fd641e8655

@ -6,8 +6,8 @@
<title>Web Serial Console</title> <title>Web Serial Console</title>
<script src="https://cdn.tailwindcss.com"></script> <script src="https://cdn.tailwindcss.com"></script>
</head> </head>
<body class="h-full m-0 p-0 bg-neutral-100"> <body class="h-full m-0 p-0 bg-neutral-100 mx-5">
<div class="flex flex-col h-screen" id="container"> <div class="flex flex-col h-3/4" id="container">
<div class="p-5" id="top-half"> <div class="p-5" id="top-half">
<h1 class="text-3xl font-bold mb-4">Web Serial Console</h1> <h1 class="text-3xl font-bold mb-4">Web Serial Console</h1>
<div class="flex flex-col md:flex-row justify-between items-start md:items-center mb-4 space-y-4 md:space-y-0"> <div class="flex flex-col md:flex-row justify-between items-start md:items-center mb-4 space-y-4 md:space-y-0">
@ -15,12 +15,9 @@
<div class="flex flex-col md:flex-row items-start md:items-center space-y-4 md:space-y-0 md:space-x-4"> <div class="flex flex-col md:flex-row items-start md:items-center space-y-4 md:space-y-0 md:space-x-4">
<label for="serial-select" class="text-sm font-medium leading-6 text-gray-900">Port:</label> <label for="serial-select" class="text-sm font-medium leading-6 text-gray-900">Port:</label>
<select id="serial-select" name="serial-select" class="font-medium bg-white block w-full md:w-auto rounded-sm border-0 py-1.5 pl-3 pr-10 text-gray-900 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-red-500 sm:text-sm sm:leading-6"> <select id="serial-select" name="serial-select" class="font-medium bg-white block w-full md:w-auto rounded-sm border-0 py-1.5 pl-3 pr-10 text-gray-900 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-red-500 sm:text-sm sm:leading-6">
<option value="">Select a device...&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</option> <option value="">Add a device...&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</option>
</select> </select>
<button id="add-port" class="ml-2 p-1 bg-gray-200 rounded-sm hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-opacity-50"> <button id="add-port" class="ml-2 p-1 bg-gray-200 rounded-sm hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-opacity-50">
<!-- <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="4">
<path stroke-linecap="square" stroke-linejoin="round" d="M12 4v16m8-8H4" />
</svg> -->
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-500" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="size-6"> <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-500" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="size-6">
<path strokeLinecap="round" strokeLinejoin="round" d="M12 4.5v15m7.5-7.5h-15" /> <path strokeLinecap="round" strokeLinejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg> </svg>
@ -43,18 +40,18 @@
<option value="">921600</option> <option value="">921600</option>
</select> </select>
</div> </div>
<button id="connect-button" class="px-4 py-1 bg-red-500 text-white rounded-sm hover:bg-red-600 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-opacity-50">Connect</button> <button id="connect-button" class="px-4 py-1 bg-emerald-500 text-white rounded-sm hover:bg-emerald-600 focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:ring-opacity-50">Connect</button>
</div> </div>
<div class="flex flex-col md:flex-row items-start md:items-center space-y-4 md:space-y-0 md:space-x-4"> <div class="flex flex-col md:flex-row items-start md:items-center space-y-4 md:space-y-0 md:space-x-4">
<label class="flex items-center space-x-2"> <label class="flex items-center space-x-2">
<input type="checkbox" id="autoscroll-checkbox" checked class="form-checkbox"> <input type="checkbox" id="autoscroll-checkbox" checked class="form-checkbox">
<span class="text-sm font-medium leading-6 text-gray-900">Autoscroll</span> <span class="text-sm font-medium leading-6 text-gray-900">Autoscroll</span>
</label> </label>
<button id="clear-button" class="px-4 py-1 bg-red-500 text-white rounded-sm hover:bg-red-600 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-opacity-50">Clear</button> <button id="clear-button" class="px-4 py-1 bg-amber-500 text-white rounded-sm hover:bg-amber-600 focus:outline-none focus:ring-2 focus:ring-amber-500 focus:ring-opacity-50">Clear</button>
</div> </div>
</div> </div>
</div> </div>
<div class="font-mono rounded-sm bg-white shadow-xl h-[calc(50vh-70px)] overflow-y-auto border border-gray-300 p-2.5 bg-gray-200 mx-5 mb-5" id="scrollable-element"></div> <div class="font-mono rounded-sm bg-white shadow-xl h-[calc(75vh-70px)] overflow-y-auto border border-gray-300 p-2.5 bg-gray-200 mx-5 mb-5" id="scrollable-element"></div>
</div> </div>
<script src="script.js"></script> <script src="script.js"></script>
</body> </body>

@ -1,43 +1,45 @@
// Globals
const scrollableElement = document.getElementById('scrollable-element'); const addDeviceMessage = `Add a device...&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`
const addPort = document.getElementById('add-port')
const autoscrollCheckbox = document.getElementById('autoscroll-checkbox'); const autoscrollCheckbox = document.getElementById('autoscroll-checkbox');
const connectButton = document.getElementById('connect-button') const baud = document.getElementById('baud');
const clearButton = document.getElementById('clear-button'); const clearButton = document.getElementById('clear-button');
const addPort = document.getElementById('add-port') const connectButton = document.getElementById('connect-button')
const refreshPorts = document.getElementById('refresh-ports'); const refreshPorts = document.getElementById('refresh-ports');
const scrollableElement = document.getElementById('scrollable-element');
const select = document.getElementById('serial-select'); const select = document.getElementById('serial-select');
const baud = document.getElementById('baud');
let autoscroll = true; let autoscroll = true;
let serialPorts = []; let serialPorts = [];
let reader; let reader;
// Event Listeners
addPort.addEventListener('click', async () => { addPort.addEventListener('click', async () => {
const port = await navigator.serial.requestPort(); const port = await navigator.serial.requestPort();
serialPorts.push(port); serialPorts.push(port);
updateSerialSelect(serialPorts); updateSerialSelect(serialPorts);
}); });
function buildPortOption(port) { autoscrollCheckbox.addEventListener('change', (e) => {
const option = document.createElement('option'); autoscroll = e.target.checked;
option.value = port; if (autoscroll) {
scrollToBottom();
try {
const info = port.getInfo();
if (info && 'usbVendorId' in info && 'usbProductId' in info) {
const { usbVendorId, usbProductId } = info;
option.text = `Device ${usbVendorId}:${usbProductId}`;
} else {
console.error('getInfo() did not return expected properties:', info);
option.text = 'Unknown Device';
}
} catch (error) {
console.error('Error retrieving port information:', error);
option.text = 'Unknown Device';
} }
});
return option; clearButton.addEventListener('click', () => {
} while (scrollableElement.firstChild) {
scrollableElement.removeChild(scrollableElement.firstChild);
}
});
connectButton.addEventListener('click', async () => {
const selectedPort = serialPorts[select.selectedIndex]
const baudRate = Math.round(baud.value)
if (selectedPort) {
await connectToSerialPort(selectedPort, baudRate);
}
});
refreshPorts.addEventListener('click', async () => { refreshPorts.addEventListener('click', async () => {
while (select.children.length > 1) { while (select.children.length > 1) {
@ -53,18 +55,27 @@ refreshPorts.addEventListener('click', async () => {
}); });
}); });
autoscrollCheckbox.addEventListener('change', (e) => { // Functions
autoscroll = e.target.checked; function buildPortOption(port) {
if (autoscroll) { const option = document.createElement('option');
scrollToBottom(); option.value = port;
}
});
clearButton.addEventListener('click', () => { try {
while (scrollableElement.firstChild) { const info = port.getInfo();
scrollableElement.removeChild(scrollableElement.firstChild); if (info && 'usbVendorId' in info && 'usbProductId' in info) {
const { usbVendorId, usbProductId } = info;
option.text = `Device ${usbVendorId}:${usbProductId}`;
} else {
console.error('getInfo() did not return expected properties:', info);
option.text = 'Unknown Device';
}
} catch (error) {
console.error('Error retrieving port information:', error);
option.text = 'Unknown Device';
} }
});
return option;
}
function addText(text) { function addText(text) {
const newText = document.createElement('p'); const newText = document.createElement('p');
@ -80,29 +91,10 @@ function scrollToBottom() {
scrollableElement.scrollTop = scrollableElement.scrollHeight; scrollableElement.scrollTop = scrollableElement.scrollHeight;
} }
async function updateSerialSelect(ports) { // Async Functions
if (ports > 0) {
select.innerHTML = '<option value="">Add a device...</option>';
} else {
select.innerHTML = '<option value="">Select a device...</option>';
}
ports.forEach(port => {
const option = buildPortOption(port)
select.appendChild(option);
});
}
connectButton.addEventListener('click', async () => {
const selectedPort = serialPorts[select.selectedIndex - 1]
const baudRate = Math.round(baud.value)
if (selectedPort) {
await connectToSerialPort(selectedPort, baudRate);
}
});
async function connectToSerialPort(port, baud) { async function connectToSerialPort(port, baud) {
await port.open({ baudRate: baud }); await port.open({ baudRate: baud });
let buffer = ''
const textDecoder = new TextDecoderStream(); const textDecoder = new TextDecoderStream();
const readableStreamClosed = port.readable.pipeTo(textDecoder.writable); const readableStreamClosed = port.readable.pipeTo(textDecoder.writable);
const reader = textDecoder.readable.getReader(); const reader = textDecoder.readable.getReader();
@ -113,7 +105,16 @@ async function connectToSerialPort(port, baud) {
if (done) { if (done) {
break; break;
} }
addText(value);
buffer += value;
while (buffer.includes('\n')) {
const newlineIndex = buffer.indexOf('\n');
const line = buffer.slice(0, newlineIndex);
buffer = buffer.slice(newlineIndex + 1);
addText(line);
}
} }
} catch (error) { } catch (error) {
console.error('Error reading data from serial port:', error); console.error('Error reading data from serial port:', error);
@ -124,7 +125,17 @@ async function connectToSerialPort(port, baud) {
} }
} }
async function updateSerialSelect(ports) {
if (ports.length < 1) {
// Simulate adding text every 2 seconds const option = document.createElement('option');
// setInterval(addText, 2000); option.text = addDeviceMessage;
select.innerHTML = ''
select.appendChild(option)
return;
}
ports.forEach(port => {
const option = buildPortOption(port)
select.appendChild(option);
});
select.removeChild(select.firstElementChild);
}