Skip to content

T3-26.33 Prod Release - #235

Merged
qiyundai merged 45 commits into
mainfrom
stage
Aug 12, 2026
Merged

T3-26.33 Prod Release#235
qiyundai merged 45 commits into
mainfrom
stage

Conversation

@qiyundai

Copy link
Copy Markdown
Collaborator

Release notes

Quoting T3-26.33 prod deployment.

  • Tier 1 Event Configurator: new DA-hosted app for authoring an event's RainFocus API URL and profile ID, now wired into the Sessions Guide block for real ESL session data (MWPW-200314)
  • Session Guide Configurator: new DA-hosted app for authoring Sessions Guide content (MWPW-194336)
  • Mobile-rider C2 block: video/audio drawer block with rounded player corners
  • Block hydration for consumer-owned blocks: hydration is now synchronous and supports blocks owned outside event-libs (e.g. da-bacom's event-speakers) via registerHydrator
  • Sessions-guide: hardened carousel row sizing, session filters, and time/track utilities with expanded test coverage
  • esp-controller: campaign GET call now points from ESL to ESP
  • Vertical spacing authoring guidance added to CLAUDE.md (MWPW-201396)

sharmeebuilds and others added 30 commits July 20, 2026 11:49
Records the Design/Consonant/Authoring decision that vertical page
spacing belongs in Figma components, not hard-coded in block
CSS/JS, so authors keep flexibility unless a baked-in value is
documented.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Extends the hydrator so it can hydrate blocks event-libs does not own, and adds
da-bacom's `event-speakers` as the first such block. The hydrator fabricates the
authored-looking rows the existing block already knows how to decorate; no block
rendering is built or changed.

Hydration is now fully synchronous. Milo calls the configured decorateArea for
fragments and personalization without awaiting it and discards the return value,
so there is no entry point at which a hydration promise could be awaited — any
asynchrony, a dynamic import() included, races the block's own init(). Hydrators
are therefore statically imported and invoked synchronously, and the now-dead
getHydrationPromise/setHydrationPromise pair and image-links' await of it are
removed. A regression test asserts the DOM is hydrated on the statement
immediately after hydrateBlocks returns.

- registerHydrator(name, fn) lets a consumer supply a hydrator for its own block.
  Rejects async functions and non-functions, returns a boolean, warns on replace.
- Successfully hydrated blocks are marked data-hydrated and skipped by later
  passes, so a re-entrant decorateEvent over a nested area cannot wipe the DOM a
  block's init() already built. A throwing hydrator is left unmarked and retried.
- logHydration buffers until window.lana exists, since hydration runs before
  consumers call loadLana and messages would otherwise be dropped.
- The event-speakers hydrator clears authored placeholder rows even on its
  bail-out paths: the block throws on a row with fewer than two cells but
  initializes cleanly with none. Name/title/company are set as text, since
  createTag parses its third argument as HTML.

Tests cover the four-cell contract, data-shape variants, bail-out paths, and an
integration check that runs da-bacom's real block init against hydrated DOM.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
docs: add vertical spacing guidance from MWPW-201396
…g DOM

No content may originate in code — including a label like "Read more". The
hydrator now repeats an authored template row rather than constructing rows and
reading field values itself.

The author writes one row of [[collection.field]] placeholders plus any static
text; the new shared repeatTemplate helper clones that row per item and rewrites
each clone's placeholders to [[collection:index.field]]. decorateEvent's existing
processTemplateInAllNodes resolves them in the same pass — hydrateBlocks runs at
the top of decorateEvent, placeholder resolution later in it — so hydration is
structure-only and every value flows through the normal decoration path.

Consequences:
- The event-speakers hydrator collapses to its selection rule (filter by variant
  class, sort by ordinal). It no longer builds cells, reads speaker fields, or
  supplies a "Read more" label; authors control all of it, including the label.
- Placeholders are authored without an index. Images bind via the alt attribute,
  as elsewhere in event decoration.
- Reuses parseMetadataPath's existing :index array syntax and updateImgTag's
  photo handling rather than duplicating either.
- A hydrated block can no longer be empty: with no template row there is nothing
  to repeat, and the hydrator leaves the block alone. Bail-out paths still clear
  the template so it cannot render literal [[tokens]].
- Drops the now-unneeded field normalization, ordinal-sort copy, and textContent
  hardening, since the hydrator never touches values.

Adds an end-to-end test in decorate.test.js asserting the authored template
resolves to real speaker content with no placeholders left behind, alongside unit
coverage for repeatTemplate.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…repeatTemplate

Addresses review feedback that bundling da-bacom's event-speakers hydrator in
event-libs inverted the ownership boundary: event-libs held detailed knowledge of
a block's cell layout and failure modes that it does not own.

event-libs now owns the data and the mechanism; each consumer owns its own blocks'
DOM. The event-speakers hydrator moves to da-bacom, beside its block, and is
registered from da-bacom's scripts.js via the registerHydrator API that already
shipped here. HYDRATORS holds only event-libs' own blocks again.

Validated against Milo that a single page-startup registration covers every
hydration pass, which was the open question:
- Milo invokes the configured decorateArea in exactly two further places,
  blocks/fragment/fragment.js and features/personalization/personalization.js.
- The registry is module state on the event-libs instance, so it outlives the
  initial page load and still applies to both.
- The fragment path passes a detached DOMParser document; hydrators still resolve
  metadata there because getMetadata reads the main document by default.
Both properties are now covered by tests.

- Export repeatTemplate from libs.js as intentional public API for consumer
  hydrators; it was previously internal.
- Delete v1/hydrate/consumers/ entirely.
- Retarget the hydrate tests at image-links and generic registered blocks so no
  event-libs test depends on a consumer's markup.
- The end-to-end decorate test now exercises the full consumer path — register a
  selection-only hydrator, then assert the authored template resolves to real
  content with no placeholders left behind.
- Document registerHydrator as the pattern for all consumer-owned blocks, and
  state the ownership boundary up front so HYDRATORS is not copied for future ones.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Bugs found by review, each now covered by a test:

- setTokenIndex corrupted a collection whose name merely started with the template
  collection's: [[speakersExtra.name]] became [[speakers:0Extra.name]]. Now requires a
  full-segment match.
- Collection derivation took the first indexless token without checking it named an
  array, so a row leading with a page-level placeholder such as [[event-title]] — or a
  misspelled collection — silently removed every row with nothing logged. It now skips
  tokens that don't resolve to a metadata array, making position irrelevant, and logs
  when nothing matches.
- hydrateBlocks marked a block data-hydrated even when the hydrator bailed out, so a
  block whose data wasn't ready on the first pass was never retried on the fragment or
  personalization pass. Marking is now gated on the hydrator not returning false.
- repeatTemplate returned true when every item was skipped, reporting success for an
  empty block. It now returns whether anything rendered.
- Dropped the redundant img[alt] rewrite pass; rewriting innerHTML already covers
  attributes.
- Extra templated rows and selectItems returning copies were both silent; now logged.

Test and doc fixes:

- Pin the selectItems(items, block) contract, including a block-class-driven selector.
  da-bacom's hydrator reads the second argument, and nothing asserted it existed —
  renaming or reordering it would have shipped green and rendered every speaker in
  every variant block.
- Assert libs.js still exports the hydration API. No test imported the barrel, so
  dropping an export would have silently disabled hydration for every consumer.
- Add resetHydrators() for tests. The registry is module state, and cleanup previously
  lived in a different describe than the test depending on it.
- Stop asserting on dataset.hydrated in a test whose fixture sets it; that is
  hydrateBlocks' own flag, so it passed even for a hydrator that never ran.
- Fix the doc's registration example, which registered the factory instead of the
  hydrator it returns — accepted by registerHydrator and then silently a no-op. Replace
  the invalid template-literal static import, and explain why injection is used.
- Correct two false doc claims: data-hydrated is not set only on success (it is now),
  and event-speakers does fall back to a hardcoded "Read more" when cell 4 is left
  empty — authors are now told to author it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Third review pass, on the token-rewriting rules.

setTokenIndex skipped any token containing ':', which was too broad. It caught the
case it meant to — an author who already indexed the collection — but also caught
nested per-item paths. [[speakers.socialMedia:0.link]] stayed un-indexed, so it
resolved against the whole speakers array and rendered empty identically on every
row; and when such a token was a row's only one, findCollection skipped it too and
the block rendered nothing. Both now work: the guard only skips a ':' at the
collection position.

Per-item conditionals genuinely cannot work and are now logged rather than failing
silently. Indexing them doesn't help: CONDITIONAL_REG's condition path excludes ':',
so [[speakers:2.isVip?(x):(y)]] parses its condition as "2.isVip" (verified against
constances.js). Leaving them unindexed evaluates against the whole collection, which
renders the same branch on every row. Neither is right, so warn and leave as-is.

Also document that a hydrated image must be a real authored image: an unwrapped <img>
can never resolve, and decoration removes its parent — for a bare <div><img></div>
that deletes the cell and shifts the positions event-speakers reads.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Review asked whether a single page-startup registration stays valid for the fragment
and personalization passes, since Milo re-invokes decorateArea for both. It does, but
that was only ever verified by hand — now it's a test, so a future change can't quietly
break the assumption the whole registerHydrator design rests on.

Covers both halves: one registration serving all three passes, and a hydrator resolving
page metadata inside the detached DOMParser document the fragment path hands it (which
works only because getMetadata defaults to the main document).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Nothing here is minified, so every comment byte is downloaded by every visitor. The
hydration modules were comment-heavy — repeat-template.js was a third comments — so they
now carry none, and the rationale moves to docs/block-hydration.md, which is not shipped.

hydrate.js 2991 -> 1782 bytes, repeat-template.js 6347 -> 3096, log.js 420 -> 206.

The doc gains a "Token rewriting rules" section: a table of every authored token shape and
what it becomes, plus the three invariants that were previously only recorded in comments —
innerHTML rewriting is deliberate because it also covers attributes (how images bind),
META_REG is stateful so never use .test(), and selectItems must return the original item
objects. Each is covered by a test.

Also hoists the inline indexed-token regex to a named constant, since the comment that
explained it is gone. Pre-existing comments in files this branch merely touched are left
alone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- image-links.js: return false on every non-rendering early exit (and
  when every item is skipped) so a block that failed to hydrate can be
  retried on a later pass, instead of being marked data-hydrated
  permanently.
- hydrate.js: guard the built-in HYDRATORS lookup with Object.hasOwn so
  a block classed e.g. "constructor" can't resolve an inherited
  Object-prototype property as a hydrator.
- hydrate.js: replace the IIFE-tuple with a module-level Map and
  function declarations for registerHydrator/resetHydrators, matching
  this repo's top-level-exports-use-function convention.
- repeat-template.js: select the template row as the first
  token-bearing row whose collection actually resolves, instead of
  always the first token-bearing row — a leading conditional/@-only
  row no longer causes every row to be wiped. Tokens are now computed
  once per row and reused, rather than re-scanned in findCollection.
- repeat-template.js: scope row removal to token-bearing rows only, on
  both the bail-out and success paths, so a static authored row (e.g.
  a header) survives hydration instead of being deleted alongside the
  template.
- log.js: when window.lana isn't ready and document.readyState is
  already 'complete' (a later fragment/personalization pass, long
  after the page's one-time load event), poll briefly for lana instead
  of relying solely on a load listener that will never fire again.

Adds regression tests for each fix.
* feat(sessions-guide): expose openSessionGuideDetail for cross-block use

Adds a sessionGuideRequest signal + openSessionGuideDetail(sessionId) export
in session-store.js so any block can open Session Guide directly to a
session's detail view without duplicating its internal drawer/URL logic.
DrawerShell subscribes and reacts by opening the drawer, setting the active
session, and updating the URL (preserving existing deep-link behavior).

MWPW-200852

* refactor(sessions-guide): dedupe defaultView ternary via getDefaultView()

Was duplicated 4 times across DrawerShell.js (3 pre-existing + 1 added by
this PR); extracted into a single helper per PR review feedback.

MWPW-200852
## What this changes

Tier 1 event pages can now make live RainFocus (RF) calls for a visitor's real schedule and favorites, closing [MWPW-200311](https://jira.corp.adobe.com/browse/MWPW-200311).

## How it works now

- **Configuration**: `rfApiUrl` and `rfProfileId` are two optional fields inside the Tier 1 Event Configurator's `config` JSON (the same payload authors paste into a page's `tier-1-event-config` metadata, alongside `trackIcons`/`featuredSessions`/etc.). If either is left blank, the page falls back to a hardcoded default profile id and an environment-aware RF endpoint — prod pages hit RainFocus's prod endpoint, everything else (including local dev, since it authenticates against stage IMS) hits the stage endpoint.
- **Real login, real data**: on page load, once the session catalog has loaded and the visitor's real IMS profile confirms they're logged in, the code exchanges their IMS user id for a RainFocus auth token, then fetches their actual schedule and favorites in one call and maps them onto the page's local session ids. Scheduling or favoriting a session now sends that same real token to RainFocus instead of a placeholder. A visitor's registration status is derived from RainFocus's own response, since RSVP data doesn't apply to Tier 1 events.
- **No more mock data**: the dev-only scaffolding (seeded fake schedule/favorites, localStorage persistence, a local-only auth fallback) has been removed entirely — a real IMS profile and real RainFocus responses are the only source of truth now.
- **Full endpoint parity**: the rest of RainFocus's endpoint contract (fetch-only schedule/favorites, dropping and swapping a session, attendee access) was ported from the legacy northstar integration for future use, though nothing calls them yet.

## Follow-up (not blocking)

The backend/MCP-tool consumer mentioned in the ticket lives in a separate repo — out of scope here. The auth-token endpoint's exact response field name is unconfirmed (no prior real traffic to verify against); the code tries the likely candidates.
…ESL session data

Wires sessions-guide's consuming side to the Tier 1 Event Configurator (allowDoubleBooking, featured sessions, eventId cross-check), replaces the mock session catalog with a real ESL/ESP fetch, and adds a page-wide track icon/color system and session-state ticker.

Partial: two known issues (stale mock-fallback in sessions-api.js, missing null-guard in icon-resolver.js), real Mobile Rider API calls, live-page verification, and doc consolidation are deferred to a follow-up PR.
feat(hydrate): support consumer blocks and make hydration synchronous
[MWPW-200845]:- Mobile-rider c2 block and border-radius changes
[MWPW-200847, MWPW-200846]:- Rounded Corners on (MPC/YouTube) Player
Add chrono-box to C2 block list
Adds a horizontal speaker-row layout (circular avatar + name/title/bio
with a Read more/Collapse toggle) to match the updated BACOM/EMC speaker
section pattern in Figma, reusing the existing grid two-up breakpoints
for the 2-up wrap and gating out modal/carousel/single behavior that
doesn't apply to this variant.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Extend the `theme` custom attribute to accept dark(blocks:name1,name2)
syntax so authors can theme specific blocks instead of the whole page.
Plain `dark`/`light` keeps today's whole-page behavior unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add missing coverage for exact-token block matching, case-insensitive
block names, and the element-scoped (non-document) call path that
production actually uses. Tighten the parseThemeValue comment to
explain the empty-vs-undefined blocksParam invariant instead of
restating what the code already says.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Puts the button on its own line below the bio text with no left
margin and regular (non-bold) weight, matching design feedback.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Replaces the 144/292-character JS truncation with the same
-webkit-line-clamp: 2 + scrollHeight/clientHeight overflow-detection
pattern already used by sessions-hub, so the Read more toggle only
appears when the bio actually overflows two rendered lines rather
than crossing an arbitrary character threshold. Also lets blade bios
keep any HTML formatting, since visual truncation is now handled by
CSS rather than JS string slicing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
qiyundai and others added 15 commits August 5, 2026 17:19
Explicitly matches --type-body-xs-size/-lh (14px) instead of relying
on inherited font-size, consistent with the surrounding card-title/
card-desc text.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Drops the redundant unprefixed line-clamp declaration (only
-webkit-line-clamp is supported broadly enough to rely on, matching
the existing mobile-rider/drawer.css convention) and stops toggling
display between -webkit-box and block on expand/collapse, since that
switch was changing the box's margin-collapsing behavior between the
two states.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add blade variant to profile-cards for BACOM speaker section design
Add block-scoped theme syntax to custom-attributes theme value
* feat(session-guide-configurator): scaffold the DA app (MWPW-194336)

Preact + HTM, no build step, following Tier 1 Event Configurator's
precedent (custom design system, DA-sheet-backed config library) blended
with Schedule Maker's URL/auto-block consumption model per PLAN.md.

- constants.js / scripts/da-controller.js: configId-keyed CRUD, not
  eventId-keyed — an event can have multiple configs (widget/page variants,
  test copies; PLAN.md 2/5), so there's no event-dedup logic to mirror from
  Tier 1 Event Configurator here.
- Four contexts: DAContext (DA SDK auth), NavigationContext, EventEnvContext
  (ESP tier picker), ConfigsContext (sheet CRUD + active config state,
  including startDuplicateConfig's in-place clone with a "(copy)"-suffixed
  Component name default).
- Library.js/ConfigEditor.js are functionally complete for what's built so
  far: New (via EventPicker/ManualEventLookup), Duplicate (clones in-place,
  no event re-picking - a real requirement difference from Tier 1's
  cross-event Duplicate), Edit, Delete, search, and Save for Component
  name/Page mode/Theme.
- EventPicker/ManualEventLookup/Modal/SearchInput/LoadingInline are local
  copies for now, not shared - found they're more tightly coupled to Tier 1's
  shipped CSS (hardcoded tec- classes) than the plan assumed. Promoting them
  means renaming classes in Tier 1's already-shipped 934-line stylesheet;
  deferring that rather than risking it mid-build. Flagged inline in each
  file.

Also extracts v1/utils/da-sheet-controller.js: the generic DA sheet-CRUD
primitives (daFetch/readSheet/writeSheet/mutateSheet), shared by this app
and Tier 1 Event Configurator's own da-controller.js (updated to build on
it instead of duplicating), rather than becoming a third independent copy.
Also removes a resulting dead export (DA_ADMIN_ORIGIN) from Tier 1's
constants.js.

