Documentation menu
Documentation / Blocks & themes / blocks-org-pack
Org Blocks
npm install @wabbit/tome-blocks-org-packFirst install from our registry?
Get a registry token from your credentials page. You need a purchase that includes this package, or a Craft Library membership.
Add the registry and your token to the
.npmrcat the root of your project, with your token in place ofYOUR_TOKEN:@wabbit:registry=https://npm.wabbit.com/ //npm.wabbit.com/:_authToken=YOUR_TOKEN
Render-ready organization/community blocks — division card, member card, member grid, event calendar, live event list, event list item, org chart, document link, and campaign banner; static except the event list, which reads @wabbit/tome-org events through a server wrapper.
Overview
Organization block pack for the Tome platform — render-ready blocks (DivisionCard, MemberCard, MemberGrid, EventCalendar, EventList, EventListItem, OrgChart, DocumentLink, CampaignBanner) for org/community surfaces.
Config descriptors are exported from .; React render components from @wabbit/tome-blocks-org-pack/render. Every block except event-list is static: it renders exactly the props an author enters. event-list reads org events at render time through the server wrapper in @wabbit/tome-blocks-org-pack/server (see "Live event list" below) and falls back to its authored events. The pack carries no @wabbit/tome-org peer dependency: it detects the layer by name (isOrgLayerPresent/resolveOrgLayer in src/index.ts) and the live read uses the layer's default collection and field names, which can be overridden. The other blocks keep their static contract; trigger to hydrate one of them: a consumer needs it to render live @wabbit/tome-org data, then add it to the @wabbit/tome-blocks-org-pack/server wrapper.
Peer dependencies
On a Tome site (meta-package path)
This pack is a declared dependency of @wabbit/tome-blocks, and its renderers ARE registered by that package's registerAllRenderers() — no separate renderer wiring needed if you already call that. Its block descriptors are a different story: they are NOT re-exported from @wabbit/tome-blocks and NOT included in registerAll() (a pre-existing scope decision — see that package's README, "Public API" section). To add these blocks to a Payload blocks field, import the descriptors from this pack directly (or call this pack's own register()), even on a Tome site.
On a stock Payload site — add to an existing blocks field
@wabbit/tome-blocks-core is a required peer and installs automatically (npm 7+/pnpm). Add the blocks you want alongside your existing ones and render them — see @wabbit/tome-blocks-core's "Add Tome blocks to an existing Payload project" for the full walkthrough:
// payload.config.ts
import { memberCardBlock, eventCalendarBlock } from '@wabbit/tome-blocks-org-pack'
// blocks: [...existingBlocks, memberCardBlock.block(), eventCalendarBlock.block()]// blockComponents.ts
import { renderers as orgRenderers } from '@wabbit/tome-blocks-org-pack/render/register'
import { adaptRenderersForPayload } from '@wabbit/tome-blocks-core/render'
// blockComponents: { ...adaptRenderersForPayload(orgRenderers) }@import '@wabbit/tome-blocks-core/styles.css';Registering everything from scratch instead:
// payload.config.ts
import { BlockRegistry, BundleRegistry } from '@wabbit/tome-blocks-core/registry'
import { register } from '@wabbit/tome-blocks-org-pack'
const blockRegistry = new BlockRegistry()
const bundleRegistry = new BundleRegistry()
register(blockRegistry, bundleRegistry)
// Use blockRegistry.resolveAll() to get Payload Block configs| Peer | Range | Required |
|---|---|---|
payload | >=3.67.0 | yes |
react | >=19.0.0 | yes |
react-dom | >=19.0.0 | yes |
@wabbit/tome-blocks-core | >=0.22.0 <1.0.0 | yes |
@wabbit/tome-org is not declared as a dependency or peer: the pack never imports it, and detection goes through @wabbit/tome-core's layer registry by name — see the detection note above.
Block list
| Slug | Name | Notes |
|---|---|---|
division-card | Division Card | Org sub-unit/division summary card (default), or a program/team entry with a captioned photo, lead line, purpose, next date and link (entry) |
member-card | Member Card | Individual member profile card |
member-grid | Member Grid | Grid of member cards |
event-calendar | Event Calendar | Calendar view of org events |
event-list | Event List | Live dated event rows: upcoming, today or a date range, with a spots-left chip and an optional .ics link |
event-list-item | Event List Item | Single event row (list context) |
org-chart | Org Chart | Hierarchical org structure display |
document-link | Document Link | Linked document/resource reference |
campaign-banner | Campaign Banner | Announcement/campaign call-out banner |
`division-card` `entry`. A program or team entry: text first in the DOM (data-division-text: the lead line data-division-lead, the name data-division-name as an h3 or, with headingLevel, an h2, the purpose data-division-body, an authored next date data-division-next with data-division-next-label, and a link data-division-link), then the photo (figure[data-division-media]: the image in data-division-image, then a figcaption[data-division-caption] with the label data-compliance-label and the credit data-division-credit, so type never sits on the photograph). mediaSide (start or end, data-media-side on the root data-division-entry) sets which column the photo takes from 48rem; the DOM order never changes, so alternate it down the page without changing the reading order. *Led by Dele Mensah* in the lead line sets the run in weight (b[data-accent]). next prints only with its text and link only with both its label and address; the next date is authored, not read from events. With no photo there is no figure and the text spans. entry ignores memberCount, logoUrl and leaderName, and its photo upload relates to media unless you call divisionCardBlock.block({ mediaCollection }). The default variant renders exactly as before.
Public API
// config (Payload collections / Pages block list)
import { divisionCardBlock } from '@wabbit/tome-blocks-org-pack'
// render — DivisionCard (and every other block component) is exported from
// the `./render` barrel; there is no per-component subpath in the exports map
import { DivisionCard } from '@wabbit/tome-blocks-org-pack/render'| Export | Subpath | Description |
|---|---|---|
Block descriptors (divisionCardBlock, memberCardBlock, memberGridBlock, eventCalendarBlock, eventListBlock, eventListItemBlock, orgChartBlock, documentLinkBlock, campaignBannerBlock) + register(blockRegistry, bundleRegistry) + isOrgLayerPresent() | . | Payload block config descriptors, bundle registration, and the @wabbit/tome-org layer-detection flag |
DivisionCard, MemberCard, MemberGrid, EventCalendar, EventList, EventListItem, OrgChart, DocumentLink, CampaignBanner + orgPackRenders map | @wabbit/tome-blocks-org-pack/render | Legacy self-registering render barrel |
renderers map + registerRenderers() | @wabbit/tome-blocks-org-pack/render/register | Explicit registration (server-safe adapter contract) — imports each component directly, no side effects on import |
getDemoProps(blockSlug, variant, ctx?) (plus tree-shakeable per-block demo functions) | @wabbit/tome-blocks-org-pack/demo | Pack-level demo-props dispatcher feeding the auto-gallery route; returns null for unknown slugs |
orgPackBlockMeta (per-block meta + variants, in registration order) | @wabbit/tome-blocks-org-pack/meta | Payload-free surface for the gallery storefront |
createHydratedRenderers({ getPayload, events? }), resolveEventListData(payload, props, options?), eventDocToEntry | @wabbit/tome-blocks-org-pack/server | Server-only hydration for event-list |
capacityChip, buildEventIcs, eventIcsDataUri, selectEvents | . | The pure event helpers behind the list: chip wording, .ics output, and the filtering and ordering rules |
Live event list
event-list shows events from the org layer as dated rows. Wire the hydrated renderer next to the static ones, on the server:
import { renderers as staticComponents } from '@wabbit/tome-blocks-org-pack/render/register'
import { createHydratedRenderers } from '@wabbit/tome-blocks-org-pack/server'
const components = {
...staticComponents,
...createHydratedRenderers({ getPayload }),
}What the block does:
- Modes.
upcoming(not yet over),today(today in the block'stimezone, not yet over) andrange(rangeStarttorangeEnd, whole days).eventTypeandlocationnarrow any mode;maxItemscaps it. Completed events never show; canceled events show only intoday, struck through, so a dropped class is explained. - Empty states.
todayrenders nothing, heading included, when nothing is true.upcomingandrangerender nothing too unlessemptyMessageis set. - Closures. Events whose type equals
closureType(defaultclosure) are flagged withdata-event-closureand listed first intoday. On aboardwith finished rows shown, a closure leaves the board once its end has passed (it never sinks with the finished rows), and the board script removes it at that instant on a cached page. - Canceled spelling. A status of
canceledor the oldercancelledmeans the same everywhere (selection, chip state, the.icsSTATUS:CANCELLED, the board). The board'slabels.canceledtext is read before the olderlabels.cancelled(hidden in the admin; a save moves its text tolabels.canceledand empties it). The old spelling is still accepted while stored data moves; writers store the new one. - Capacity chip. "N spots left" from
maxAttendeesminusattendeeCount; a full event reads "Full", or "Waitlist open" when the event keeps a waitlist (waitlist:autoreads the collection'swaitlistCount,always,never). No chip when either number is missing, for closures, for canceled events, or withshowCapacityoff. - Calendar links.
showIcsadds an "Add to calendar" link per row. By default it is a self-containeddata:URI, so no route is needed; seticsHrefTemplate(for example/events/{slug}/calendar.ics) to point at your own route, and servebuildEventIcs([...])from it. - Access. The read runs as an anonymous visitor (
overrideAccess: false, no user) and asks forsecurityLevel = public. The org layer's Event collection is readable by signed-in users only by default, so a public timetable needs read access opened on the events collection; until then the block shows its authored events. Collection and field names (org-events,securityLevel,attendeeCount,waitlistCount,location) are overridable through theeventsoption. - Fallback. With no wrapper,
source: authored, or a failed read, the block renders its authoredevents, filtered by the same rules. A read that succeeds and finds nothing is an empty list, not a reason to fall back.
Row times. A row with an end prints a range with an en dash, "12:30 PM – 2:00 PM" (it printed a spaced hyphen before). timeDisplay (range, the default, or start) chooses; start prints the start only. Board closure rows keep their "Until 3:30 PM" wording either way.
Theme hooks are data-event-* attributes: data-event-list, -mode, -heading, -items, -item, -status, -closure, -date (with -weekday, -month, -day), -time, -title, -location, -capacity (spots, full or waitlist), -ics and -empty. The filed variant lays the rows out as dated entries with the date in its own column.
`cards`. A still list of event cards: a date tile (the existing data-event-date parts), the title, the time and place, an optional audience line (data-event-audience), an optional price chip (data-event-price) and the short summary (with showSummary). The two new fields live on each authored event: priceLabel is the chip text as typed ("$20 · $5 arcade credit included") and audience is the age or audience line ("All ages"); each prints only when entered. Prices belong to authored events only: live org events carry neither field, so their cards print without a chip or an audience line. The list has no marquee and no auto-scroll, and root carries data-block-variant="cards". The other variants render exactly as before.
`filed` lead. leadLayout: 'first' shows the first selected event as a lead article above the rows (article[data-event-lead]). By default that is the earliest event, and the rows below skip it. Set leadEvent (an event id or slug) to choose a different one: that event leads, the rows keep their order without it, and the cap cannot drop it (it takes the last place when it would fall outside maxItems). excludeLeadFromRows (default on once leadEvent is set) leaves the lead out of the rows; turn it off to list it in both places. A leadEvent that the list does not select (past, canceled, a different type) falls back to the first event. The leadImage photo is read for the chosen lead. With neither field set the block renders exactly as before.
Server / client posture
All 9 renderers under src/render/ are plain components — zero carry a 'use client' directive (verified 2026-07-12; scripts/assert-rsc-boundaries.mjs MANIFEST: blocks-org-pack: 0). They render safely from either a server or client component graph. Both render subpaths (@wabbit/tome-blocks-org-pack/render/register and the legacy @wabbit/tome-blocks-org-pack/render barrel) load under plain Node — each renderer's per-block stylesheet ships precompiled as a JS module — so seed scripts and tests can import them. Keep payload.config.ts on the root entry point (block descriptors + register(), no React): that keeps React out of your config. Between the two render subpaths, prefer @wabbit/tome-blocks-org-pack/render/register: it has no import-time side effects.
Built with tsup (bundle: false, dual ESM/CJS, CSS mirrored to dist/).
Block reference
division-card
A single division/department display — logo, name, description, member count, and named leader. The unit-level companion to member-card, summarizing a sub-org rather than a person. Fully static — renders the authored fields; no @wabbit/tome-org peer required.
- When to use
- Introducing a division, department, or regional sub-unit on an about/structure page
- A "Our divisions" section where each unit gets a summary card with its leader and headcount
- Leading into a member-grid or org-chart that drills into that division
- A list of programs or crews, each with a photo, its lead, what it does and when it next meets (the entry variant)
- Page types
- about
- docs
- dossier
- How to use
Provide name (required) plus description, memberCount, logoUrl, and leaderName — these are the only data source today, not a static fallback. @wabbit/tome-org is not wired in yet; the isOrgLayerPresent/resolveOrgLayer seam in src/index.ts is scaffolding for a future hydration wave, to be wired when a consumer needs this card to render a live Division record. Use one card per unit, or several side by side, as the header for the people that belong to it. Two variants: default (the card above) and entry, a program or team entry that sets a captioned photo beside a lead line ("Led by ..."), the name, the purpose, an authored next date and a link. Entry ignores memberCount, logoUrl and leaderName; its photo is an upload, and its label and credit print in a caption after the image, never on it. Alternate mediaSide down the page.
- Pairs with
- member-grid
- member-card
- org-chart
- document-link
- Follows
- org-chart
- Precedes
- member-grid
- member-card
- Avoid when
- Profiling an individual rather than a unit — use member-card
- You need the full three-level structure tree — use org-chart
- Register in
application
member-card
A single member profile card — avatar, display name, role, and position for one person. Fully static — renders the authored fields; no @wabbit/tome-org peer required.
- When to use
- Spotlighting one person on an about/leadership page (a director, a guild leader, a founder)
- A "meet the director" callout where a single profile carries the section
- Inside a custom layout where you place individual cards by hand rather than a generated grid
- Page types
- about
- portfolio
- dossier
- How to use
Provide one member: displayName (required), plus avatarUrl, rank, and position — these are the only data source today. @wabbit/tome-org is not wired in yet; the isOrgLayerPresent/resolveOrgLayer seam in src/index.ts is scaffolding for a future hydration wave, to be wired when a consumer needs this card to render a live Member record. This is the single-member primitive — to lay out many people in a generated grid use member-grid, which renders the same card shape from an array. One variant (default).
- Pairs with
- member-grid
- division-card
- org-chart
- Follows
- division-card
- Precedes
- member-grid
- Avoid when
- Showing a team or roster — use member-grid (array-driven) instead of hand-placing cards
- You need the org hierarchy itself rather than individuals — use org-chart
- Register in
application
member-grid
A responsive grid of member cards under an optional heading — the team/roster directory block. Renders the same card shape as member-card from a list, with column count and an item cap. Fully static — renders the authored members array; no @wabbit/tome-org peer required.
- When to use
- A "Meet the team" / "Our people" / "Leadership" section listing many members at once
- A company or community directory page where you want every member tiled in a grid
- Any directory of people you want generated from data rather than placed card by card
- Page types
- about
- portfolio
- dossier
- How to use
Set a heading, pick columns (2, 3, or 4), and cap with maxItems. Supply the members array — it is the only data source today, not a static fallback. @wabbit/tome-org is not wired in yet; the isOrgLayerPresent/resolveOrgLayer seam in src/index.ts is scaffolding for a future hydration wave, to be wired when a consumer needs this grid to render a live Member collection. This is the array/layout counterpart to member-card — same per-person card, generated in bulk. One variant (default).
- Pairs with
- member-card
- division-card
- org-chart
- campaign-banner
- Follows
- division-card
- org-chart
- Precedes
- event-calendar
- Avoid when
- Featuring a single person — use member-card
- You want the reporting hierarchy rather than a flat people grid — use org-chart
- Register in
application
event-calendar
An upcoming-events list under an optional heading — each row carries a title, start time, status (scheduled/live/completed/cancelled), and location. The array/section counterpart to event-list-item. Fully static — renders the authored events array; no @wabbit/tome-org peer required.
- When to use
- An "Upcoming events" / "What's on" section on an org, community, or company page
- A schedule of workshops, meetups, or streams where each entry needs date and status
- Any place you want the next N events generated from data with a single block
- Page types
- about
- docs
- dossier
- How to use
Set a heading and cap with maxItems, then supply the events array (title required, plus startsAt ISO string, status, location) — it is the only data source today. @wabbit/tome-org is not wired in yet; the isOrgLayerPresent/resolveOrgLayer seam in src/index.ts is scaffolding for a future hydration wave, to be wired when a consumer needs this block to render the live Event collection. Use this for the full list; drop a single event-list-item into a sidebar or feed when you only need one row. One variant (default).
- Pairs with
- event-list-item
- campaign-banner
- member-grid
- Follows
- campaign-banner
- member-grid
- Precedes
- document-link
- Avoid when
- You only need one event row in a sidebar or feed — use event-list-item
- Promoting a single flagship campaign rather than a dated list — use campaign-banner
- Register in
application
event-list-item
A single event row — title, start time, status, and an optional click-through — sized for feeds, sidebars, and inline placement. The single-item primitive behind event-calendar. Fully static — renders the authored fields; no @wabbit/tome-org peer required.
- When to use
- Surfacing one upcoming event in a sidebar, footer, or "next event" callout
- Inline within editorial content where a full calendar would be too heavy
- Hand-placing individual event rows rather than generating a list
- Page types
- about
- docs
- blog
- How to use
Provide title (required) plus startsAt (ISO string), status, and an optional href — these are the only data source today. @wabbit/tome-org is not wired in yet; the isOrgLayerPresent/resolveOrgLayer seam in src/index.ts is scaffolding for a future hydration wave, to be wired when a consumer needs this row to render a live Event record. This is the single-row counterpart to event-calendar — same row shape, one at a time. One variant (default).
- Pairs with
- event-calendar
- member-card
- document-link
- Follows
- event-calendar
- Avoid when
- Listing several upcoming events — use event-calendar (array-driven) instead of stacking rows
- You need date, status, and location laid out as a section header — use event-calendar
- Register in
application
event-list
Events read from the org layer at render time and shown as rows: date, time, title, location, and a capacity chip ("N spots left", "Full" or "Waitlist open") that appears only when the event has capacity data. Modes: upcoming, today (a board that renders nothing when nothing is true, with closures first) and a date range. Each row can offer an .ics link. The live sibling of event-calendar.
- When to use
- A class timetable or workshop schedule that should show how many places are left
- A "today" board for a venue: today's events, closures first, hidden on quiet days
- A dated record of events where each row is a filed entry
- A same-day venue board that says what is live now, what starts next and what has finished
- A "what is on" strip of event cards with a date tile and a price chip for each authored event
- Any events list that must stay current without republishing the page
- Page types
- landing
- about
- dossier
- How to use
Leave source on live and pick a mode (upcoming, today or range). Narrow with eventType or location, set timezone to the venue's IANA zone so "today" and the times are the venue's, and cap with maxItems. The chip reads maxAttendees and attendeeCount from the event and is hidden when either is missing; a full event reads "Waitlist open" when the event keeps a waitlist (see waitlist). Closures are events whose type matches closureType; in today mode they come first. Today mode renders nothing when there is nothing to say, heading included. Turn on showIcs for a per-event calendar link, or point icsHrefTemplate at your own route. Live reads need the createHydratedRenderers wrapper from the pack's /server entry and an Event collection that anonymous visitors can read; without it the block shows its authored events array, filtered by the same rules. Four variants: default, filed (dated rows; can open with a lead event), board (a same-day board whose rows say Live now, Starts at a time or Finished and stay true on a cached page) and cards (a still list of date-tile cards; authored events can carry a price chip and an audience line, live org events carry neither). Set facilityHrefTemplate (e.g. #plan-{key}) to link each event's place to its facility on a site plan.
- Pairs with
- event-calendar
- campaign-banner
- document-link
- Follows
- campaign-banner
- Precedes
- document-link
- Avoid when
- The events are fixed copy that never changes - use event-calendar
- You need one hand-placed row in a sidebar - use event-list-item
- Register in
application
org-chart
A simple three-level hierarchy display — a top-level unit, its teams, and their groups — the structure-of-the-org block, showing reporting lines rather than individual profiles. Fully static — renders the authored teams array; no @wabbit/tome-org peer required.
- When to use
- An "Org structure" / "How we are organized" section showing the levels from department down to working groups
- Orienting a reader before they meet the people — the map that member-grid then fills in
- A dossier page where the unit hierarchy itself is the information
- Page types
- about
- docs
- dossier
- How to use
Set divisionName (required) and supply the teams array (each team carries its squads) — it is the only data source today. @wabbit/tome-org is not wired in yet; the isOrgLayerPresent/resolveOrgLayer seam in src/index.ts is scaffolding for a future hydration wave, to be wired when a consumer needs this chart to render live org-structure records. Use it once near the top of a structure page to frame the divisions and people that follow. One variant (default).
- Pairs with
- division-card
- member-grid
- member-card
- Precedes
- division-card
- member-grid
- Avoid when
- Showing people rather than structure — use member-grid or member-card
- Summarizing one unit with its leader and headcount — use division-card
- Register in
application
document-link
A linked document card — title, category label (SOP, Policy, Handbook), and last-updated timestamp pointing at a document URL. The resource/reference primitive for an org. Fully static — renders the authored fields; no @wabbit/tome-org peer required.
- When to use
- A "Resources" / "Documents" / "Policies" section linking out to SOPs, handbooks, or forms
- A knowledge-base or onboarding page where members find the canonical documents
- Anywhere you want a clean, dated link card instead of a bare hyperlink
- Page types
- docs
- about
- dossier
- How to use
Provide title (required) and href (required) plus category and updatedAt (ISO string) — these are the only data source today. @wabbit/tome-org is not wired in yet; the isOrgLayerPresent/resolveOrgLayer seam in src/index.ts is scaffolding for a future hydration wave, to be wired when a consumer needs this card to render a live Document record. Stack several to form a resource list, often under a division-card or near an org-chart on a structure/docs page. One variant (default).
- Pairs with
- division-card
- org-chart
- event-calendar
- Follows
- division-card
- org-chart
- Avoid when
- Linking to an event rather than a document — use event-list-item
- You want rich body content inline rather than a link out — use a richText/content block
- Register in
application
campaign-banner
A full-width campaign hero — campaign name, tagline, full-bleed banner image, objective count, and a CTA. The promotional top-of-page block for an org campaign or initiative. Fully static — renders the authored fields; no @wabbit/tome-org peer required.
- When to use
- Topping a campaign or initiative page with a cinematic banner and a single call to action
- Announcing an active campaign on a community/org home or landing section
- Driving members toward a campaign detail view or sign-up via the CTA
- Page types
- about
- portfolio
- dossier
- How to use
Set codename (the campaign's display name, required) plus tagline, bannerUrl, objectiveCount, and the ctaLabel/ctaUrl pair — these are the only data source today. @wabbit/tome-org is not wired in yet; the isOrgLayerPresent/resolveOrgLayer seam in src/index.ts is scaffolding for a future hydration wave, to be wired when a consumer needs this banner to render a live Campaign record. Place it at the very top of the page as the hero, with the objective list, member-grid, or event-calendar following beneath. One variant (default).
- Pairs with
- event-calendar
- member-grid
- org-chart
- Precedes
- event-calendar
- member-grid
- Avoid when
- You need a generic marketing hero outside the org-campaign context — use a marketing hero block
- Listing dated events rather than promoting one campaign — use event-calendar
- Register in
application
Exports
| Import path |
|---|
@wabbit/tome-blocks-org-pack |
@wabbit/tome-blocks-org-pack/render |
@wabbit/tome-blocks-org-pack/render/register |
@wabbit/tome-blocks-org-pack/demo |
@wabbit/tome-blocks-org-pack/meta |
@wabbit/tome-blocks-org-pack/server |
Changelog
0.11.xMinor
event-listgains acardsvariant: a still list of event cards with a date tile, the title, the time and place, an optional audience line, an optional price chip and a short description.Authored events take two new optional fields,
priceLabelandaudience(hooksdata-event-priceanddata-event-audience); each prints only when entered. Live org events carry neither.The
default,filedandboardvariants render exactly as before.
0.10.xMinor
A save now moves renamed keys to their American names and empties the old ones.
This is the second of three steps toward American spelling in stored values.
On the class schedule block and collection,
enrolLabel,enrolHref,enrolandlabels.enrolmove toenrollLabel,enrollHref,enrollandlabels.enroll. On the event list board,labels.cancelledmoves tolabels.canceled.Anything that still writes an old key lands in the new one.
This fixes clearing a field: a value sent for the new key, even a blank one, wins and the old value is emptied, so it no longer comes back on the next read or shows on the public page.
The old group and label no longer have default values, so a new block stores nothing under them. Renderers still read the old keys, so a block that hasn't been saved or backfilled looks the same.
The class schedule and event calendar demos use the new names.
BREAKING:
@wabbit/tome-blocks-local-packnow needs@wabbit/tome-local0.6.0 or later, which exports these save hooks from@wabbit/tome-local/classes.
0.9.xMinor
The event list gains a live same-day
boardvariant, facility links and a lead event; the division card can link its facility.Everything is opt-in: a block you already have renders exactly as before.
- `board` variant. A new
event-listvariant (afterdefaultandfiled) for a same-day board. Each row carries a state chip computed from its start and end in the block's time zone: "Live now", "Starts 6:30 PM" or "Finished", plus "Canceled". The clock decides, never the status text, and a row without a usable end gets no chip rather than a guess. In Today mode the board keeps today's finished events (newshowFinished, on by default for the board) and lists them last, after closures and the events still to come, so the item cap drops finished rows first. Closure rows read "Until 3:30 PM". All the words are in a newlabelsgroup, including{clock}for a time without AM or PM. - The board stays true on a cached page. The board ships this pack's first client file,
EventBoardEnhance.client.tsx, mounted only onboard. It works on the rendered markup: at load it corrects any state that changed since the page was rendered, without announcing it; after that it sets one timer for the next start or end, re-checks when the tab comes back, and announces a real change once through a polite live region ("Under-12 final is now live"). At the end of the zone's day it hides the board rather than show yesterday. It never adds, removes or re-sorts rows. Without JavaScript the board shows the states true when it was rendered. Hooks: rootdata-block-variant="board",data-event-timezone,data-event-day-end; rowsdata-event-state,data-event-start,data-event-end,data-event-facility;span[data-event-state-chip]in[data-event-aside];div[data-event-board-live]. - The chip state is a public function.
eventChipState(entry, now), exported from the package root, is pure and isomorphic; the renderer and the island both call it, so they agree for any instant. - Selection keeps finished events on request.
selectEventstakesincludeFinished(Today mode only, default off). With it unset the function behaves exactly as before. - Facility links. Set
facilityHrefTemplate(for example#plan-{key}) and give events a facility key, and each row's place becomes one link holding the facility's plate and name,a[data-event-facility-link]>span[data-event-facility-plate]. Plates come from the block's newfacilityPlateslist, else the event's ownfacilityPlate. Keys are lowercase letters, numbers and hyphens, up to 24 characters. A row without a key, or a blank template, links nothing. - Reading the facility key and three more fields from your events.
ResolveEventListOptions.fieldNamesgainsfacilityKey,summary,registrationUrlandleadImage. Each is read only when you name it, so the default query selects exactly what it did before. Invalid keys, summaries over 140 characters and unsafe links are dropped. The lead photo is read with one extra query, for the lead event only. The/serverentry now exportseventFacilityKeyField(), a ready-made indexed text field with the key's validation, for your Event collection'sextraFields, andbuildEventSelect. - Filed list: summaries, type tags and a lead event.
showSummaryprints each event's one-line summary under its title (any variant). Onfiled,showEventTypeprints the event type as a tag (eventTypeLabelssets the words), andleadLayout: 'first'shows the first event as a lead with its photo and credit, a meta line ("Saturday and Sunday · Tournament", set byleadMetaTemplate), its summary and place, and "Event details" and "Register" links (leadActions). New hooks:p[data-event-summary],span[data-event-type],article[data-event-lead]withfigure[data-event-lead-media],figcaption[data-event-lead-credit],p[data-event-lead-meta]anda[data-event-lead-action="details|register"]. - Authored rows. The block's own
eventsrows gain optionalfacilityKey,facilityPlateandsummary. - Division card facility plate. The
entryvariant gains optionalfacilityKey,facilityPlate,facilityNameandfacilityHrefTemplate. With a key and a template it prints a plate link just before the name,a[data-division-facility-link]>span[data-division-facility-plate], named "Show Field 4 on the plan". Without them the card is unchanged. - Choose the lead. On
filedwithleadLayout: 'first',leadEvent(an event id or slug) picks which event leads instead of the earliest, so one block can show a lead that is not first in date order.excludeLeadFromRows(default on onceleadEventis set) leaves the lead out of the rows; off lists it in both. The cap cannot drop the chosen lead, its photo is read for it, and aleadEventthe list does not select falls back to the first.selectEventstakes a matchingkeepoption. With neither field set nothing changes.
New fields are nullable columns: on a SQL database, regenerate your migration.
Checkpoint A: row times. The time range on a row (every variant) now uses an en dash, "12:30 PM – 2:00 PM", where it printed a spaced hyphen, "12:30 PM - 2:00 PM". This is a deliberate fix to the default output, and the golden fixtures are updated for it; nothing else about the default changes.
New opt-in
timeDisplay(range, the default, orstart): withstart, a row prints the start time only. Board closure rows keep "Until 3:30 PM".The new field is a nullable column: on a SQL database, regenerate your migration.
- `board` variant. A new
Internal Tome dependencies are published as compatible ranges, not exact versions, so installs share one copy of tome-core.
Before, these packs pinned the exact tome-core version that was current when they shipped, so an install could end up with two copies of tome-core and split its shared registries. They now declare a compatible
^range that npm can resolve to a single copy.
3 fixes (0.9.1 – 0.9.3)
- 0.9.3
Canceled statuses now accept both spellings.
This is the first of three steps toward American spelling in stored values, and nothing that gets written changes yet.
Training events, attendance, exam tickets, LMS orders, events, campaigns, CRM activities, resource requests, task forces and the event, status board and operation briefing blocks gain the American
canceled(CANCELEDon the status board) next to the existing value. Reads, queries, the closed-event lock, event board state and selection, the calendar export (STATUS:CANCELLEDis unchanged, it is the iCalendar literal), the status board badge, theme selectors fordata-event-statusand the erasure trail treat the two spellings as one meaning.The event list board labels gain a
canceledkey that is read before the oldcancelledkey. The old field is hidden, not removed, so stored text is kept on save.The admin shows both options, because Payload cannot hide one select option. The old one is labeled "Canceled (legacy spelling)".
Writers still store the old values.
- 0.9.2
Admin labels, field descriptions, demo content and messages now use American spellings.
- 0.9.1
A closure leaves the Today board when it ends, instead of staying at the top for the rest of the day.
Before, a
boardwith finished rows shown kept every closure that touched the day, so a "Field 5 closed · Until 3:30 PM" row still led the board after 3:30 PM while the facility it closed was open again. Now a closure is dropped once its end has passed (or its status iscompleted), on the server and in the resolver.The board's script removes the same row at the same instant on a page that was cached earlier, and hides a board that has no rows left, as the server would render it. Canceled closures and closures with no end are unchanged.
0.8.xMinor
division-cardgains anentryvariant: a program or team entry with a captioned photo, a lead line, the name, the purpose, an authored next date and a link.The text comes first in the DOM (
data-division-text, withdata-division-lead,-name,-body,-nextand-link), then the photo asfigure[data-division-media]with its label and credit in afigcaptionafter the image, never on it.mediaSideplaces the photo on the start or end from 48rem without reordering the DOM;headingLevelsets the name as anh3(default) orh2;*run*in the lead line sets a weight-only emphasis.With no photo the text spans. The photo upload relates to
mediaby default and todivisionCardBlock.block({ mediaCollection })when given.memberCount,logoUrlandleaderNameare hidden forentryand ignored.The
defaultvariant is unchanged: its markup is held byte for byte by a golden cut from the pre-change renderer, and it gains no attribute.
1 fix (0.8.1)
- 0.8.1
Gallery and preview demo props no longer invent site paths.
A demo link is now the caller's
placeholderHrefor#, so rendered demos stop emitting real<a href>s to pages that exist on no site. README: subpath and file mentions are written package-qualified (for example@wabbit/tome-x/render) or as code text, so crawlers no longer resolve them as site URLs.
0.7.xMinor
Adds the
event-listblock: a live list of org events with an optional "N spots left" chip, a "today" mode that renders nothing on quiet days, and an optional .ics link per event.It reads upcoming, same-day or date-range events through the new
./serverentry (createHydratedRenderers), as an anonymous visitor and public events only, and falls back to its authored events when no wrapper is wired or a read fails. The chip shows "N spots left", "Full" or "Waitlist open" and is hidden when capacity data is missing.Rows carry
data-event-*theme hooks and ship adefaultand afiled(dated rows) variant.capacityChip,buildEventIcs,eventIcsDataUriandselectEventsare exported from the root.event-calendarandevent-list-itemare unchanged.
1 fix (0.7.1)
- 0.7.1
Security/visibility fix: the live
event-listresolver now fetches only the fields it renders, so it can never pull an event's briefing, organizer or co-hosts.resolveEventListDatapasses a Payloadselect(title, slug, dates, status, type, location and the capacity counters, following any renamed fields). It works with@wabbit/tome-org's new opt-in public read rule (publicRead: { events: true }): anonymous visitors then see the live public events instead of the authored fallback.The authored fallback still applies when the org layer is absent or the read is refused.
0.6.xMinor
Breaking: block stylesheets are now per-block (`.tome-css`). Each block's CSS ships only on pages that render it, instead of in every page's CSS bundle.
The 8 stylesheets moved from
X.module.csstoX.tome-css, and each renderer renders<BlockStyles sheet={styles} />from@wabbit/tome-blocks-core/block-styles.Required in the consuming site: wrap next.config with
withTomeBlockStyles(@wabbit/tome-blocks-core/next, blocks-core 0.22.0 or later); without it the.tome-cssimports fail to build. See the blocks-core README, "Per-block stylesheets".- The
@wabbit/tome-blocks-corepeer range is now>=0.22.0 <1.0.0. - No client renderers. Exported names and props are unchanged.
- Block CSS now loads after all bundled CSS. A site-level rule that overrode one of this pack's classes at equal specificity, and won only by loading later, no longer wins.
- The
6 fixes (0.6.1 – 0.6.8)
- 0.6.8
Block descriptions read as plain descriptions of what each block shows, not developer notes.
53 block and variant descriptions carried package names, "Fully static — no peer required",
data-variant, slots and hydration details. These are what a visitor reads in a block catalog, so each now says in plain words what the block shows and when to use it.The developer facts aren't lost: each one moves into the block's
usage.howToUse(or, where a block has none, a code comment above its description), and the 20 thathowToUsealready covered are simply dropped from the description. CTA'swith-formvariant no longer claims to render lead-capture fields: it says it leaves room for a form the site supplies, which is what it does. - 0.6.4
Published packages now contain compiled JavaScript and type declarations under a one-line license banner, and no longer include source maps.
What you install: one compiled
.js(ESM) and.cjs(CommonJS) file per source module, its.d.ts/.d.ctsdeclarations, and the stylesheets, fonts and other assets a package already shipped. Every JavaScript module opens with a comment naming the package and its license:/*! @wabbit/<package> — © Wabbit, LLC. Wabbit Tome Commercial License (see LICENSE.md). Not for redistribution. */.The
.mapfiles and thesourceMappingURLcomments that pointed at them are gone, which roughly halves the size of each tarball.Debugging: the code is still unbundled and unminified, one readable file per module, so a stack trace points at real code with real names. Line numbers in a stack trace are one higher than before, because of the banner line.
A
'use client'directive stays the first statement of its module (the banner is a comment above it), so React Server Component boundaries are unchanged.No API change, no runtime behavior change, and nothing to do on upgrade.
In
@wabbit/tome-blocks-gallery, the source snapshotsextractGallerySourcewrites from an installed pack leave out the license banner line, so a component or config snapshot starts at the code and a paid block's preview shows its first 15 lines of real code. - 0.6.3
The event-list-item block now fits a 320px phone screen: the date moves above the title and the title track can shrink.
The fixed date track no longer competes with the status chip at phone width.
- 0.6.2
The organization pack's event calendar preview now uses a fictional venue name.
The event calendar preview locations name an invented headquarters. Block fields and variants are unchanged.
- 0.6.2
The event calendar block no longer overflows a phone-width screen; each event's date moves to its own row.
The fixed date column is kept from 768px up. Below that, the date sits above the title and status, and long titles wrap.
- 0.6.1
The pack works on a stock Next.js site: its per-block stylesheets now ship precompiled, so the site needs no next.config plugin.
Each renderer's
.tome-cssstylesheet is compiled when the pack is built, into a JS module next to it (<Name>.tome-css.js/.cjs), and the renderers import that. A site no longer has to wrap next.config withwithTomeBlockStylesto use the pack, and./renderand./render/registernow load under plain Node, so seed scripts, tests and the Payload CLI can import them.Each block's CSS is still inlined only on pages that render the block. A site that already uses
withTomeBlockStylesneeds no change.Scoped class names change once, because they are now keyed on the package rather than on where it is installed. The raw
.tome-cssfiles stay in the package as readable source.
0.5.xMinor
Tome block packs now install into an existing Payload project the way the README says: one
npm install, one CSS import, no undocumented steps.Proven by the new fresh-install smoke test (
scripts/blocks-fresh-install-smoke.mjs) against a brand-newcreate-payload-appwebsite-template site.Consumers: list `@wabbit/tome-blocks-core` and `@wabbit/tome-ui` in your own `package.json` if you import from them (npm 7+ and pnpm install required peers automatically, so a fresh
npm installof a pack already brings them in).- One shared `blocks-core` per site. Every pack,
blocks-houseandblocks-extrasnow declare@wabbit/tome-blocks-core(and, where used,-house/-extras) as a required peer with an explicit range instead of a regular dependency, so a site gets exactly one hoisted copy and one adapter registry. - No more ERESOLVE in plain Payload sites.
blocks-coreno longer declares@wabbit/tome-coreor@wabbit/tome-catalog(their optional peer graph pulledbetter-auth→@sveltejs/kit→vite@8against a site'svite@7). Theblock-bundleproduct type still auto-registers when both are installed; new structural typesBlockBundleProductTypeDeps,BlockBundleProductTypeRegistryLike,RegisterProductTypeHooksLike. - Tokens in one line:
@import '@wabbit/tome-blocks-core/styles.css';(new export; imports@wabbit/tome-ui/tokens).@wabbit/tome-uiis now a required peer ofblocks-core. - Rich text and images render with no adapter setup. Built-in defaults render Lexical through
@payloadcms/richtext-lexical/reactand resolve populated Payload uploads; an unpopulated upload id warns once in every environment (previously content vanished silently in production). Registered adapters still win. - Payload's spread-props convention: new
adaptRenderersForPayload(renderers)/adaptRendererForPayload(Component)wrap any pack'srenderersmap for a site that renders<Block {...block} />. - Slug collisions with Payload's templates (
cta,banner,archive,content,code): newapplyBlockSlugOverrides(blocks, overrides)andremapRendererSlugs(renderers, overrides)(@wabbit/tome-blocks-core/slugOverrides). Defaults are unchanged; no stored data migrates. - `blocks-house` owns
gsapandhls.jsas dependencies (previously optional peers that still broke the build when missing), and registers GSAP'sScrollTriggeritself before first use. - Full-bleed bands actually span the grid. Eight
pinnedBandblocks (cinema-pack AmbientBand, MediaPanel, PullInterlude, SceneCaption, ScenePlate, ScrubStory, StatementBand; blocks-house FullBleedInterstitial) now declaregrid-column: 1 / -1at their root as the contract requires. Visible change: inside a tome-ui.grid, these render edge to edge where they were previously squeezed to content width. - `@wabbit/tome-ui`:
.griddeclaresreading-start/reading-endbelow 768px (aliased to the content column), so blocks placed on the reading column no longer collapse to a sliver on phones. - Every pack README gains an "Install into an existing Payload project" section and a peer table that matches
package.json;blocks-core's README carries the full walkthrough.
- One shared `blocks-core` per site. Every pack,
5 fixes (0.5.1 – 0.5.6)
- 0.5.6
Three display-family headings now read the
--tome-type-weight-displaytoken instead of a hard-coded weight.@wabbit/tome-blocks-org-pack: themember-gridblock's heading and member-name headings now read the--tome-type-weight-displaytoken (falling back to their existing 600 weight when a theme leaves it unset), so a theme that serves its display face at a lighter weight is no longer browser-faked bold.@wabbit/tome-blocks-lms-pack: thecourse-cardblock's title heading gets the same--tome-type-weight-displaytoken treatment.@wabbit/tome-blocks-catalog-pack: thefeatured-productblock's title heading gets the same--tome-type-weight-displaytoken treatment. - 0.5.5
Seven org blocks now render their muted panels and heading sizes instead of falling back to nothing.
Affected: campaign banner, document link, event calendar, event list, member card, member grid and org chart.
They read
--tome-color-muted,--tome-text-xl,--tome-text-2xland--tome-text-basewithout a fallback, and no package declares those names. They now read--tome-color-surface-muted,--tome-type-size-xl,--tome-type-size-2xland--tome-text-body. - 0.5.3
CSS files are now copied to
dist/by a post-build script instead of a tsuponSuccesshook; no behavior change, and the publisheddist/is identical. - 0.5.2
Drops the no-op
peerDependenciesMetaentry for@wabbit/tome-org, which was never a declared peer and is never imported. - 0.5.1
Layer detection now runs through blocks-core's
createLayerProbeinstead of a localtryGetLayerRegistrycopy.The dynamic core import stays in this pack, and memo semantics are unchanged. The
@wabbit/tome-blocks-corepeer floor goes up to>=0.18.0because that is the first version exporting the helper.
0.4.xMinor
Raise the
react/react-dompeer floor to>=19.0.0(ruled 2026-09-01).The platform declared React peers in five different shapes —
>=18.0.0,>=18,^18 || ^19,^18.3.0 || ^19.0.0,^19.0.0— while its kernel (@wabbit/tome-core) and five app-layer packages already required>=19. Any package advertising React 18 was advertising a configuration that could not be installed alongside the kernel, so the split was never a supported matrix; it was drift.One shape now, and it is the honest one.
These nine version independently of the
linkedblocks family (which gets its own coordinated bump), so they are listed here:@wabbit/tome-admin,@wabbit/tome-admin-pro— from^18.3.0 || ^19.0.0@wabbit/tome-blocks-gallery— from^18 || ^19; devDepsreact/@types/react^18.0.0→^19.0.0@wabbit/tome-blocks-org-pack— from>=18.0.0; same devDep correction@wabbit/tome-engine,@wabbit/tome-motion,@wabbit/tome-rpg,@wabbit/tome-webgl— from>=18@wabbit/tome-ui— from>=18.0.0
The
^18devDependency pins on the two block-shaped packages were already fiction: the rootpnpm.overridespins@types/reactto19.2.14, so both have been building against React 19 types regardless. Correcting them changes the manifest, not the resolved tree.Consumer impact: a React 18 consumer can no longer install these. That install was already impossible with the kernel in the graph.
dist now raw-Node loadable: relative specifiers get explicit extensions post-build.
buildgains&& node ../../scripts/fix-dist-extensions.mjs --strictas its last step, joining the 13 packages that already ran it. tsup buildsbundle: falseand emits relative specifiers exactly as the TypeScript source wrote them — extensionless — which bundlers resolve and raw Node does not (ESMERR_MODULE_NOT_FOUND; CJS worse,require('./x')finds the ESM.jstwin and Node 22+require(esm)then dies on that file's own extensionless import). Every consumer outside a bundler hit this: the payload CLI under plain node,generate:types,generate:importmap, ops scripts, codegen tools.No source changes, no API changes, and bundler consumers are unaffected — extensioned relative specifiers are universally resolvable.
Two supporting changes made the wiring possible, both in repo scripts rather than package source.
fix-dist-extensions.mjsnow skips bundler-asset specifiers (.css,.module.css,.scss, fonts, images, shaders) by explicit extension allowlist instead of reporting them as unresolvable — that single gap is why the 13 prior adopters were exactly the 13 packages that ship no CSS, since--strictexited 1 on any package with a relative stylesheet import.Dotted MODULE names (
./config.meta,./x.variants,./y.demo) are deliberately NOT treated as assets and still get.js/.cjsappended.assert-node-loadable.mjsgained the matching carve-outs so the new repo-wide CI gate reports real defects only: a resolution failure whose path lands undernode_modulesis a peer SKIP (next@15 has no exports map, sonext/imagefails as an absolute path), and a bundler-asset load failure is an environmental SKIP (CJS surfaces it asSyntaxError: Unexpected token '.'raised from inside the stylesheet).Verified before/after on four packages built one at a time: print 8 FAIL → 0, readout 22 FAIL → 0, ai 3 FAIL → 0, gamification 2 FAIL → 0 (its failure was the other signature — a
directory importmissing/index). cop was already clean on a fresh build, so the audit's "27 of 46 fail" figure includes at least one package whose local dist was merely stale.Manifest metadata:
homepage,bugs,engines.All 46 publishable manifests were missing the three fields a consumer sees before any code (2026-09-01 sale-readiness audit §6). Metadata only — no source, no build, no runtime change.
homepagedeep-links to that package README on GitHub (.../tree/main/packages/<dir>#readme). Without it a registry page links to the monorepo root and the reader has to guess which of 46 folders they want.bugs.urlpoints at the repo issue tracker, so a paying customer has a place to report a defect that is not email.engines.nodeis>=22, matching the rootenginesand.nvmrcset the same day. This is a real floor, not decoration: CI on Node 20 could not expand the glob the block packs use fornode --test, and a package installed on Node 20 fails at a runtime the installer cannot connect back to the version.
The forcing function ships with the change:
scripts/assert-manifest-metadata.mjs(rootpnpm assert:manifest-metadata, wired intoplatform-discipline.ymlbesideassert:license-metadata) fails when any publishable manifest lacksdescription,repository.directorymatching its own folder,homepage,bugs,engines.nodeequal to the repo floor,license,filesorsideEffects. It reported 138 violations before this change and 0 after.Updated dependencies [57875ba]
Updated dependencies [b01ca1f]
Updated dependencies [0836ef5]
Updated dependencies [73081e6]
Updated dependencies [090e984]
Updated dependencies [73081e6]
- @wabbit/tome-blocks-core@0.16.0
6 fixes (0.4.1 – 0.4.5)
- 0.4.5
@wabbit/tome-blocks-core@0.16.0
- 0.4.4
@wabbit/tome-blocks-core@0.16.0
- 0.4.3
@wabbit/tome-blocks-core@0.16.0
- 0.4.2
@wabbit/tome-blocks-core@0.16.0
- 0.4.1
Demo imagery now resolves without a network.
Every org-pack image field is a plain-text URL (no Payload upload), and the demos pointed at
https://cdn.wabbit.example/…, a domain that does not exist — so member-card, member-grid, division-card and campaign-banner rendered broken-image glyphs in any gallery preview or thumbnail capture. Newdemo-media.tsemits a self-contained SVG data URI per subject (neutral tonal field + initials; no brand color), andDemoContextgainsplaceholderImageUrlso a host can substitute real imagery.Found while registering Org Blocks on wabbit.com/blocks (2026-09-05).
- 0.4.1
Updated dependencies
- @wabbit/tome-blocks-core@0.16.0
0.3.xMinor
Route pack block links through the
LinkAdapterinstead of raw<a href>.LinkAdapter(0.14.0) shipped the seam; this connects it. 67 anchors across 55 renderer files in 10 packs now render through<TomeLink>, so a consuming site that registers a link adapter gets its route transition on pack blocks — previously impossible by construction, since a pack cannot import the consumer's transition component and the consumer cannot reach into a pack's render tree.New in `@wabbit/tome-blocks-core`: `<TomeLink>`, a component form of
resolveLink().resolveRichText()/resolveMedia()are functions because they turn a data value into content; a link _wraps children_, and the function form forces multi-line JSX through achildren:prop.<TomeLink href={…}>…</TomeLink>is a drop-in for the<a>it replaces. It delegates toresolveLink(), so there is exactly one resolution path, and it stays directive-free so RSC pack renderers can use it without becoming client components.`LinkProps.href` is now `string | null | undefined`. Block data routinely carries an optional URL, and the raw
<a href={undefined}>these calls replaced was legal markup. Narrowing it tostringwould have forced ~10 non-null assertions across the packs and changed behavior at each.NOOP_LINK_ADAPTERnormalizes null toundefinedso React omits the attribute — the unregistered path stays byte-identical to the pre-adapter markup.Behavior is unchanged for every consumer that has not registered a link adapter, which is currently all of them:
resolveLinkfalls back to a plain<a>.Deliberately left as raw
<a>:EditorialFootnotes— its#fnref-*anchors are in-page backlinks. Client-routing them would play a page transition for a jump within the same document.PricingPlans/PricingPlanCard— these already accept an injectable anchor component, a more expressive consumer mechanism that predates the adapter.LogoSlider— a self-closing, childlesstarget="_blank"overlay anchor. Always external, so the adapter would hand it straight back to the browser.@wabbit/tome-longform— it has zero runtime dependencies and does not peer onblocks-core. Adding that edge to the layer graph is its own decision, not a sweep side effect.
Updated dependencies [510036f]
- @wabbit/tome-blocks-core@0.15.0
18 fixes (0.3.1 – 0.3.17)
- 0.3.17
@wabbit/tome-blocks-core@0.15.9
- 0.3.16
@wabbit/tome-blocks-core@0.15.9
- 0.3.15
@wabbit/tome-blocks-core@0.15.9
- 0.3.14
@wabbit/tome-blocks-core@0.15.9
- 0.3.13
@wabbit/tome-blocks-core@0.15.9
- 0.3.12
@wabbit/tome-blocks-core@0.15.9
- 0.3.11
@wabbit/tome-blocks-core@0.15.9
- 0.3.10
@wabbit/tome-blocks-core@0.15.9
- 0.3.9
@wabbit/tome-blocks-core@0.15.9
- 0.3.8
@wabbit/tome-blocks-core@0.15.9
- 0.3.7
@wabbit/tome-blocks-core@0.15.9
- 0.3.6
Republish with correctly-built artifacts, and close the hole that made it necessary.
The 0.15.11 line shipped
defaultBreakoutin its version notes but not in its tarballs: source on main carried all 151 descriptors, the publisheddist/carried zero.pnpm publishdoes not build — it packs whatever is already indist/— so a release ships whatever a previous, possibly unrelated, build left behind.Here the dist had been built from a branch that predated the metadata, and nothing in the pipeline compares artifact to source.
Caught by grepping the _installed_ package in a consumer rather than trusting the version number.
Systemic fix:
prepublishOnly: pnpm run buildadded to all 41 publishable packages that lacked it (onlyblocks-org-packhad one — which is why it was the single package whose build ran during the previous publish). Every publish now rebuilds from source first, so a stale-dist release becomes impossible rather than merely unlikely.Same family as the two publish hazards already documented in this repo (
workspace:*literals reaching the registry, and exact-pin dependencies forcing nested duplicate copies): the publish path had no guard that what ships matches what is committed. - 0.3.5
Populate
BlockMeta.defaultBreakoutacross all nine block packs — 151 descriptors now declare their natural width on the page grid.The 2026-06-28 layout-grid + nesting contract (Decision 3 / Amendment A1) defined
defaultBreakoutas the per-block breakout POLICY co-located with the block, replacing a central hand-maintained table. No pack had ever filled it in, so every consumer fell through to'article'(the reading column) and a full-bleed hero previewed at prose width.Values are drawn from the canonical
@wabbit/tome-uiBreakoutWidthValuevocabulary and assigned from each block's render CSS, not its name:- Root at a named grid line (
content-start / content-end,full-start / full-end,marginalia-right-*, …) —defaultBreakoutmirrors that exact line. - Root
1 / -1+ subgrid with an inner wrapper atcontent-start / content-end— a self-banding block:'full-bleed'. - Root and inner both
1 / -1(width-agnostic) — assigned editorially: bands/heroes'full-bleed', page sections'content', cards and single-column components'breakout-md', prose/inline components'article'. - Where a block already ships its own
breakoutWidthField({ defaultValue }),defaultBreakoutmatches that value exactly rather than contradicting it.
Distribution:
content52,full-bleed41,article33,breakout-md23,breakout-lg1,marginalia-right1.Also declares
pinnedBand: trueon the three blocks that are unambiguously full-bleed bands whose ownbreakoutWidthfield drives INNER content (compareColumns,editorialSection,editorialSpread), andnestable: falseon 22 full-bleed heroes / band primitives / containers that carried nonestabledeclaration.Purely additive optional metadata: no descriptor field, block structure, or CSS changed, and no existing
nestable: truewas flipped, so the derivedlayoutGridchild allowlist is byte-identical (98 nestable blocks before and after). - Root at a named grid line (
- 0.3.4
Updated dependencies [71d3b09]
- @wabbit/tome-blocks-core@0.15.9
- 0.3.3
Neutralize gaming/military-flavored language across the org surfaces — labels, descriptions, and demo content only; zero schema changes (all field names, collection slugs, and enum/select VALUES are byte-identical, so no consumer data migration).
- blocks-org-pack: CampaignBanner's
codenamefield is now labeled "Name" with a business example ("Spring Launch" demo replaces "Operation Nightfall … contested systems"); MemberCard/MemberGridrankfields labeled "Role" with business-ladder demo values (Principal/Staff/Senior replace Captain/Lieutenant/Sergeant, "Fleet Commander" → "Design Lead"); EventCalendar demo uses business events (workshop, hiring open house, quarterly business review — "Upcoming Operations" heading → "Upcoming Events"); OrgChart meta/variants describe a generic three-level hierarchy instead of Division → Teams → Squads (render output was already 100% data-driven — level headings come from the authored rows, so no new props were needed); block meta descriptions/usage neutralized throughout. - tome-org: flavored admin LABELS get neutral text while stored values stay put — Event status
boarding/debrieflabeled "Check-In"/"Wrap-Up"; eventTypeoperation/patrol/examlabeled "Initiative"/"Outreach"/"Assessment";securityLevellabeled "Access"; Campaigncodenamelabeled "Internal Name" and campaignTyperecurring_op/special_operation/deploymentlabeled "Recurring Series"/"Special Initiative"/"Rollout"; Memberclassificationlabeled "Directory Visibility" withclassifiedlabeled "Private", "Chain of command" → "Reporting line"; RanksecurityClearancelabeled "Access Level" and categorycommandlabeled "Management"; Squad squadTypefire_team/flightlabeled "Crew"/"Pod",callsignlabeled "Nickname"; Membershipsquadronlabeled "Unit", role example "Pointman, Medic" → "Coordinator, Facilitator"; Position abbreviation example "CO, XO" → "COO, PM", categorycommandlabeled "Executive". The configurableDEFAULT_ORG_TERMINOLOGY(Division/Team/Squad/Rank) is deliberately unchanged — it is the documented override seam and@wabbit/tome-scinherits it for its themed collections. - blocks-core: BLOCK_CATALOG entries for campaign-banner, member-card, and org-chart re-mirror the updated pack meta descriptions (catalog is generated from pack meta; only the entries owned by this change were refreshed).
- blocks-org-pack: CampaignBanner's
- 0.3.3
Updated dependencies [6779aa1]
- @wabbit/tome-blocks-core@0.15.8
- 0.3.2
@wabbit/tome-blocks-core@0.15.0
- 0.3.1
Starter-launch fixes across four packages.
- tome-chrome: Navbar5's desktop menu now hides on mobile — the responsive
.desktopMenuclass moved to a wrapper<div>so tome-ui'snavigation-menuroot rule (display: flex) no longer clobbers thedisplay: nonetoggle below 64em (the bar was blowing out to ~500px on phones, pushing the hamburger off-canvas). - tome-blocks-lms-pack: CourseCard no longer renders the rating star twice — the JSX
★is removed; the styleable.tome-course-card__rating::beforestar in styles.css is the single source. - tome-blocks-catalog-pack: CategoryStrip renders real lucide icons for kebab-case icon names (target, joystick, book-open, settings, package) instead of painting the raw name as text; unmapped names render nothing, authored emoji still render. Adds
lucide-reactas a peer dependency (>=0.460.0, matching tome-chrome). - tome-blocks-content-writer: archive, related-posts, and blog catalog copy (meta
description/usage.summary) now leads with the supported mode and frames unimplemented query-driven modes as roadmap scope instead of "renders nothing". No behavior change. - tome-blocks-org-pack: CampaignBanner drops its 20rem min-height when no
bannerUrlis set — the floor exists to give the banner image room; without one it rendered a tall empty box above the bottom-anchored content.
- tome-chrome: Navbar5's desktop menu now hides on mobile — the responsive
0.2.xMinor
Batch 6 (domain packs) + Batch 7 (primitives + usage manifest) of the 2026-06-27 inserter/variant architecture — audit + usage/intent authoring (Decision 4).
The domain packs pull from distinct collections (courses / products / members / fleet), so per the spec they stay schema-distinct — this is metadata, not consolidation.
- Authored usage/intent metadata on all 25 domain blocks: LMS (course-card, lesson-list, progress-bar, quiz-summary, instructor-card, enrollment-cta), Catalog (product-card, product-grid, category-strip, price-table, inventory-badge, featured-product), Org (division-card, member-card, member-grid, event-calendar, event-list-item, org-chart, document-link, campaign-banner), SC (fleet-summary, signal-hero-sc, task-force-roster, op-briefing-panel, rsi-handle-card). Also authored usage on the 9 free
extras-primitives(section, spacer, grid, stacking-wrapper, content, text-block, code, section-header, content-two-column — in @wabbit/tome-blocks-extras, already bumping). - Audit (clean): no dual-mechanism drift, no slug-splits, and the card-vs-grid / item-vs-calendar pairs are genuine single-object-vs-array shape differences (NOT layout variants) — correctly kept as separate blocks. The relationship is encoded in each block's
usage.pairsWith/avoidWhenso an assembling agent picks the right one. - `buildUsageManifest` verified end-to-end (@wabbit/tome-blocks-core, Batch 0): builds a sane manifest from the now-authored descriptors —
byRegister(application / editorial / marketing-landing / structural / dossier),byPageType, variant flow-through, andunauthoredtracking. The consumer-side manifest generation + exposure to assembling agents is a live-run wiring step.
Note: the domain packs use the inline-meta pattern (BlockMeta passed to
defineBlockin each block's index.ts), sousagewas added there. Ships in the linked family's 0.8.0 minor.- Authored usage/intent metadata on all 25 domain blocks: LMS (course-card, lesson-list, progress-bar, quiz-summary, instructor-card, enrollment-cta), Catalog (product-card, product-grid, category-strip, price-table, inventory-badge, featured-product), Org (division-card, member-card, member-grid, event-calendar, event-list-item, org-chart, document-link, campaign-banner), SC (fleet-summary, signal-hero-sc, task-force-roster, op-briefing-panel, rsi-handle-card). Also authored usage on the 9 free
Updated dependencies [249b670]
- @wabbit/tome-blocks-core@0.8.0
23 fixes (0.2.1 – 0.2.10)
- 0.2.10
Updated dependencies
- @wabbit/tome-blocks-core@0.14.0
- 0.2.9
Updated dependencies [f4d55c9]
- @wabbit/tome-blocks-core@0.13.0
- 0.2.8
Updated dependencies [e11d5a2]
- @wabbit/tome-blocks-core@0.11.2
- 0.2.7
sc-pack: dead CSS-copy tsup hook deleted (the pack ships zero CSS); its deliberately-lightweight profile (no meta.ts, rides tome-sc's token theme) is now documented in the source header with the convergence trigger (gallery browse surface needs meta).
./demosubpath rule: all 10 renderer packs now expose it — added to org/lms/catalog/sc packs plus agency-essentials (found missing in the consistency sweep); verified the demo import graph never reaches registering code. - 0.2.7
Documentation truth pass: all "hydrates from @wabbit/tome-X when present" claims across READMEs, block meta, bundle descriptions, render headers, and admin field descriptions are rewritten to the honest contract — these blocks are fully static today; the layer-presence flags are the seam for a future hydration wave (trigger documented in place). content-writer's
RelatedPosts(auto mode) andArchive(collection mode) no longer render fake placeholder UI — the unimplemented modes render nothing and say so in the admin field description. - 0.2.7
Every package now declares an explicit
sideEffectsfield (38 added; motion/engine/forms already correct).Registration-bearing modules (render files'
registerRenderer,blocks/*/index.tsdefineBlockself-registration, widgetregister.tsfiles, productHooks, permission self-registrations, print templates, chrome built-in variants) are listed so bundlers can tree-shake everything else WITHOUT dropping import-time registrations — previously the field was unset, which blocked cross-module tree-shaking through the barrels entirely. Never blanketfalseon a package with registration or CSS. - 0.2.7
The clientization migration: 127 render components across seven packs dropped
'use client'— every file individually re-verified hook/handler/context-free before stripping; adapter-consuming static blocks converted toresolveRichText/resolveMedia.Exactly 20 of 155 renderers remain client, each for a verified reason (state/effects/motion, or a documented client-shell composition contract), enforced by the new
assert:rsc-boundariesCI script (per-pack manifest; fails loudly if a directive creeps back or a count drifts). Every renderer-bearing pack now exports./render/register(renderersmap + explicitregisterRenderers()), aggregated by@wabbit/tome-blocks's newregisterAllRenderers()— the format-safe registration path for server component graphs, where the legacy import-time barrel registration never executes (that legacy path is unchanged and remains supported until the spec's deprecation trigger).RenderBlockis rewritten server-safe: directive-free, optionalcomponentsprop (RenderBlocks parity) → registry fallback, dev warn-once naming both fixes on a miss; its docs state the explicit-registration prerequisite. Rendered output is byte-identical everywhere; behavior change only for consumers rendering migrated blocks in RSC WITHOUT a provider or registration — they get the documented warn + graceful degradation instead of silent client bundling. - 0.2.7
DRY adoption sweep (the audit's "adoption, not extraction" rule): crm/deals capability presets delegate to core's
sessionHasCapabilityOrLegacyAdmin; new corebuildOwnershipWhere/ownershipOrBypass(via./access) adopted by core's vendorScoped, catalog's vendor-scoping, and org's ownOrScoped (public APIs unchanged);slugField()adopted at 7 sites where semantics matched exactly (core lms collections + createMemberCollection — replacing a third independent slugify), with ~25 sites honestly skipped for named semantic divergences (auto-regenerate-on-clear vs allow-empty, collection-level hook pattern) now listed as core-enhancement candidates; newformatDisplayDatein blocks-core utilities (UTC-pinned, hydration-safe) adopted at 5 verified-identical sites; lms-ui consolidates its two certificate date formatters locally;useMediaQuery/useIsMobilepublished from tome-ui and adopted by AppShell + admin's SidebarProvider; gamification'sawardPointsnow uses the authoritativegetPointsBalance(fixes a divergent 1000-row scan cap vs the correct 10000). - 0.2.7
Updated dependencies [26dfa07]
- 0.2.7
Updated dependencies [36e537a]
- 0.2.7
Updated dependencies [36e537a]
- 0.2.7
Updated dependencies [36e537a]
- 0.2.7
Updated dependencies [a93f478]
- 0.2.7
Updated dependencies [5f78397]
- 0.2.7
Updated dependencies [5f78397]
- 0.2.7
Updated dependencies [aef2725]
- @wabbit/tome-blocks-core@0.11.0
- 0.2.6
0.2.5 published to npm.wabbit.com without its
dist/output: the tarball contained onlypackage.json/README.md/CHANGELOG.md/LICENSE.md(4.5 kB) even thoughfilesdeclaresdistandmain/module/exportsall point into it, breaking every consumer's install (missing entry point).Root cause: 0.2.5 was a pure dependency-ripple patch (blocks-core 0.10.0 bump, no source change to this package) published via
pnpm publishwith no lifecycle guard verifyingdist/existed at pack time —pnpm pack/publishsilently omits a listedfilesentry when the path is absent rather than erroring, and this package (like its blocks-family siblings) had noprepublishOnlybuild guard. Reproduced locally:rm -rf dist && pnpm packproduced the identical 4-file/~1.6kB-unpacked artifact; withdist/present,pnpm packcorrectly emits 276 files / 63.9 kB, matching the healthy 0.2.4 baseline.Fix: added
"prepublishOnly": "pnpm run build"to this package's scripts. tsup's own config already setsclean: true, so a plain build both clears stale output and guaranteesdist/exists beforepnpm publishpacks the tarball — verified viapnpm publish --dry-run --no-git-checkswithdist/deleted beforehand: the hook rebuilt dist and the resulting dry-run tarball matched the 276-file/63.9 kB healthy shape.This is a republish, not a code change — no runtime behavior differs from what 0.2.4/0.2.5-intended shipped.
Note (not fixed here — out of scope for this package's patch): this dist-less-publish class of defect is repo-wide, since no package in the monorepo has a
prepublishOnly/prepackbuild guard prior to this change (confirmed viagrep -l prepublishOnly packages/*/package.jsonreturning nothing). Any package published without a preceding fresh build is equally exposed.Recommend a shared guard (e.g. a
scripts/verify-dist-before-publish.mjsinvoked from each package'sprepublishOnly, or a rootpnpm publishwrapper that runspnpm --filter <pkg>... buildfirst) rather than hand-addingprepublishOnly: "pnpm run build"to all ~30 packages individually.Separately (also not fixed here): this package's pre-existing
"clean": "rimraf dist"script is independently broken —rimrafis not declared in this package'sdevDependencies(nor at the workspace root), sopnpm run cleanfails with "'rimraf' is not recognized" if invoked directly. The same gap exists across the whole blocks family (blocks, blocks-core, blocks-extras, and all 8 sibling packs) pluscore,ui,motion,lms,lms-ui,gamification— none declarerimrafeven though theircleanscript calls it; sibling packages likeadmin,crm,marketing,accounts,org,scdo declare it (^5.0.0).This guard's
prepublishOnlyavoids the gap by not callingcleanat all (relying on tsup's ownclean: true), so it is unaffected, but the standalonecleanscript remains latent-broken for these ~18 packages.- @wabbit/tome-blocks-core@0.10.0
- 0.2.5
@wabbit/tome-blocks-core@0.10.0
- 0.2.4
Updated dependencies
- @wabbit/tome-blocks-core@0.10.0
- 0.2.3
Docs-manifest emitter pipeline (W3 ship-readiness).
@wabbit/tome-blocks-corenow ships a standalone Node ESM CLI atscripts/emit-docs-manifests.mjsthat emits per-package documentation manifests (index.json, packages/<slug>.json, changelog.json) by reading what packages already carry — READMEs, the payload-free<pkg>/metablock-usage barrels, package.json exports maps, and CHANGELOG.md. It is the docs-pipeline sibling of the gallery source extractor and is consumed by host sites at prebuild:node node_modules/@wabbit/tome-blocks-core/scripts/emit-docs-manifests.mjs --output-dir <dir> --scope <scope.json>.To let the emitter import block metadata uniformly without dragging Payload config into a build script, the
./metapayload-free subpath (BlockMetaEntry[]) is extended to the remaining offered blocks packs — agency-essentials, catalog-pack, lms-pack, org-pack, and signal-theme — mirroring the existing editorial-pack / marketing-starter / content-writer / extras barrels. Each block'sBlockMetawas relocated verbatim into a payload-free sibling meta module and re-imported by its block config; no meta values changed.Every supported-core package additionally adds
CHANGELOG.mdto its publishedfilesarray so the next publish cascade ships changelogs the emitter can read from installed tarballs at prebuild. - 0.2.3
Updated dependencies [bed3f90]
- @wabbit/tome-blocks-core@0.9.4
- 0.2.2
Updated dependencies
- @wabbit/tome-blocks-core@0.9.2
- 0.2.1
Updated dependencies [c7d0afc]
- @wabbit/tome-blocks-core@0.9.0
0.1.x
8 fixes (0.1.1 – 0.1.7)
- 0.1.7
Updated dependencies [66c611c]
- @wabbit/tome-blocks-core@0.7.0
- 0.1.6
Updated dependencies [4b2f368]
- @wabbit/tome-blocks-core@0.6.2
- 0.1.5
@wabbit/tome-blocks-core@0.5.9
- 0.1.4
@wabbit/tome-blocks-core@0.5.7
- 0.1.3
Onboard to the dist-publish pipeline — first registry publish to npm.wabbit.com (the pack was previously source-only / path-alias consumption).
Adds a
tsupbuild (bundle: false, dual ESM/CJS,'use client'preserved, CSS mirrored todist/), dist-pointingexports(.config +./rendercomponents), andfiles/publishConfig/license/author/repositorymetadata. No source or behavior change.Enables registry consumption (e.g. tome-starter moving off path-alias-to-source).
- 0.1.3
Updated dependencies
- @wabbit/tome-blocks-core@0.4.2
- 0.1.2
Updated dependencies [b76f684]
- @wabbit/tome-blocks-core@0.4.0
- 0.1.1
Updated dependencies [f2202cd]
- @wabbit/tome-blocks-core@0.3.0