@sobree/core 0.1.8 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,26 @@
1
+ import { Paper } from './paper';
2
+ /**
3
+ * For each paper, find the footnote refs that landed on it after
4
+ * pagination and populate that paper's `.paper-footnotes` zone with the
5
+ * cited bodies — Word's "footnote bodies render at the bottom of the page
6
+ * where they're referenced" behaviour.
7
+ *
8
+ * Footnote bodies are sourced from two places:
9
+ * 1. The doc-end `<aside class="sobree-footnotes">` the renderer
10
+ * initially appends to paper[0].content. We harvest the `<li>`s out
11
+ * of it before the aside itself gets re-paginated as a block.
12
+ * 2. Any footnote zones already populated from a previous repaginate
13
+ * pass — those bodies stay in scope across iterations.
14
+ *
15
+ * Caveat: body budget is NOT reduced here for footnote-zone height; that
16
+ * reservation is `footnotePageHeights`'s job, fed back into the next
17
+ * pagination pass.
18
+ */
19
+ export declare function distributeFootnotes(papers: readonly Paper[]): void;
20
+ /**
21
+ * Per-page body budget after subtracting each page's footnote-zone
22
+ * height — entry `i` is page `i`'s budget. Trailing entries equal to the
23
+ * baseline are trimmed so two arrays differing only by an unrelated tail
24
+ * compare equal (keeps the repaginate loop's `arraysEqual` honest).
25
+ */
26
+ export declare function footnotePageHeights(papers: readonly Paper[], baselineBudgetPx: number): number[];
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Unequal multi-column flow.
3
+ *
4
+ * CSS multi-column only produces equal columns, so a Word section with
5
+ * explicit per-column widths (`<w:cols w:equalWidth="0">`) can't be laid
6
+ * out by CSS. The renderer (`openColumnContainerIfNeeded`) emits a flat
7
+ * `.sobree-cols-unequal` wrapper stamped with the column geometry; this
8
+ * pass — run after layout, when heights are measurable — restructures it
9
+ * into explicit-width column tracks and flows the section's blocks across
10
+ * them.
11
+ *
12
+ * Fill model: BALANCE, not fill-to-page. Word balances a multi-column
13
+ * section that has no explicit column break and fits one page — it
14
+ * equalises the column heights rather than packing the first column to
15
+ * the page bottom and spilling the remainder. (CSS `column-count` does
16
+ * the same for the equal-width path; this is the unequal-width analogue.)
17
+ * A naive "fill column 1 to the page height, then spill" instead packs
18
+ * the wide column nearly full before the narrow one starts, and — worse —
19
+ * the page-height threshold sits far above the actual content, so a few
20
+ * pixels of font-metric drift between renders (web fonts loaded vs not)
21
+ * flip blocks across the boundary. Balancing both matches Word and is
22
+ * stable: the split lands at the block boundary that minimises the
23
+ * tallest column, which block granularity pins in place.
24
+ *
25
+ * Scope: single-page column sections (a section whose content fits one
26
+ * page), which matches the equal-column path's current capability — that
27
+ * wraps a section's content in one monolithic block the paginator places
28
+ * on a single page. Blocks are moved whole (a paragraph is not split
29
+ * across a column boundary); that's faithful for the templates this
30
+ * targets, where columns break at block boundaries. `colHeightPx` is the
31
+ * page content height — a hard ceiling no column may exceed (content
32
+ * taller than that genuinely overflows the page, out of this scope).
33
+ * Idempotent: re-running re-flattens and re-fills, so the iterative
34
+ * paginate loop can call it each pass.
35
+ */
36
+ /**
37
+ * Flow every unequal-column section under `root` into its width tracks.
38
+ * `colHeightPx` is the page content-height budget (the height each
39
+ * column may fill before spilling to the next).
40
+ */
41
+ export declare function flowUnequalColumnSections(root: HTMLElement, colHeightPx: number): void;
@@ -1,7 +1,7 @@
1
- import { PageSetup } from './pageSetup';
1
+ import { AnchoredFrame, SectionProperties } from '../doc/types';
2
2
  import { AnchorLayerContext } from '../editor/view/docRenderer/anchorLayer';
3
+ import { PageSetup } from './pageSetup';
3
4
  import { ZoneRenderContext } from './paperZone';
4
- import { AnchoredFrame, SectionProperties } from '../doc/types';
5
5
  export type { ZoneRenderContext } from './paperZone';
