Skip to content
mason

Mason Changelog

Mason — Changelog

All notable changes to Mason are recorded here. Versions follow the add-on's addon.setup.php version.

1.14.1

Fixed

  • A rich-text column inside a Grid could not be typed in or pasted into. The cell was wrapped in a <label>, and a label hands a click to its first labelable control — which in a rich-text column is a toolbar button. The click never reached the editor.

    The same bug was found and fixed for brick-level fields some time ago; the Grid cell was missed, so it survived one level down.

1.14.0

Changed

  • Components are now called presets. The name was wrong twice over. Bloqs already uses "component" for a container that keeps existing after you insert it, and Mason's does the opposite — it dissolves into ordinary bricks and the link ends. Same word, different contract, which is the kind of collision nothing looks wrong about. It also read as a schema word next to binders, which genuinely are live references, so the two names gave no clue which was which.

    "Preset" says starting point. Binders share a definition; presets share a starting point.

    Labels, menus, messages and documentation only — the stored data is untouched. The table, the export bundle's components key and every internal handle keep their names, exactly as "Grid" kept the repeater handle. Existing presets, bundles and imports are unaffected, and no migration runs.

  • Presets can actually be selected now. A brick is an atom node, so clicking one produced a selection of exactly one — and the only gesture that selected more was dragging a text selection through the document past bricks that are full of form inputs, where clicking does not move the selection at all. The gesture anyone would try, click one brick and shift-click another, did nothing. Saving a preset was effectively unreachable.

    Shift-click a second brick's title bar to select the range. It grows or shrinks from the brick you clicked first, in either direction, and the range highlights as you go.

  • "Save as preset" now tells you what it will do. It is disabled until two or more bricks are selected and reads "Save 3 bricks as preset" once they are. Previously it was permanently enabled and clicking it with nothing selected just produced an alert telling you to go and select something — an action that looked available and was a dead end.

  • Presets have their own section at the top of the insert menu. They used to be filed under the stack they belong to, so you only found one by opening the right group and noticing it above the bricks — a feature you had to already know about stayed invisible. Their stack is still matched by the search box.

  • The editor toolbar puts "Brick" last. Inserting a brick is the constant action, so it now holds the end of the row where the hand returns to, and Save as preset — occasional, and only available with two or more bricks selected — sits before it as a secondary outline button rather than competing at equal weight.

1.13.0

Added

  • A Grid can now drive a condition. Any field or column can be shown based on whether a Grid has rows — "show the caption once the gallery has images". The rule reads has rows / has no rows rather than the generic is empty, because an author scanning a list of rules should see what the source actually is.

    Only those two operators. Comparisons like has more than 3 rows are deliberately absent: the evaluators unwrap an array to its first element for scalar comparison, so a count rule would have silently compared against row one's contents and quietly never fired. Row counting needs its own operators, and offering the existing ones would have been a trap.

  • A nested Grid can now carry a condition. A Grid inside a Grid was the one place conditions could not be set — an unexplained guard excluded it, copied from the block above where skipping a repeater is correct. Both halves of the renderer already honoured a condition there; only the settings screen refused to let you write one.

    The rule is rendered below the nested column list, so it cannot be misread as belonging to the grid inside it.

1.12.1

Fixed

  • Reordering fields and grid columns was a guess. The only feedback was the card you had picked up going half-transparent — nothing showed where it would land, and dropping on a card gave no way to say "above this one" or "below it". Combined with a grab handle barely larger than the icon in it, reordering a brick's fields was fiddly and unpredictable.

    The hovered card now draws a line on the edge the item will land against, so the outcome is visible before you let go. Which edge follows the pointer: top half means above, bottom half means below. The handle is a proper target with a hover state, so it is findable without hunting for it.

    The line is drawn on the card rather than inserted between cards, so nothing reflows under the pointer mid-drag — a list that moves while you are aiming at it is worse than no indicator at all.

