Skip to content

Commit 758db78

Browse files
authored
Merge pull request #124 from keyxmakerx/claude/foundry-module-review-IrVxd
Claude/foundry module review ir vxd
2 parents 4b19804 + 8bf5809 commit 758db78

12 files changed

Lines changed: 400 additions & 39 deletions

File tree

.ai/status.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@
88
<!-- ====================================================================== -->
99

1010
## Last Updated
11-
2026-03-11 -- **Session fix + Owner Dashboard (two-dashboard architecture).**
11+
2026-03-11 -- **Foundry module review — bug fixes and feature completion.**
12+
13+
31. **Foundry module review.** Comprehensive code review of the Foundry VTT sync module found 13 issues. Fixed 9 (deferred ApplicationV2 upgrade):
14+
- **Runtime bugs**: Shop window `{{json}}` helper crash (replaced with data-item-id lookup), drawing coordinate conversion missing percentage↔pixel (tokens had it, drawings didn't), fog reconciliation `_syncing` flag corruption (extracted `_createFogDrawingData`, batch creates), entity_type_id:0 invalid in syncapi handler (added first-type fallback).
15+
- **Data flow gaps**: Scene-to-map linking (context menu + auto-link), `_onEntityCreated` fetches full entity from REST (WS payload partial), `onSyncMapping` added to MapSync and CalendarSync, `onInitialSync` added to MapSync (pulls drawings/tokens/fog on connect).
16+
- **Docs/metadata**: SimpleCalendar in module.json, calendar hint fix, .ai.md corrections (fog status, App v1 not v2), TESTING.md fog + scene-linking sections.
1217

1318
30. **Session completion fix + two-dashboard architecture.** Two changes:
1419
- **Session "Mark Complete" bug fix**: `hx-vals` always sends form-encoded data regardless of `hx-headers` Content-Type. `UpdateSessionAPI` expects JSON, so the decode failed → 400 error → red notification bar. Replaced with `Chronicle.apiFetch()` onclick handler using `data-url` and `data-name` attributes. Also fixes XSS risk from session name interpolation into JSON template string.

.ai/todo.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ _WASM-sandboxed backend logic via Extism/wazero. See ADR-021._
289289
- [ ] Calendar timezone support / print-PDF export
290290
- [ ] Map hex/square grid overlay
291291
- [x] Fog of war bidirectional sync (Chronicle ↔ Foundry)
292+
- [x] Foundry module review: runtime bugs (json helper, coord conversion, fog flag, entity_type_id), data flow (scene linking, onSyncMapping, onInitialSync for MapSync, full entity fetch), docs
292293
- [ ] Webhook support for external event notifications
293294
- [ ] Widget inline CSS → CSS classes migration
294295
- [ ] Reusable modal/dropdown component library

foundry-module/.ai.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ All sync modules use a `_syncing` boolean flag to prevent infinite loops:
4343
| `scripts/api-client.mjs` | REST client (Bearer auth) + WebSocket (auto-reconnect, message queue, event emitter) |
4444
| `scripts/sync-manager.mjs` | Orchestrator. Owns API client, routes WS messages, manages sync mapping lookups |
4545
| `scripts/journal-sync.mjs` | Entity ↔ JournalEntry bidirectional sync. Monk's Enhanced Journal support |
46-
| `scripts/map-sync.mjs` | Map drawings/tokens sync. Percentage↔pixel coordinate conversion. Fog is TODO |
46+
| `scripts/map-sync.mjs` | Map drawings/tokens/fog sync. Percentage↔pixel coordinate conversion. Scene-to-map linking via context menu |
4747
| `scripts/calendar-sync.mjs` | Adapter pattern for Calendaria and SimpleCalendar. 0-indexed↔1-indexed conversion |
48-
| `scripts/shop-widget.mjs` | Shop window (ApplicationV2). Context menu, drag-to-character-sheet, real-time updates |
48+
| `scripts/shop-widget.mjs` | Shop window (Application v1). Context menu, drag-to-character-sheet, real-time updates |
4949
| `templates/shop-window.hbs` | Handlebars template for shop inventory display |
5050
| `styles/chronicle-sync.css` | Status indicator + shop window styles |
5151
| `lang/en.json` | English localization for all UI strings |

foundry-module/TESTING.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,19 @@ Requires a running Chronicle instance and Foundry VTT with the chronicle-sync mo
6060
- [ ] Drawing at (0,0) maps correctly
6161
- [ ] Drawing at scene edge maps correctly
6262

63+
## Fog of War
64+
65+
### Chronicle -> Foundry
66+
- [ ] Create fog region in Chronicle -> Semi-transparent polygon drawing appears on scene
67+
- [ ] Create multiple fog regions -> All render correctly as overlay drawings
68+
- [ ] Reset fog in Chronicle -> All fog drawings cleared from scene
69+
- [ ] Fog region reconciliation: add/remove regions correctly on re-fetch
70+
71+
### Foundry -> Chronicle
72+
- [ ] Draw a dark polygon (black fill, alpha > 0.5) -> Pushes as fog region to Chronicle
73+
- [ ] Delete a fog drawing in Foundry -> Fog region deleted in Chronicle
74+
- [ ] Non-fog polygon (light color or low alpha) -> Syncs as regular drawing, not fog
75+
6376
## Calendar Sync
6477

6578
### Chronicle -> Foundry
@@ -91,6 +104,16 @@ Requires a running Chronicle instance and Foundry VTT with the chronicle-sync mo
91104
- [ ] Multiple shop windows can be open simultaneously
92105
- [ ] Closing shop window cleans up properly
93106

107+
## Scene-to-Map Linking
108+
109+
- [ ] Right-click scene in nav bar -> "Link to Chronicle Map" option visible (GM only)
110+
- [ ] Dialog shows all Chronicle maps for the campaign
111+
- [ ] Selecting a map links the scene (sets flag)
112+
- [ ] Unlinking clears the flag
113+
- [ ] Auto-link: if campaign has exactly one map, scene auto-links on initial sync
114+
- [ ] Multi-map warning: if campaign has multiple maps, log warning with instructions
115+
- [ ] Linked scene shows correct map ID in flag inspector
116+
94117
## Initial Sync
95118

96119
- [ ] Fresh connection triggers initial sync (GET /sync/pull)

foundry-module/lang/en.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
},
2929
"SyncCalendar": {
3030
"Name": "Sync Calendar",
31-
"Hint": "Sync Chronicle calendar with Calendaria module"
31+
"Hint": "Sync Chronicle calendar with Calendaria or Simple Calendar module"
3232
}
3333
},
3434
"Status": {

foundry-module/module.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@
4545
"type": "module",
4646
"compatibility": {},
4747
"reason": "Optional: Calendar sync between Chronicle and Calendaria"
48+
},
49+
{
50+
"id": "foundryvtt-simple-calendar",
51+
"type": "module",
52+
"compatibility": {},
53+
"reason": "Optional: Calendar sync between Chronicle and Simple Calendar"
4854
}
4955
]
5056
}

