chore: add JS/ESLint toolchain, refactor onclick to event delegation

- Add ESLint config for web/ui (flat config, golangci-lint-compatible)
- Add js-lint task to mise.toml (npm run lint in web/ui)
- Add ESLint check to check-toolchain.sh
- Add .golangci.yml for golangci-lint v2 with correct exclusions
- Refactor app.js: replace inline onclick with data-action + event delegation
- Add null checks and HTTP status checks to browseWavs
- Add web/ui/node_modules/ to .gitignore
This commit is contained in:
2026-06-22 00:10:49 -07:00
parent f716902580
commit 4f03524668
8 changed files with 1201 additions and 4 deletions
+3
View File
@@ -25,5 +25,8 @@ wavs
.DS_Store .DS_Store
Thumbs.db Thumbs.db
# JS dependencies
web/ui/node_modules/
# Temp # Temp
/tmp/ /tmp/
+14
View File
@@ -0,0 +1,14 @@
version: "2"
linters:
default: none
enable:
- errcheck
- govet
- staticcheck
- unused
exclusions:
paths:
- web/ui
- electron
- third_party/akaiutil/
+2 -1
View File
@@ -11,7 +11,8 @@ build = "go build -buildvcs=false -o fetch ."
akaiutil = "make -C third_party/akaiutil" akaiutil = "make -C third_party/akaiutil"
all = { run = "go build -buildvcs=false -o fetch . && make -C third_party/akaiutil" } all = { run = "go build -buildvcs=false -o fetch . && make -C third_party/akaiutil" }
serve = { run = "go build -buildvcs=false -o fetch . && ./fetch serve" } serve = { run = "go build -buildvcs=false -o fetch . && ./fetch serve" }
lint = "go vet ./..." lint = "golangci-lint run"
js-lint = "cd web/ui && npm run lint"
electron-deps = "cd electron && npm install" electron-deps = "cd electron && npm install"
electron-dev = { run = "go build -buildvcs=false -o fetch . && cd electron && npx electron ." } electron-dev = { run = "go build -buildvcs=false -o fetch . && cd electron && npx electron ." }
electron-build = { run = "go build -buildvcs=false -o fetch . && make -C third_party/akaiutil && cd electron && npx electron-builder --dir" } electron-build = { run = "go build -buildvcs=false -o fetch . && make -C third_party/akaiutil && cd electron && npx electron-builder --dir" }
+11
View File
@@ -123,5 +123,16 @@ else
fi fi
fi fi
echo ""
echo "=== JS Linting (ESLint) ==="
if [ -f web/ui/node_modules/.bin/eslint ]; then
ok "ESLint installed at web/ui/node_modules/.bin/eslint"
eslint_ver=$(cd web/ui && ./node_modules/.bin/eslint --version 2>/dev/null)
ok "ESLint: $eslint_ver"
else
warn "ESLint not installed (needed for web UI linting)"
echo " Install: cd web/ui && npm install"
fi
echo "" echo ""
echo "=== Core toolchain check complete ===" echo "=== Core toolchain check complete ==="
+17 -3
View File
@@ -46,8 +46,8 @@ function renderResults(items) {
<span>${r.downloads.toLocaleString()} downloads</span> <span>${r.downloads.toLocaleString()} downloads</span>
</div> </div>
<div class="actions"> <div class="actions">
<button class="btn-dl" onclick="downloadItem('${esc(r.identifier)}')">Download ISOs</button> <button class="btn-dl" data-action="download" data-identifier="${esc(r.identifier)}">Download ISOs</button>
<button class="btn-list" onclick="listItem('${esc(r.identifier)}')">List Files</button> <button class="btn-list" data-action="list" data-identifier="${esc(r.identifier)}">List Files</button>
</div> </div>
<div id="list-${sanitize(r.identifier)}" class="file-list"></div> <div id="list-${sanitize(r.identifier)}" class="file-list"></div>
</div> </div>
@@ -218,7 +218,7 @@ async function browseWavs() {
list.innerHTML = data.map(f => ` list.innerHTML = data.map(f => `
<div class="wav-item" data-file="${esc(f)}"> <div class="wav-item" data-file="${esc(f)}">
<span class="wav-name" title="${esc(f)}">${esc(f)}</span> <span class="wav-name" title="${esc(f)}">${esc(f)}</span>
<button class="wav-play-btn" onclick="playWav('${esc(f)}')">Play</button> <button class="wav-play-btn" data-action="play" data-wav="${esc(f)}">Play</button>
</div> </div>
`).join(''); `).join('');
} catch (e) { } catch (e) {
@@ -332,3 +332,17 @@ function formatBytes(bytes) {
// Initial load // Initial load
doSearch(); doSearch();
// Event delegation for dynamically generated buttons
document.addEventListener('click', function(e) {
const btn = e.target.closest('[data-action]');
if (!btn) return;
const action = btn.dataset.action;
if (action === 'download') {
downloadItem(btn.dataset.identifier);
} else if (action === 'list') {
listItem(btn.dataset.identifier);
} else if (action === 'play') {
playWav(btn.dataset.wav);
}
});
+45
View File
@@ -0,0 +1,45 @@
const globals = require('globals');
module.exports = [
{
files: ['**/*.js'],
languageOptions: {
ecmaVersion: 2022,
sourceType: 'script',
globals: {
...globals.browser,
AudioContext: 'readonly',
requestAnimationFrame: 'readonly',
cancelAnimationFrame: 'readonly',
fetch: 'readonly',
EventSource: 'readonly',
document: 'readonly',
console: 'readonly',
setTimeout: 'readonly',
Math: 'readonly',
JSON: 'readonly',
location: 'readonly',
encodeURIComponent: 'readonly',
decodeURIComponent: 'readonly',
parseInt: 'readonly',
Number: 'readonly',
Promise: 'readonly',
Object: 'readonly',
Array: 'readonly',
Map: 'readonly',
process: 'readonly',
__dirname: 'readonly',
require: 'readonly',
module: 'readonly',
exports: 'readonly'
}
},
rules: {
"no-unused-vars": ["warn", { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }],
"no-redeclare": "warn",
"no-unreachable": "warn",
"eqeqeq": ["error", "always"],
"prefer-const": "warn"
}
}
];
+1098
View File
File diff suppressed because it is too large Load Diff
+11
View File
@@ -0,0 +1,11 @@
{
"name": "akai-utils-web-ui",
"version": "1.0.0",
"description": "Web UI for AKAI Utils",
"scripts": {
"lint": "eslint *.js"
},
"devDependencies": {
"eslint": "^9.0.0"
}
}