1.12.0

Added

  • Grid columns can be conditional, per row. A column can now carry the same conditions a brick field can — "show when Media type is video" — and they are judged against that row's values. Two rows of the same grid can show different inputs.

    Sources are the other columns in the same row. A rule against one of the brick's own fields would be true for every row at once, which is a field condition, and still belongs there.

    Hiding never deletes: a value already entered stays in the row and returns if the condition becomes true again. On the front end a hidden column emits nothing, so its token renders empty rather than the row vanishing.

    Neither ExpressionEngine's Grid nor Bloqs offers this, and it removes a common workaround — a media grid that takes an image or a video is one grid now, not two fields and a note in the instructions.

1.11.2

Fixed

  • A brick whose template contained a <script> tag broke the whole editor. Every brick on the page rendered as "Unknown brick type … Its content is preserved" — even bricks that were correctly assigned to the field and had nothing to do with the one at fault.

    Mason passes its configuration to the editor inside an inline <script> block, and that configuration includes each brick's template. An HTML parser ends a script element at the first literal </script it sees, whatever the surrounding JavaScript means — so a template containing a script tag closed Mason's own block early. The result was a JavaScript syntax error, no configuration, no brick types, and a page full of orphans.

    Nothing was lost: the content was intact throughout, and reloading after the fix restores every brick. A template may contain a script tag — that is a reasonable thing for a template to do, and Mason now escapes it so it cannot terminate the block.

1.11.1

Fixed

  • Importing a brick destroyed its markup. The bundle was run through ExpressionEngine's XSS filter on the way to being saved, which mangles exactly what a partial legitimately contains: <script> and <link> became [removed], style="…" was stripped, and <iframe> was entity-encoded so it rendered as visible text on the page.

    It failed in the worst way available. The preview screen reads the uploaded file directly, so it showed the brick intact and reported a successful import — only the saved copy was corrupted, and nothing revealed it until the brick was rendered on a page. No error, no warning.

    Any brick imported before this whose partial contained a script, a stylesheet link, an iframe or an inline style needs re-importing.

1.11.0

Added

  • Components — save an arrangement of bricks and use it again. A brick library says what the pieces are; it never said how they go together. Every page needing "section heading, then a split image, then stats" was rebuilt by hand, and a pattern an author worked out once could not be reused.

    Select two or more bricks while editing an entry and choose Save as component. It appears in the insert menu, in its stack, showing how many bricks it will add.

    A component is a stamp, not a link: inserting it drops ordinary bricks with the saved values, and the connection ends there. Authors edit, reorder and delete them freely, and editing the component later cannot rewrite a page somebody has already built. That is deliberately the opposite of a binder, which is a live reference — a binder shares a definition and should follow its source, while a component supplies a starting point and must not.

    Bricks that have hit their usage limit are skipped rather than blocking the whole component, and are named when they are. Asking for a five-brick section and getting four with an explanation beats getting nothing and guessing why.

    Components travel in an export, bringing the bricks they name and those bricks' binders with them. A member that cannot be resolved on the far side is dropped and reported, never silently swapped for something else.

    Manage them under Mason → Components: rename, restack, delete, export. There is no "new component" button on purpose — building an arrangement away from the page is the thing this feature exists to avoid.

Fixed

  • Imported bricks arrived without their markup. 1.10.0 added partials to the bundle, but the import rebuilt each brick from a fixed list of keys that did not include partial — so the markup was discarded before the code that looks for it ever ran. Export wrote it, import dropped it, and both halves reported success.

    Any bundle imported since 1.10.0 landed with its bricks rendering Mason's default output. Re-importing that bundle now brings the markup across.

  • Exports could produce a file that would not import. BrickTransfer read a property it never declared, and on a site that displays errors PHP printed the warning into the response — which for an export is the file, so the downloaded bundle was no longer valid JSON. Sites with errors hidden were unaffected, which is why it went unnoticed.