foundry-module/scripts/calendar-sync.mjs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,21 @@ export class CalendarSync {
8787
}
8888
}
8989

90+
/**
91+
* Handle a sync mapping received during initial sync.
92+
* Stores calendar event mappings for later lookup.
93+
* @param {object} mapping
94+
*/
95+
async onSyncMapping(mapping) {
96+
if (mapping.chronicle_type !== 'calendar_event') return;
97+
if (!getSetting('syncCalendar') || !this._calendarModule) return;
98+
99+
// Store the mapping so we can correlate local ↔ Chronicle events.
100+
if (mapping.external_id && mapping.chronicle_id) {
101+
this._storeEventMapping(mapping.external_id, mapping.chronicle_id);
102+
}
103+
}
104+
90105
/**
91106
* Perform initial calendar sync on WebSocket connect.
92107
* Fetches Chronicle calendar structure and syncs current date.

foundry-module/scripts/journal-sync.mjs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,9 @@ export class JournalSync {
103103

104104
/**
105105
* Create a new JournalEntry from a Chronicle entity.
106-
* @param {object} entity - Chronicle entity data.
106+
* Fetches full entity data from the API since WebSocket payloads may
107+
* not include content fields (entry_html, fields_data, tags).
108+
* @param {object} entity - Chronicle entity data (possibly partial).
107109
* @private
108110
*/
109111
async _onEntityCreated(entity) {
@@ -115,7 +117,15 @@ export class JournalSync {
115117
);
116118
if (existing) return;
117119

118-
await this._createJournalFromEntity(entity);
120+
// Fetch full entity data (WS payload may be partial).
121+
try {
122+
const fullEntity = await this._api.get(`/entities/${entity.id}`);
123+
await this._createJournalFromEntity(fullEntity || entity);
124+
} catch (err) {
125+
// Fallback to WS payload data if fetch fails.
126+
console.warn('Chronicle: Failed to fetch full entity, using WS payload', err);
127+
await this._createJournalFromEntity(entity);
128+
}
119129
}
120130

121131
/**

0 commit comments

Comments
 (0)