6
6
  /**
7
7
  * A single paper — exactly one page. Fixed width and height from the page
@@ -1,5 +1,5 @@
1
- import { PageSetup } from './pageSetup';
2
1
  import { AnchoredFrame, Block, NamedStyle, NumberingDefinition, SectionProperties } from '../doc/types';
2
+ import { PageSetup } from './pageSetup';
3
3
  /**
4
4
  * Sourced from `SobreeDocument`: the rich AST + the dependencies
5
5
  * `renderBlocks` needs (numbering, named styles, embedded media bytes).
@@ -120,26 +120,6 @@ export declare class PaperStack {
120
120
  * flow instead of accumulating inter-fragment margins.
121
121
  */
122
122
  repaginate(): void;
123
- /**
124
- * Per-page footnote pinning. After body pagination completes, scan
125
- * each paper for footnote references (`<sup class="sobree-footnote-ref">`)
126
- * and populate that paper's `.paper-footnotes` zone with the cited
127
- * bodies — matching Word's "footnote bodies render at the bottom of
128
- * the page where they're referenced" behaviour.
129
- *
130
- * Footnote bodies are sourced from two places:
131
- * 1. The doc-end `<aside class="sobree-footnotes">` the renderer
132
- * initially appends to paper[0].content. We harvest the `<li>`s
133
- * out of it before the aside itself gets re-paginated as a block.
134
- * 2. Any footnote zones already populated from a previous repaginate
135
- * pass — those bodies stay in scope across iterations.
136
- *
137
- * Caveat: body budget is NOT reduced for footnote-zone height. On
138
- * pages with many footnotes, the footnote zone may extend past the
139
- * page bottom into the footer area. True budget-reservation is its
140
- * own paginator feature.
141
- */
142
- private distributeFootnotes;
143
123
  /**
144
124
  * One round of consolidate → merge → paginate → distribute.
145
125
  * Re-entrant: each call re-collects blocks from every paper, so the
@@ -154,7 +134,6 @@ export declare class PaperStack {
154
134
  * trailing entries equal to the baseline, so consumers can detect
155
135
  * "no per-page overrides needed" via `length === 0`.
156
136
  */
157
- private computePageHeights;
158
137
  /**
159
138
  * Largest content overflow across all papers, in CSS px. Zero means
160
139
  * every paper fits within its content area. Used by the iterative
@@ -223,6 +202,7 @@ export declare class PaperStack {
223
202
  */
224
203
  private pickRichZone;
225
204
  }
205
+ /** Parse "sobree-footnote-7" → 7; returns null for anything else. */
226
206
  /**
227
207
  * Walk the paginator's output back-to-front, collapsing trailing pages
228
208
  * whose blocks are all visually empty into the previous page.
@@ -1,5 +1,5 @@
1
- import { AnchorLayerContext } from '../editor/view/docRenderer/anchorLayer';
2
1
  import { AnchoredFrame, Block, NamedStyle, NumberingDefinition } from '../doc/types';
2
+ import { AnchorLayerContext } from '../editor/view/docRenderer/anchorLayer';
3
3
  /**
4
4
  * Everything a header/footer zone needs to render its FLOW content. The
5
5
  * floating layer (anchored frames) is painted separately via
@@ -1,6 +1,6 @@
1
1
  import { Editor } from '../editor';
2
+ import { AttachPresenceOptions, PresenceHandle } from './attach';
2
3
  import { AwarenessLike } from './awareness';
3
- import { PresenceHandle, AttachPresenceOptions } from './attach';
4
4
  export interface AttachPresenceOverlayOptions extends AttachPresenceOptions {
5
5
  /**
6
6
  * Element to render the overlay into. Should be an ancestor of the
package/dist/sobree.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { PageSetup } from './paperStack/pageSetup';
2
- import { Editor, OutlineItem, TrackChangesState } from './editor';
3
1
  import { SobreeDocument } from './doc/types';
2
+ import { Editor, OutlineItem, TrackChangesState } from './editor';
3
+ import { PageSetup } from './paperStack/pageSetup';
4
4
  export type SobreeMode = "edit" | "read";
5
5
  export type SobreeEvent = "change" | "paginate" | "setup" | "mode-change" | "track-changes-change" | "docx:import" | "docx:export";
6
6
  export interface SobreeEventPayload {
@@ -75,6 +75,9 @@ export declare class Sobree {
75
75
  private readonly stack;
76
76
  private setup;
77
77
  private mode;
78
+ /** Guards a single in-flight `document.fonts.ready` repagination so
79
+ * repeated triggers while fonts load coalesce into one re-run. */
80
+ private fontSettleScheduled;
78
81
  private readonly listeners;
79
82
  private readonly detachPaginate;
80
83
  private readonly detachChange;
@@ -136,6 +139,24 @@ export declare class Sobree {
136
139
  * `requestAnimationFrame` after the container is in the DOM).
137
140
  */
138
141
  repaginate(): void;