1.10.0

Added

  • Brick markup travels with an export. A bundle carried a brick's fields but not the template that renders them, so an imported brick produced Mason's generic default until somebody copied a file across by hand — half the feature moving through the browser and half of it over FTP.

    Export takes whatever actually renders: the file when there is one, the stored copy otherwise. Import writes it back to both, exactly as saving in the control panel does, and a site with no writable template directory keeps it in the database and still renders.

    The import preview says which of the two will happen, and says so before you commit. A bundle exported by an older Mason simply carries no markup; that is reported rather than treated as an error, and importing one no longer blanks the markup of a brick it overwrites.

  • A limit on how many times a brick can be used. Some bricks only make sense once on a page — a masthead, a contact block, a feature showcase with anchor links that would collide. Set a limit on the brick and the insert menu stops offering it once a field has that many.

    It is greyed rather than hidden, and says why: an author looking for a brick should find it and learn it is spent, not wonder where it went. The count is read from the document each time the menu opens, so it stays right however a brick arrived — inserted, pasted, duplicated or undone.

  • A title bar colour for a brick, matching what Tab-EE does for a Grid row. Empty is the default. Label colour is derived from the tint, so a dark choice gets light labels without anyone having to think about it.

Changed

  • The documentation has moved out of Mason's sidebar and into the add-on's own menu on the Add-Ons screen, beside Settings and Uninstall. Nine doc links and their header made the working sidebar — the stacks you actually reorganise — the smaller half of the rail, and a manual is something you go and read, not something you navigate past on every visit.

    Every doc page, anchor and bookmark still works; only the way in has moved.

1.9.0

Added

  • Edit a brick's template in the control panel. A brick was created, configured, duplicated, exported and imported in the browser — and then its markup had to be put on the server by hand. Every brick screen now has a Template toggle that opens the partial for editing.

    It is a toggle rather than an always-open panel because most visits to that screen are about fields, and a code editor sitting above them is in the way. A brick that already has markup opens with it showing, so a template that matters is never hidden behind a switch you did not know to flip.

    The file and the control panel stay in step, following ExpressionEngine's own "Save Templates as Files": the file is the markup whenever one exists, saving here writes that file, and a partial that arrived over FTP is picked up and shown the first time you open the brick. Edit in either place; both agree afterwards.

    Sites with no writable template directory keep their markup in the database instead, which is the case this was built for — the control panel is most useful exactly where the filesystem is out of reach.

    A read-only file is reported, not worked around. If the partial exists but cannot be written, saving says so and tells you the file is still what renders. The alternative — storing the edit and letting the old file carry on — would look like a successful save that changed nothing.

    Renaming a brick moves its partial with it. A partial is tied to its brick by filename, so without that the markup is orphaned and the brick quietly falls back to default output.

1.8.0

Added

  • Export and import of bricks and stacks. A brick library was real work that lived in exactly one database. It can now be written to a JSON file and read back — to move a brick to another site, hand one to a colleague, keep a library in version control, or seed a new build from an existing one.

    Export a selection (tick bricks, then with selected → Export), a stack from its own screen, or everything from Transfer → Export all bricks. Import is under Transfer → Import bricks.

    Binders travel with the bricks that bind them, even if you did not select them — without the binder, a brick imports cleanly and silently loses those fields.

    Importing shows you what it will do before it does it. Every brick in the bundle is listed as new or as a conflict with something you already have, along with any stacks that will be created. For each conflict you choose skip, overwrite, or import as a copy. Skip is the default: overwriting is something you pick, not something that happens because you did not look.

    References are translated, not copied. Most of a brick already refers to things by name — channels, filter fields, binders — and those survive a move. But an upload directory and a relationship's channels are stored as numeric ids, and an id means something different on every site. Mason converts these to names on export and looks them up again on import.

    If a name does not exist on the target site, the setting is left empty and the import report says so. The brick still imports and still works; you pick a directory for it. The alternative — keeping the number — would produce a brick quietly saving uploads to whatever happened to hold that id, which is worse than an obvious gap.

    Export is not licence gated, so a site whose licence has lapsed can still get its own brick library out. Import writes, so it is gated like any other change.

