Story 5.2: Video Widget Width Customization

Implements configurable video widget widths for small and large dock layouts:
- Add widgetWidthSm and widgetWidthMd world settings (defaults: 83px, 150px)
- Add width dropdown selectors in Director's Board UI
- Apply width settings to participant avatars via CSS custom properties
- Update strip width/height calculations to use custom widths
- Add localization strings for widget width settings
- Add CSS styling for widget width dropdown controls
- Fix Handlebars template syntax for select element selected values

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
2026-05-25 12:43:06 +02:00
parent 748c7d7f85
commit 7a0d935239
15 changed files with 1967 additions and 19 deletions
+39 -11
View File
@@ -215,6 +215,12 @@ export class ScryingPoolStrip extends _AppBase {
const isExpanded = dockLayout === 'vertical-md';
const showName = dockLayout.endsWith('-md');
// Story 5.2: Video widget width customization
const widgetWidthSm = this._adapter.settings?.get?.('widgetWidthSm') ?? '80';
const widgetWidthMd = this._adapter.settings?.get?.('widgetWidthMd') ?? '120';
const isLarge = effectiveSize === 'md';
const effectiveWidth = isLarge ? widgetWidthMd : widgetWidthSm;
const isGM = this._adapter.users.isGM?.() ?? false;
return {
@@ -226,6 +232,8 @@ export class ScryingPoolStrip extends _AppBase {
showFirstOpenTip: showFirstOpenTip && isGM,
hasStreamAccess,
isGM,
// Story 5.2: Video widget width customization
widgetWidth: effectiveWidth,
};
}
@@ -317,6 +325,18 @@ export class ScryingPoolStrip extends _AppBase {
}
}
/**
* Gets the widget width for a given layout size.
* Story 5.2: Video widget width customization
* @param {'sm'|'md'} size - The size variant
* @returns {number} Width in pixels.
*/
_getWidgetWidth(size) {
const value = this._adapter.settings?.get?.(`widgetWidth${size === 'md' ? 'Md' : 'Sm'}`);
// Defaults match settings defaults: 83px for sm, 150px for md
return typeof value === 'string' ? parseInt(value, 10) : (size === 'md' ? 150 : 83);
}
/**
* Computes the strip window width for a given dock layout and participant count.
* @param {string} layout - The dock layout key.
@@ -329,14 +349,18 @@ export class ScryingPoolStrip extends _AppBase {
const maxCols = 4;
const rowWidth = (tileSize, cols) =>
cols * tileSize + Math.max(0, cols - 1) * GAP + PAD + BORDER_W;
const smWidth = this._getWidgetWidth('sm');
const mdWidth = this._getWidgetWidth('md');
switch (layout) {
case 'vertical-sm': return 85; // 83px tile + 2px border
case 'vertical-md': return 242; // 240px strip + 2px border
case 'horizontal-sm': return rowWidth(83, Math.min(maxCols, n));
case 'horizontal-md': return rowWidth(150, Math.min(maxCols, n));
case 'mosaic-sm': return rowWidth(83, Math.min(maxCols, Math.ceil(Math.sqrt(n))));
case 'mosaic-md': return rowWidth(150, Math.min(maxCols, Math.ceil(Math.sqrt(n))));
default: return 85;
case 'vertical-sm': return smWidth + 2; // widget + 2px border
case 'vertical-md': return 242; // 240px strip + 2px border (expanded view has fixed width)
case 'horizontal-sm': return rowWidth(smWidth, Math.min(maxCols, n));
case 'horizontal-md': return rowWidth(mdWidth, Math.min(maxCols, n));
case 'mosaic-sm': return rowWidth(smWidth, Math.min(maxCols, Math.ceil(Math.sqrt(n))));
case 'mosaic-md': return rowWidth(mdWidth, Math.min(maxCols, Math.ceil(Math.sqrt(n))));
default: return smWidth + 2;
}
}
@@ -361,15 +385,19 @@ export class ScryingPoolStrip extends _AppBase {
const BORDER_H = 2; // 1px border top + 1px border bottom on app element
const GAP = 4, TILE_PAD = 8; // 4px padding each side in .sp-strip__participants
const maxCols = 4;
const smWidth = this._getWidgetWidth('sm');
const mdWidth = this._getWidgetWidth('md');
const gridHeight = (tileSize, cols) => {
const rows = Math.ceil(n / cols);
return rows * tileSize + Math.max(0, rows - 1) * GAP + TILE_PAD;
};
switch (layout) {
case 'horizontal-sm': return CHROME + gridHeight(83, Math.min(maxCols, n)) + BORDER_H;
case 'horizontal-md': return CHROME + gridHeight(150, Math.min(maxCols, n)) + BORDER_H;
case 'mosaic-sm': return CHROME + gridHeight(83, Math.min(maxCols, Math.ceil(Math.sqrt(n)))) + BORDER_H;
case 'mosaic-md': return CHROME + gridHeight(150, Math.min(maxCols, Math.ceil(Math.sqrt(n)))) + BORDER_H;
case 'horizontal-sm': return CHROME + gridHeight(smWidth, Math.min(maxCols, n)) + BORDER_H;
case 'horizontal-md': return CHROME + gridHeight(mdWidth, Math.min(maxCols, n)) + BORDER_H;
case 'mosaic-sm': return CHROME + gridHeight(smWidth, Math.min(maxCols, Math.ceil(Math.sqrt(n)))) + BORDER_H;
case 'mosaic-md': return CHROME + gridHeight(mdWidth, Math.min(maxCols, Math.ceil(Math.sqrt(n)))) + BORDER_H;
default: return 'auto';
}
}