Changelog

What’s new in Leiho.

Every release, with what was added and fixed. The plugin and the leiho package share one version number.

1.0.0-rc.5 2026-11-09 9 added

Added

  • Security tab. The "WordPress as a back office" options move out of Settings into their own tab, next to new hardening options: - Turn off the theme and plugin file editor (same as DISALLOW_FILE_EDIT). - Protect the login page: login errors no longer say whether a username exists, and 5 failed attempts block that username from that address for 15 minutes. Behind a proxy such as Cloudflare, the leiho_login_ip filter gives the real visitor address. - Turn comments off: comments and pings closed, Comments menu hidden, comments API closed to visitors. Off by default, since a front end can use comments. - Lock down WordPress now also removes the X-Pingback header and returns a 404 for author pages (?author=1, /author/…/) to visitors, so login names can't be listed. - New installs get the file editor and login protection on. Sites that already have Leiho keep their current behaviour until an administrator changes these options. - Application passwords, used by AI agents, keep working.
  • Leiho Zero. An empty theme shipped with Leiho, for a WordPress that only serves as a back office: no styles, no scripts, no templates, while featured images and menus keep working in wp-admin. One button in the Security tab installs and activates it, and another one restores the previous theme. Before switching, Leiho reads the active theme's code and lists what would stop working with it (register_post_type, acf_add_local_field_group, register_nav_menus…).
  • Login address. In the Security tab, the login page can move to an address of your choice (for example /my-office/). wp-login.php and wp-admin then answer "not found" to visitors, and every login, logout, lost-password and e-mail link points to the new address. Right after saving, a window shows the address with a Copy button, the bookmark shortcut for your system (⌘ D or Ctrl + D), a button to drag to the bookmarks bar and a link to open the login page. The administrator also receives the address by e-mail. Locked out: define( 'LEIHO_DEFAULT_LOGIN', true ); in wp-config.php brings the usual login page back. Off until an address is set. Not available on multisite.
  • Leiho login page. Same look as leiho.dev (black background, lime dot field, Geist font). Each screen has its own title ("Log in to …", "Reset your password", "Choose a new password", "Check your inbox"), "Lost your password?" sits next to the password field, and the button spans the card. WordPress's extras are gone: language switcher, link back to the site, privacy policy link. The leiho.dev badge sits at the bottom of the page. The page now wins over another login branding (plugin, mu-plugin or theme) instead of mixing with it: turn the Leiho login page off in the Security tab to keep your own.
  • Texts follow the fields plugin in use. Leiho tells ACF, ACF PRO and Secure Custom Fields apart and names the right one in the editor. The "Get started" prompt asks the agent to build the content model in that plugin or, when none is installed, in Leiho Collections, with the matching instructions.
  • Edit block-editor content in Leiho, and back in WordPress. Content written with the WordPress block editor was read-only in Leiho. When it's made of common blocks (paragraphs, headings, lists, quotes, images, separators), Leiho now edits it and saves it back as blocks: unchanged blocks are kept as they were, settings included, and edited ones become native blocks the block editor opens without any warning. This works for posts, pages and every content type with a content field. Other blocks (tables, buttons, columns, plugin blocks) show as grey locked boxes inside the text ("Table block — edit it in WordPress") and come back untouched on save, while the rest stays editable. Deleting a box removes its block.
  • Hide content edited in Leiho from the WordPress menu (Security tab, off by default). The content types edited in Leiho, Leiho collections included, leave the WordPress sidebar and the "New" menu. Their screens still open from a direct link.
  • Leiho favicon in wp-admin, on the login page and at /favicon.ico while the site has no icon of its own (Settings → General → Site Icon).
  • Agents can change the new options through leiho/update-settings (headless_comments, headless_menus, security_file_edit, security_login).
1.0.0-rc.4 2026-11-08 1 fixed

Fixed

  • The visual editor opens collections whose pages get their address from a field. A Leiho collection without a URL pattern was treated as having no pages on the site and always opened as a form, even when its entries got their path from the "Field holding the URL" setting or from their own address (typical after an ACF import). A collection now has pages when it has a URL pattern, when the field holding the URL is one of its fields, or when its entries carry their own address.
1.0.0-rc.3 2026-11-06 1 fixed

