diff --git a/src/App.svelte b/src/App.svelte index 6df34f2..44fd3bb 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -56,6 +56,7 @@ import DocPropertiesDialog from './lib/components/DocPropertiesDialog.svelte'; import CommentsPane from './lib/components/CommentsPane.svelte'; import RevisionsPane from './lib/components/RevisionsPane.svelte'; + import ConnectorLayer from './lib/components/ConnectorLayer.svelte'; import NavigatorPane from './lib/components/NavigatorPane.svelte'; import { OPEN_COMMENT_EVENT } from './lib/editor/extensions/comment'; import AutoCorrectDialog from './lib/components/AutoCorrectDialog.svelte'; @@ -1511,6 +1512,9 @@ {#if revisionsOpen} (revisionsOpen = false)} /> {/if} + {#if commentsOpen || revisionsOpen} + + {/if} {#if findOpen && editor}
@@ -1602,6 +1606,8 @@ display: flex; flex: 1; min-height: 0; + /* The origin ConnectorLayer draws in — it spans the scroller and the panes. */ + position: relative; } /* Find & Replace bar: floats at the top-right of the editing area, just under the diff --git a/src/lib/components/CLAUDE.md b/src/lib/components/CLAUDE.md index 6a39e3b..cf4f753 100644 --- a/src/lib/components/CLAUDE.md +++ b/src/lib/components/CLAUDE.md @@ -138,7 +138,9 @@ pane beside it (`.editor-row` in `App.svelte`) keeps the sheet at its true width `comments(editor.state.doc)` in document order — click a card to select the annotated range, edit / resolve / remove in place. `App.svelte` owns the New-comment prompt, which both the Review tab and the context menu (`OPEN_COMMENT_EVENT`) fire; the author comes -from the document properties. +from the document properties. The card of the comment the selection lies within (`commentIdAt`) +is marked and scrolled into view, the pane half of the pairing `pm-comment-active`, +`ChangeBarLayer` and `ConnectorLayer` draw on the text. ## Revisions (`RevisionsPane.svelte`) @@ -146,6 +148,8 @@ The reviewing pane both word processors list revisions in, docked beside `Commen and built the same way: one row per change **id** (a paragraph boundary splits one change into several ranges), in the author's own colour, click to select the text, accept or reject in place through `acceptRevision`/`rejectRevision`. Opened from the Review tab. +The row of the change the selection lies within (`revisionIdAt`) is marked with an inset accent in the +author's colour — the same colour its margin bar takes — and scrolled into view. ## AutoText (`AutoTextDialog.svelte`, `editor/extensions/autoText.ts`) @@ -235,6 +239,30 @@ floating frame's band is no line of its anchor paragraph. A line past the page s skipped, or its number would print into the page gap. Re-measured on each edit and each pagination settle — the whole document each time, which is the ceiling noted in the file. +## Change bars (`ChangeBarLayer.svelte`) + +The changed-lines bar both word processors draw in the margin: a stroke beside every line +a recorded change or an unresolved comment covers, in the revision author's colour or the +comment amber, so a pane entry can be found in the text. Each range's ends give it +(`coordsAtPos`), clipped to every page's text area — a range crossing a page break strokes +once per page instead of through the gap. Inside the scaled `.paper` like the other +layers, and inside the line numbers, which sit further out. The change or comment at the +caret is drawn heavier and last, or a neighbour on the same line would cover it. Always +mounted: with nothing marked it renders nothing. + +## Reviewing leader (`ConnectorLayer.svelte`) + +The dashed leader both word processors draw from an annotated range to its balloon, here +from the text out to the reviewing pane's card: a stub along the anchor's line, then a +diagonal onto the card's head. Only for the comment and the change **at the caret** — a +line per comment would cross the whole page, and one is what pairs card and text. It sits +in `.editor-row` (hence that row's `position: relative`), the one box spanning the +scroller and the panes both, and reads the active card straight off the DOM +(`li.active`), so nothing has to be threaded through `App.svelte`. Redrawn on every +scroller (a capturing `scroll` listener), on resize and on each edit; dropped when the +anchor scrolls out of the page view, and when a *second* pane is docked between the text +and the target card — the card is still marked and the margin bar still names the line. + ## Debug tooling (dev only) In dev builds a **Debug** button (`App.svelte`) downloads a JSON snapshot combining `getPageBreakDebug(view)` (leaves, placements, rendered spacers, table-break bands, live overlay geometry), `getTableCellDebug(view)` (per cell: the `verticalAlign` attr beside the computed value, cell/row height, the gap above and below the content, and every block's margins/`--space-before` — cell alignment reads as broken whenever the content fills the box, so the numbers that decide that travel with it), `getFrameDebug(view)` (`caption.ts` — per picture: its attrs, its rendered span in the text column and its caption's; every other section measures the flow downwards, and a misplaced caption is a sideways question), the style sheet + spacing model, and `getColorDebug(editor)` (selection marks, text runs, document colors, DOM spans, and `painted`: the colour the browser actually computes per rendered run, the element that declares it, the page's colour variables and the document stylesheet's colour rules — a mark says what was asked for, and a decoration paints a colour no mark carries). diff --git a/src/lib/components/ChangeBarLayer.svelte b/src/lib/components/ChangeBarLayer.svelte new file mode 100644 index 0000000..6fd9b64 --- /dev/null +++ b/src/lib/components/ChangeBarLayer.svelte @@ -0,0 +1,142 @@ + + + + + diff --git a/src/lib/components/CommentsPane.svelte b/src/lib/components/CommentsPane.svelte index 4cc6e7d..28c3df8 100644 --- a/src/lib/components/CommentsPane.svelte +++ b/src/lib/components/CommentsPane.svelte @@ -1,7 +1,7 @@ + + + + diff --git a/src/lib/components/Editor.svelte b/src/lib/components/Editor.svelte index 1b368b8..56b2764 100644 --- a/src/lib/components/Editor.svelte +++ b/src/lib/components/Editor.svelte @@ -27,6 +27,7 @@ import PageDecorLayer from './PageDecorLayer.svelte'; import PageSheetLayer from './PageSheetLayer.svelte'; import LineNumberLayer from './LineNumberLayer.svelte'; import FoldMarkLayer from './FoldMarkLayer.svelte'; +import ChangeBarLayer from './ChangeBarLayer.svelte'; import { DEFAULT_LINE_NUMBERING, type LineNumbering } from '../storage/lineNumbering'; import { EMPTY_PAGE_DECOR, type PageDecor } from '../storage/pageDecor'; import Ruler from './Ruler.svelte'; @@ -1477,6 +1478,7 @@ import { EMPTY_PAGE_DECOR, type PageDecor } from '../storage/pageDecor'; {#if lineNumbering.on} {/if} + import type { Editor } from '@tiptap/core'; import { TextSelection } from '@tiptap/pm/state'; - import { revisions, authorColorIndex, REVISION_AUTHOR_COLORS, type Revision } from '../editor/extensions/trackChanges'; + import { revisions, revisionIdAt, authorColorIndex, REVISION_AUTHOR_COLORS, type Revision } from '../editor/extensions/trackChanges'; import { t } from '../i18n/i18n.svelte'; // The reviewing pane both word processors list revisions in, beside the comments one: @@ -22,6 +22,18 @@ let colors = $derived(authorColorIndex(list)); + // The change the caret sits in: its card is marked and pulled into view, so the + // margin bar beside the text and the row here name each other. + let activeId = $derived.by(() => { + if (tick < 0 || !editor) return null; + return revisionIdAt(editor.state); + }); + + let cards: Record = {}; + $effect(() => { + if (activeId) cards[activeId]?.scrollIntoView({ block: 'nearest' }); + }); + function colorOf(r: Revision): string { return REVISION_AUTHOR_COLORS[colors.get(r.author) ?? 0]; } @@ -55,7 +67,7 @@
    {#each list as r (r.id)} -
  • +