Fixed

  • The Duplicate button on the brick list drew its icon twice, a few pixels apart, so it looked smudged. ExpressionEngine supplies that icon itself; Mason was adding a second copy of the same one.

1.7.0

Added

  • AI assistant access without shell access. 1.6.0's support relied on ExpressionEngine's MCP Server, which speaks stdio — it needs SSH and a long-running process, so on a lot of client hosting it simply isn't available.

    Mason now serves the same tools from its own authenticated HTTP endpoint. An assistant can reach a live site with no terminal at all, and EE's MCP Server add-on isn't required.

    It is off until you set a token — until then the endpoint returns 404 and is indistinguishable from an add-on without the feature. Add $config['mason_mcp_token'] to config.php (or set MASON_MCP_TOKEN), then see the new Mason → AI Access screen for the endpoint URL. The token is kept out of the database on purpose, so a database dump cannot leak it.

    Everything remains read-only: a leaked token cannot change content, bricks or settings. It can read your brick definitions, and — via mason_render_preview — rendered entry content, so use HTTPS and treat the token as a credential. See Documentation → AI assistant.

  • ExpressionEngine's code generators now understand Mason fields. Previously EE could only tell that a Mason field was a tag pair and emitted "we could not determine the possible variables to use inside tag pair" — it had no way to know, since a Mason field's contents are the bricks assigned to it and those live in the database rather than in files.

    Mason now registers a template generator, so generated code carries your real brick handles and field tokens: the {if brick_type} chain, Grid and Listing loops with their actual columns and row variables, and binder-supplied fields. A field whose assigned bricks have since been deleted says so explicitly and names them, rather than rendering nothing with no explanation.

    This works everywhere the generator is used — Code Generators in the control panel, generate:templates on the CLI, and the MCP add-on's get_field_template_structure and validate_template_tags tools.

Changed

  • Tool definitions moved into a single internal registry shared by both the stdio and HTTP transports, so the two cannot drift apart. No change in behaviour.

1.6.0

