flowedit.
FLOWEDIT DOCUMENTATION

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.ts controller, and a grip-vertical Lucide 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.ts storage helpers with tests and a ui/draft-recovery.ts controller.
  • Markdown paste: pasting a multi-line markdown document (from a .md file, 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 pure src/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 of event.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 — the hidden attribute vs display:flex conflict 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 when posAtCoords() 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 isVideoLike guard in media.ts and a video option on uploadPageMedia.
  • 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 toc custom block (slash menu: Table of contents, Lucide list icon) with optional title and min/max heading-level fields. The block’s Twig emits an empty <nav class="flow-toc"> marker; classes/TocBuilder.php fills it at render time (after shortcodes) with the page’s rendered headings — including headings inside [flow-block] / [flow-columns] bodies — and injects matching id attributes. Slugs are kebab-case exactly like the editor’s #anchor link suggestions, duplicate headings get -2 suffixes, and headings that already have an id keep it. Styles ship in css/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-focusable flow-table-scroll region, so wide tables pan horizontally on small screens instead of breaking the layout. New pure classes/TableWrapper.php (outermost tables only, idempotent) hooked to onPageContentProcessed after shortcode-core, plus css/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 a flow-image class and new css/flow-image.css, so align=/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 the icon field 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. New src/field/icons.ts registry + lucide dependency (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/gifs proxies search (permission api.pages.write, content_filter=medium), POST /flow-edit/gifs/download fetches 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. New KlipyGifs.php (HTTP + normalization), gifs.ts + gif-picker.ts (client), and a klipy_enabled flag 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. New GET /flow-edit/routes endpoint (permission api.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 pure content-check.ts covers 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/stock proxies/normalizes search results (permission api.pages.write), POST /flow-edit/stock/download fetches 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). New StockPhotos.php (HTTP + normalization), stock.ts + stock-picker.ts (client), and a stock_enabled flag in the settings payload.
  • Custom blocks Pro: four new field types — color (native picker, #rrggbb), number (integers/decimals), date (ISO yyyy-mm-dd), and icon (emoji with quick-pick presets, max 4 code points) — all stored as plain shortcode attributes and sanitized on read (sanitizeBlockFieldValue). Custom blocks also accept icon: (emoji shown in the slash menu and on the block card) and category: (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 /embed slash 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="…"]; FlowEmbedShortcode re-validates the URL against the same provider table server-side (https-only, no credentials) and renders a lazy iframe with front-end CSS in css/flow-embed.css. New built-in toggle builtin_blocks.embed; client logic in embed.ts.
  • Snippets: user-configured snippets: (label, keywords, markdown body) appear in the slash menu under “Snippet” and insert their body (through preprocessMarkdown, so nested flow shortcodes become editor blocks) at the cursor. Two examples ship in flow-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 by classes/Snippets.php and returned from GET /flow-edit/settings as snippets/starters.
  • Link popover suggestions: the link URL field now autocompletes internal pages (matched by title and route via GET /flow-edit/pages, permission api.pages.write, title scan capped) and #anchors for the current page’s H1–H3 headings. New helpers: link-check.ts (pure Markdown link analysis that skips flow-* shortcode spans; flags broken internal routes, plain HTTP, and unsafe schemes — feeds a future review panel) and page-search.ts (API fetch + heading collection + slugify).
  • Columns: new /columns slash 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 via templates/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 FlowBlockShortcode into a shared Grav\Plugin\FlowEdit\RichTextSanitizer used 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 an EditorContext plus 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 finfo before the GD re-encode; custom-block rich-text parsing passes LIBXML_NONET; [flow-video] src must end in .mp4 on 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 setContent refreshes 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 with rel="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.t when 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/crop then 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-end css/flow-gallery.css.
  • Video: [flow-video] for page/site MP4 and Vimeo (YouTube unchanged), plus css/flow-video.css.
  • Crop: optional center-crop derivative via POST /flow-edit/crop before insert.
  • SVG: optional allow_svg setting (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 by display: grid on .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] src values (//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 with PLUGIN_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 minimal css/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-Token and 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-media migration 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 under templates/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 without id/fields are 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 as figcaption). 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 ![alt](src).
  • Extended [flow-youtube] with optional width and align parameters; 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 to admin-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.