diff --git a/CHANGELOG.md b/CHANGELOG.md index 62c10b17..c7003b7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,8 +14,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **Place several images side by side in a row.** A new "Inline (side by side)" alignment mode in the image bubble menu renders consecutive inline - images as a row that wraps onto the next line on narrow screens. Unlike the - float modes, text does not wrap around inline images. The mode round-trips + images as a row that wraps onto the next line on narrow screens. The row is + centered horizontally by default in modern browsers (CSS `:has()`), falling + back to start-aligned rows in browsers without support. Unlike the float + modes, text does not wrap around inline images. The mode round-trips losslessly through markdown as `data-align`, like the other alignment values. @@ -84,6 +86,53 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 with the `||text||` input rule; the rendered span blurs until clicked to reveal. The mark is preserved losslessly through Markdown export/import (as a raw ``) and on public shares. (#259) +- **Dock the AI chat window into the side menu.** The floating chat window can + be pinned to the sidebar — drag it onto the navbar (a drop-zone highlight + shows where it lands) or use the new "Dock to sidebar" header button; while + docked it fills the sidebar area and follows its live size. "Undock" (or + dragging it back out) restores the floating window, a collapsed/absent + sidebar falls back to floating, and the docked state survives a reload. + (#276, #282) +- **Hovering commented text shows the comment thread in a tooltip.** Pointing + at a highlighted comment mark pops a small card with the author and plain + text of the root comment and its replies, so a thread can be skimmed without + opening the side panel. The card appears after a short delay (no flicker on a + passing glance), skips resolved and text-less threads, and dismisses on + scroll or click — clicking a mark still opens the comments panel. (#268, + #271) +- **"Move to trash" button in the temporary-note banner.** Besides "Make + permanent", the banner on an open temporary note now also offers to trash the + note immediately instead of waiting out its lifetime. It reuses the regular + soft-delete path, so the "Page moved to trash" undo toast is the safety net — + no confirmation dialog. (#273, #277) +- **Code-block controls float as an overlay instead of taking a row above the + code.** The language selector and copy button now sit in the block's top-right + corner, and the selector stays invisible until the block is hovered or the + selector is focused, so reading code is chrome-free. In read-only views only + the copy button renders. (#275, #278) +- **The AI agent is told about your page edits between turns.** The server + snapshots the open page's Markdown at the end of every agent turn and, on the + next turn, injects a unified diff of what changed in between, so the agent + knows its earlier copy of the page is stale and builds on the user's edits + instead of reverting or overwriting them. The diff is whitespace-normalized + (pure formatting churn injects nothing) and size-capped, with a hint to + re-read the full page via `getPage` when truncated. (#274, #281) +- **Stress-accent button (U+0301) in the bubble menu.** Select a vowel and + toggle a combining acute accent over it — a Russian-style stress mark. The + accent is stored as plain text (no custom mark), so it survives Markdown/HTML + export, full-text search and public shares unchanged; the toggle is a single + undo step and re-clicking removes the accent. (#270, #280) +- **Reading position survives a reload.** The editor remembers how far you + scrolled in each page (per tab, in `sessionStorage`) and restores that + position after an F5 or reopening the document, waiting for the collaborative + content to finish laying out first. A URL `#hash` anchor still wins — restore + is a no-op then. (#266, #267) +- **The slash menu finds commands typed in the wrong keyboard layout.** A query + typed with the wrong layout active (e.g. `/сщву` for `/code`, or `/cyjcrf` + for the Cyrillic «сноска» → Footnote) is additionally remapped ЙЦУКЕН↔QWERTY + by physical key position and matched against the commands; genuine Cyrillic + search terms keep priority over remapped candidates, and short wrong-layout + prefixes match by command title. (#283, #285, #287) ### Changed @@ -149,6 +198,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 emits a single-use "intentional clear" signal that lets exactly that one empty write through the guard, so genuinely emptying a page is persisted while accidental empties are blocked. (#248, #251) +- **Ctrl+Z works again right after using a table menu.** Closing a table + row/column menu (grip or chevron) left focus on the menu's portaled target + outside the editor, so undo keystrokes went nowhere until you clicked back + into a cell. The editor is now refocused after the menu closes — unless you + deliberately moved focus to another input or editable (e.g. the page title). + (#269, #279) +- **The AI reindex progress counter no longer freezes at 0.** Right after + "Reindex now" the client could read the stale pre-reindex snapshot of an + already-indexed workspace (`reindexing=false`, all pages counted) as + "finished" and stop polling on the very first tick, leaving the counter + frozen until a manual reload. Polling now keeps going until it has actually + observed the active run. (#262, #264) +- **An MCP edit can no longer be silently lost to a duplicate collab document.** + When the agent addressed a page by its short slugId, the MCP opened a + collaboration document named after that slugId while the web editor always + uses the page's canonical UUID — two independent live documents for one page, + whose debounced stores clobbered each other. The MCP now resolves every page + id to the canonical UUID before opening the collab doc (a UUID input + short-circuits locally; a slugId is resolved once and cached). (#260, #265) ### Security diff --git a/README.md b/README.md index 5f0357f3..d2b471de 100644 --- a/README.md +++ b/README.md @@ -104,7 +104,7 @@ community feature, with no enterprise license. Open it from the page header; the - ✅ **Page templates** — flag a page as a template and embed its whole content live into other pages; edits to the template propagate to every place it is inserted (whole-page transclusion on top of the existing synced blocks). - ✅ **Public-share AI assistant** — anonymous visitors of a shared page can ask the AI agent, scoped strictly to that share's page tree (read-only, share-scoped search), behind a workspace toggle. - ✅ **Footnotes** — academic-style footnotes: a numbered superscript reference inline (read it in place via a hover popover), with the note text living as a real, editable block at the bottom of the page; auto-numbered, collaboration-safe, and round-trips through Markdown export/import and the AI agent / MCP. -- ✅ **Temporary notes** — mark a note as temporary and it auto-moves to Trash after a configurable per-workspace lifetime (default 24h) unless made permanent first; create one in a click from the Home screen, any space overview, or the space sidebar, with a "Make permanent" rescue banner on the open note. +- ✅ **Temporary notes** — create a note as temporary and it auto-moves to Trash after a configurable per-workspace lifetime (default 24h) unless made permanent first; create one in a click from the Home screen, any space overview. ### In progress @@ -187,14 +187,17 @@ start the new migrations apply on top of your existing schema (`CREATE EXTENSION - Spaces - Permissions management - Groups -- Comments (with resolve / re-open) +- Comments (with resolve / re-open and hover tooltips showing the comment text) - Page history - Search - File attachments - Embeds (Airtable, Loom, Miro and more) - Translations (10+ languages) - Embedded MCP server (`/mcp`) -- AI agent chat over your wiki (read + write, RAG search, external MCP / web access) +- AI agent chat over your wiki (read + write, RAG search, external MCP / web access); the chat window docks into the side menu, and the agent is told about your in-page edits between turns +- Code-block buttons as an overlay, with the language selector revealed on hover +- Stress-accent button (U+0301) in the bubble menu +- Reading scroll position restored on reload ### Screenshots diff --git a/README.ru.md b/README.ru.md index 0e9becde..62552fce 100644 --- a/README.ru.md +++ b/README.ru.md @@ -105,7 +105,7 @@ real-time-коллаборации Docmost, поэтому запись нико - ✅ **Шаблоны страниц** — пометить страницу шаблоном и вставлять её содержимое живой ссылкой в другие страницы; правки шаблона распространяются на все места вставки (whole-page-транслюзия поверх существующих synced-блоков). - ✅ **AI-ассистент на публичных шарах** — анонимный зритель расшаренной страницы может спросить AI-агента, который ищет строго по дереву этой шары (read-only, share-scoped поиск), за тумблером воркспейса. - ✅ **Сноски** — сноски академического вида: нумерованная ссылка-надстрочник прямо в тексте (читается на месте во всплывающем окне по наведению), а текст сноски живёт реальным редактируемым блоком внизу страницы; авто-нумерация, безопасна для совместного редактирования, переживает экспорт/импорт Markdown и доступна AI-агенту / MCP. -- ✅ **Временные заметки** — пометьте заметку временной, и она автоматически уедет в корзину по истечении настраиваемого срока жизни воркспейса (по умолчанию 24 ч), если её предварительно не сделать постоянной; создать такую можно в один клик с домашнего экрана, с обзора любого пространства или из сайдбара пространства, а на открытой заметке есть баннер «Сделать постоянной». +- ✅ **Временные заметки** — создайте временную заметку, и она автоматически уедет в корзину по истечении настраиваемого срока жизни (по умолчанию 24 ч); создать такую можно в один клик с домашнего экрана, с обзора любого пространства или из сайдбара пространства. ### В процессе @@ -174,14 +174,18 @@ dump/restore, существующий каталог данных переис - Пространства (Spaces) - Управление правами доступа - Группы -- Комментарии (с резолвом / переоткрытием) +- Комментарии (с резолвом / переоткрытием и всплывающими подсказками с текстом комментария при наведении) - История страниц - Поиск - Вложения файлов - Встраивания (Airtable, Loom, Miro и другие) - Переводы (10+ языков) - Встроенный MCP-сервер (`/mcp`) -- Чат с AI-агентом по вики (чтение + запись, RAG-поиск, внешние MCP / доступ в интернет) +- Чат с AI-агентом по вики (чтение + запись, RAG-поиск, внешние MCP / доступ в интернет); окно чата закрепляется в боковом меню, а агент узнаёт о ваших правках страницы между ходами +- Кнопки код-блока оверлеем, селектор языка появляется при наведении +- Кнопка «Ударение» (U+0301) в bubble-меню +- Позиция чтения (прокрутка) восстанавливается после перезагрузки +- Slash-меню терпимо к неправильной раскладке (ЙЦУКЕН↔QWERTY) ### Скриншоты diff --git a/apps/client/src/features/editor/styles/media.css b/apps/client/src/features/editor/styles/media.css index dde79fa9..8afc3121 100644 --- a/apps/client/src/features/editor/styles/media.css +++ b/apps/client/src/features/editor/styles/media.css @@ -71,3 +71,22 @@ } } +/* Inline image rows (#284): center the anonymous line boxes formed by + consecutive [data-image-align="inline"] node-view containers. A row has no + DOM wrapper of its own, so its horizontal placement is controlled by the + text-align of the nearest block ancestor (the editor root or a nested + block container: blockquote, callout, list item, table cell, details). + Centering is enabled only in containers that actually hold an inline + image (:has), and every other child of such a container gets its default + alignment back so ordinary text is unaffected. Explicit per-block + alignment from the toolbar is an inline style and still wins. Browsers + without :has() degrade to left-pinned rows. */ +.ProseMirror:has(> [data-image-align="inline"]), +.ProseMirror :has(> [data-image-align="inline"]) { + text-align: center; +} + +.ProseMirror:has(> [data-image-align="inline"]) > :not([data-image-align="inline"]), +.ProseMirror :has(> [data-image-align="inline"]) > :not([data-image-align="inline"]) { + text-align: start; +} diff --git a/packages/editor-ext/src/lib/image/image.ts b/packages/editor-ext/src/lib/image/image.ts index 7e6e48ae..66f520ed 100644 --- a/packages/editor-ext/src/lib/image/image.ts +++ b/packages/editor-ext/src/lib/image/image.ts @@ -449,7 +449,9 @@ export function applyAlignment(container: HTMLElement, align: string) { // the next line when the viewport is narrow. The right/bottom padding // provides the gap between images in a row and between wrapped rows; // vertical-align: top keeps rows of different-height images aligned by - // their top edge. + // their top edge. Horizontal centering of the whole row is handled by the + // client stylesheet (media.css) via a :has() rule on the parent block + // container, since the row has no wrapper element of its own. container.style.display = "inline-block"; container.style.verticalAlign = "top"; container.style.padding = "0 10px 10px 0";