142
+ /**
143
+ * Repaginate, unless a header/footer zone is being edited in place
144
+ * (live zone edits manage their own reflow). Also schedules a re-run
145
+ * once any still-loading document fonts settle.
146
+ */
147
+ private paginateUnlessZoneEditing;
148
+ /**
149
+ * Pagination and column balancing measure laid-out text, so they depend
150
+ * on the actual font glyph metrics. A document's embedded fonts load
151
+ * asynchronously — a pass that runs before they arrive (notably on a
152
+ * cold reload, or right after `loadDocx` registers new faces) measures
153
+ * with FALLBACK metrics, which can mis-balance columns or mis-place page
154
+ * breaks. When fonts are still loading, re-run once they settle so the
155
+ * final layout reflects real glyphs. No-op when nothing is pending
156
+ * (the common warm-cache / steady-editing case), and a single in-flight
157
+ * re-run is coalesced so rapid changes during a load don't pile up.
158
+ */
159
+ private repaginateWhenFontsSettle;
139
160
  /** Delegate to `editor.getOutline()`. */
140
161
  getOutline(): OutlineItem[];
141
162
  /** Current mode. Default is `"edit"`. */
package/dist/tokens.css CHANGED
@@ -37,15 +37,15 @@
37
37
 
38
38
  :root {
39
39
  /* Primary — Sobree Amber. */
40
- --sobree-50: #fdf6ee;
40
+ --sobree-50: #fdf6ee;
41
41
  --sobree-100: #f9e6d0;
42
42
  --sobree-500: #c96f22;
43
43
  --sobree-600: #b2591a;
44
44
 
45
45
  /* Neutrals — warm ink, not gray. */
46
- --ink-0: #ffffff;
47
- --ink-25: #fbfaf7;
48
- --ink-50: #f6f4ef;
46
+ --ink-0: #ffffff;
47
+ --ink-25: #fbfaf7;
48
+ --ink-50: #f6f4ef;
49
49
  --ink-100: #eceae3;
50
50
  --ink-200: #dedbd0;
51
51
  --ink-300: #c4bfaf;
@@ -57,20 +57,20 @@
57
57
  --ink-900: #14130f;
58
58
 
59
59
  /* Semantic tokens — what components reference. */
60
- --bg: var(--ink-25);
61
- --bg-elevated: var(--ink-0);
62
- --bg-subtle: var(--ink-50);
63
- --bg-hover: var(--ink-100);
64
- --fg: var(--ink-800);
65
- --fg-strong: var(--ink-900);
66
- --fg-muted: var(--ink-600);
67
- --fg-subtle: var(--ink-500);
60
+ --bg: var(--ink-25);
61
+ --bg-elevated: var(--ink-0);
62
+ --bg-subtle: var(--ink-50);
63
+ --bg-hover: var(--ink-100);
64
+ --fg: var(--ink-800);
65
+ --fg-strong: var(--ink-900);
66
+ --fg-muted: var(--ink-600);
67
+ --fg-subtle: var(--ink-500);
68
68
  --fg-on-primary: var(--ink-0);
69
- --border: var(--ink-200);
69
+ --border: var(--ink-200);
70
70
  --border-strong: var(--ink-300);
71
- --primary: var(--sobree-500);
71
+ --primary: var(--sobree-500);
72
72
  --primary-hover: var(--sobree-600);
73
- --primary-soft: var(--sobree-50);
73
+ --primary-soft: var(--sobree-50);
74
74
 
75
75
  /* Radii. */
76
76
  --radius-xs: 2px;
@@ -89,9 +89,9 @@
89
89
 
90
90
  /* Motion. */
91
91
  --ease-standard: cubic-bezier(0.2, 0, 0, 1);
92
- --dur-fast: 120ms;
93
- --dur-base: 180ms;
94
- --dur-slow: 280ms;
92
+ --dur-fast: 120ms;
93
+ --dur-base: 180ms;
94
+ --dur-slow: 280ms;
95
95
 
96
96
  /* Spacing — 4px base. */
97
97
  --space-1: 4px;
@@ -136,9 +136,8 @@
136
136
  --sobree-revision-tint: 8%;
137
137
 
138
138
  /* Comment styling. */
139
- --sobree-comment-range-bg: rgba(255, 217, 0, 0.25);
139
+ --sobree-comment-range-bg: rgba(255, 217, 0, 0.25);
140
140
  --sobree-comment-range-border: rgba(180, 130, 0, 0.5);
141
- --sobree-comment-accent: #f59e0b; /* card rule when author colour absent */
142
- --sobree-comment-resolved: #16a34a; /* resolved-thread green */
141
+ --sobree-comment-accent: #f59e0b; /* card rule when author colour absent */
142
+ --sobree-comment-resolved: #16a34a; /* resolved-thread green */
143
143
  }
144
-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sobree/core",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },