Commit Graph
30 Commits
Author SHA1 Message Date
FrankandClaude Sonnet 5 8406f06738 docker: NPM joins dmtools_default, not a shared proxy network
NPM (nginx_app_1) is connected to each stack's own default network by
hand, not to a shared docker_default. Drop the unused external `proxy`
network from the compose file; nginx just lives on dmtools_default.
setup.sh now connects NPM to it (no-op if already connected) and prints
the proxy-host settings.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 21:32:10 +02:00
FrankandClaude Sonnet 5 25b68cb1df Fix db host-port collision: separate DB_HOST_PORT from the internal port
DB_PORT was doing double duty - the container-internal port baked into
DATABASE_URL (must be 3306) and the host-published port in
docker-compose.yml. Setting it to 3306 for the URL made compose try to
publish host :3306, which collides with another stack's MySQL.

DATABASE_URL now hardcodes database:3306 (always correct inside the
compose network); the host publish uses ${DB_HOST_PORT:-3310}.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 21:07:51 +02:00
FrankandClaude Sonnet 5 ade11494be docker/php: build extensions via mlocati/php-extension-installer
Hand-managed apk build deps + docker-php-ext-install was failing to build
on the server. Switch to the extension-installer image (same approach the
escapepage stack uses), which resolves system libs itself.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 20:59:57 +02:00
FrankandClaude Sonnet 5 7a9f72a3de Overview page: navigation tiles instead of the demo table
Replaces the leftover scaffold DataTable with a grid of tiles linking to
Characters, NPC's, Enemies, Tracker, Art and Storyline.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 19:43:37 +02:00
FrankandClaude Sonnet 5 cb23d5b9ac Show AC on each tracker row
Combatant snapshots armor_class from the enemy when added (like maxHp),
shown as an "AC n" pill next to the name for quick hit checks. Migration
backfills existing combatants from their enemy.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 19:34:24 +02:00
FrankandClaude Sonnet 5 2ed169fc78 Tracker: unfold a combatant's stat block inline
- extract the 5e stat-block markup into enemy/_statblock.html.twig
  (sb.block(enemy) macro); the enemy detail page now uses it too
- each tracker combatant gets a "stat block" toggle that expands the
  block in place (Bootstrap collapse), so you don't have to leave the
  encounter to check AC / attacks
- open blocks are remembered in localStorage and re-opened after each
  damage/heal reload, and the caret rotates when expanded

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 19:29:52 +02:00
FrankandClaude Sonnet 5 cbe4b0a0e1 Combat tracker: multiple named encounters
- Encounter entity; Combatant now belongs to an encounter (ON DELETE
  CASCADE). Migration adds encounter_id, rehoming any existing combatants.
- /tracker lists encounters (name, count, last updated) with a "New
  encounter" form; /tracker/{id} is the HP tracker for one encounter.
- Encounters can be renamed, have their combatants cleared, or be deleted.
- The Enemies list gains an encounter picker next to "Add to tracker"
  (existing encounter or "— New encounter —", which prompts for a name);
  an encounter's "Add enemies" link pre-selects it via ?encounter=.
- Routes for combatant HP/remove moved under /tracker/combatant/ to keep
  the numeric {id} for encounters.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 19:24:37 +02:00
FrankandClaude Sonnet 5 9854e6c034 Add combat HP tracker
- one running tracker (Combatant rows); no encounters to manage
- Enemies list gets a "Track" column (checkbox + count) and an
  "Add to tracker" button; picking N of an enemy makes "Goblin 1..N",
  numbering continues if some are already on the tracker
- /tracker: per-combatant current/max HP with an HP bar, Damage / Heal
  by amount, remove one, "Clear all". HP is clamped to [0, max];
  0 HP shows a "Down" badge. Stats are snapshotted from the Enemy on add.
- "Tracker" nav item; tracker_picker Stimulus controller; app.js gains a
  data-format="pick" cell renderer

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 19:16:12 +02:00
FrankandClaude Sonnet 5 e417ff9655 Add app:enemy:seed - 8 SRD monsters for testing the Enemies section
Goblin, Wolf, Zombie, Bandit Captain, Ogre, Owlbear, Wight and Adult Red
Dragon (CR 1/4 to 17; humanoid, beast, undead, giant, monstrosity,
dragon). Idempotent by name; --purge removes them.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 19:04:43 +02:00
FrankandClaude Sonnet 5 ed22b81e97 Art: full-image thumbnails, detail page with copy-to-clipboard
- gallery thumbnails now object-fit: contain (whole image, letterboxed),
  and a card links to the new detail page instead of the raw file
- /art/{id} detail page: large uncropped image, "Open full size",
  "Delete" (moved here from the gallery), and "Copy image"
- clipboard Stimulus controller copies the image to the OS clipboard
  (converts non-PNG via canvas) for pasting into WhatsApp etc.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 16:53:49 +02:00
