Skip to content

Map entity appearances#101

Open
ray-hc wants to merge 7 commits into
lmparppei:masterfrom
ray-hc:map_entity_appearances
Open

Map entity appearances#101
ray-hc wants to merge 7 commits into
lmparppei:masterfrom
ray-hc:map_entity_appearances

Conversation

@ray-hc

@ray-hc ray-hc commented Jul 8, 2026

Copy link
Copy Markdown

Summary

Adds a new plugin, Map Appearances by Scene — a visual timeline showing which scenes each "entity" appears in.

Based on Map Character Appearances, but re-written completely as a separate plugin: it generalizes the idea from characters to arbitrary entity types and adds sorting, tooltips, export, and user-defined types.

Main Window

Demo of the results window

Note: export controls are toggled by the Export button in top right.

Manage Custom Entities Window

Screenshot 2026-07-11 at 2 10 28 AM

Note: I did not use the Beat modals for adding new patterns to avoid another set of data passing btwn plugin.js and ui.html

Features

  • Timeline table: one row per entity, one cell per scene, cell widths proportional to scene length in page-eighths; row color is a stable hash of the entity, so colors are consistent across runs
  • Built-in entity types: Characters (dialogue cues + name mentions in action lines), Locations (from scene headings), Hashtags (#tag, excluding Fountain's #42# scene-number syntax)
  • Custom entity types: users define regex patterns in a "Manage Custom Entity Types" window
  • Sorting by page length, alphabetical, or first appearance; filtering by entity type
  • Hover tooltips showing scene number, heading, and every visible entity in that scene
  • Export to CSV or JSON (visible entities or all)
  • Screen-reader support: ARIA table semantics, text equivalents for the visual timeline, live announcements on filter changes

Testing

I manually checked that each entity type rendered correctly on a fully populated script and an empty script. I tested flows including changing between each entity type, toggling sort order, exporting both as CSV & JSON, creating bad RegEx.

I used VoiceOver on Mac to test screen-reader support.

I also created tests.js for unit tests, evaluates plugin.js and shared_helpers.js using mocked Beat global and a screenplay fixture; call via node tests.js

Example output:

🦎 Plugins/Map Appearances by Scene.beatPlugin [map_entity_appearances] node tests.js
ok - characters are found by dialogue cue and by mention in action lines
ok - page eighths are summed across a character's scenes
ok - firstAppearance is the earliest scene index, even when found out of order
ok - locations keep sublocations distinct and drop the time of day
ok - single-segment scene headings still produce a location
ok - hashtags are collected; Fountain #scene number# syntax is excluded
ok - custom patterns create entities from their first capture group
ok - entity colors are deterministic hex values derived from the entity key
ok - sceneData is keyed by scene number and preserves script order via index
ok - scene headings are cleaned of markers and trailing scene numbers
ok - descriptions cover built-in types and custom patterns
ok - entity type groups include custom types with zero results
ok - saveCustomEntityTypes persists valid patterns, drops invalid ones, and ends the plugin
ok - saving unchanged patterns closes the window without persisting, alerting, or ending
ok - closing the manage window without saving persists nothing and keeps the plugin alive
ok - exportEntities writes the window-provided content via the save dialog
ok - plugin becomes resident so window callbacks keep working
ok - formatPageLength renders eighths the screenplay way
ok - toCSV escapes quotes, commas, and newlines, and joins scenes
ok - isNameTaken is case-insensitive and trims whitespace
ok - validateNewPattern reports each failure mode and passes valid input
ok - both HTML templates load the shared helpers and don't redefine them
ok - shared_helpers.js is safe to inline into a script tag

AI Disclosure

I made extensive use of AI (mostly Claude) in doing this re-write but I attest that I read through all the code & understand it & believe there is no slop in it.

More Info

See the Readme.MD for implementation notes

@lmparppei

lmparppei commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Wow, nice work, thank you!

I think the UI requires some work, though. How about refactoring the current prompt window as a dropdown, maybe as a fixed top or bottom bar next to the map?

You could then have "Custom Regex" as one option, saving some screen space and making the warning less prominent. After selecting Custom Regex, you could have another dropdown next to that with the currently defined ones, and a button to clear all or add new, similar to what you now have.

@ray-hc

ray-hc commented Jul 11, 2026

Copy link
Copy Markdown
Author

Wow, nice work, thank you!

I think the UI requires some work, though. How about refactoring the current prompt window as a dropdown, maybe as a fixed top or bottom bar next to the map?

You could then have "Custom Regex" as one option, saving some screen space and making the warning less prominent. After selecting Custom Regex, you could have another dropdown next to that with the currently defined ones, and a button to clear all or add new, similar to what you now have.

I agree... I was avoiding doing so as I figured that would require a full rewrite but I went ahead & did so & it's definitely much better off for it. Updated PR w/ new demo images. LMK what you think.

@ray-hc ray-hc marked this pull request as ready for review July 11, 2026 06:18
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.

2 participants