Skip to content

Align with TPEN messaging contract — opt into TPEN_HYDRATED_CONTEXT#16

Merged
thehabes merged 5 commits into
mainfrom
5-2-tools-align
May 8, 2026
Merged

Align with TPEN messaging contract — opt into TPEN_HYDRATED_CONTEXT#16
thehabes merged 5 commits into
mainfrom
5-2-tools-align

Conversation

@thehabes
Copy link
Copy Markdown
Member

@thehabes thehabes commented May 8, 2026

Summary

Align TPEN-Prompts with the canonical TPEN messaging contract. The lean TPEN_CONTEXT boot payload doesn't carry the populated project/page/canvas objects this tool needs for prompt-template rendering, so on receipt of TPEN_CONTEXT it now sends REQUEST_POPULATED_PROJECT and REQUEST_POPULATED_PAGE upstream and renders once both TPEN_POPULATED_PROJECT and TPEN_POPULATED_PAGE replies have arrived. The full contract is defined in CenterForDigitalHumanities/TPEN-interfaces#564.

Changes

  • message-handler.js
    • New TPEN_POPULATED_PROJECT and TPEN_POPULATED_PAGE cases. The handler accumulates both halves into a populated bag and only calls app.acceptContext({ project, page, canvas, currentLineId }) once both have landed — so the workspace renders once, with the full bundle.
    • TPEN_CONTEXT case now triggers requestPopulatedContext(), which posts both REQUEST_POPULATED_PROJECT and REQUEST_POPULATED_PAGE.
    • The previous requestHydratedContext() / TPEN_HYDRATED_CONTEXT path is removed.
  • main.js
    • Module docstring and acceptContext JSDoc updated to describe the split-then-bundle boot flow.

Coordinated cut

Hard cut, all PRs must merge together. Full contract and other PRs:

Test plan

Developer-validated locally on 2026-05-08 against CenterForDigitalHumanities/TPEN-interfaces#564 on :4000, services on :3012:

  • In TPEN-interfaces (jekyll s), add TPEN-Prompts to a project's tools. Open /transcribe.
  • Awaiting screen renders, then templates render once both populated replies have arrived.
  • DevTools: exactly one TPEN_CONTEXT (parent → tool), then exactly one REQUEST_POPULATED_PROJECT and one REQUEST_POPULATED_PAGE (tool → parent), and one TPEN_POPULATED_PROJECT + one TPEN_POPULATED_PAGE reply (parent → tool). No re-requests on the same boot.
  • Click a line in the parent → UPDATE_CURRENT_LINE arrives and the tool's current-line state updates.
  • Click "Authenticate" in the awaiting screen → outbound REQUEST_TPEN_ID_TOKEN, parent toast confirms, inbound TPEN_ID_TOKEN, prompts use the token.
  • Standalone mode (open the tool URL with ?projectID=...) still works.
  • Generated prompt copies to clipboard. The async Clipboard API is denied by the parent's iframe Permissions Policy (no allow="clipboard-write" on the parent's <iframe> element), so the existing document.execCommand('copy') fallback engages and copy succeeds. The [Violation] console warning is benign — kept the fallback rather than expanding the contract scope.

The parent now ships a lean TPEN_CONTEXT (project identity + URIs) on
boot. Prompt templates need fully-hydrated project/page/canvas, so on
receipt of TPEN_CONTEXT we send REQUEST_HYDRATED_CONTEXT upstream and
acceptContext() now consumes TPEN_HYDRATED_CONTEXT instead.

init() also requests hydration eagerly to handle the case where the
parent posted TPEN_CONTEXT before the listener was wired up; duplicate
requests are cheap and idempotent on the parent side.
The MessageHandler listener attaches in the PromptsApp constructor at
DOMContentLoaded, before the parent's iframe load event fires — so the
'listener not yet wired up' scenario the eager call was guarding
against can't actually happen. The TPEN_CONTEXT case in MessageHandler
is the single source of truth for kicking off the hydration handshake;
exactly one round trip per boot.
@thehabes thehabes marked this pull request as ready for review May 8, 2026 17:53
@thehabes thehabes requested a review from cubap May 8, 2026 17:53
Comment thread main.js Outdated
// a `TPEN_CONTEXT` message that lands during `await initTemplates()`
// renders the workspace, and then this init() continuation silently
// overwrites it with the awaiting screen.
// a `TPEN_HYDRATED_CONTEXT` message that lands during
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm. This is kind of describing what happens when it launches, but the hydrated context isn't actually in the first init, so the comment maybe doesn't belong here. The whole block doesn't seem much like jsdoc and more like narration. The async story feels like thinking, not documenting.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — trimmed the init() JSDoc to one line and tightened the inline 'paint sync' comment to just the load-bearing why in f49ba2a.

