Changelog
From the plugin documentation · View source ↗
All notable changes to FlowEdit are documented in this file.
1.0.0-rc.1 - 2026-09-15
First release candidate for FlowEdit 1.0 on Grav 2 + Admin2. This prerelease focuses on reliable authoring, rendering, and a stable storage contract. Existing pages require no migration. See release readiness for the remaining manual checks and RC soak before a stable release.
- Simplify editor block cards with soft surfaces, compact accessible actions, and sanitized Markdown previews instead of code-style field summaries.
- Hide the text-formatting bubble when selecting a whole block; retain text selection formatting and dedicated media controls.
- Protect FlowEdit content saves against stale Markdown baselines through the API’s pre-save event, without modifying Admin2 or API files. Keep conflicted drafts visible, offer Markdown selection and confirmed saved-content reload, and retain protection when saving from another Admin2 form tab. Metadata-only saves remain the host’s responsibility; page storage is unchanged.
- Keep multiline prose and Markdown pastes out of spreadsheet conversion; preserve explicit HTML tables and tab-separated data.
- Fit slash menus above or below the caret within the visible editor, including after scrolling; keyboard selection scrolls only the menu.
- Render images, galleries, video, supported embeds, and structured blocks inside columns while retaining author-HTML sanitization; preserve empty columns.
- Preserve inline formatting in aligned paragraphs/headings across save/reopen.
- Render WebM/Ogg video accepted by the upload control with the correct MIME type.
- Make stock/Klipy key inputs password fields; verify the required API’s read masking and unchanged-secret preservation. Editor settings omit all keys.
- Identify crop derivatives by coordinates, dimensions, and source fingerprint; bound provider downloads during transfer; send Klipy share JSON correctly after authorized storage succeeds.
- Back up Markdown-source drafts and flush on unmount; release block-handle listeners/timers and ignore stale settings responses after navigation.
- Show legacy HTML blocks as read-only visual previews with an en/de warning. Preserve existing source; document explicit replacement without auto-conversion.
- Freeze the Markdown/shortcode, custom-field, and theme-override storage contract; refresh product copy and add the 1.0 release acceptance checklist.
- Add API/security and translation verification scripts alongside rendering regressions. No page migration is required; older crop files remain valid.
0.14.0 - 2026-09-14
- Block handles (Notion-style): hovering a top-level block shows a grip at
the editor’s edge. Click it for a menu — Turn into (text, headings 1–3,
bullet/numbered list, quote; hidden for structural blocks like tables,
columns, and media where conversion would destroy data), Duplicate, and
Delete — or drag it to reorder the block with a drop indicator line
(Esc cancels). New pure
src/field/block-actions.ts(turn/duplicate/ delete/move on a TipTap doc) with jsdom tests,ui/block-handle.tscontroller, and agrip-verticalLucide icon. - Draft recovery: unsaved edits are mirrored to localStorage (debounced,
per page route) while the document differs from what was loaded. Reopening
the editor after a crash or accidental navigation offers a banner —
Restore applies the draft and enables Admin2’s save; Discard clears it.
A draft that matches the loaded page means it was already saved and is
cleaned up silently. New pure
src/field/draft.tsstorage helpers with tests and aui/draft-recovery.tscontroller. - Markdown paste: pasting a multi-line markdown document (from a
.mdfile, VS Code, etc.) now parses into real blocks — headings, lists, quotes, code fences, and pipe tables arrive as their block types instead of one merged paragraph of literal*stars*and#characters. The stock clipboard hook only parsed inline (and only for text/plain), so highlighted HTML from code editors bypassed it entirely. New puresrc/field/paste-markdown.ts(conservative block-signal gate + block-mode parse through the editor’s own markdown-it) with jsdom tests. - Fixes: block-menu items are clickable again — the outside-click closer
now inspects
event.composedPath()instead ofevent.target, which shadow DOM retargets to the host element (every mousedown, including on menu items, dismissed the menu before the click fired). Regression test mounts the real field and clicks a turn-into item end to end. The block-handle grip no longer vanishes (and unarms) when the pointer moves onto it — thehiddenattribute vsdisplay:flexconflict is resolved and the last hovered block stays actionable, so the menu opens and drag-reorder works (tables included; the indicator line now also shows over the source block as no-op feedback). Handles appear on atom node views (gallery, custom blocks, embeds) via a DOM-walk fallback whenposAtCoords()has no caret position. Gallery/accordion/tab list-item reorder buttons use Lucide arrows instead of mojibake glyphs (all double-encoded characters in the source tree fixed). The table of contents block renders a “No headings on this page yet.” note instead of an empty box when there is nothing to list. - Self-hosted video: the video modal now shows the upload drop zone —
drop or browse an MP4 / WebM / Ogg file to upload it into the page’s media
and fill the video source in one step (the upload panel’s copy and the file
picker filter switch to video wording while the modal is open). Typed URLs
and Vimeo IDs are unchanged. New
isVideoLikeguard inmedia.tsand avideooption onuploadPageMedia. - Gallery lightbox: on the rendered site, clicking a
[flow-gallery]slide opens a lightbox (js/flow-gallery.js+css/flow-lightbox.css, loaded only on pages with a gallery) with the full-size image, caption, prev/next buttons, a position counter, Arrow/Esc keyboard navigation, backdrop-click close, body scroll lock, and focus restore. Vanilla JS, no dependencies; aria-modal dialog semantics. - Table of contents block: new
toccustom block (slash menu: Table of contents, Lucidelisticon) with optional title and min/max heading-level fields. The block’s Twig emits an empty<nav class="flow-toc">marker;classes/TocBuilder.phpfills it at render time (after shortcodes) with the page’s rendered headings — including headings inside[flow-block]/[flow-columns]bodies — and injects matchingidattributes. Slugs are kebab-case exactly like the editor’s#anchorlink suggestions, duplicate headings get-2suffixes, and headings that already have an id keep it. Styles ship incss/flow-blocks.css. - Responsive tables: rendered
<table>markup (Markdown tables and tables inside[flow-block]/[flow-columns]bodies alike) is wrapped at render time in a keyboard-focusableflow-table-scrollregion, so wide tables pan horizontally on small screens instead of breaking the layout. New pureclasses/TableWrapper.php(outermost tables only, idempotent) hooked toonPageContentProcessedafter shortcode-core, pluscss/flow-tables.css. - Crop aspect presets: the image modal’s crop tools gain 1:1 / 4:3 / 16:9
presets next to the free Draw mode. The drawn rectangle and all resize
handles keep the chosen ratio (anchored at the drag-start corner when
drawing, at the opposite corner when resizing); Draw stays free-form and
None clears the crop as before. New ratio math in
crop-overlay.ts(boxFromCornersWithRatio,resizeBoxWithRatio) with pure-math tests.
0.13.0 - 2026-09-14
- Text alignment: the text bubble toolbar gains left / center / right /
justify buttons (Lucide icons) applied to the current paragraph or heading
via
@tiptap/extension-text-align. Aligned blocks persist as inline HTML (<p style="text-align:center">,<h2 …>) through custom paragraph/heading Markdown serializers — the stock serializers drop node attributes, which made alignment changes invisible to the save button. The markup round-trips through the Markdown (HTML passthrough) and renders on the site without theme CSS. Images also gain aflow-imageclass and newcss/flow-image.css, soalign=/width=placement (left / center / right auto-margins) renders on the front end like it already did for gallery and video; gallery/video pick up the same placement rules in the editor. - Lucide icons: editor chrome (bubble toolbar, slash menu, find bar, media
carousel, YouTube play, stock search, upload drop) uses curated Lucide SVGs
instead of unicode glyphs. Custom-block
icon:and theiconfield type prefer Lucide kebab-case names (e.g.lightbulb, max 48 chars) with emoji still accepted as a fallback; shipped block defaults migrate to Lucide names. Newsrc/field/icons.tsregistry +lucidedependency (tree-shaken). - Klipy GIF picker: with a Klipy app key (
klipy_api_key, free tier from the Klipy partner panel), the GIF media modal gains a Klipy GIFs section — trending GIFs on open, debounced library search, and one-click download into page media (no hotlinking; dedupes by slug). The app key stays server-side:GET /flow-edit/gifsproxies search (permissionapi.pages.write,content_filter=medium),POST /flow-edit/gifs/downloadfetches from Klipy’s static delivery hosts only (static.klipy.com,static1/2.klipy.com), enforces a 20 MB cap and gif/webp content-type check, authorizes the page update like the crop endpoint, and fires Klipy’s share trigger (best-effort) so picks are counted. Per Klipy’s attribution guidelines the search placeholder is literally “Search KLIPY” and the section carries a Powered by Klipy mark. NewKlipyGifs.php(HTTP + normalization),gifs.ts+gif-picker.ts(client), and aklipy_enabledflag in the settings payload. - Content review panel (Workflow): a new Review toolbar button opens an
on-demand scan of the document — broken internal links (checked against a
flat route index), plain-HTTP and unsafe-scheme links, images without alt
text (
and[flow-image]), and empty headings. Findings are grouped with severity dots (error/warning/info) and click a finding to jump straight to it in the editor; a badge on the toolbar button shows the error+warning count of the last scan. NewGET /flow-edit/routesendpoint (permissionapi.pages.write, 20k cap) feeds the internal-link check;link-check.ts(from the link-intelligence phase) is now wired to it, and the new purecontent-check.tscovers the accessibility checks. When the route index fails to load, internal links are skipped (never flagged as broken). Fenced code blocks are excluded from all checks. - Stock photo search (Media Pro): configure a free stock provider
(
stock_provider:pexels/pixabay/unsplash+stock_api_key:) and the image media modal gains a Stock photos section — debounced search, paginated results with author credits, and one-click download into page media (no hotlinking; dedupes by provider id). The API key stays server-side:GET /flow-edit/stockproxies/normalizes search results (permissionapi.pages.write),POST /flow-edit/stock/downloadfetches from allow-listed provider hosts only (images.pexels.com,pixabay.com/cdn.pixabay.com,images.unsplash.com), enforces a 20 MB cap and image content-type check, and authorizes the page update like the crop endpoint; Unsplash downloads fire the provider’s download trigger for photographer credit. Photo ids are normalized to strings (Unsplash ids are alphanumeric). NewStockPhotos.php(HTTP + normalization),stock.ts+stock-picker.ts(client), and astock_enabledflag in the settings payload. - Custom blocks Pro: four new field types —
color(native picker,#rrggbb),number(integers/decimals),date(ISOyyyy-mm-dd), andicon(emoji with quick-pick presets, max 4 code points) — all stored as plain shortcode attributes and sanitized on read (sanitizeBlockFieldValue). Custom blocks also accepticon:(emoji shown in the slash menu and on the block card) andcategory:(slash-menu grouping; blocks sort by category, then label). The five shipped blocks now carry icons/categories. - Save as snippet: custom block cards gain a Save as snippet action that
stores the filled-in block (as shortcode text) under a chosen name in
localStorage(saved-snippets.ts, capped at 50). Saved snippets appear in the slash menu under Saved (★ icon) with a × removal affordance and insert through the normal snippet pipeline. - Embeds: new
/embedslash command (plus pasting a supported URL onto an empty selection) inserts allow-listed third-party embeds — X/Twitter posts, GitHub Gists, Spotify tracks/albums/playlists, and Google Maps. Stored as[flow-embed url="…"];FlowEmbedShortcodere-validates the URL against the same provider table server-side (https-only, no credentials) and renders a lazyiframewith front-end CSS incss/flow-embed.css. New built-in togglebuiltin_blocks.embed; client logic inembed.ts. - Snippets: user-configured
snippets:(label, keywords, markdown body) appear in the slash menu under “Snippet” and insert their body (throughpreprocessMarkdown, so nested flow shortcodes become editor blocks) at the cursor. Two examples ship inflow-edit.yaml. - Page starter templates: user-configured
page_templates:(label, template name, body) pre-fill the editor on the Admin2 new-page screen when the document is empty and the starter’s template matches (empty/*= any; one-shot per tab via sessionStorage). All starters are also listed in the slash menu under “Starter”. Normalized server-side byclasses/Snippets.phpand returned fromGET /flow-edit/settingsassnippets/starters. - Link popover suggestions: the link URL field now autocompletes internal
pages (matched by title and route via
GET /flow-edit/pages, permissionapi.pages.write, title scan capped) and#anchorsfor the current page’s H1–H3 headings. New helpers:link-check.ts(pure Markdown link analysis that skipsflow-*shortcode spans; flags broken internal routes, plain HTTP, and unsafe schemes — feeds a future review panel) andpage-search.ts(API fetch + heading collection + slugify). - Columns: new
/columnsslash command inserts a two-column layout; the bubble toolbar gains +C / −C (add/remove the current column, 2–4 total) and ✕C (delete the block) while the caret is inside a columns block. Stored as[flow-columns count="N"]with nested[flow-column]Markdown bodies; rendered server-side viatemplates/flow-blocks/columns.html.twig+css/flow-columns.css(a responsive CSS grid that collapses below 768 px). Column bodies pass through the shared rich-text allow-list. The built-in can be toggled under Built-in slash commands (builtin_blocks.columns). - Internal: the rich-text sanitizer moved from
FlowBlockShortcodeinto a sharedGrav\Plugin\FlowEdit\RichTextSanitizerused by the new columns renderer.
0.12.0 - 2026-09-12
- Fixed: the editor field could fail to load in Admin2 (
[CustomField] Failed to load flow-edit/flow-edit: TypeError) — Admin2 assigns the field value before the element is connected to the DOM; the value accessors now tolerate that and seed the editor on mount. - Fixed: keyboard navigation in the slash-command menu no longer leaves the selection off-screen — the selected item is scrolled into view as you arrow past the menu fold.
- Internal: field source restructured from a single ~4.2k-line module into feature
controllers (
ui/: outline, find, cheatsheet, empty state, bubble, table chrome, slash menu, modal subsystem, editor setup) behind anEditorContextplus slash-command and modal-kind registries. No behavior change. Test suite grew from 32 to 181 tests, including a jsdom lifecycle test that mounts the real field element. - Hardening: crop dimensions capped server-side (4096 px/side); identical re-crops
reuse the fresh derivative instead of re-encoding (a replaced source still forces a
fresh crop via mtime); crop source type verified with
finfobefore the GD re-encode; custom-block rich-text parsing passesLIBXML_NONET;[flow-video]src must end in.mp4on the path (query strings allowed).
0.11.0 - 2026-09-11
- Gallery polish: ordered slide list in the insert/edit modal with drag / ↑↓ reorder, per-slide alt and caption, remove, and lightbox preview (node thumbs + modal). Selecting a gallery or video node shows the media bubble width/align controls.
- Undo clarity: preview/cache
setContentrefreshes and external value sync skip the history stack (addToHistory: false), so undo after insert/crop/replace undoes the media change itself rather than a silent reparse. Crop derivatives still remain on disk.
0.10.0 - 2026-09-11
- Links: in-bubble edit popover (URL, open in new tab, unlink); paste a URL onto
selected text to apply a link.
target="_blank"is kept withrel="noopener noreferrer"(may drop on Markdown round-trip depending on serializer). - Tables: bubble actions for merge/split, header row/column toggle, and delete table; paste TSV or simple HTML tables from spreadsheets (merged Excel cells flatten to a grid). Floating + Row / − Row / + Col / − Col chrome under the active table (visible with a caret in a cell, when the bubble would otherwise stay hidden).
0.9.0 - 2026-09-11
- Outline: toolbar toggle opens a heading navigator (H1–H3); click jumps to the
heading. Open state persists in
localStorage. - Find & replace: toolbar bar + Ctrl/Cmd+F / Ctrl/Cmd+H; works in Visual and
Markdown. Replaces operate on the Markdown string and skip
flow-*shortcode spans. - Onboarding: empty-document tips (
/, headings, media drop) and a?keyboard cheatsheet dialog; placeholder string is i18n’d.
0.8.0 - 2026-09-11
- Editable Markdown: source mode uses an editable textarea; switching Visual ↔ Markdown keeps content in sync (TipTap ↔ Markdown round-trip).
- i18n: modal, toolbar, crop, and media-list strings use
PLUGIN_FLOW_EDIT.*keys with en/de ICU entries (Admin2__GRAV_I18N.twhen available, English fallbacks otherwise). - Media performance: General Media loads only when the section is opened, with
API pagination (
per_page: 24) and Load more; page media strips window thumbs in chunks.
0.7.1 - 2026-09-11
- Crop UI: image insert/replace modal uses None | Draw on the preview
(replacing the center-crop checkbox). Draw a rectangle, resize with handles, and
move the selection; Insert writes a page-media derivative via
POST /flow-edit/cropthen inserts that file. Crop is page media only (not remote URLs or site library). Undo does not delete the derivative file. - Replace preview: double-clicking a page-local image seeds the modal preview again.
0.7.0 - 2026-09-11
- Authoring: paste/drop images into the editor; slash command recents; Duplicate on custom blocks and list items; drag-reorder list items; clearer pending/broken image states; undo-friendly image replace.
- Gallery:
[flow-gallery]with nested[flow-slide src]slides, slash insert, editor node, and front-endcss/flow-gallery.css. - Video:
[flow-video]for page/site MP4 and Vimeo (YouTube unchanged), pluscss/flow-video.css. - Crop: optional center-crop derivative via
POST /flow-edit/cropbefore insert. - SVG: optional
allow_svgsetting (default off); SVG only as<img src>.
0.6.4 - 2026-09-11
- Fixed page-local
[flow-image]previews in Admin under subdirectory installs (e.g./cms): Grav media URLs like/user/pages/…are prefixed with the site base so Visual mode can load uploaded page media. - Restyled the image modal file picker as a drop-to-upload zone (click or drag).
- Pin Cancel/Insert to the bottom of the insert dialog while body content scrolls.
- Keep page/site media pickers out of non-image dialogs (
[hidden]was overridden bydisplay: gridon.flow-media-list).
0.6.3 - 2026-09-11
- Image/GIF insert modal lists already-uploaded page media and site media
(
GET /media) as a thumbnail grid so authors can pick without re-uploading. - Page/site picks update the layout Preview (click to select, Insert to confirm, double-click to insert immediately) and prefer the public media URL for thumbnails so previews are not blank.
- Media picker UI: collapsible Page Media / General Media sections with horizontal scrolling lists, plus a styled Upload or URL panel. Uploads select for preview (Insert confirms) instead of inserting immediately.
0.6.2 - 2026-09-11
- Reject protocol-relative
[flow-image]srcvalues (//host/…) on the server renderer, matching the editor allow-list; add regression coverage. - Add Admin2 ICU translations (
languages/en.yaml,languages/de.yaml) and replace hardcoded blueprint labels/help withPLUGIN_FLOW_EDIT.*/PLUGIN_ADMIN.*keys.
0.6.1 - 2026-09-01
- Render custom-block Markdown bodies as sanitized rich text instead of visible escaped HTML; unsafe elements, attributes, and URL schemes are removed before the value is marked safe for Twig.
- Isolate nested
[flow-item]state per page and block occurrence, and generate unique stable DOM identifiers for repeated accordion/tab blocks. - Add accessible modal dialog semantics, keyboard focus trapping/restoration, Escape handling, labelled controls, native radio-group semantics for CSS-only tabs, and live Admin2 LTR/RTL direction updates.
- Log Twig rendering failures with block/template context instead of silently swallowing them.
- Fix YouTube title/caption entities being double-encoded during editor round-trips.
- Add automated Markdown round-trip and full Grav shortcode rendering regression tests.
0.6.0 - 2026-09-01
- Added a list custom-block field type for repeating title + body items,
stored as nested
[flow-item]shortcodes (max 32). - Shipped default accordion (native
<details>, exclusive or multiple open) and tabs (CSS-only radio panels) blocks with theme-overridable Twig and minimalcss/flow-blocks.css.
0.5.4 - 2026-09-01
- Allow
mailto:links in the editor dialog, TipTap paste/parse allowlist, and structured block URL fields.
0.5.3 - 2026-09-01
- Send both
X-API-Tokenand Bearer authorization headers for Admin2 settings, media listing, and media uploads, preserving authentication on FastCGI hosts. - Replace the native link prompt with the Admin2 form dialog and reject unsafe, protocol-relative, or unsupported link schemes.
- Reject protocol-relative structured-block URLs, exclude SVG from FlowEdit’s media picker/upload control, and constrain configurable Twig template paths.
- Replace hand-written class includes with a PSR-4 autoloader.
- Add Grav 2 compatibility plus GPM project and author contact metadata.
0.5.2 - 2026-08-31
- Hardened Admin2 rendering of configurable slash-menu labels and legacy HTML block previews. Preview markup now uses an inert formatting allow-list that removes attributes, interactive controls, and resource-loading elements.
- Fixed legacy
flow-mediamigration so remote and root-relative image sources retain their complete URLs instead of being reduced to page-local basenames. - Fixed
[flow-image]entity handling so URLs, alt text, and captions round-trip without double encoding; remote image query parameters now render correctly. - Fixed pasted HTTPS images discarding the alt text entered in the media modal.
0.5.1 - 2026-08-31
- Added Built-in slash commands toggles (paragraph, headings, lists, quote, code, divider, table, image, GIF, YouTube). Disabled items are hidden from the slash menu only; existing Markdown still renders on the front end.
- Added developer documentation for creating custom blocks (docs/custom-blocks.md).
0.5.0 - 2026-08-31
- Custom blocks are now structured: typed fields, stored as
[flow-block …]shortcodes, rendered via Twig undertemplates/flow-blocks/. - Themes override block markup by shipping the same Twig path (e.g.
templates/flow-blocks/callout.html.twig). - Per-type Enabled toggle: disabled types are hidden from the slash menu and do not render on the front end.
- Ships default callout, cta, and pullquote block definitions.
- Legacy
<!--flow-block:…-->HTML markers still edit and round-trip; new inserts use structured shortcodes. Freeform config entries withoutid/fieldsare ignored by the settings API.
0.4.1 - 2026-08-31
- Alt text and caption controls now appear only for media modals (images, GIFs, and YouTube); custom-block editing no longer shows them.
- YouTube embeds support an optional caption (stored on
[flow-youtube]and rendered asfigcaption). Video title remains the accessibility label.
0.4.0 - 2026-08-31
- Fixed page-local images not previewing in Admin (resolve media API URLs in the editor node view).
- Browse/upload no longer uses the filename as alt text (default alt is empty).
- Added optional Alt text and Caption fields on image/GIF insert and replace.
- Non-default width/align or a caption now stores as
[flow-image …], rendered via Shortcode Core with Grav page-media URL resolution (fixes broken relative HTML<img>markup on the front end).
0.3.0 - 2026-08-31
- Added width (25–100%) and left/center/right alignment for images, GIFs, and YouTube embeds, editable in the insert/edit modal (with live preview) and via the selection bubble.
- Non-default image layout stores as HTML
<img class="flow-media …" style="width:N%">; defaults still use Markdown. - Extended
[flow-youtube]with optionalwidthandalignparameters; the shortcode renderer emits matching figure classes and inline width.
0.2.0 - 2026-08-31
- Added slash Table support via TipTap table extensions and row/column bubble actions.
- Added configurable custom blocks (buttons, banners, …) on the plugin
settings page; exposed to the editor via
GET /flow-edit/settings. - Custom blocks store markup between
<!--flow-block:…-->markers and show an editable preview card in the visual editor. - Double-click images/GIFs to replace via page media, upload, or URL.
- YouTube nodes show a thumbnail, play in-editor (privacy-enhanced embed), and
support edit of id/title while keeping
[flow-youtube]storage.
0.1.0 - 2026-08-27
- Added opt-in FlowEdit registration for Grav Admin2 while preserving Default, CodeMirror, and third-party editor choices.
- Added TipTap-based Markdown WYSIWYG with slash commands, bubble formatting toolbar, and a read-only Markdown source inspector.
- Field source is TypeScript (
src/field/) built toadmin-next/fields/flow-edit.js. - Added page-media image/GIF pick and upload via the Grav API, storing page-local Markdown image syntax.
- Added strict
[flow-youtube]storage and a Shortcode Core renderer that emits a privacy-enhanced YouTube embed. - Declared Admin2, API, and Shortcode Core compatibility requirements.