Skip to content
История версий

Журнал изменений

Следите за каждым улучшением, исправлением и новой функцией в развитии Blok

  1. v1.12.0Минорный26 авг. 2026 г.

    `@bloklabs/server` is one C# implementation now, in two formsThe service that shipped in 1.11.0 as a Go binary is ASP.NET Core, and gained a second delivery with the port: install Blok.Server.AspNetCore from NuGet and the same handlers run inside an ASP.NET Core app you already have — AddBlokServer(), UseAuthorization<T>(), MapBlokServer("/api/blok") — with no second process, port or container to operate, and your own authorization policy guarding uploads and previews rather than a second set of credentials. npx @bloklabs/server and ghcr.io/jackuait/blok-server still deliver the standalone host, now a self-contained .NET binary published for the same six OS/architecture targets and verified against checksums.txt on first run. Routes, flags, environment variables, the ticket/CORS/rate-limit ordering, archive names and every response wire are unchanged, and a 58-case conformance suite runs the C# host against the contract the Go one froze, so an existing deployment sees the same service. IBlokAuthorization is the permission seam for the document routes that come later; it is not consulted for uploads or unfurling. The packages still store no documents and carry no database-block or MySQL integration — that work follows this delivery migration.

    Добавлено`@bloklabs/server` is one C# implementation now, in two formsThe service that shipped in 1.11.0 as a Go binary is ASP.NET Core, and gained a second delivery with the port: install Blok.Server.AspNetCore from NuGet and the same handlers run inside an ASP.NET Core app you already have — AddBlokServer(), UseAuthorization<T>(), MapBlokServer("/api/blok") — with no second process, port or container to operate, and your own authorization policy guarding uploads and previews rather than a second set of credentials. npx @bloklabs/server and ghcr.io/jackuait/blok-server still deliver the standalone host, now a self-contained .NET binary published for the same six OS/architecture targets and verified against checksums.txt on first run. Routes, flags, environment variables, the ticket/CORS/rate-limit ordering, archive names and every response wire are unchanged, and a 58-case conformance suite runs the C# host against the contract the Go one froze, so an existing deployment sees the same service. IBlokAuthorization is the permission seam for the document routes that come later; it is not consulted for uploads or unfurling. The packages still store no documents and carry no database-block or MySQL integration — that work follows this delivery migration.
    ДобавленоAnswers pasted from ChatGPT and Gemini keep their structureThose apps render an answer as a component tree rather than a document, and its two halves failed in opposite directions: the markup carrying meaning (a formula's TeX, a code block's language) sits in attributes and header chrome the sanitizer strips, while the markup carrying none (KaTeX's glyph layout, copy buttons) is made of tags it keeps. A pasted formula therefore arrived as roughly 870 nested layout spans flattening to x=a 2 ,, and a ChatGPT code block arrived twice, because its <pre> and the CodeMirror instance inside it both survived. Math is now replaced by the <span data-latex> the equation tool already whitelists, read from data-math-source, data-math or KaTeX's own MathML annotation — the three shapes such a renderer leaves behind. A code block collapses to one <pre><code> with its newlines intact. Gemini prints the code language as text beside its copy and download buttons, so SQL used to land as a stray line above the block; it now rides the <pre> as data-blok-code-language and the Code tool adopts it, mapping the aliases models actually emit (js, py) to Prism ids and ignoring any it cannot resolve. Screen-reader speaker labels ("ChatGPT said:"), icon glyphs and the nodes Gemini itself flags as hidden no longer become content. All of it is verified against real clipboard payloads captured from public share pages rather than hand-written fixtures. There is deliberately no Claude branch: its share pages sit behind a bot check headless Chromium does not clear, and shipping a detector built on selectors nobody has verified is exactly what the fixture-provenance rule exists to prevent.
    ИсправленоMarkdown copied out of an AI answer lost half its structureThe same apps' Copy buttons put markdown on the clipboard as text/plain, which is a different path into Blok, and five things were lost on it. A code fence or a second paragraph nested under a list item vanished without trace — numbered steps with code under each step, which is the single most common shape an assistant writes; nested content is now emitted as sibling blocks in document order, because Blok's flat list model has no way to indent a non-list block under an item, and items resuming after the interposed content carry start so their markers do not restart at 1. Fence languages were passed through raw, so the ``js a model actually emits was not a Prism id: no highlighting, and a bogus js label; the normalizer markdownToHtml already had now lives in one module used by every path — including the Code tool's HTML paste, which had grown its own copy — and knows the aliases models reach for (c++, c#, golang, zsh, console, docker, tex). Bold, inline code and strikethrough inside a list item rendered and were then silently saved as plain text, because the List tool allowed only br, a, b and i; it now spreads the same inline whitelist every other text tool uses, which also retires a fixed target: '_blank' rule that rewrote same-page links to open in a new tab on save. A parser throw made the paste do nothing at all, escaping past the plain-text fallback — only the import and conversion are wrapped now, since a throw after the blocks are inserted must not report failure or the text is pasted twice. And a blockquote-only answer arrived as literal > lines: there was no blockquote signal, and the new one is anchored to the line start with a mandatory space, which structurally excludes ->, =>, 5 > 3 and >>>` prompts.
    ИзмененоThe Go implementation is gone, and the tag ships the C# oneThe Go source, its build and its release wiring were removed only after the dual-target conformance gate passed unwaived, so the C# host inherited a proven contract rather than a promise. The tag workflow now tests and formats the .NET 10 solution, packs Blok.Server and Blok.Server.AspNetCore, proves the produced NuGets by consuming them from an isolated feed, builds the six self-contained host archives plus checksums, smoke-tests both the native archive and the linux/amd64 image — including that a public unauthenticated configuration still refuses to start — publishes the NuGet, GitHub and GHCR outputs, verifies each is observable, and only then publishes the GitHub release, which is created as a draft. The NuGet credential is minted over GitHub OIDC immediately before the push rather than held as a long-lived secret, so it cannot outlive the build that uses it. The docs release event therefore cannot advertise a package, archive or image that is still missing.
    ИзмененоTwo gates that only CI could seeThe repository carries no .editorconfig, so dotnet format fell back to its four-space default in CI while the C# is written two-space against a developer's own home settings, and the check was red on main from the day it landed — 12689 whitespace complaints across every file, and every step after it in the job never ran. The .NET tree now carries its own root = true config, so a laptop and a runner agree. In the same family: a conformance case cannot narrow an it that is already narrowed, and the two that need the ordinary binary now take their own binding, which keeps the whole file skipped when the harness has not built a server.
  2. v1.11.0Минорный23 авг. 2026 г.

    `@bloklabs/presets`A new package of ready-made uploaders, so config.uploader stops being something every project writes from scratch: supabaseStorage, presignedStorage (S3-compatible — the browser PUTs straight to a signed URL your own backend mints, so credentials never reach the page), cloudinaryStorage, fetchStorage for any endpoint that speaks Blok's documented upload wire contract, and indexedDBStorage for demos. Zero runtime dependencies: a vendor client is passed in, never imported, so your own auth session applies and the preset is not pinned to a client version. Buckets and Cloudinary resource types resolve per asset kind rather than per tool, which is what UploadContext actually carries — audio rides Cloudinary's video pipeline by Cloudinary's own design. Only fetchStorage and cloudinaryStorage implement uploadByUrl; the other three leave it undefined on purpose, so Blok's URL-verbatim fallback applies. That is deliberate, not an omission — a browser cannot fetch a third-party URL, and a pass-through stub would type-check identically while silently reporting a re-host that never happened. indexedDBStorage exists because Blok's built-in fallback (URL.createObjectURL) does not survive a reload, so an unconfigured demo looks fine until the page refreshes and every image breaks; it stores the bytes and returns a stable blok:asset/<id> reference, and resolveBlokObjectUrl turns that back into a blob: URL — it mints a fresh one on every call with no cache and no revoke, so a repeatedly re-rendered view has to revoke its own. A failed upload names the preset that produced it, so a 403 is traceable to the signer, the CORS rule or the PUT that actually failed. The uploader contract types are now exported from @bloklabs/core: BlokUploader, UploadContext and UploadedAsset were declared but reachable from nowhere, so a consumer writing an uploader got TS2305 and hand-copied the shape.

    Добавлено`@bloklabs/presets`A new package of ready-made uploaders, so config.uploader stops being something every project writes from scratch: supabaseStorage, presignedStorage (S3-compatible — the browser PUTs straight to a signed URL your own backend mints, so credentials never reach the page), cloudinaryStorage, fetchStorage for any endpoint that speaks Blok's documented upload wire contract, and indexedDBStorage for demos. Zero runtime dependencies: a vendor client is passed in, never imported, so your own auth session applies and the preset is not pinned to a client version. Buckets and Cloudinary resource types resolve per asset kind rather than per tool, which is what UploadContext actually carries — audio rides Cloudinary's video pipeline by Cloudinary's own design. Only fetchStorage and cloudinaryStorage implement uploadByUrl; the other three leave it undefined on purpose, so Blok's URL-verbatim fallback applies. That is deliberate, not an omission — a browser cannot fetch a third-party URL, and a pass-through stub would type-check identically while silently reporting a re-host that never happened. indexedDBStorage exists because Blok's built-in fallback (URL.createObjectURL) does not survive a reload, so an unconfigured demo looks fine until the page refreshes and every image breaks; it stores the bytes and returns a stable blok:asset/<id> reference, and resolveBlokObjectUrl turns that back into a blob: URL — it mints a fresh one on every call with no cache and no revoke, so a repeatedly re-rendered view has to revoke its own. A failed upload names the preset that produced it, so a 403 is traceable to the signer, the CORS rule or the PUT that actually failed. The uploader contract types are now exported from @bloklabs/core: BlokUploader, UploadContext and UploadedAsset were declared but reachable from nowhere, so a consumer writing an uploader got TS2305 and hand-copied the shape.
    Добавлено`@bloklabs/server`A new Go sidecar (npx @bloklabs/server, or the ghcr.io/jackuait/blok-server container image) that handles file uploads and link previews, so a consumer writes no backend code for either. POST /upload takes a file, POST /upload-by-url re-hosts a pasted remote URL, and GET /unfurl returns the OpenGraph / Twitter-card metadata a bookmark card is built from. Both routes that fetch a consumer-supplied URL go through one SSRF-guarded outbound client — the only place in the service an outbound request happens at all, held there by a static test that fails the build if any package outside it constructs an HTTP client, with a single documented exemption for the S3 driver, which talks to the operator's own endpoint. Uploaded bytes land in a local directory or any S3-compatible bucket, signed with SigV4 against the standard library so the binary carries no AWS SDK; addressing style auto-detects, because path-style breaks new AWS buckets and virtual-hosted style breaks MinIO and R2. Three access modes — none, proxy, ticket — are enforced by startup interlocks rather than warnings: a mode that trusts every caller refuses to start on anything but a loopback bind, a public service refuses to start without an origin allowlist, and each refusal names the flag to change and spells out what the address really opens (:4000 reads narrow and is not). Access passes are verified against exactly one algorithm, which removes the algorithm-confusion family by construction instead of by configuration. The service stores no documents: it may own what passes through it — the bytes being uploaded, the page being fetched — and never your records, which is an ownership line rather than a missing feature. Two limits are stated plainly on the /server docs page instead of buried: link previews reach roughly 70% of sites and the rest degrade to a plain link, and the S3 signatures are covered by tests but have not been checked against a real bucket.
    ДобавленоCross-block text selectionDragging across blocks now selects the characters under the pointer instead of whole blocks. Two things had to change together, and either alone would have done nothing: the old handler deliberately converted any cross-block drag into a block selection, and every engine clamps a drag-driven selection to the anchor's editing host — and each block's tool element is its own editing host, so the range could not grow past the first block by itself. The spanning range is therefore asserted outright and re-asserted and re-applied whenever something rewrites it. Everything that consumes such a range learned to split it per host: copy and cut serialize each host's slice, because Selection.toString() clamps in Chromium and WebKit and a native copy dropped every block after the first; Backspace, Delete, typing, Enter and paste replace exactly the range as one undo entry; the mark engine splits before applying, since running whole on a cross-host range deleted the selected text instead of marking it, making cross-block ⌘B destructive; and a table cell no longer steals the caret back and repaints its own rectangle over the selection. Painting is substituted only where an engine cannot paint a spanning range itself — Chromium can, and its ::selection covers the line box where ::highlight() covers only the text box, so substituting there shrank the band by a fifth and opened gaps between lines. It works in Firefox, which Notion's own implementation does not. Escape promotes the text selection to a selection of the same blocks; Shift+Click and Shift+Arrow keep their block-level semantics; a drag spanning a block with no editable content still selects whole blocks. Two deliberate contract changes come with it, both aligning selection with the shipped hover behaviour: a lasso over columns selects the blocks inside the columns rather than the column list, and a drag from one column into another selects the run it spans instead of nothing.
    ДобавленоBlock controls on either sideconfig.toolbarPosition: 'left' | 'right' (live, via toolbar.setPosition()) moves the plus button and drag handle between the editor's inline-start and inline-end gutters, and moves the reserved width with them — the start gutter collapses, an equal one opens at the end, and the text reclaims the space the controls used to occupy. Logical tokens throughout, so RTL mirrors for free. CSS only gets the layout halfway: four JS paths hard-coded "the bar grows leftwards" and are mirrored too — the viewport clamp (a ceiling on the offset now, not a floor), the nested-content nudge (a list item's right edge *is* the block's, so the nudge that closes the gap on the left would open one on the right), the callout-emoji overlap suppression (the emoji is a start-edge affordance; with the bar on the other side the controls stay visible), and the block-settings menu, which is placed beside its trigger and would otherwise open straight over the block it belongs to. Before this, a host that wanted the controls out of the reading path had no lever but hiding them entirely with hideToolbar.
    ДобавленоDev override seamEvery published @bloklabs/core entry now consults globalThis.__BLOK_DEV_OVERRIDE__ before falling back to its bundled implementation, so a local build can be injected into a running app by Blok's own developer tooling. It is a passive, in-realm read: nothing is fetched, evaluated or resolved from a URL, and the seam never consults localStorage, meta tags, URL parameters or DOM attributes, so it grants nothing to an attacker who cannot already run script in your origin. Payloads that are DOM nodes are rejected, which closes DOM clobbering, and a payload that throws on property access (a cross-origin WindowProxy will) warns once and falls back to the bundle rather than crashing the chunk at module evaluation. The editor root carries data-blok-version, so what is running is inspectable. If you want the branch out of your bundle entirely, ./dist/* is now an exported subpath and the docs give the alias — as an exact-match regex, because a plain string alias key also prefix-matches every subpath, and each entry (/tools, /full, …) needs its own pair. The extension that drives the seam is unpacked, Chrome-only tooling that lives in this repository; it is not published and consumers never install it.
    Исправлено`onChange` fired late, or not at allThe change pipeline restarted its 400ms batch window on every mutation, so the first change to an idle document waited out the whole window before any host UI could react, and — worse — sustained typing never closed the window at all: any gap under 400ms pushed delivery further out, so a user typing a paragraph got zero onChange calls until they paused. The window now opens on the first change and is never extended. onChange leads it on the next microtask (the same frame the user typed in, with one tick of deferral so a multi-block paste is still a single call) and everything after is coalesced into one trailing call, bounding latency at one window. onSave keeps the trailing edge only — serializing the whole document is too expensive to front-run a batch with — so its cost is unchanged at one serialization per window. Read-only and destroy are re-checked at delivery, since a queued microtask, unlike a timeout, cannot be cancelled by destroy().
    ИсправленоSelection beside a nested blockA lasso drawn in the left gutter beside a table nested in a toggle heading selected the whole toggle section instead of the table, because selection decided which block owns a row by a different rule than the block toolbar does: nested blocks were excluded from lasso candidates outright, the endpoint resolvers walked to the top-level block, and the per-block horizontal test measured the content column while the lasso deliberately extends into the gutter. Selection now resolves a row through the same rule the toolbar already uses. In the same family: a flat-index range selected every block stored between the endpoints, so a container *and* its children; the drag range was toggled per hover, so out-of-order moves left holes; the band was clamped to the holders under the first throttled mousemove, so a fast drag silently dropped every row above it; Shift+Arrow stepped by flat index and walked into the next container; and one keystroke inside a nested block ran the pipeline once per ancestor, so Shift+ArrowDown in a toggle extended by two rows per press. A lasso that ends the drag also stands the hover down, so a queued mousemove can no longer yank the toolbar onto the last block a frame after mouseup and swallow the user's next deliberate hover. Underneath all of it, throttle dropped any call arriving in the dead zone left behind by a trailing invocation — the call was lost for good, which is why a fast lasso flick stranded the band one row short of the pointer, permanently.
    ИсправленоEmpty-block placeholders displaced the caretAn empty block's placeholder is generated content at the start of the first line box, and left in normal flow it consumes real inline advance, so the caret was painted past the placeholder text — 315px to the right in a paragraph, 118px in an h2 — and the block read as if the placeholder were something the user had typed. It needs the caret's anchor to own a layout box, which is why it looked intermittent; a block holding the mark engine's pending-format zero-width space is one such case, and still counts as empty. Every placeholder gated on the empty mark now paints its text while claiming zero advance. Media captions are deliberately left in normal flow: they are gated on :empty, where the caret always parks at the line start anyway, and a zero-width box there would anchor at the alignment point and spill a centred or right-aligned caption to one side. Separately, caret/focus.ts overwrote a correctly placed caret with an unset Range, which spans (document, 0) and drops the caret out of the element entirely.
    ИсправленоA bookmark that could not be previewedThe same block showed an error placeholder right after a failed paste and an ordinary card after a reload, because the error state never reached saved data. A failed preview now degrades to a plain link on both paths, which is what the reload path already rendered. Roughly 30% of sites publish no preview data to anything that is not a browser, so this is the ordinary case rather than a fault — there is no notifier and no distinction drawn between "the service is down" and "this site publishes nothing", since both end as the same link and a reader can act on neither.
    ИсправленоAudio player cardDescenders in the title and artist lines were sliced flat. text-overflow: ellipsis forces overflow: hidden, which clips at the padding box, and at a line height of exactly 1 that box is shorter than the font's content area — 3.02px of descender ink against 1.5px of room, measured in Chromium. Both lines now use the same line-height token the file-name row already carries.
    ИзмененоRelease and CI wiringNothing in this repository iterates workspaces automatically, so a new package missing from a list silently drops out of that step — and there are seven such lists, several of which sat red on main while @bloklabs/presets was registered in some of them but not the rest. Both new packages are now registered across all of them — version lockstep, the publish list, the build task graph, their own CI test steps, the tests that pin both of those graphs, the docs-deploy gate that holds the deploy until npm has propagated, and the package-metadata law, which requires a real README plus keywords, homepage, repository and bugs, checked in both the source and the packed tarball. The server's binaries and container image ship from a tag-triggered workflow rather than from CI or from release.mjs, since a maintainer's laptop has no Go toolchain, no Docker daemon and no registry credentials, and the npm wrapper resolves its download from the release matching its own version — so binaries ship on every tag, or npx @bloklabs/server@<new> would 404. Drift laws were added where a mirror can rot silently: @bloklabs/presets hand-copies the uploader types and its own option interfaces into its published .d.ts (a published declaration cannot import outside its own tarball, and a zero-dependency package cannot pull in core's types), so a compiler-API law now fingerprints every one of them against its real source, checks export parity in both directions, and fails on any mirrored type nobody remembered to cover — the same rot that once cost types/data-attributes.d.ts 17 of its keys.
    ИзмененоDocsNew /presets and /server pages, both linked from the navigation, both localized in English and Russian down to the table cells (the page chrome reuses existing keys rather than adding new ones), and both covered by the SEO and prerender manifests their route trees are checked against. /server leads with the path that runs no service at all, and states its limits on the page rather than in a footnote — including that files served from the local directory should sit on a different origin than the app, since a file someone uploads is served by your domain and can act as if it were your page. A new docs page documents the dev override seam, its threat model and its opt-out. SECURITY.md now commits to a response window before the first report rather than after it: acknowledged within five business days, fixed or publicly advised within ninety. The design documents behind the backend service, the storage presets, the editor wiring and the database-block query shape live in docs/plans/.
  3. v1.10.1Патч20 авг. 2026 г.
    ДобавленоToggle heading adoption"Turn into → Toggle heading" now adopts the heading's section, matching Notion: every following sibling up to (but not including) the next heading of the same or higher rank becomes a child of the new toggle. Before, the section was left stranded outside and the content had to be hand-fed into the fresh toggle one block at a time. Descendants of adopted siblings ride along rather than being reparented directly, a heading inside a column adopts only within that column, and a source that is already a toggle keeps exactly the children it had. The adoption runs inside a move transaction, so undo restores the section — a bare reparent loop left those writes invisible to history and undoing the convert stranded the section under a plain heading. Multi-select convert opts out, so each selected block becomes its own toggle instead of earlier conversions swallowing later ones.
    ДобавленоToggle disclosure arrowsThe arrows now read as clickable. At rest the chevron was a fixed 12px glyph whose hover pill only appeared once the pointer was already inside the 28px square, so there was nothing to tell a user the arrow was interactive. The chevron scales with its title's font size, so heading arrows grow with their level while toggle lists keep the historical 12px; the pill tints on hover anywhere over the title row rather than over the arrow alone; and a localized Expand/Collapse tooltip is read from the state-synced aria-label.
    ИсправленоNested block menusA block inside a toggle list or toggle-heading section could never show its own menu, and a table nested in a section had a menu that could not be caught: hovering cell content anchored the toolbar to the table, but the moment the pointer crossed onto the cell padding, the section's gutter strip, or the toolbar icons, resolution bounced up to the toggle parent and the menu jumped out from under the cursor. Hover and touch now share one resolver in which the deepest block wrapper under the pointer owns the toolbar. Cell blocks still anchor their table and a child-toolbar container's first child still anchors the container; a pointer in a container's own chrome descends by line to the child whose band contains it; hover detection stands down entirely over the toolbar; and the page-margin fallback is band-, depth- and x-aware, so the margin beside a nested table anchors the table rather than the whole section.
    ИсправленоAccessible names for icon-only controlsAn icon-only popover item now takes its accessible name from its hint title. A hint is wired via aria-describedby, which describes a control but never names it, so the entire inline toolbar — bold, italic, link, marker, underline, strikethrough, inline code — was nameless; the fix is central, so every icon-only popover item is repaired. Colour swatches are named from the label they already formatted (background swatches had no name at all and text swatches all announced as "A") and expose applied state via aria-pressed. Captions on image, video, file and embed are named from their localized placeholder and marked multiline, and drop role="textbox" in read-only where they are static text; the embed iframe has a title. The file card no longer nests an editable textbox inside its button. No new i18n keys — every label reuses one already present in all 71 locales.
    ИсправленоList and popover semanticsThe list wrapper carries role="list"; every item declared role="listitem" but nothing ever declared the list, leaving every item in the editor an orphan. Toolbox section headers were role="separator" children of a role="listbox", which may own only options and groups, and are now presentational. A popover is only stamped as a menu or listbox when it actually has default items — the link editing field and the block colour submenu are built from a single Html item and were advertising a menu with no menu items.
    ИсправленоTable and database semanticsHeading cells expose columnheader/rowheader roles, matching what the view renderer already emits as real <th> (the tag itself cannot be swapped: heading toggles repaint attributes on existing cells, and rebuilding them would destroy elements that host mounted child blocks and are held by reference across selection, merge, resize and corner-drag state). The header row/column toggle is a real role="switch" button with aria-checked and Enter/Space. Add-row, add-column and the row/column grips are named, focusable buttons with keyboard activation — they were pointer-only, so a keyboard user could not add or reorder anything. Database view tabs are a real tablist with roving tabindex and aria-selected.
    ИсправленоEscape no longer strands focusEscape pressed inside the toolbar left focus on <body>. The document-level handler stopped propagation while still in the capture phase, so the toolbar's own bubble-phase handler could never restore the caret; it now stands down for targets inside the toolbar. The inline-toolbar branch stops propagation too, so one Escape no longer both closes the toolbar and enters navigation mode. Modal dialogs return focus to the element that opened them rather than trusting activeElement, which fixes focus loss on WebKit — it does not focus a <button> on click.
    ИсправленоContrastThe grey text token measured 4.55:1 on white, clearing AA by 0.05 and therefore failing on every tinted surface (4.28:1 on secondary background, 3.95:1 under hover). It is darkened, along with links, inline code — now its own token, so it stops borrowing the persisted marker red — and embed text that sat on the decorative tier. The Prism dark palette was entirely dead: every rule was gated on a .dark class that nothing in Blok sets, so dark code blocks rendered the light palette with punctuation at 1.54:1. Database column pills opt out of the marker palette onto a dedicated on-pill colour, because every light --blok-color-*-text fails AA on its own background and that palette is persisted into saved documents.
    ИзмененоAccessibility suiteCoverage was two copy-pasted axe scans on the table tool. It is now a layered suite running on Chromium, Firefox and WebKit: axe scans of all 20 block tools in edit and read-only, editor chrome (toolbar, toolbox, block settings, inline toolbar, nested popovers, tooltips, link field), keyboard and focus behaviour, live-region announcements, and regression specs pinning each audited defect. No rule is waived anywhere, every scan is preceded by a visibility assertion so a scoped scan cannot pass vacuously, and a static architecture law pins names, role-gated ARIA state, decorative icons and a bidirectional a11y.* i18n ledger. Two defects remain pinned with reasons in place: navigation mode exposes aria-current but not a selection role, and the inline toolbar still intermittently strands focus. The nested-block hover spec runs on all three engines, since CI's matrix never executes chromium-default and the spec would otherwise not run in CI at all.
  4. v1.10.0Минорный7 авг. 2026 г.

    Framework adaptersuseBlocks takes { within: blockId } in React, Vue and Angular, so a container block re-renders only for changes inside its own subtree; unscoped, a page of N containers turned one keystroke into N re-renders. It bounds reactivity, not reads — the returned API still sees the whole tree — and a change whose block can no longer be placed (a removal emitting after the fact) counts as in-scope, so a container can never be left rendering a dead child. childContentAttributes applies per-child decoration one level in, on each child's [data-blok-element-content] wrapper, which is where a numbered rail or connector line has to align; core's decoration law always blessed it, but only the holder was reachable, so containers hard-coded Blok's wrapper chain in their own CSS. toolbarAnchorRef (React/Vue) and ctx.setToolbarAnchor (Angular) answer getToolbarAnchorElement from inside the component tree instead of forcing a self-invented data attribute and a querySelector; the ref outranks the declared hook only while it holds a mounted element, so the toolbar is never positioned against a detached node.

    ДобавленоFramework adaptersuseBlocks takes { within: blockId } in React, Vue and Angular, so a container block re-renders only for changes inside its own subtree; unscoped, a page of N containers turned one keystroke into N re-renders. It bounds reactivity, not reads — the returned API still sees the whole tree — and a change whose block can no longer be placed (a removal emitting after the fact) counts as in-scope, so a container can never be left rendering a dead child. childContentAttributes applies per-child decoration one level in, on each child's [data-blok-element-content] wrapper, which is where a numbered rail or connector line has to align; core's decoration law always blessed it, but only the holder was reachable, so containers hard-coded Blok's wrapper chain in their own CSS. toolbarAnchorRef (React/Vue) and ctx.setToolbarAnchor (Angular) answer getToolbarAnchorElement from inside the component tree instead of forcing a self-invented data attribute and a querySelector; the ref outranks the declared hook only while it holds a mounted element, so the toolbar is never positioned against a detached node.
    ДобавленоViewrenderLatex and createLatexRenderer are exported from @bloklabs/core/view. The KaTeX chunk was already in the bundle — the code tool, the equation inline tool and the markdown importer all use it — but no entry exported the renderer, so types/view.d.ts told hosts to add katex as a second dependency of their own. Both apply the options Blok itself trusts for untrusted input (trust: false, capped maxExpand/maxSize, no throw on malformed math), and the CSS injection is skipped where there is no document, so SSR and workers get identical markup. inlineRenderers is synchronous, so createLatexRenderer() awaits the load once and hands back a sync renderer — the async function alone would stringify as [object Promise].
    Исправлено`insertChild({ caret })`The caret was lost for a portal-rendered child. Placement ran synchronously after the insert, but a React/Vue/Angular child's render() returns an empty host and commits its editable a frame later, so the block was not yet focusable and Caret.setToBlock took its only other branch: clear the selection, blur, highlight the block. It was permanent, because nothing re-ran the placement. It now re-applies once the child's holder produces an input, one-shot, and stands down if focus moved away in the meantime.
    ИзмененоAdapter parityThe per-child decoration pass was triplicated across the three adapters and is now one implementation. Three architecture laws pin the new surface (child-decoration, toolbar-anchor-ref, useblocks-scope), and the view entry gained an export ↔ declaration drift check — the guard whose absence let renderLatex ship undeclared.
  5. v1.9.0Минорный6 авг. 2026 г.

    Container toolsFive things a container block used to hand-roll are now declared. static childTools = { allow?, deny? } lets any container state which tools may be its direct children, enforced by core on insert (a disallowed tool is demoted to allow[0], so Enter always produces a block), on a cross-boundary move (refused) and in the toolbox (hidden) — the selective, insert-aware counterpart to ownsChildren, and the generic form of the Table-only restrictedTools. data-blok-keyboard-owner marks a subtree whose keyboard belongs to the tool: block-level and editor-level keydown/input handling stand down inside it, tag-agnostically, replacing per-key stopPropagation handlers. BlockAPI.insertChild gained an options argument with the same vocabulary as the adapters' useBlocks insert (focus, caret, id, tunes, replace), threaded through insertInsideParent.

    ДобавленоContainer toolsFive things a container block used to hand-roll are now declared. static childTools = { allow?, deny? } lets any container state which tools may be its direct children, enforced by core on insert (a disallowed tool is demoted to allow[0], so Enter always produces a block), on a cross-boundary move (refused) and in the toolbox (hidden) — the selective, insert-aware counterpart to ownsChildren, and the generic form of the Table-only restrictedTools. data-blok-keyboard-owner marks a subtree whose keyboard belongs to the tool: block-level and editor-level keydown/input handling stand down inside it, tag-agnostically, replacing per-key stopPropagation handlers. BlockAPI.insertChild gained an options argument with the same vocabulary as the adapters' useBlocks insert (focus, caret, id, tunes, replace), threaded through insertInsideParent.
    ДобавленоInline toolsInlineToolConstructable.hydrate(root) is a declared contract, called by Block after render, after an in-place setData and after onPaste. Whatever a hook writes is marked data-blok-mutation-free, so re-rendering derived markup is not an edit.
    ДобавленоViewblocksToHtml and <BlokView> accept inlineRenderers: tag-keyed, post-sanitize, output inserted verbatim — the inline counterpart of renderers. A DOM-free render can plug in katex.renderToString or a mention chip without Blok shipping either.
    ДобавленоBlock tree{ blocks: [...] } is now a spec node (BlockRunSpec), valid at the root or as a child of a tree node. A migration holding an already-flat saved document can splice it verbatim: ids are kept, only un-parented blocks are re-parented onto the enclosing node, and only those join its content. Passing a pre-flat block as a tree node throws instead of silently dropping its parent/content links.
    ИсправленоInline equationsA KaTeX span's markup is derived from data-latex, and the sanitizer dropped its tags while keeping their text, so E=mc^2 was persisted as E=mc2E=mc^2E=mc2. What was supposed to hide that — re-rendering on load — never ran: EquationInlineTool.hydrate() had zero call sites repo-wide, so equations went inert on every reload, in the editor and not just in the view. The sanitizer rule now rewrites an equation span's content back to its source on both the DOM and the parse5 pipeline, so new saves are clean and a load heals documents that already carry residue. htmlTextContent reads a mark's source, so previews, outline and search stop reading rendered fragments.
    Исправлено`onChange` armingThe documented "handler presence arms the change pipeline" contract was false: core defaulted onChange to a no-op unconditionally, so the gate could never disarm and all three adapters' careful handler-presence omission was pointless. Hosts were passing dummy handlers to arm a pipeline that was already armed — and an onSave one also forces a full serialization per change batch. The injection is gone, so the contract holds as written.
    ИсправленоPublished typestypes/data-attributes.d.ts was hand-transcribed and had drifted: 17 attributes missing (including nestedBlocks, the container slot every nesting tool's stylesheet targets) and one phantom key the runtime never had. It is now generated from source and any drift fails an architecture test. The React BlokViewProps was also missing classes.
    ИзмененоDependenciespostcss, brace-expansion, fast-uri, ip-address and undici bumped across the root and docs workspaces.
  6. v1.8.0Минорный6 авг. 2026 г.

    Superscript / subscriptA new inline tool takes the tenth slot in the inline toolbar, bound to ⌘/Ctrl+Period and ⌘/Ctrl+Comma. The two modes are mutually exclusive, so applying one clears the other. Shipped with glyphs drawn into the shared inline-toolbar type system, translations in all 69 locales, and docs.

    ДобавленоSuperscript / subscriptA new inline tool takes the tenth slot in the inline toolbar, bound to ⌘/Ctrl+Period and ⌘/Ctrl+Comma. The two modes are mutually exclusive, so applying one clears the other. Shipped with glyphs drawn into the shared inline-toolbar type system, translations in all 69 locales, and docs.
    ДобавленоToolboxSlash-menu entries are grouped under labeled sections, so a long tool list is scannable instead of a flat run of items. Each entry declares its own section.
    ДобавленоTool contractBlocks carry creation provenance (user/load/api/paste/convert/replay/probe), so a container can tell an author's gesture apart from a document load, a refetch or an undo replay. Enter's container-escape became a per-tool declaration instead of a hardcoded list of Blok's own tool names, so a custom container keeps Enter inside itself without an editor-global onEnter hook. insertInsideParent and BlockAPI.insertChild take an optional tool name, making an appended typed child one atomic operation and one undo entry.
    ДобавленоFramework adaptersThe React, Vue and Angular block spec gained a general statics passthrough (toolbar anchor, ownsChildren, conversionConfig, the new Enter policy), an api handle, child-tree reactivity, per-child decoration, and an onMounted signal backed by a block:childrenMounted event. Handler presence is runtime-settable through api.handlers.set(), so passing onEnter at all no longer permanently decides Enter's semantics.
    ДобавленоEditorA new opt-in captureClicksBelowEditor places the caret in the last block when the host clicks empty space below the editor. Off by default; registered as a config key in the React and Vue adapters too.
    ДобавленоLinkThe link field sizes itself to its content, capped at the previous fixed width, and its create, error and edit states were polished.
    ИсправленоNestingsetBlockParent no longer lets a container on the block's own ancestor or descendant chain veto a reparent; an enclosing container claiming a holder used to leave model and DOM permanently divergent (Enter inside a callout nested in another container), and a descendant doing the same nested every appended child inside its predecessor. mountChildBlocks now reclaims a holder stranded in any ancestor container, re-mounts it at its model position rather than last, and preserves the caret across the adoption. Depth indentation moved from an inline margin with a hardcoded exemption list to a depth multiplier resolved through --blok-block-indent-step, so a container tool declines the indent with ordinary CSS and no !important.
    ИсправленоBlock APIEvery handed-out BlockAPI is live, so getChildren() can no longer answer [] for a populated container and setParent/insertChild/moveChild can no longer silently no-op. api.blocks.update() prefers a tool's in-place setData when there are no tunes to apply, so it stops composing a replacement block that destroyed the previous one's portal and left the holder permanently empty.
    ИсправленоInline toolbardestroy() threw and aborted blok.destroy(), leaking listeners in every module torn down after it: hide() emits Closed synchronously, the handler then ran close(), which nulled the popover mid-teardown. The reference is now detached before hiding.
    ИсправленоPopoverControl-less HTML items (the new toolbox section headers) are no longer keyboard focus stops, so a role="presentation" header can't steal initial focus, shift every Arrow/Tab, or be pointed at by aria-activedescendant. Sibling active states refresh after an item is activated, and clicking the item that opened a popover now does nothing instead of reopening it.
    ИсправленоListTab/Shift+Tab moved the model but not the render: both handlers wrote depth into the tool's live data before calling api.blocks.update, whose in-place diff then saw no change, so save() and the DOM silently disagreed.
    ИсправленоEvents and dataKeydown from a native form control a tool renders is no longer claimed by the editor. The toolbox merge and the save() extraction stop mutating tool-owned objects, so a toolbox entry carrying data no longer throws on a frozen save() result. A load is not an edit: a block arriving without a timestamp is no longer stamped at construction, so a save round-trip equals the document it came from.
    ИсправленоAdaptersThe portal registries reject a teardown from a superseded instance, so a same-id re-register survives a late destroy; Vue additionally defined one wrapper component per tool *type*, so a re-register patched the superseded instance and rendered stale data — the wrapper is now per block instance. A React element toolbox icon serializes instead of rendering as [object Object], and the imperative handle keeps the controlled baseline in sync so restoring a draft after clear() is no longer swallowed as an echo.
    ИсправленоStylesThe icon stroke is scoped to Blok's own icon markup instead of every path in the subtree. The column floor moved to --blok-column-min-width in CSS, and the static-gutter attribute plus the block-padding tokens are now declared.
    ИзмененоBuildscripts/build-angular.mjs derived its adapters contract from a hand-copied list that silently drifted whenever a staged module gained an import; it now derives it from src/adapters.ts, and the staging law test walks the same graph so drift fails at test time instead of build time.
    ИзмененоTests23 red e2e tests were root-caused rather than weakened: three were real product bugs (fixed above) and four were stale specs asserting behaviour that later commits deliberately changed. A leaked 600ms language-detection debounce in the code-tool unit tests was also cancelled, removing a load-dependent flake.
  7. v1.7.0Минорный4 авг. 2026 г.

    EmbedA stored generic embed the host hasn't allowed to be framed now renders as a clickable link card instead of the inert "No embed link" state, so the URL stays visible in both read and edit views and can never be silently lost. A new linkPaste.allowedEmbedOrigins hostname/wildcard trust list gives hosts fine-grained control as a middle ground before the all-or-nothing allowGenericEmbed flag. Tampered stored data stays fully inert, and no new iframe paths are introduced.

    ДобавленоEmbedA stored generic embed the host hasn't allowed to be framed now renders as a clickable link card instead of the inert "No embed link" state, so the URL stays visible in both read and edit views and can never be silently lost. A new linkPaste.allowedEmbedOrigins hostname/wildcard trust list gives hosts fine-grained control as a middle ground before the all-or-nothing allowGenericEmbed flag. Tampered stored data stays fully inert, and no new iframe paths are introduced.
    ИсправленоEditorQueries for a block's first editable element now skip mutation-free markers such as a list item's bullet span, closing the remaining "marker ghost" sites where Enter could write an item's own HTML into the bullet and the toolbar centred on the marker's box instead of the text. An architecture test now enforces the guard on every contenteditable selector in the codebase.
  8. v1.6.2Патч2 авг. 2026 г.
    ИсправленоCalloutEnter can leave the panel again. Previously it could only ever add another line inside, so every press stamped one more empty paragraph into the callout; those blanks are saved with the document, and the callout reloaded with its text pinned to the top of a panel padded out by invisible lines. Behaviour now matches Notion: Enter adds a line inside, Enter on an empty last line steps out. A callout nested in a column hands that block to the column rather than the document root, the exit block is saved in the position it renders in, and the whole exit is a single undo/redo step. Toggles and columns keep their existing behaviour.
  9. v1.6.1Патч2 авг. 2026 г.
    ИсправленоCalloutThe panel's vertical inset now reads its own --blok-callout-padding-block token (flat 5px default) instead of riding --blok-block-padding-top/-bottom, so a host's compact-rhythm override no longer collapses the callout card onto its text. The emoji button deliberately keeps reading the rhythm tokens so the glyph tracks the first text line under any rhythm a host sets. Defaults render byte-identically; hosts that wanted squashed callouts opt back in via --blok-callout-padding-block: 0.
    ИзмененоDocs151 verified inaccuracies root-caused and fixed across the docs site, a new guide explains how to set per-block font sizes correctly, and the docs test suite now resolves Blok's public members through the TypeScript compiler instead of a regex.
  10. v1.6.0Минорный1 авг. 2026 г.

    Font sizeconfig.style.fontSize now carries one entry per text scenario in every text-bearing block (paragraph, headings, quote, callout, code, toggle, list, checklist, table cells, captions, bookmark cards). Each entry writes that block's --blok-*-font-size custom property, so the same knob is reachable through style.tokens, editor.tokens.set() and plain CSS. An unconfigured editor renders byte-identically to before.

    ДобавленоFont sizeconfig.style.fontSize now carries one entry per text scenario in every text-bearing block (paragraph, headings, quote, callout, code, toggle, list, checklist, table cells, captions, bookmark cards). Each entry writes that block's --blok-*-font-size custom property, so the same knob is reachable through style.tokens, editor.tokens.set() and plain CSS. An unconfigured editor renders byte-identically to before.
    ДобавленоAPIbeginTransaction/endTransaction group operations spanning a pointer gesture into a single undo entry, for cases where the synchronous transact(fn) cannot reach across async boundaries.
    ДобавленоTable corner dragThe bottom-right corner now behaves the way Notion documents it: the grid tracks the pointer through live geometry instead of a unit frozen at pointerdown, a Notion-style grip replaces the old 8px dot, hovering it explains the gesture, appended columns are full width, and the whole gesture commits as one undo entry.
    ДобавленоTable auto-scrollHolding the corner at the scroll container's edge or in the viewport's top/bottom band scrolls and grows the table as it goes. Growth follows the pointer — 8px/s for every pixel held past the edge — rather than a fixed clock.
    ДобавленоTable selectionDragging a row or column grip paints the entire dragged range instead of boxing only the focused cell.
    ДобавленоPlaygroundA text-size slider (80%–150%) scales every style.fontSize scenario from its own CSS default.
    ИсправленоTableAn inward corner drag stops at the first trailing row or column that holds content instead of deleting typed cells; the handle is anchored to the grid rather than the wrapper, so it no longer drifts inside the table as it grows; a caret click no longer leaves the corner handle and "+" buttons dead until the user clicks outside; clicking inside a merged cell boxes only that cell, and expanding a selection reaches merge origins, not just spans; a stale pointercancel listener no longer accumulates per completed drag.
    ИсправленоStylesCallout emoji and list markers stay aligned with their text as the font size scales, and the bookmark card's padding, line boxes, gaps and favicon scale with the card's own type. fontSize.callout now wins over fontSize.paragraph in the callout body.
    ИсправленоTypesThe public Blok class exposes its full API surface.
  11. v1.5.0Минорный27 июл. 2026 г.

    View renderer@bloklabs/core/view now reaches visual parity with the read-only editor: every core block (paragraph, header, quote, list, checklist, code, callout, toggle, divider, spacer) renders from single-sourced class modules shared with the editor, view.css is generated from those modules, and a soft isolation root plus reproduced block scaffolding keep the output pixel-equal. A class-parity gate and a view↔read-only visual parity gate now run in CI, and the playground gains a side-by-side Blok View comparison panel.

    ДобавленоView renderer@bloklabs/core/view now reaches visual parity with the read-only editor: every core block (paragraph, header, quote, list, checklist, code, callout, toggle, divider, spacer) renders from single-sourced class modules shared with the editor, view.css is generated from those modules, and a soft isolation root plus reproduced block scaffolding keep the output pixel-equal. A class-parity gate and a view↔read-only visual parity gate now run in CI, and the playground gains a side-by-side Blok View comparison panel.
    ДобавленоPasteGoogle Docs layout tables are now recognized: multi-row two-column tables carrying photos convert into column blocks, and single-column layout tables unwrap into their content instead of arriving as one-cell tables.
    ДобавленоInline toolbarThe convert menu now opens sideways with its trigger marked selected, and the convert row is styled as the card's header.
    ДобавленоCalloutA newly picked emoji jumps into the old one's place, and the emoji picker opens instantly without waiting on its dataset to download.
    ДобавленоToolsdata-blok-tool is now stamped on code, callout, divider and spacer roots.
    ДобавленоDocsThe docs site advertises its markdown mirrors and declares Content-Signal; the published @bloklabs/core/icons subpath is documented; the README is restructured around benefits, facts and a runnable example.
    ИсправленоSecurityStored javascript: XSS closed on two sinks: media download hrefs are scheme-gated, and stored embed renders are gated on the provider registry rather than a bare https: check.
    ИсправленоDrag & DropDrops on the seam between two blocks now resolve instead of dead-ending; the drop indicator no longer promises a nesting the drop would refuse; side-drops are gated on the columns tool actually being registered.
    ИсправленоColumnsThe drop animation no longer balloons the row while a block lands.
    ИсправленоCodeIn read-only mode the language label renders as plain text and the language dropdown toggle is hidden; the copy button keeps the same height in its "Copied!" state.
    ИсправленоToolbarMultiple editors on one page now show one set of block controls instead of stacking duplicates; the slash-search caret is sized to the input, not the block.
    ИсправленоTooltipA bubble is never parked in the viewport corner when its anchor stops rendering.
    ИсправленоListThe checklist checkbox is centred on its text at any font size.
    ИсправленоEmbedProvider popups (e.g. "open in app") can escape the iframe sandbox again.
    ИсправленоAudioCover art uploads are routed to the image pipeline instead of the audio endpoint.
    ИсправленоShortcutsBACKSLASH/SLASH render as \ and / in shortcut hints.
    ИсправленоDependenciesAll 40 open Dependabot alerts remediated at their root causes.
  12. v1.4.5Патч25 июл. 2026 г.
    ДобавленоMigrateThe legacy-format interpreter is now extensible from the host: rules entries are matched before the built-in grammar (so they can override it) while reusing container recursion, orphan re-parenting and 1:N splits; expanders may consume following siblings via { blocks, consumed }; and passing generateId makes migration pure, so a document migrates to an equal result twice. Adds migrate(data, options) composing the data-rule and grammar passes in the correct order, matchLegacyRule() for per-block matching, and a report carrying lossyFields/errors instead of console-only warnings. Editor.js list v2 meta fields (start, checked) are now read.
    ИсправленоMigrateconfig.migrations now runs before format analysis, so dataModel: 'auto' inspects post-migration blocks instead of collapsing the document back to its legacy shape on save.
    ИсправленоTypestypesVersions now maps the migrate subpath, fixing TS2307 for consumers on moduleResolution: "node".
  13. v1.4.4Патч25 июл. 2026 г.
    ДобавленоMigrateHosts can now declare per-block-type migration rules from the outside to upgrade stored blocks from an old data shape to a new one without editing the tool class. Rules are applied at load (after each tool's own upgradeData) and also exposed as a standalone migrateOutputData via @bloklabs/core/migrate for offline batch upgrades. Available on all three framework adapters.
  14. v1.4.3Патч24 июл. 2026 г.
    ДобавленоAPIThe output-data helpers are promoted to a stable entry point, alongside new document-query utilities.
    ДобавленоTypesNamed config aliases and a BlokData interface adapter.
    ДобавленоToolsType-safe tool authoring helpers, plus an explicit paste priority.
    ДобавленоViewRoot-caused view gaps #29–#33 and #35 from the hr-platform/KB audit.
    Добавленоi18nRoot-caused i18n audit gaps #36–#41.
    ДобавленоMigrate / Tools / PasteRoot-caused hr-platform/KB gaps #50–#53.
    ИсправленоControlled editorFive audit findings root-caused, followed by findings #7 and #9 (and the missing test for #8).
    ИсправленоMarks / PasteFour inline-tool and sanitizer audit findings root-caused.
    ИсправленоBuild (Angular)toRenderableData is exported from the staged adapters-contract.
    ИсправленоTypesWidened the exports cast so the string-valued ./view.css entry type-checks.
    Измененоi18n message files are named by locale.
    ИзмененоRepaired the test/build drift that blocked the release preflight.
  15. v1.4.2Патч23 июл. 2026 г.
    ИсправленоMarkdownTyping a list marker (- , 1. ) inside a heading no longer converts the heading into a list; it stays a heading.
  16. v1.4.1Патч23 июл. 2026 г.
    ИсправленоEditorEnter now honours the IME composition and Shift contract; added an onError channel; data-blok-tool is opt-in.
    ИсправленоSanitizeRedundant inline markup is now collapsed on every path that stores HTML.
    ИсправленоVideoUnplayable sources are surfaced to the user instead of rendering a black player.
    ИсправленоViewAdded a classList facade so MarkSpec inline tools render correctly in BlokView.
    Исправленоi18nLocalized video stats and database overflow, added tr() interpolation, and completed locale audits across Yiddish, Vietnamese, Traditional and Simplified Chinese, Urdu, Ukrainian, Uyghur, Turkish, Thai, Telugu, Tamil, Swahili, Serbian, Albanian and Slovenian.
  17. v1.4.0Минорный23 июл. 2026 г.

    MarksNew range-aware api.marks inline-formatting engine, with the built-in bold/italic/underline/strikethrough tools migrated onto it. Exposes MarkSpec identity/family/transparent semantics for authoring custom inline tools.

    ДобавленоMarksNew range-aware api.marks inline-formatting engine, with the built-in bold/italic/underline/strikethrough tools migrated onto it. Exposes MarkSpec identity/family/transparent semantics for authoring custom inline tools.
    ДобавленоView rendererNew synchronous, DOM-free view renderer published at @bloklabs/core/view (defineBlokSchema + a central dispatcher, plus a React BlokView), for rendering stored block content without booting a full editor.
    Добавленоi18nNew runtime editor.i18n.update() API that makes config.i18n live and repaints already-rendered block DOM, wired reactively through all three adapters.
    ДобавленоConfigReactive contract for readOnly, hideToolbar and inlineToolbar: setting them after construction now takes effect live across the React, Vue and Angular adapters (with sanitize-cache invalidation on inlineToolbar).
    ДобавленоReadinessScoped, reactive editor readiness: whenAllReady({ within, settleOn }), readyState/subscribeReady, and a useBlokReady hook in all three adapters.
    ДобавленоDocsThe documentation site is now crawlable (prerendered metadata, sitemap, AI mirrors) and carries Google Analytics coverage across pages.
    ДобавленоPlaygroundDev settings panel gains a language picker and an RTL toggle.
    ИсправленоMarksFixed the trailing-whitespace extension eating later content when a mark ended on a boundary.
    ИсправленоStylesSurface background tokens (--blok-bg-light/-secondary/-tertiary) are now a test-enforced public contract; the gutter stays put in plain read-only so readOnly flips no longer shift layout.
    ИсправленоTypesMarkSpec is now imported into the main declaration entry so it resolves for consumers.
    ИсправленоDocsRussian pages are headed by their localized H1; @bloklabs/core/view resolves in the docs build and React fixtures.
    ИсправленоReleaseThe Angular README is now staged into the directory npm packs.
    Исправленоi18nLocale audits completed across Slovak, Sinhala, Sindhi, Russian, Romanian, Portuguese, Pashto, Polish, Punjabi, Norwegian, Dutch, Nepali, Burmese, Malay, Marathi, Mongolian, Malayalam, Macedonian, Latvian, Lithuanian, Lao, Sorani, Korean and Kannada.
    ИзмененоMigrated the docs site to React Router framework mode.
    ИзмененоRenamed the default branch from master to main.
    ИзмененоCompleted npm metadata across the package family and hardened the view/reactive-contract law tests.
  18. v1.3.0Минорный21 июл. 2026 г.

    APINew public editor.tokens getter/setter for runtime --blok-* theme tokens, with replace semantics and pre-ready buffering (like theme/width/placeholder). Wired reactively through all three adapters: React style.tokens, Vue style.tokens, Angular [styleTokens]. --blok-content-max-width is now honored by database centring and toolbar control placement.

    ДобавленоAPINew public editor.tokens getter/setter for runtime --blok-* theme tokens, with replace semantics and pre-ready buffering (like theme/width/placeholder). Wired reactively through all three adapters: React style.tokens, Vue style.tokens, Angular [styleTokens]. --blok-content-max-width is now honored by database centring and toolbar control placement.
    ДобавленоToolsCustom-tool authoring fixes: tools.update(name, { toolbox }) now flips a tool's toolbox entry at runtime (permission-style insert gating no longer requires recreating the editor), including its insertion shortcut; React createReactBlock accepts a viewComponent rendered while the editor is read-only; React toolbox icons may be React elements; commit() echo idempotency is now a documented public contract.
    ДобавленоEmbedGoogle Docs/Sheets/Slides/Forms/Drive embeds render at a user-adjustable pixel height with a bottom resize handle (200–2000px), persisted to data.height. Aspect-ratio media providers (YouTube, Vimeo, …) are unchanged.
    ИсправленоCDN bundlesdist/blok.iife.js and dist/blok.umd.js shipped zero generated Tailwind utilities since the v3→v4 migration, so every unpkg/jsDelivr consumer loaded an unstyled editor. Both configs now run the Tailwind plugin (+34KB gzip each), guarded by a dist-level assertion.
    ИсправленоAdaptersFive root causes from a downstream audit: a stale controlled-data echo no longer clobbers the caret (bounded echo window instead of last-payload-only dedup); equalsOutputData compares block ids only when both sides carry one; React inline tools accept a titleKey for localization; the zero-specificity gutter default is a test-enforced public contract.
    ИсправленоDependenciesSeven phantom dependencies (bare imports resolving only via hoisting, including @testing-library/jest-dom used by the global unit setup) are now declared, with a law test preventing new ones.
    Исправленоi18nReview passes completing Armenian, Croatian, Dhivehi, Filipino, Georgian, Gujarati, Hebrew, Hungarian, Indonesian, Japanese, Kannada and Khmer, plus tool interpolation variable forwarding.
    ИзмененоUpgraded to Node 26, Angular 22 and TypeScript 6, then root-caused six defects the upgrade shipped greenmost importantly the entire unit-angular vitest project loading zero tests (a fake green), and the docs test suite missing the Node 26 webstorage guard.
    ИзмененоLocal E2E suite runtime cut from ~47.5 min to ~10 min (parallel/skip-when-fresh build:test, shared page per worker), plus broad E2E stabilization.
    ИзмененоFixed ESLint cache poisoning that replayed stale errors in CI lint runs.
  19. v1.2.6Патч20 июл. 2026 г.
    ДобавленоPasteGoogle Docs tables used as fake column layouts (every row has exactly 2 or 3 cells) now paste as real column layouts instead of table blocks. Genuine tables (4+ columns, ragged rows, single column, Google Sheets, nested tables) still paste as tables.
    ДобавленоAPIFour first-class capabilities replacing host workarounds: a style.nativeSelection config opt-out for the forced ::selection repaint, public --blok-block-padding-top/-bottom/-inline tokens for compact read-only rendering, a static Blok.whenAllReady() collective-readiness aggregate, and createReactInlineTool in the React adapter with a full inline-tool destroy() lifecycle.
    ИсправленоData integrityThree silent data-loss defects fixed: code containing < was corrupted on render and save (if (a<b) became if (a), empty code blocks were dropped on save, and a document whose only block was / saved as empty. Tools can now declare a field as literal text via a PLAINTEXT sanitizer rule.
    ИсправленоCaretHighlighting a non-focusable block now blurs stale input focus, so Chromium can no longer restore the current block from a stale collapsed range.
    Исправленоi18nLocale audit corrections across dozens of locales, including Dutch color labels and shared toggle guidance.
  20. v1.2.5Патч20 июл. 2026 г.
    Добавленоi18nNew Taiwan Traditional Chinese locale (zh-TW) with its own emoji dictionary, bringing the corpus to 69 locale variants. A full audit pass over every shipped locale corrected terminology and localized previously hardcoded runtime strings (media captions, database defaults, emoji category scopes, recent color labels, action search, move announcements), with hardened locale validation.
    ДобавленоThemingNew style.tokens config lets each editor instance pass --blok-* token overrides that also reach body-mounted UI (popovers, menus). The edit-mode gutter now defaults to 56px and is owned by Blok, checklists get a dedicated padding token (falling back to the list token), hideToolbar config now actually hides the toolbar and collapses the gutter, and --blok-placeholder-color is a public hook.
    ДобавленоInline toolbarRestructured as a card with a convert row plus a five-column tool grid, including a new clear-format tool and redrawn toggle-heading icons.
    ДобавленоColorText and background tunes merged into a single Color submenu; the color picker gained a Recently Used section remembering the last 5 colors; slash-menu color command titles are localized.
    ДобавленоTableCompact/comfortable text-size switch, grouped with density under a text-size submenu.
    ДобавленоHeaderHeading level converts are grouped under a heading submenu in block settings.
    ДобавленоImageOverlay controls use discrete size tiers (full/medium/compact) instead of fluid scaling, keeping them legible at small widths.
    ДобавленоData compatibilityEditor.js-shaped data is accepted losslessly: null-tolerant LooseOutputData inputs, public equalsOutputData/isEmptyOutputData utilities, echo-safe blocks.render() (re-rendering identical data is a no-op), and a synchronous isRendered flag.
    ИсправленоPopoversBlock-settings menu placement fixes: the menu centers on the six-dots handle, stays attached to it near viewport edges, never touches the screen border, and submenus always open to the right and appear only when hovering the trigger or the submenu itself; page scroll is locked while the menu is open.
    ИсправленоEditorToggle-heading level conversion no longer strands child blocks in a detached DOM (silent data loss); a save-time invariant gate now rejects stranded holders outright.
    ИсправленоAdapters/CoreSix consumer runtime workarounds root-caused and fixed in core, types, and adapters.
    ИсправленоUIInputs show a single focus indicator (no double border), and the popover search icon was removed.
    ИсправленоAccessibilityMove and duplicate announcements are count-neutral and carry correct totals; search results announcements keep their context.
    ИзмененоCIShorter critical path (parallel build start, faster lint), with contract tests enforcing the workflow shape.
    ИзмененоTestsAnimation-frame APIs polyfilled for jsdom environments; extreme-position e2e sweeps guard menu placement at screen edges.
  21. v1.2.4Патч18 июл. 2026 г.
    Исправленоi18nCapitalized toolNames keys (e.g. toolNames.TestTool) resolve again in the toolbox: the lookup now tries the raw tool name first, then the capitalized key, restoring the published contract that 1.2.3's raw-name lookup silently dropped. An empty toolbox title no longer short-circuits the fallback chain and renders a blank slash-menu item — it is treated as absent and falls back to the capitalized tool name.
    ИзмененоCIThe docs site now deploys only on package releases, and the deploy is gated by a verifier that checks the published packages (tied to the release version family) before the site goes live.
  22. v1.2.3Патч18 июл. 2026 г.
    ДобавленоThemingMore host customization hooks: heading tokens (keyed off data-blok-heading-level) and embed tokens, a --blok-list-gap token for list spacing, and palette tokens declared at zero specificity via :where() so host overrides always win. --blok-content-max-width is now authoritative when the editor is in width: 'full' mode.
    ДобавленоRead-onlyThe editor wrapper is stamped with data-blok-readonly as a public styling hook, and the block-controls gutter auto-collapses in read-only mode so content uses the full column.
    ДобавленоAdaptersThe three React-integration workarounds were removed by fixing their root causes in core.
    Исправленоi18nCustom tool titles now localize via toolNames.<toolName> dictionary keys.
    ИсправленоImageGIF→video auto-conversion is skipped when no video tool is registered, instead of failing the upload.
    ИзмененоBuilddist/ output is now minified and JSON data is emitted as JSON.parse strings, cutting published bundle weight.
    ИзмененоDocsDocumented the theming hooks, data-blok-readonly attribute, contentAlign, toolbox: false gating, the GIF guard, the readOnly object form, and toolNames keys.
  23. v1.2.2Патч17 июл. 2026 г.
    ДобавленоThemingNew public --blok-* custom properties let host apps customize editor layout without reaching into internals: --blok-content-max-width (content column cap), --blok-editor-gutter-start/--blok-editor-gutter-end (space reserved for the floating block controls, RTL-correct), --blok-list-padding-start (list indent), and --blok-search-input-placeholder (popover search placeholder color). Defaults preserve current behavior; documented in the docs-site Styles API section.
    ДобавленоReactFirst-class block authoring: createReactBlock renders block tools authored as React components through a shared portal host inside the host app's React tree, so app-level context (providers, themes, stores) reaches block components directly — no more createRoot per block or context bridges. Every function in a tool's config (including nested ones like uploader.uploadByFile) is now re-bound to the latest render's closure, so inline closures work without freezing identities or recreating the editor.
    ИсправленоFloating UIA hardening sweep across every floating surface: popovers anchored inside nested scroll containers no longer drift or detach on scroll (snapshot anchors, virtual selection anchors, and fixed-position menus all track correctly); tooltips dismiss on nested scroll; the emoji picker and link hover card follow moving anchors; root boundary calculation is normalized for scrolled and 100vh host bodies. An architecture test now enforces that all floating UI goes through the central positioning module.
    ИсправленоRendererStored block data is now sanitized on render with the same per-tool sanitize config the Saver applies, closing stored-HTML injection for legacy data that never round-tripped through save (e.g. a raw <iframe width> baked into paragraph text overflowing its column). Fixed-width iframes/embeds are additionally capped at max-width: 100%.
    ИсправленоLinkThe hover card now requires actual pointer motion before opening, so it no longer opens when a link merely renders or scrolls under a stationary cursor.
    ИзмененоReleasePreflight (eslint ∥ tsc ∥ vitest) and build pipelines were parallelized (~2× faster wall clock), with per-step timeouts so a hung build can no longer stall a release. GitHub Packages mirror tarballs now rewrite @bloklabs/core specifiers in every shipped file (previously react/vue types/index.d.ts still referenced the npm scope, breaking consumer tsc on GHP-only installs).
  24. v1.2.1Патч16 июл. 2026 г.
    ДобавленоImageImages now display at the full width of the article by default. Previously an image without an explicit size preset rendered at the medium (520px) preset; the default is now the full preset. Images with an explicitly saved size are unaffected.
    ИсправленоPopoverThe search input's focus ring was clipped along its bottom edge by the context label's opaque background painting over it; the search wrapper now renders in the positioned paint layer so the full ring is visible. The gap between the search field and the context label was also widened slightly.
  25. v1.2.0Минорный16 июл. 2026 г.

    MediaPlayback speed and loop preferences now persist across audio and video blocks. They are stored in localStorage under shared per-media-type keys (blok:audio:rate/loop, blok:video:rate/loop), joining the existing shared volume and per-source position keys, and are restored when a player attaches — without dirtying block data.

    ДобавленоMediaPlayback speed and loop preferences now persist across audio and video blocks. They are stored in localStorage under shared per-media-type keys (blok:audio:rate/loop, blok:video:rate/loop), joining the existing shared volume and per-source position keys, and are restored when a player attaches — without dirtying block data.
    ИсправленоQuoteSaving a quote block no longer strips href/target/rel from links (leaving dead anchors in stored content) or unwraps bold/italic marks. Quote now uses the same inline-text sanitize rules as paragraph and header, so links, formatting, and color styles survive save and conversion.
    ИсправленоAudioOneDrive share links from SPO-migrated accounts (the new /u/c/<cid> form) can't be resolved anonymously and produced a silently dead player; they now surface a clear "needs an uploader backend" error instead.
    ИсправленоToolbarThe plus button and drag handle no longer stay stuck at the wrong offset after the slash-command popover opens or closes; the toolbar repositions on toolbox open/close instead of relying on a resize side effect.
    ИсправленоToolboxOpening the toolbox silenced the current block's mutation watching and never re-armed it, leaving the block permanently deaf to later content changes until re-render. Watching is now re-armed on both close paths, and the toolbar follows any inner-geometry change that doesn't resize the block holder.
    ИзмененоCIThe build artifact now ships the extracted packages/*/dist adapter bundles alongside dist/, fixing downstream unit and E2E jobs; yarn.lock was synced to the ^1.1.1 core peer range.
    ИзмененоDocsREADME and the docs site updated for the @bloklabs/* package family; the rename notice was subsequently dropped from the README.
  26. v1.1.1Патч15 июл. 2026 г.
    Ломающее изменение`@bloklabs/core` has zero peer dependencies. The react/react-dom/vue optional peers are goneinstalls no longer warn about frameworks you don't use, and the GitHub Packages peerDependenciesMeta-stripping bug (which forced Yarn Berry consumers to add a packageExtensions workaround) no longer applies. Delete that .yarnrc.yml entry after upgrading.
    Ломающее изменениеAdapters declare hard, accurate peers: each requires its framework plus @bloklabs/core at the matching version.
    Ломающее изменениеGitHub Packages mirrors: @dodopizza/blok remains the core mirror; adapters mirror as @dodopizza/blok-react, @dodopizza/blok-vue, @dodopizza/blok-angular; the CLI stays @dodopizza/blok-cli.
    Ломающее изменениеMigration: the bundled codemod (npx -p @bloklabs/core migrate-from-editorjs) now also rewrites legacy @jackuait/* import specifiers and package.json dependency keys to the new names.
  27. v1.1.0Минорный15 июл. 2026 г.

    AudioShare links from seven more services — Dropbox, OneDrive, GitHub, GitLab, Hugging Face, Google Cloud Storage, and Internet Archive — are recognized and rewritten to their direct-content form, so they play in the browser with no backend. Google Drive share links (hotlink-blocked server-side) are normalized and routed through the consumer's uploadByUrl backend, with a Drive-specific error message when none is configured. The error state gets a styled callout with a retry button.

    ДобавленоAudioShare links from seven more services — Dropbox, OneDrive, GitHub, GitLab, Hugging Face, Google Cloud Storage, and Internet Archive — are recognized and rewritten to their direct-content form, so they play in the browser with no backend. Google Drive share links (hotlink-blocked server-side) are normalized and routed through the consumer's uploadByUrl backend, with a Drive-specific error message when none is configured. The error state gets a styled callout with a retry button.
    ДобавленоRead-onlyreadOnly now accepts an object form: { hideControls: true } enables read-only mode and suppresses the toolbar, block settings, and inline toolbar. Exposed via isControlsHidden, normalized across the React, Vue, and Angular adapters, and ReadOnlyModeConfig is exported from the types root.
    ДобавленоImagecompress: { format: 'avif' } now produces real AVIF via WebCodecs when the canvas encoder cannot, and a new fallbackFormat option (e.g. 'webp') covers browsers with no AV1 encoder instead of silently uploading the original bytes.
    ИсправленоAudioAudio inserted by URL is now enriched like uploads: waveform, title/artist metadata, and cover art, failing soft to a plain scrubber when the host blocks the CORS fetch.
    ИсправленоTableUndo inside a table no longer duplicates cell blocks into invisible ghosts that reappear under the table after save; blocks placed at the top of a cell no longer drift to the bottom or become orphans on save. The Saver gains save-boundary guards for cell membership and cell block order (throw in dev/test, repair the emitted output in production).
    ИсправленоTooltipThe tooltip bubble is click-transparent, so it never swallows clicks on controls it covers (e.g. color-picker swatches under a bottom-row tooltip).
    ИсправленоPopoverNested popovers no longer collapse to their padding in WebKit; the marker color picker rendered as a 12px sliver in Safari.
    ИсправленоStylesBody-mounted UI (link hover card, notifier toasts, drag previews) now carries the scope attribute, so its styles survive in consumer apps; a new architecture test enforces the invariant for every document.body mount.
    ИсправленоAngularThe ng-packagr build stages the readonly-config module, fixing a CI-only TS2307; an architecture test now walks the adapter's import graph to catch unstaged modules.
    ИзмененоPlaygroundGallery empty states run the real tool per state (with per-state tool config) instead of static mockups, including a live Google Drive error demo.
    ИзмененоTestsAdversarial table undo probes (merge undo, insert undo/redo), tooltip click-transparency lifecycle guards, and an exhaustive swatch hit-test sweep.
  28. v1.0.0Мажорный14 июл. 2026 г.

    HeaderToggle headings are now offered at all six levels, staying in sync 1:1 with regular headings.

    ДобавленоHeaderToggle headings are now offered at all six levels, staying in sync 1:1 with regular headings.
    ИсправленоMigrationThe legacy Editor.js grammar is now authored as ESM so consumer dev servers can load blok's source graph without a build step.
    ИсправленоColumnsBlocks inserted via the plus button no longer save at the bottom of the column; saved order now matches the on-screen (WYSIWYG) order.
    ИсправленоStylesInjected utilities are scoped so a host application's CSS reset can't flatten the editor.
    ИзмененоMigrationSingle-source LEGACY_GRAMMAR shared by both the runtime and the codemod; the codemod's source rewrite is now AST-guided (via the consumer's @babel/parser) to avoid mangling comments, strings, and unrelated identifiers.
    ИзмененоCIRoot-caused CI failures: scoped-utility drift on body-mounted UI, lint, and test flake fixes (webkit paste timing, six-level toggle-heading counts).
  29. v0.25.0Минорный13 июл. 2026 г.

    SpacerNew adjustable-height spacer block. Drag either edge to resize (dual-edge grips), with a text-block-height floor, snap-to-sibling and snap-to-column alignment guidelines, an on-edge capsule resize pill, and accent hover cues. Fully invisible in read-only/published renders.

    ДобавленоSpacerNew adjustable-height spacer block. Drag either edge to resize (dual-edge grips), with a text-block-height floor, snap-to-sibling and snap-to-column alignment guidelines, an on-edge capsule resize pill, and accent hover cues. Fully invisible in read-only/published renders.
    ДобавленоImageUploads are now automatically compressed and re-encoded (compress, on by default), with opt-in smaller output formats. In-cell images gain a resize floor and fluid chrome via container queries.
    ДобавленоToolbarCmd/Ctrl+Slash opens the block menu in read-only mode.
    ИсправленоTableLarge batch of Notion-parity fixes: paste-header handling, the cell color picker, the cell menu, arrow-key navigation between cells, and column width reset. The cell box now follows the caret (instead of the pointer) and the resize handle no longer forces overflow. Focus stays inside a cell when its content is deleted, the caret stays put after clearing a multi-cell selection, multi-line cell selections merge into one rounded shape, drag-selecting several lines within a single cell works, and list items scale to the cell font instead of outsizing sibling paragraphs.
    ИсправленоColumnsStranded resize separators left behind by removing a column no longer render as a phantom column.
    ИсправленоCoreNever Tab-indent a block into a tool-owned container.
    ИсправленоToolboxKeep plus-button blocks on a table out of its cells, and anchor fuzzy search at word boundaries.
    ИсправленоSelectionStop hijacking intra-line text drags inside table cells.
    ИсправленоEmbedValidate stored URLs at render time (stored-XSS guard).
    ИсправленоListKeep bullet markers non-editable so Enter never ghosts an item.
    ИсправленоToolbarRead-only drag-handle refinements: announce it as a menu button, show a pointer cursor, and drop the ⌘/ and "Drag to move" hint lines; no read-only handle appears beside blocks that paint nothing.
    ИсправленоTunesHide the copy-link shortcut hint in read-only mode.
    ИсправленоStylesRight contentAlign no longer collapses into centering, and preflight resets are scoped to @layer base so Blok's own utilities win.
    ИсправленоReactGuard against stale dist exports and a StrictMode readiness race.
  30. v0.24.3Патч10 июл. 2026 г.
    ДобавленоHeaderOpt-in anchorIds config derives stable heading anchor ids from heading text.
    ИсправленоLinkPad the edit-menu input wrapper so the focus ring isn't clipped, and enlarge the remove-link (trash) icon.
    ИзмененоCIFetch mirror tags before pushing to avoid creating over an existing tag.
  31. v0.24.2Патч9 июл. 2026 г.
    ДобавленоLinkNew link.transform config, a superset of transformHref: consumers can set per-anchor href/target/rel plus extra attributes (class/title/data-*) without post-processing the rendered DOM. Applies consistently across every anchor path (render, paste, and hand-created links); omitted fields fall back to existing defaults (including the same-page _self rule) and extra attributes never clobber the managed href/target/rel.
    ИсправленоColumnsKeep the inter-column gutter in read-only/published renders. The gap was previously produced entirely by the (no-op in read-only) resize handles, so read-only columns rendered flush; the gutter is now decoupled from the resizers.
    ИсправленоLinkCenter the hover card under the pointer (shifting near viewport edges) with a fixed gap to the link, and stop the block toolbar leaking through the card when hovering top-layer chrome.
    ИсправленоNotifierFix top-layer placement so the toast stays in its corner (no UA Canvas box or top-left jump), remove the in-pill dismiss cross (auto-dismiss/Escape still close it), and tighten the pill's vertical padding.
  32. v0.24.1Патч9 июл. 2026 г.
    ИсправленоTypesFixed a publishing defect (introduced in 0.24.0) where importing @dodopizza/blok/react — or @dodopizza/blok/markdown — made a consumer's TypeScript compiler follow the published declarations into raw src/ implementation, producing spurious errors about unresolved micromark-util-types / @types/mdast (TS2307) and implicit any (TS7006). The public types/*.d.ts surface is now self-contained and no longer re-exports from src/.
    ИзмененоTypesMechanically enforce that no published types/*.d.ts re-exports or imports from src/, and generate the self-contained icon declarations from source (scripts/generate-icons-dts.mjs).
  33. v0.24.0Минорный9 июл. 2026 г.

    VueNew first-class @jackuait/blok/vue adapter: BlokEditor component, useBlok, and provideBlok, plus createVueBlock/useBlocks for authoring custom blocks and driving the block tree from Vue. Custom Vue blocks support read-only in-place toggling.

    ДобавленоVueNew first-class @jackuait/blok/vue adapter: BlokEditor component, useBlok, and provideBlok, plus createVueBlock/useBlocks for authoring custom blocks and driving the block tree from Vue. Custom Vue blocks support read-only in-place toggling.
    ДобавленоAngularNew first-class @jackuait/blok/angular adapter shipped as an Angular Package Format build (ng-packagr): the editor component/directive, a block portal registry, the BLOK_BLOCK_CONTEXT render-context token, createAngularBlock authoring, and the reactive injectBlocks block-tree API.
    ДобавленоReactNew useBlocks hook exported from @jackuait/blok/react: a reactive block-tree API with reads, insert (position + append-to-parent, explicit id, tunes, replace), move (before/after/toIndex), nest/unnest/remove/transact, insertMany, atomic insertTree for nested subtrees, and additive insertMarkdown. Block-creation semantics are hardened across hierarchy edges with compile-time drift guards against the core API.
    ДобавленоAdaptersClosed React/Vue/Angular parity gaps across component paths and escape hatches; shared one blocks-api core so Vue's useBlocks reaches React parity, and extracted shared fillDefaults/PropSchema helpers. Custom-block authoring is now first-class on the public API surface.
    ДобавленоAccessibilityFive-wave overhaul adapting shadcn/ui interaction patterns to Blok's UI primitives: dismissal-layer/popover teardown/scroll-lock/announcer foundations, an anchored-positioning engine and shared modal Dialog, keyboard reachability for toolbars/menus/radios/rename, and assistive-tech feedback parity across selection, drag, menus, and arrival.
    ДобавленоLinkClickable links with a hover card in both edit and read-only modes (with enter/leave animation), an edit mode featuring a title field and remove-link action, refined hover-card chrome, blocking of unsafe-scheme navigation, and a consistent same-page/anchor-link rule that opens such links in the same window across all link-creation paths.
    ДобавленоBlocksStructural parentId nesting: any block can now be nested inside any list (flat per-block indent), with list keyboard nesting and drag/serialization migrated onto the structural block tree.
    ДобавленоKeyboardCmd+Left/Cmd+Right navigate between blocks at block edges; Backspace at the start of a nested block removes one indent level; mixed-list Tab indents both kinds; and numerous Notion-parity Tab/arrow/Delete/Backspace fixes.
    ДобавленоPastePaste-without-formatting (Cmd/Ctrl+Shift+V); recovery of buildin/Notion toggles and soft breaks from lossy GFM HTML fallback.
    ДобавленоInlineLink-markdown auto-format and a link-paste menu; shortcut-triggered Link/Equation/Marker now open a standalone menu positioned right under the selection.
    ДобавленоPopoverCustom cross-platform scrollbar that hides the classic OS bar while keeping a stable gutter; reel-like edge distortion replacing the scroll haze.
    ДобавленоConvertShared buildConvertMenuEntries with titleKey resolution, used by both block settings and the inline "Turn into" menu (which now lists the full text family), guarded by parity E2E.
    ДобавленоImageConfigurable auto-retry on image load failure (default 5).
    ИсправленоCRDT/YjsUndo/redo no longer yanks the caret to the top of the document; the caret restores to the correct position, split() inherits full tool data (correct heading undo caret), redo moves the caret to the new block on an Enter split, and five further Yjs sync gaps in tools and modules were closed.
    ИсправленоListClosed dozens of Notion-parity divergences across convert, keyboard, drag, selection, and copy/paste; source ordered lists renumber when an item is dragged away; bullet glyphs refresh on depth change.
    ИсправленоText/HeaderFixed 30+ Notion-parity bugs by root cause (slash+space, duplicate pulse, indent toolbar, caret offset preservation on turn-into, and more).
    ИсправленоTablePreserve merged cells and lists when pasting external tables, and keep list markup when copying cells out to external apps.
    ИсправленоPastePreserve lists and quote-ness in pasted blockquotes; pasted links use the default link color; the link menu shows on non-empty blocks without erasing content; closed remaining sanitizer/merge data-loss gaps found by an audit.
    ИсправленоBlocksRe-parent a merged block's children onto the survivor instead of orphaning them; release toggle children as siblings when turning a toggle into text; fire the tool moved() hook on setBlockParent; never write split text into a mutation-free decoration.
    ИсправленоColumnsMatch Notion's inter-column gutter spacing; restore DOM order when undoing column creation.
    ИсправленоDragList-item drop line tucks under the text with a marker lead-in; depth changes apply on same-slot drops; non-list blocks stop previewing nested drops they can't reach.
    ИсправленоSelectionFake highlight matches the native selection color and stays visible while a menu input is focused; Cmd+A container-scoped staging.
    ИсправленоMarkerReset <mark> background so colored text never shows the browser's yellow highlight.
    ИсправленоToggleArrow container stays a constant 28px square and pins to the first line for multi-line toggle/heading.
    ИсправленоStylesReserve a scrollbar gutter on all scrollable components and keep it in nested inline-toolbar menus; auto-hide scrollbars system-style while keeping the gutter.
    ИзмененоPasteMechanically enforce the paste attribute law and the paste stamp law via architecture tests.
    ИзмененоTestsRepaired all CI-matrix E2E shards; added regression coverage across list keyboard shortcuts, columns undo order, popover scrollbar spec, and adapter integration/e2e.
    ИзмененоLintResolved all 69 root ESLint problems at the root cause.
  34. v0.23.5Патч25 июн. 2026 г.
    ДобавленоCoreThe link config ({ target, rel, transformHref }) now also applies on the render and paste paths, not just the interactive link tool. Anchors coming from stored block HTML (rendered via blocks.render()) and <a> arriving through the clipboard now get the configured target/rel forced and transformHref applied to their href — so consumers no longer need to post-process the rendered or pasted DOM. Because the render path rewrites live anchors whose href round-trips into saved data, transformHref must be idempotent.
    ДобавленоCoreNew onBeforeRender(blocks) => blocks config transforms the blocks array before every render (the initial render and each blocks.render()), letting you run app-specific data migrations inside Blok instead of pre-processing the data yourself. It runs on the raw saved blocks before format analysis, so it can also inject blocks into an empty document.
    ДобавленоCoreNew onAfterRender(api) config fires after a render completes and the blocks are in the DOM (initial render and every blocks.render()), for post-render side effects such as scroll restoration — distinct from the once-only onReady.
    ДобавленоCoreA stable data-blok-rendered attribute (exposed as DATA_ATTR.rendered) is now set on the editor wrapper when a render batch finishes inserting blocks, and removed while a re-render is in flight — a DOM-level render-readiness gate that complements the existing blocks:rendered event.
    ДобавленоBlock TunesA custom tune's render(context) now receives an optional BlockTuneRenderContext whose getPopoverElement() returns the host tune popover element ([data-blok-popover]), so tunes can anchor sub-menus or portals inside Blok's popover without reaching into the DOM via closest(...). The element resolves once the popover mounts (it is null synchronously during render()).
    ДобавленоReact<BlokEditor>/useBlok now accept onBeforeRender and onAfterRender. Both are attached only when provided and are ref-stable, so updating the callbacks never recreates the editor.
  35. v0.23.4Патч25 июн. 2026 г.
    ДобавленоCoreNew onSave(data, api) config delivers the full serialized OutputData (debounced via the existing change-batch window) whenever content changes — the "output half" of a controlled editor. Pair it with the data config to mirror editor state into your own store with a single callback instead of calling saver.save() by hand. Only user-driven changes trigger it; programmatic render() does not (the change observer is disabled during render), so a controlled round-trip won't recurse. Available to all consumers, not just React.
    ДобавленоReact<BlokEditor>/useBlok now accept onSave, making <BlokEditor data={data} onSave={setData} /> a true controlled component to pair with the reactive data prop from 0.23.3. The callback is ref-stable (never recreates the editor) and attached only when provided. Echoing the payload straight back via onSave={setData} is caret-stable: the adapter records the editor's own emitted output as the content baseline, so the round-trip deep-equal–dedupes to a no-op (no re-render, no caret reset) while genuine external data changes still render in place.
  36. v0.23.3Патч25 июн. 2026 г.
    ДобавленоReactThe <BlokEditor>/useBlok data prop is now reactive: passing new content re-renders the editor in place via editor.render() instead of being read only once at creation. Identical content is de-duplicated (deep-equality) so the caret is never clobbered, rapid changes are serialized, and a freshly-seeded editor is not double-rendered.
    ДобавленоCoreNew typed render events: blocks:rendered (payload { count }) fires when a batch finishes rendering, and block:rendered (payload { blockId }) fires per block. The runtime event-name constants BlocksRendered/BlockRendered are exported, so consumers can react to rendering instead of polling the DOM. The public Events API is now typed against an event/payload map while still accepting arbitrary string events.
    ДобавленоCoreNew link config ({ target, rel, transformHref }) lets consumers configure the anchors the link tool creates instead of post-processing the DOM. Defaults (_blank/nofollow) are preserved, configured values now survive save, and URL validation/allowlisting is unchanged.
    ДобавленоPasteNew onBeforePaste(html) => string | null config hook transforms (or drops) raw clipboard HTML before Blok preprocessing; returning null falls back to plain-text paste.
    ДобавленоAPINew editor.tools.update(name, config) shallow-merges a tool's config in place — e.g. swap an uploader — without recreating the editor.
    ИзмененоTestsExported stable TEST_ID constants (plus button, settings toggler, block wrapper) wired into the editor chrome via data-blok-testid, so consumers no longer query internal selectors.
  37. v0.23.2Патч25 июн. 2026 г.
    ИзмененоDocsDocumented two React-adapter caveats. <BlokEditor> must not be wrapped in styled() or any HOC that reserves the theme prop: styled-components claims theme for its own ThemeProvider, so it never reaches the editor and theme sync silently breaks — render it directly and style the container via className. And deps values must be referentially stable (each value compared individually, not the array wrapper), otherwise the editor is recreated on every render. Both caveats now appear in the README, the docs site, and the BlokEditor/useBlok JSDoc and published type declarations.
  38. v0.23.1Патч25 июн. 2026 г.
    ДобавленоCoreA new editor.placeholder runtime API (get/set) lets consumers read and change the empty-paragraph placeholder on a live editor, mirroring the existing width API. Updates apply to existing blocks and to blocks created afterwards.
    ДобавленоReact<BlokEditor> now accepts a reactive placeholder prop (backed by the new core API) that updates the editor in place without recreating it, and forwards all standard <div> attributes — id, aria-*, data-*, and the like — to the editor container.
  39. v0.23.0Минорный24 июн. 2026 г.

    ReactA blessed <BlokEditor> component is now the recommended way to embed Blok in React. It forwards a typed ref to the live editor instance, takes an uncontrolled data seed, and reactively syncs readOnly, autofocus, theme, and width props without recreating the editor. Its onReady callback fires after the ref commits, so consumers can safely call ref.current from inside it. The lower-level useBlok hook plus BlokContent remain available as an escape hatch.

    ДобавленоReactA blessed <BlokEditor> component is now the recommended way to embed Blok in React. It forwards a typed ref to the live editor instance, takes an uncontrolled data seed, and reactively syncs readOnly, autofocus, theme, and width props without recreating the editor. Its onReady callback fires after the ref commits, so consumers can safely call ref.current from inside it. The lower-level useBlok hook plus BlokContent remain available as an escape hatch.
    ИзмененоReactuseBlok now reactively syncs theme and width prop changes to the editor instance, mirroring the existing readOnly/autofocus pattern.
    ИзмененоDocsThe demo wrapper now dogfoods BlokEditor, and the README React section documents the recommended BlokEditor path, the uncontrolled data contract, reactive props, and the useBlok + BlokContent escape hatch.
    ИзмененоTestsAdded e2e coverage for save-via-ref and live prop toggles, a published-vs-source type-drift guard, and data-blok-testid-based locators.
  40. v0.22.0Минорный24 июн. 2026 г.

    PasteContent copied from buildin.ai now imports as native Blok blocks at full fidelity. buildin's clipboard carries a lossless text/next-space-blocks JSON payload beside a lossy Markdown/HTML twin; Blok previously fell back to the twin, where tables collapsed to literal |pipes|, media degraded to links, and callouts, toggles, columns, to-dos, and code language flattened away. A new handler decodes the JSON directly, reconstructing the same native blocks as a Blok→Blok paste — paragraphs, to-dos, H1–H4, tables (grid + parented cells), bulleted/numbered lists, toggles, dividers, quotes, callouts (emoji + colour), code (with language), equations, toggle-headings, column lists, and image/video/audio/file/embed-bookmark media. (Inline marks — bold/italic/link/colour — are a documented follow-up.)

    ДобавленоPasteContent copied from buildin.ai now imports as native Blok blocks at full fidelity. buildin's clipboard carries a lossless text/next-space-blocks JSON payload beside a lossy Markdown/HTML twin; Blok previously fell back to the twin, where tables collapsed to literal |pipes|, media degraded to links, and callouts, toggles, columns, to-dos, and code language flattened away. A new handler decodes the JSON directly, reconstructing the same native blocks as a Blok→Blok paste — paragraphs, to-dos, H1–H4, tables (grid + parented cells), bulleted/numbered lists, toggles, dividers, quotes, callouts (emoji + colour), code (with language), equations, toggle-headings, column lists, and image/video/audio/file/embed-bookmark media. (Inline marks — bold/italic/link/colour — are a documented follow-up.)
    ИсправленоPasteCallout body and colour now survive import from both Notion and buildin.ai. Blok's callout stores its body in child blocks, so the inline title/body text the parsers emitted as data.text was silently discarded — the callout is now emitted (colours only) plus a child paragraph carrying its text. Out-of-palette callout colours (e.g. buildin's British grey, or any name outside Blok's 9-colour preset) previously produced an undefined CSS variable that dropped both the background and the border; colours now normalize through Blok's preset palette (greygray; unknown names clamp to null).
  41. v0.21.1Патч24 июн. 2026 г.
    ДобавленоMediaImage, video, audio, and file blocks can now be restricted to upload-only or link-only via configuration, so consumers can offer a single source instead of always exposing both.
    ИзмененоMediaDeduped MediaSource into a single shared type across the media tools.
    ИзмененоDocsDocumented the audio block tool, and added upload-only and link-only empty states to the playground gallery.
  42. v0.21.0Минорный23 июн. 2026 г.

    TestsAdded regression coverage for HTML-over-Markdown routing, document-order preservation, internal-reference rescue, pasted audio titles, and date-glyph handling.

    ИсправленоPasteRich clipboards (Notion and similar) ship both a faithful HTML payload and a lossy Markdown twin. Routing now prefers the HTML handler, so pasted images, links, and structure the Markdown twin drops are preserved.
    ИсправленоPasteNotion content keeps its document order: nested children no longer render above their parents (only table cells stay children-first, since the table tool resolves cell ids on insert). Internal references that previously vanished — sub-pages, linked databases / collection views, and inline page mentions — now paste as Notion bookmarks/links instead of being dropped or leaking the raw "‣" glyph, and uploaded media whose binary isn't on the clipboard becomes a Notion-link bookmark carrying the filename rather than a bare filename paragraph.
    ИсправленоPastePasted external Notion audio now shows its title (the player reads data.title, which was left blank), and a malformed inline date annotation no longer leaks the raw "‣" placeholder glyph.
    ИзмененоTestsAdded regression coverage for HTML-over-Markdown routing, document-order preservation, internal-reference rescue, pasted audio titles, and date-glyph handling.
  43. v0.20.0Минорный22 июн. 2026 г.

    PasteContent copied from Notion now migrates as native blocks with full state preserved. When Notion's lossless clipboard JSON is present it is used directly (the high-fidelity path), with an HTML fallback for sources that only expose markup. Inline equations and page mentions are mapped to their Blok equivalents.

    ДобавленоPasteContent copied from Notion now migrates as native blocks with full state preserved. When Notion's lossless clipboard JSON is present it is used directly (the high-fidelity path), with an HTML fallback for sources that only expose markup. Inline equations and page mentions are mapped to their Blok equivalents.
    ДобавленоAudioCustom cover art. A cover picker (file upload or image URL) opens from an editable overlay button on the player; covers can be set, replaced, or removed (via a "Remove cover" block setting), with an animated picker open, a sliding Upload/Link tab transition, a themed surface that matches the player, and i18n across all locales. Audio blocks with no cover now show an inertial spinning-vinyl turntable placeholder instead of an empty panel.
    ИсправленоAudioRepaired the transport controls and the video-style playback-speed menu, which now stays open after picking a preset. The volume bar fill is fixed so a muted track reads differently from a full one, and the playing waveform pulses smoothly without gouging its dots. The caption toggle stays on the compositor, and the cover picker no longer jumps height on tab swap, gets an explicit width so the URL field isn't cramped, and revokes leaked cover blobs on a destroy race.
    ИсправленоTablePinned the top toolbar anchor flush to the table edge.
    ИзмененоTestsAdded unit and e2e coverage for custom cover set/remove and for real Notion page-mention arity, and cleared the lint/type violations the cover-art work introduced.
  44. v0.19.2Патч20 июн. 2026 г.
    Ломающее изменениеTypesOutputBlockData's data field is now typed Record<string, unknown> instead of any (matching BlockToolData and SavedData). Reading a property off a saved block's data — e.g. on save() output — now yields unknown rather than any, so code that indexes into block.data may need a cast or type guard. This is a type-only change with no runtime effect.
    ИсправленоTypesPublished .d.ts declarations are now self-contained and no longer re-export raw src/*.ts. A bare import { Blok } from '@jackuait/blok' previously dragged editor source into the consumer's TypeScript program (through tooltip and popoverflipper), surfacing internal type errors under strict consumer flags such as noUncheckedIndexedAccess. The tooltip and popover declarations now inline their public types, so consuming the package no longer type-checks Blok's internals. (The opt-in /markdown subpath is unchanged.)
    ИсправленоTypesThe published declarations are now internally consistent under skipLibCheck: false. Fixed an incorrect BlockToolData import path in the database/header/list tool declarations (which also produced spurious "incorrectly extends BlockTool" errors), added a missing InlineToolConstructable/InlineToolConstructorOptions import in the type entry, and removed phantom Dictionary/DictValue re-exports.
    ИзмененоDependenciesMoved nanoid to devDependencies; it is bundled into every dist artifact and was never a runtime external.
  45. v0.19.1Патч20 июн. 2026 г.
    ДобавленоTypesisReady now resolves with the fully-initialized Blok instance (was Promise<void>), so const editor = await blok.isReady yields a ready, fully-typed editor without a cast. New exported PendingBlok type describes the surface available synchronously after new Blok() and before isReady resolves (isReady, destroy, theme, width) — type a reference held during that window as PendingBlok instead of widening to Partial<Blok>, then await isReady to narrow it to the full API. new Blok() still returns the full Blok, so existing usage is unaffected.
  46. v0.19.0Минорный20 июн. 2026 г.

    WidthNew public width API on the editor instance: instance.width.get(), set('full' | 'narrow'), and toggle() switch the content layout between 'narrow' (the default, constrained to --max-width-content) and 'full' (the content max-width is removed so it fills its container). It mirrors the theme API, including buffering a set() call made before the editor is ready and replaying it once the editor is initialized.

    ДобавленоWidthNew public width API on the editor instance: instance.width.get(), set('full' | 'narrow'), and toggle() switch the content layout between 'narrow' (the default, constrained to --max-width-content) and 'full' (the content max-width is removed so it fills its container). It mirrors the theme API, including buffering a set() call made before the editor is ready and replaying it once the editor is initialized.
    ИсправленоTypesDeclare the history API on the exported Blok instance type. history (clear(), undo()/redo(), canUndo()/canRedo()) was already available at runtime; consumers no longer need to cast the instance to reach it.
  47. v0.18.0Минорный19 июн. 2026 г.

    AudioNew native Audio block tool. Now-playing card with cover art (lazy music-metadata extraction), a waveform canvas with click/drag seek, transport controls (play/pause, volume, playback speed, loop, keyboard shortcuts, persisted preferences), file and URL upload, paste handling routed away from the File block, read-only support, and i18n across all locales. The player card is a full-bleed redesign — a tall cover panel (music-note placeholder when there is no art), a hero waveform scrubber with rounded bars, a slim transport bar, and motion polish.

    ДобавленоAudioNew native Audio block tool. Now-playing card with cover art (lazy music-metadata extraction), a waveform canvas with click/drag seek, transport controls (play/pause, volume, playback speed, loop, keyboard shortcuts, persisted preferences), file and URL upload, paste handling routed away from the File block, read-only support, and i18n across all locales. The player card is a full-bleed redesign — a tall cover panel (music-note placeholder when there is no art), a hero waveform scrubber with rounded bars, a slim transport bar, and motion polish.
    ДобавленоMediaImage, video, and audio blocks now accept any file of their media family (image/*, video/*, audio/*) by default. Restrict the accepted types through the existing types config, which now accepts both exact MIME types (image/png) and family wildcards (image/*).
    ИсправленоTypesExport File, Audio, and Video (and their data/config/uploader types) and add the file/audio/video keys to defaultBlockTools from the @jackuait/blok/tools types entry. The runtime already exported these tools; consumers no longer need a local ambient type shim to import them.
    ИзмененоPlaygroundAudio block states in the block-states gallery (real ID3-tagged track and a "No cover art" state) plus an e2e harness for insert/upload/play/seek.
  48. v0.17.0Минорный19 июн. 2026 г.

    VideoNew native Video block with a custom Airbnb-style player, brought to YouTube parity: full keyboard control (j/l/k, 09, Home/End, frame-step, volume, speed), a scrubber with buffered range, hover frame-preview tooltip and mini progress bar, an in-player gear menu (Notion-style playback speed with glide, loop, ambient-glow intensity), and view modes — picture-in-picture, a FLIP-morphed theater/cinema mode, and a fade-in ambient glow. Player polish includes click-to-toggle play/pause, a centre play/pause burst, press-and-hold for 2× playback, arrow-key ±5s seek with side indicators, idle auto-hide, buffer spinner, time-remaining toggle, right-click menu, stats overlay and persisted preferences.

    ДобавленоVideoNew native Video block with a custom Airbnb-style player, brought to YouTube parity: full keyboard control (j/l/k, 09, Home/End, frame-step, volume, speed), a scrubber with buffered range, hover frame-preview tooltip and mini progress bar, an in-player gear menu (Notion-style playback speed with glide, loop, ambient-glow intensity), and view modes — picture-in-picture, a FLIP-morphed theater/cinema mode, and a fade-in ambient glow. Player polish includes click-to-toggle play/pause, a centre play/pause burst, press-and-hold for 2× playback, arrow-key ±5s seek with side indicators, idle auto-hide, buffer spinner, time-remaining toggle, right-click menu, stats overlay and persisted preferences.
    ДобавленоVideoCustom fullscreen surface with a top caption bar, a "Hide controls" tune for a control-free player, and GIF-style autoplay/loop tunes.
    ДобавленоImageAuto-convert dropped, pasted and remote-URL GIFs into a looping Video block via WebCodecs + webm-muxer, gated by the convertGifToVideo config (default on); the original GIF is kept on CORS failure, with a "Converting…" label shown during conversion.
    ДобавленоMedia30MB default upload limit with per-type maxSize configuration and human-readable too-large errors.
    ИсправленоPasteURL paste always prompts now; the previous auto-embed behaviour has been removed. Breaking: consumers relying on silent auto-embed must opt in through the paste menu.
    ИсправленоVideoReserve the aspect ratio before metadata loads to prevent squeeze-on-load, centre and letterbox the fullscreen player, strip editor chrome in fullscreen, and hide the bottom mini progress bar while fullscreen.
    ИсправленоVideoExit theater mode reliably on Escape via a capture-phase listener with a smooth deferred dismiss, and drive the scrubber fill with requestAnimationFrame for smooth playback tracking.
    ИзмененоBuildMove webm-muxer to devDependencies so it is bundled rather than treated as an external runtime dependency.
    ИзмененоREADMEReplace the logo with the optimized noodle mascot.
    ИзмененоPlaygroundUse real self-hosted videos in the block-states gallery.
  49. v0.16.0Минорный16 июн. 2026 г.

    FileNew File block tool. Tabbed empty state with upload (validation, progress bar, cancel), URL and drag-and-drop; per-type icon and tint; editable filename; consumer upload endpoints and download card. Rich preview modal dispatched by kind: PDF (top-layer modal with open-in-new-tab), Office (docx/xlsx/pptx via lazy renderers, xlsx parsed through JSZip), and text/code/markdown — including advanced markdown (math, footnotes, references, alerts, anchors, safe block-level raw HTML) with an animated Rendered ⇄ Raw toggle. Read-only support, i18n in every locale, and Storybook stories.

    ДобавленоFileNew File block tool. Tabbed empty state with upload (validation, progress bar, cancel), URL and drag-and-drop; per-type icon and tint; editable filename; consumer upload endpoints and download card. Rich preview modal dispatched by kind: PDF (top-layer modal with open-in-new-tab), Office (docx/xlsx/pptx via lazy renderers, xlsx parsed through JSZip), and text/code/markdown — including advanced markdown (math, footnotes, references, alerts, anchors, safe block-level raw HTML) with an animated Rendered ⇄ Raw toggle. Read-only support, i18n in every locale, and Storybook stories.
    ДобавленоEmbedGeneric embed: frame arbitrary URLs through a gated resolver, offered in the paste menu behind the linkPaste.allowGenericEmbed flag with an api.config accessor. Replace the source via an empty-state URL bar and an overlay more-menu item.
    ДобавленоMigrationComplete Editor.js block-type coverage plus a drop-in UMD build; adapt legacy Editor.js inline tools and linkTool data.
    ДобавленоPlaygroundBlock-states selector as a fixed left side menu; real docx/xlsx/pptx, code and text samples for the File block; richer quarterly-budget sheet; File block wired into the editor demo.
    ИсправленоTableResolve merged-cell coordinate bugs; preserve merges on load; split overlapped merges on paste so no destination data is dropped; keep empty cells editable on the read-only→edit toggle; harden input, clipboard and move-guard handling.
    ИсправленоFileUnbreak pptx preview; vertically center the preview modal; stop wrong-colour strips and toggle flicker during preview transitions; block javascript: URLs in download hrefs.
    ИсправленоBuildMake the published install self-contained (ship src, keep markdown and nanoid as runtime deps) so bundlephobia can build; green the self-contained-install and css-token audits.
    ИзмененоRefactorExtract table visual-subsystem orchestration into TableSubsystems; split BlockOperations into focused worker classes; share the media uploader empty state across the image and file tools.
    ИзмененоCodeCover every Prism token in both the light and dark themes.
    ИзмененоLintResolve all lint errors by root cause and mute advisory-only rules.
    ИзмененоDocsRefresh the README tool list and entry points; add a File block tool reference entry.
  50. v0.15.1Патч13 июн. 2026 г.
    ИсправленоTypesDeclare Embed and Bookmark (and their defaultBlockTools entries) in the published @jackuait/blok/tools types. The runtime exported them in 0.15.0 but the .d.ts did not, so import { Embed, Bookmark } failed to typecheck.
  51. v0.15.0Минорный12 июн. 2026 г.

    Link PastePasting a URL now offers a Notion-style menu to keep it as a link, or convert it into a Bookmark card or rich Embed block. The pasted link shows immediately with the menu anchored at its end, and menu labels name the detected link type via provider metadata.

    ДобавленоLink PastePasting a URL now offers a Notion-style menu to keep it as a link, or convert it into a Bookmark card or rich Embed block. The pasted link shows immediately with the menu anchored at its end, and menu labels name the detected link type via provider metadata.
    ДобавленоEmbedWorldwide embed registry covering ~115 services across video, audio, social, documents, design and developer domains, including Google published docs/forms and draw.io. Per-source minimum resize widths keep each provider's iframe legible, and fixed-width providers hug their content with figure, handles and toolbar.
    ДобавленоBookmarkNotion-parity bookmark card with a dev unfurl endpoint; crawler-UA retry recovers metadata from bot-blocked sites.
    ДобавленоPlaygroundSmooth cross-fade theme switching via View Transitions, plus an Airbnb-style neutral redesign.
    ИсправленоEmbedPreserve the live iframe across every editor action: caption and alignment toggles now apply in place instead of reloading the player. Selection highlight hugs the figure dimensions.
    ИсправленоDrag & DropFLIP-animate the column drop moment and slim the vertical drop bar to read like the horizontal line.
    ИсправленоSecurityHarden URL-scheme filtering and neutralize XSS gaps in markdown paste, the inline link tool, and the paste/render pipeline.
    ИсправленоToolsImplement setReadOnly on embed, bookmark and column tools so read-only toggles in place without a full re-render.
    ИсправленоIconsUnify the icon set on the 20×20 / 1.25 house spec; refine heading family, quote, caption, pencil, cells, toggles, and numbered-list glyphs.
    ИсправленоCIPin Node 24.14.1 to dodge a Playwright install hang; share Playwright setup to stop Storybook browser install hanging; repair 6 failing CI specs (5 stale expectations, 1 real drop-indicator regression).
    ИзмененоDependenciesResolve all 52 open Dependabot alerts; bump brace-expansion, ws, smol-toml.
    ИзмененоTestsEmbed/bookmark/link stories with screenshot baselines, wave-2 embed visual-regression baselines, verified real sample URLs replacing fixtures, refreshed main.css golden snapshot.
    ИзмененоDocs & PlaygroundEmbed, bookmark and link entries in tools data and the editor demo.
  52. v0.14.1Патч8 июн. 2026 г.
    ДобавленоToolsRegister the Columns tool with a single Columns group key. Tool-group "provides" manifests expand into their underlying block tools during prepare(), so consumers add one key instead of wiring each block.
    ИсправленоToolsExport Columns from public types for single-key registration; keep defaultBlockTools settings-only so the group key forwards settings without re-registering.
  53. v0.14.0Минорный5 июн. 2026 г.

    ColumnsNew side-by-side layout tool (#67). Create 2–5 column presets from the toolbox, or drag a block beside another to spawn a column. Drop anywhere left/right of a block to make a new column, or into a column body to stack inside it. Columns nest, auto-unwrap when emptied, and stack vertically on narrow viewports.

    ДобавленоColumnsNew side-by-side layout tool (#67). Create 2–5 column presets from the toolbox, or drag a block beside another to spawn a column. Drop anywhere left/right of a block to make a new column, or into a column body to stack inside it. Columns nest, auto-unwrap when emptied, and stack vertically on narrow viewports.
    ДобавленоColumns"Turn into columns" command wraps a multi-block selection into a column layout, available from the Convert-to menu.
    ДобавленоColumnsHover-revealed resize separators between columns: drag to resize, keyboard-resizable with ARIA slider semantics, double-click a divider to equalize widths.
    ДобавленоColumnsHorizontal arrow keys traverse between sibling columns; new columns animate in Notion-style.
    ДобавленоInline ToolbarAppears instantly on selection release, no animation delay.
    ИсправленоInline ToolbarRemoved entry animation that delayed appearance.
    Измененоi18nColumn resize aria-labels and turn-into-columns strings across all locales.
    ИзмененоCIRepair unit tests, e2e merge, and mirror push on master; mirror push works for both branch and tag events.
    ИзмененоTestsExhaustive block-in-column compatibility suite, live-drag lifecycle specs for every block type, multi-select block-settings header i18n regression.
    ИзмененоPlaygroundColumns example in the editor demo and block-states gallery.
  54. v0.13.2Патч30 мая 2026 г.
    ИсправленоRead-OnlyCollapse the empty bottom click-to-add zone to 0px in read-only mode and restore the configured min-height when editing
  55. v0.13.1Патч29 мая 2026 г.
    ДобавленоImageAuto-retry failed image loads with loading overlay; distinguish upload-failed vs broken-image error states; predict loading-placeholder dimensions from URL, SVG, and cache; pipe upload progress to bar (#41)
    ДобавленоCodemodDefault migrated images to size: 'full' and inherit the stretched flag into migrated image size
    ДобавленоBlock LinkHighlight pulse on hash-link arrival
    ДобавленоRead-OnlyShow copy-link menu on block hover
    ДобавленоPlaygroundAdd loading image state demo in block-states gallery
    ИсправленоImageForce full width and compact overlay for short images; reuse looping-arrows glyph for replace icon
    ИсправленоPasteKeep Google Docs images inside tables and stop double-bolding headings; prevent default page background collapsing to gray preset
    ИсправленоTableRecover migrated cell text detached by a pre-fix save
    ИсправленоToolbarAlign plus/drag handle with content lane for stretched blocks
    ИсправленоBlock SettingsAnchor popover to trigger instead of (0,0); translate popover context label
    ИсправленоDatabaseCenter block toolbar on the title line
    Измененоi18nTranslate strings identical to English across 25 locales
    ИзмененоLintResolve all ESLint and tsc problems
    ИзмененоImageMove inline upload-failed SVG to icons module
    ИзмененоTestsCover migrated cell content surviving load→save round-trip; fix CSS guard test failures from image loading shimmer
  56. v0.12.0Минорный22 апр. 2026 г.

    ImageNew image block tool (#66): drag-drop/URL/file upload, captions, alt text via inline popover, resize handles with symmetric growth, edge-pinned aspect-ratio resize, crop editor (rect/circle/oval) in modal, fullscreen lightbox with wheel/pinch zoom, drag-to-pan, rubber-band, alignment popover (left/center/right), block settings entries (size/download/copy-url), three-dots overflow menu for narrow images, empty/uploading/error states with unified card design, light-theme crop editor, legacy editor.js shape migration

    ДобавленоImageNew image block tool (#66): drag-drop/URL/file upload, captions, alt text via inline popover, resize handles with symmetric growth, edge-pinned aspect-ratio resize, crop editor (rect/circle/oval) in modal, fullscreen lightbox with wheel/pinch zoom, drag-to-pan, rubber-band, alignment popover (left/center/right), block settings entries (size/download/copy-url), three-dots overflow menu for narrow images, empty/uploading/error states with unified card design, light-theme crop editor, legacy editor.js shape migration
    ДобавленоCodeMigrate from Shiki to Prism.js for syntax highlighting with lazy grammar loading and class-based applier; add auto-indent and bracket expansion on Enter; add Mermaid highlighting with One Dark/Light palette; gutter line-number click focuses the line
    ДобавленоFontsBundle @fontsource fonts via generator script; new fontFamilySans/Serif/Mono/Handwriting config fields with CSS variable injection; font-display: swap for body text
    ДобавленоPopoverRender above all elements via CSS Top Layer; nested-submenu viewport clamping on both axes; close transition via ghost clone; tighter item sizing; end-of-list padding hidden on empty search; simpler animations
    ДобавленоToolbarHide plus and dots buttons while toolbox is open; place block settings popover left of the dots button
    ДобавленоToolboxNowrap pill with tighter radius and unified plus/slash search styling
    ДобавленоPlaygroundIcon gallery lightbox; block states gallery tab; settings panel shortcuts; hide header on scroll; logotype image example
    ДобавленоCSS VariablesTokenize radii, spacing, icon sizes, border widths, z-index ladder, duration/easing, typography; extract direct rgba literals to palette tokens; migrate @apply arbitrary hex values; split actions-icon/divider vars; add audit test and visual regression baselines
    ДобавленоDatabaseMatch Notion card shadow and radius on kanban cards; showcase all 10 column color variants
    ДобавленоBlock SettingsAdd shortcut keys to i18n with regression tests
    ДобавленоIconsMigrate inline SVGs to shared icon layer
    ИсправленоInline ToolbarTighten item padding and radius; suppress toolbar inside code blocks; apply symmetric top/bottom padding
    ИсправленоCodePin caret color so it does not inherit Prism token colors; restore trailing <br> after highlight so Enter works once; refresh gutter/highlight after native paste; focus line end when clicking empty strip of short lines; scope inline-code styling to not leak into code block; support contenteditable="plaintext-only" and preserve view mode on undo; correct syntax highlighting offset calculation
    ИсправленоToolbarReposition + / ⋮⋮ live while hovered block resizes; disable pointer-events on every actions descendant for left-edge blocks; keep slash search in inserted block after plus button
    ИсправленоPopoverDistinguish synthesized hover from real hover; hide context label while searching; keep block settings menu visible and attached to dots trigger
    ИсправленоBlock ManagerSkip cross-container auto-heal inside move group
    ИсправленоTooltipAnchor wrapper with position: fixed to survive page scroll; render above popover and survive UA stylesheet
    ИсправленоFontsAdd error handling for font load failures
    ИзмененоStylesSplit main.css into 11 concern-files
    ИзмененоLicenseAdd fork attribution and NOTICE file
    ИзмененоBuildReplace shiki with prismjs
    ИзмененоTestsFix 60+ unit + E2E failures across the suite; add Prism integration test for all highlightable languages
    ИзмененоChoreUntrack .vscode; remove stale root files; add favicon to dev playground; drop .editorconfig
  57. v0.11.1Патч16 апр. 2026 г.
    ДобавленоBundlesShip CJS (require()) and IIFE (<script> tag / CDN) bundles alongside ESM; add "main", "browser", "unpkg", and "jsdelivr" fields to package.json
    ИзмененоREADMEAdd installation section documenting ESM, CJS, and CDN usage
  58. v0.10.9Патч14 апр. 2026 г.
    ДобавленоToggleGray arrow icon when toggle body is empty
    ИсправленоDragEliminate "wrong block dropped" with multi-layer stale-block defense; block paste, undo/redo, and move shortcuts during active drag; integrate drag-reparent with undo as a single step
    ИсправленоHierarchyReject dangling parentId at universal chokepoint; reconcile remote Yjs reparents; close remaining container drift vectors; exempt Yjs remote sync from dangling parent throw
    ИсправленоPasteInherit container parent on replace-insert and x-blok root paste; harden container paste ejection across all container block types
    ИсправленоCalloutRestore plus button and drag handle; stop paste from ejecting children via stale contentIds; prevent Enter from inserting new block inside callout
    ИсправленоUndoCollapse multi-block paste and alt-drag duplicate into one undo group; eliminate spurious entries from metadata-only writes
    ИсправленоToolbarKeep drag handle visible when editing inside table cell
    ИсправленоInsertUniversally protect all Enter paths from nested-block leak
    ИсправленоTableTighten list item spacing inside table cells
    ИсправленоYjsMap 'no-capture' origin to local to prevent mid-op sync clobbering tool state
    ИзмененоYjsMake DocumentStore.ydoc private; enforce local origin whitelist with exhaustive mapper
    ИзмененоCIShard E2E tests via reusable workflow; add merge-reports job; run spec-file coverage validator on every PR; remove size-limit bundle size check
  59. v0.10.8Патч13 апр. 2026 г.
    ИсправленоTableNormalize flat-array table child parents at every entry point
    ИсправленоData ModelRecursively expand legacy nested toggleList/callout bodies
  60. v0.10.7Патч13 апр. 2026 г.
    ИсправленоThemePrevent nested editor instances from overriding parent theme on prepare
  61. v0.10.6Патч12 апр. 2026 г.
    ДобавленоConfigReplace user.name with user.id + resolveUser callback for multi-editor identity tracking
    ДобавленоKeyboardDel shortcut for block delete; markdown shortcuts for quote (" + space) and code (` ` ` + space) blocks
    Исправленоi18nUse Blok locale for date formatting with full month names; strip trailing abbreviation suffixes for ru/uk locales
    ИсправленоPopoverDisplay scroll haze instantly on open instead of fading in
    ИзмененоDepsAdd lodash-es resolution to pin ^4.18.0
  62. v0.10.5Патч11 апр. 2026 г.
    ДобавленоDatabaseDatabaseView rendering layer with kanban board DOM
    Исправленоi18nLocalize hardcoded "Last edited" strings in block settings footer; add missing translations across 67 locales
    ИсправленоThemeExpose theme API before isReady to prevent dark theme race condition
    ИзмененоLintResolve all 226 lint issues across source and test files
    ИзмененоCIEnable Corepack before setup-node to resolve Yarn version mismatch; remove dead version-check job
    ИзмененоTestsResolve 119 failing tests across E2E, unit, and docs suites
  63. v0.10.4Патч11 апр. 2026 г.
    ДобавленоDatabaseKanban board view with drag-and-drop cards and columns, card drawer with nested editor, inline title editing, list view with collapsible sections, multi-view tabs with drag reorder, property-based data model, column controls, backend sync, and read-only mode
    ДобавленоCopy block linkCopyLinkTune block tune with Cmd+Ctrl+L shortcut and automatic scroll-to-block on URL hash load
    ДобавленоBlock edit metadataTrack lastEditedAt/lastEditedBy on every block mutation with Yjs sync, saved output inclusion, and block settings footer display; new user config option
    ДобавленоPopoverScroll haze indicators on popover lists
    ДобавленоShortcut keysRender shortcut keys as SVG icons with readable tooltip on hover
    ИсправленоInline toolsPreserve trailing nbsp through format/unformat cycles; preserve trailing spaces when applying inline formatting; extend trailing-whitespace range detection; unwrap whitespace-only bold ancestors when un-bolding partial selection
    ИсправленоBlockDefault lastEditedAt to Date.now() so footer always shows; preserve user-provided block IDs and deduplicate on render; validate block ID format in constructor
    ИсправленоPastePrevent new table block when pasting into table cell with lost focus; handle hsl/hsla color formats
    ИсправленоScroll to blockGuard decodeURIComponent against malformed URL hash; encode block ID in URL hash
    ИсправленоThemePrevent nested editor from resetting parent theme
  64. v0.10.3Патч9 апр. 2026 г.
    ИсправленоTableGuard addBlockToCell and setCellBlocks against writing into covered (merged) cells; resolve paste target and copy source coordinates from model attributes instead of DOM visual position; fix overlay/pill missing and wrong merge/split button after rect expansion; expand selection rect to include full spans of merged cells; use logical cell coordinates in getCellPosition; don't intercept copy/cut when user has text selected in a single cell; reindexCoordinates assigns model coordinates instead of DOM physical indices; center row grip on merged cell using getBoundingClientRect
    ИсправленоToolbarCorrect marginLeft for nested blocks and popover position when scrolled; restore focus to originally-typed block after plus+Escape
    ИсправленоToolboxPosition popover at caret when inside nested blocks (toggle/callout)
    ИсправленоKeyboardPrevent text-jumping by preserving focus on toolbar interactions
  65. v0.10.0Минорный8 апр. 2026 г.

    Code blockNew CodeTool with syntax highlighting (via Shiki), line numbers toggle, language selector popover, copy button, wrap toggle, and preview tab for KaTeX/Mermaid rendering

    ДобавленоCode blockNew CodeTool with syntax highlighting (via Shiki), line numbers toggle, language selector popover, copy button, wrap toggle, and preview tab for KaTeX/Mermaid rendering
    ДобавленоInline codeNew InlineCodeTool with CMD+E shortcut
    ДобавленоQuote blockNotion-style quote block with size options submenu
    ДобавленоDivider blockHorizontal rule block with --- markdown shortcut
    ДобавленоCallout blockCallout block with emoji picker and skin tone persistence
    ДобавленоToggle listCollapsible toggle list block with drag & drop support inside toggles ([#46](https://github.com/JackUait/blok/pull/46), [#52](https://github.com/JackUait/blok/pull/52))
    ДобавленоToggle headingsToggle heading blocks with markdown shortcuts (>#, >##, >###) and body placeholder
    ДобавленоMarker inline toolColor text/background inline tool with color picker and dark mode support
    ДобавленоUnderline & StrikethroughNew inline tools with CMD+U and CMD+SHIFT+S shortcuts
    ДобавленоTable enhancementsCell color picker, cell placement picker, HTML <table> rendering, corner drag, Tab/Arrow escape from cells, and cross-table block protection ([#38](https://github.com/JackUait/blok/pull/38), [#45](https://github.com/JackUait/blok/pull/45), [#63](https://github.com/JackUait/blok/pull/63))
    ДобавленоMarkdown importimportMarkdown() API method and paste handler with GFM support including math (KaTeX) extensions
    ДобавленоReact adapteruseBlok hook and BlokContent component for React integration
    ДобавленоRead-only toggleSeamless in-place readonly mode toggle with scroll position preservation
    ДобавленоEditor width APIeditor.width namespace with WidthManager module and config.width options
    ДобавленоContent alignmentconfig.style.contentAlign option for global block content alignment
    ДобавленоFont family configconfig.style.fontFamily option for editor and popover typography
    ДобавленоTheme APIThemeAPI module for programmatic dark/light theme control
    ДобавленоFuzzy toolbox searchRanked fuzzy search in slash menu with animated filtering
    ДобавленоToolbox plus buttonOpens blocks menu directly without inserting /
    ДобавленоLink suggestion chipURL type detection chip in inline toolbar
    ДобавленоGoogle Docs pasteExpand <details> tags into toggle blocks with parent-child wiring
    Добавленоblok-cli packageNew @jackuait/blok-cli package with convert (HTML→JSON) and convert-gdocs commands
    Добавленоi18n search termsMultilingual toolbox search via searchTermKeys across all 68 locales
    ИсправленоDrag & dropToggle hierarchy, ghost preview, subtree depth preservation, and spring-load auto-expand for closed toggles
    ИсправленоToolbarDrag handle reachability, left-edge overflow, and actions not intercepting toggle arrow clicks
    ИсправленоInline toolbarCross-block selection positioning, background element cleanup on close
    ИсправленоTableCross-table block stealing, undo/redo focus, cell selection border persistence, and arrow key navigation between blocks
    ИсправленоMarkerPartial selection color removal, dark theme palette, and active color display on toolbar button
    ИсправленоToggleBackspace/Delete boundary crossing, undo atomicity for Enter, children DOM nesting, and collapse in read-only mode
    ИсправленоListTab indent for multi-selected items, depth reduction cascade on outdent, and bullet marker pinning
    ИсправленоPasteTable cell content appearing outside table, marker formatting preservation, and math formula detection
  66. v0.5.0Минорный23 янв. 2026 г.

    CRDT-based undo/redoThe undo/redo system now uses Conflict-Free Replicated Data Type principles for better conflict resolution and history tracking

    ДобавленоCRDT-based undo/redoThe undo/redo system now uses Conflict-Free Replicated Data Type principles for better conflict resolution and history tracking
    Исправленоtoolbar hover behavior after cross-block selectionThe inline toolbar now resets its positioning state when extending selections across multiple blocks
    ИсправленоPatternPasteEvent for internal cut/pasteInternal cut and paste operations now emit PatternPasteEvent, so external code can react to all clipboard actions
  67. v0.4.1-beta.5Патч7 дек. 2025 г.
    ИсправленоTailwind CSS conflictsFixed CSS conflicts that caused external plugins to break by isolating Tailwind's style precedence
    Добавленоdata-blok-header-level attributeHeaders in the formatting popover now include a data-blok-header-level attribute for styling and testing hooks
  68. v0.4.1-beta.3Патч6 дек. 2025 г.
    Добавленоundo/redoAdded keyboard shortcuts (Ctrl/Cmd+Z and Ctrl/Cmd+Shift+Z) for editing history navigation
  69. v0.4.1-beta.0Патч16 дек. 2025 г.
    ДобавленоRTL language supportAdded translations for Hebrew, Persian, Urdu, Yiddish, Pashto, Sindhi, Uyghur, Kurdish, and Dhivehi with right-to-left layout
    ДобавленоEastern European languagesAdded Czech, Romanian, and Hungarian translations
    ДобавленоSoutheast Asian languagesAdded Thai, Ukrainian, and Greek translations
    ДобавленоSouth Asian languagesAdded Hindi, Bengali, Indonesian, and Vietnamese translations
    ДобавленоTurkic languagesAdded Turkish and Azerbaijani translations
    ДобавленоArabicAdded Arabic translation with RTL support
    ДобавленоNorthern European languagesAdded Dutch, Polish, and Swedish translations
    ДобавленоKorean, Japanese, Italian, Portuguese, German, French, SpanishAdded translations
    ДобавленоArmenian, Chinese, RussianAdded translations
    Добавленоrename checklist to to-do listChanged terminology from "checklist" to "to-do list"
    Добавленоdrag & dropRewrote the drag and drop system for smoother interactions
    Добавленоflat data modelChanged from nested to flat structure using parentId and contentIds references
    Добавленоlists: flat data modelList items now use the flat data structure
    Добавленоkeyboard navigationAdded keyboard shortcuts for editing without the mouse
    Добавленоlist toolsAdded numbered lists, ordered (nested) lists, and to-do lists with checkboxes
    Добавленоparagraph tool: custom configurationThe paragraph tool supports custom configuration for placeholder text and styling
    Добавленоheader tool: custom configurationThe header tool supports custom configuration for levels and placeholder text
    Добавленоnavigation modeAdded arrow key navigation through blocks, separate from text editing
    ДобавленоUX improvementsFocus management, cursor positioning, and block interactions
    Исправленоtranslation keys: camelCaseConverted all translation keys to camelCase
    Исправленоtranslation key parsingFixed nested translation key parsing
    Исправленоremove redundant translation keysCleaned up duplicate and unused translation keys
    Исправленоfix Russian translationCorrected a missing word in the Russian translation
    Исправленоfake selection displayFixed how fake (visual-only) selections render
    Исправленоclose inline toolbar on outside clickThe inline toolbar now closes when clicking outside the editor
    Исправленоtoolbar centeringFixed toolbar positioning to stay centered regardless of content width
  70. v0.3.1-beta.0Патч3 дек. 2025 г.
    Измененоcodemod improvementsBetter pattern matching and safer transformations for the Editor.js migration
  71. v0.3.0Минорный2 дек. 2025 г.

    bundle paragraph and header toolsThese tools are now included by default in the core bundle

    Добавленоbundle paragraph and header toolsThese tools are now included by default in the core bundle
  72. v0.2.0Минорный2 дек. 2025 г.

    drag & dropBlock reordering via the block handle (☰) icon

    Добавленоdrag & dropBlock reordering via the block handle (☰) icon
    Исправленоremove debug loggingCleaned up console.log statements and resolved performance bottlenecks
    Измененоrebrand to BlokUpdated logos, color schemes, and documentation
  73. v0.1.0Минорный24 нояб. 2025 г.

    fork from Editor.jsBlok forked from Editor.js, preserving the block-based editing architecture

    Измененоfork from Editor.jsBlok forked from Editor.js, preserving the block-based editing architecture
    Измененоinitial feature setBlock management, inline formatting, slash toolbox, and plugin system