Module cleanup and tests
CI / ci (push) Failing after 7s

This commit is contained in:
2026-05-24 23:13:45 +02:00
parent 63d83e999a
commit 5dc9b3b8d4
72 changed files with 2545 additions and 1220 deletions
+82 -13
View File
@@ -47,12 +47,12 @@ export class DirectorsBoard extends _AppBase {
id: 'scrying-pool-directors-board',
classes: ['scrying-pool', 'directors-board'],
window: { title: "Director's Board", resizable: true },
position: { width: 400, height: 300 },
position: { width: 420, height: 480 },
};
static PARTS = {
board: {
template: 'modules/video-view-manager/templates/directors-board.hbs',
template: 'modules/scrying-pool/templates/directors-board.hbs',
},
};
@@ -116,7 +116,7 @@ export class DirectorsBoard extends _AppBase {
/** Loads saved window position from GM user flag. */
_loadPosition() {
try {
const saved = game.user?.getFlag('video-view-manager', 'directorsBoardState');
const saved = game.user?.getFlag('scrying-pool', 'directorsBoardState');
if (saved?.open === true && saved.left != null && saved.top != null) {
// Ensure options.position exists and is mutable
if (this.options?.position) {
@@ -124,8 +124,8 @@ export class DirectorsBoard extends _AppBase {
Object.assign(this.options.position, {
left: saved.left,
top: saved.top,
width: saved.width ?? 400,
height: saved.height ?? 300,
width: saved.width ?? 420,
height: saved.height ?? 480,
});
}
}
@@ -374,6 +374,8 @@ export class DirectorsBoard extends _AppBase {
autoApplyPresetName: autoApplyConfig.presetName,
autoApplyPreDelay: autoApplyConfig.preDelay,
presets: this._scenePresetManager?.list?.() ?? [],
// A/V mode — reflects current world AV state (0 = disabled, 3 = audio+video)
avModeEnabled: (game.webrtc?.settings?.world?.mode ?? 0) !== 0,
};
}
@@ -416,6 +418,9 @@ export class DirectorsBoard extends _AppBase {
case 'import-presets': this._onImportPresets(); break;
// Story 3.2: Scene auto-apply panel toggle
case 'toggle-preset-panel': this._togglePresetPanel(); break;
case 'toggle-av-mode': this._onToggleAVMode(); break;
case 'open-av-config': this._onOpenAVConfig(); break;
case 'close': this.close(); break;
}
};
this._focusinHandler = (e) => {
@@ -428,6 +433,28 @@ export class DirectorsBoard extends _AppBase {
root.addEventListener('click', this._clickHandler);
root.addEventListener('focusin', this._focusinHandler);
root.addEventListener('keydown', this._keydownHandler);
// Drag grip — custom drag (ApplicationV2 header is hidden)
const grip = root.querySelector('[data-action="drag-grip"]');
if (grip) {
grip.addEventListener('mousedown', e => {
if (e.button !== 0) return;
e.preventDefault();
const startX = e.clientX;
const startY = e.clientY;
const { left: startLeft, top: startTop } = this.position;
const onMove = mv => this.setPosition({
left: startLeft + (mv.clientX - startX),
top: startTop + (mv.clientY - startY),
});
const onUp = () => {
document.removeEventListener('mousemove', onMove);
document.removeEventListener('mouseup', onUp);
};
document.addEventListener('mousemove', onMove);
document.addEventListener('mouseup', onUp);
});
}
// Story 3.2: Append ScenePresetPanel to DOM and refresh
this._appendPresetPanel(root);
@@ -542,8 +569,8 @@ export class DirectorsBoard extends _AppBase {
const localize = (key) => game.i18n?.localize(key) ?? key;
const getBinding = (actionKey) => {
// Check both namespaces due to migration from video-view-manager to scrying-pool
const namespaces = ['scrying-pool', 'video-view-manager'];
// Check both namespaces due to migration from scrying-pool to scrying-pool
const namespaces = ['scrying-pool', 'scrying-pool'];
for (const ns of namespaces) {
const bindings = game.keybindings?.bindings?.get(`${ns}.${actionKey}`);
if (bindings?.[0]) {
@@ -556,10 +583,10 @@ export class DirectorsBoard extends _AppBase {
};
const shortcuts = [
{ label: localize('video-view-manager.directorsBoard.shortcuts.openBoard'), binding: getBinding('openDirectorsBoard') ?? 'Ctrl+Shift+V' },
{ label: localize('video-view-manager.directorsBoard.shortcuts.showAll'), binding: getBinding('showAll') ?? 'Ctrl+Shift+S' },
{ label: localize('video-view-manager.directorsBoard.shortcuts.hideAll'), binding: getBinding('hideAll') ?? 'Ctrl+Shift+H' },
{ label: localize('video-view-manager.directorsBoard.shortcuts.spotlight'), binding: getBinding('spotlightParticipant') ?? 'Ctrl+Shift+P' },
{ label: localize('scrying-pool.directorsBoard.shortcuts.openBoard'), binding: getBinding('openDirectorsBoard') ?? 'Ctrl+Shift+V' },
{ label: localize('scrying-pool.directorsBoard.shortcuts.showAll'), binding: getBinding('showAll') ?? 'Ctrl+Shift+S' },
{ label: localize('scrying-pool.directorsBoard.shortcuts.hideAll'), binding: getBinding('hideAll') ?? 'Ctrl+Shift+H' },
{ label: localize('scrying-pool.directorsBoard.shortcuts.spotlight'), binding: getBinding('spotlightParticipant') ?? 'Ctrl+Shift+P' },
];
// Escape HTML to prevent injection via localised strings or keybinding labels
@@ -570,7 +597,7 @@ export class DirectorsBoard extends _AppBase {
if (typeof Dialog !== 'undefined') {
new Dialog({
title: localize('video-view-manager.directorsBoard.shortcuts.title'),
title: localize('scrying-pool.directorsBoard.shortcuts.title'),
content,
buttons: { close: { label: 'Close' } },
default: 'close',
@@ -592,6 +619,48 @@ export class DirectorsBoard extends _AppBase {
}
}
/**
* Toggles Foundry A/V mode between AUDIO_VIDEO (3) and DISABLED (0).
* The module is the single point of control for A/V activation — Foundry's
* native AVConfig dialog is redirected here by initScryingPoolCameraViews.
*
* Uses reestablish() rather than explicit connect()/disconnect() to avoid
* racing with Foundry's internal mode-change listeners (AVMaster hooks into
* the settings change event itself).
*/
async _onToggleAVMode() {
if (!game.webrtc) {
console.warn('[ScryingPool] DirectorsBoard: game.webrtc not available');
return;
}
const currentMode = game.webrtc.settings?.world?.mode ?? 0;
// AV_MODES: DISABLED=0, AUDIO=1, VIDEO=2, AUDIO_VIDEO=3
const newMode = currentMode === 0 ? 3 : 0;
try {
await game.webrtc.settings.set('world', 'mode', newMode);
// reestablish() tears down and rebuilds the WebRTC connection honouring the
// new mode — same approach used by Foundry's own AVConfig save handler.
await game.webrtc.reestablish?.();
} catch (err) {
console.error('[ScryingPool] DirectorsBoard: failed to toggle A/V mode:', err);
}
if (this.rendered) this.render({ force: true });
}
/**
* Opens Foundry's native AVConfig dialog for signaling server configuration.
* This is separate from the A/V mode toggle — AVConfig is where you set up the
* LiveKit/WebRTC server address, username, password, etc.
* The module controls A/V mode (on/off); Foundry's dialog handles infrastructure.
*/
_onOpenAVConfig() {
if (!game.webrtc?.config) {
console.warn('[ScryingPool] DirectorsBoard: game.webrtc.config not available');
return;
}
game.webrtc.config.render({ force: true });
}
/**
* Opens the PresetSaveDialog for saving the current visibility matrix as a preset.
*/
@@ -749,7 +818,7 @@ export class DirectorsBoard extends _AppBase {
*/
_savePosition(state) {
try {
game.user?.setFlag('video-view-manager', 'directorsBoardState', state);
game.user?.setFlag('scrying-pool', 'directorsBoardState', state);
} catch (err) {
console.error('[ScryingPool] Failed to save directors board position:', err);
}