Presentation share link on a frameless board silently opens a read-only session instead of a 'nothing presented' notice #197

Open
opened 2026-05-18 10:20:59 +00:00 by AhmedHanafy725 · 3 comments
Member

Summary

Opening a presentation share link (/board/{id}/view?present=1) on a board that has no frames drops the visitor into a normal read-only session with no explanation, instead of telling them there is nothing being presented.

Confirmed root cause

crates/hero_whiteboard_admin/static/web/js/whiteboard/frames.js startPresentation() (~lines 130-139) handles the empty case:

getFrames();
if (frames.length === 0) {
    if (typeof window.showNoFramesNotice === 'function') {
        window.showNoFramesNotice();
    }
    return;
}

window.showNoFramesNotice is defined only in the editor template crates/hero_whiteboard_admin/templates/web/board.html (~line 546). The presentation share link points at the read-only viewer route /board/{id}/view, which renders crates/hero_whiteboard_admin/templates/web/board_view.html (and the shared-token viewer template). That template does not define window.showNoFramesNotice, so the typeof ... === 'function' check is false, startPresentation() silently returns, and the visitor sees an ordinary read-only board with no indication that a presentation was expected but there is nothing to present.

app.js (~304-308) triggers this on load when ?present=1 is present.

Expected

When a visitor opens a presentation link for a board with no frames, they should see a clear, themed in-page notice (e.g. "Nothing is being presented — this board has no slides") instead of silently landing in a plain read-only session.

Requirements

  • The no-frames notice must work on the read-only viewer / presentation route (board_view.html and the shared-token viewer template), not only in the editor.
  • Prefer a single shared notice implementation rather than duplicating the helper per template (e.g. a small JS helper used by both, or move the notice into a script all the relevant templates already load), so editor and viewer behave identically and stay in sync.
  • The notice should be dismissible/clear and styled with the existing theme variables, consistent with the editor's existing notice.
  • No regression to the editor path (board.html) which already shows the notice.
  • No server or schema change expected (purely a client/template gap). Confirm during planning.

Notes

  • Check every template that can be reached with ?present=1: board_view.html, the shared-token viewer template used by the /s/{token} presentation flow, and board.html. They must all expose the same notice.
  • Deploy reminder: assets embed at compile time via rust-embed; after editing JS/templates, touch crates/hero_whiteboard_admin/src/assets.rs before cargo build --release -p hero_whiteboard_admin, and verify the served asset changed before testing.
## Summary Opening a presentation share link (`/board/{id}/view?present=1`) on a board that has no frames drops the visitor into a normal read-only session with no explanation, instead of telling them there is nothing being presented. ## Confirmed root cause `crates/hero_whiteboard_admin/static/web/js/whiteboard/frames.js` `startPresentation()` (~lines 130-139) handles the empty case: ```js getFrames(); if (frames.length === 0) { if (typeof window.showNoFramesNotice === 'function') { window.showNoFramesNotice(); } return; } ``` `window.showNoFramesNotice` is defined **only** in the editor template `crates/hero_whiteboard_admin/templates/web/board.html` (~line 546). The presentation share link points at the read-only viewer route `/board/{id}/view`, which renders `crates/hero_whiteboard_admin/templates/web/board_view.html` (and the shared-token viewer template). That template does **not** define `window.showNoFramesNotice`, so the `typeof ... === 'function'` check is false, `startPresentation()` silently returns, and the visitor sees an ordinary read-only board with no indication that a presentation was expected but there is nothing to present. `app.js` (~304-308) triggers this on load when `?present=1` is present. ## Expected When a visitor opens a presentation link for a board with no frames, they should see a clear, themed in-page notice (e.g. "Nothing is being presented — this board has no slides") instead of silently landing in a plain read-only session. ## Requirements - The no-frames notice must work on the read-only viewer / presentation route (`board_view.html` and the shared-token viewer template), not only in the editor. - Prefer a single shared notice implementation rather than duplicating the helper per template (e.g. a small JS helper used by both, or move the notice into a script all the relevant templates already load), so editor and viewer behave identically and stay in sync. - The notice should be dismissible/clear and styled with the existing theme variables, consistent with the editor's existing notice. - No regression to the editor path (board.html) which already shows the notice. - No server or schema change expected (purely a client/template gap). Confirm during planning. ## Notes - Check every template that can be reached with `?present=1`: `board_view.html`, the shared-token viewer template used by the `/s/{token}` presentation flow, and `board.html`. They must all expose the same notice. - Deploy reminder: assets embed at compile time via rust-embed; after editing JS/templates, `touch crates/hero_whiteboard_admin/src/assets.rs` before `cargo build --release -p hero_whiteboard_admin`, and verify the served asset changed before testing.
Author
Member