Added

  • AI assistant support. Mason can now describe itself to Claude Code, Claude Desktop, Cursor, Codex or anything else speaking the Model Context Protocol, via ExpressionEngine's MCP Server add-on.

    This closes a real gap. Bricks are defined in the database, not in files, so an assistant reading your templates has no way to know that {brick:heading} is valid for one brick and meaningless for another — it guesses, and the guesses look plausible. These tools let it read the actual brick library instead:

    • mason_list_bricks — every brick, its fields and kinds
    • mason_get_brick — one brick in full, including binder-supplied fields, and the exact tokens each field produces
    • mason_validate_template — check markup (or a brick's partial on disk) against the real schema: unknown tokens, invalid sub-variables, unclosed loops
    • mason_list_fields — which fields use Mason, their mode and assigned bricks, flagging assignments whose brick no longer exists
    • mason_render_preview — the HTML an entry's field actually outputs

    Also exposes Mason's documentation at mason://docs and a mason_write_brick_partial prompt that drafts from a brick's real fields.

    Everything is read-only — nothing can change content, bricks or settings. Mason is completely unaffected when the MCP Server isn't installed; the tools are simply unavailable. See Documentation → AI assistant.

    Most useful when a brick renders as nothing: Mason fails quietly by design, so a token naming a field that doesn't exist produces no error anywhere. The validator names the exact token instead.

1.5.4

Added

  • Listing filters show how many entries they'd return. Filtering a listing to a category with nothing related to it renders an empty listing, which reads as a broken brick rather than an empty result. The filter picker now shows a count beside every choice and flags the ones that would return nothing, so it's visible before choosing; the count stays on the chip afterwards.
  • Field settings hide what doesn't apply. Toolbar and Custom tools disappear on a Bricks-Only field, and Custom tools appears only when the Toolbar preset is Custom — so you're no longer offered settings that will be ignored.

Fixed

  • Brick controls (toggle / duplicate / delete) had drifted to the left of the header bar on any brick without tabs; they're pinned right again.
  • The editor's canvas had uneven gutters (a wider lane on the left reserved for the insert +). The + now straddles the field's left edge, so the text sits evenly between both margins.

1.5.3

Fixed

  • Unreadable content is no longer silently replaced with an empty field. If a Mason field's stored JSON couldn't be parsed, it was handed to the editor as an empty document — and the author's next save wrote that emptiness over the original, destroying any chance of recovery. Nothing warned anyone.

    The publish form now detects stored content it cannot read and shows a warning in place of the editor, with the raw value displayed read-only so it can be copied out. The value is passed through untouched, so saving the entry preserves it and the rest of the entry stays editable.

    This complements 1.5.2: that release removed the most likely cause of such damage (silent column truncation); this one makes any remaining case visible and survivable instead of invisible and permanent.

1.5.2

Fixed

  • Long entries could silently lose their content on save. Mason stores an entire ProseMirror document as JSON, which runs several times the length of the prose an author sees — but EE gave the field a TEXT column, capped at 65,535 bytes. On a server not running MySQL in strict mode, anything past that was truncated silently: the stored JSON became unparseable, and because an unparseable document falls back to an empty one, the author's work simply vanished with no error shown anywhere. Long pages — documentation, an entry built from many bricks — were the ones at risk.

    Mason now declares a MEDIUMTEXT column (16MB). New fields get it automatically; existing fields are widened by the add-on update, so run the update in Add-Ons after upgrading rather than only replacing files.

    Note this cannot recover content already truncated — that data is gone from the database. Re-enter anything lost before upgrading.

1.5.1

Fixed

  • Braces in content no longer break the template. EE's parser scans a fieldtype's output, so a literal { written by an author was lexed as the start of a tag. Content that showed EE syntax — documentation, a tutorial, a JSON snippet — took the whole page down with "You have an invalid conditional in your template… Unexpected 'quot' (VARIABLE); expected RD", because {if brick_type == "callout"} escapes to {if brick_type == &quot;callout&quot;}. Literal braces in authored content are now emitted as &#123; / &#125;, which browsers render as ordinary braces, so authors can write about template code without breaking the page displaying it.

    Note this also means EE tags typed into a Mason field are shown as text rather than executed. That was never supported, and treating author input as data rather than as template code is the safer default.

1.5.0

Added

  • Per-field editor toolsets. A new Toolbar setting beside Editor Mode scopes the rich-text tools to each field — Basic, Standard, Full, or Custom with a per-tool checklist. Previously every Mason field got the same fixed toolbar; this is the field-level equivalent of EE's RTE configurations. Defaults to Full, the toolbar every field already had, so nothing changes on upgrade. Narrowing a toolbar only hides buttons — it never unloads editor features, so formatting already in a field (tables, images, code blocks) keeps loading and saving intact.
  • Author-facing category filter on Listing fields. When a listing brick sets a Filter field, the brick now shows a category picker, so one listing brick can be reused across pages pointed at a different category each time. The picker searches only the channel that relationship field targets. Clearing it lists everything. The render side already honoured a filter value; there was simply no way to set one.

Fixed

  • Listing bricks could lose their channel when edited in the CP. The brick builder's Channel dropdown is keyed by channel id, but a listing's channel may be stored as a channel name (both have always been valid, and CLI- or script-authored definitions write the name). A name-stored value matched no option, so the dropdown rendered blank — and saving the brick from that state wrote an empty channel, silently reducing the listing to no results. The dropdown now resolves either form for display and leaves the stored value alone unless a channel is actually picked.
  • The Listing field's summary had no styling of its own, and on the publish page it couldn't resolve the channel's name — it now reads e.g. "Lists Portfolio".

1.4.1

Added

  • Richer rich-text toolbar. The Full preset now adds underline, H4, text alignment (left/center/right), subscript/superscript, horizontal rule, text color, and highlight on top of the existing tools. Standard gains underline + headings.
  • Per-block class & style (Full toolbar). New .class and style tools apply a freeform CSS class or inline style to the specific block the cursor is in (<p>, heading, quote, list) — rendered straight onto that element. An inline style on the element itself overrides theme rules a wrapper class can't.

Fixed

  • Rich-text fields wouldn't accept typing or paste — the field wrapper was a <label>, which redirected clicks to a toolbar button and stole focus from the editor. The wrapper is now a <div>.
  • Rich-text lists rendered with the bullet stacked above the text (a CP reset had flipped list-style-position to inside); forced back to outside.
  • The brick Duplicate action's clone icon was mis-sized / off-center in the Bricks list toolbar.

1.4.0

Added

  • Duplicate a brick — each row in the Bricks list has a clone action that copies the whole brick definition (fields, tabs, binder flag) under a fresh unique handle and opens the copy in the editor. Ideal for building bricks that differ only slightly (e.g. a variant tweak) without rebuilding the field set by hand.
  • Relationship fields expose the related entry's fields — a relationship / entry kind now surfaces the picked entry's custom fields to templates. A single-pick relationship flattens to {brick:short_name:field} sub-variables (files resolve to a URL) alongside the entry title, so a brick can pull a cover image, client, year, etc. straight off the related entry.

Fixed

  • File fields no longer broadcast an upload to every other file field on the same page — each field's control is namespaced and scoped.
  • Saved file values re-display correctly on reload (value-populated file protos).
  • The brick builder auto-assigns unique handles to new columns / fields and no longer pre-fills placeholder names, so new columns and fields start empty.

1.3.0

Added

  • Binders — reusable, bindable field-sets. Flag a brick Binding only (binder) and it becomes a binding-only definition (hidden from the insert menu) whose fields can be bound into other bricks. A bound binder appears as a locked, tinted, badged block that moves as a single unit (drag to reorder, drop into a tab) and keeps its own field organization. It's a live reference — editing the binder updates every brick bound to it — and its fields merge flat into the host's {brick:short_name} namespace. The builder warns on short-name clashes and on dangling references.
  • Listing (channel query) field kind — turns a field into a live loop of channel entries resolved at render time (portfolio grids, blog lists, category indexes). Settings: channel, limit, offset, order by, sort, link prefix, and an optional relationship filter field. Each row exposes id, title, url_title, url, a formatted date, and every custom field by its short name (files resolve to a URL). Loops with the same syntax as Grid.
  • Documentation — in-CP docs expanded to cover Binders, the Listing kind and its settings, and Listing loop templating with a worked example.

1.2.0

Added

  • Managed brick groups — groups are first-class DB entities (a group_id column plus exp_mason_brick_groups). The Bricks sidebar mirrors EE's Fields page: create, rename, delete, and filter by group; deleting a group only ungroups its bricks. Groups also organize the in-editor insert menu.

Earlier releases

The initial release line established Mason's core: a global brick library reused across fields; the field-kind set (text, textarea, rich text, number, URL, email, toggle, select/multi-select/radio/checkboxes/button group, color, range, date, link, grid with two-level nesting, relationship, file/asset, and entry); per-brick tabs; conditional fields; per-field width and editor height; the native-EE brick builder (searchable Lucide icon picker, toggles, drag reorder); slash-command and canvas brick insertion; front-end templating ({mason_field}, {bricks} loops, grid loops, sub-value tokens, and custom templates/bricks/<handle>.html overrides); and in-CP documentation.