Add settings panel with strip reopen button and AV disabled notification
CI / ci (push) Successful in 46s

Features:
- Added ScryingPoolSettings application with reopen/close strip buttons
- Registered settings menu in module settings (GM only)
- Added template for settings panel with styled buttons
- Added translations for settings UI
- Added info notification when AV is not available

Files:
- src/ui/gm/ScryingPoolSettings.js: New settings application
- templates/settings.hbs: Settings panel template
- lang/en.json: Added translations
- module.js: Registered settings menu and AV notification

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
2026-05-25 15:57:58 +02:00
parent 3c8ebaf82a
commit 6bbde5c1cf
4 changed files with 151 additions and 2 deletions
+8 -2
View File
@@ -27,7 +27,8 @@
"gmShowed": "GM showed {name}'s camera",
"personalHidden": "GM has hidden your camera. Your portrait is shown to other Participants.",
"personalShowed": "Your camera is now visible to the table.",
"avConfigGMOnly": "A/V settings are managed by the GM."
"avConfigGMOnly": "A/V settings are managed by the GM.",
"AVDisabled": "Audio/Video is not available in this FoundryVTT instance. Scrying Pool camera control features are disabled."
},
"settings": {
"showGMSelfFeed": {
@@ -205,7 +206,12 @@
"PlayerPrivacyPanelHint": "Opt in or out of Reaction Cam and other automation features",
"GMPlayerPrivacySelector": "View Player Privacy Settings",
"GMPlayerPrivacySelectorLabel": "View and manage player privacy consent settings",
"GMPlayerPrivacySelectorHint": "Select a player to view their automation opt-in preferences (read-only)"
"GMPlayerPrivacySelectorHint": "Select a player to view their automation opt-in preferences (read-only)",
"Title": "Scrying Pool Settings",
"Hint": "Configure Scrying Pool module behavior",
"StripStatus": "Strip Status",
"ReopenStrip": "Open Strip",
"CloseStrip": "Close Strip"
}
}
}
+21
View File
@@ -35,6 +35,12 @@ import { ConfirmationBar } from './src/ui/gm/ConfirmationBar.js';
import { PlayerPrivacyPanelMenu, initPlayerPrivacyPanelMenu } from './src/ui/player/PlayerPrivacyPanelMenu.js';
import { initGMPlayerPrivacySelector } from './src/ui/gm/GMPlayerPrivacySelector.js';
import { ScryingPoolCameraViews, initScryingPoolCameraViews } from './src/ui/shared/ScryingPoolCameraViews.js';
import { ScryingPoolSettings } from './src/ui/gm/ScryingPoolSettings.js';
// Factory function to create ScryingPoolSettings with roleRenderer dependency
function initScryingPoolSettings(roleRendererRef) {
return () => new ScryingPoolSettings(roleRendererRef);
}
import { SOCKET_EVENTS } from './src/contracts/socket-message.js';
// Module-level references — constructed in init hook, used across hooks
@@ -262,6 +268,10 @@ Hooks.once("ready", () => {
} else if (outcome === 'unsupported') {
adapter.webrtc = null;
console.log('[ScryingPool] WebRTC not available - AV features disabled');
// Show info message to GM about AV being disabled
if (game.user?.isGM) {
ui.notifications?.info(game.i18n.localize('SCRYING_POOL.Notifications.AVDisabled'));
}
} else {
// Legacy: track-disable or css-fallback (deprecated)
adapter.webrtc = FoundryAdapter.buildWebRTCSurface(game.webrtc);
@@ -403,6 +413,17 @@ Hooks.once("ready", () => {
type: PlayerPrivacyPanelMenu,
restricted: false,
});
// Story 5.3: Register ScryingPoolSettings in module settings
// Provides button to reopen the strip
game.settings.registerMenu('scrying-pool', 'stripSettings', {
name: 'SCRYING_POOL.Settings.Title',
label: 'SCRYING_POOL.Settings.Title',
hint: 'SCRYING_POOL.Settings.Hint',
icon: 'fa-solid fa-cog',
type: initScryingPoolSettings(roleRenderer),
restricted: true, // GM only
});
} catch (err) {
console.error('[ScryingPool] Module initialization failed:', err);
throw err; // Re-throw to prevent module from loading in broken state
+69
View File
@@ -0,0 +1,69 @@
// @ts-nocheck
/**
* Scrying Pool Settings Application
* Provides a settings panel with a button to reopen the strip
*/
export class ScryingPoolSettings extends foundry.applications.api.ApplicationV2 {
static DEFAULT_OPTIONS = {
id: 'scrying-pool-settings',
classes: ['scrying-pool-settings'],
window: { title: 'Scrying Pool Settings', resizable: true },
position: { width: 400, height: 200 },
};
static PARTS = {
form: {
template: 'modules/scrying-pool/templates/settings.hbs',
},
};
/**
* @param {object} options - Application options
* @param {object} roleRenderer - The role renderer instance to access openStrip/closeStrip
*/
constructor(roleRenderer, options = {}) {
super(options);
this._roleRenderer = roleRenderer;
}
/** @inheritdoc */
async _prepareContext(options) {
return {
hasStrip: this._roleRenderer?._strip?.rendered ?? false,
};
}
/** @inheritdoc */
async _onRender(context, options) {
super._onRender(context, options);
// Add click handler for reopen button
const reopenBtn = this.element.querySelector('[data-action="reopen-strip"]');
if (reopenBtn) {
reopenBtn.addEventListener('click', () => {
this._roleRenderer?.openStrip();
this.close();
});
}
// Add click handler for close button
const closeBtn = this.element.querySelector('[data-action="close-strip"]');
if (closeBtn) {
closeBtn.addEventListener('click', () => {
this._roleRenderer?.closeStrip();
this.close();
});
}
}
/**
* Toggle the strip open/closed
*/
_toggleStrip() {
if (this._roleRenderer?._strip?.rendered) {
this._roleRenderer.closeStrip();
} else {
this._roleRenderer?.openStrip();
}
}
}
+53
View File
@@ -0,0 +1,53 @@
<form class="scrying-pool-settings-form">
<div class="form-group">
<h2>{{localize "SCRYING_POOL.Settings.Title"}}</h2>
<p class="hint">{{localize "SCRYING_POOL.Settings.Hint"}}</p>
</div>
<div class="form-group">
<label>{{localize "SCRYING_POOL.Settings.StripStatus"}}</label>
{{#if hasStrip}}
<button type="button" data-action="close-strip" class="scrying-pool-btn scrying-pool-btn-close">
<i class="fa-solid fa-eye-slash"></i> {{localize "SCRYING_POOL.Settings.CloseStrip"}}
</button>
{{else}}
<button type="button" data-action="reopen-strip" class="scrying-pool-btn scrying-pool-btn-reopen">
<i class="fa-solid fa-eye"></i> {{localize "SCRYING_POOL.Settings.ReopenStrip"}}
</button>
{{/if}}
</div>
<style>
.scrying-pool-settings-form {
padding: 15px;
}
.scrying-pool-settings-form .form-group {
margin-bottom: 15px;
}
.scrying-pool-settings-form .hint {
color: var(--text-muted);
font-size: 0.9em;
}
.scrying-pool-btn {
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
display: inline-flex;
align-items: center;
gap: 8px;
}
.scrying-pool-btn-reopen {
background-color: var(--success);
color: white;
}
.scrying-pool-btn-close {
background-color: var(--danger);
color: white;
}
.scrying-pool-btn:hover {
opacity: 0.9;
}
</style>
</form>