Skip to content

v14 - #45

Closed
camrun91 wants to merge 17 commits into
mainfrom
staging
Closed

v14#45
camrun91 wants to merge 17 commits into
mainfrom
staging

Conversation

@camrun91

@camrun91 camrun91 commented Jun 8, 2026

Copy link
Copy Markdown
Owner

Description

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Code quality improvement (refactoring, linting, etc.)
  • Release preparation

Checklist

  • I have run npm run lint:fix and fixed any remaining errors
  • I have tested my changes in Foundry VTT
  • I have updated the documentation (if applicable)
  • I have added/updated tests (if applicable)
  • I have updated CHANGELOG.md (if this is a user-facing change)
  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code

Testing

Foundry VTT Version:
Game System:

Screenshots (if applicable)

Related Issues

Closes #

gharezlak and others added 12 commits April 8, 2026 09:32
compatibility.verified was "13.351" while minimum was "14.359" -
verified must never be lower than minimum. Looks like a v13/v14 field
cross-contamination similar to the incident that caused the original
v14 rollback. Reset verified to match minimum until tested against a
newer 14.x build.
Previously only the quest title round-tripped to the API; every other
field (giver, category, status, success/failure/next-action/resolution)
was display-only after first import, objectives were local-only, and
Quests sat outside the shared linking system entirely.

- Info tab is now a structured edit form instead of a stubbed ProseMirror
  body (Quest has no generic `description` field in the API); drop the
  hack that stuffed successDefinition/nextAction into page content on
  import.
- _commitEdits sends the full updateQuest payload, not just questName.
- Objectives (add/remove/edit text+status) sync to the API instead of
  being local-flag-only.
- Quest relationships persist via relatedEntityRefs on the quest itself
  (updateQuest), since the API's link system has no ENTITY_CONFIG entry
  for Quest in either direction. Links tab now renders real, clickable,
  unlinkable cards backed by entity IDs instead of static name chips.
- Character/Item/Location/Faction sheets get a read-only "Quests" tab
  (client-side filtered from listQuests(), since there's no reverse
  lookup endpoint) so relationships are visible from both sides.
- reconcile-service now includes quests/journals in the ongoing sync
  pass, not just first import.
- Documented Quest/Journal/Links shapes in the Cursor API rules doc,
  which previously covered every other entity but not these.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Historically users couldn't import existing Foundry Actor/Item images
because they're stored locally and the module had no way to send image
bytes to the API. Confirmed the API's actual upload shape: a 3-step
presigned-URL flow (init -> PUT bytes directly to R2 -> complete), not
a raw multipart POST. 4.5MB cap and content-sniffing are server-enforced.

- archivist-api.js: initImageUpload/uploadImageBytes/completeImageUpload
  plus a uploadEntityImage convenience wrapper running all three steps.
- Character/Item/Location/Faction sheets get an "Upload local image"
  header button (GM-only) that reads the linked Actor/Item/Scene's (or
  the journal's own) local img via fetch() and pushes it to Archivist.
  Not available for Quest — the API has no image support for that type.
- archivist-api.js: added updateLink (PATCH) alongside the existing
  createLink/deleteLink so a link's alias can be corrected in place
  instead of delete+recreate, once a UI flow needs it.

Other endpoints surveyed but intentionally left for a future pass to
keep this change bounded: unified /campaigns/{id}/search, the
lightweight /entities picker endpoint, session transcript/handout ->
Journal auto-generation, the recordings/transcription pipeline, and
journal-folder CRUD (create/rename/delete).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Confirmed zero references anywhere in the repo (grep across .js/.json).
Stray backup file, not part of the build.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Project Descriptions is documented as "Append an Archivist section to
the most likely description field," but projectDescription() only
appended for HTML-flagged slots (via mergeArchivistSection). For the
one plain-text slot in the registry (dnd5e/pf2e Actor
system.details.notes, adapter-registry.js html:false) — which becomes
the top candidate for actor subtypes lacking a biography field, e.g.
dnd5e's vehicle/group types — the code did stripHtml(archivistHtml)
with no reference to the field's existing content at all, silently
replacing whatever was there. This matches user reports of losing
existing descriptions.

Added mergeArchivistPlainSection (merge.js), which does for plain-text
fields what mergeArchivistSection already did for HTML ones: wrap the
Archivist content in a recognizable marker, append it if none exists
yet, and replace only that marked block on future syncs — never
touching the rest of the field's content.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Quests were already being auto-imported exactly like Factions/Recaps —
_importArchivistMissing() pulls them in unconditionally, no reconcile/
selection step, and the count was already being computed
(count.imp.quests / plan.importFromArchivist.quests). They just never
made it into the summaryRows array the Step 6 table renders from, so
the count was invisible before clicking "Start Sync." Added a Quests
row identical in shape to the Recaps row, and updated Step 4's helper
note (which named Factions/Recaps as auto-imported) to mention Quests
too.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Two real bugs, both regressions from the Quests/image-upload work:

1. The upload-local-image button reused .archivist-edit-toggle's exact
   `position: absolute; top: 8px; right: 8px` slot with no offset, so
   it rendered directly on top of the edit-mode toggle — completely
   covering it. That's why editing a Faction (or any Character/Item/
   Location) sheet looked impossible: the button was there, just
   unclickable underneath the newer one. Added a distinguishing
   .archivist-upload-toggle class with `right: 48px` so they sit side
   by side.

