fix: the index's leader fills its own gap, at LibreOffice's indents
Every row carried 200 dots — clipped on screen, but the clipboard, the PDF and any measurement got all of them. Cut each to what its gap holds, and step the levels by 0.5cm, which is what LibreOffice's Contents styles use. The parity runner now waits for the page numbers to settle too.
This commit is contained in:
1 parent
8690129c83
commit
c26aac1bee
3 files changed
+28
-6
No files matched your search
@@ -14,8 +14,10 @@ export type TocEntry = { text: string; level: number; page: number };
|
||||
// ("Inhalt", "Sommaire", …), or none at all where the file put its heading in a
|
||||
// separate paragraph (`''`); a freshly inserted one takes this.
|
||||
const TITLE = 'Table of Contents';
|
||||
// Enough leader dots to cross the widest gap a page can offer; the row clips the rest.
|
||||
// Enough leader dots to cross the widest gap a page can offer; fillLeaders cuts each
|
||||
// row's back to what its own gap holds, measuring one dot with this sample.
|
||||
const LEADER_DOTS = 200;
|
||||
const LEADER_PROBE = '..........';
|
||||
|
||||
declare module '@tiptap/core' {
|
||||
interface Commands<ReturnType> {
|
||||
@@ -235,6 +237,22 @@ class TocView {
|
||||
}
|
||||
this.dom.appendChild(row);
|
||||
});
|
||||
this.fillLeaders();
|
||||
}
|
||||
|
||||
// As many leader dots as the gap holds. The row clips the rest on screen, but nothing
|
||||
// else does: 200 of them reach the PDF, the clipboard and every measurement as text.
|
||||
private fillLeaders(): void {
|
||||
const leaders = Array.from(this.dom.querySelectorAll<HTMLElement>('.toc-leader'));
|
||||
if (!leaders.length) return;
|
||||
leaders[0].textContent = LEADER_PROBE;
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(leaders[0]);
|
||||
const dot = range.getBoundingClientRect().width / LEADER_PROBE.length;
|
||||
if (!(dot > 0)) return;
|
||||
for (const el of leaders) {
|
||||
el.textContent = '.'.repeat(Math.max(0, Math.floor(el.getBoundingClientRect().width / dot)));
|
||||
}
|
||||
}
|
||||
|
||||
// Scroll the heading into view and drop the cursor into it.
|
||||
|
||||
@@ -219,10 +219,11 @@
|
||||
.paper .tiptap .toc-entry:hover .toc-text {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.paper .tiptap .toc-level-2 { padding-left: 0.6cm; }
|
||||
.paper .tiptap .toc-level-3 { padding-left: 1.2cm; }
|
||||
.paper .tiptap .toc-level-4 { padding-left: 1.8cm; }
|
||||
.paper .tiptap .toc-level-5 { padding-left: 2.4cm; }
|
||||
/* One level = 0.5cm, the indent LibreOffice's own Contents 1…5 styles carry. */
|
||||
.paper .tiptap .toc-level-2 { padding-left: 0.5cm; }
|
||||
.paper .tiptap .toc-level-3 { padding-left: 1cm; }
|
||||
.paper .tiptap .toc-level-4 { padding-left: 1.5cm; }
|
||||
.paper .tiptap .toc-level-5 { padding-left: 2cm; }
|
||||
.paper .tiptap .toc-text {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -98,8 +98,11 @@ async function settle(page) {
|
||||
// The spacers' heights too, not just their count: they go on settling for a while
|
||||
// after the last one is placed, and the page a line lands on moves with them.
|
||||
const spacers = Array.from(document.querySelectorAll('[data-page-break-spacer]'));
|
||||
// The index's page numbers settle after the spacers do — a stale one is a line of
|
||||
// text that still changes.
|
||||
const toc = Array.from(document.querySelectorAll('.toc-page')).map((t) => t.textContent).join(',');
|
||||
const key = el.style.minHeight + '|' + el.children.length + '|' + spacers.length + '|'
|
||||
+ spacers.reduce((sum, s) => sum + s.offsetHeight, 0);
|
||||
+ spacers.reduce((sum, s) => sum + s.offsetHeight, 0) + '|' + toc;
|
||||
const w = window;
|
||||
if (w.__parityKey !== key) { w.__parityKey = key; w.__paritySince = performance.now(); return false; }
|
||||
return performance.now() - (w.__paritySince ?? 0) > 3000;
|
||||
|
||||
Reference in new issue
Block a user