Phase 8: session tooling — handouts to players
- GM "📣 Handout" composer in the header (title + body + optional image) sets uiStore.activeHandout; a clear button hides it. - snapshot gains an optional handout {title, body, imageId}; buildSnapshot streams the handout image over the existing image channel (id "handout"). - Player view (local + networked) shows the handout as a prominent card above the boards. resizeToMax() keeps handout images compact. - (Live scene/map switching already works via "Show to players".) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,26 @@ function loadImage(src: string): Promise<HTMLImageElement> {
|
||||
});
|
||||
}
|
||||
|
||||
/** Downscale an image to fit within maxDim (preserving aspect), as a JPEG data URL. */
|
||||
export async function resizeToMax(src: string, maxDim = 1024, quality = 0.85): Promise<string> {
|
||||
try {
|
||||
const img = await loadImage(src);
|
||||
const scale = Math.min(1, maxDim / Math.max(img.naturalWidth, img.naturalHeight));
|
||||
const w = Math.max(1, Math.round(img.naturalWidth * scale));
|
||||
const h = Math.max(1, Math.round(img.naturalHeight * scale));
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = w; canvas.height = h;
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (!ctx) return src;
|
||||
ctx.imageSmoothingEnabled = true;
|
||||
ctx.imageSmoothingQuality = 'high';
|
||||
ctx.drawImage(img, 0, 0, w, h);
|
||||
return canvas.toDataURL('image/jpeg', quality);
|
||||
} catch {
|
||||
return src;
|
||||
}
|
||||
}
|
||||
|
||||
/** Natural pixel size of an image data URL. */
|
||||
export function imageSize(src: string): Promise<{ w: number; h: number }> {
|
||||
return loadImage(src).then((img) => ({ w: img.naturalWidth, h: img.naturalHeight }));
|
||||
|
||||
Reference in New Issue
Block a user