2. _onRender fired _renderLinkedGrids()/_renderActorItemCards()/
   _renderRelatedQuests() without awaiting them, all async (dynamic
   import + DOM work). Verified against Foundry's own core source
   (client/applications/api/application.mjs): _render() does
   `await this._doEvent(this._onRender, ...)`, but since our _onRender
   wasn't declared async, that await resolved as soon as the
   synchronous body returned — before grid population finished. Opening
   a sheet by clicking a card in another custom sheet chains
   .render({force:true}).then(bringToFront) right after that early
   resolution, so the window could appear with cards populated but tab
   counts (the "(N)" suffix, set later by the same population pass)
   never applied — a real, reproducible gap, not just a cosmetic flash.
   Made _onRender (base class, plus the Location and Quest overrides
   that call super._onRender) async and properly awaited so Foundry's
   pipeline doesn't consider the render complete until population
   actually finishes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…l work

A collaborator independently built the same quest/journal feature set
on his fork (upstream/quest-journal-review-fixes, from the same 40d439f
base). His module.json/compatibility changes and the ask-chat-sidebar-tab.js
deletion are NOT ported — that branch reintroduces the exact incident this
whole effort started from (a v14-only build with compat.minimum low enough
for v13 clients to install, missing the Sidebar.TABS registration v13
actually needs). But several of his bug fixes are real, verified
independently against this branch's actual call sites, and are ported here:

- sync-dialog.js: journal content was being dropped entirely on import/sync
  (read row.description/row.summary but never row.content, which is where
  Journal's actual content lives) — the most serious bug found. Also:
  shift+click range-select was getting flipped back by the row-level click
  handler (missing stopPropagation); journal names were interpolated
  unescaped into the delete-confirmation dialog's HTML; no folder fallback
  for journals/quests on worlds that predate this feature, landing them in
  the world root. Wholesale-replaced with the fixed version, then re-added
  this branch's own relatedEntityRefs hydration (needed by the richer
  quest-linking Links tab) which his simpler version doesn't populate.
- world-setup-dialog.js: added _updateSyncStatusUI() (direct progress-bar
  DOM update) to the six "always imported in full" loops (characters,
  items, locations, factions, journals, quests), replacing a full
  await this.render() per entity — a real perf cost on large imports.
- archivist-sync.js: the realtime page-content-edit hook was PATCHing the
  quest's unchanged name back to the API on every content keystroke commit;
  quest content isn't a synced field and renames are already handled by a
  separate title-sync hook, so this was pure waste. Now a no-op return.
- archivist-api.js: _normalizeQuestPayload rewritten from a blocklist
  (rename+delete known keys, pass everything else through) to a strict
  whitelist, matching QuestCreate/QuestUpdate's extra="forbid" schema.
  Verified no current call site was actually vulnerable (all send narrow
  literal payloads), but the whitelist is a strictly safer contract for any
  future caller — verified functionally: read-only fields (questGiverId,
  progressLogEntries, first/lastSession) are dropped, empty-text objectives
  are dropped (API requires min_length=1), relatedEntityRefs' nested keys
  stay camelCase as the backend's alias_generator expects. Also fixed the
  keepalive guard: Chromium rejects keepalive fetches over ~64KB, so it's
  now conditional on body size instead of unconditional for all writes.
- merge.js: stripHtml no longer calls foundry.utils.TextEditor.stripHTML,
  which doesn't exist in either v13 or v14 core (verified against the
  actual installed Foundry app) — always fell through to the manual
  DOM-based strip anyway, just via a dead branch first.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: camrun01 <cameron.b.llewellyn@gmail.com>
Adopts camrun91's approach: QuestPageSheetV2._localize(key) tries
game.i18n.localize() and returns null (not the raw key) on a miss or
when i18n isn't ready yet, so callers can chain `|| fallback` cleanly.
statusLabel/categoryLabel now try ARCHIVIST_SYNC.quest.status.*/
category.* (already present in lang/en.json from the earlier merge of
his branch's new keys) before falling back to the hardcoded English
strings that were the only source before.

Not in scope here: the edit-mode <select> option text in quest.hbs
(questCategory/status dropdowns) — those don't exist on his branch at
all (it kept the free-text Info tab instead of the structured form),
so there's nothing of his to adopt for them; they're still hardcoded
English, same as before this change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: camrun01 <cameron.b.llewellyn@gmail.com>
camrun91 and others added 5 commits July 31, 2026 16:11
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
…n API rules

- Add _invalidateQuestsCache() and call from _commitEdits (quest),
  _commitQuestLinks, and _syncObjectives so related-quest tabs refresh
  immediately after linking or editing
- Correct 40-archivist-api.mdc to document that _normalizeQuestPayload
  emits snake_case on the wire while _normalizeQuestResponse ingests
  camelCase internally

Co-authored-by: Cameron Llewellyn <cameron.b.llewellyn@gmail.com>
Read form objectives before building the commit payload so _commitEdits
sends a single atomic PATCH covering all editable quest fields, avoiding
local/API divergence when per-field objective sync calls fail silently.

Co-authored-by: Cameron Llewellyn <cameron.b.llewellyn@gmail.com>
Finish Quests feature, add image upload + link PATCH, fold in review fixes from #46
@camrun91

camrun91 commented Aug 3, 2026

Copy link
Copy Markdown
Owner Author

these were pulled into another PR

@camrun91 camrun91 closed this Aug 3, 2026
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.

3 participants