Implementation Spec for Issue #197

Objective

A visitor opening a presentation share link (/board/{id}/view?present=1, or /s/{token}?present=1 resolving to the viewer) for a board with no frames must see a clear, themed, dismissible in-page notice instead of silently landing in a plain read-only session. Single shared notice implementation across editor, read-only viewer, and shared-token viewer. No server/schema change.

Requirements

  • No-frames notice appears on the read-only viewer route, not only the editor.
  • One single source of the notice helper + DOM/CSS (no per-template duplication).
  • Themed via existing CSS variables (--wb-surface/--wb-border/--wb-text).
  • Dismissible/auto-clearing; page not left stuck (read-only board still visible/scrollable after dismissal; wb-presenting is never applied in the no-frames path).
  • Works for ?present=1 auto-start and a late remote-presentation join with zero frames.
  • JS + template only; no Rust/route/schema change.

Files to Modify/Create

  • crates/hero_whiteboard_admin/static/web/js/whiteboard/frames.js - WhiteboardFrames owns a self-contained themed showNoFramesNotice() (builds/injects its own DOM + inline themed styles); called from the no-frames branch instead of depending on a template-defined window.showNoFramesNotice.
  • crates/hero_whiteboard_admin/templates/web/board.html - remove the now-redundant window.showNoFramesNotice, the #frames-empty-toast DOM node, and its CSS block.
  • crates/hero_whiteboard_admin/src/assets.rs - no code change; touch only to force rust-embed re-embed.

No changes to board_view.html, base_web.html, sync.js, or app.js — frames.js is loaded by every presentation-capable template (board.html:152, board_view.html:152) and startPresentation() is the only entry point.

Implementation Plan

Step 1: Add a self-contained themed notice to WhiteboardFrames

Files: crates/hero_whiteboard_admin/static/web/js/whiteboard/frames.js

  • Add private showNoFramesNotice() in the IIFE (near module-state ~220-222, before startPresentation): lazily create/reuse a single document.body node (id wb-no-frames-notice, idempotent like the existing _framesEmptyTimer reuse), apply themed inline styles equivalent to the existing #frames-empty-toast CSS (board.html:127-141; position:fixed;top:24px;left:50%;transform:translateX(-50%);background:var(--wb-surface);border:1px solid var(--wb-border);color:var(--wb-text);border-radius:8px;padding:10px 16px;font-size:13px;z-index:10000;box-shadow:0 4px 16px rgba(0,0,0,0.15);), message e.g. "Nothing is being presented — this board has no slides yet.", auto-dismiss via a module timer (clear prior; ~4000ms so an audience can read it), optional click-to-dismiss. Hiding sets display:none and never touches wb-presenting.
  • Export showNoFramesNotice on the return block (~332-347).
  • No-frames branch (~132-139): replace the if (typeof window.showNoFramesNotice === 'function') {...} with a direct showNoFramesNotice(); call; keep the early return;.
    Dependencies: none

Step 2: Remove the duplicated editor-only notice from board.html

Files: crates/hero_whiteboard_admin/templates/web/board.html

  • Delete the #frames-empty-toast CSS block (~125-141), the <div id="frames-empty-toast"> node + its comment (~364-365), and the window.showNoFramesNotice definition + _framesEmptyTimer var (~543-552). Editor now uses the same single WhiteboardFrames.showNoFramesNotice().
    Dependencies: Step 1 (JS replacement must exist first). Both land in one change set; can be edited in parallel since different files.

Step 3: Force asset re-embed and rebuild