Fixed

  • "Field holding the URL" works for content types served by Leiho Collections. The path setting read the field from WordPress post meta only. Once a type is served by a Leiho collection (a collection of its own, or an ACF import switched to Leiho), the field lives in the collection's table: entries created afterwards got no path, and a path changed in Leiho kept its old value, so the preview, the headless redirect and the path of the content API didn't match the front end. Leiho now reads the collection's field when the type has one with that name (post meta otherwise), and finds an entry by that path the same way.
1.0.0-rc.2 2026-11-06 10 fixed

Fixed

  • Text fields keep what you typed. Text fields (Leiho Collections text and multiline text, ACF text and textarea edited with Leiho, link labels) went through WordPress's sanitize_text_field(), which changed legitimate values: a < on its own was saved as &lt; (so "< 1 000 UFC/L" showed as "&lt; 1 000 UFC/L" on the site), leading and trailing spaces were removed (words stuck together when a front end builds a sentence from several fields), double spaces were merged and sequences such as %de were dropped. Values are now stored exactly as typed and escaped when they are displayed. What is still removed: HTML tags (their text and the spaces around them stay: a <b>x</b> b gives a x b; a tag left open, such as a <img src=x, is removed up to the end of the value), invalid UTF-8 and control characters; a single-line text still turns line breaks into spaces. Use a code field to keep HTML as typed. Values saved before this fix keep their &lt;: save them again (or run the ACF import again) to correct them.
  • ACF import of such values reports no difference. An ACF text such as "< 1 000 UFC/L" or " suite" now imports identically, so the import no longer stops in needs review because of them.
  • Changing a text field into rich text escapes < and &. The converted value displays the same text instead of being refused; an existing &lt; is not escaped twice.
  • AI translations keep < in text fields instead of writing &lt;, and search descriptions built from text fields keep x<1 or <3.
  • llms.txt and Markdown responses are sent with X-Content-Type-Options: nosniff, so a browser never reads them as HTML.
  • Publishing with Coolify rebuilds the site. Coolify's webhook URL ends in force=false, and publishing content doesn't change the commit: Coolify reused the previous image and skipped the build, so published content never reached the site. Leiho now always asks for a fresh build.
  • ACF import never switches a content type to Leiho when values failed or differ. An import that ends with a refused value or a value served differently used to switch the type to Leiho anyway, so the content API could serve emptied fields and the next build would publish empty pages. Such an import now stops in a needs review state: ACF keeps serving the type, the report (failures and differences) stays available in the import window, the REST status and wp leiho acf-import run, which now exits with an error. Once reviewed, either go back to ACF (fix the values, then import again) or switch anyway with Switch to Leiho anyway, POST /collections/import-acf/accept or wp leiho acf-import accept <type> (run --accept does it in one go).
  • ACF field groups attached to specific entries are part of the import. A group shown on one entry only (location rule Post is equal to …) was ignored by the preview and the import. It is now listed (with its entries), and when such a group defines a field with the same name but a different shape than another group (a repeater here, a flexible content there), the preview names the field and refuses the import instead of losing values. != rules don't attach a group to an entry.
  • Values over an ACF limit are found before the import. ACF only checks limits (maximum rows, minimum rows, maximum length, number bounds, maximum items) in its own editor, so existing data can exceed them, and Leiho refused those values during the import. The preview now lists them with their entries, and the generated collection widens each limit to the existing values, so nothing is refused or lost; you can tighten them later in the collection settings.
  • Headless robots.txt keeps media crawlable. With noindex on, robots.txt blocked everything, including the images a front end loads from /wp-content/uploads/. It now allows the uploads folder; WordPress pages stay out of search engines through X-Robots-Tag.
1.0.0-rc.1 2026-11-05 11 added · 3 fixed · 13 security

Added

  • Languages for Leiho Collections, without duplicating entries. Declare the site's languages once (Collections → Languages, or "i18n": { "default": "en", "locales": ["en", "fr"] } in leiho.schema.json); each root field is translatable (text, rich text, link, list, blocks and group by default) or shared by every language (prices, images, dates, references), with a switch in the field builder. The default language stays where it was; translations live in one wp_leiho_l10n table (entry, language, title, values). Translated entries get prefixed paths (/fr/services/audit), and the front falls back on the original for every field, row or sub-field not translated yet.
  • Edit in every language. The visual editor and the entry form get a language switch with each language's state (not translated, partly translated, original changed): values, title and preview follow the language, empty fields show the original as a hint, and saving writes the translation only. The entry list shows a badge for the languages left to translate.
  • Translate with AI (WordPress 7.0+): fills the texts not translated yet with the site's AI provider and the brand context, keeping HTML tags, product names and length limits; the result arrives as unsaved edits to review, never saved by itself.
  • Translation abilities for agents (MCP): leiho/list-translations (state per entry), leiho/get-translation (original and translation by path), leiho/update-translation (on a published entry it only fills empty translations, as suggestions an editor reviews and saves in the editor: nothing reaches the site before; drafts are free) and leiho/translate-entry (site AI, optional save). leiho/list-collections gives each collection's languages and which fields are translatable.
  • Content API in a language: ?locale=fr on /export, /content and /content/{id} (or /content?path=/fr/…), with locale and translations (path in each language) on every translated item. loadLeihoContent( { locale: 'fr' } ) keeps one snapshot per language, and the generated types describe both properties.
  • Search and AI, from the fields you have. A collection says what its entries are (schema.org type: article, documentation, FAQ, product, event, service, person…) and which fields hold the description, image and questions (Collections → Search and AI, or "seo" in the schema). Every item of the content API then carries a seo block with its title, description, image, FAQ and JSON-LD nodes, in the language of the request. Without a description field, Leiho finds one (summary, intro…), then reads Yoast, Rank Math, SEOPress or All in One SEO, then the excerpt.
  • llms.txt and Markdown from WordPress: GET /wp-json/leiho/v1/llms.txt (?full=1 for every entry's text, ?locale=fr) and GET /wp-json/leiho/v1/markdown?path=… (one entry as Markdown), for front ends rendered on request. leiho/list-collections tells agents which fields make the description and the FAQ.
  • leiho/seo in the package: leihoGraph() builds a page's schema.org graph (site, organization, breadcrumbs and the item's nodes on the canonical address), jsonLd() makes it safe in a <script>, seoMeta() gives the description and Open Graph tags.
  • leiho/ai-ready in the package: after a static build, every page as Markdown next to its HTML, llms.txt and llms-full.txt per language, and a sitemap.xml with hreflang alternates and dates. An Astro integration (aiReady()), a function (buildAiFiles()) and a command (npx leiho ai-ready --site … --dir …). No dependency added.
  • Code field in Leiho Collections: multiline text kept exactly as typed (HTML tags included, where a text field strips them), shown in a fixed-width font with Tab support in the editor, served unchanged by the content API. For code samples and configuration shown on the page: render it as text, never as HTML. Only users who may publish unfiltered HTML (administrators, editors) can put HTML tags in it; authors, contributors and agents get markup_not_allowed.
  • More AI clients in the MCP guide. The AI screen now gives a ready-to-copy configuration for Codex (codex mcp add command and ~/.codex/config.toml block, shared by the CLI, IDE extension and app), VS Code with GitHub Copilot (servers format), Gemini CLI and Windsurf, besides Claude Desktop, Claude Code and Cursor, plus a generic one for any client that runs local MCP servers.

Fixed

  • Moving between Leiho's screens no longer flashes: the menu opens in its saved state before the page is painted (it used to start collapsed and slide open on every page), and supporting browsers cross-fade from one screen to the next with the menu standing still (off with reduced motion).
  • Field type badges in Collections show the type's icon instead of two letters of its translated name.
  • Translations: a row added to a list or flexible field of the original after it was translated can now be translated too (it failed with row_not_found).

Security

  • Content API: password-protected entries are no longer served to visitors (filter leiho_content_include_protected to serve them anyway), ACF password fields are never served, and an anonymous request can no longer make WordPress scan every post (unknown paths) or rebuild a whole export on each call (other languages, types and llms.txt are now cached even without a persistent object cache). /content returns at most 100 items per page.
  • Editor: stored HTML is read in an inert document (the change history and the AI context could run an onerror handler in wp-admin). The bridge sanitizes rich text with an allowlist and only keeps http(s), mailto, tel and relative addresses; links clicked in the preview only open http(s) addresses. Local drafts are kept per user and site, for a week at most.
  • Agents (MCP): translations written by an agent on a published entry are suggestions, applied as edits to review when an editor opens that language, and served only once saved; leiho/update-settings no longer changes the site, preview, build hook or Coolify addresses or the GitHub repository (an administrator sets them in Settings), agent writes always go through wp_kses_post and never change the author or date, and translation and search lists only show entries the user can edit.
  • Data: changing a field's type runs the new type's sanitizing (a code field can't become a script in rich text or a javascript: link); ACF sub-fields Leiho doesn't edit are filtered on save; l10n is reserved as a collection id; uninstalling also drops the translations table.
  • Publishing: saving a draft no longer starts a build (only published content, or content leaving the site, does), and the minimum time between two builds is 15 seconds.
  • Outgoing requests: the site, build hook and Coolify addresses can no longer point at private or local network addresses outside a local or development environment (filter leiho_allow_private_urls), build hooks and Coolify are called without following redirects, and the Coolify address must be https. The GitHub repository setting must look like owner/name.
  • Signed updates: every release archive is signed (Ed25519, over the name, the version and the archive's SHA-256, so an old archive can't be republished as a newer version) by the release workflow, and Leiho checks the signature against the public key it ships before WordPress installs an update; an unsigned or altered archive is refused. Only archives from the official repository's GitHub releases are considered. The release workflow pins its actions by commit and its npm version.
  • Concurrent edits: one save at a time per entry, with the revision checked under that lock (two people editing two rows of the same list no longer overwrite each other); the editor's save route requires the revision.
  • AI requests: the per-user limit is counted under a lock (parallel requests no longer slip through) and a site-wide limit is added (120 per minute, filter leiho_ai_site_rate_limit).
  • Editing routes only work on the content types Leiho edits (settings and Collections), and Duplicate only copies public meta, the featured image, the template and ACF field references (filter leiho_duplicate_meta), never the original's own address.
  • Secrets are encrypted and authenticated with two separate keys derived from the WordPress salts; older secrets are read and re-encrypted on first use.
  • Pickers (parent page, references) no longer list the titles of other authors' drafts or private entries, and a parent must be readable.
  • Change log: entries are kept 180 days (filter leiho_activity_retention_days), anonymized when their user is deleted, and erased with WordPress's personal data eraser.
1.0.0-beta.13 2026-11-04 7 added · 5 fixed

Added

  • AI screen (navigation, administrators): the AI provider's status with its setup link and privacy note, the brand context (moved from Settings, with export and import), the context of every content type Leiho edits — ACF types and pages included, not only Leiho collections — the recent AI actions from the change log, and the abilities offered to external agents. It has its own form: saving it changes nothing else.
  • field(), block() and entry() in the leiho package (also leiho/content and leiho/attrs): they return the annotation attributes as an object to spread on an element — <h1 {...field(page, 'hero_title')}> writes data-wp-post and data-wp-field; options html, refresh, style; block(page, 'sections', i, 'Label') for flexible content rows. Pure functions, usable in server components and at build time. The onboarding, the agent prompts and the integration guide use them.
  • Get started is now an onboarding. Pick your framework (Next.js, Astro, Nuxt, SvelteKit, React + Vite, or anything else), then either follow six steps with code written for it — allow the editor to display the site, install the bridge, read the content, annotate, test the connection — or copy one prompt for your coding agent (Claude Code, Cursor, Codex…), with the framework's snippets and the full integration guide, for an existing or a new site. Progress is tracked as checks pass.
  • AI agents over MCP. The AI screen explains how to connect Claude Desktop, Claude Code or Cursor through the WordPress MCP Adapter, with this site's endpoint and username filled in, and example prompts.
  • New abilities for agents: leiho/search-entries, leiho/create-draft (always a draft, validated like the editor) and leiho/suggest-alt-text (when the provider reads images; saves nothing).
  • Site administration by an agent (administrators, filter leiho_ai_admin_capability): leiho/get-diagnostics (versions, settings, secrets as set / empty / unreadable only, frame headers, GitHub preflight, recent deployments, content API and AI status, and a list of problems with their fix), leiho/update-settings (same validation as the Settings screen, partial updates, dry_run, logged), leiho/set-secret (write-only) and leiho/test-connection. No ability publishes or deploys.
  • Live preview of the site's home page on Leiho's home, rendered at desktop width and scaled to the card; clicking it opens the home page in the visual editor. Shown once the connection test has succeeded, so the site is known to allow being displayed in wp-admin.

Fixed

  • Content client: a snapshot that can't be written (read-only server, serverless rendering) no longer makes loadLeihoContent fail; the content is served and a warning is logged.
  • Tags field: typing a tag that is already chosen and pressing Enter no longer removes it (removing is done with its ×); the typed text is cleared.
  • Entry list: the sort menu (« Dernière modification ») is no longer cut; filter menus take the width of their label and wrap in a narrow column. In French, the sort is now « Modifiés récemment ».
  • Field builder: a long collection name in the header wraps on two lines (full name on hover) instead of being cut.
  • « Go back to ACF » says that WordPress's own fields (title, content, excerpt, slug, categories, tags) changed after the import are not undone.
1.0.0-beta.12 2026-11-04 1 changed · 2 added · 1 fixed

Changed

  • Okno is now Leiho (Basque for window). Another headless CMS already used the name Okno. Plugin leiho/leiho.php, repository pixelersagency/leiho, npm package leiho (was @pixelersagency/okno-bridge), CLI npx leiho, REST namespace leiho/v1, attributes data-leiho-*, hooks leiho_*, classes Leiho_*, text domain leiho, new logo.

Added

  • Migration from Okno. Activated after Okno is deactivated, Leiho takes over Okno's options (values and autoload kept), tables, post and user meta, and re-encrypts its tokens; it runs once, before the headless defaults, so an Okno site is never treated as a fresh install. While Okno is active, Leiho waits and asks to deactivate it. A notice confirms the migration; deleting Okno afterwards leaves Leiho's data alone.
  • Compatibility with Okno until 2.0: okno/v1 REST routes, X-Okno-Token, okno_* filters and actions (deprecated notices under WP_DEBUG), public Okno_* classes, wp okno, admin.php?page=okno…, okno.schema.json files; in the editor, the bridge of Okno (ns: 'okno'); in the bridge, data-okno-* attributes, initOknoBridge and OknoBridge; in the content client, loadOknoContent, OKNO_URL, OKNO_TOKEN and the .okno/ snapshot.

Fixed

  • On databases that cannot rename a table (the WordPress SQLite driver, used by Playground, accepts ALTER TABLE … RENAME TO without doing it), Okno's tables are kept under their name and found by Leiho, instead of being reported as renamed.
1.0.0-beta.11 2026-11-04 10 added · 3 changed

Added

  • AI in Okno, through WordPress's AI client (WordPress 7.0+): Okno uses wp_ai_client_prompt() and whatever provider the site configured in Settings → Connectors. It stores no API key and chooses no provider. Without WordPress 7.0, when wp_supports_ai is false, or when no configured provider can generate text, the AI tools are hidden; administrators get Set up an AI provider (Settings → Connectors) on the Collections AI button and in the settings.
  • Brand context (Settings → Brand context (AI), administrators): brand name, activity and offer, audience, tone of voice, words to prefer, words to avoid, forbidden claims (one per line, matched without case or accents at the start of a word — “certifié” also flags “certifiée”, “4 h” also flags “4h” and “4 heures”; /pattern/i for a regular expression), mandatory facts, language and up to three sample texts. Export (JSON) / Import…. The settings say which provider state applies (ready, not configured, unavailable) and what is sent to the provider.
  • Per-collection AI context: purpose and readers, in the collection's General settings (saved on its own, apart from the schema). Sent with the brand context for entries of that type.
  • AI menu on text fields — text, textarea and rich text, ACF and Okno collections, native title, excerpt and content — in the Collections form and the visual editor's panel, nested cells of lists and blocks included: Generate… (from a short instruction), Rewrite, Shorten, Expand, Fix spelling and grammar, Change the tone…, Translate…, and Suggest a search title / description on SEO-like fields (seo_title, meta_description… detected by name or label) and the excerpt, with the recommended length (60 / 155 characters, or the field's own limit). The prompt carries the brand context, the collection context, the entry's other fields as they are in the editor (unsaved changes included), the field's label, instructions, format (plain text, single line or not, or HTML for rich text) and maximum length.
  • Preview before anything changes: the proposal opens with a word-level diff, a character count against the field's limit, the formatted result for rich text, and a warning that highlights forbidden claims and flags a text that's too long (accepting cuts it at the last word that fits). Accept / Regenerate / Cancel. Accepting writes into the field like typing: undo works, the preview updates, nothing is saved until you save.
  • Alternative text with AI on image fields (saved on the image) and in Media's details panel, when the provider can read images; hidden otherwise.
  • New entry with AI (sparkle button of a collection's entries): a brief, then a structured JSON answer matching the type's fields (text, rich text, choices, yes / no, numbers, lists, groups, blocks with _layout), checked against the schema (lengths, choices, layouts, row limits) and previewed field by field with forbidden claims highlighted, then Create the draft through the editor's save path.
  • Abilities API (category okno), for external AI agents such as the MCP adapter: okno/get-brand-context, okno/list-collections (content types and their fields), okno/get-entry, okno/update-entry-fields (drafts and pending entries only, validated like the editor, revision check, never changes the status). Registered on WordPress 6.9+, with or without an AI provider.
  • REST: GET okno/v1/ai/status, POST ai/field, POST ai/alt, POST ai/entry, POST ai/entry/create, GET|POST ai/brand, GET|POST ai/collections/{type} — nonce, capability (okno_ai_capability, default edit_posts) and rights on the content, bounded inputs, per-user rate limit (okno_ai_rate_limit, 20 requests a minute), generation timeout (okno_ai_timeout, 60 s). Provider errors (timeout, refused key, rate limit, token limit) are turned into readable messages.
  • Change log: each AI action is a line of its own (who, which field, which action — never the prompt or the text), shown in the entry's history and on the home page. They don't count as content changes.

Changed

  • The « changes to publish » dot on the Publish button is dark instead of orange, so it no longer clashes with the lime button.
  • Repeater rows (lists in lists) no longer stack a grey background per level: rows are outlined, nested rows get a left rule, and fields keep the same contrast at every depth.
  • Okno_Activity::lookup() and Okno_Rest::post_item() / can_create_type() are public; Okno_Field_Adapter::describe_type() describes a content type's fields without an entry.
1.0.0-beta.10 2026-11-04 2 fixed · 3 changed

Fixed

  • Escape in a menu or a calendar opened inside an Okno dialog closes only the menu, not the dialog.
  • Settings → Publishing: the radio button of each publishing mode card no longer covers the card's title.

Changed

  • Dates and times are picked with Okno's own picker instead of the browser's (date, date and time, time fields of ACF and Okno collections, and the publish date). The field shows the value in the site's language (« 11 août 2026, 21:56 »), with a calendar icon and a clear button when the field isn't required. The calendar follows the app's theme in light and dark: weeks start on the day set in Settings → General, today is marked, the selected day is lime, days of the neighbouring months are dimmed, days outside a field's minimum / maximum are disabled; the month title opens a month grid, then a year list; Today / Now and Clear actions. Date and time fields get an hour and a minutes menu (24 h or 12 h after the site's time format, the field's step for minutes) and Done. Keyboard: arrows move by day / week, Page Up / Page Down by month (with Shift: by year), Home / End to the start / end of the week, Enter picks, Escape closes and returns to the field. dialog / grid semantics, selected day and month changes announced. The native input stays in the page, hidden, with the same value formats (YYYY-MM-DD, YYYY-MM-DDTHH:MM, HH:MM): nothing changes in what is saved.
  • window.OknoUI.date( input ) enhances a date, date-time or time input; OknoUI.autoSelects() now also upgrades the date inputs it finds, including those added later. The site's language, first day of the week and clock are passed to the scripts as window.OknoUIConfig.
  • Add a field: each field type now shows an icon.
1.0.0-beta.9 2026-11-04 10 added · 8 changed · 4 fixed

Added

  • Articles are fully editable in Collections. The entry form shows WordPress's own fields for the types that support them: Content (WordPress's rich text editor with paragraph / headings, bold, italic, links, lists, quote, alignment and Add media, saved in the classic editor's format), Excerpt (with a character count), URL slug (with a preview of the full address), categories, tags and every other public taxonomy (chips with search; typing a new tag offers « Create “…” », the term is created on save — a new category needs the right to manage categories), Author (users who can write that type; needs edit_others_posts), Publish date (a future date schedules a published entry), and Parent / Order for hierarchical types. Pages get their content too. Everything goes through Okno's save path: one wp_update_post(), server-side sanitization (wp_kses_post() without unfiltered_html), per-field capability checks, conflict detection and the change log. These fields stay out of the visual editor's panel.
  • Content written with the block editor is protected: the form shows a read-only preview and Open in the WordPress editor; the server refuses to overwrite it (blocks_protected). Other fields of the entry (excerpt, taxonomies, slug…) stay editable.
  • REST: POST /okno/v1/preferences (theme: light | dark). Native save paths _content, _excerpt, _slug, _date, _author, _parent, _menu_order, _tax_<taxonomy> on POST /okno/v1/save/{id}.
  • Import from ACF into Okno Collections, one content type at a time (Okno → Collections, Import into Okno on an ACF type, administrators). A preview lists the ACF field groups attached to the type, what each field becomes in Okno and what doesn't carry over (fields without an equivalent such as page_link or taxonomy, conditional logic, default values, media restrictions, location rules other than the content type, ACF Pro repeater options), with the number of entries, fields and stored values. The import creates the collection, then copies the entries in batches with a progress bar, through the editor's validation; refused values are listed with a link to the entry without stopping the import, and every entry is checked afterwards: the JSON Okno serves is compared field by field with what ACF served. An interrupted import resumes where it stopped.
  • After the import the type is served by Okno: /okno/v1/export, /content and /types give the same JSON as before, and the visual editor's dotted paths keep working. Nothing is deleted from ACF (postmeta, field groups and post types stay); the ACF field groups no longer apply to the imported type, on Okno's side and on the classic edit screen.
  • Go back to ACF in the settings of an imported collection: ACF serves its original values again and the Okno table of the collection is deleted. Values edited in Okno after the import are not written back to ACF (the dialog says so).
  • Content types declared in ACF's Post Types screen stay registered (same labels, slug, archive, supports…) when ACF is deactivated after their import; types registered by the theme or a plugin keep their registration, Okno only owns their fields.
  • REST: GET /okno/v1/collections/import-acf, GET …/import-acf/preview, POST …/import-acf/run, GET …/import-acf/status, POST …/import-acf/rollback. WP-CLI: wp okno acf-import list|preview|run|rollback. Filters okno_acf_import_batch and okno_collections_save_revision; actions okno_acf_import_started, okno_acf_imported, okno_acf_import_rolled_back.
  • Clone fields: a seamless clone is imported as the fields it clones, a clone shown as a group becomes a group.
  • Collections schema: external (a content type registered outside Okno, which only owns its fields) and source: "acf" (field and block names keep their ACF API name, camelCase and hyphens included).

Changed

  • The app is at a comfortable scale on large screens: 14 px body text (13 px secondary, 13 px labels, 16–20 px section titles), 36 px fields, selects and buttons, 38 px list rows and navigation links, a 48 px top bar, larger icons and spacing, and a 720 px form column, through new size tokens (--okno-text-*, --okno-control*, --okno-row, --okno-bar, --okno-pane-*, --okno-form-width) shared by every view and the visual editor's chrome. Panes are a little narrower on 1440 px screens so the entry keeps its room; the entries filters no longer cut « Tous les statuts ».
  • Menus instead of native selects everywhere in the app (status filters, sorting, the entry's status, choice fields of forms, settings): a button and a list with a check mark on the current item, a coloured dot per status (published, scheduled, draft, pending review, private, trash), arrows / Home / End / Enter / Escape and type-ahead, listbox / option semantics, closing on outside click, placed inside the window. The native <select> stays in the page, hidden, for its value and for PHP forms.
  • Multiple choices are chips with search (ACF relationship and taxonomy fields, multiple choices of Okno collections, WordPress taxonomies).
  • Checkboxes, radios and switches drawn by Okno in light and dark: rounded square with a lime check, circle with a lime dot, radio cards (collection nature) with a clear selected state (border, tint, dot), switches for true / false fields, proper disabled states.
  • WordPress's media library, link dialog and rich text editor match Okno inside the app: Okno surfaces and text in light and dark, lime primary button instead of WordPress blue, accent selection ring and check in the grid, themed tabs, drop zone and attachment details; the editor's toolbar, tabs and text area follow the theme.
  • A whole number stored in a decimal number column is served as 5, not 5.0, like ACF and like numbers inside lists.
  • Rich text of a collection imported from ACF is rendered like ACF renders it (paragraphs, typography, shortcodes, embeds) even once ACF is deactivated.
  • An Okno collection imported from ACF can't be removed with DELETE /okno/v1/collections/{id}: go back to ACF instead.

Fixed

  • No more light flash when switching views in dark mode (and no dark flash in light mode). The theme is saved per user on the server (user meta, cookie as a fallback) and printed by PHP on <html>, the app shell and the visual editor before the first paint; a choice made before this version in the browser is picked up and saved once. Dark stays the default.
  • No more blue frame on click. Buttons, links, navigation items and form controls only show a focus ring with the keyboard (:focus-visible), in Okno's accent ink; WordPress's :focus box-shadows (#2271b1) no longer leak into the app.
  • The media library no longer looks greyed out when opened from the visual editor or Collections (« Replace » on a featured image, image / gallery / file fields): its backdrop was stacked above the window. Same fix for the link dialog and TinyMCE's windows and menus (paragraph / heading list).
  • The Visual editor button of an entry keeps an accessible name when its label is hidden on narrow screens.
1.0.0-beta.8 2026-11-04 3 changed · 2 fixed

Changed

  • Content and Collections are one view: Collections. Types on the left, whatever their engine (WordPress pages and posts, ACF or theme post types, Okno collections and single pages), grouped and with their entry counts, plus New collection for administrators; the entries of the selected type in the middle (search, status filter, sort by last modified, date or title, create, duplicate, publish / back to draft, trash and restore); the selected entry edited in place on the right with the editor's form (same widgets, undo / redo, local drafts, conflict protection, history), its status, Save, and Visual editor / View on the site for entries that have a page. Leaving an entry with unsaved changes asks first. Deep links: view=collections&type=<post_type>&id=<post_id>. The Fields tab of an Okno collection opens the schema builder (administrators); ACF types say their fields are managed in ACF. Editors without manage_options get the view too, without the builder.
  • The former view=content and view=form addresses redirect to Collections with their filters or their entry, and every link that pointed to them (change log, the editor's page list, Edit with Okno on the edit screen, home shortcuts) now opens the entry in Collections.
  • REST: GET /okno/v1/posts also sorts by orderby=date.

Fixed

  • Check again (Dashboard → Updates) now clears Okno's cached release, so a version published in the last hours shows up right away instead of after the 6-hour cache.
  • The home screen of the Okno app is centred instead of sticking to the left on wide screens.
1.0.0-beta.7 2026-11-04 8 added · 4 changed

Added

  • Okno opens full screen and is the back office. One wp-admin page (admin.php?page=okno) hides the WordPress menu, admin bar, footer and other plugins' notices (this page only; the admin bar isn't even rendered) and shows Okno's own navigation: Home, Visual editor, Content, Collections, Media, Publishing, Settings, plus Back to WordPress for administrators. The navigation collapses, works with the keyboard (arrows, Escape) and becomes a drawer below 782 px. The top bar carries the site name, a Publish button that shows when changes aren't published yet, the light / dark switch shared with the editor, and a user menu (profile, log out).
  • Content view: every editable entry (pages, posts, ACF post types, collections) with search, filters by type and status (trash included), create, duplicate, publish / back to draft, trash and restore, and opening in the visual editor or in a form.
  • Entry form: the editor's field panel without the preview, for content that has no page on the site (legal pages, testimonials, settings singletons). Same widgets, undo / redo, local drafts, conflict protection and change history as the visual editor. Collections without a URL open there from everywhere (Content, the editor's page list, the edit screen, the change log).
  • Collections builder on top of the okno/v1/collections API: list of collections and single pages, create / edit (id, labels, kind, URL pattern, features), field editor with drag and drop and keyboard reordering, every one of the 19 field types and their options (choices, min / max, reference targets, nested sub-fields of lists and groups, block types of blocks fields), keys generated from the name and fixed once applied, review of the migration plan (dry_run) before applying, validation errors attached to the field concerned, archived columns with purge (typed confirmation), removal of a collection, import and export of okno.schema.json.
  • Media view: the media library as a grid, with search, type filter, upload by button or drag and drop, and a details panel (alternative text, title, file URL, delete).
  • Status from the editor: a status menu in the editor toolbar (draft, pending review, published, private) so content created in Okno can go live without wp-admin. Users who can't publish submit for review.
  • Editors land in Okno after logging in (login_redirect, for users without manage_options, unless another destination was asked for). Setting Editors → After logging in, on by default.
  • REST: GET /okno/v1/posts takes type, status and orderby=modified and returns modified; POST /okno/v1/posts/{id}/status, POST /okno/v1/posts/{id}/restore, GET /okno/v1/deploy/state (pending changes, active and last deployment).

Changed

  • Okno has a single menu entry. The former pages (okno-start, okno-settings, okno-deploys, okno-editor) still answer and redirect to their view, with their parameters.
  • Changes are marked as pending even without automatic publishing (as long as a publishing mode is set), so the Publish button can show them; Publish clears the mark as before.
  • Duplicating a collection entry copies its field values.
  • The editor offers the featured image only for content types that support it (a collection without the Featured image feature no longer shows one), or when the content already has one.

Older releases on GitHub →