FrankandClaude Sonnet 5 f04c8ccc51 Raise upload limit to 8 MB
- docker/php/php.ini: upload_max_filesize 8M / post_max_size 10M
- docker/nginx/default.conf: client_max_body_size 10M
- ArtworkType: Image maxSize 8M with a clear message + help text

(local C:\php\php.ini was bumped from 2M the same way — not in the repo)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 16:44:30 +02:00
FrankandClaude Sonnet 5 ed9390319b Add Art section: upload named images, gallery, delete
- Artwork entity (name, stored filename, original name, mime, size, date)
- /art gallery grid; /art/upload (name + image file, PNG/JPEG/GIF/WebP,
  20M cap); /art/{id}/file streams the image behind auth; /art/{id}/delete
  (CSRF) removes row + file
- files stored under var/uploads/art/ (git-ignored, on the php_var volume
  in Docker), served through a controller so they stay login-gated
- "Art" nav item

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 16:41:33 +02:00
FrankandClaude Sonnet 5 1f4b5290b2 Drop HP and Updated columns from the enemies table
Still on the entity and shown on the detail page.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 16:41:33 +02:00
FrankandClaude Sonnet 5 7870be9dfb Add Enemies section: D&D stat-block table + detail/edit pages
- Enemy entity: full 5e stat block (size/type/subtype/alignment, AC, HP,
  speed, six ability scores, CR/XP/prof, saves/skills/senses/languages,
  damage & condition modifiers, traits/actions/bonus/reactions/legendary,
  description, source) plus a DM game-notes field and updated_at.
- CreatureSize / CreatureType enums.
- /enemies: server-side DataTable (name, size, type, CR, AC, HP, notes),
  "Add enemy" button.
- /enemies/{id}: stat block rendered read-only + editable game notes.
- /enemies/new and /enemies/{id}/edit: the full stat-block form (no notes).
- "Enemies" nav item between NPC's and Storyline.
- Table left empty on purpose.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 16:36:17 +02:00
FrankandClaude Sonnet 5 574d903715 Add family tree page, linked from the NPC's family name
- /npcs/family/{family} (app_npc_family) renders a parent->child tree for
  everyone sharing that family name; co-parents who are both in the family
  are paired into one couple node, elders first
- FamilyTreeBuilder does the flat-list -> tree assembly
- the NPC detail page's family name (subtitle + Details row) now links there
- .family-tree CSS: nested list with connector lines, small portrait avatars

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 16:23:03 +02:00
FrankandClaude Sonnet 5 d43746970b Split NPC page: read-only detail + game notes, separate full edit form
- /npcs/{id} (app_npc_show): portrait, all fields shown read-only, and
  the only editable control is the game-notes textarea. Father/Mother
  link through to their own pages.
- /npcs/{id}/edit (app_npc_edit): the full structured form, now WITHOUT
  game notes; saving returns to the detail page.
- NpcType drops gameNotes; new NpcGameNotesType holds just that field.
- The DataTable row link (/npcs/{id}) now lands on the detail page.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 16:17:03 +02:00
FrankandClaude Sonnet 5 71b5cb6633 Show the NPC portrait on the detail page
Npc::getPortraitPath() maps the name to image/npc-pictures/<NameNoSpaces>.png;
the edit page renders it beside the heading (with family · occupation), and
drops the <img> via onerror when a portrait is missing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 16:10:00 +02:00
FrankandClaude Sonnet 5 4d5c3253ed Add final 41 NPC portraits; every NPC now has a picture
- 41 cut from the "Wren Thorn … Mira Rook" sheet (space-free names)
- removed 17 stale files that matched no NPC: the 16 hallucinated names
  from the first sheet (CalenCooper, *Mill, *Reed) now superseded by the
  real families, plus HiyHearth

assets/image/npc-pictures/ = 180 files, one per NPC.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 16:05:10 +02:00
FrankandClaude Sonnet 5 60a6904b22 Update portraits-needed list to the final 41 NPCs
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 15:53:13 +02:00
FrankandClaude Sonnet 5 1d043c35c8 Add 101 NPC portraits (lists 1 & 2), drop spaces from all filenames
- 50 portraits cut from the "Sten Voss … Ralf Brewster" sheet
- 51 from the "Mara Brewster … Poppy Thorn" sheet (incl. one stray
  "HiyHearth" the generator invented between Eli and May Hearth)
- existing 55 files renamed to remove spaces (Garrick Voss.png ->
  GarrickVoss.png)

assets/image/npc-pictures/ now holds 156 files.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 15:52:30 +02:00
FrankandClaude Sonnet 5 710b383bc1 Strip parent mentions from NPC relationships text
Parents now live in the father/mother fields, so "son/daughter/child of
X [and Y]" is redundant noise in Relationships / Notes.

