Skip to content

feat(Header): hero block extension - #1229

Draft
makhnatkin wants to merge 19 commits into
mainfrom
feat/header-extension
Draft

makhnatkin wants to merge 19 commits into
mainfrom
feat/header-extension

Conversation

@makhnatkin

@makhnatkin makhnatkin commented Sep 15, 2026

Copy link
Copy Markdown
Collaborator

Draft. The block is still taking shape: nothing here is final, and no rework is planned for now — please hold off on review. Visual snapshots have not been regenerated yet.

Header blocks

  • Added editable headers using directive syntax.
  • Added image uploads, layout settings and individual button colours.
  • Added drag-and-drop and keyboard action ordering.
  • Added a soft-shape pattern on the header fill.
  • Fixed popup dismissal when clicking inside settings.

makhnatkin and others added 6 commits September 16, 2026 01:08
The hero block is expressed as a `:::header` directive: the inline content
carries the title, the first paragraph becomes the subtitle and `::action`
leaves become call-to-action buttons. Anything else inside the container is
moved out after the block rather than dropped.

The node structure is fixed (`header_title header_subtitle header_actions`),
so no unpaired state exists and no repairing plugin is needed. Presentation
settings live in node attributes as token names, never raw colours, and every
value is normalised in a single place shared by the parser, parseDOM and the
upcoming toolbar commands.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The blob layer and the background image are pure functions of the node
attributes, so `toDOM` draws them and no node view is needed: re-rendering on
an attribute change, copy-paste and server-side rendering all come for free.
Image URLs are escaped before they reach `style`, and non-http schemes are
dropped.

Commands read the node from the document by position rather than from a
closure, so two attribute changes in a row no longer overwrite each other, and
a stale position from the toolbar returns false instead of throwing. Inserting
the block keeps the text of a non-empty paragraph, and backspace at the start
of the title unwraps the block back into paragraphs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The floating toolbar is built on the shared BaseTooltipPluginView, so
positioning, focus tracking and escape handling come from the editor rather
than from a hand-rolled popup. Every control is the same command that the
keyboard uses, called without dispatch for isEnable, so a greyed-out button and
a refused shortcut can never disagree.

Colours are a single SCSS map shared by the block and the palette swatches, on
semantic Gravity UI tokens: the document stores a token name, so it survives a
theme change without migrating any markup. Text contrast is part of that map
instead of luminance arithmetic over a hex value, and the scrim over a
background image is neutral rather than tinted with the fill, which is what
made the seam between the veil and the image visible in the mockup.

Uploading goes through the editor-wide FileUploadHandler, sets the background
and the URL in one transaction, and reports a failure instead of quietly
removing the progress indicator.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Fourteen stories cover the states that only show up visually: every fill in the
palette, all four border styles, curated and generated shapes, a background
image with light and dark text, an image beside the text, an empty image slot,
a host without an upload handler, and the block nested inside a cut.

The toolbar item lives in the extension rather than in the core toolbar config
and reads the action through an optional chain, so the same configuration works
whether or not the extension is plugged in.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Thirteen screenshots in both themes. Two defects only the pictures revealed:
the call-to-action rendered as plain link-coloured text because the editor's
own anchor styles outweighed a single-class selector, and its label took the
colour of the block fill, which is a background token and has no contrast
against the button. The label now uses an explicit inverse text token that the
fill map derives alongside the text colour.

The serializer also emitted a blank line after the subtitle even when no
buttons followed; a round-trip test now pins that down.

Storybook serves the same static assets as playwright, so the story and the
snapshot cannot drift apart.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The placeholder dictionary comes from BehaviorPreset, which may be plugged in
after Header, so reading it at registration time made the extension depend on
the order of use(). It is now read inside the node-spec callback, and the text
is a function rather than a literal, so it survives a language change.

Inner slots opt out of the slash menu declaratively, and the block carries a
data-qa attribute so visual tests do not have to target layout classes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gravity-ui

gravity-ui Bot commented Sep 15, 2026

Copy link
Copy Markdown

Storybook Deployed

@gravity-ui

gravity-ui Bot commented Sep 15, 2026

Copy link
Copy Markdown

🎭 Playwright Report

makhnatkin and others added 13 commits September 16, 2026 01:56
A document written by a newer version of the extension loses an attribute it
does not recognise on the next open-and-save. Preserving them needs somewhere
to keep foreign keys, which argues with keeping every setting a typed
attribute, so the decision is deferred rather than guessed at.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Палитра переехала с полупрозрачных `--g-color-base-*-light` на непрозрачные
`*-solid`-ступени: светлые семантические токены рассчитаны на подсветку строки
и на поверхности в треть экрана сливались в одинаково белёсый фон. Поскольку
`*-solid` сами меняются по теме, цвет текста задаётся ролью поверхности
(`normal`/`inverted`), а не статической парой цветов.

Декоративные узоры (`blobs`, `seed`) убраны вместе с генератором геометрии.

Картинка вместо полупрозрачной вуали растворяется в заливке маской, привязанной
к ширине текстовой колонки: вуаль оставляла вертикальный стык там, где фон
иллюстрации не совпадал с заливкой, а теперь текст всегда лежит на чистой
заливке. Демо-ассеты заменены на панораму и портрет вместо скриншота редактора.

Типографика и отступы переведены на миксины uikit и `--g-spacing-*`, кнопка CTA —
на токены бренда. В панели настроек выпадающие списки перестали светиться
постоянно (`alwaysActive`), сама панель уехала под блок и больше не перекрывает
липкий тулбар редактора.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Директива переименована в `:::header-block`, а её тело стало одним yaml-документом:
`title`, `description` и список `actions` с `type`/`title`/`href`. Оформление осталось
в атрибутах директивы — две разные сущности больше не перемешаны в одной строке.

Инлайн-контент не подходил: внутри блока форматирования нет вовсе, а markdown-разбор
всё равно превращал `**` и `[]()` в марки, которых схема не разрешает, и round-trip их
терял. Слоты приезжают в документ одним токеном с `noCloseToken`, поэтому текст попадает
в дерево ровно теми символами, которыми его написали.

В prosemirror слоты стали обычными текстовыми нодами (`content: 'text*'`, `marks: ''`):
ни жирного, ни курсива, ни ссылки в заголовке — ссылка бывает только у кнопки, в `href`.
Кнопка получила `type` (`button` / `link`) вместо `variant`, и тип теперь правится
в панели у кнопки под курсором.

Значения печатаются в кавычках всегда, читаются с `JSON_SCHEMA`: иначе двоеточие
в заголовке меняло бы структуру при следующем чтении, `yes` становился булевым,
а `2020-01-01` — датой. Сломанное тело даёт пустой блок, а не исключение.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Добавлен мягкий узор на цветной заливке обложки, он включён по умолчанию
- Яркость узора подстраивается под выбранный цвет и тему оформления
- В настройках вида появился переключатель узора, доступный только для цветного фона
- На узкой колонке узор не рисуется, чтобы не попадать под текст

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant