+82
-13
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user