Skip to content

DM Screen: a GM panel that unfolds out of a falling-apart d20 - #989

Merged
keyxmakerx merged 7 commits into
mainfrom
claude/dm-screen-o15her
Oct 3, 2026
Merged

keyxmakerx merged 7 commits into
mainfrom
claude/dm-screen-o15her

Conversation

@keyxmakerx

@keyxmakerx keyxmakerx commented Oct 3, 2026 •

Copy link
Copy Markdown
Owner

Requested by Key Maker · project thread

Fixes #975
Security implication: two new owner/scribe-only routes (GET panel, POST reveal). Reveal can only make an NPC or creature in the same campaign visible; it never hides anything and refuses other entity types and other campaigns. The downtime switch reuses the existing owner-only Armory endpoint.
Consumer-verified: the downtime buttons post open to the existing POST /campaigns/:id/armory/downtime; the panel refreshes on that endpoint's existing armory-moved HX-Trigger.
Mockup: signed Sign-offs card mock-dm-screen (rounds 1–3, round 3 approved "Yes, build it like this"); mockup at https://claude.ai/artifact/HZsaAMnxuW4RvEyR71u9tB

What this changes

Owners and scribes get a tri-fold icon in the campaign header, just before the avatar. Clicking it rolls a d20 to the middle of the screen. The die falls apart into pieces that drop, bounce off the floor and walls, and settle and fade, and the DM Screen unfolds out of it. Clicking anywhere or pressing Escape skips the whole animation, and reduced-motion users get a plain fade. On the panel:

  • Left leaf: the game state switch (downtime or adventuring, which only the owner can flip), the pending requests count, and the in-world date, time and weather.
  • Middle leaf: the party, showing each hero's meters.
  • Right leaf: a searchable conditions lookup and a list of hidden NPCs, each with a Reveal button.
  • Top strip: the next game night with its RSVP tally, and whether Foundry is connected.

The party meters and conditions come from the game system package, as Key Maker asked. This PR adds a new optional dm_screen block to the system manifest. In it, a package names up to six meters by field key (current/max, warn threshold) and a reference category for the conditions lookup. Until a package ships the block, the panel says "{System} doesn't fill in hero numbers yet" and leaves the Rules tab out.

Signed off: the folded heroes (with class and conditions, and the open hero always shown whole) were approved on the Sign-offs card mock-dm-fold round 2.

Why

#973 (parent). Key Maker asked for a GM control panel opened from the top right with a distinctive effect. The plugin follows T-B2: internal/plugins/dmscreen depends only on source interfaces, and the adapters over armory, calendar, sessions, entities, campaigns and the Foundry presence hub live in internal/app/dm_screen_adapters.go. Each source is optional; a failing one drops its section with a log line instead of failing the panel.

Load-bearing lines:

  • internal/plugins/dmscreen/routes.go: both routes are RequireAuth + RequireCampaignAccess + RequireRole(RoleScribe), and the service re-checks the role.
  • internal/app/dm_screen_adapters.go Reveal: checks the campaign and that the type is an NPC/creature, then calls the new idempotent EntityService.SetPrivateInCampaign(..., false). The toggle isn't used, so a double-click can't re-hide.
  • internal/systems/manifest_dm_screen.go: validation of the new manifest block.
  • internal/plugins/armory/routes.go: new AddonSlug constant so the adapter doesn't repeat the plugin name (plugin-isolation guard).

Honest deviations from the signed mockup, each with its follow-up (on the Sign-offs small-differences card diff-dm-screen):

Package halves: keyxmakerx/Chronicle-Draw-Steel#70 and keyxmakerx/Chronicle-DnD-5.5e#5 add the dm_screen block; both validated with this branch's ValidateManifest.

Test plan

  • go build ./... and go vet ./...: clean.
  • go test ./... -short -v (after merging the latest main): 3,179 passed, 92 skipped, 0 failed.
  • New tests: internal/plugins/dmscreen (service roles, downtime toggle rights, meters, conditions, failing sources, reveal; panel render), internal/systems (manifest dm_screen validation), internal/app (reveal never hides, never crosses campaigns, refuses non-NPC types; night label), internal/plugins/entities (SetPrivateInCampaign set-not-toggle and campaign guard).
  • Route and sanitize snapshots regenerated: +2 routes, +1 service.
  • make test-js: 361 passed, 0 failed.
  • Guards: plugin-isolation, migration-immutability, page-scripts, widget-mounts, templ-packages, customize-discipline and no-instance-hostname all pass. motion-discipline had nothing to check.
  • golangci-lint: not run locally (the installed binary is older than the module's Go version). CI runs it.
  • UI: tried in headless Chromium at 1200px and 400px against a local harness. The roll, break-apart and bounce, unfold, click-to-skip, Escape and close-back-to-icon all work, nothing is left behind after closing, and reduced motion gets a fade. Not yet tried on a live server.

Tenet self-check

  • T-B1 security: role-gated routes, CSRF on both POSTs, reveal-only and campaign-scoped writes, all text escaped by templ.
  • T-B2 plugin isolation: the plugin talks to others only through interfaces wired in internal/app, with no new plugin-name literals.
  • T-B3 production UI: opening, skip and closing transitions; a loading state while the panel fetches; a toast if it fails; reduced motion respected; dialog role and labelled buttons.
  • T-B4 docs: internal/plugins/dmscreen/.ai.md.

Stop-and-flag

If during review you find this PR violates a tenet that the author missed, flag it explicitly in a review comment citing the tenet by number.

🤖 Generated with Claude Code

https://claude.ai/code/session_011hzkeAmSZQx5cQw5QL3ngM


Generated by Claude Code


Generated by Claude Code

claude added 3 commits October 3, 2026 05:05
A tri-fold icon in the campaign header opens a GM control panel: a d20
rolls in, falls apart into pieces that bounce and settle, and the screen
unfolds out of it. Click or Escape skips the whole animation.

The panel shows game state (downtime switch and pending requests), the
in-world date, time and weather, the next game night with its RSVP
tally, Foundry presence, the party's meters, a conditions lookup and a
reveal list for hidden NPCs. Party meters and conditions come from the
system package through a new optional manifest `dm_screen` block.

Part of #973.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011hzkeAmSZQx5cQw5QL3ngM
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011hzkeAmSZQx5cQw5QL3ngM
Reveal now sets the private flag instead of toggling it, so a
double-click can't hide the character again, and it refuses entity
types the panel doesn't list. The Reveal button drops repeat clicks
while a request is in flight, and the Rules tab is skipped when a
package has no data provider.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011hzkeAmSZQx5cQw5QL3ngM
claude added 4 commits October 3, 2026 05:20
Heroes start folded to their name, health bar and the meters that have
no maximum (a class resource, an armour class); clicking a hero folds
the player's name and the remaining meters open below. With more heroes
than fit, the list scrolls inside the panel, fades at the edge and says
how many more are below.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011hzkeAmSZQx5cQw5QL3ngM
The system's dm_screen block can now name the sheet fields for a hero's
class and current conditions. The folded line shows a count of
conditions; the opened hero shows the class and each condition. An
opened hero is always shown completely: the list scrolls the others
aside and grows when one hero alone wouldn't fit.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011hzkeAmSZQx5cQw5QL3ngM
@keyxmakerx
keyxmakerx marked this pull request as ready for review October 3, 2026 15:21
@keyxmakerx
keyxmakerx merged commit 2bf70a5 into main Oct 3, 2026
10 checks passed
@keyxmakerx
keyxmakerx deleted the claude/dm-screen-o15her branch October 3, 2026 15:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DM Screen on the Chronicle site

2 participants