BWF Analyser: browser page and macOS app

Reads and edits BWF metadata for production sound. One source tree builds a
single self-contained page and a native Tauri app with a Rust audio engine and
WAV writer. Around 370 checks across seven test suites.

First commit of the existing state, so that from here every change can be
seen and undone.
This commit is contained in:
2026-08-17 22:50:39 +08:00
commit 2d0b7fe8b5
50 changed files with 42383 additions and 0 deletions
+274
View File
@@ -0,0 +1,274 @@
/**
* A log you can read inside the app.
*
* The app ships without developer tools, so when something goes wrong the
* usual answer — "open the console and tell me what it says" — isn't
* available. This captures everything the console would have shown, plus the
* things that turn out to matter when a control looks fine and does nothing,
* and puts it behind Cmd-Shift-L with a Copy button.
*
* Loaded before the app so it catches errors thrown during start-up.
*/
( function () {
"use strict";
var LIMIT = 800;
var lines = [];
var panel = null;
var output = null;
function stamp() {
var now = new Date();
function two( n ) { return ( n < 10 ? "0" : "" ) + n; }
return two( now.getHours() ) + ":" + two( now.getMinutes() ) + ":" +
two( now.getSeconds() ) + "." +
( "00" + now.getMilliseconds() ).slice( -3 );
}
/** Anything at all, as one short readable string. */
function describe( value ) {
if ( value === null ) { return "null"; }
if ( value === undefined ) { return "undefined"; }
if ( typeof value === "string" ) { return value; }
if ( typeof value !== "object" ) { return String( value ); }
if ( value instanceof Error ) {
return value.name + ": " + value.message +
( value.stack ? "\n " + String( value.stack ).split( "\n" ).slice( 1, 4 )
.join( "\n " ) : "" );
}
if ( value.nodeType === 1 ) { return element( value ); }
try {
var json = JSON.stringify( value );
return json && json.length > 300 ? json.slice( 0, 300 ) + "…" : String( json );
} catch ( e ) {
return Object.prototype.toString.call( value );
}
}
/** An element as you would point at it: tag, class, and its data hooks. */
function element( node ) {
if ( ! node || node.nodeType !== 1 ) { return String( node ); }
var out = node.tagName.toLowerCase();
if ( node.id ) { out += "#" + node.id; }
var cls = typeof node.className === "string" ? node.className :
( node.getAttribute && node.getAttribute( "class" ) ) || "";
if ( cls ) { out += "." + cls.trim().split( /\s+/ ).join( "." ); }
var hooks = [];
if ( node.getAttributeNames ) {
node.getAttributeNames().forEach( function ( name ) {
if ( name.indexOf( "data-bwfa" ) === 0 ) {
var v = node.getAttribute( name );
hooks.push( v ? name + "=" + v : name );
}
} );
}
if ( hooks.length ) { out += " [" + hooks.join( " " ) + "]"; }
return out;
}
function add( kind, text ) {
lines.push( stamp() + " " + kind + " " + text );
if ( lines.length > LIMIT ) { lines.shift(); }
if ( output ) { render(); }
}
// Console, kept working as well as captured.
[ "log", "info", "warn", "error" ].forEach( function ( name ) {
var original = window.console && window.console[ name ];
window.console[ name ] = function () {
var parts = Array.prototype.slice.call( arguments ).map( describe );
add( name.toUpperCase(), parts.join( " " ) );
if ( original ) { original.apply( window.console, arguments ); }
};
} );
window.addEventListener( "error", function ( e ) {
if ( e.error ) {
add( "THROWN", describe( e.error ) );
} else {
add( "THROWN", e.message + " (" + e.filename + ":" + e.lineno + ")" );
}
} );
window.addEventListener( "unhandledrejection", function ( e ) {
add( "REJECTED", describe( e.reason ) );
} );
/**
* Every click, with what was actually hit.
*
* This is the line that matters when a button looks alive and isn't: the
* thing under the pointer is not the button you aimed at, either because
* something covers it or because it has been squashed to no width at all.
*/
document.addEventListener( "click", function ( e ) {
var hit = e.target;
var button = hit && hit.closest ? hit.closest( "button" ) : null;
var note = "hit " + element( hit );
if ( button && button !== hit ) { note += " inside " + element( button ); }
if ( ! button ) { note += " (no button in the ancestry)"; }
add( "CLICK", note );
}, true );
/**
* Measures the controls, because a control can be visible and unclickable.
* For each one: its box, and whatever the browser says is on top at the
* middle of that box. Those two disagreeing is the whole diagnosis.
*/
function probe() {
var selectors = [
"[data-bwfa-mixer-open]", "[data-bwfa-spectro-open]",
"[data-bwfa-player-edit]", "[data-bwfa-player-export]",
"[data-bwfa-player-playpause]"
];
add( "PROBE", "measuring the player's controls" );
// This panel covers the bottom of the window, so measuring with it on
// screen reports the panel as the topmost thing over every control.
// Ask it to step out of the way first.
var wasShowing = panel && panel.style.display !== "none";
if ( wasShowing ) {
panel.style.display = "none";
}
selectors.forEach( function ( selector ) {
var node = document.querySelector( selector );
if ( ! node ) {
add( "PROBE", selector + " MISSING from the page" );
return;
}
var box = node.getBoundingClientRect();
var size = Math.round( box.width ) + "x" + Math.round( box.height ) +
" at " + Math.round( box.left ) + "," + Math.round( box.top );
// elementFromPoint is the whole point of this, but it does not
// exist everywhere the tests run.
var mid = document.elementFromPoint ? document.elementFromPoint(
box.left + box.width / 2, box.top + box.height / 2 ) : null;
var reaches = ! document.elementFromPoint ||
( mid && ( mid === node || ( mid.closest && mid.closest( selector ) ) ) );
add( "PROBE", selector + " " + size +
( box.width < 4 || box.height < 4 ? " SQUASHED" : "" ) +
" topmost: " + element( mid ) +
( reaches ? " reachable" : " NOT REACHABLE — clicks land elsewhere" ) );
} );
if ( wasShowing ) {
panel.style.display = "flex";
}
var modal = document.querySelector( "[data-bwfa-mixer]" );
add( "PROBE", "mixer modal in the page: " +
( modal ? "yes, hidden=" + modal.hidden + ", open class=" +
modal.classList.contains( "open" ) : "NO — markup missing" ) );
// Unhidden is not the same as visible, which is exactly the trap that
// made this look like a dead button. Report what is actually painted.
add( "PROBE", "mixer modal display: " +
( modal ? window.getComputedStyle( modal ).display : "n/a" ) );
add( "PROBE", "page built " + ( window.BWFA_BUILD || "unknown" ) );
}
function render() {
output.textContent = lines.join( "\n" );
output.scrollTop = output.scrollHeight;
}
function build() {
panel = document.createElement( "div" );
// Inline styles throughout: this has to work when the app's own
// stylesheet is the thing that's broken.
panel.setAttribute( "style", [
"position:fixed", "left:0", "right:0", "bottom:0", "height:45vh",
"z-index:2147483647", "background:#1c1c1e", "color:#e8e8ed",
"font:11px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace",
"display:flex", "flex-direction:column",
"box-shadow:0 -8px 24px rgba(0,0,0,.4)"
].join( ";" ) );
var bar = document.createElement( "div" );
bar.setAttribute( "style", [
"display:flex", "align-items:center", "gap:8px", "padding:8px 12px",
"border-bottom:1px solid #3a3a3c", "flex:none"
].join( ";" ) );
var title = document.createElement( "strong" );
title.textContent = "BWF Analyser log";
title.setAttribute( "style", "margin-right:auto;font-weight:600" );
bar.appendChild( title );
function chip( label, onClick ) {
var b = document.createElement( "button" );
b.type = "button";
b.textContent = label;
b.setAttribute( "style", [
"font:inherit", "padding:3px 10px", "border-radius:6px",
"border:1px solid #5a5a5e", "background:#2c2c2e", "color:inherit",
"cursor:pointer"
].join( ";" ) );
b.addEventListener( "click", onClick );
bar.appendChild( b );
return b;
}
chip( "Copy", function () {
var text = lines.join( "\n" );
if ( navigator.clipboard && navigator.clipboard.writeText ) {
navigator.clipboard.writeText( text ).then( function () {
title.textContent = "BWF Analyser log — copied";
}, function () {
fallbackCopy( text, title );
} );
} else {
fallbackCopy( text, title );
}
} );
chip( "Measure controls", function () { probe(); } );
chip( "Clear", function () { lines = []; render(); } );
chip( "Close", function () { toggle( false ); } );
output = document.createElement( "pre" );
output.setAttribute( "style", [
"margin:0", "padding:10px 12px", "overflow:auto", "flex:1",
"white-space:pre-wrap", "word-break:break-word"
].join( ";" ) );
panel.appendChild( bar );
panel.appendChild( output );
document.body.appendChild( panel );
}
function fallbackCopy( text, title ) {
var area = document.createElement( "textarea" );
area.value = text;
document.body.appendChild( area );
area.select();
try {
document.execCommand( "copy" );
title.textContent = "BWF Analyser log — copied";
} catch ( e ) {
title.textContent = "BWF Analyser log — select the text and copy it";
}
area.remove();
}
function toggle( wanted ) {
if ( ! panel ) { build(); }
var show = wanted === undefined ? panel.style.display === "none" : wanted;
panel.style.display = show ? "flex" : "none";
if ( show ) {
probe();
render();
}
}
document.addEventListener( "keydown", function ( e ) {
if ( ( e.metaKey || e.ctrlKey ) && e.shiftKey &&
String( e.key ).toLowerCase() === "l" ) {
e.preventDefault();
toggle();
}
} );
window.BWFA_DIAG = {
lines: function () { return lines.slice(); },
open: function () { toggle( true ); },
close: function () { toggle( false ); },
probe: probe,
describe: element
};
}() );