Files: crates/hero_whiteboard_admin/src/assets.rs

  • touch it (no content change) so rust-embed (#[folder="static/web/"]) picks up the edited frames.js; cargo build --release -p hero_whiteboard_admin; verify the served js/whiteboard/frames.js contains the new function before testing.
    Dependencies: Steps 1, 2

Acceptance Criteria

  • /board/{id}/view?present=1 with zero frames shows the themed notice (not a silent read-only session).
  • Shared-token presentation link (/s/{token}?present=1, viewer role → BoardViewTemplate) shows the same notice.
  • Editor path (/board/{id}, or /s/{token} editor role → BoardTemplate) still shows a no-frames notice on Present — no regression.
  • Notice is themed (correct in dark/light) and dismissible/auto-clears.
  • Remote-presentation-join with no frames is covered (auto-start startPresentation() → no-frames branch fires the notice; a late presentation.slide only sets _pendingRemoteSlide and never presents without startPresentation() succeeding).
  • After dismissal the read-only board is visible/scrollable (no stuck state; wb-presenting never applied in this path).
  • No server/route/schema change; only frames.js + board.html (+ touch assets.rs).

Notes

  • Single-source = Option (a): notice owned by frames.js. startPresentation() is the only presentation entry point and lives in frames.js, which every presentation-capable template loads (board.html:152, board_view.html:152). Duplicating into board_view.html (Option b) is rejected — it reintroduces exactly the editor/viewer drift that caused this bug.
  • ?present=1 reaches: /board/{id}/view → BoardViewTemplate → board_view.html; /s/{token} → BoardTemplate (editor role) or BoardViewTemplate (viewer); /board/{id} → BoardTemplate. All load frames.js + app.js; all extend base_web.html which has no presentation JS/CSS — so JS-owned is the only guaranteed-shared location.
  • Editor's window.showNoFramesNotice is removed (not shimmed): the only definition is board.html:546 and the only reference is frames.js:135-136 — no other caller, no shim needed. window.showBoardDeletedNotice is unrelated and untouched.
  • Notice CSS is applied as inline styles / one-time injected style from frames.js using existing theme vars (already used by #presentation-controls in both templates) — DOM+CSS+JS in one file = true single source.
  • Deploy: rust-embed embeds at compile time; after editing frames.js, touch crates/hero_whiteboard_admin/src/assets.rs before cargo build --release -p hero_whiteboard_admin, verify served frames.js changed before testing.
## Implementation Spec for Issue #197 ### Objective A visitor opening a presentation share link (`/board/{id}/view?present=1`, or `/s/{token}?present=1` resolving to the viewer) for a board with no frames must see a clear, themed, dismissible in-page notice instead of silently landing in a plain read-only session. Single shared notice implementation across editor, read-only viewer, and shared-token viewer. No server/schema change. ### Requirements - No-frames notice appears on the read-only viewer route, not only the editor. - One single source of the notice helper + DOM/CSS (no per-template duplication). - Themed via existing CSS variables (`--wb-surface`/`--wb-border`/`--wb-text`). - Dismissible/auto-clearing; page not left stuck (read-only board still visible/scrollable after dismissal; `wb-presenting` is never applied in the no-frames path). - Works for `?present=1` auto-start and a late remote-presentation join with zero frames. - JS + template only; no Rust/route/schema change. ### Files to Modify/Create - `crates/hero_whiteboard_admin/static/web/js/whiteboard/frames.js` - `WhiteboardFrames` owns a self-contained themed `showNoFramesNotice()` (builds/injects its own DOM + inline themed styles); called from the no-frames branch instead of depending on a template-defined `window.showNoFramesNotice`. - `crates/hero_whiteboard_admin/templates/web/board.html` - remove the now-redundant `window.showNoFramesNotice`, the `#frames-empty-toast` DOM node, and its CSS block. - `crates/hero_whiteboard_admin/src/assets.rs` - no code change; `touch` only to force rust-embed re-embed. No changes to `board_view.html`, `base_web.html`, `sync.js`, or `app.js` — frames.js is loaded by every presentation-capable template (board.html:152, board_view.html:152) and `startPresentation()` is the only entry point. ### Implementation Plan #### Step 1: Add a self-contained themed notice to WhiteboardFrames Files: `crates/hero_whiteboard_admin/static/web/js/whiteboard/frames.js` - Add private `showNoFramesNotice()` in the IIFE (near module-state ~220-222, before `startPresentation`): lazily create/reuse a single `document.body` node (id `wb-no-frames-notice`, idempotent like the existing `_framesEmptyTimer` reuse), apply themed inline styles equivalent to the existing `#frames-empty-toast` CSS (board.html:127-141; `position:fixed;top:24px;left:50%;transform:translateX(-50%);background:var(--wb-surface);border:1px solid var(--wb-border);color:var(--wb-text);border-radius:8px;padding:10px 16px;font-size:13px;z-index:10000;box-shadow:0 4px 16px rgba(0,0,0,0.15);`), message e.g. "Nothing is being presented — this board has no slides yet.", auto-dismiss via a module timer (clear prior; ~4000ms so an audience can read it), optional click-to-dismiss. Hiding sets display:none and never touches `wb-presenting`. - Export `showNoFramesNotice` on the return block (~332-347). - No-frames branch (~132-139): replace the `if (typeof window.showNoFramesNotice === 'function') {...}` with a direct `showNoFramesNotice();` call; keep the early `return;`. Dependencies: none #### Step 2: Remove the duplicated editor-only notice from board.html Files: `crates/hero_whiteboard_admin/templates/web/board.html` - Delete the `#frames-empty-toast` CSS block (~125-141), the `<div id="frames-empty-toast">` node + its comment (~364-365), and the `window.showNoFramesNotice` definition + `_framesEmptyTimer` var (~543-552). Editor now uses the same single `WhiteboardFrames.showNoFramesNotice()`. Dependencies: Step 1 (JS replacement must exist first). Both land in one change set; can be edited in parallel since different files. #### Step 3: Force asset re-embed and rebuild Files: `crates/hero_whiteboard_admin/src/assets.rs` - `touch` it (no content change) so rust-embed (`#[folder="static/web/"]`) picks up the edited frames.js; `cargo build --release -p hero_whiteboard_admin`; verify the served `js/whiteboard/frames.js` contains the new function before testing. Dependencies: Steps 1, 2 ### Acceptance Criteria - [ ] `/board/{id}/view?present=1` with zero frames shows the themed notice (not a silent read-only session). - [ ] Shared-token presentation link (`/s/{token}?present=1`, viewer role → BoardViewTemplate) shows the same notice. - [ ] Editor path (`/board/{id}`, or `/s/{token}` editor role → BoardTemplate) still shows a no-frames notice on Present — no regression. - [ ] Notice is themed (correct in dark/light) and dismissible/auto-clears. - [ ] Remote-presentation-join with no frames is covered (auto-start `startPresentation()` → no-frames branch fires the notice; a late `presentation.slide` only sets `_pendingRemoteSlide` and never presents without `startPresentation()` succeeding). - [ ] After dismissal the read-only board is visible/scrollable (no stuck state; `wb-presenting` never applied in this path). - [ ] No server/route/schema change; only frames.js + board.html (+ touch assets.rs). ### Notes - Single-source = Option (a): notice owned by frames.js. `startPresentation()` is the only presentation entry point and lives in frames.js, which every presentation-capable template loads (board.html:152, board_view.html:152). Duplicating into board_view.html (Option b) is rejected — it reintroduces exactly the editor/viewer drift that caused this bug. - `?present=1` reaches: `/board/{id}/view` → BoardViewTemplate → board_view.html; `/s/{token}` → BoardTemplate (editor role) or BoardViewTemplate (viewer); `/board/{id}` → BoardTemplate. All load frames.js + app.js; all extend base_web.html which has no presentation JS/CSS — so JS-owned is the only guaranteed-shared location. - Editor's `window.showNoFramesNotice` is removed (not shimmed): the only definition is board.html:546 and the only reference is frames.js:135-136 — no other caller, no shim needed. `window.showBoardDeletedNotice` is unrelated and untouched. - Notice CSS is applied as inline styles / one-time injected style from frames.js using existing theme vars (already used by `#presentation-controls` in both templates) — DOM+CSS+JS in one file = true single source. - Deploy: rust-embed embeds at compile time; after editing frames.js, `touch crates/hero_whiteboard_admin/src/assets.rs` before `cargo build --release -p hero_whiteboard_admin`, verify served frames.js changed before testing.
Author
Member

Test Results

  • Total: 0
  • Passed: 0
  • Failed: 0

cargo test --workspace --lib: ok - all workspace lib suites passed (0 tests present; suite is the regression gate)
node --check frames.js: ok

Note: #197 is a JS/template-only change (single-source no-frames presentation notice). No JS unit harness exists in this repo; the Rust suite is the regression gate and the notice on the viewer/shared-token presentation route is verified manually in-browser.

## Test Results - Total: 0 - Passed: 0 - Failed: 0 cargo test --workspace --lib: ok - all workspace lib suites passed (0 tests present; suite is the regression gate) node --check frames.js: ok Note: #197 is a JS/template-only change (single-source no-frames presentation notice). No JS unit harness exists in this repo; the Rust suite is the regression gate and the notice on the viewer/shared-token presentation route is verified manually in-browser.
Author
Member

Implementation Summary

JS/template-only, single-source. The no-frames presentation notice now lives in frames.js (WhiteboardFrames.showNoFramesNotice) instead of being defined only in the editor template, so the editor, the read-only viewer, and the shared-token presentation route all use the same implementation. No server/route/schema change.

Changes

frames.js

  • Added a self-contained showNoFramesNotice(): lazily creates/reuses a single #wb-no-frames-notice DOM node appended to document.body, themed via inline styles using the existing theme vars (--wb-surface / --wb-border / --wb-text, mirroring the old #frames-empty-toast rule), message "Nothing is being presented — this board has no slides yet.", auto-dismiss after 4000ms via a module timer, plus click-to-dismiss. Hiding only sets display:none and never touches wb-presenting.
  • Exported showNoFramesNotice on the module.
  • startPresentation()'s no-frames branch now calls showNoFramesNotice() directly instead of the template-defined window.showNoFramesNotice; the frames.length === 0 guard and early return are unchanged.

board.html

  • Removed the now-duplicated editor-only pieces: the #frames-empty-toast CSS block, the #frames-empty-toast DOM node, and the window.showNoFramesNotice definition + _framesEmptyTimer. window.showBoardDeletedNotice and unrelated code untouched.

A repo-wide grep confirms the only remaining references to the notice are the three new frames.js sites (definition, call, export); board.html is fully cleaned.

Behavior after change

  • Opening a presentation link (/board/{id}/view?present=1, or /s/{token}?present=1 resolving to the viewer) for a board with no frames now shows the themed notice instead of silently dropping into a plain read-only session.
  • Editor Present with no frames still shows the notice (now via the shared function) — no regression.
  • Notice is themed (dark/light), auto-dismisses, click-dismissible; the read-only board stays visible/scrollable afterward (wb-presenting is never applied in the no-frames path).

Tests

  • cargo test --workspace --lib: green, no Rust regression (JS/template-only; no JS unit harness in repo).
  • node --check frames.js: ok.
  • The notice on the viewer/shared-token presentation route verified manually in-browser after a forced-embed rebuild and redeploy.
## Implementation Summary JS/template-only, single-source. The no-frames presentation notice now lives in frames.js (WhiteboardFrames.showNoFramesNotice) instead of being defined only in the editor template, so the editor, the read-only viewer, and the shared-token presentation route all use the same implementation. No server/route/schema change. ### Changes frames.js - Added a self-contained showNoFramesNotice(): lazily creates/reuses a single #wb-no-frames-notice DOM node appended to document.body, themed via inline styles using the existing theme vars (--wb-surface / --wb-border / --wb-text, mirroring the old #frames-empty-toast rule), message "Nothing is being presented — this board has no slides yet.", auto-dismiss after 4000ms via a module timer, plus click-to-dismiss. Hiding only sets display:none and never touches wb-presenting. - Exported showNoFramesNotice on the module. - startPresentation()'s no-frames branch now calls showNoFramesNotice() directly instead of the template-defined window.showNoFramesNotice; the frames.length === 0 guard and early return are unchanged. board.html - Removed the now-duplicated editor-only pieces: the #frames-empty-toast CSS block, the #frames-empty-toast DOM node, and the window.showNoFramesNotice definition + _framesEmptyTimer. window.showBoardDeletedNotice and unrelated code untouched. A repo-wide grep confirms the only remaining references to the notice are the three new frames.js sites (definition, call, export); board.html is fully cleaned. ### Behavior after change - Opening a presentation link (/board/{id}/view?present=1, or /s/{token}?present=1 resolving to the viewer) for a board with no frames now shows the themed notice instead of silently dropping into a plain read-only session. - Editor Present with no frames still shows the notice (now via the shared function) — no regression. - Notice is themed (dark/light), auto-dismisses, click-dismissible; the read-only board stays visible/scrollable afterward (wb-presenting is never applied in the no-frames path). ### Tests - cargo test --workspace --lib: green, no Rust regression (JS/template-only; no JS unit harness in repo). - node --check frames.js: ok. - The notice on the viewer/shared-token presentation route verified manually in-browser after a forced-embed rebuild and redeploy.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_whiteboard#197
No description provided.