- app:npc:strip-parent-text removes that clause and tidies leftover
  punctuation ("Son of X, husband of Y" -> "Husband of Y"; standalone
  "Son of X and Y" -> cleared). Keeps "grandson of", "father of",
  "daughter-in-law of". 92 rows cleaned.
- data/npcs.csv regenerated from the DB with the cleaned text plus new
  Father / Mother name columns
- app:npc:import resolves those optional columns in a second pass, so a
  fresh install reproduces the parent links without app:npc:link-parents

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 15:41:02 +02:00
FrankandClaude Sonnet 5 54d326572e Add 55 NPC portrait images (assets/image/npc-pictures/)
Cut from the contact sheet, one PNG per character named by the label
under each portrait. 39 match an existing NPC row; 16 (the extra Cooper
kids, the "Mill" family, the "Reed" block) have no matching NPC in the
database yet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 15:32:06 +02:00
FrankandClaude Sonnet 5 5bf25d1802 Add father/mother links to NPCs, derived from the relationships text
- Npc gains self-referencing father/mother (nullable, ON DELETE SET NULL);
  editable as dropdowns on the per-NPC form
- app:npc:link-parents parses "son/daughter/child of X [and Y]" from the
  Relationships / Notes column, resolves the names within the same family,
  and works out which parent is father vs mother from husband/wife/widow(er)
  and son/daughter/sister/brother cues (plus a small name-gender table for
  the few the text can't disambiguate). --dry-run / --force. 92/180 linked,
  none ambiguous.
- migrations now pin ENGINE=InnoDB - the local WAMP MariaDB defaults to
  MyISAM, which silently drops foreign keys

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 15:21:03 +02:00
FrankandClaude Sonnet 5 3c622d7dd8 Destroy DataTables before Turbo caches the page
The back button restored a Turbo snapshot that still held DataTables'
injected search / length controls; re-init on turbo:load then stacked
another set each time. Tear every instance down on turbo:before-cache
so the cached snapshot is a plain table again.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 15:11:59 +02:00
FrankandClaude Sonnet 5 01d5fd13b2 Drop Family, Race and Updated columns from the NPC table view
Still stored on the entity and editable on the per-NPC form; just no
longer shown in the /npcs list.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 15:07:36 +02:00
FrankandClaude Sonnet 5 02e3fe3eb6 Default DataTable page length to 10
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 15:06:32 +02:00
FrankandClaude Sonnet 5 1ccab6ed68 Add NPC database table with server-side DataTable and per-NPC game notes
- Npc entity (village spreadsheet columns) + game_notes text field for
  the DM's live play notes, plus updated_at touched on every change
- Race / VillageStatus / BloodCartInvolvement / MysteryImportance as
  string-backed PHP enums, rendered as dropdowns on the edit form
- /npcs: server-side DataTables (paging, global search and column sort
  all run in the backend via NpcRepository::findForDataTable); name cell
  links to the edit page, game notes shown truncated
- /npcs/{id}: Bootstrap-themed edit form (all fields + a large game
  notes textarea); Symfony forms now use the bootstrap_5 theme
- app.js: generic server-side DataTable wiring driven by data-* on the
  <table>/<th> (data-datatable-src, data-row-url, data-format)
- app:npc:import console command - upserts from data/npcs.csv by
  (family, name), never overwrites game_notes; --purge / --dry-run
- data/npcs.csv: 180 villagers exported from the working spreadsheet

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 15:04:39 +02:00
FrankandClaude Sonnet 5 cc4fbf425d Add nav menu with Characters, NPC's and Storyline pages
Navbar (collapsible) with active-state highlighting driven by a nav list
in base.html.twig. Three routed, auth-gated controllers each rendering a
shared placeholder partial - data models still to be designed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 14:36:17 +02:00
FrankandClaude Sonnet 5 9f6763040e Scaffold Symfony 8 app with Bootstrap/DataTables and Docker stack
- Symfony 8.1 webapp skeleton on PHP 8.5 (AssetMapper/importmap, no Node)
- Form-login gate: User entity, security.yaml, SecurityController,
  app:user:create command, initial migration (user + messenger_messages)
- Bootstrap 5 + DataTables layout (base/login/home templates); any
  table.datatable is auto-initialised
- trusted_proxies/headers wired for the shared Nginx Proxy Manager
- Docker (servers only): docker-compose.yml (php:8.5-fpm-alpine,
  nginx:1.30-alpine, mariadb:12.3; host ports 8085/3310; external
  docker_default network) + docker/{Dockerfile,php.ini,nginx,setup.sh,restart.sh}
- .env with CHANGEME placeholders (no secrets), .gitattributes enforces
  LF so the deploy scripts stay runnable on Linux

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 14:11:36 +02:00
Frank 39240a98ec Initial commit 2026-09-06 11:20:46 +00:00