pagepilot-visual-editor 1.0.26 → 1.0.27

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.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,28 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.0.27] — 2026-09-21
10
+
11
+ ### Added
12
+ - `render-core/repeatRowManagement.ts`, exported from the `./render` subpath:
13
+ `reconcileRepeatFieldRowCount(documentTree, field, targetRowCount, growDirection?)`
14
+ — grows or shrinks a container-repeat field's materialized row-block count
15
+ (cloning via `addRepeatRow`/growing the container via `growContainerToFit`,
16
+ or removing trailing row subtrees via `removeSubtree` +
17
+ `shrinkContainerToFit`) to match a target row count, e.g. so
18
+ server-side-regenerated HTML can show exactly as many cards as a live API
19
+ response returned, not just whatever was authored at Save time. Pure port
20
+ of the editor's `documentUtils.ts` block-tree mutators (`cloneSubtree`,
21
+ `addRepeatRow`, `removeSubtree`, `growContainerToFit`,
22
+ `shrinkContainerToFit`, `findRepeatableContainerOf`,
23
+ `getRepeatableContainerInfo`), rewritten to thread a plain document value
24
+ instead of the editor's Zustand-backed store — no new dependencies, no
25
+ editor/MUI imports, safe for the render-only bundle (same precedent as the
26
+ already-ported `collectSubtreeIds`). Grid's two-axis growth direction has
27
+ no default encoded in the pure logic (the editor asks the author via
28
+ `GrowDirectionDialog`); callers must pass `growDirection` explicitly for
29
+ serve-time use.
30
+
9
31
  ## [1.0.0] — 2026-07-06
10
32
 
11
33
  Initial public release.
@@ -0,0 +1,96 @@
1
+ export type TemplateArrayRow = Record<string, string>;
2
+ /**
3
+ * Deep-clone a subtree under fresh block ids. Returns the new blocks keyed
4
+ * by new id, the idMap (oldId → newId) and the new root id. Pure — caller
5
+ * splices.
6
+ */
7
+ export declare function cloneSubtree(documentTree: Record<string, any>, rootId: string): {
8
+ newBlocks: Record<string, any>;
9
+ idMap: Record<string, string>;
10
+ newRootId: string;
11
+ };
12
+ export declare function insertDocument(documentTree: Record<string, any>, targetChildId: string, insertedChildId: string): {
13
+ parentId: string;
14
+ updatedParent: any;
15
+ } | null;
16
+ /**
17
+ * Materialize one repeat row: clone the `sourceBlockId` subtree and place
18
+ * the clone after `afterBlockId` (the previous row's block). Returns the
19
+ * updated document + the new clone's root id, or `null` when the
20
+ * container has no room and can't auto-grow (caller decides whether to
21
+ * grow the container first via `growContainerToFit` and retry).
22
+ */
23
+ export declare function addRepeatRow(documentTree: Record<string, any>, sourceBlockId: string, afterBlockId: string): {
24
+ updatedDocument: Record<string, any>;
25
+ newRootId: string;
26
+ } | null;
27
+ /**
28
+ * Remove a set of block ids and filter them out of every parent's
29
+ * child-id arrays. Used to delete a repeat row's cloned container
30
+ * subtree.
31
+ */
32
+ export declare function removeSubtree(documentTree: Record<string, any>, idsToRemove: Set<string>): Record<string, any>;
33
+ export type RepeatableSlot = {
34
+ childrenIds: string[];
35
+ };
36
+ export type RepeatableContainerInfo = {
37
+ containerId: string;
38
+ type: "Grid" | "FlexColumns" | "Carousel" | "Marquee" | "Pagination" | "TabsBlock";
39
+ slotCount: number;
40
+ slots: RepeatableSlot[];
41
+ firstFilledSlot: number;
42
+ emptySlotCount: number;
43
+ };
44
+ export declare function isRepeatableContainerType(type: string | undefined): boolean;
45
+ export declare function getRepeatableContainerInfo(documentTree: Record<string, any>, containerId: string): RepeatableContainerInfo | null;
46
+ /** Find the repeatable container whose slots hold `childId`. */
47
+ export declare function findRepeatableContainerOf(documentTree: Record<string, any>, childId: string): string | null;
48
+ /**
49
+ * Grow a repeatable container so it has at least `neededSlots` slots.
50
+ * Grid is the one two-axis case needing a `direction` choice (no default
51
+ * encoded in the pure logic — there's no human to ask at serve time, so
52
+ * callers must pass one explicitly). Every other container type ignores
53
+ * `direction` and grows its single axis. Capped by each block type's
54
+ * schema max (Carousel 20, Marquee 30, TabsBlock 10) — returns the
55
+ * unchanged document if the cap is already hit.
56
+ */
57
+ export declare function growContainerToFit(documentTree: Record<string, any>, containerId: string, neededSlots: number, direction: "rows" | "columns"): Record<string, any>;
58
+ /**
59
+ * Shrink a repeatable container back down to `neededSlots`. Only trailing
60
+ * EMPTY slots are dropped — a filled slot always stops the shrink, so
61
+ * authored content already emptied by a prior `removeSubtree` call is what
62
+ * actually gets trimmed here, never content that's still in use.
63
+ */
64
+ export declare function shrinkContainerToFit(documentTree: Record<string, any>, containerId: string, neededSlots: number): Record<string, any>;
65
+ export interface ReconcileRowCountResult {
66
+ /** Updated document tree (same reference as input if nothing changed). */
67
+ document: Record<string, any>;
68
+ /** The field's new sourceBlockId (only ever changes if row 0 itself had to move — in
69
+ * practice it never does; kept for symmetry/future-proofing). */
70
+ sourceBlockId: string;
71
+ /** The field's new rowIds (one per materialized row beyond row 0). */
72
+ rowIds: string[];
73
+ /** How many rows are actually materialized after this call (1 + rowIds.length). */
74
+ achievedRowCount: number;
75
+ /** True if `achievedRowCount < targetRowCount` — the container ran out of room
76
+ * and could not grow further (e.g. a full non-auto-growing Grid/Carousel/Marquee). */
77
+ capacityLimited: boolean;
78
+ }
79
+ /**
80
+ * Reconcile a container-repeat field's materialized row-block count to
81
+ * exactly `targetRowCount`. Grows by cloning (`addRepeatRow`, growing the
82
+ * container first via `growContainerToFit` if every slot is already full)
83
+ * or shrinks by removing trailing rows' subtrees (`removeSubtree` +
84
+ * `shrinkContainerToFit`). Never touches row 0 (the field's own
85
+ * `sourceBlockId`) — `targetRowCount` is clamped to at least 1.
86
+ *
87
+ * Pure: does not mutate `documentTree`. Fails open in the sense that if
88
+ * growth runs out of room partway through, it returns whatever was
89
+ * achieved (`capacityLimited: true`) rather than throwing — callers decide
90
+ * how to handle a partial reconciliation (e.g. drop the data beyond what
91
+ * fit, log a warning).
92
+ */
93
+ export declare function reconcileRepeatFieldRowCount(documentTree: Record<string, any>, field: {
94
+ sourceBlockId?: string;
95
+ rowIds?: string[];
96
+ }, targetRowCount: number, growDirection?: "rows" | "columns"): ReconcileRowCountResult | null;