Not yet built: Headings, Behavior Flags, Filters, Swimlane ordering, the
Tier 1 config lookup/display, and the Copy Link export.

Verified: lint clean, full suite 984/984 passing (unaffected — this app has
no unit tests yet, same as Tier 1 Event Configurator's own convention of
manual/local-harness testing), and the entire new import graph resolves
cleanly via a Node syntax/import check.

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

* feat(session-guide-configurator): default New config to a Tier 1 config picker

PLAN.md §2 already established that a Session Guide config should hook
into an existing Tier 1 Event Configurator config as its event source, but
the scaffold's New config flow still defaulted to ESP browse/manual entry.
Flips the default: New config now opens Tier1ConfigPicker (lists events
that already have a Tier 1 config, reusing Tier 1's own getConfigs()/
getDisplayTitle() directly - a deliberate cross-app dependency, not a
duplication risk like TRACK_ATTRIBUTE_NAME was, since this integration is
the explicit design). EventPicker (ESP browse) and ManualEventLookup are
still available as an explicit fallback, via a "pick it another way" link,
for an event that doesn't have a Tier 1 config yet.

Library.js's picker gains a third mode ('tier1' | 'browse' | 'manual',
defaulting to 'tier1') instead of the old binary EVENT_BROWSE_ENABLED/
browseFailed switch. handlePickEvent now accepts an optional
eventServiceEnv (supplied only by Tier1ConfigPicker, from the picked
event's own authored env) and syncs it into the global env context before
creating the new config, same reasoning as openEdit/handleDuplicate's
existing env-restore logic.

Verified: lint clean, full suite 984/984 passing (unaffected), new
cross-app import paths confirmed resolving via a Node import check.

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

* feat(session-guide-configurator): filters, swimlane order, and export plumbing (MWPW-194336)

Adds FiltersEditor/SwimlaneOrderEditor and wires them into ConfigEditor along
with the Copy Link export action. decorate.js's prebuildAutoBlock gains a
sessions-guide auto-block builder that decodes the encoded config and picks
the widget/full-page block class; parse-config.js now reads
data-session-guide-config instead of the old authoring table, with no
fallback. The new authored filterCategories shape is kept under
authoredFilterCategories to avoid colliding with FilterPanel.js's still-legacy
filterCategories key until that consuming side is rewired (separate work).

* docs(session-guide-configurator): add consuming-side handoff checklist

Consolidates the deferred DrawerHeader/FilterPanel/OnDemandView/behavior-flag
wiring into MWPW-194336-CONSUMPTION-HANDOFF.md, mirroring the
MWPW-200314-HANDOFF.md precedent from the Tier 1 Event Configurator, so it's
a trackable checklist instead of scattered PLAN.md prose.

* feat(session-guide-configurator): allow deselecting swimlanes/tracks

Authors can now uncheck a track in the swimlane order editor to drop it
from the rendered guide entirely, not just reorder it. swimlaneOrder's
shape changes from a plain track-name array to [{ track, enabled }],
mirroring filterCategories' enable/reorder pattern minus the rename field.
Updates the consuming-side handoff doc (item 3) and PLAN.md's data model
to match — the disable behavior is authoring-side only for now; wiring
OnDemandView.js to actually drop disabled tracks is still deferred,
separate work.

* feat(session-guide-configurator): swimlane rename + show original value

Swimlanes gain the same rename capability filters already had: each
swimlaneOrder entry now carries a `displayName` (defaults to the raw
`track` value, author-editable), alongside `track`/`enabled`. Filters
gained a matching `label` field, kept alongside `displayName` since it
was previously being overwritten and lost on save.

Both editors now show the original value (track name / ESP label) next
to the editable field, so authors can see what they're overriding.
FiltersEditor.js also gained its own CSS block (it had none before) and
an is-disabled row treatment matching the swimlane editor's.

Updates PLAN.md's data model and the consumption handoff doc's item 1/3
field shapes to match.

* fix(session-guide-configurator): backfill label/displayName on reseed

seedFilterCategories/seedSwimlaneOrder only touched genuinely-new entries
when merging in live session data — an entry already seeded before the
label/displayName fields existed (any config created before this session's
earlier commits) passed through stillValid untouched, leaving label/
displayName undefined forever. That's why "original name" rendered blank
for existing filters. Both now backfill the missing field from the live
candidate/track data whenever it's absent, on the next seed pass.

* feat(session-guide-configurator): copy a rich hyperlink, not a bare URL

Copy Link now writes an actual <a> element (via a text/html ClipboardItem)
to the clipboard instead of the plain URL string, same technique as
Schedule Maker's ScheduleURLUtility.copyScheduleToClipboard. Pasting into
DA's rich-text editor drops in a working link labeled "Session Guide:
{title} – {updated date}" instead of a wall of base64 text. Falls back to
copyTextToClipboard's plain-text path when the rich-clipboard write API
isn't available.

* feat(session-guide-configurator): add Copy link to each library row

Lets authors grab a saved config's link straight from the library list
without opening it in the editor first — same rich-hyperlink copy logic
as ConfigEditor.js's Copy Link (copySessionGuideConfigLink()), just
operating on a row from the list instead of activeConfig.

* refactor(session-guide-configurator): trim comments, dedupe copy-link logic

Comment cleanup pass across the app and this PR's changes to shared
files: trimmed narrative comments (decision history, ticket/PLAN.md
references, cross-tool attribution) down to the actual invariant, or
removed them where the code already reads clearly. No behavior changes.

Also collapsed ConfigEditor.js's and Library.js's near-identical
handleCopyLink implementations into one shared copyRowLinkWithToast()
in utils.js.

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
[Dev → Stage] Tier 1 Event Configurator, sessions-guide live data, chrono-box/mobile-rider C2 blocks, profile-cards blade variant
The overflow check ran via a one-shot double-rAF measurement right after
render, before the page's web font finished loading, so it could measure
against fallback-font metrics and freeze in a wrong "truncated" state
forever. Mirrors the fix already used by sessions-hub: wait for
document.fonts.ready before the first measurement, then back it with a
debounced ResizeObserver so later layout shifts get re-measured.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fix blade profile-cards Read more toggle showing for short bios
Extend dark(blocks:name1,name2) to accept name[first], name[last], and
name[N] (1-based index) so authors can theme one specific instance of
a repeated block, e.g. dark(blocks:text[first],agenda).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add positional selectors to block-scoped theme syntax
Dev -> Stage Fast Track Changes Follow
@aem-code-sync

aem-code-sync Bot commented Aug 12, 2026

Copy link
Copy Markdown

Hello, I'm the AEM Code Sync Bot and I will run some actions to deploy your branch and validate page speed.
In case there are problems, just click a checkbox below to rerun the respective action.

  • Re-run all PSI checks
  • Re-run failed PSI checks
  • Re-sync branch
Commits

@qiyundai
qiyundai merged commit cd77bff into main Aug 12, 2026
9 of 12 checks passed
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