Comment thread main.js
* @param {string|null} lineId full line IRI or null.
*/
updateCurrentLine(lineId) {
this.ui.updateCurrentLine(lineId ? (trailingId(lineId) ?? '') : '')
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a lot of support here for showing what line you are on, but I don't think it is ever used for anything...

updateCurrentLine(lineID) {
        this.state.lineID = lineID ?? ''
        this.state.line = lineID && this.state.page?.items
            ? (this.state.page.items.find(it => trailingId(it) === lineID) ?? null)
            : null
        if (this.#lineMetaValue) {
            this.#lineMetaValue.textContent = this.state.lineID
                ? labelOf(this.state.line, this.state.lineID)
                : '(none selected)'
        }
    }

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's used by ui-manager.js — state.lineID and state.line drive the workspace's 'Line' meta row (line 377 / 703-712) so it stays in sync as the user navigates lines in the parent. Templates don't reference it directly today, but state.line is exposed for any future template that wants the active annotation. Happy to remove the machinery if you'd rather not carry that surface area.

Comment thread message-handler.js Outdated
* `REQUEST_HYDRATED_CONTEXT` reply and the parent answers with
* `TPEN_HYDRATED_CONTEXT` carrying the hydrated objects.
*
* Line navigation arrives as `UPDATE_CURRENT_LINE` deltas. The token is
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mentioned elsewhere, but why is current line relevant?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reworked the file docstring in f49ba2a — it no longer enumerates the protocol. The file just routes inbound parent messages into PromptsApp; relevance of any particular message lives at the call site.

Comment thread message-handler.js Outdated
case 'TPEN_CONTEXT':
// Lean payload (project identity + URIs). Templates wait for
// TPEN_HYDRATED_CONTEXT, so we just request hydration here.
this.requestHydratedContext()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks 👍 — note that this case has since rewritten in f49ba2a. The lean TPEN_CONTEXT arrival now triggers requestPopulatedContext() (which posts both REQUEST_POPULATED_PROJECT and REQUEST_POPULATED_PAGE), so the inline comment got slimmer too.

thehabes and others added 3 commits May 8, 2026 15:13
Replace REQUEST_HYDRATED_CONTEXT / TPEN_HYDRATED_CONTEXT (one envelope
with a mismatched grab-bag shape) with two request/reply pairs whose
names match their projection: REQUEST_POPULATED_PROJECT →
TPEN_POPULATED_PROJECT, REQUEST_POPULATED_PAGE → TPEN_POPULATED_PAGE.

MessageHandler accumulates both halves into a populated bag and only
calls app.acceptContext once both have arrived, so the workspace
renders once with the full bundle. Mirrors parent change in
CenterForDigitalHumanities/TPEN-interfaces#564.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cubap flagged the init() JSDoc and the message-handler file-level
docstring as narration rather than documentation. Trim both:

- main.js: init() JSDoc reduced to one line; the inline "paint sync"
  comment kept (the WHY is non-obvious) but tightened.
- message-handler.js: file-level docstring no longer enumerates the
  whole protocol. Describes what this file does (route inbound parent
  messages into PromptsApp) and keeps the non-obvious parentOrigin
  capture rule.

Addresses cubap's review on PR #16.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the partial has* flag reset with a full accumulator reset, so
stale project/page/canvas/currentLineId fields can't leak into a future
bundle if the flush gate is ever loosened.
@thehabes thehabes merged commit 110d61f into main May 8, 2026
@thehabes thehabes deleted the 5-2-tools-align branch May 12, 2026 18:16
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